Repository: twpayne/chezmoi Branch: master Commit: 132329211927 Files: 962 Total size: 4.1 MB Directory structure: gitextract_mhbu3wo7/ ├── .config/ │ ├── capslock-summary.json │ ├── editorconfig-checker.json │ ├── golangci.yml │ ├── goreleaser.yaml │ └── markdownlint-cli2.yaml ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── CODE_OF_CONDUCT.md │ ├── CONTRIBUTING.md │ ├── ISSUE_TEMPLATE/ │ │ ├── 01_support_request.md │ │ ├── 02_feature_request.md │ │ └── 03_bug_report.md │ ├── PULL_REQUEST_TEMPLATE.md │ ├── SECURITY.md │ ├── actions/ │ │ ├── free-disk-space/ │ │ │ └── action.yml │ │ └── setup-go/ │ │ └── action.yml │ └── workflows/ │ ├── clear-pr-caches.yml │ ├── govulncheck.yml │ ├── installer.yml │ ├── lock-threads.yml │ └── main.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── assets/ │ ├── chezmoi.io/ │ │ ├── .gitignore │ │ ├── CNAME │ │ ├── docs/ │ │ │ ├── chezmoi/ │ │ │ │ └── index.html │ │ │ ├── comparison-table.md │ │ │ ├── developer-guide/ │ │ │ │ ├── architecture.md │ │ │ │ ├── building-on-top-of-chezmoi.md │ │ │ │ ├── contributing-changes.md │ │ │ │ ├── index.md │ │ │ │ ├── install-script.md │ │ │ │ ├── packaging.md │ │ │ │ ├── releases.md │ │ │ │ ├── security.md │ │ │ │ ├── testing.md │ │ │ │ ├── using-make.md │ │ │ │ └── website.md │ │ │ ├── docs.go │ │ │ ├── extra/ │ │ │ │ └── refresh_on_toggle_dark_light.js │ │ │ ├── hooks.py │ │ │ ├── index.md.tmpl │ │ │ ├── install.md.tmpl │ │ │ ├── license.md │ │ │ ├── links/ │ │ │ │ ├── articles.md.tmpl │ │ │ │ ├── articles.md.yaml │ │ │ │ ├── dotfile-repos.md │ │ │ │ ├── podcasts.md.tmpl │ │ │ │ ├── podcasts.md.yaml │ │ │ │ ├── related-software.md │ │ │ │ ├── social-media.md │ │ │ │ ├── videos.md.tmpl │ │ │ │ └── videos.md.yaml │ │ │ ├── migrating-from-another-dotfile-manager.md │ │ │ ├── quick-start.md │ │ │ ├── reference/ │ │ │ │ ├── application-order.md │ │ │ │ ├── command-line-flags/ │ │ │ │ │ ├── common.md │ │ │ │ │ ├── developer.md │ │ │ │ │ ├── global.md │ │ │ │ │ └── index.md │ │ │ │ ├── commands/ │ │ │ │ │ ├── add.md │ │ │ │ │ ├── age-keygen.md │ │ │ │ │ ├── age.md │ │ │ │ │ ├── apply.md │ │ │ │ │ ├── archive.md │ │ │ │ │ ├── cat-config.md │ │ │ │ │ ├── cat.md │ │ │ │ │ ├── cd.md │ │ │ │ │ ├── chattr.md │ │ │ │ │ ├── commands.go │ │ │ │ │ ├── commands_test.go │ │ │ │ │ ├── completion.md │ │ │ │ │ ├── data.md │ │ │ │ │ ├── decrypt.md │ │ │ │ │ ├── destroy.md │ │ │ │ │ ├── diff.md │ │ │ │ │ ├── docker.md │ │ │ │ │ ├── doctor.md │ │ │ │ │ ├── dump-config.md │ │ │ │ │ ├── dump.md │ │ │ │ │ ├── edit-config-template.md │ │ │ │ │ ├── edit-config.md │ │ │ │ │ ├── edit-encrypted.md │ │ │ │ │ ├── edit.md │ │ │ │ │ ├── encrypt.md │ │ │ │ │ ├── execute-template.md │ │ │ │ │ ├── forget.md │ │ │ │ │ ├── generate.md │ │ │ │ │ ├── git.md │ │ │ │ │ ├── help.md │ │ │ │ │ ├── ignored.md │ │ │ │ │ ├── import.md │ │ │ │ │ ├── index.md │ │ │ │ │ ├── init.md │ │ │ │ │ ├── license.md │ │ │ │ │ ├── list.md │ │ │ │ │ ├── manage.md │ │ │ │ │ ├── managed.md │ │ │ │ │ ├── merge-all.md │ │ │ │ │ ├── merge.md │ │ │ │ │ ├── podman.md │ │ │ │ │ ├── purge.md │ │ │ │ │ ├── re-add.md │ │ │ │ │ ├── remove.md │ │ │ │ │ ├── rm.md │ │ │ │ │ ├── secret.md │ │ │ │ │ ├── source-path.md │ │ │ │ │ ├── ssh.md │ │ │ │ │ ├── state.md │ │ │ │ │ ├── status.md │ │ │ │ │ ├── target-path.md │ │ │ │ │ ├── unmanage.md │ │ │ │ │ ├── unmanaged.md │ │ │ │ │ ├── update.md │ │ │ │ │ ├── upgrade.md │ │ │ │ │ └── verify.md │ │ │ │ ├── concepts.md │ │ │ │ ├── configuration-file/ │ │ │ │ │ ├── editor.md │ │ │ │ │ ├── hooks.md │ │ │ │ │ ├── index.md │ │ │ │ │ ├── interpreters.md │ │ │ │ │ ├── pinentry.md │ │ │ │ │ ├── textconv.md │ │ │ │ │ ├── umask.md │ │ │ │ │ ├── variables.md.tmpl │ │ │ │ │ ├── variables.md.yaml │ │ │ │ │ └── warnings.md │ │ │ │ ├── index.md │ │ │ │ ├── plugins.md │ │ │ │ ├── release-history.md.tmpl │ │ │ │ ├── source-state-attributes.md │ │ │ │ ├── special-directories/ │ │ │ │ │ ├── chezmoidata.md │ │ │ │ │ ├── chezmoiexternals.md │ │ │ │ │ ├── chezmoiscripts.md │ │ │ │ │ ├── chezmoitemplates.md │ │ │ │ │ └── index.md │ │ │ │ ├── special-files/ │ │ │ │ │ ├── chezmoi-format-tmpl.md │ │ │ │ │ ├── chezmoidata-format.md │ │ │ │ │ ├── chezmoiexternal-format.md │ │ │ │ │ ├── chezmoiignore.md │ │ │ │ │ ├── chezmoiremove.md │ │ │ │ │ ├── chezmoiroot.md │ │ │ │ │ ├── chezmoiversion.md │ │ │ │ │ └── index.md │ │ │ │ ├── target-types.md │ │ │ │ └── templates/ │ │ │ │ ├── 1password-functions/ │ │ │ │ │ ├── index.md │ │ │ │ │ ├── onepassword.md │ │ │ │ │ ├── onepasswordDetailsFields.md │ │ │ │ │ ├── onepasswordDocument.md │ │ │ │ │ ├── onepasswordItemFields.md │ │ │ │ │ └── onepasswordRead.md │ │ │ │ ├── aws-secrets-manager-functions/ │ │ │ │ │ ├── awsSecretsManager.md │ │ │ │ │ ├── awsSecretsManagerRaw.md │ │ │ │ │ └── index.md │ │ │ │ ├── azure-key-vault-functions/ │ │ │ │ │ └── azureKeyVault.md │ │ │ │ ├── bitwarden-functions/ │ │ │ │ │ ├── bitwarden.md │ │ │ │ │ ├── bitwardenAttachment.md │ │ │ │ │ ├── bitwardenAttachmentByRef.md │ │ │ │ │ ├── bitwardenFields.md │ │ │ │ │ ├── bitwardenSecrets.md │ │ │ │ │ ├── index.md │ │ │ │ │ ├── rbw.md │ │ │ │ │ └── rbwFields.md │ │ │ │ ├── dashlane-functions/ │ │ │ │ │ ├── dashlaneNote.md │ │ │ │ │ ├── dashlanePassword.md │ │ │ │ │ └── index.md │ │ │ │ ├── directives.md │ │ │ │ ├── doppler-functions/ │ │ │ │ │ ├── doppler.md │ │ │ │ │ ├── dopplerProjectJson.md │ │ │ │ │ └── index.md │ │ │ │ ├── ejson-functions/ │ │ │ │ │ ├── ejsonDecrypt.md │ │ │ │ │ ├── ejsonDecryptWithKey.md │ │ │ │ │ └── index.md │ │ │ │ ├── functions/ │ │ │ │ │ ├── abortEmpty.md │ │ │ │ │ ├── comment.md │ │ │ │ │ ├── completion.md │ │ │ │ │ ├── decrypt.md │ │ │ │ │ ├── deleteValueAtPath.md │ │ │ │ │ ├── encrypt.md │ │ │ │ │ ├── ensureLinePrefix.md │ │ │ │ │ ├── eqFold.md │ │ │ │ │ ├── exec.md │ │ │ │ │ ├── findExecutable.md │ │ │ │ │ ├── findOneExecutable.md │ │ │ │ │ ├── fromIni.md │ │ │ │ │ ├── fromJson.md │ │ │ │ │ ├── fromJsonc.md │ │ │ │ │ ├── fromToml.md │ │ │ │ │ ├── fromYaml.md │ │ │ │ │ ├── getRedirectedURL.md │ │ │ │ │ ├── glob.md │ │ │ │ │ ├── hexDecode.md │ │ │ │ │ ├── hexEncode.md │ │ │ │ │ ├── include.md │ │ │ │ │ ├── includeTemplate.md │ │ │ │ │ ├── index.md │ │ │ │ │ ├── ioreg.md │ │ │ │ │ ├── isExecutable.md │ │ │ │ │ ├── joinPath.md │ │ │ │ │ ├── jq.md │ │ │ │ │ ├── lookPath.md │ │ │ │ │ ├── lstat.md │ │ │ │ │ ├── mozillaInstallHash.md │ │ │ │ │ ├── output.md │ │ │ │ │ ├── outputList.md │ │ │ │ │ ├── pruneEmptyDicts.md │ │ │ │ │ ├── quoteList.md │ │ │ │ │ ├── replaceAllRegex.md │ │ │ │ │ ├── setValueAtPath.md │ │ │ │ │ ├── stat.md │ │ │ │ │ ├── toIni.md │ │ │ │ │ ├── toPrettyJson.md │ │ │ │ │ ├── toString.md │ │ │ │ │ ├── toStrings.md │ │ │ │ │ ├── toToml.md │ │ │ │ │ ├── toYaml.md │ │ │ │ │ └── warnf.md │ │ │ │ ├── github-functions/ │ │ │ │ │ ├── gitHubKeys.md │ │ │ │ │ ├── gitHubLatestRelease.md │ │ │ │ │ ├── gitHubLatestReleaseAssetURL.md │ │ │ │ │ ├── gitHubLatestTag.md │ │ │ │ │ ├── gitHubRelease.md │ │ │ │ │ ├── gitHubReleaseAssetURL.md │ │ │ │ │ ├── gitHubReleases.md │ │ │ │ │ ├── gitHubTags.md │ │ │ │ │ └── index.md │ │ │ │ ├── gopass-functions/ │ │ │ │ │ ├── gopass.md │ │ │ │ │ ├── gopassRaw.md │ │ │ │ │ └── index.md │ │ │ │ ├── index.md │ │ │ │ ├── init-functions/ │ │ │ │ │ ├── exit.md │ │ │ │ │ ├── index.md │ │ │ │ │ ├── promptBool.md │ │ │ │ │ ├── promptBoolOnce.md │ │ │ │ │ ├── promptChoice.md │ │ │ │ │ ├── promptChoiceOnce.md │ │ │ │ │ ├── promptInt.md │ │ │ │ │ ├── promptIntOnce.md │ │ │ │ │ ├── promptMultichoice.md │ │ │ │ │ ├── promptMultichoiceOnce.md │ │ │ │ │ ├── promptString.md │ │ │ │ │ ├── promptStringOnce.md │ │ │ │ │ ├── stdinIsATTY.md │ │ │ │ │ └── writeToStdout.md │ │ │ │ ├── keepassxc-functions/ │ │ │ │ │ ├── index.md │ │ │ │ │ ├── keepassxc.md │ │ │ │ │ ├── keepassxcAttachment.md │ │ │ │ │ └── keepassxcAttribute.md │ │ │ │ ├── keeper-functions/ │ │ │ │ │ ├── index.md │ │ │ │ │ ├── keeper.md │ │ │ │ │ ├── keeperDataFields.md │ │ │ │ │ └── keeperFindPassword.md │ │ │ │ ├── keyring-functions/ │ │ │ │ │ └── keyring.md │ │ │ │ ├── lastpass-functions/ │ │ │ │ │ ├── index.md │ │ │ │ │ ├── lastpass.md │ │ │ │ │ └── lastpassRaw.md │ │ │ │ ├── pass-functions/ │ │ │ │ │ ├── index.md │ │ │ │ │ ├── pass.md │ │ │ │ │ ├── passFields.md │ │ │ │ │ └── passRaw.md │ │ │ │ ├── passhole-functions/ │ │ │ │ │ ├── index.md │ │ │ │ │ └── passhole.md │ │ │ │ ├── protonpass-functions/ │ │ │ │ │ ├── index.md │ │ │ │ │ ├── protonPass.md │ │ │ │ │ └── protonPassJSON.md │ │ │ │ ├── secret-functions/ │ │ │ │ │ ├── index.md │ │ │ │ │ ├── secret.md │ │ │ │ │ └── secretJSON.md │ │ │ │ ├── variables.md │ │ │ │ └── vault-functions/ │ │ │ │ └── vault.md │ │ │ ├── user-guide/ │ │ │ │ ├── advanced/ │ │ │ │ │ ├── customize-your-source-directory.md │ │ │ │ │ ├── install-packages-declaratively.md │ │ │ │ │ ├── install-your-password-manager-on-init.md │ │ │ │ │ ├── migrate-away-from-chezmoi.md │ │ │ │ │ └── use-chezmoi-with-watchman.md │ │ │ │ ├── command-overview.md │ │ │ │ ├── daily-operations.md │ │ │ │ ├── encryption/ │ │ │ │ │ ├── age.md │ │ │ │ │ ├── gpg.md │ │ │ │ │ ├── index.md │ │ │ │ │ ├── rage.md │ │ │ │ │ └── transparent.md │ │ │ │ ├── frequently-asked-questions/ │ │ │ │ │ ├── design.md │ │ │ │ │ ├── encryption.md │ │ │ │ │ ├── general.md │ │ │ │ │ ├── troubleshooting.md │ │ │ │ │ └── usage.md │ │ │ │ ├── include-files-from-elsewhere.md │ │ │ │ ├── machines/ │ │ │ │ │ ├── containers-and-vms.md │ │ │ │ │ ├── general.md │ │ │ │ │ ├── linux.md │ │ │ │ │ ├── macos.md │ │ │ │ │ └── windows.md │ │ │ │ ├── manage-different-types-of-file.md │ │ │ │ ├── manage-machine-to-machine-differences.md │ │ │ │ ├── password-managers/ │ │ │ │ │ ├── 1password.md │ │ │ │ │ ├── aws-secrets-manager.md │ │ │ │ │ ├── azure-key-vault.md │ │ │ │ │ ├── bitwarden.md │ │ │ │ │ ├── custom.md │ │ │ │ │ ├── dashlane.md │ │ │ │ │ ├── doppler.md │ │ │ │ │ ├── ejson.md │ │ │ │ │ ├── gopass.md │ │ │ │ │ ├── index.md │ │ │ │ │ ├── keepassxc.md │ │ │ │ │ ├── keeper.md │ │ │ │ │ ├── keychain-and-windows-credentials-manager.md │ │ │ │ │ ├── lastpass.md │ │ │ │ │ ├── pass.md │ │ │ │ │ ├── passhole.md │ │ │ │ │ ├── proton-pass.md │ │ │ │ │ └── vault.md │ │ │ │ ├── setup.md │ │ │ │ ├── templating.md │ │ │ │ ├── tools/ │ │ │ │ │ ├── diff.md │ │ │ │ │ ├── editor.md │ │ │ │ │ ├── http-or-socks5-proxy.md │ │ │ │ │ └── merge.md │ │ │ │ └── use-scripts-to-perform-actions.md │ │ │ ├── voice_30-04-2025_09-20-13.ogx │ │ │ ├── voice_30-04-2025_09-20-48.ogx │ │ │ ├── voice_30-04-2025_09-20-56.ogx │ │ │ ├── what-does-chezmoi-do.md │ │ │ └── why-use-chezmoi.md │ │ ├── mkdocs.yml │ │ └── snippets/ │ │ ├── common-flags/ │ │ │ ├── exclude.md │ │ │ ├── format.md │ │ │ ├── include.md │ │ │ ├── init.md │ │ │ ├── nul-path-separator.md │ │ │ ├── override-data-file.md │ │ │ ├── override-data.md │ │ │ ├── parent-dirs.md │ │ │ ├── path-style.md │ │ │ ├── recursive.md │ │ │ └── tree.md │ │ └── config-format.md │ ├── cosign/ │ │ ├── cosign.key │ │ └── cosign.pub │ ├── docker/ │ │ ├── alpine.Dockerfile │ │ ├── archlinux.Dockerfile │ │ ├── entrypoint.sh │ │ ├── fedora.Dockerfile │ │ ├── test.sh │ │ └── voidlinux.Dockerfile │ ├── get.chezmoi.io/ │ │ ├── .nojekyll │ │ └── CNAME │ ├── images/ │ │ ├── logotype_black.ai │ │ └── logotype_blue.ai │ ├── scripts/ │ │ ├── format-yaml.py │ │ ├── install-local-bin.sh │ │ ├── install.ps1 │ │ ├── install.sh │ │ └── stow-to-chezmoi.sh │ ├── templates/ │ │ ├── COMMIT_MESSAGE.tmpl │ │ ├── install-init-shell.sh.tmpl │ │ ├── install.sh │ │ ├── templates.go │ │ └── versioninfo.json.tmpl │ └── vagrant/ │ ├── freebsd14.Vagrantfile │ ├── freebsd14.test-chezmoi.sh │ ├── openbsd7.Vagrantfile │ ├── openbsd7.test-chezmoi.sh │ └── test.sh ├── bin/ │ └── .gitignore ├── completions/ │ ├── chezmoi-completion.bash │ ├── chezmoi.fish │ ├── chezmoi.ps1 │ └── chezmoi.zsh ├── go.mod ├── go.sum ├── internal/ │ ├── archivetest/ │ │ ├── archivetest.go │ │ ├── tar.go │ │ ├── tar_test.go │ │ ├── zip.go │ │ └── zip_test.go │ ├── chezmoi/ │ │ ├── abspath.go │ │ ├── abspath_test.go │ │ ├── actualstateentry.go │ │ ├── ageencryption.go │ │ ├── ageencryption_test.go │ │ ├── archive.go │ │ ├── archive_test.go │ │ ├── archivereadersystem.go │ │ ├── archivereadersystem_test.go │ │ ├── attr.go │ │ ├── attr_test.go │ │ ├── autotemplate.go │ │ ├── autotemplate_test.go │ │ ├── boltpersistentstate.go │ │ ├── boltpersistentstate_test.go │ │ ├── byteordermarks.go │ │ ├── chezmoi.go │ │ ├── chezmoi_test.go │ │ ├── chezmoi_unix.go │ │ ├── chezmoi_unix_test.go │ │ ├── chezmoi_windows.go │ │ ├── compression.go │ │ ├── data.go │ │ ├── data_test.go │ │ ├── debugencryption.go │ │ ├── debugpersistentstate.go │ │ ├── debugsystem.go │ │ ├── debugsystem_test.go │ │ ├── diff.go │ │ ├── dryrunsystem.go │ │ ├── dryrunsystem_test.go │ │ ├── dumpsystem.go │ │ ├── dumpsystem_test.go │ │ ├── duration.go │ │ ├── encryption.go │ │ ├── encryption_test.go │ │ ├── entrystate.go │ │ ├── entrystate_test.go │ │ ├── entrytypefilter.go │ │ ├── entrytypeset.go │ │ ├── entrytypeset_test.go │ │ ├── erroronwritesystem.go │ │ ├── erroronwritesystem_test.go │ │ ├── errors.go │ │ ├── externaldiffsystem.go │ │ ├── externaldiffsystem_test.go │ │ ├── findexecutable.go │ │ ├── findexecutable_darwin_test.go │ │ ├── findexecutable_unix_test.go │ │ ├── findexecutable_windows_test.go │ │ ├── format.go │ │ ├── format_test.go │ │ ├── gitdiffsystem.go │ │ ├── gitdiffsystem_test.go │ │ ├── github.go │ │ ├── glob.go │ │ ├── gpgencryption.go │ │ ├── gpgencryption_test.go │ │ ├── hexbytes.go │ │ ├── hexbytes_test.go │ │ ├── interpreter.go │ │ ├── lookpath.go │ │ ├── mockpersistentstate.go │ │ ├── mockpersistentstate_test.go │ │ ├── mode.go │ │ ├── noencryption.go │ │ ├── noencryption_test.go │ │ ├── nullpersistentstate.go │ │ ├── nullsystem.go │ │ ├── nullsystem_test.go │ │ ├── path_unix.go │ │ ├── path_windows.go │ │ ├── path_windows_test.go │ │ ├── patternset.go │ │ ├── patternset_test.go │ │ ├── persistentstate.go │ │ ├── persistentstate_test.go │ │ ├── readonlysystem.go │ │ ├── readonlysystem_test.go │ │ ├── realsystem.go │ │ ├── realsystem_test.go │ │ ├── realsystem_unix.go │ │ ├── realsystem_windows.go │ │ ├── recursivemerge.go │ │ ├── recursivemerge_test.go │ │ ├── refreshexternals.go │ │ ├── relpath.go │ │ ├── sourcerelpath.go │ │ ├── sourcerelpath_test.go │ │ ├── sourcestate.go │ │ ├── sourcestate_test.go │ │ ├── sourcestateentry.go │ │ ├── sourcestatetreenode.go │ │ ├── sourcestatetreenode_test.go │ │ ├── system.go │ │ ├── system_test.go │ │ ├── targetstateentry.go │ │ ├── targetstateentry_test.go │ │ ├── tarwritersystem.go │ │ ├── tarwritersystem_test.go │ │ ├── template.go │ │ ├── template_test.go │ │ ├── templatefuncs.go │ │ ├── transparentencryption.go │ │ ├── transparentencryption_test.go │ │ ├── zipwritersystem.go │ │ └── zipwritersystem_test.go │ ├── chezmoiassert/ │ │ └── chezmoiassert.go │ ├── chezmoibubbles/ │ │ ├── boolinputmodel.go │ │ ├── boolinputmodel_test.go │ │ ├── chezmoibubbles.go │ │ ├── chezmoibubbles_test.go │ │ ├── choiceinputmodel.go │ │ ├── choiceinputmodel_test.go │ │ ├── intinputmodel.go │ │ ├── intinputmodel_test.go │ │ ├── multichoiceinputmodel.go │ │ ├── passwordinputmodel.go │ │ ├── passwordinputmodel_test.go │ │ ├── stringinputmodel.go │ │ ├── stringinputmodel_test.go │ │ └── test-all.sh │ ├── chezmoierrors/ │ │ └── chezmoierrors.go │ ├── chezmoigit/ │ │ ├── chezmoigit.go │ │ ├── status.go │ │ └── status_test.go │ ├── chezmoilog/ │ │ ├── chezmoilog.go │ │ ├── nullhandler.go │ │ └── nullhandler_test.go │ ├── chezmoiset/ │ │ └── chezmoiset.go │ ├── chezmoitest/ │ │ ├── chezmoitest.go │ │ ├── chezmoitest_test.go │ │ ├── chezmoitest_unix.go │ │ └── chezmoitest_windows.go │ ├── cmd/ │ │ ├── addcmd.go │ │ ├── addcmd_test.go │ │ ├── agecmd.go │ │ ├── agekeygencmd.go │ │ ├── annotation.go │ │ ├── applycmd.go │ │ ├── applycmd_test.go │ │ ├── archivecmd.go │ │ ├── autobool.go │ │ ├── awssecretsmanagertemplatefuncs.go │ │ ├── azurekeyvaulttemplatefuncs.go │ │ ├── bitwardensecretstemplatefuncs.go │ │ ├── bitwardentemplatefuncs.go │ │ ├── catcmd.go │ │ ├── catcmd_test.go │ │ ├── catconfigcmd.go │ │ ├── cdcmd.go │ │ ├── chattrcmd.go │ │ ├── chattrcmd_test.go │ │ ├── choiceflag.go │ │ ├── cmd.go │ │ ├── cmd_test.go │ │ ├── completioncmd.go │ │ ├── config.go │ │ ├── config_tags_test.go │ │ ├── config_test.go │ │ ├── dashlanetemplatefuncs.go │ │ ├── datacmd.go │ │ ├── datacmd_test.go │ │ ├── dataformat.go │ │ ├── decryptcmd.go │ │ ├── destroycmd.go │ │ ├── diffcmd.go │ │ ├── diffcmd_test.go │ │ ├── dockercmd.go │ │ ├── doctorcmd.go │ │ ├── doctorcmd_unix.go │ │ ├── doctorcmd_windows.go │ │ ├── dopplertemplatefuncs.go │ │ ├── dumpcmd.go │ │ ├── dumpconfigcmd.go │ │ ├── editcmd.go │ │ ├── editconfigcmd.go │ │ ├── editconfigtemplatecmd.go │ │ ├── editencryptedcmd.go │ │ ├── ejsontemplatefuncs.go │ │ ├── encryptcmd.go │ │ ├── encryptiontemplatefuncs.go │ │ ├── errors.go │ │ ├── executetemplatecmd.go │ │ ├── forgetcmd.go │ │ ├── generatecmd.go │ │ ├── gitcmd.go │ │ ├── githubtemplatefuncs.go │ │ ├── gopasstemplatefuncs.go │ │ ├── helpcmd.go │ │ ├── helps.gen.go │ │ ├── ignoredcmd.go │ │ ├── importcmd.go │ │ ├── importcmd_test.go │ │ ├── initcmd.go │ │ ├── initcmd_test.go │ │ ├── inittemplatefuncs.go │ │ ├── inittemplatefuncs_test.go │ │ ├── interactivetemplatefuncs.go │ │ ├── internaltestcmd.go │ │ ├── interpreters.go │ │ ├── interpreters_test.go │ │ ├── interpreters_unix_test.go │ │ ├── interpreters_windows_test.go │ │ ├── keepassxctemplatefuncs.go │ │ ├── keepassxctemplatefuncs_test.go │ │ ├── keepertemplatefuncs.go │ │ ├── keyringtemplatefuncs.go │ │ ├── keyringtemplatefuncs_freebsdnocgo.go │ │ ├── lastpasstemplatefuncs.go │ │ ├── lastpasstemplatefuncs_test.go │ │ ├── lazyscryptidentity.go │ │ ├── lazywriter.go │ │ ├── license.gen.go │ │ ├── licensecmd.go │ │ ├── mackupcmd_darwin.go │ │ ├── mackupcmd_darwin_test.go │ │ ├── mackupcmd_nodarwin.go │ │ ├── main_test.go │ │ ├── managedcmd.go │ │ ├── managedcmd_test.go │ │ ├── mergeallcmd.go │ │ ├── mergecmd.go │ │ ├── mockcommand.cmd.tmpl │ │ ├── mockcommand.tmpl │ │ ├── noupgradecmd.go │ │ ├── onepasswordtemplatefuncs.go │ │ ├── onepasswordtemplatefuncs_test.go │ │ ├── passholetemplatefuncs.go │ │ ├── passtemplatefuncs.go │ │ ├── pathlist.go │ │ ├── pathlist_test.go │ │ ├── pathstyle.go │ │ ├── pinentry.go │ │ ├── prompt.go │ │ ├── protonpasstemplatefuncs.go │ │ ├── purgecmd.go │ │ ├── rbwtemplatefuncs.go │ │ ├── readdcmd.go │ │ ├── readdcmd_test.go │ │ ├── readhttpresponse.go │ │ ├── readhttpresponse_test.go │ │ ├── removecmd.go │ │ ├── secretcmd.go │ │ ├── secretkeyringcmd.go │ │ ├── secretkeyringcmd_freebsdnocgo.go │ │ ├── secrettemplatefuncs.go │ │ ├── shellquote.go │ │ ├── shellquote_test.go │ │ ├── sourcepathcmd.go │ │ ├── sshcmd.go │ │ ├── statecmd.go │ │ ├── statuscmd.go │ │ ├── statuscmd_test.go │ │ ├── symlinks_test.go │ │ ├── targetpathcmd.go │ │ ├── templatefuncs.go │ │ ├── templatefuncs_test.go │ │ ├── testdata/ │ │ │ └── scripts/ │ │ │ ├── add.txtar │ │ │ ├── addattributes.txtar │ │ │ ├── addautotemplate.txtar │ │ │ ├── addencrypted.txtar │ │ │ ├── addnew.txtar │ │ │ ├── addsecrets.txtar │ │ │ ├── age.txtar │ │ │ ├── ageencryption.txtar │ │ │ ├── ageencryptionsymmetric.txtar │ │ │ ├── agekeygen.txtar │ │ │ ├── apply.txtar │ │ │ ├── applychmod_unix.txtar │ │ │ ├── applyexact.txtar │ │ │ ├── applyremove.txtar │ │ │ ├── applyskipencrypted.txtar │ │ │ ├── applysourcepath.txtar │ │ │ ├── applystate.txtar │ │ │ ├── applytype.txtar │ │ │ ├── applyverbose.txtar │ │ │ ├── archivetar.txtar │ │ │ ├── archivezip.txtar │ │ │ ├── autocommit.txtar │ │ │ ├── autopush.txtar │ │ │ ├── bitwarden.txtar │ │ │ ├── bitwardenunlock.txtar │ │ │ ├── builtinage.txtar │ │ │ ├── builtingit.txtar │ │ │ ├── cat.txtar │ │ │ ├── catconfig.txtar │ │ │ ├── cd_unix.txtar │ │ │ ├── cd_windows.txtar │ │ │ ├── chattr.txtar │ │ │ ├── chattrencrypted.txtar │ │ │ ├── completion.txtar │ │ │ ├── completion_unix.txtar │ │ │ ├── config.txtar │ │ │ ├── configstate.txtar │ │ │ ├── create.txtar │ │ │ ├── dashlane.txtar │ │ │ ├── data.txtar │ │ │ ├── debug.txtar │ │ │ ├── destroy.txtar │ │ │ ├── diff.txtar │ │ │ ├── diffcommand_unix.txtar │ │ │ ├── diffcommand_windows.txtar │ │ │ ├── discussion4732.txtar │ │ │ ├── doctor_unix.txtar │ │ │ ├── doctor_windows.txtar │ │ │ ├── doppler.txtar │ │ │ ├── dumpconfig.txtar │ │ │ ├── dumpjson.txtar │ │ │ ├── dumpyaml.txtar │ │ │ ├── edgecases.txtar │ │ │ ├── edgecasesumask.txtar │ │ │ ├── edit.txtar │ │ │ ├── editconfig.txtar │ │ │ ├── editconfigtemplate.txtar │ │ │ ├── editencrypted.txtar │ │ │ ├── edithardlink.txtar │ │ │ ├── ejson.txtar │ │ │ ├── encryptiontemplatefuncs.txtar │ │ │ ├── errors.txtar │ │ │ ├── exclude.txtar │ │ │ ├── exectemplatefunc.txtar │ │ │ ├── executetemplate.txtar │ │ │ ├── external.txtar │ │ │ ├── externalarchiveinclude.txtar │ │ │ ├── externalcompression.txtar │ │ │ ├── externaldiff.txtar │ │ │ ├── externaldir.txtar │ │ │ ├── externalencrypted.txtar │ │ │ ├── externalfileurl.txtar │ │ │ ├── externalfilter.txtar │ │ │ ├── externalgitrepo.txtar │ │ │ ├── externalguess.txtar │ │ │ ├── externalrar.txtar │ │ │ ├── externalzip.txtar │ │ │ ├── forget.txtar │ │ │ ├── generate.txtar │ │ │ ├── git.txtar │ │ │ ├── githubtemplatefuncs.txtar │ │ │ ├── gitleaks.txtar │ │ │ ├── gopass.txtar │ │ │ ├── gpg.txtar │ │ │ ├── gpgencryption.txtar │ │ │ ├── gpgencryptionsymmetric.txtar │ │ │ ├── help.txtar │ │ │ ├── hooks.txtar │ │ │ ├── hooks_windows.txtar │ │ │ ├── ignore.txtar │ │ │ ├── ignored.txtar │ │ │ ├── import.txtar │ │ │ ├── importtarzst.txtar │ │ │ ├── importxz.txtar │ │ │ ├── importzip.txtar │ │ │ ├── init.txtar │ │ │ ├── initconfig.txtar │ │ │ ├── initgitlfs.txtar │ │ │ ├── inittemplatefuncs.txtar │ │ │ ├── issue1161.txtar │ │ │ ├── issue1213.txtar │ │ │ ├── issue1237.txtar │ │ │ ├── issue1365.txtar │ │ │ ├── issue1666.txtar │ │ │ ├── issue1794.txtar │ │ │ ├── issue1832.txtar │ │ │ ├── issue1866.txtar │ │ │ ├── issue1869.txtar │ │ │ ├── issue2016.txtar │ │ │ ├── issue2092.txtar │ │ │ ├── issue2132.txtar │ │ │ ├── issue2137.txtar │ │ │ ├── issue2177.txtar │ │ │ ├── issue2283.txtar │ │ │ ├── issue2300.txtar │ │ │ ├── issue2302.txtar │ │ │ ├── issue2315.txtar │ │ │ ├── issue2354.txtar │ │ │ ├── issue2380.txtar │ │ │ ├── issue2427.txtar │ │ │ ├── issue2500.txtar │ │ │ ├── issue2510.txtar │ │ │ ├── issue2573.txtar │ │ │ ├── issue2577.txtar │ │ │ ├── issue2597.txtar │ │ │ ├── issue2599.txtar │ │ │ ├── issue2609.txtar │ │ │ ├── issue2628.txtar │ │ │ ├── issue2695.txtar │ │ │ ├── issue2752.txtar │ │ │ ├── issue2820.txtar │ │ │ ├── issue2858.txtar │ │ │ ├── issue2861.txtar │ │ │ ├── issue2865.txtar │ │ │ ├── issue2934.txtar │ │ │ ├── issue2937.txtar │ │ │ ├── issue2942.txtar │ │ │ ├── issue2954.txtar │ │ │ ├── issue2964.txtar │ │ │ ├── issue2977.txtar │ │ │ ├── issue2995.txtar │ │ │ ├── issue3005.txtar │ │ │ ├── issue3008.txtar │ │ │ ├── issue3051.txtar │ │ │ ├── issue3064.txtar │ │ │ ├── issue3113.txtar │ │ │ ├── issue3126.txtar │ │ │ ├── issue3127.txtar │ │ │ ├── issue3163.txtar │ │ │ ├── issue3206.txtar │ │ │ ├── issue3232.txtar │ │ │ ├── issue3240.txtar │ │ │ ├── issue3257.txtar │ │ │ ├── issue3268.txtar │ │ │ ├── issue3325.txtar │ │ │ ├── issue3344.txtar │ │ │ ├── issue3349.txtar │ │ │ ├── issue3371.txtar │ │ │ ├── issue3374.txtar │ │ │ ├── issue3414.txtar │ │ │ ├── issue3415.txtar │ │ │ ├── issue3418.txtar │ │ │ ├── issue3421.txtar │ │ │ ├── issue3510.txtar │ │ │ ├── issue3525.txtar │ │ │ ├── issue3582.txtar │ │ │ ├── issue3590.txtar │ │ │ ├── issue3602.txtar │ │ │ ├── issue3630.txtar │ │ │ ├── issue3652.txtar │ │ │ ├── issue3666.txtar │ │ │ ├── issue3693.txtar │ │ │ ├── issue3703.txtar │ │ │ ├── issue3744.txtar │ │ │ ├── issue3772.txtar │ │ │ ├── issue3887.txtar │ │ │ ├── issue3891.txtar │ │ │ ├── issue3987.txtar │ │ │ ├── issue4002.txtar │ │ │ ├── issue4012.txtar │ │ │ ├── issue4024.txtar │ │ │ ├── issue4027.txtar │ │ │ ├── issue4104.txtar │ │ │ ├── issue4181.txtar │ │ │ ├── issue4315.txtar │ │ │ ├── issue4454.txtar │ │ │ ├── issue4479.txtar │ │ │ ├── issue4496.txtar │ │ │ ├── issue4500.txtar │ │ │ ├── issue4634.txtar │ │ │ ├── issue4647.txtar │ │ │ ├── issue4656.txtar │ │ │ ├── issue4662.txtar │ │ │ ├── issue4703.txtar │ │ │ ├── issue4727.txtar │ │ │ ├── issue4743.txtar │ │ │ ├── issue4745.txtar │ │ │ ├── issue4787.txtar │ │ │ ├── issue4796.txtar │ │ │ ├── issue4816.txtar │ │ │ ├── issue4824.txtar │ │ │ ├── issue4827.txtar │ │ │ ├── issue4845.txtar │ │ │ ├── issue4927.txtar │ │ │ ├── issue796.txtar │ │ │ ├── keepassxc.txtar │ │ │ ├── keeper.txtar │ │ │ ├── keepgoing.txtar │ │ │ ├── lastpass.txtar │ │ │ ├── license.txtar │ │ │ ├── literal.txtar │ │ │ ├── mackupbrew_darwin.txtar │ │ │ ├── mackupmacports_darwin.txtar │ │ │ ├── mackuppip_darwin.txtar │ │ │ ├── mackuppipx_darwin.txtar │ │ │ ├── managed.txtar │ │ │ ├── managedtree.txtar │ │ │ ├── merge_unix.txtar │ │ │ ├── mergeall_unix.txtar │ │ │ ├── mergeencryptedage_unix.txtar │ │ │ ├── mergeencryptedgpg_unix.txtar │ │ │ ├── modesymlink.txtar │ │ │ ├── modify_unix.txtar │ │ │ ├── modify_windows.txtar │ │ │ ├── modifyencrypted.txtar │ │ │ ├── modifypython_windows.txtar │ │ │ ├── noencryption.txtar │ │ │ ├── nosourcedir.txtar │ │ │ ├── onepassword2.txtar │ │ │ ├── onepassword2connect.txtar │ │ │ ├── onepassword2service.txtar │ │ │ ├── options.txtar │ │ │ ├── pager.txtar │ │ │ ├── pass.txtar │ │ │ ├── passhole.txtar │ │ │ ├── plugin.txtar │ │ │ ├── protonpass.txtar │ │ │ ├── purge.txtar │ │ │ ├── rbw.txtar │ │ │ ├── re-add.txtar │ │ │ ├── readd-exact.txtar │ │ │ ├── readdreencrypt.txtar │ │ │ ├── remove.txtar │ │ │ ├── removedir.txtar │ │ │ ├── root.txtar │ │ │ ├── runscriptdir_unix.txtar │ │ │ ├── script.txtar │ │ │ ├── script_unix.txtar │ │ │ ├── script_windows.txtar │ │ │ ├── scriptenv.txtar │ │ │ ├── scriptinterpreters_unix_pwsh.txtar │ │ │ ├── scriptinterpreters_windows.txtar │ │ │ ├── scriptinterpreterstemplate.txtar │ │ │ ├── scriptonce_unix.txtar │ │ │ ├── scriptonce_windows.txtar │ │ │ ├── scriptonchange_unix.txtar │ │ │ ├── scriptorder_unix.txtar │ │ │ ├── scriptorder_windows.txtar │ │ │ ├── scriptperl.txtar │ │ │ ├── scriptpython.txtar │ │ │ ├── scriptruby.txtar │ │ │ ├── scriptsdir_unix.txtar │ │ │ ├── scriptsubdir_unix.txtar │ │ │ ├── scriptsubdir_windows.txtar │ │ │ ├── scripttempdir.txtar │ │ │ ├── secret.txtar │ │ │ ├── sourcedir.txtar │ │ │ ├── sourcepath.txtar │ │ │ ├── state.txtar │ │ │ ├── state_unix.txtar │ │ │ ├── state_windows.txtar │ │ │ ├── status.txtar │ │ │ ├── symlinks.txtar │ │ │ ├── symlinks_windows.txtar │ │ │ ├── targetpath.txtar │ │ │ ├── templatedata.txtar │ │ │ ├── templatedirectives.txtar │ │ │ ├── templatefuncs.txtar │ │ │ ├── templatevars.txtar │ │ │ ├── textconv.txtar │ │ │ ├── tilde.txtar │ │ │ ├── transparentencryption.txtar │ │ │ ├── umask_unix.txtar │ │ │ ├── unmanaged.txtar │ │ │ ├── unmanagedtree.txtar │ │ │ ├── update.txtar │ │ │ ├── upgrade.txtar │ │ │ ├── vault.txtar │ │ │ ├── verify.txtar │ │ │ ├── version.txtar │ │ │ └── workingtree.txtar │ │ ├── textconv.go │ │ ├── unmanagedcmd.go │ │ ├── unmanagedcmd_test.go │ │ ├── updatecmd.go │ │ ├── upgradecmd.go │ │ ├── upgradecmd_test.go │ │ ├── upgradecmd_unix.go │ │ ├── upgradecmd_windows.go │ │ ├── util.go │ │ ├── util_test.go │ │ ├── util_unix.go │ │ ├── util_windows.go │ │ ├── vaulttemplatefuncs.go │ │ ├── verifycmd.go │ │ └── verifycmd_test.go │ └── cmds/ │ ├── execute-template/ │ │ ├── main.go │ │ └── main_test.go │ ├── generate-commit/ │ │ └── main.go │ ├── generate-helps/ │ │ ├── helps.go.tmpl │ │ └── main.go │ ├── generate-install.sh/ │ │ ├── install.sh.tmpl │ │ ├── main.go │ │ └── main_test.go │ ├── generate-license/ │ │ ├── license.go.tmpl │ │ └── main.go │ ├── hexencode/ │ │ └── main.go │ ├── lint-commit-messages/ │ │ ├── main.go │ │ └── main_test.go │ ├── lint-txtar/ │ │ └── main.go │ └── lint-whitespace/ │ ├── main.go │ └── main_test.go ├── main.go ├── main_test.go └── pyproject.toml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .config/capslock-summary.json ================================================ { "chezmoi.io/chezmoi": [ "CAPABILITY_ARBITRARY_EXECUTION", "CAPABILITY_CGO", "CAPABILITY_EXEC", "CAPABILITY_FILES", "CAPABILITY_MODIFY_SYSTEM_STATE", "CAPABILITY_NETWORK", "CAPABILITY_OPERATING_SYSTEM", "CAPABILITY_READ_SYSTEM_STATE", "CAPABILITY_REFLECT", "CAPABILITY_RUNTIME", "CAPABILITY_SYSTEM_CALLS", "CAPABILITY_UNANALYZED", "CAPABILITY_UNSAFE_POINTER" ], "chezmoi.io/chezmoi/internal/chezmoi": [ "CAPABILITY_ARBITRARY_EXECUTION", "CAPABILITY_CGO", "CAPABILITY_EXEC", "CAPABILITY_MODIFY_SYSTEM_STATE", "CAPABILITY_NETWORK", "CAPABILITY_OPERATING_SYSTEM", "CAPABILITY_RUNTIME", "CAPABILITY_SYSTEM_CALLS" ], "chezmoi.io/chezmoi/internal/cmd": [ "CAPABILITY_ARBITRARY_EXECUTION", "CAPABILITY_CGO", "CAPABILITY_EXEC", "CAPABILITY_FILES", "CAPABILITY_MODIFY_SYSTEM_STATE", "CAPABILITY_NETWORK", "CAPABILITY_OPERATING_SYSTEM", "CAPABILITY_READ_SYSTEM_STATE", "CAPABILITY_REFLECT", "CAPABILITY_RUNTIME", "CAPABILITY_SYSTEM_CALLS", "CAPABILITY_UNANALYZED", "CAPABILITY_UNSAFE_POINTER" ], "filippo.io/age": [ "CAPABILITY_MODIFY_SYSTEM_STATE", "CAPABILITY_OPERATING_SYSTEM", "CAPABILITY_RUNTIME" ], "filippo.io/age/agessh": [ "CAPABILITY_MODIFY_SYSTEM_STATE" ], "filippo.io/age/internal/format": [ "CAPABILITY_RUNTIME" ], "filippo.io/age/plugin": [ "CAPABILITY_OPERATING_SYSTEM" ], "github.com/Masterminds/sprig/v3": [ "CAPABILITY_REFLECT" ], "github.com/bodgit/sevenzip": [ "CAPABILITY_CGO", "CAPABILITY_EXEC", "CAPABILITY_MODIFY_SYSTEM_STATE", "CAPABILITY_OPERATING_SYSTEM", "CAPABILITY_RUNTIME" ], "github.com/dsnet/compress/bzip2": [ "CAPABILITY_UNANALYZED" ], "github.com/dsnet/compress/internal/prefix": [ "CAPABILITY_UNANALYZED" ], "github.com/fatih/color": [ "CAPABILITY_FILES" ], "github.com/fsnotify/fsnotify": [ "CAPABILITY_READ_SYSTEM_STATE" ], "github.com/go-viper/mapstructure/v2": [ "CAPABILITY_REFLECT" ], "github.com/godbus/dbus/v5": [ "CAPABILITY_MODIFY_SYSTEM_STATE", "CAPABILITY_UNSAFE_POINTER" ], "github.com/gopasspw/gopass/internal/backend/crypto/age": [ "CAPABILITY_MODIFY_SYSTEM_STATE", "CAPABILITY_OPERATING_SYSTEM" ], "github.com/gopasspw/gopass/internal/store/root": [ "CAPABILITY_FILES" ], "github.com/gopasspw/gopass/pkg/gopass/api": [ "CAPABILITY_FILES" ], "github.com/klauspost/compress/internal/cpuinfo": [ "CAPABILITY_ARBITRARY_EXECUTION" ], "github.com/klauspost/compress/s2": [ "CAPABILITY_ARBITRARY_EXECUTION" ], "github.com/klauspost/compress/zstd": [ "CAPABILITY_ARBITRARY_EXECUTION" ], "github.com/mholt/archives": [ "CAPABILITY_CGO", "CAPABILITY_EXEC", "CAPABILITY_MODIFY_SYSTEM_STATE", "CAPABILITY_OPERATING_SYSTEM", "CAPABILITY_RUNTIME", "CAPABILITY_UNANALYZED" ], "github.com/twpayne/go-shell": [ "CAPABILITY_ARBITRARY_EXECUTION", "CAPABILITY_CGO", "CAPABILITY_RUNTIME", "CAPABILITY_UNSAFE_POINTER" ], "github.com/zalando/go-keyring": [ "CAPABILITY_MODIFY_SYSTEM_STATE", "CAPABILITY_UNSAFE_POINTER" ], "github.com/zalando/go-keyring/secret_service": [ "CAPABILITY_MODIFY_SYSTEM_STATE" ], "github.com/zricethezav/gitleaks/v8/detect": [ "CAPABILITY_CGO", "CAPABILITY_EXEC", "CAPABILITY_MODIFY_SYSTEM_STATE", "CAPABILITY_OPERATING_SYSTEM", "CAPABILITY_RUNTIME", "CAPABILITY_UNANALYZED" ], "github.com/zricethezav/gitleaks/v8/sources": [ "CAPABILITY_CGO", "CAPABILITY_EXEC", "CAPABILITY_MODIFY_SYSTEM_STATE", "CAPABILITY_OPERATING_SYSTEM", "CAPABILITY_RUNTIME", "CAPABILITY_UNANALYZED" ], "go.etcd.io/bbolt": [ "CAPABILITY_SYSTEM_CALLS" ], "go4.org/readerutil": [ "CAPABILITY_CGO", "CAPABILITY_EXEC", "CAPABILITY_MODIFY_SYSTEM_STATE", "CAPABILITY_OPERATING_SYSTEM", "CAPABILITY_RUNTIME" ], "golang.org/x/sys/unix": [ "CAPABILITY_SYSTEM_CALLS" ] } ================================================ FILE: .config/editorconfig-checker.json ================================================ { "Exclude": [ "^completions/" ] } ================================================ FILE: .config/golangci.yml ================================================ version: '2' run: go: '1.25' linters: enable: - asciicheck - bidichk - bodyclose - canonicalheader - containedctx - copyloopvar - decorder - dogsled - dupword - durationcheck - embeddedstructfieldcheck - err113 - errchkjson - errname - errorlint - exptostd - fatcontext - forbidigo - forcetypeassert - funcorder - gocheckcompilerdirectives - gochecksumtype - gocritic - godoclint - godot - gomodguard - goprintffuncname - gosmopolitan - grouper - iface - importas - inamedparam - interfacebloat - intrange - iotamixing - loggercheck - makezero - mirror - misspell - modernize - nilerr - nilnesserr - nosprintfhostport - perfsprint - predeclared - protogetter - reassign - revive - rowserrcheck - sloglint - spancheck - sqlclosecheck - staticcheck - tagalign - tagliatelle - testableexamples - thelper - unconvert - unparam - unqueryvet - usestdlibvars - usetesting - wastedassign - whitespace disable: - arangolint - asasalint - contextcheck - cyclop - depguard - dupl - exhaustive - exhaustruct - funlen - ginkgolinter - gochecknoglobals - gochecknoinits - gocognit - goconst - gocyclo - godox - goheader - gomoddirectives - gosec - ireturn - lll - maintidx - musttag - nakedret - nestif - nilnil - nlreturn - noctx - nolintlint - nonamedreturns - paralleltest - prealloc - promlinter - recvcheck - testifylint - testpackage - tparallel - varnamelen - wrapcheck - wsl_v5 - zerologlint settings: forbidigo: forbid: - pattern: ^archive/zip\. - pattern: ^compress/gzip\. - pattern: ^fmt\.Print.*$ - pattern: ^ioutil\..*$ - pattern: ^os\.(DirEntry|ErrExist|ErrNotExist|FileInfo|FileMode|Is.*|Mode.*)$ gocritic: enable-all: true disabled-checks: - emptyFallthrough - hugeParam - rangeValCopy - unnamedResult - whyNoLint godoclint: default: all disable: - require-doc govet: disable: - fieldalignment - shadow enable-all: true misspell: locale: US revive: enable-all-rules: true rules: - name: add-constant disabled: true - name: cognitive-complexity disabled: true - name: cyclomatic disabled: true - name: deep-exit disabled: true - name: empty-block disabled: true - name: enforce-switch-style disabled: true - name: exported disabled: true - name: filename-format arguments: - ^[a-z][-0-9_a-z]*(?:\.gen)?\.go$ - name: flag-parameter disabled: true - name: function-length disabled: true - name: function-result-limit disabled: true - name: import-shadowing disabled: true - name: identical-switch-branches disabled: true - name: line-length-limit disabled: true - name: max-control-nesting disabled: true - name: max-public-structs disabled: true - name: nested-structs disabled: true - name: unhandled-error disabled: true - name: unused-parameter disabled: true - name: unused-receiver disabled: true - name: useless-fallthrough disabled: true staticcheck: checks: - all sloglint: attr-only: true exclusions: generated: lax presets: - common-false-positives - legacy - std-error-handling rules: - linters: - godoclint path: internal/chezmoi/chezmoi_windows.go # https://github.com/godoc-lint/godoc-lint/issues/56 - linters: - err113 text: do not define dynamic errors, use wrapped static errors instead - linters: - forbidigo path: internal/cmds/ - linters: - forcetypeassert path: _test\.go$ - linters: - forbidigo path: assets/scripts/generate-commit.go formatters: enable: - gci - gofmt - gofumpt - goimports - golines settings: gci: sections: - standard - default - prefix(chezmoi.io) gofumpt: module-path: chezmoi.io extra-rules: true goimports: local-prefixes: - chezmoi.io golines: max-len: 128 tab-len: 4 ================================================ FILE: .config/goreleaser.yaml ================================================ # yaml-language-server: $schema=https://goreleaser.com/static/schema.json version: 2 project_name: chezmoi before: hooks: - go tool generate-commit -o COMMIT - go mod download all - make ensure-syft builds: - id: chezmoi-cgo-glibc env: - CGO_ENABLED=1 goos: - linux goarch: - amd64 ldflags: - -s - -w - -X main.version={{ .Version }} - -X main.commit={{ .Commit }} - -X main.date={{ .Date }} - -X main.builtBy=goreleaser - id: chezmoi-cgo-musl env: - CC=/usr/bin/musl-gcc - CGO_ENABLED=1 goos: - linux goarch: - amd64 ldflags: - -s - -w - -X main.version={{ .Version }} - -X main.commit={{ .Commit }} - -X main.date={{ .Date }} - -X main.builtBy=goreleaser - -linkmode external - --extldflags "-static" - id: chezmoi-nocgo env: - CGO_ENABLED=0 goos: - android - darwin - freebsd - linux - openbsd - windows goarch: - '386' - amd64 - arm - arm64 - loong64 - mips64 - mips64le - ppc64 - ppc64le - riscv64 - s390x goarm: - '' ldflags: - -s - -w - -X main.version={{ .Version }} - -X main.commit={{ .Commit }} - -X main.date={{ .Date }} - -X main.builtBy=goreleaser ignore: - goos: android goarch: '386' - goos: android goarch: amd64 - goos: android goarch: arm - goos: darwin goarch: '386' - goos: linux goarch: amd64 - goos: openbsd goarch: riscv64 - goos: windows goarch: arm archives: - ids: - chezmoi-cgo-glibc # Required for chezmoi upgrade for versions <= 2.0.5 - chezmoi-nocgo files: - LICENSE - README.md - completions/* name_template: >- {{- .ProjectName }}_ {{- .Version }}_ {{- .Os }}_ {{- if eq .Arch "386" }}i386 {{- else if eq .Arch "mips64" }}mips64_hardfloat {{- else if eq .Arch "mips64le" }}mips64le_hardfloat {{- else }}{{ .Arch }}{{ end -}} format_overrides: - goos: windows formats: - zip - id: glibc ids: - chezmoi-cgo-glibc files: - LICENSE - README.md - completions/* name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}-glibc_{{ .Arch }}' - id: musl ids: - chezmoi-cgo-musl files: - LICENSE - README.md - completions/* name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}-musl_{{ .Arch }}' changelog: groups: - title: Features regexp: ^.*?feat(\([[:word:]]+\))??!?:.+$ order: 0 - title: Fixes regexp: ^.*?fix(\([[:word:]]+\))??!?:.+$ order: 1 - title: Documentation regexp: ^.*?docs?(\([[:word:]]+\))??!?:.+$ order: 2 - title: Other order: 999 filters: exclude: - ^.*?chore(\(.*\))??!?:.+$ checksum: extra_files: - glob: ./dist/chezmoi-nocgo_darwin_amd64_v1/chezmoi name_template: chezmoi-darwin-amd64 - glob: ./dist/chezmoi-nocgo_darwin_arm64_v8.0/chezmoi name_template: chezmoi-darwin-arm64 - glob: ./dist/chezmoi-cgo-glibc_linux_amd64_v1/chezmoi name_template: chezmoi-linux-amd64 - glob: ./dist/chezmoi-cgo-musl_linux_amd64_v1/chezmoi name_template: chezmoi-linux-amd64-musl - glob: ./dist/chezmoi-nocgo_windows_amd64_v1/chezmoi.exe name_template: chezmoi-windows-amd64.exe nfpms: - ids: - chezmoi-cgo-glibc - chezmoi-nocgo vendor: Tom Payne homepage: https://chezmoi.io/ maintainer: Tom Payne description: Manage your dotfiles across multiple diverse machines, securely. license: MIT formats: - archlinux - deb - rpm bindir: /usr/bin overrides: deb: file_name_template: >- {{- .ProjectName }}_ {{- .Version }}_ {{- .Os }}_ {{- if eq .Arch "386" }}i386 {{- else if eq .Arch "arm" }}armel {{- else }}{{ .Arch }}{{ end -}} contents: - src: completions/chezmoi-completion.bash dst: /usr/share/bash-completion/completions/chezmoi - src: completions/chezmoi.fish dst: /usr/share/fish/vendor_completions.d/chezmoi.fish - src: completions/chezmoi.zsh dst: /usr/share/zsh/vendor-completions/_chezmoi rpm: file_name_template: >- {{- .ProjectName }}- {{- .Version }}- {{- if eq .Arch "amd64" }}x86_64 {{- else if eq .Arch "386" }}i686 {{- else if eq .Arch "arm" }}armhfp {{- else if eq .Arch "arm64" }}aarch64 {{- else }}{{ .Arch }}{{ end -}} contents: - src: completions/chezmoi-completion.bash dst: /usr/share/bash-completion/completions/chezmoi - src: completions/chezmoi.fish dst: /usr/share/fish/vendor_completions.d/chezmoi.fish - src: completions/chezmoi.zsh dst: /usr/share/zsh/site-functions/_chezmoi - id: apks ids: - chezmoi-cgo-musl - chezmoi-nocgo vendor: Tom Payne homepage: https://chezmoi.io/ maintainer: Tom Payne description: Manage your dotfiles across multiple diverse machines, securely. license: MIT formats: - apk bindir: /usr/bin chocolateys: - owners: twpayne authors: Tom Payne project_url: https://chezmoi.io url_template: https://github.com/twpayne/chezmoi/releases/download/v{{ .Version }}/{{ .ArtifactName }} icon_url: https://github.com/twpayne/chezmoi/raw/master/assets/images/logo-144px.png copyright: Copyright (c) 2018-{{ .Now.Format "2006" }} Tom Payne license_url: https://github.com/twpayne/chezmoi/blob/master/LICENSE project_source_url: https://github.com/twpayne/chezmoi docs_url: https://chezmoi.io bug_tracker_url: https://github.com/twpayne/chezmoi/issues tags: configuration dotfile dotfiles summary: Manage your dotfiles across multiple diverse machines, securely. description: | ## What does chezmoi do? chezmoi helps you manage your personal configuration files (dotfiles, like `~/.gitconfig`) across multiple machines. chezmoi is helpful if you have spent time customizing the tools you use (e.g. shells, editors, and version control systems) and want to keep machines running different accounts (e.g. home and work) and/or different operating systems (e.g. Linux, macOS, and Windows) in sync, while still being able to easily cope with differences from machine to machine. chezmoi scales from the trivial (e.g. copying a few dotfiles onto a Raspberry Pi, development container, or virtual machine) to complex long-lived multi-machine development environments (e.g. keeping any number of home and work, Linux, macOS, and Windows machines in sync). In all cases you only need to maintain a single source of truth (a single branch in git) and getting started only requires adding a single binary to your machine (which you can do with `curl`, `wget`, or `scp`). chezmoi has strong support for security, allowing you to manage secrets (e.g. passwords, access tokens, and private keys) securely and seamlessly using a password manager and/or encrypt whole files with your favorite encryption tool. release_notes: '{{ .Changelog }}' api_key: '{{ .Env.CHOCOLATEY_API_KEY }}' release: extra_files: - glob: ./assets/cosign/cosign.pub name_template: chezmoi_cosign.pub - glob: ./dist/chezmoi-nocgo_darwin_amd64_v1/chezmoi name_template: chezmoi-darwin-amd64 - glob: ./dist/chezmoi-nocgo_darwin_arm64_v8.0/chezmoi name_template: chezmoi-darwin-arm64 - glob: ./dist/chezmoi-cgo-glibc_linux_amd64_v1/chezmoi name_template: chezmoi-linux-amd64 - glob: ./dist/chezmoi-cgo-musl_linux_amd64_v1/chezmoi name_template: chezmoi-linux-amd64-musl - glob: ./dist/chezmoi-nocgo_windows_amd64_v1/chezmoi.exe name_template: chezmoi-windows-amd64.exe sboms: - cmd: ../bin/syft args: - $artifact - --output - spdx-json=$document - --enrich - all disable: false scoops: - repository: owner: twpayne name: scoop-bucket token: '{{ .Env.SCOOP_GITHUB_TOKEN }}' commit_author: name: Tom Payne email: twpayne@gmail.com homepage: https://chezmoi.io description: Manage your dotfiles across multiple diverse machines, securely. license: MIT signs: - cmd: cosign stdin: '{{ .Env.COSIGN_PWD }}' args: - sign-blob - --key=assets/cosign/cosign.key - --output-signature=${signature} - --yes - ${artifact} artifacts: checksum snapcrafts: - ids: - chezmoi-cgo-glibc - chezmoi-nocgo summary: Manage your dotfiles across multiple diverse machines, securely. description: Manage your dotfiles across multiple diverse machines, securely. publish: true grade: stable confinement: classic license: MIT base: bare apps: chezmoi: command: chezmoi completer: completions/chezmoi-completion.bash source: enabled: true prefix_template: '{{ .ProjectName }}-{{ .Version }}/' files: - COMMIT winget: - name: chezmoi publisher: twpayne publisher_url: https://github.com/twpayne short_description: Manage your dotfiles across multiple diverse machines, securely. license: MIT commit_author: name: Tom Payne email: twpayne@gmail.com homepage: https://chezmoi.io license_url: https://github.com/twpayne/chezmoi/blob/master/LICENSE copyright: Copyright (c) 2018-{{ .Now.Format "2006" }} Tom Payne release_notes: '{{ .Changelog }}' release_notes_url: https://github.com/twpayne/chezmoi/releases/tag/{{ .Tag }} tags: - cli - configuration - dotbot - dotfile - dotfiles - stow - yadm author: Tom Payne publisher_support_url: https://github.com/twpayne/chezmoi/issues repository: owner: twpayne name: winget-pkgs branch: chezmoi-{{ .Version }} token: '{{ .Env.WINGET_GITHUB_TOKEN }}' pull_request: enabled: true base: owner: microsoft name: winget-pkgs branch: master ================================================ FILE: .config/markdownlint-cli2.yaml ================================================ globs: - '**/*.md' ignores: - .github/ISSUE_TEMPLATE/*.md - .venv/ - assets/chezmoi.io/docs/reference/release-history.md config: line-length: false # FIXME enable no-trailing-punctuation: false # FIXME enable fenced-code-language: false # FIXME enable code-block-style: false # FIXME enable link-image-reference-definitions: false # FIXME enable ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [*.ps1] charset = utf-8 end_of_line = crlf insert_final_newline = true trim_trailing_whitespace = true [*.py] indent_size = 4 indent_style = space [*.sh] indent_size = 1 indent_style = tab [{*.yaml,*.yml}] indent_size = 2 indent_style = space ================================================ FILE: .gitattributes ================================================ * -text # Make GitHub language breakdown more accurate, see https://github.com/github/linguist *.gen.go linguist-generated assets/scripts/install.sh linguist-generated assets/scripts/install-local-bin.sh linguist-generated completions/* linguist-generated ================================================ FILE: .github/CODE_OF_CONDUCT.md ================================================ # Code of Conduct chezmoi follows the [Contributor Covenant Code Of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) with the following additions for LLM (Large Language Model)-generated contributions: * Any contribution of any LLM-generated content will be rejected and result in an immediate ban for the contributor, without recourse. ================================================ FILE: .github/CONTRIBUTING.md ================================================ # Contributing > [!WARNING] > > If you use an LLM (Large Language Model, like ChatGPT, Claude, Gemini, GitHub > Copilot, or Llama) to make any kind of contribution then you will immediately > be banned without recourse. See [`CODE_OF_CONDUCT.md`][coc] for more > information. See the [contributing guide][guide] for full details. [guide]: https://chezmoi.io/developer-guide/contributing-changes/ [coc]: https://github.com/twpayne/chezmoi/blob/master/.github/CODE_OF_CONDUCT.md ================================================ FILE: .github/ISSUE_TEMPLATE/01_support_request.md ================================================ --- name: Support request about: Get help with using chezmoi title: '' labels: support assignees: '' --- ## What exactly are you trying to do? Describe in as much detail as possible. ## What have you tried so far? Describe what you have tried so far. ## Where else have you checked for solutions? * [ ] I have read [chezmoi's user guide](https://chezmoi.io/user-guide/command-overview/), and not found the answer. * [ ] I have searched [chezmoi's reference guide](https://chezmoi.io/reference/), and not found the answer. * [ ] Other, please give details. ## Output of any commands you've tried with `--verbose` flag ```console $ chezmoi --verbose $COMMAND ``` ## Output of `chezmoi doctor`
```console $ chezmoi doctor ```
## Additional context Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/02_feature_request.md ================================================ --- name: Feature request about: Request a new feature title: '' labels: enhancement assignees: '' --- ## Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is, for example "I'm always frustrated when...". ## Describe the solution you'd like A clear and concise description of what you want to happen. ## Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered. ## Additional context Add any other context or screenshots about the feature request here. ================================================ FILE: .github/ISSUE_TEMPLATE/03_bug_report.md ================================================ --- name: Bug report about: Report a bug title: '' assignees: '' --- ## Describe the bug A clear and concise description of what the bug is. ## To reproduce Steps to reproduce the behavior. ## Expected behavior A clear and concise description of what you expected to happen. ## Output of command with the `--verbose` flag ```console $ chezmoi --verbose $COMMAND ``` ## Output of `chezmoi doctor`
```console $ chezmoi doctor ```
## Additional context Add any other context about the problem here. ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ================================================ FILE: .github/SECURITY.md ================================================ # Security See the [security guide](https://chezmoi.io/developer-guide/security/). ================================================ FILE: .github/actions/free-disk-space/action.yml ================================================ name: free-disk-space description: Frees disk space by deleting unused files runs: using: composite steps: - name: remove-android shell: bash run: sudo rm -rf /usr/local/lib/android - name: remove-dotnet shell: bash run: sudo rm -rf /usr/share/dotnet ================================================ FILE: .github/actions/setup-go/action.yml ================================================ name: setup-go description: Set up Go environment and enable caching inputs: cache-prefix: description: Cache prefix default: cache-go required: false go-version: description: Go version to set up required: true upload-cache: description: Upload cache or only restore default: 'true' required: false runs: using: composite steps: - name: setup-go-env shell: bash run: | if [ "$RUNNER_OS" = "Windows" ]; then echo "GOCACHE=D:\\golang\\cache" >> $GITHUB_ENV echo "GOMODCACHE=D:\\golang\\modcache" >> $GITHUB_ENV echo "GOPATH=D:\\golang\\go" >> $GITHUB_ENV echo "USERPROFILE=D:\\homedir" >> $GITHUB_ENV elif [ "$RUNNER_OS" = "macOS" ]; then echo "GOCACHE=/Users/runner/go/pkg/mod" >> $GITHUB_ENV echo "GOMODCACHE=/Users/runner/Library/Caches/go-build" >> $GITHUB_ENV else echo "GOCACHE=/home/runner/go/pkg/mod" >> $GITHUB_ENV echo "GOMODCACHE=/home/runner/.cache/go-build" >> $GITHUB_ENV fi - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 if: ${{ inputs.upload-cache == 'true' }} with: path: | ${{ env.GOCACHE }} ${{ env.GOMODCACHE }} key: ${{ inputs.cache-prefix }}-${{ runner.os }}-${{ runner.arch }}-go-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ inputs.cache-prefix }}-${{ runner.os }}-${{ runner.arch }}-go-${{ inputs.go-version }}- - uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 if: ${{ inputs.upload-cache != 'true' }} with: path: | ${{ env.GOCACHE }} ${{ env.GOMODCACHE }} key: ${{ inputs.cache-prefix }}-${{ runner.os }}-${{ runner.arch }}-go-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ inputs.cache-prefix }}-${{ runner.os }}-${{ runner.arch }}-go-${{ inputs.go-version }}- - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: cache: false go-version: ${{ inputs.go-version }} ================================================ FILE: .github/workflows/clear-pr-caches.yml ================================================ name: clear-pr-caches on: pull_request_target: types: - closed permissions: actions: write jobs: clear-caches: runs-on: ubuntu-24.04 steps: - name: clear-caches env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge run: | cacheKeysForPR=$(gh cache list --ref "$BRANCH" --limit 100 --json id --jq '.[].id') set +e for cacheKey in $cacheKeysForPR; do gh cache delete "$cacheKey" done ================================================ FILE: .github/workflows/govulncheck.yml ================================================ name: govulncheck on: pull_request: branches: - master push: branches: - master tags: - v* schedule: - cron: 2 2 * * * concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: govulncheck: runs-on: ubuntu-22.04 permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: go-version id: go-version run: | echo go-version="$(awk '/GO_VERSION:/ { print $2 }' .github/workflows/main.yml | tr -d \')" >> "${GITHUB_OUTPUT}" - uses: ./.github/actions/setup-go with: go-version: ${{ steps.go-version.outputs.go-version }} upload-cache: false - uses: golang/govulncheck-action@b625fbe08f3bccbe446d94fbf87fcc875a4f50ee # v1.0.4 with: cache: false go-version-input: ${{ steps.go-version.outputs.go-version }} ================================================ FILE: .github/workflows/installer.yml ================================================ name: installer on: pull_request: branches: - master push: branches: - master concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true env: SHA: ${{ github.event_name == 'push' && github.sha || github.event.pull_request.head.sha }} jobs: changes: runs-on: ubuntu-22.04 outputs: sh: ${{ steps.filter.outputs.sh }} ps1: ${{ steps.filter.outputs.ps1 }} permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - id: filter uses: dorny/paths-filter@9d7afb8d214ad99e78fbd4247752c4caed2b6e4c # v4.0.0 with: filters: | shared: &shared - '.github/workflows/installer.yml' sh: - *shared - 'assets/scripts/install*.sh' - 'internal/cmds/generate-install.sh/install.sh.tmpl' ps1: - *shared - 'assets/scripts/install.ps1' misspell: runs-on: ubuntu-22.04 permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: reviewdog/action-misspell@d6429416b12b09b4e2768307d53bef58d172e962 # v1.27.0 with: locale: US test-install-sh: if: ${{ needs.changes.outputs.sh == 'true' }} strategy: matrix: os: - macos-14 - ubuntu-22.04 - windows-2022 needs: changes runs-on: ${{ matrix.os }} env: BINARY: ${{ matrix.os == 'windows-2022' && 'bin/chezmoi.exe' || 'bin/chezmoi' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: test-${{ matrix.os }}-local shell: bash run: | rm -f ${{ env.BINARY }} sh assets/scripts/install.sh -d ${{ env.BINARY }} --version - name: test-${{ matrix.os }}-url shell: bash run: | rm -f ${{ env.BINARY }} sh -c "$(curl -fsLS https://raw.githubusercontent.com/twpayne/chezmoi/${{ env.SHA }}/assets/scripts/install.sh)" -- -d ${{ env.BINARY }} --version test-install-ps1: if: ${{ needs.changes.outputs.ps1 == 'true' }} strategy: matrix: os: - macos-14 - ubuntu-22.04 - windows-2022 needs: changes runs-on: ${{ matrix.os }} env: BINARY: ${{ matrix.os == 'windows-2022' && 'bin/chezmoi.exe' || 'bin/chezmoi' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: test-${{ matrix.os }}-local-pwsh shell: pwsh run: | if (Test-Path -Path ${{ env.BINARY }}) { Remove-Item -Force ${{ env.BINARY }} } assets/scripts/install.ps1 -d ${{ env.BINARY }} --version - name: test-${{ matrix.os }}-local-powershell if: matrix.os == 'windows-2022' shell: powershell run: | if (Test-Path -Path ${{ env.BINARY }}) { Remove-Item -Force ${{ env.BINARY }} } assets/scripts/install.ps1 -d ${{ env.BINARY }} --version - name: test-${{ matrix.os }}-url-pwsh shell: pwsh run: | if (Test-Path -Path ${{ env.BINARY }}) { Remove-Item -Force ${{ env.BINARY }} } iex "&{$(irm 'https://raw.githubusercontent.com/twpayne/chezmoi/${{ env.SHA }}/assets/scripts/install.ps1')} -d" ${{ env.BINARY }} --version - name: test-${{ matrix.os }}-url-powershell if: matrix.os == 'windows-2022' shell: powershell run: | if (Test-Path -Path ${{ env.BINARY }}) { Remove-Item -Force ${{ env.BINARY }} } iex "&{$(irm 'https://raw.githubusercontent.com/twpayne/chezmoi/${{ env.SHA }}/assets/scripts/install.ps1')} -d" ${{ env.BINARY }} --version ================================================ FILE: .github/workflows/lock-threads.yml ================================================ name: lock-threads on: schedule: - cron: 17 2 * * * workflow_dispatch: {} permissions: issues: write pull-requests: write concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: action: runs-on: ubuntu-22.04 steps: - uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0 with: process-only: issues, prs issue-lock-reason: resolved issue-inactive-days: 28 pr-lock-reason: resolved pr-inactive-days: 28 log-output: true ================================================ FILE: .github/workflows/main.yml ================================================ name: main on: pull_request: branches: - master push: branches: - master tags: - v* schedule: - cron: 32 2 * * * concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true env: AGE_VERSION: 1.3.1 # https://github.com/FiloSottile/age/releases CHOCOLATEY_VERSION: 2.6.0 # https://github.com/chocolatey/choco/releases COSIGN_VERSION: 2.6.2 # https://github.com/sigstore/cosign/releases FIXME upgrade to cosign v3 GO_VERSION: 1.26.1 # https://go.dev/doc/devel/release GOLANGCI_LINT_VERSION: 2.11.3 # https://github.com/golangci/golangci-lint/releases GORELEASER_VERSION: 2.14.3 # https://github.com/goreleaser/goreleaser/releases PYTHON_VERSION: '3.14' # https://www.python.org/downloads/ RAGE_VERSION: 0.11.1 # https://github.com/str4d/rage/releases SYFT_VERSION: 1.42.2 # https://github.com/anchore/syft/releases UV_VERSION: 0.10.10 # https://github.com/astral-sh/uv/releases jobs: changes: runs-on: ubuntu-22.04 outputs: code: ${{ steps.filter.outputs.code }} permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - id: filter uses: dorny/paths-filter@9d7afb8d214ad99e78fbd4247752c4caed2b6e4c # v4.0.0 with: filters: | code: - '**/*.go' - '.github/actions/setup-go/action.yml' - '.github/workflows/main.yml' - '.goreleaser.yaml' - 'Makefile' - 'assets/**/*.tmpl' - 'assets/docker/**' - 'assets/scripts/*.py' - 'assets/scripts/generate-commit.go' - 'assets/scripts/stow-to-chezmoi.sh' - 'assets/vagrant/**' - 'completions/**' - 'go.*' - 'internal/**/!(install.sh.tmpl)' codeql: needs: changes if: github.event_name == 'push' || needs.changes.outputs.code == 'true' runs-on: ubuntu-22.04 permissions: security-events: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 1 persist-credentials: false - uses: ./.github/actions/setup-go with: go-version: ${{ env.GO_VERSION }} upload-cache: false - uses: github/codeql-action/init@0c0c5dc2f136b98cb0537075ccfa21f94cd9a63e # codeql-bundle-v2.24.3 with: languages: go - uses: github/codeql-action/analyze@0c0c5dc2f136b98cb0537075ccfa21f94cd9a63e # codeql-bundle-v2.24.3 misspell: runs-on: ubuntu-22.04 permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: reviewdog/action-misspell@d6429416b12b09b4e2768307d53bef58d172e962 # v1.27.0 with: locale: US test-alpine: needs: changes if: github.event_name == 'push' || needs.changes.outputs.code == 'true' runs-on: ubuntu-22.04 permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: | ~/.go-alpine key: alpine-go-${{ hashFiles('**/go.sum') }} restore-keys: | alpine-go- lookup-only: true - name: test env: CHEZMOI_GITHUB_TOKEN: ${{ secrets.CHEZMOI_GITHUB_TOKEN }} run: | export DOCKER_GOCACHE="$HOME/.go-alpine" ./assets/docker/test.sh alpine test-archlinux: needs: changes if: github.event_name == 'push' || needs.changes.outputs.code == 'true' runs-on: ubuntu-22.04 permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: | ~/.go-archlinux key: archlinux-go-${{ hashFiles('**/go.sum') }} restore-keys: | archlinux-go- lookup-only: true - name: test env: CHEZMOI_GITHUB_TOKEN: ${{ secrets.CHEZMOI_GITHUB_TOKEN }} run: | export DOCKER_GOCACHE="$HOME/.go-archlinux" ./assets/docker/test.sh archlinux test-macos: name: test-macos needs: changes if: github.event_name == 'push' || needs.changes.outputs.code == 'true' runs-on: macos-15 permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: ./.github/actions/setup-go with: go-version: ${{ env.GO_VERSION }} - name: build run: | go build -v ./... - name: run run: | go tool chezmoi --version - name: install-age run: | brew install age age --version - name: install-rage run: | brew tap str4d.xyz/rage https://str4d.xyz/rage brew install rage rage --version - name: install-keepassxc run: | brew install keepassxc keepassxc-cli --version - name: test env: CHEZMOI_GITHUB_TOKEN: ${{ secrets.CHEZMOI_GITHUB_TOKEN }} run: | go test ./... -race test-release: needs: changes runs-on: ubuntu-22.04 # use older Ubuntu for older glibc, update minimum glibc version in install.sh.tmpl if this changes permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - uses: ./.github/actions/free-disk-space - uses: ./.github/actions/setup-go with: cache-prefix: release-go go-version: ${{ env.GO_VERSION }} - name: install-release-dependencies if: github.event_name == 'push' || needs.changes.outputs.code == 'true' run: | sudo apt-get --quiet update sudo apt-get --no-install-suggests --no-install-recommends --quiet --yes install musl-tools snapcraft mkdir -p /opt/chocolatey wget -q -O - "https://github.com/chocolatey/choco/releases/download/${CHOCOLATEY_VERSION}/chocolatey.v${CHOCOLATEY_VERSION}.tar.gz" | tar -xz -C "/opt/chocolatey" echo '#!/bin/bash' >> /usr/local/bin/choco echo 'mono /opt/chocolatey/choco.exe $@' >> /usr/local/bin/choco chmod +x /usr/local/bin/choco - name: create-syso run: | make create-syso - name: build-release if: github.event_name == 'push' || needs.changes.outputs.code == 'true' uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0 with: version: ${{ env.GORELEASER_VERSION }} args: release --skip=sign --snapshot --timeout=1h - name: upload-artifact-chezmoi-darwin-amd64 if: github.event_name == 'push' || needs.changes.outputs.code == 'true' uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: chezmoi-darwin-amd64 path: dist/chezmoi-nocgo_darwin_amd64_v1/chezmoi - name: upload-artifact-chezmoi-darwin-arm64 if: github.event_name == 'push' || needs.changes.outputs.code == 'true' uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: chezmoi-darwin-arm64 path: dist/chezmoi-nocgo_darwin_arm64_v8.0/chezmoi - name: upload-artifact-chezmoi-linux-amd64 if: github.event_name == 'push' || needs.changes.outputs.code == 'true' uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: chezmoi-linux-amd64 path: dist/chezmoi-cgo-glibc_linux_amd64_v1/chezmoi - name: upload-artifact-chezmoi-linux-musl-amd64 if: github.event_name == 'push' || needs.changes.outputs.code == 'true' uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: chezmoi-linux-amd64-musl path: dist/chezmoi-cgo-musl_linux_amd64_v1/chezmoi - name: upload-artifact-chezmoi-windows-amd64.exe if: github.event_name == 'push' || needs.changes.outputs.code == 'true' uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: chezmoi-windows-amd64 path: dist/chezmoi-nocgo_windows_amd64_v1/chezmoi.exe test-ubuntu: name: test-ubuntu-umask${{ matrix.umask }} strategy: fail-fast: false matrix: umask: - '022' - '002' needs: changes runs-on: ubuntu-22.04 permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 if: github.event_name == 'push' || needs.changes.outputs.code == 'true' || matrix.umask == '022' with: fetch-depth: 0 persist-credentials: false - uses: ./.github/actions/setup-go if: github.event_name == 'push' || needs.changes.outputs.code == 'true' || matrix.umask == '022' with: go-version: ${{ env.GO_VERSION }} - name: install-age if: github.event_name == 'push' || needs.changes.outputs.code == 'true' run: | cd "$(mktemp -d)" curl -fsSL "https://github.com/FiloSottile/age/releases/download/v${AGE_VERSION}/age-v${AGE_VERSION}-linux-amd64.tar.gz" | tar xzf - sudo install -m 755 age/age /usr/local/bin sudo install -m 755 age/age-keygen /usr/local/bin - name: install-rage if: github.event_name == 'push' || needs.changes.outputs.code == 'true' run: | cd "$(mktemp -d)" curl -fsSL "https://github.com/str4d/rage/releases/download/v${RAGE_VERSION}/rage-v${RAGE_VERSION}-x86_64-linux.tar.gz" | tar xzf - sudo install -m 755 rage/rage /usr/local/bin sudo install -m 755 rage/rage-keygen /usr/local/bin - name: build if: github.event_name == 'push' || needs.changes.outputs.code == 'true' || matrix.umask == '022' run: | go build -v ./... - name: run if: github.event_name == 'push' || needs.changes.outputs.code == 'true' || matrix.umask == '022' run: | go tool chezmoi --version - name: test-umask-${{ matrix.umask }} if: github.event_name == 'push' || needs.changes.outputs.code == 'true' env: CHEZMOI_GITHUB_TOKEN: ${{ secrets.CHEZMOI_GITHUB_TOKEN }} TEST_FLAGS: -ldflags="-X github.com/twpayne/chezmoi/internal/chezmoitest.umaskStr=0o${{ matrix.umask }}" -race -timeout=1h run: | go test ./... ${{ env.TEST_FLAGS }} test-website: runs-on: ubuntu-22.04 permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: ./.github/actions/setup-go with: cache-prefix: website-go go-version: ${{ env.GO_VERSION }} - uses: astral-sh/setup-uv@e06108dd0aef18192324c70427afc47652e63a82 # v7.5.0 with: enable-cache: false version: ${{ env.UV_VERSION }} - name: install-website-dependencies run: | uv python install ${{ env.PYTHON_VERSION }} uv sync --locked - name: check-hooks run: uv run -v task pycheck - name: build-website run: uv run -v task build-docs env: CHEZMOI_GITHUB_TOKEN: ${{ secrets.CHEZMOI_GITHUB_TOKEN }} test-windows: name: test-windows needs: changes if: github.event_name == 'push' || needs.changes.outputs.code == 'true' runs-on: windows-2022 permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: ./.github/actions/setup-go with: go-version: ${{ env.GO_VERSION }} - name: build run: | go build -v ./... - name: run run: | go tool chezmoi --version - name: test env: CHEZMOI_GITHUB_TOKEN: ${{ secrets.CHEZMOI_GITHUB_TOKEN }} run: | go test ./... -race check: runs-on: ubuntu-22.04 permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - uses: ./.github/actions/setup-go with: go-version: ${{ env.GO_VERSION }} upload-cache: false - name: generate run: | go generate git diff --exit-code - name: capslock run: | make capslock git diff --exit-code - name: actionlint run: | go tool actionlint - uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0 with: ignore_paths: completions - name: editorconfig-checker run: | go tool editorconfig-checker -config=.config/editorconfig-checker.json - name: lint-whitespace run: | go tool lint-whitespace - name: lint-txtar run: | find . -name '*.txtar' -print0 | xargs -0 go tool lint-txtar - name: find-typos run: | go tool find-typos -format=github-actions chezmoi . - name: lint-commit-messages if: github.event_name == 'push' run: | go tool lint-commit-messages HEAD~1..HEAD - name: lint-commit-messages if: github.event_name == 'pull_request' && github.event.pull_request.draft == false run: | go tool lint-commit-messages ${{ github.event.pull_request.head.sha }}~${{ github.event.pull_request.commits }}..${{ github.event.pull_request.head.sha }} - name: lint-markdown uses: DavidAnson/markdownlint-cli2-action@07035fd053f7be764496c0f8d8f9f41f98305101 # v22.0.0 lint: name: lint-${{ matrix.runs-on }} strategy: fail-fast: false matrix: runs-on: - macos-15 - ubuntu-22.04 - windows-2022 needs: changes if: github.event_name == 'push' || needs.changes.outputs.code == 'true' runs-on: ${{ matrix.runs-on }} permissions: contents: read steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - uses: ./.github/actions/setup-go with: go-version: ${{ env.GO_VERSION }} upload-cache: false - uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 with: version: v${{ env.GOLANGCI_LINT_VERSION }} args: --config=.config/golangci.yml --timeout=5m - name: format # FIXME add make format-yaml run: | make format git diff --exit-code release: # FIXME this should be merged into test-release above if: startsWith(github.ref, 'refs/tags/') needs: - check - lint - test-alpine - test-archlinux - test-macos - test-release - test-ubuntu - test-website - test-windows runs-on: ubuntu-22.04 # use older Ubuntu for older glibc, update minimum glibc version in install.sh.tmpl if this changes permissions: contents: write steps: - name: install-build-dependencies run: | sudo apt-get --quiet update sudo apt-get --no-install-suggests --no-install-recommends --quiet --yes install musl-tools snapcraft mkdir -p /opt/chocolatey wget -q -O - "https://github.com/chocolatey/choco/releases/download/${CHOCOLATEY_VERSION}/chocolatey.v${CHOCOLATEY_VERSION}.tar.gz" | tar -xz -C "/opt/chocolatey" echo '#!/bin/bash' >> /usr/local/bin/choco echo 'mono /opt/chocolatey/choco.exe $@' >> /usr/local/bin/choco chmod +x /usr/local/bin/choco - name: check-snapcraft-credentials run: snapcraft whoami env: SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false - uses: ./.github/actions/free-disk-space - uses: ./.github/actions/setup-go with: cache-prefix: release-go go-version: ${{ env.GO_VERSION }} - uses: sigstore/cosign-installer@ba7bc0a3fef59531c69a25acd34668d6d3fe6f22 # v4.1.0 with: cosign-release: v${{ env.COSIGN_VERSION }} - name: create-syso run: | make create-syso - uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0 with: version: ${{ env.GORELEASER_VERSION }} args: release --timeout=1h env: CHOCOLATEY_API_KEY: ${{ secrets.CHOCOLATEY_API_KEY }} COSIGN_PWD: ${{ secrets.COSIGN_PWD }} GITHUB_TOKEN: ${{ secrets.GORELEASER_GITHUB_TOKEN }} SCOOP_GITHUB_TOKEN: ${{ secrets.SCOOP_GITHUB_TOKEN }} SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} WINGET_GITHUB_TOKEN: ${{ secrets.WINGET_GITHUB_TOKEN }} deploy-website: needs: - release runs-on: ubuntu-22.04 permissions: contents: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: true - uses: ./.github/actions/setup-go with: cache-prefix: website-go go-version: ${{ env.GO_VERSION }} - uses: astral-sh/setup-uv@e06108dd0aef18192324c70427afc47652e63a82 # v7.5.0 with: enable-cache: false version: ${{ env.UV_VERSION }} - name: prepare-chezmoi.io run: | uv sync --locked uv python install ${{ env.PYTHON_VERSION }} uv run -v task build-docs env: CHEZMOI_GITHUB_TOKEN: ${{ secrets.CHEZMOI_GITHUB_TOKEN }} - name: push-chezmoi.io run: | uv run -v task deploy-docs - name: prepare-get.chezmoi.io run: | cp assets/scripts/install.sh assets/get.chezmoi.io/index.html cp assets/scripts/install-local-bin.sh assets/get.chezmoi.io/lb cp assets/scripts/install.ps1 assets/get.chezmoi.io/ps1 cp LICENSE assets/get.chezmoi.io/LICENSE - name: push-get.chezmoi.io uses: cpina/github-action-push-to-another-repository@55306faa4ed53b815ae49e564af8cfb359d32ae2 # v1.7.3 env: SSH_DEPLOY_KEY: ${{ secrets.GET_CHEZMOI_IO_SSH_DEPLOY_KEY }} with: source-directory: assets/get.chezmoi.io destination-github-username: chezmoi destination-repository-name: get.chezmoi.io target-branch: gh-pages commit-message: 'chore: Update from ORIGIN_COMMIT' user-email: twpayne@gmail.com ================================================ FILE: .gitignore ================================================ /.vagrant /COMMIT /chezmoi /chezmoi.exe /coverage.out /dist /resource_windows_*.syso /versioninfo.json ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2018 Tom Payne Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ GO?=go GOOS=$(shell ${GO} env GOOS) GOARCH=$(shell ${GO} env GOARCH) GOLANGCI_LINT_VERSION=$(shell awk '/GOLANGCI_LINT_VERSION:/ { print $$2 }' .github/workflows/main.yml) GORELEASER_VERSION=$(shell awk '/GORELEASER_VERSION:/ { print $$2 }' .github/workflows/main.yml) SYFT_VERSION=$(shell awk '/SYFT_VERSION:/ { print $$2 }' .github/workflows/main.yml) UPSTREAM=$(shell git remote -v | awk '/github.com[:\/]twpayne\/chezmoi(.git)? \(fetch\)/ {print $$1}') ifdef VERSION GO_LDFLAGS+=-X main.version=${VERSION} endif ifdef COMMIT GO_LDFLAGS+=-X main.commit=${COMMIT} endif ifdef DATE GO_LDFLAGS+=-X main.date=${DATE} endif ifdef BUILT_BY GO_LDFLAGS+=-X main.builtBy=${BUILT_BY} endif PREFIX?=/usr/local .PHONY: default default: build .PHONY: smoke-test smoke-test: run build-all test lint format .PHONY: build build: ifeq (${GO_LDFLAGS},) ${GO} build . || ( rm -f chezmoi ; false ) else ${GO} build -ldflags "${GO_LDFLAGS}" . || ( rm -f chezmoi ; false ) endif .PHONY: install install: build mkdir -p "${DESTDIR}${PREFIX}/bin" install -m 755 --target-directory "${DESTDIR}${PREFIX}/bin" chezmoi .PHONY: install-from-git-working-copy install-from-git-working-copy: ${GO} install -ldflags "-X main.version=$(shell git describe --abbrev=0 --tags) \ -X main.commit=$(shell git rev-parse HEAD) \ -X main.date=$(shell git show -s --format=%ct HEAD) \ -X main.builtBy=source" .PHONY: build-in-git-working-copy build-in-git-working-copy: ${GO} build -ldflags "-X main.version=$(shell git describe --abbrev=0 --tags) \ -X main.commit=$(shell git rev-parse HEAD) \ -X main.date=$(shell git show -s --format=%ct HEAD) \ -X main.builtBy=source" .PHONY: build-all build-all: build-darwin build-freebsd build-linux build-windows .PHONY: build-darwin build-darwin: GOOS=darwin GOARCH=amd64 ${GO} build -o /dev/null . GOOS=darwin GOARCH=arm64 ${GO} build -o /dev/null . .PHONY: build-freebsd build-freebsd: GOOS=freebsd GOARCH=amd64 ${GO} build -o /dev/null . .PHONY: build-linux build-linux: GOOS=linux GOARCH=amd64 ${GO} build -o /dev/null . GOOS=linux GOARCH=amd64 ${GO} build -tags=noupgrade -o /dev/null . .PHONY: build-windows build-windows: create-syso GOOS=windows GOARCH=amd64 ${GO} build -o /dev/null . .PHONY: run run: ${GO} tool chezmoi --version .PHONY: test-all test-all: test test-release rm-dist test-docker test-vagrant .PHONY: rm-dist rm-dist: rm -rf dist .PHONY: test test: ${GO} test -ldflags="-X chezmoi.io/chezmoi/internal/chezmoitest.umaskStr=0o022" ./... ${GO} test -ldflags="-X chezmoi.io/chezmoi/internal/chezmoitest.umaskStr=0o002" ./... .PHONY: test-docker test-docker: ( cd assets/docker && ./test.sh alpine archlinux fedora ) .PHONY: test-vagrant test-vagrant: ( cd assets/vagrant && ./test.sh freebsd14 ) .PHONY: coverage-html coverage-html: coverage ${GO} tool cover -html=coverage.out .PHONY: coverage coverage: ${GO} test -coverprofile=coverage.out -coverpkg=chezmoi.io/chezmoi/... ./... .PHONY: capslock capslock: ${GO} tool capslock -output=package > .config/capslock-summary.json .PHONY: lint lint: ensure-golangci-lint shellcheck ${GO} tool actionlint ${GO} tool editorconfig-checker -config=.config/editorconfig-checker.json ./bin/golangci-lint run --config=.config/golangci.yml ${GO} tool lint-whitespace find . -name \*.txtar | xargs ${GO} tool lint-txtar ${GO} tool find-typos chezmoi . ${GO} tool lint-commit-messages ${UPSTREAM}/master..HEAD .PHONY: lint-markdown lint-markdown: markdownlint-cli2 --config=.config/markdownlint-cli2.yaml --strict-config .PHONY: format format: ensure-golangci-lint ./bin/golangci-lint fmt find . -name \*.txtar | xargs ${GO} tool lint-txtar -w .PHONY: format-yaml format-yaml: find . -name \*.yaml -o -name \*.yml | xargs uv run task format-yaml .PHONY: create-syso create-syso: ${GO} tool execute-template -output ./versioninfo.json ./assets/templates/versioninfo.json.tmpl ${GO} tool goversioninfo -platform-specific .PHONY: ensure-tools ensure-tools: \ ensure-golangci-lint \ ensure-goreleaser \ ensure-syft .PHONY: ensure-golangci-lint ensure-golangci-lint: if [ ! -x bin/golangci-lint ] || ( ./bin/golangci-lint version | grep -Fqv "version ${GOLANGCI_LINT_VERSION}" ) ; then \ curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- v${GOLANGCI_LINT_VERSION} ; \ fi .PHONY: ensure-goreleaser ensure-goreleaser: if [ ! -x bin/goreleaser ] || ( ./bin/goreleaser --version | grep -Fqv "${GORELEASER_VERSION}" ) ; then \ GOBIN=$(shell pwd)/bin ${GO} install "github.com/goreleaser/goreleaser/v2@v${GORELEASER_VERSION}" ; \ fi .PHONY: ensure-syft ensure-syft: if [ ! -x bin/syft ] || ( ./bin/syft --version | grep -Fqv "${SYFT_VERSION}" ) ; then \ curl -fsLS https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}/syft_${SYFT_VERSION}_$(shell ${GO} env GOOS)_$(shell ${GO} env GOARCH).tar.gz | tar -C bin -xzf - syft ; \ fi .PHONY: generate generate: CHEZMOIDEV=ignoreflags=1,ignorehelp=1 ${GO} generate .PHONY: release release: ensure-goreleaser ./bin/goreleaser release \ --clean \ ${GORELEASER_FLAGS} .PHONY: shellcheck shellcheck: find . -type f -name \*.sh | xargs shellcheck .PHONY: test-release test-release: ensure-goreleaser ./bin/goreleaser release \ --clean \ --skip=chocolatey,sign \ --snapshot \ ${GORELEASER_FLAGS} # FIXME parse go.mod instead of hardcoding tools here .PHONY: update-go-tools update-go-tools: go get \ github.com/editorconfig-checker/editorconfig-checker/v3@latest \ github.com/google/capslock@latest \ github.com/josephspurrier/goversioninfo@latest \ github.com/rhysd/actionlint@latest \ github.com/twpayne/find-typos@latest \ github.com/twpayne/go-jsonstruct/v3@latest ================================================ FILE: README.md ================================================ # ![chezmoi logo](assets/images/logo-144px.svg) chezmoi [![GitHub Release](https://img.shields.io/github/release/twpayne/chezmoi.svg)](https://github.com/twpayne/chezmoi/releases) Manage your dotfiles across multiple diverse machines, securely. chezmoi's documentation is at [chezmoi.io](https://chezmoi.io/). If you're contributing to chezmoi, then please read the [developer guide](https://www.chezmoi.io/developer-guide/). ## Contributors ![Contributor avatars](https://contrib.rocks/image?repo=twpayne/chezmoi&max=1024) ## License MIT ================================================ FILE: assets/chezmoi.io/.gitignore ================================================ /docs/index.md /docs/install.md /docs/links/articles.md /docs/links/podcasts.md /docs/links/videos.md /docs/reference/configuration-file/variables.md /docs/reference/release-history.md /site ================================================ FILE: assets/chezmoi.io/CNAME ================================================ www.chezmoi.io ================================================ FILE: assets/chezmoi.io/docs/chezmoi/index.html ================================================ ================================================ FILE: assets/chezmoi.io/docs/comparison-table.md ================================================ # Comparison table | | [chezmoi][chezmoi] | [dotbot][dotbot] | [rcm][rcm] | [vcsh][vcsh] | [yadm][yadm] | [bare git][bare git] | | -------------------------------------- | ------------------ | ----------------- | ----------------- | ------------------------ | ---------------------------- | -------------------- | | Distribution | Single binary | Python package | Multiple files | Single script or package | Single script | - | | Install method | Many | git submodule | Many | Many | Many | Manual | | Non-root install on bare system | ✅ | ⁉️ | ✅ | ✅ | ✅ | ✅ | | Windows support | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | | Bootstrap requirements | None | Python, git | Bash | sh, git | git | git | | Source repos | Single | Single | Multiple | Multiple | Single | Single | | dotfiles are... | Files | Symlinks | Symlinks | Files | Files | Files | | Config file | Optional | Required | Optional | None | Optional | Optional | | Private files | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | | Show differences without applying | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | | Whole file encryption | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | | Password manager integration | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | | Machine-to-machine file differences | Templates | Alternative files | Alternative files | Branches | Alternative files, templates | ⁉️ | | Custom variables in templates | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | | Executable files | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | File creation with initial contents | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | | Externals | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | | Manage partial files | ✅ | ❌ | ❌ | ⁉️ | ✅ | ⁉️ | | File removal | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | | Directory creation | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | Run scripts | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | Run once scripts | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | | Machine-to-machine symlink differences | ✅ | ❌ | ❌ | ⁉️ | ✅ | ⁉️ | | Shell completion | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | | Archive import | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | | Archive export | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | | Implementation language | Go | Python | Bash | POSIX Shell | Bash | C | ✅ Supported, ⁉️ Possible with significant manual effort, ❌ Not supported For more comparisons, visit [dotfiles.github.io/utilities][utils]. [chezmoi]: https://chezmoi.io/ [dotbot]: https://github.com/anishathalye/dotbot [rcm]: https://github.com/thoughtbot/rcm [vcsh]: https://github.com/RichiH/vcsh [yadm]: https://yadm.io/ [bare git]: https://www.atlassian.com/git/tutorials/dotfiles "bare git" [utils]: https://dotfiles.github.io/utilities/ ================================================ FILE: assets/chezmoi.io/docs/developer-guide/architecture.md ================================================ # Architecture This document gives a high-level overview of chezmoi's source code for anyone interested in contributing to chezmoi. You can generate Go documentation for chezmoi's source code with `go doc`, for example: ```sh go doc -all -u github.com/twpayne/chezmoi/internal/chezmoi ``` You can also [browse chezmoi's generated documentation online][go-docs]. ## Directory structure The important directories in chezmoi are: | Directory | Contents | | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `assets/chezmoi.io/docs/` | The documentation single source of truth. Help text, examples, and the [chezmoi.io][website] website are generated from the files in this directory | | `internal/chezmoi/` | chezmoi's core functionality | | `internal/cmd/` | Code for the `chezmoi` command | | `internal/cmd/testdata/scripts/` | High-level tests of chezmoi's commands using [`testscript`][testscript] | ## Key concepts As described in the [reference manual][ref], chezmoi evaluates the source state to compute a target state for the destination directory (typically your home directory). It then compares the target state to the actual state of the destination directory and performs any changes necessary to update the destination directory to match the target state. These concepts are represented directly in chezmoi's code. chezmoi uses the generic term *entry* to describe something that it manages. Entries can be files, directories, symlinks, scripts, amongst other things. ## `internal/chezmoi/` directory All of chezmoi's interaction with the operating system is abstracted through the `System` interface. A `System` includes functionality to read and write files and directories and execute commands. chezmoi makes a distinction between idempotent commands that can be run multiple times without modifying the underlying system and arbitrary commands that may modify the underlying system. The real underlying system is implemented via a `RealSystem` struct. Other `System`s are composed on top of this to provide further functionality. For example, the `--debug` flag is implemented by wrapping the `RealSystem` with a `DebugSystem` that logs all calls to the underlying `RealSystem`. `--dry-run` is implemented by wrapping the `RealSystem` with a `DryRunSystem` that allows reads to pass through but silently discards all writes. The `SourceState` struct represents a source state, including reading a source state from the source directory, executing templates, applying the source state (i.e. updating a `System` to match the desired source state), and adding more entries to the source state. Entries in the source state are abstracted by the `SourceStateEntry` interface implemented by the `SourceStateFile` and `SourceStateDir` structs, as the source state only consists of regular files and directories. A `SourceStateFile` includes a `FileAttr` struct describing the attributes parsed from its file name. Similarly, a `SourceStateDir` includes a `DirAttr` struct describing the directory attributes parsed from a directory name. `SourceStateEntry`s can compute their target state entries, i.e. what the equivalent entry should be in the target state, abstracted by the `TargetStateEntry` interface. Actual target state entries include `TargetStateFile` structs, representing a file with contents and permissions, `TargetStateDir` structs, representing a directory, `TargetStateSymlink` for symlinks, `TargetStateRemove` for entries that should be removed, and `TargetStateScript` for scripts that should be run. The actual state of an entry in the target state is abstracted via the `ActualStateEntry` interface, with `ActualStateAbsent`, `ActualStateDir`, `ActualStateFile`, `ActualStateSymlink` structs implementing this interface. Finally, an `EntryState` struct represents a serialization of an `ActualEntryState` for storage in and retrieval from chezmoi's persistent state. It stores a SHA256 of the entry's contents, rather than the full contents, to avoid storing secrets in the persistent state. With these concepts, chezmoi's apply command is effectively: 1. Read the source state from the source directory. 2. For each entry in the source state (`SourceStateEntry`), compute its `TargetStateEntry` and read its actual state in the destination state (`ActualStateEntry`). 3. If the `ActualStateEntry` is not equivalent to the `TargetStateEntry` then apply the minimal set of changes to the `ActualStateEntry` so that they are equivalent. Furthermore, chezmoi stores the `EntryState` of each entry that it writes in its persistent state. chezmoi can then detect if a third party has updated a target since chezmoi last wrote it by comparing the actual state entry in the target state with the entry state in the persistent state. ## `internal/cmd/` directory `internal/cmd/*cmd.go` files contain the code for each individual command. `internal/cmd/*templatefuncs.go` files contain the template functions. Commands are defined as methods on the `Config` struct. The `Config` struct is large, containing all configuration values read from the config file, command line arguments, and computed and cached values. The `Config.persistentPreRunRootE` and `Config.persistentPostRunRootE` methods set up and tear down state for individual commands based on the command's `Annotations` field, which defines how the command interacts with the file system and persistent state. ## Path handling chezmoi uses separate types for absolute paths (`AbsPath`) and relative paths (`RelPath`) to avoid errors where paths are combined (e.g. joining two absolute paths is an error). The type `SourceRelPath` is a relative path within the source directory and handles file and directory attributes. Internally, chezmoi normalizes all paths to use forward slashes with an optional upper-cased Windows volume so they can be compared with string comparisons. Paths read from the user may include tilde (`~`) to represent the user's home directory, use forward or backward slashes, and are treated as external paths (`ExtPath`). These are normalized to absolute paths. chezmoi is case-sensitive internally and makes no attempt to handle case-insensitive or case-preserving file systems. ## Persistent state Persistent state is treated as a two-level key-value store with the pseudo-structure `map[Bucket]map[Key]Value`, where `Bucket`, `Key`, and `Value` are all `[]byte`s. The `PersistentState` interface defines interaction with them. Sometimes temporary persistent states are used. For example, in dry run mode (`--dry-run`) the actual persistent state is copied into a temporary persistent state in memory which remembers writes but does not persist them to disk. ## Encryption Encryption tools are abstracted by the `Encryption` interface that contains methods of encrypting and decrypting files and `[]byte`s. Implementations are the `AGEEncryption` and `GPGEncryption` structs. A `DebugEncryption` struct wraps an `Encryption` interface and logs the methods called. ## `run_once_` and `run_onchange_` scripts The execution of a `run_once_` script is recorded by storing the SHA256 of its contents in the `scriptState` bucket in the persistent state. On future invocations the script is only run if no matching contents SHA256 is found in the persistent state. The execution of a `run_onchange_` script is recorded by storing its target name in the `entryState` bucket along with its contents SHA256 sum. On future invocations the script is only run if its contents SHA256 sum has changed, and its contents SHA256 sum is then updated in the persistent state. ## Testing chezmoi has a mix of unit, integration, and end-to-end tests. Unit and integration tests use the [`github.com/alecthomas/assert/v2`][assert] framework. End-to-end tests use [`github.com/rogpeppe/go-internal/testscript`][testscript] with the test scripts themselves in `internal/cmd/testdata/scripts/$TEST_NAME.txtar`. You can run individual end-to-end tests with ```sh go test ./internal/cmd -run=TestScript/$TEST_NAME ``` Tests should, if at all possible, run unmodified on all operating systems tested in CI (Linux, macOS, Windows, and FreeBSD). Windows will sometimes need special handling due to its path separator and lack of POSIX-style file permissions. [go-docs]: https://pkg.go.dev/github.com/twpayne/chezmoi [website]: https://chezmoi.io [testscript]: https://pkg.go.dev/github.com/rogpeppe/go-internal/testscript [ref]: /reference/concepts.md [assert]: https://pkg.go.dev/github.com/alecthomas/assert ================================================ FILE: assets/chezmoi.io/docs/developer-guide/building-on-top-of-chezmoi.md ================================================ # Building on top of chezmoi chezmoi is designed with UNIX-style composability in mind, and the command line tool is semantically versioned. Building on top of chezmoi should primarily be done by executing the `chezmoi` binary with arguments and the standard input and output configured appropriately. The `chezmoi dump` and `chezmoi state` commands allows the inspection of chezmoi's internal state. ================================================ FILE: assets/chezmoi.io/docs/developer-guide/contributing-changes.md ================================================ # Contributing changes !!! warning chezmoi has a strict zero-tolerance policy on LLM contributions. If you use an LLM (Large Language Model, like ChatGPT, Claude, Gemini, GitHub Copilot, or Llama) to make any kind of contribution then you will immediately be banned without recourse. See [`CODE_OF_CONDUCT.md`][coc] for more information. Bug reports, bug fixes, and documentation improvements are always welcome. Please [open an issue][issue] or [create a pull request][pr] with your report, fix, or improvement. If you want to make a more significant change, please first [open an issue][issue] to discuss the change that you want to make. Dave Cheney gives a [good rationale][rationale] as to why this is important. All changes are made via pull requests. In your pull request, please make sure that: * All existing tests pass. You can ensure this by running `make test`. * There are appropriate additional tests that demonstrate that your PR works as intended. * The documentation is updated, if necessary. For new features you should add an entry in `assets/chezmoi.io/docs/user-guide/` and a complete description in `assets/chezmoi.io/docs/reference/`. See the [website developer guide][website] for instructions on how to build and view a local version of the documentation. By default, chezmoi will panic if a flag is undocumented or a long help is missing for a command. You can disable this panic during development by setting the environment variable `CHEZMOIDEV` to `ignoreflags=1,ignorehelp=1`. Once you have documented the command and its flags, run `make generate` to generate the embedded documentation. * All generated files are up to date. You can ensure this by running `make generate` and including any modified files in your commit. * The code is correctly formatted. You can ensure this by running `make format`. * The code passes [`golangci-lint`][golangci-lint]. You can ensure this by running `make lint`. * The commit messages adhere to the [conventional commits specification][commits], with the following additional requirements: * The first character of the commit message is uppercase, e.g: ```text chore: Fix typo in test ``` The purpose of this is to maintain consistency in chezmoi's release notes, which are generated directly from the commit messages. * The commits do not have scopes (e.g. `chore(scope): Message` is invalid). The following criteria can be used to determine the commit type: * `fix`: bug fixes in chezmoi code * `feat`: extending an existing feature or adding a new feature * `docs`: adding to or updating the documentation * `chore`: small changes, such as fixing a typo, correcting grammar (including in documentation), or anything not covered by the above Examples can be found in the [commit history][history]. * Commits are logically separate, with no merge or "fixup" commits. * The branch applies cleanly to `master`. [issue]: https://github.com/twpayne/chezmoi/issues/new/choose [pr]: https://help.github.com/en/articles/creating-a-pull-request [rationale]: https://dave.cheney.net/2019/02/18/talk-then-code [golangci-lint]: https://github.com/golangci/golangci-lint [commits]: https://www.conventionalcommits.org/en/v1.0.0/ [history]: https://github.com/twpayne/chezmoi/commits/master/ [website]: /developer-guide/website.md [coc]: https://github.com/twpayne/chezmoi/blob/master/.github/CODE_OF_CONDUCT.md ================================================ FILE: assets/chezmoi.io/docs/developer-guide/index.md ================================================ # Developer guide !!! warning If you use an LLM (Large Language Model, like ChatGPT, Claude, Gemini, GitHub Copilot, or Llama) to make any kind of contribution then you will immediately be banned without recourse. See [`CODE_OF_CONDUCT.md`][coc] for more information. chezmoi is written in [Go][go] and development happens on [GitHub][github]. chezmoi is a standard Go project, using standard Go tooling. chezmoi requires Go 1.25 or later. Checkout chezmoi: ```sh git clone https://github.com/twpayne/chezmoi.git cd chezmoi ``` Build chezmoi: ```sh go build ``` Run all tests: ```sh go test ./... ``` chezmoi's tests include integration tests with other software. If the other software is not found in `$PATH` the tests will be skipped. Running the full set of tests requires `age`, `base64`, `bash`, `bzip2`, `git`, `gpg`, `gzip`, `perl`, `python3`, `rage`, `ruby`, `sed`, `sha256sum`, `tr`, `true`, `unzip`, `xz`, `zip`, and `zstd`. Run chezmoi: ```sh go tool chezmoi ``` Run a set of smoke tests, including cross-compilation, tests, and linting: ```sh make smoke-test ``` Test building chezmoi for all architectures: ```sh make test-release ``` !!! hint If you use `fish` as your primary shell, you may get warnings from Fish during tests: ```text error: can not save history warning-path: Unable to locate data directory derived from $HOME: '/home/user/.local/share/fish'. warning-path: The error was 'Operation not supported'. warning-path: Please set $HOME to a directory where you have write access. ``` These can be avoided with by running tests with `SHELL=bash` or `SHELL=zsh`: ```sh SHELL=bash make test SHELL=zsh make smoke-test SHELL=bash go test ./... ``` [coc]: https://github.com/twpayne/chezmoi/blob/master/.github/CODE_OF_CONDUCT.md [go]: https://golang.org [github]: https://github.com ================================================ FILE: assets/chezmoi.io/docs/developer-guide/install-script.md ================================================ # Install script chezmoi generates the [install script][install] from a single source of truth. You must run ```sh make generate ``` if your change includes any of the following: * Modifications to the install script template. * Additions or modifications to the list of supported operating systems and architectures. chezmoi's continuous integration verifies that all generated files are up to date. Changes to generated files should be included in the commit that modifies the source of truth. [install]: https://github.com/twpayne/chezmoi/blob/master/assets/scripts/install.sh ================================================ FILE: assets/chezmoi.io/docs/developer-guide/packaging.md ================================================ # Packaging If you're packaging chezmoi for an operating system or distribution: chezmoi has no build dependencies other than the standard Go toolchain. chezmoi has no runtime dependencies, but is usually used with `git`, so many packagers choose to make `git` an install dependency or recommended package. Please set the version number, git commit, and build time in the binary. This greatly assists debugging when end users report problems or ask for help. You can do this by passing the following flags to `go build`: ```text -ldflags "-X main.version=$VERSION -X main.commit=$COMMIT -X main.date=$DATE -X main.builtBy=$BUILT_BY" ``` `$VERSION` should be the chezmoi version, e.g. `1.7.3`. Any `v` prefix is optional and will be stripped, so you can pass the git tag in directly. !!! hint The command `git describe --abbrev=0 --tags` will return a suitable value for `$VERSION`. `$COMMIT` should be the full git commit hash at which chezmoi is built, e.g. `4d678ce6850c9d81c7ab2fe0d8f20c1547688b91`. !!! hint The `internal/cmds/generate-commit/main.go` script will return a suitable value for `$COMMIT`. You can run it with `go tool generate-commit`. !!! hint The source archive contains a file called `COMMIT` containing the commit hash. `$DATE` should be the date of the build as a UNIX timestamp or in RFC3339 format. !!! hint The command `git show -s --format=%ct HEAD` returns the UNIX timestamp of the last commit, e.g. `1636668628`. The command `date -u +%Y-%m-%dT%H:%M:%SZ` returns the current time in RFC3339 format, e.g. `2019-11-23T18:29:25Z`. `$BUILT_BY` should be a string indicating what system was used to build the binary. Typically it should be the name of your packaging system, e.g. `homebrew`. Please enable cgo, if possible. chezmoi can be built and run without cgo, but the `.chezmoi.username` and `.chezmoi.group` template variables may not be set correctly on some systems. chezmoi includes an `upgrade` command which attempts to self-upgrade. You can remove this command completely by building chezmoi with the `noupgrade` build tag. chezmoi includes shell completions in the `completions` directory. Please include these in the package and install them in the shell-appropriate directory, if possible. If the instructions for installing chezmoi in chezmoi's [install guide][guide] are absent or incorrect, please open an issue or submit a PR to correct them. [guide]: /install.md ================================================ FILE: assets/chezmoi.io/docs/developer-guide/releases.md ================================================ # Releases Releases are managed with [`goreleaser`][goreleaser]. ## Testing To build a test release, without publishing, (Ubuntu Linux only) first ensure that the `musl-tools` and `snapcraft` packages are installed: ```sh sudo apt-get install musl-tools snapcraft ``` Then run: ```sh make test-release ``` ## Publishing Publish a new release by creating and pushing a tag, for example: ```sh git tag v1.2.3 git push --tags ``` This triggers a [GitHub Action][gha] that builds and publishes archives, packages, and snaps, creates a new [GitHub Release][release], and deploys the [website][website]. ### Snaps Publishing [Snaps][snaps] requires a `SNAPCRAFT_STORE_CREDENTIALS` [repository secret][secret]. Snapcraft store credentials periodically expire. This is visible in the release GitHub Action reporting: ``` Run snapcraft whoami Store operation failed: - macaroon-authorization-required: The request is missing an Authorization header field containing a valid macaroon ``` Create new snapcraft store credentials by running: ```sh snapcraft export-login --snaps=chezmoi --channels=stable,candidate,beta,edge --acls=package_upload - ``` This command requires a Ubuntu machine with snapcraft installed and a keyring, which is typically not available over SSH connections. Login to a GNOME session on a Ubuntu machine and run: ```sh sudo snap install --classic snapcraft snapcraft login ``` ### Homebrew [Homebrew][homebrew] automation will automatically detect new releases of chezmoi within a few hours and open a pull request in [github.com/Homebrew/homebrew-core][homebrew-core] to bump the version. If needed, the pull request can be created with: ```sh brew bump-formula-pr --tag=v1.2.3 chezmoi ``` ### Scoop chezmoi is in [Scoop][scoop]'s Main bucket. Scoop's automation will automatically detect new releases within a few hours. ## Signing chezmoi uses [GoReleaser's support for signing][signing] to sign the checksums of its release assets with [cosign][cosign]. Details: * The cosign private key was generated with cosign v1.12.1 on a private recently-installed Ubuntu 22.04.1 system with a single user and all available updates applied. * The private key uses a long (more than 32 character) password generated locally by a password manager. * The password-protected private key is stored in chezmoi's public GitHub repo. * The private key's password is stored as a [GitHub Actions secret][gha-secret] and only available to the `release` step of `release` job of the `main` workflow. * The cosign public key is included in the release assets and also uploaded to [`https://chezmoi.io/cosign.pub`][pubkey]. Since [`https://chezmoi.io`][website] is served by [GitHub pages][pages], it probably has equivalent security to [chezmoi's GitHub Releases page][release], which is also managed by GitHub. [goreleaser]: https://goreleaser.com/ [gha]: https://github.com/twpayne/chezmoi/actions [release]: https://github.com/twpayne/chezmoi/releases [website]: https://chezmoi.io [snaps]: https://snapcraft.io/ [secret]: https://github.com/twpayne/chezmoi/settings/secrets/actions [homebrew]: https://brew.sh/ [homebrew-core]: https://github.com/Homebrew/homebrew-core [scoop]: https://scoop.sh/ [signing]: https://goreleaser.com/customization/sign/ [cosign]: https://github.com/sigstore/cosign [gha-secret]: https://docs.github.com/en/actions/security-guides/encrypted-secrets [pubkey]: https://chezmoi.io/cosign.pub [pages]: https://pages.github.com/ ================================================ FILE: assets/chezmoi.io/docs/developer-guide/security.md ================================================ # Security ## Supported versions Only the most recent version of chezmoi is supported with security updates. ## Virus scanner false positives Virus scanning software, especially on Windows machines, occasionally report viruses or trojans in the chezmoi binary. This is almost certainly a false positive. For more information see [Why does my virus-scanning software think my Go distribution or compiled binary is infected?][false] in the Go FAQ. ## Reporting a vulnerability Please report vulnerabilities by [opening a GitHub issue][issue] or sending an email to [`twpayne+chezmoi-security@gmail.com`][email]. [false]: https://go.dev/doc/faq#virus [issue]: https://github.com/twpayne/chezmoi/issues/new/choose [email]: mailto:twpayne%2Bchezmoi-security@gmail.com ================================================ FILE: assets/chezmoi.io/docs/developer-guide/testing.md ================================================ # Testing chezmoi uses multiple levels of testing: 1. Unit testing, using [`testing`][testing], and [`github.com/alecthomas/assert/v2`][assert], tests that functions and small components behave as expected for a wide range of inputs, especially edge cases. These are generally found in `internal/chezmoi/*_test.go`. 2. File system integration tests, using `testing` and [`github.com/twpayne/go-vfs/v5`][vfs], test chezmoi's effects on the file system. This include some tests in `internal/chezmoi/*_test.go`, and higher level command tests in `internal/cmd/*cmd_test.go`. 3. High-level integration tests using [`github.com/rogpeppe/go-internal/testscript`][testscript] are in `internal/cmd/testdata/scripts/*.txtar` and are run by `internal/cmd/main_test.go`. 4. Linux distribution and OS tests run the full test suite using Docker for different Linux distributions (in `assets/docker`) and Vagrant for different OSes (in `assets/vagrant`). Windows tests are run in GitHub Actions. [testing]: https://pkg.go.dev/testing [assert]: https://pkg.go.dev/github.com/alecthomas/assert/v2 [vfs]: https://pkg.go.dev/github.com/twpayne/go-vfs/v5 [testscript]: https://pkg.go.dev/github.com/rogpeppe/go-internal/testscript ================================================ FILE: assets/chezmoi.io/docs/developer-guide/using-make.md ================================================ # Building and installing with `make` chezmoi can be built with GNU make, assuming you have the Go toolchain installed. Running `make` will build a `chezmoi` binary in the current directory for the host OS and architecture. To embed version information in the binary and control installation the following variables are available: | Variable | Example | Purpose | | ----------- | ---------------------- | ---------------------------------------------- | | `$VERSION` | `v2.0.0` | Set version | | `$COMMIT` | `3895680a`... | Set the git commit at which the code was built | | `$DATE` | `2019-11-23T18:29:25Z` | The time of the build | | `$BUILT_BY` | `homebrew` | The packaging system performing the build | | `$PREFIX` | `/usr` | Installation prefix | | `$DESTDIR` | `install-root` | Fake installation root | Running `make install` will install the `chezmoi` binary in `${DESTDIR}${PREFIX}/bin`. ================================================ FILE: assets/chezmoi.io/docs/developer-guide/website.md ================================================ # Website The [website][website] is generated with [Material for MkDocs][material] from the contents of the `assets/chezmoi.io/docs/` directory. It is hosted by [GitHub pages][pages] from the [`gh-pages` branch][branch]. To build the website locally, Go 1.25 (or later) and [uv][uv] 0.5.0 (or later) must be installed. Python 3.10 (or later) is required, but may be installed with `uv`: !!! note "" If Python 3.10 (or later) is not currently installed, install it with `uv`: ```sh uv python install 3.10 ``` Install the dependencies (the `--frozen` is optional but recommended): ```sh uv sync --frozen ``` Test the website locally by running: ```sh uv run task serve-docs ``` and visiting [http://127.0.0.1:8000/][serve]. ## Maintainers The website is automatically deployed when new releases are created, but manual deployments can be triggered by maintainers with appropriate access using: ```sh uv run task deploy-docs ``` [website]: https://chezmoi.io [material]: https://squidfunk.github.io/mkdocs-material/ [pages]: https://pages.github.com/ [branch]: https://github.com/twpayne/chezmoi/tree/gh-pages [uv]: https://docs.astral.sh/uv/getting-started/installation/ [serve]: http://127.0.0.1:8000/ ================================================ FILE: assets/chezmoi.io/docs/docs.go ================================================ // Package docs contains chezmoi's documentation. package docs import _ "embed" // License is the license. // //go:embed license.md var License string ================================================ FILE: assets/chezmoi.io/docs/extra/refresh_on_toggle_dark_light.js ================================================ // The light palette is always the first palette. In some cases, it is numbered // as __palette_0 and in others it is numbered as __palette_1. The dark palette // is numbered as __palette_1 or __palette_2, depending on the index used for // the light palette. var paletteSwitcherLight = document.getElementById("__palette_0"); var paletteSwitcherDark = document.getElementById("__palette_1"); if (!paletteSwitcherLight) { paletteSwitcherLight = paletteSwitcherDark; paletteSwitcherDark = document.getElementById("__palette_2"); } paletteSwitcherLight.addEventListener("change", () => location.reload()); paletteSwitcherDark.addEventListener("change", () => location.reload()); ================================================ FILE: assets/chezmoi.io/docs/hooks.py ================================================ from __future__ import annotations import subprocess from pathlib import Path, PurePosixPath from mkdocs import utils from mkdocs.config.defaults import MkDocsConfig from mkdocs.structure.files import Files non_website_paths = [ 'docs.go', 'hooks.py', 'reference/commands/commands.go', 'reference/commands/commands_test.go', ] templates = [ 'index.md', 'install.md', 'links/articles.md', 'links/podcasts.md', 'links/videos.md', 'reference/configuration-file/variables.md', 'reference/release-history.md', ] def on_pre_build(config: MkDocsConfig, **kwargs) -> None: docs_dir = PurePosixPath(config.docs_dir) for src_path in templates: output_path = docs_dir.joinpath(src_path) template_path = output_path.parent / (output_path.name + '.tmpl') data_path = output_path.parent / (output_path.name + '.yaml') args = ['go', 'tool', 'execute-template'] if Path(data_path).exists(): args.extend(['-data', data_path]) args.extend(['-output', output_path, template_path]) subprocess.run(args, check=False) def on_files(files: Files, config: MkDocsConfig, **kwargs) -> Files: # remove non-website files for src_path in non_website_paths: files.remove(files.get_file_from_path(src_path)) # remove templates and data for src_path in templates: files.remove(files.get_file_from_path(src_path + '.tmpl')) data_path = src_path + '.yaml' if data_path in files: files.remove(files.get_file_from_path(data_path)) return files def on_post_build(config: MkDocsConfig, **kwargs) -> None: config_dir = Path(config.config_file_path).parent site_dir = config.site_dir # copy GitHub pages config utils.copy_file(Path(config_dir, 'CNAME'), Path(site_dir, 'CNAME')) # copy installation scripts utils.copy_file(Path(config_dir, '../scripts/install.sh'), Path(site_dir, 'get')) utils.copy_file( Path(config_dir, '../scripts/install-local-bin.sh'), Path(site_dir, 'getlb'), ) utils.copy_file( Path(config_dir, '../scripts/install.ps1'), Path(site_dir, 'get.ps1'), ) # copy cosign.pub utils.copy_file( Path(config_dir, '../cosign/cosign.pub'), Path(site_dir, 'cosign.pub'), ) ================================================ FILE: assets/chezmoi.io/docs/index.md.tmpl ================================================ {{- $latestRelease := gitHubLatestRelease "twpayne/chezmoi" -}} {{- $version := $latestRelease.Name | trimPrefix "v" -}} # chezmoi Manage your dotfiles across multiple diverse machines, securely. The latest version of chezmoi is {{ $version }} ([release notes][notes], [release history][history]). ## What does chezmoi do? chezmoi helps you manage your personal configuration files (dotfiles, like `~/.gitconfig`) across multiple machines. chezmoi provides many features beyond symlinking or using a bare git repo including: * templates (to handle small differences between machines) * password manager support (to store your secrets securely) * importing files from archives (great for shell and editor plugins) * full file encryption (using age, gpg, git-crypt, or transcrypt) * running scripts (to handle everything else) With chezmoi, pronounced /ʃeɪ mwa/ (shay-mwa) (listen [1][listen1] [2][listen2] [3][listen3]), you can install chezmoi and your dotfiles from your GitHub dotfiles repo on a new, empty machine with a single command: ```sh sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply $GITHUB_USERNAME ``` As well as the `curl | sh` installation, you can [install chezmoi with your favorite package manager][install]. Updating your dotfiles on any machine is a single command: ```sh chezmoi update ``` chezmoi runs on all popular operating systems, is distributed as a single statically-linked binary with no dependencies, and does not require root access. ## How do I start with chezmoi? [Install chezmoi][install] then read the [quick start guide][quick-start]. The [user guide][guide] covers most common tasks. For a full description, consult the [reference][reference]. ## Should I use chezmoi? See what other people think about chezmoi by reading [articles][articles], listening to [podcasts][podcasts], and watching [videos][videos] about chezmoi. Read how [chezmoi compares to other dotfile managers][compares]. Explore other people's [dotfile repos that use chezmoi][repos]. ## How do I get help using chezmoi? chezmoi has extensive documentation. First, use the search bar at the top of this page using a few, short, and specific keywords related to your problem. chezmoi is an open source project with tens of thousands of users, so it is very likely that someone else has already encountered and solved your problem. Search [chezmoi's GitHub repo][github] for issues and discussions with keywords related to your problem. If your question is still unanswered, please [open a GitHub issue for support][support]. ## I like chezmoi. How do I say thanks? Please [give chezmoi a star on GitHub][star]. [Share chezmoi][share] and, if you're happy to share your public dotfiles repo, then [tag your repo with `chezmoi`][tag]. [Contributions are very welcome][contributions] and every [bug report, support request, and feature request][issues] helps make chezmoi better. Thank you :) chezmoi does not accept financial contributions. Instead, please make a donation to a charity or cause of your choice. [articles]: /links/articles.md [compares]: /comparison-table.md [contributions]: /developer-guide/contributing-changes.md [github]: https://github.com/twpayne/chezmoi [guide]: /user-guide/setup.md [history]: /reference/release-history.md [install]: /install.md [issues]: https://github.com/twpayne/chezmoi/issues [listen1]: /voice_30-04-2025_09-20-13.ogx [listen2]: /voice_30-04-2025_09-20-48.ogx [listen3]: /voice_30-04-2025_09-20-56.ogx [notes]: {{ $latestRelease.HTMLURL }} [podcasts]: /links/podcasts.md [quick-start]: /quick-start.md [reference]: /reference/index.md [repos]: /links/dotfile-repos.md [share]: /links/social-media.md [star]: https://github.com/twpayne/chezmoi [support]: https://github.com/twpayne/chezmoi/issues/new?assignees=&labels=support&projects=&template=01_support_request.md&title=. [tag]: /links/dotfile-repos.md [videos]: /links/videos.md ================================================ FILE: assets/chezmoi.io/docs/install.md.tmpl ================================================ {{- $latestRelease := gitHubLatestRelease "twpayne/chezmoi" -}} {{- $version := $latestRelease.Name | trimPrefix "v" -}} # Install The latest version of chezmoi is {{ $version }} ([release notes][notes], [release history][history]). ## One-line package install Install chezmoi with your package manager with a single command: === "Linux" === "Alpine" ```sh apk add chezmoi ``` === "Arch" ```sh pacman -S chezmoi ``` === "Fedora" ```sh dnf install chezmoi ``` === "NixOS" ```sh nix-env -i chezmoi ``` === "openSUSE Tumbleweed" ```sh zypper install chezmoi ``` === "RHEL (EPEL)" ```sh dnf install epel-release && dnf install chezmoi ``` === "Termux" ```sh pkg install chezmoi ``` === "Void" ```sh xbps-install -S chezmoi ``` === "macOS" === "Homebrew" ```sh brew install chezmoi ``` === "MacPorts" ```sh port install chezmoi ``` === "Nix" ```sh nix-env -i chezmoi ``` === "Windows" === "Chocolatey" ```batch choco install chezmoi ``` === "Scoop" ```batch scoop install chezmoi ``` === "Winget" ```batch winget install twpayne.chezmoi ``` === "FreeBSD" ```sh pkg install chezmoi ``` === "OpenIndiana" ```sh pkg install application/chezmoi ``` chezmoi is available in many cross-platform package managers: === "am" ```sh am -i chezmoi ``` === "asdf" ```sh asdf plugin add chezmoi && asdf install chezmoi {{ $version }} ``` === "mise" ```sh mise use --global chezmoi@{{ $version }} ``` === "Homebrew" ```sh brew install chezmoi ``` === "Nix" ```sh nix-env -i chezmoi ``` === "snap" ```sh snap install chezmoi --classic ``` For more packages, see [chezmoi on repology.org][repology]. ## One-line binary install Install the correct binary for your operating system and architecture in `./bin` with a single command: === "curl" ```sh sh -c "$(curl -fsLS get.chezmoi.io)" ``` === "wget" ```sh sh -c "$(wget -qO- get.chezmoi.io)" ``` === "PowerShell" ```powershell iex "&{$(irm 'https://get.chezmoi.io/ps1')}" ``` To provide the script with arguments, place them at the end of the quote: ```powershell iex "&{$(irm 'https://get.chezmoi.io/ps1')} -b '~/bin'" ``` !!! hint If you already have a dotfiles repo using chezmoi on GitHub at `https://github.com/$GITHUB_USERNAME/dotfiles` then you can install chezmoi and your dotfiles with the single command: ```sh sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply $GITHUB_USERNAME ``` Private GitHub repos require other [authentication methods][https-clone]: ```sh sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply git@github.com:$GITHUB_USERNAME/dotfiles.git ``` !!! hint If you want to install chezmoi in `./.local/bin` instead of `./bin` you can use `get.chezmoi.io/lb` or `chezmoi.io/getlb` instead. !!! hint To install the chezmoi binary in a different directory, use the `-b` option, for example: ```sh sh -c "$(curl -fsLS get.chezmoi.io)" -- -b $HOME/.local/bin ``` ## Download a pre-built Linux package Download a package for your distribution and architecture. === "deb" {{ range $arch := list "amd64" "arm64" "armel" "i386" "loong64" "mips64" "mips64le" "ppc64" "ppc64le" "riscv64" "s390x" }} [`{{ $arch }}`](https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_{{ $version }}_linux_{{ $arch }}.deb) {{- end }} === "rpm" {{ range $arch := list "aarch64" "armhfp" "i686" "loong64" "mips64" "mips64le" "ppc64" "ppc64le" "s390x" "riscv64" "x86_64" }} [`{{ $arch }}`](https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi-{{ $version }}-{{ $arch }}.rpm) {{- end }} === "apk" {{ range $arch := list "386" "amd64" "arm" "arm64" "loong64" "mips64_hardfloat" "mips64le_hardfloat" "ppc64" "ppc64le" "riscv64" "s390x" }} [`{{ $arch }}`](https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_{{ $version }}_linux_{{ $arch }}.apk) {{- end }} === "Arch Linux" {{ range $arch := list "386" "amd64" "arm" "arm64" "loong64" "mips64_hardfloat" "mips64le_hardfloat" "ppc64" "ppc64le" "riscv64" "s390x" }} [`{{ $arch }}`](https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_{{ $version }}_linux_{{ $arch }}.pkg.tar.zst) {{- end }} ## Download a pre-built binary Download an archive for your operating system and architecture containing a pre-built binary and shell completions. === "Linux" {{ range $arch := list "amd64" "arm" "arm64" "i386" "loong64" "mips64" "mips64le" "ppc64" "ppc64le" "riscv64" "s390x" }} [`{{ $arch }}`](https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_{{ $version }}_linux_{{ $arch }}.tar.gz) {{- end }} [`amd64` (glibc)](https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_{{ $version }}_linux-glibc_amd64.tar.gz) [`amd64` (musl)](https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_{{ $version }}_linux-musl_amd64.tar.gz) [`arm64` (Termux)](https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_{{ $version }}_android_arm64.tar.gz) === "macOS" {{ range $arch := list "amd64" "arm64" }} [`{{ $arch }}`](https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_{{ $version }}_darwin_{{ $arch }}.tar.gz) {{- end }} === "Windows" {{ range $arch := list "amd64" "arm64" "i386" }} [`{{ $arch }}`](https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_{{ $version }}_windows_{{ $arch }}.zip) {{- end }} === "FreeBSD" {{ range $arch := list "amd64" "arm" "arm64" "i386" "riscv64" }} [`{{ $arch }}`](https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_{{ $version }}_freebsd_{{ $arch }}.tar.gz) {{- end }} === "OpenBSD" {{ range $arch := list "amd64" "arm" "arm64" "i386" "ppc64" "riscv64" }} [`{{ $arch }}`](https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_{{ $version }}_openbsd_{{ $arch }}.tar.gz) {{- end }} ## Install from source Download, build, and install chezmoi for your system with Go 1.25 or later: ```sh git clone https://github.com/twpayne/chezmoi.git cd chezmoi make install-from-git-working-copy ``` ## Verify your download chezmoi's release process signs the SHA256 checksums of [all released assets][release-assets] with [cosign][cosign]. To verify an asset that you have downloaded: Download the [checksum file](https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_{{ $version }}_checksums.txt), [checksum file signature](https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_{{ $version }}_checksums.txt.sig), and [public signing key](https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_cosign.pub). ```sh curl --location --remote-name-all \ https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_{{ $version }}_checksums.txt \ https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_{{ $version }}_checksums.txt.sig \ https://github.com/twpayne/chezmoi/releases/download/v{{ $version }}/chezmoi_cosign.pub ``` Verify the signature of the checksum file with cosign. ```sh cosign verify-blob --key=chezmoi_cosign.pub \ --signature=chezmoi_{{ $version }}_checksums.txt.sig \ chezmoi_{{ $version }}_checksums.txt ``` !!! important cosign should print `Verified OK` Verify the that the SHA256 sum of your downloads matches the SHA256 sum in the verified checksum file. All the downloaded files must be in the current directory. === "Linux" ```sh sha256sum --check chezmoi_{{ $version }}_checksums.txt --ignore-missing ``` === "macOS" ```sh shasum --algorithm 256 --check chezmoi_{{ $version }}_checksums.txt --ignore-missing ``` For more information on chezmoi's release signing process, see the [developer documentation on chezmoi's releases][developer-release]. [history]: /reference/release-history.md [notes]: {{ $latestRelease.HTMLURL }} [repology]: https://repology.org/project/chezmoi/versions [https-clone]: https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls [release-assets]: https://github.com/twpayne/chezmoi/releases/tag/v{{ $version }} [cosign]: https://github.com/SigStore/cosign [developer-release]: /developer-guide/releases.md ================================================ FILE: assets/chezmoi.io/docs/license.md ================================================ # License The MIT License (MIT) Copyright (c) 2018 Tom Payne Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: assets/chezmoi.io/docs/links/articles.md.tmpl ================================================ # Articles !!! tip Recommended article: [Fedora Magazine: Take back your dotfiles with Chezmoi](https://fedoramagazine.org/take-back-your-dotfiles-with-chezmoi/) | Date | Version | Language | Title | | ---- | ------- | -------- | ----- | {{- range reverse .articles }} | {{ .date }} | {{ .version }} | {{ index . "lang" | default "EN" }} | [{{ .title }}]({{ .url }}) | {{- end }} ================================================ FILE: assets/chezmoi.io/docs/links/articles.md.yaml ================================================ articles: - date: '2019-01-10' version: 0.0.11 title: 'Linux Fu: The kitchen sync' url: https://hackaday.com/2019/01/10/linux-fu-the-kitchen-sync/ - date: '2020-04-01' version: 1.7.17 title: Managing dotfiles and secret with chezmoi url: https://blog.arkey.fr/2020/04/01/manage_dotfiles_with_chezmoi/ - date: '2020-04-03' version: 1.7.17 title: 'Fedora Magazine: Take back your dotfiles with Chezmoi' url: https://fedoramagazine.org/take-back-your-dotfiles-with-chezmoi/ - date: '2020-04-17' version: 1.7.17 lang: CN title: 用 Chezmoi 取回你的点文件 url: https://blog.csdn.net/F8qG7f9YD02Pe/article/details/105548429 - date: '2020-04-16' version: 1.7.19 lang: FR title: Chezmoi, visite guidée url: https://blog.wescale.fr/2020/04/16/chezmoi-visite-guidee/ - date: '2020-04-19' version: 1.7.19 lang: FR title: 'Git & dotfiles : versionner ses fichiers de configuration' url: https://www.armandphilippot.com/dotfiles-git-fichiers-configuration/ - date: '2020-04-20' version: 1.8.0 lang: FR title: Gestion des dotfiles et des secrets avec chezmoi url: https://blog.arkey.fr/2020/04/01/manage_dotfiles_with_chezmoi.fr/ - date: '2020-04-27' version: 1.8.0 title: Managing my dotfiles with chezmoi url: http://blog.emilieschario.com/post/managing-my-dotfiles-with-chezmoi/ - date: '2020-06-15' version: 1.8.2 title: Dotfiles management using chezmoi - How I Use Linux Desktop at Work Part5 url: https://blog.benoitj.ca/2020-06-15-how-i-use-linux-desktop-at-work-part5-dotfiles/ - date: '2020-07-03' version: 1.8.3 title: Feeling at home in a LXD container url: https://ubuntu.com/blog/feeling-at-home-in-a-lxd-container - date: '2020-08-03' version: 1.8.3 title: Automating a Linux in Windows Dev Setup url: https://matt.aimonetti.net/posts/2020-08-automating-a-linux-in-windows-dev-setup/ - date: '2020-08-09' version: 1.8.3 title: Automating and testing dotfiles url: https://seds.nl/posts/automating-and-testing-dotfiles/ - date: '2020-08-13' version: 1.8.3 title: Using BitWarden and Chezmoi to manage SSH keys url: https://www.jx0.uk/chezmoi/bitwarden/unix/ssh/2020/08/13/bitwarden-chezmoi-ssh-key.html - date: '2020-10-03' version: 1.8.6 title: Chezmoi Merging url: https://benoit.srht.site/2020-10-03-chezmoi-merging/ - date: '2020-10-05' version: 1.8.6 title: Dotfiles with Chezmoi url: https://blog.lazkani.io/posts/backup/dotfiles-with-chezmoi/ - date: '2020-11-05' version: 1.8.8 title: Using chezmoi to manage dotfiles url: https://pashinskikh.com/posts/chezmoi/ - date: '2020-11-06' version: 1.8.8 title: Chezmoi – Securely Manage dotfiles across multiple machines url: https://computingforgeeks.com/chezmoi-manage-dotfiles-across-multiple-machines/ - date: '2021-01-12' version: 1.8.10 title: Automating the Setup of a New Mac With All Your Apps, Preferences, and Development Tools url: https://www.moncefbelyamani.com/automating-the-setup-of-a-new-mac-with-all-your-apps-preferences-and-development-tools/ - date: '2021-01-29' version: 1.8.10 lang: CN title: 用 Chezmoi 管理配置文件 url: https://axionl.me/p/%E5%BD%92%E6%A1%A3-%E7%94%A8-chezmoi-%E7%AE%A1%E7%90%86%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6/ - date: '2021-02-07' version: 1.8.10 lang: JP title: chezmoi始めた url: https://joe-noh.hatenablog.com/entry/2021/02/07/215733 - date: '2021-02-17' version: 1.8.11 lang: JP title: chezmoi で dotfiles を手軽に柔軟にセキュアに管理する url: https://zenn.dev/ryo_kawamata/articles/introduce-chezmoi - date: '2021-04-01' version: 2.0.7 title: ChezMoi url: https://johnmathews.is/chezmoi.html - date: '2021-04-08' version: 2.0.9 lang: FR title: Bienvenue chez moi url: https://blogduyax.madyanne.fr/2021/bienvenue-chez-moi/ - date: '2021-05-10' version: 2.0.11 title: Development Environment (2021) url: https://ideas.offby1.net/posts/development-environment-2021.html - date: '2021-05-12' version: 2.0.12 title: 'My Dotfiles Story: A Journey to Chezmoi' url: https://www.mikekasberg.com/blog/2021/05/12/my-dotfiles-story.html - date: '2021-05-14' version: 2.0.12 title: A brief history of my dotfile management url: https://jonathanbartlett.co.uk/2021/05/14/a-brief-history-of-my-dotfiles.html - date: '2021-07-15' version: 2.1.2 lang: CN title: 使用Chezmoi管理配置文件 url: https://marvinsblog.net/post/2021-07-15-chezmoi-intro/ - date: '2021-07-23' version: 2.1.2 title: PBS 121 of X — Managing Dot Files and an Introduction to Chezmoi url: https://pbs.bartificer.net/pbs121 - date: '2021-08-04' version: 2.1.2 lang: PT title: Como instalar o Chezmoi, um gerenciador de dotfiles, no Ubuntu, Linux Mint, Fedora, Debian url: https://sempreupdate.com.br/como-instalar-o-chezmoi-um-gerenciador-de-dotfiles-no-ubuntu-linux-mint-fedora-debian/ - date: '2021-08-08' version: 2.1.2 title: PBS 122 of X — Managing Dot Files with Chezmoi url: https://pbs.bartificer.net/pbs122 - date: '2021-08-22' version: 2.1.2 title: PBS 123 of X — Backing up and Syncing Dot Files with Chezmoi url: https://pbs.bartificer.net/pbs123 - date: '2021-09-04' version: 2.1.2 title: PBS 124 of X — Chezmoi Templates url: https://pbs.bartificer.net/pbs124 - date: '2021-09-04' version: 2.2.0 title: Configuration Management url: https://cj.rs/blog/my-setup/chezmoi/ - date: '2021-09-06' version: 2.2.0 title: chezmoi dotfile management url: https://www.jacobbolda.com/chezmoi-dotfile-management - date: '2021-09-14' version: 2.2.0 title: Managing preference plists under Chezmoi url: https://zacwe.st/2021/09/14/managing-preference-plists-under-chezmoi/ - date: '2021-09-18' version: 2.1.2 title: PBS 125 of X — Chezmoi on Multiple Computers url: https://pbs.bartificer.net/pbs125 - date: '2021-10-25' version: 2.7.3 title: Share credentials across machines using chezmoi and bitwarden url: https://medium.com/@josemrivera/share-credentials-across-machines-using-chezmoi-and-bitwarden-4069dcb6e367 - date: '2021-10-26' version: 2.7.3 lang: RU title: Синхронизация системных настроек url: https://habr.com/en/post/585578/ - date: '2021-11-26' version: 2.8.0 title: Weekly Journal 47 - chezmoi, neovim url: https://scottbanwart.com/blog/2021/11/weekly-journal-47-chezmoi-neovim/ - date: '2021-12-01' version: 2.9.1 title: Chezmoi 2 url: https://johnmathews.is/chezmoi-2.html - date: '2021-12-04' version: 2.9.2 title: Advanced features of Chezmoi url: https://zerokspot.com/weblog/2021/12/04/advanced-chezmoi/ - date: '2021-12-13' version: 2.9.3 title: Managing Dotfiles With Chezmoi url: https://budimanjojo.com/2021/12/13/managing-dotfiles-with-chezmoi/ - date: '2021-12-20' version: 2.9.3 title: How chezmoi Implements Cross-Platform CI url: https://gopheradvent.com/calendar/2021/how-chezmoi-implements-cross-platform-ci/ - date: '2021-12-23' version: 2.9.3 title: Use Chezmoi to guarantee idempotency of terminal url: https://www.wazaterm.com/blog/advent-calendar-2021/use-chezmoi-to-guarantee-idempotency-of-terminal - date: '2022-01-12' version: 2.9.5 lang: IT title: Come funzionano i miei Mac url: https://correntedebole.com/come-funzionano-i-miei-mac/ - date: '2022-01-26' version: 2.10.1 lang: JP title: chezmoi で dotfiles を管理する url: https://blog.zoncoen.net/2022/01/26/chezmoi/ - date: '2022-02-01' version: 2.10.1 lang: JP title: chezmoi で dotfiles を手軽に柔軟にセキュアに管理する url: https://zenn.dev/ryo_kawamata/articles/introduce-chezmoi - date: '2022-02-02' version: 2.11.0 lang: FR title: Controler ses dotfiles en environnement éphémère url: https://blog.wescale.fr/2022/02/02/controler-ses-dotfiles-en-environnement-ephemere-2/ - date: '2022-02-12' version: 2.11.2 title: How To Manage Dotfiles With Chezmoi url: https://jerrynsh.com/how-to-manage-dotfiles-with-chezmoi/ - date: '2022-02-17' version: 2.12.0 lang: ES title: Qué son y cómo gestionar archivos dotfiles con chezmoi url: https://picodotdev.github.io/blog-bitix/2022/02/que-son-y-como-gestionar-archivos-dotfiles-con-chezmoi/ - date: '2022-02-22' version: 2.12.1 lang: JP title: chezmoi を使って VSCode devcontainer 対応 dotfiles を作る url: https://www.mizdra.net/entry/2022/02/22/022109 - date: '2022-03-03' version: 2.13.0 title: 'Local Environment-as-Code: Is It Possible Yet?' url: https://thenewstack.io/local-environment-as-code-is-it-possible-yet/ - date: '2022-03-13' version: 2.14.0 title: 'Tools I love: Chezmoi' url: https://messmore.org/post/chezmoi/ - date: '2022-04-25' version: 2.15.1 title: Easily moving Linux installs url: https://christitus.com/chezmoi/ - date: '2022-05-16' version: 2.16.0 title: Chezmoi for DotFiles url: https://www.spatacoli.com/blog/2022/05/chezmoi-for-dotfiles/ - date: '2022-06-02' version: 2.17.1 title: 'Local Env as Code: Is it possible yet' url: https://www.cncf.io/blog/2022/06/02/local-env-as-code-is-it-possible-yet/ - date: '2022-06-11' version: 2.17.1 lang: JP title: chezmoi で Linux と macOS 両方で使える dotfiles を作る url: https://www.docswell.com/s/iosiftakakura/K2EXM5-2022-06-11-chezmoi - date: '2022-08-05' version: 2.20.0 lang: CN title: 使用chezmoi管理dotfiles url: https://zhaohongxuan.github.io/2022/08/05/use-chezmoi-manage-dotfiles/ - date: '2022-08-11' version: 2.20.0 title: Chezmoi - a very cool tool to manage your dotfiles url: https://wyssmann.com/blog/2022/08/chezmoi-a-very-cool-tool-to-manage-your-dotfiles/ - date: '2022-09-13' version: 2.22.1 lang: IT title: 'Come installare Chezmoi: gestisci in modo sicuro i dotfile su più macchine' url: https://grayguide.net/it/come-installare-chezmoi-gestisci-in-modo-sicuro-i-dotfile-su-piu-macchine - date: '2022-09-28' version: 2.24.0 title: Shit Hot Dotfiles url: https://kolv.in/posts/dotfile-managment - date: '2023-01-05' version: 2.29.1 lang: JP title: 既存の dotfiles を chezmoi で管理する url: https://zenn.dev/johnmanjiro13/articles/d14825f4ef3184 - date: '2023-01-12' version: 2.29.1 lang: JP title: Chezmoiでかんたんクロスプラットフォームdotfiles管理のススメ url: https://deflis.hatenablog.com/entry/hatena-advent-calendar-2022-chezmoi-dotfiles - date: '2023-01-13' version: 2.29.1 title: Making the most out of distrobox and toolbx url: https://www.ypsidanger.com/making-the-most-out-of-distrobox-and-toolbx/ - date: '2023-01-22' version: 2.29.3 lang: JP title: dotfile manager の chezmoi に移行してみる url: https://blog.yamano.dev/posts/2023/01/chezmoi-setup/ - date: '2023-01-22' version: 2.29.3 title: Managing dotfiles url: https://dnitza.com/post/managing-dotfiles - date: '2023-02-26' version: 2.31.0 title: Managing dotfiles with chezmoi url: https://moesgaard.dev/posts/2023-02-26-managing-dotfiles-with-chezmoi/ - date: '2023-03-21' version: 2.32.0 lang: JP title: AWS CLI のプロファイルを chezmoi とBitwarden で管理する url: https://zenn.dev/nh8939/articles/8a6a4f5eb967a9 - date: '2023-03-17' version: 2.32.0 title: Automating the Setup of a New Mac With All Your Apps, Preferences, and Development Tools url: https://www.moncefbelyamani.com/automating-the-setup-of-a-new-mac-with-all-your-apps-preferences-and-development-tools/ - date: '2023-03-25' version: 2.33.0 lang: KR title: chezmoi, 세상 편리하게 dotfile 관리하기 url: https://songkg7.github.io/posts/chezmoi-awesome-dotfile-manager/ - date: '2023-04-08' version: 2.33.1 lang: KR title: chezmoi, 본격적으로 활용하기 url: https://songkg7.github.io/posts/chezmoi-basic-settings/ - date: '2023-04-15' version: 2.33.1 lang: JP title: dotfiles の管理に chezmoi を導入して fswatch で自動 apply できるようにしたg url: https://blog.mono0x.net/2023/04/15/chezmoi/ - date: '2023-04-26' version: 2.33.1 title: Managing my /home directory url: https://aspatel.com/20230426180939/ - date: '2023-04-29' version: 2.33.1 lang: JP title: chezmoi のテンプレート機能を使ってシェルの起動を高速化する url: https://blog.mono0x.net/2023/04/29/optimize-rcfiles-using-chezmoi/ - date: '2023-05-16' version: 2.33.6 lang: JP title: chezmoi url: https://www.ebiyuu.com/dotfiles/chezmoi/ - date: '2023-06-14' version: 2.34.1 lang: RU title: chezmoi url: https://principal-engineering.ru/posts/chezmoi/ - date: '2023-08-05' version: 2.36.1 title: Dotfiles with chezmoi url: https://www.steveyackey.com/post/dotfiles-with-chezmoi/ - date: '2023-09-07' version: 2.39.1 lang: JP title: 【chezmoi】dotfileのセキュアな値をdashlaneから参照できるようにする url: https://zenn.dev/hulk510/articles/chezmoi-dashlane - date: '2023-09-13' version: 2.39.1 lang: JP title: 複数OSに対応しているchezmoiを使ってdotfilesを効率的に管理する url: https://www.asobou.co.jp/blog/web/chezmoi - date: '2023-10-29' version: 2.40.4 lang: CN title: 用 chezmoi 管理 dotfiles url: https://thewang.net/blog/manage-dotfiles-with-chezmoi - date: '2023-12-10' version: 2.42.2 lang: JP title: chezmoi で dotfiles を管理するときに便利な機能についてまとめる url: https://zenn.dev/ganariya/articles/useful-features-of-chezmoi - date: '2023-12-16' version: 2.42.3 lang: JP title: chezmoi を使った dotfiles の管理方法 url: https://zenn.dev/yukionodera/articles/how-to-manage-dotfiles - date: '2023-12-17' version: 2.42.3 lang: JP title: chezmoi を サブマシンにも導入する url: https://zenn.dev/yukionodera/articles/setup-second-machine - date: '2023-12-18' version: 2.42.3 lang: JP title: chezmoi 管理のdotfiles でマシン毎に設定を変えたい url: https://zenn.dev/yukionodera/articles/chezmoi-use-template - date: '2023-12-23' version: 2.42.3 lang: DE title: Lokale Kofigurationsdateien sicher mit chezmoi und Git synchronisieren url: https://www.heise.de/ratgeber/Lokale-Kofigurationsdateien-sicher-mit-chezmoi-und-Git-synchronisieren-9580741.html - date: '2024-01-07' version: 2.43.0 title: Chezmore Chezmoi url: https://www.grumpymetalguy.com/tech_stack/chezmore/ - date: '2024-02-25' version: 2.47.0 title: Atuin and chezmoi are the dog's bollocks url: https://henry.catalinismith.com/2024/02/25/atuin-and-chezmoi-are-the-dogs-bollocks - date: '2024-03-02' version: 2.47.0 title: The Ultimate Dotfiles Management Tool url: https://medium.com/@bartelloni.guglielmo_39715/the-ultimate-dotfiles-management-tool-456177493974 - date: '2024-03-11' version: 2.47.1 lang: CN title: Chezmoi:優雅管理Linux的dotfile,使用Git儲存庫備份,類似GNU Stow url: https://ivonblog.com/posts/chezmoi-manage-dotfiles/ - date: '2024-03-23' version: 2.47.2 lang: CN title: 使用 chezmoi & vscode, 管理你的 dotfiles url: https://shansan.top/2024/03/23/using-chezmoi-to-manage-dotfiles/ - date: '2024-03-23' version: 2.47.2 title: A tour around chezmoi url: https://www.rousette.org.uk/archives/a-tour-around-chezmoi/ - date: '2024-03-25' version: 2.47.2 title: Whatever happened to dotfiles? url: https://ryan0x44.substack.com/p/whatever-happened-to-dotfiles - date: '2024-04-24' version: 2.47.4 title: 'Chezmoi: Manage Your Dotfiles Across Multiple Linux Systems' url: https://linuxtldr.com/installing-chezmoi/ - date: '2024-04-27' version: 2.48.0 lang: CN title: 使用 chezmoi & vscode, 管理你的 dotfiles url: https://juejin.cn/post/7362028633425608754 - date: '2024-05-01' version: 2.48.0 lang: TH title: จัดการ dotfiles ด้วย chezmoi url: https://www.anuwong.com/blog/manage-dotfiles-with-chezmoi/ - date: '2024-05-27' version: 2.48.1 title: Managing Dotfiles with Chezmoi url: https://1729.org.uk/posts/managing-dotfiles-with-chezmoi/ - date: '2024-06-26' version: 2.49.1 title: Automate Your Dotfiles with Chezmoi url: https://learn.typecraft.dev/tutorial/our-place-chezmoi/ - date: '2024-07-28' version: 2.51.0 title: Development Environment Setup with Chezmoi url: https://danielmschmidt.de/posts/dev-env-setup-with-chezmoi/ - date: '2024-07-31' version: 2.51.0 title: Managing dotfiles with chezmoi url: https://wqplease.com/p/managing-dotfiles-with-chezmoi - date: '2024-08-30' version: 2.52.1 lang: JP title: dotfiles管理をchezmoiに移行する url: https://nsakki55.hatenablog.com/entry/2024/08/30/125246 - date: '2024-09-08' version: 2.52.1 title: Managing dotfiles with chezmoi url: https://stoddart.github.io/2024/09/08/managing-dotfiles-with-chezmoi.html - date: '2024-09-09' version: 2.52.2 title: Keeping your Dotfiles in Sync and your Secrets in Gopass url: https://blog.m5e.de/post/keeping-your-dotfiles-in-sync-and-your-secrets-in-gopass/ - date: '2024-09-15' version: 2.52.2 title: Cross-Platform Dotfiles with Chezmoi, Nix, Brew, and Devpod url: https://medium.com/@alfor93/cross-platform-dotfiles-with-chezmoi-nix-brew-and-devpod-0fdb478e40ce - date: '2024-01-07' version: 2.52.3 title: How I manage Neovim configuration with Chezmoi url: https://www.lorenzobettini.it/2024/10/how-i-manage-neovim-configuration-with-chezmoi/ - date: '2024-11-07' version: 2.53.1 title: 'dotfiles management: chezmoi' url: https://www.gkr.one/blg-20241107-chezmoi - date: '2024-11-16' version: 2.54.0 title: Swapping to Chezmoi url: https://jsnfwlr.com/blog/2024/11/16/swapping-to-chezmoi/ - date: '2024-11-19' version: 2.54.0 lang: AR title: شيموا (chezmoi) ببساطة url: https://github.com/mohamedhany01/chezmoi-tutorial-arabic - date: '2024-12-02' version: 2.55.0 lang: JP title: chezmoiを使ったローカル環境爆速構築 url: https://qiita.com/cb400sp2/items/c04ec9472455d9022532 - date: '2024-12-18' version: 2.56.0 title: Exploring Tools For Managing Your Dotfiles url: https://gberga.medium.com/exploring-tools-for-managing-your-dotfiles-7f0a46305bde - date: '2024-12-19' version: 2.56.0 lang: JP title: chezmoiでdotfilesを管理する url: https://devops-blog.virtualtech.jp/entry/20241219/1734585262 - date: '2025-01-03' version: 2.57.0 title: Frictionless Dotfile Management With Chezmoi url: https://marcusb.org/posts/2025/01/frictionless-dotfile-management-with-chezmoi/ - date: '2025-01-09' version: 2.57.0 title: Using chezmoi to manage dotfiles url: https://kidoni.dev/using-chezmoi - date: '2025-01-19' version: 2.58.0 title: Managing dotfiles with chezmoi url: https://natelandau.com/managing-dotfiles-with-chezmoi/ - date: '2025-01-26' version: 2.58.0 lang: JP title: chezmoi で macOS の user defaults も管理する url: https://pihero.hatenadiary.jp/entry/2025/01/26/130123 - date: '2025-02-02' version: 2.59.0 title: My journey in managing dotfiles url: https://seds.nl/notes/my-journey-in-managing-dotfiles/ - date: '2025-02-19' version: 2.59.1 lang: JP title: WSLとWindowsの設定ファイルを「chezmoi」を使って安全に管理しよう url: https://thinkit.co.jp/article/37943 - date: '2025-03-04' version: 2.60.0 title: Tools, tools, tools url: https://peterbraden.co.uk/article/tools-tools-tools/ - date: '2025-04-03' version: 2.61.0 title: Migrating From Nix and Home Manager to Homebrew and Chezmoi url: https://htdocs.dev/posts/migrating-from-nix-and-home-manager-to-homebrew-and-chezmoi/ - date: '2025-04-06' version: 2.62.0 lang: FR title: Chezmoi, comment je gère mes dotfiles url: https://julien-noblet.github.io/posts/2025/chezmoi/ - date: '2025-04-06' version: 2.62.0 lang: CN title: dotfiles:一键配置你的开发环境 url: https://blog.csdn.net/gitblog_00574/article/details/147021711 - date: '2025-04-07' version: 2.62.0 title: Using chezmoi Part 2 — Templates url: https://kidoni.dev/using-templates-with-chezmoi - date: '2025-04-11' version: 2.62.0 title: Installing the Tmux Plugin Manager (tpm) with Chezmoi url: https://www.lorenzobettini.it/2025/04/installing-the-tmux-plugin-manager-tpm-with-chezmoi/ - date: '2025-04-13' version: 2.62.0 title: Managing External Dependencies with Chezmoi url: https://stoeps.de/posts/2025/managing_external_dependencies_with_chezmoi/ - date: '2025-04-19' version: 2.62.2 title: 'Continuous Improvement in DevOps: Streamlining with chezmoi and mise' url: https://manuelchichi.com.ar/blog/personal-toolset-2025/ - date: '2025-05-23' version: 2.62.5 lang: JP title: chezmoiでdotfilesを管理する url: https://hiratake.dev/blog/20250523/ - date: '2025-06-20' version: 2.62.6 title: Protecting Secrets in Dotfiles with Chezmoi url: https://kidoni.dev/chezmoi-templates-and-secrets - date: '2025-07-13' version: 2.63.0 title: Sync Claude Code commands and hooks across machines with chezmoi and age url: https://www.arun.blog/sync-claude-code-with-chezmoi-and-age/ - date: '2025-07-20' version: 2.63.0 lang: JP title: なぜchezmoiが最強のdotfiles管理ツールなのか? url: https://zenn.dev/smasato/articles/e382902ec4aa17 - date: '2025-07-25' version: 2.63.0 title: Habits in the Shell, shared url: https://offby1.website/posts/habits-in-the-shell-shared.html - date: '2025-07-27' version: 2.36.1 lang: CN title: 使用 chezmoi & vscode, 管理你的 dotfiles url: https://www.shuzhiduo.com/A/kvJ3V17Xzg/ - date: '2025-08-02' version: 2.63.1 title: Dotfile management with Chezmoi and Vim url: https://www.probableodyssey.blog/all-posts/dotfile_management_with_chezmoi_and_vim/ - date: '2025-08-12' version: 2.64.0 title: From Dotfiles to Portable Dev Environments url: https://dakaiser.substack.com/p/from-dotfiles-to-portable-dev-environments - date: '2025-08-18' version: 2.64.0 title: secure dotfiles management with chezmoi url: https://walterra.dev/blog/2025-08-18-chezmoi-keychain-integration - date: '2025-08-27' version: 2.56.0 lang: JP title: chezmoi url: https://www.ebiyuu.com/dotfiles/chezmoi/ - date: '2025-08-31' version: 2.56.0 title: Automating My Developer Environment - Instantly set up any new laptop url: https://www.linkedin.com/pulse/automating-my-developer-environment-instantly-set-up-joseph-lee-omw7e/ - date: '2025-09-08' version: 2.65.1 title: Improving My Dotfiles Posture url: https://benprisby.com/blog/improving-my-dotfiles-posture/ - date: '2025-09-25' version: 2.65.2 title: Managing KDE Dotfiles with Chezmoi and Chezmoi Modify Manager url: https://www.lorenzobettini.it/2025/09/managing-kde-dotfiles-with-chezmoi-and-chezmoi-modify-manager/ - date: '2025-10-06' version: 2.65.2 title: Testable Dotfiles Management With Chezmoi url: https://shunk031.me/post/testable-dotfiles-management-with-chezmoi/ - date: '2025-10-07' version: 2.65.2 title: Dotfiles with Chezmoi url: https://aohorodnyk.com/post/2025-10-07-dotfiles-with-chezmoi/ - date: '2025-11-02' version: 2.66.1 title: Maintaining KDE dotfiles with Chezmoi Modify Manager url: https://www.lorenzobettini.it/2025/11/maintaining-kde-dotfiles-with-chezmoi-modify-manager/ - date: '2025-12-31' version: 2.68.1 lang: CN title: 换新电脑不再折腾:用 chezmoi × Nix 一键复刻开发环境 url: https://zhuanlan.zhihu.com/p/1989720652035424585 - date: '2026-01-13' version: 2.69.1 title: Taking Control of My Dotfiles with chezmoi url: https://blog.cmmx.de/2026/01/13/taking-control-of-my-dotfiles-with-chezmoi/ - date: '2026-01-22' version: 2.69.3 lang: JP title: 【chezmoi】AIエージェントの指示ファイルと権限管理を共通化してラクしよう【Hooks】 url: https://zenn.dev/waki285/articles/chezmoi-dotfiles - date: '2026-01-24' version: 2.69.3 lang: CN title: Chezmoi 管理和同步多平台 dotfiles url: https://v3n0.top/post/2026/chezmoi/ - date: '2026-01-31' version: 2.69.3 title: Dotfiles Secrets in Chezmoi, Without Password Headaches url: https://www.mikekasberg.com/blog/2026/01/31/dotfiles-secrets-in-chezmoi.html - date: '2026-02-09' version: 2.69.3 lang: JP title: dotfiles管理でchezmoiを使う url: https://blog.freks.jp/chezmoi/ - date: '2026-02-12' version: 2.69.4 title: How chezmoi Transformed My Dotfile Management Across Multiple Systems url: https://medium.com/@tiomothybryant3/how-chezmoi-transformed-my-dotfile-management-across-multiple-systems-0db4029b518e - date: '2026-02-07' version: 2.69.4 lang: FR title: Mon environnement de développement avec mise et chez-moi url: https://david.drugeon-hamon.bzh/blog/2026/02/env-dev-avec-mise-et-chezmoi/ - date: '2026-02-13' version: 2.69.4 title: I Deprecated Dotfiles and Oh My Zsh, and Moved to Chezmoi url: https://samwize.com/2026/02/13/i-deprecated-dotfiles-and-oh-my-zsh-and-moved-to-chezmoi/ - date: '2026-02-22' version: 2.69.4 title: Dotfiles (series) url: https://knuth.info/posts/the-solo-stack/dotfiles/ - date: '2026-02-27' version: 2.69.4 title: Syncing Dotfiles Across Machines with Chezmoi url: https://rifqimfahmi.dev/blog/chezmoi-to-backup-dotfiles - date: '2026-03-07' version: 2.69.4 title: One Skills Brain for Codex, Claude, Cursor, and Copilot with chezmoi url: https://dev.to/dotwee/one-skills-brain-for-codex-claude-cursor-and-copilot-with-chezmoi-2p3k - date: '2026-03-07' version: 2.69.4 title: One MCP Configuration for Codex, Claude, Cursor, and Copilot with chezmoi url: https://dev.to/dotwee/one-mcp-configuration-for-codex-claude-cursor-and-copilot-with-chezmoi-925 ================================================ FILE: assets/chezmoi.io/docs/links/dotfile-repos.md ================================================ # Dotfile repos * [GitHub](https://github.com/topics/chezmoi?o=desc&s=updated) * [GitLab](https://gitlab.com/explore/projects/topics/chezmoi) * [Codeberg](https://codeberg.org/explore/repos?sort=recentupdate&q=chezmoi&tab=) ================================================ FILE: assets/chezmoi.io/docs/links/podcasts.md.tmpl ================================================ # Podcasts !!! tip Recommended podcast: [Managing Dot Files and an Introduction to Chezmoi](https://www.podfeet.com/blog/2021/07/ccatp-693/) | Date | Version | Language | Title | | ---- | ------- | -------- | ----- | {{- range reverse .podcasts }} | {{ .date }} | {{ .version }} | {{ .lang | default "EN" }} | [{{ .title }}]({{ .url }}) | {{- end }} ================================================ FILE: assets/chezmoi.io/docs/links/podcasts.md.yaml ================================================ podcasts: - date: '2026-02-24' version: 2.69.4 lang: DE title: Hyper, Meh und Linux Laptops url: https://www.youtube.com/watch?v=jMNh8u0W274&t=6133s - date: '2019-11-20' version: 1.7.2 title: 'FLOSS weekly episode 556: chezmoi' url: https://twit.tv/shows/floss-weekly/episodes/556 - date: '2021-07-23' version: 2.1.2 title: 'CCATP #693 – Bart Busschots on PBS 121 of X — Managing Dot Files and an Introduction to Chezmoi' url: https://www.podfeet.com/blog/2021/07/ccatp-693/ - date: '2021-08-08' version: 2.1.2 title: 'CCATP #695 – Bart Busschots on PBS 122 – Managing Dot Files with Chezmoi' url: https://www.podfeet.com/blog/2021/08/ccatp-695/ - date: '2021-08-22' version: 2.1.2 title: 'CCATP #696 – Bart Busschots on PBS 123 of X — Backing up and Syncing Dot Files with Chezmoi' url: https://www.podfeet.com/blog/2021/08/ccatp-696/ - date: '2021-09-04' version: 2.1.2 title: 'CCATP #698 – Bart Busschots on PBS 124 of X – Chezmoi Templates' url: https://www.podfeet.com/blog/2021/09/ccatp-698/ - date: '2021-09-18' version: 2.1.2 title: 'CCATP #699 – Bart Busschots on PBS 125 of X – Chezmoi on Multiple Computers' url: https://www.podfeet.com/blog/2021/09/ccatp-699/ - date: '2022-03-11' version: 2.14.0 title: 'The Real Python Podcast: Episode 101: Tools for Setting Up Python on a New Machine' url: https://realpython.com/podcasts/rpp/101/#t=3368 - date: '2022-05-27' version: 2.17.0 title: Fédérer une communauté technique autour d'un projet Open Source url: https://www.podcastics.com/podcast/episode/federer-une-communaute-technique-autour-dun-projet-open-source-131694/ - date: '2023-01-30' version: 2.29.3 lang: ES title: 459 - Soy un zoquete, otra vez hice un rm -rf url: https://atareao.es/podcast/soy-un-zoquete-otra-vez-hice-un-rm-rf/ - date: '2023-05-22' version: 2.33.6 lang: ES title: 491 - Tres herramientas que han revolucionado mi terminal Linux url: https://atareao.es/podcast/tres-herramientas-que-han-revolucionado-mi-terminal-linux/ - date: '2024-10-17' version: 2.52.4 title: Releasing more BSDs url: https://www.bsdnow.tv/581?t=2064 - date: '2025-11-24' version: 2.67.0 lang: JP title: EP326 10年モノのdotfilesを整理 url: https://yuru28.com/326#t=1081 ================================================ FILE: assets/chezmoi.io/docs/links/related-software.md ================================================ # Related software ## Editor integration ### [`github.com/andre-kotake/nvim-chezmoi`](https://github.com/andre-kotake/nvim-chezmoi) { id="andre-kotake/nvim-chezmoi" } A NeoVim plugin that integrates with chezmoi. ### [`github.com/alker0/chezmoi.vim`](https://github.com/alker0/chezmoi.vim) { id="alker0/chezmoi.vim" } Intelligent VIM syntax highlighting when editing files in your source directory. Works with both `chezmoi edit` and editing files directly. ### [`github.com/tuh8888/chezmoi.el`](https://github.com/tuh8888/chezmoi.el) { id="tuh8888/chezmoi.el" } Convenience functions for interacting with chezmoi in Emacs. ### [`github.com/Lilja/vim-chezmoi`](https://github.com/Lilja/vim-chezmoi) { id="lilja/vim-chezmoi" } A plugin for VIM to apply the dotfile you are editing on `:w`. ### [`github.com/Lindydancer/tmpl-mode`](https://github.com/Lindydancer/tmpl-mode) { id="Lindydancer/tmpl-mode" } Emacs minor mode for "tmpl" template files. ### [`github.com/xvzc/chezmoi.nvim`](https://github.com/xvzc/chezmoi.nvim) { id="xvzc/chezmoi.nvim" } Edit your chezmoi-managed files and automatically apply. ### [`github.com/GianniBYoung/chezmoi-telescope.nvim`](https://github.com/GianniBYoung/chezmoi-telescope.nvim) { id="GianniBYoung/chezmoi-telescope.nvim" } Custom Telescope Picker for Chez Moi Managed Dot files. ## Frontends ### [`github.com/daptify14/chezit`](https://github.com/daptify14/chezit) { id="daptify14/chezit"} Terminal UI for chezmoi dotfile management. ### [`github.com/johan-weitner/chezmoi-ui`](https://github.com/johan-weitner/chezmoi-ui) { id="johan-weitner/chezmoi-ui" } A web UI for managing a list of apps to seed/feed a chezmoi setup. ### [`github.com/matmaer/chezmoi-mousse`](https://github.com/matmaer/chezmoi-mousse) { id="matmaer/chezmoi-mousse" } Visual interface in the terminal for the chezmoi dotfile manager, with a wink to the mouse. ## Other ### [`atuin.sh`](https://atuin.sh/) { id="atuin" } Sync, search and backup shell history. ### [`github.com/b3nj5m1n/xdg-ninja`](https://github.com/b3nj5m1n/xdg-ninja) { id="b3nj5m1n/xdg-ninja" } A shell script which checks your $HOME for unwanted files and directories. ### [`github.com/hussainweb/ansible-role-chezmoi`](https://github.com/hussainweb/ansible-role-chezmoi) { id="hussainweb/ansible-role-chezmoi" } Installs chezmoi on Ubuntu and Debian servers. ### [`github.com/joke/asdf-chezmoi`](https://github.com/joke/asdf-chezmoi) { id="joke/asdf-chezmoi" } chezmoi plugin for asdf version manager. ### [`github.com/main-branch/chezroot`](https://github.com/main-branch/chezroot) { id="main-branch/chezroot" } A `sudo` wrapper for chezmoi to manage root-owned files across your entire filesystem. ### [`github.com/mass8326/zsh-chezmoi`](https://github.com/mass8326/zsh-chezmoi) { id="mass8326/zsh-chezmoi" } Add completion and aliases for chezmoi to make managing dotfiles easier to zsh. ### [Chezetc](https://silverrainz.me/chezetc/) { id="silverrainz.me/chezetc" } Extending chezmoi to manage files under /etc and other root-owned directories. ### [`github.com/tcaxle/drapeau`](https://github.com/tcaxle/drapeau) { id="tcaxle/drapeau" } An add-on to synchronize your color schemes across systems and allow easy colorscheme switching using chezmoi templates. ### [`github.com/VorpalBlade/chezmoi_modify_manager`](https://github.com/VorpalBlade/chezmoi_modify_manager) { id="vorpalblade/chezmoi_modify_manager" } An add-on to deal with config files that contain a mix of settings and transient state, such as with GUI program settings files also containing recently used files and window positions. ### [`install.doctor`](https://install.doctor) { id="install.doctor" } Desktop provisioning system. ### [`github.com/halostatue/fish-chezmoi`](https://github.com/halostatue/fish-chezmoi) A plug-in for the Fish shell that ensures that completions are always loaded and a function that wraps `chezmoi cd` to not create a sub-shell. ================================================ FILE: assets/chezmoi.io/docs/links/social-media.md ================================================ # Social media | Platform | Search term | | ------------------ | -------------------------------------------------------------------------------------------------------------------------- | | Hacker News | [`chezmoi`](https://hn.algolia.com/?dateRange=all&page=0&prefix=false&query=chezmoi&sort=byDate&type=comment) | | LinkedIn | [`chezmoi dotfiles`](https://www.linkedin.com/search/results/all/?keywords=chezmoi%20dotfiles&origin=GLOBAL_SEARCH_HEADER) | | lobste.rs comments | [`chezmoi`](https://lobste.rs/search?q=chezmoi&what=comments&order=newest) | | lobste.rs stories | [`chezmoi`](https://lobste.rs/search?q=chezmoi&what=stories&order=newest) | | Reddit | [`chezmoi dotfiles`](https://www.reddit.com/search/?q=chezmoi+dotfiles&sort=new) | | Stack Overflow | [`chezmoi`](https://stackoverflow.com/questions/tagged/chezmoi) | | YouTube | [`chezmoi dotfiles`](https://www.youtube.com/results?search_query=%22chezmoi%22+dotfiles) | ================================================ FILE: assets/chezmoi.io/docs/links/videos.md.tmpl ================================================ # Videos !!! tip Recommended video: [chezmoi: manage your dotfiles across multiple, diverse machines, securely](https://fosdem.org/2021/schedule/event/chezmoi/) | Date | Version | Language | Title | | ---- | ------- | -------- | ----- | {{- range reverse .videos }} | {{ .date }} | {{ .version }} | {{ .lang | default "EN" }} | [{{ .title }}]({{ .url }}) | {{- end }} ================================================ FILE: assets/chezmoi.io/docs/links/videos.md.yaml ================================================ videos: - date: '2019-11-20' version: 1.7.2 title: 'FLOSS weekly episode 556: chezmoi' url: https://twit.tv/shows/floss-weekly/episodes/556 - date: '2020-03-12' version: 1.7.16 title: Managing Dotfiles with ChezMoi url: https://www.youtube.com/watch?v=HXx6ugA98Qo - date: '2020-07-06' version: 1.8.3 title: 'Conf42: chezmoi: Manage your dotfiles across multiple machines, securely' url: https://www.youtube.com/watch?v=JrCMCdvoMAw - date: '2021-02-06' version: 1.8.10 title: 'chezmoi: manage your dotfiles across multiple, diverse machines, securely' url: https://fosdem.org/2021/schedule/event/chezmoi/ - date: '2021-09-06' version: 2.2.0 title: 'chezmoi: Organize your dotfiles across multiple computers' url: https://www.youtube.com/watch?v=L_Y3s0PS_Cg - date: '2021-11-27' version: 2.8.0 lang: TH title: 'Command ไร 2021-11-27 : ย้าย dotfiles ไป chezmoi' url: https://www.youtube.com/watch?v=8ybNfCfnF2Y - date: '2021-12-08' version: 2.9.3 title: How Go makes chezmoi possible url: https://www.youtube.com/watch?v=5XiewS8ZbH8&t=1044s - date: '2022-04-27' version: 2.15.1 title: Easily moving Linux installs url: https://www.youtube.com/watch?v=x6063EuxfEA - date: '2022-09-13' version: 2.22.1 title: Using chezmoi to automate dotfiles / config files (+ my bashrc) url: https://www.youtube.com/watch?v=id5UKYuX4-A - date: '2022-12-15' version: 2.27.3 lang: ES title: Archivos de configuración fácil con chezmoi url: https://www.youtube.com/watch?v=Xsh2DGSe6Lg - date: '2023-12-03' version: 2.42.2 title: The ultimate dotfiles setup url: https://www.youtube.com/watch?v=-RkANM9FfTM - date: '2024-01-14' version: 2.24.0 title: 'managing dotfiles: a guided tour through my own setup' url: https://www.youtube.com/watch?v=fQ3txCIxiiU - date: '2024-02-17' version: 2.47.0 title: 12 GREAT command line programs YOU recommended! url: https://www.youtube.com/watch?v=nCS4BtJ34-o&t=324s - date: '2024-06-22' version: 2.47.1 title: Automating Development Environments with Ansible & Chezmoi url: https://www.youtube.com/watch?v=P4nI1VhoN2Y - date: '2024-11-18' version: 2.54.0 lang: AR title: Chezmoi, Dotfiles, Workflow, Templates and Encryption Arabic شرح # removed | from title as it breaks Markdown tables url: https://www.youtube.com/watch?v=Jrrd2dHYBmY - date: '2025-03-22' version: 2.61.0 lang: DE title: 'Effiziente Dotfile-Verwaltung: Chezmoi im Einsatz' url: https://media.ccc.de/v/clt25-240-effiziente-dotfile-verwaltung-chezmoi-im-einsatz - date: '2025-04-23' version: 2.62.2 title: Managing Dotfiles with Chezmoi url: https://www.youtube.com/watch?v=KsEj_pvOXdE - date: '2025-06-19' version: 2.62.6 lang: DE title: 'Immutable Linux Desktops: Produktive Arbeit mit Fedora Silverblue, Chezmoi und Distrobox' url: https://media.ccc.de/v/gpn23-31-immutable-linux-desktops-produktive-arbeit-mit-fedora-silverblue-chezmoi-und-distrobox#t=2424 - date: '2025-09-13' version: 2.65.1 title: How CHEZMOI manages dotfiles url: https://www.youtube.com/watch?v=xXemcEdoI9Y - date: '2025-10-12' version: 2.66.0 title: Supercharge your development workflow with these CLI tools url: https://www.youtube.com/watch?v=rqpiVgWZBOg&t=742s - date: '2026-01-01' version: 2.69.3 lang: FR title: Nouveau laptop ? Configurez TOUT en 5 min avec Chezmoi url: https://www.youtube.com/watch?v=6nqhBjvVVqE ================================================ FILE: assets/chezmoi.io/docs/migrating-from-another-dotfile-manager.md ================================================ # Migrating from another dotfile manager ## Migrate from a dotfile manager that uses symlinks Many dotfile managers replace dotfiles with symbolic links to files in a common directory. If you `chezmoi add` such a symlink, chezmoi will add the symlink, not the file. To assist with migrating from symlink-based systems, use the `--follow` option to `chezmoi add`, for example: ```sh chezmoi add --follow ~/.bashrc ``` This will tell `chezmoi add` that the target state of `~/.bashrc` is the target of the `~/.bashrc` symlink, rather than the symlink itself. When you run `chezmoi apply`, chezmoi will replace the `~/.bashrc` symlink with the file contents. ================================================ FILE: assets/chezmoi.io/docs/quick-start.md ================================================ # Quick start ## Concepts Roughly speaking, chezmoi stores the desired state of your dotfiles in the directory `~/.local/share/chezmoi`. When you run `chezmoi apply`, chezmoi calculates the desired contents for each of your dotfiles and then makes the minimum changes required to make your dotfiles match your desired state. chezmoi's concepts are [described more accurately in the reference manual][concepts]. ## Start using chezmoi on your current machine Assuming that you have already [installed chezmoi][install], initialize chezmoi with: ```sh chezmoi init ``` This will create a new git local repository in `~/.local/share/chezmoi` where chezmoi will store its source state. By default, chezmoi only modifies files in the working copy. Manage your first file with chezmoi: ```sh chezmoi add ~/.bashrc ``` This will copy `~/.bashrc` to `~/.local/share/chezmoi/dot_bashrc`. Edit the source state: ```sh chezmoi edit ~/.bashrc ``` This will open `~/.local/share/chezmoi/dot_bashrc` in your `$EDITOR`. Make some changes and save the file. !!! hint You don't have to use `chezmoi edit` to edit your dotfiles. See [this FAQ entry][faq-edit] for more details. See what changes chezmoi would make: ```sh chezmoi diff ``` Apply the changes: ```sh chezmoi -v apply ``` All chezmoi commands accept the `-v` (verbose) flag to print out exactly what changes they will make to the file system, and the `-n` (dry run) flag to not make any actual changes. The combination `-n` `-v` is very useful if you want to see exactly what changes would be made. Next, open a shell in the source directory, to commit your changes: ```sh chezmoi cd git add . git commit -m "Initial commit" ``` [Create a new repository on GitHub][new-repo] called `dotfiles` and then push your repo: ```sh git remote add origin git@github.com:$GITHUB_USERNAME/dotfiles.git git branch -M main git push -u origin main ``` !!! hint chezmoi can be configured to automatically [add, commit, and push][autogit] changes to your repo. chezmoi can also be used with [GitLab][gitlab], or [BitBucket][bitbucket], [Source Hut][srht], or any other git hosting service. Finally, exit the shell in the source directory to return to where you were: ```sh exit ``` These commands are summarized in this sequence diagram: ```mermaid sequenceDiagram participant H as home directory participant W as working copy participant L as local repo participant R as remote repo H->>L: chezmoi init H->>W: chezmoi add $FILE W->>W: chezmoi edit $FILE W-->>H: chezmoi diff W->>H: chezmoi apply H-->>W: chezmoi cd W->>L: git add W->>L: git commit L->>R: git push W-->>H: exit ``` ## Using chezmoi across multiple machines On a second machine, initialize chezmoi with your dotfiles repo: ```sh chezmoi init https://github.com/$GITHUB_USERNAME/dotfiles.git ``` !!! hint Private GitHub repos require other [authentication methods][private-auth]: ```sh chezmoi init git@github.com:$GITHUB_USERNAME/dotfiles.git ``` This will check out the repo and any submodules and optionally create a chezmoi config file for you. Check what changes that chezmoi will make to your home directory by running: ```sh chezmoi diff ``` If you are happy with the changes that chezmoi will make then run: ```sh chezmoi apply -v ``` If you are not happy with the changes to a file then either edit it with: ```sh chezmoi edit $FILE ``` Or, invoke a merge tool (by default `vimdiff`) to merge changes between the current contents of the file, the file in your working copy, and the computed contents of the file: ```sh chezmoi merge $FILE ``` On any machine, you can pull and apply the latest changes from your repo with: ```sh chezmoi update -v ``` These commands are summarized in this sequence diagram: ```mermaid sequenceDiagram participant H as home directory participant W as working copy participant L as local repo participant R as remote repo R->>W: chezmoi init $REPO W-->>H: chezmoi diff W->>H: chezmoi apply W->>W: chezmoi edit $FILE W->>W: chezmoi merge $FILE R->>H: chezmoi update ``` ## Set up a new machine with a single command You can install your dotfiles on new machine with a single command: ```sh chezmoi init --apply https://github.com/$GITHUB_USERNAME/dotfiles.git ``` If you use GitHub and your dotfiles repo is called `dotfiles` then this can be shortened to: ```sh chezmoi init --apply $GITHUB_USERNAME ``` !!! hint Private GitHub repos require other [authentication methods][private-auth]: ```sh chezmoi init --apply git@github.com:$GITHUB_USERNAME/dotfiles.git ``` This command is summarized in this sequence diagram: ```mermaid sequenceDiagram participant H as home directory participant W as working copy participant L as local repo participant R as remote repo R->>H: chezmoi init --apply $REPO ``` ## Next steps For a full list of commands run: ```sh chezmoi help ``` chezmoi has much more functionality. Good starting points are reading [what other people say about chezmoi][articles], adding more dotfiles, and using templates to manage files that vary from machine to machine and retrieve secrets from your password manager. Read the [user guide][user-guide] to explore and see [how people use chezmoi][repos] for inspiration. [articles]: /links/articles.md [bitbucket]: https://bitbucket.org [concepts]: /reference/concepts.md [faq-edit]: /user-guide/frequently-asked-questions/usage.md#how-do-i-edit-my-dotfiles-with-chezmoi [gitlab]: https://gitlab.com [install]: /install.md [new-repo]: https://github.com/new [private-auth]: https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls [repos]: /links/dotfile-repos.md [srht]: https://sr.ht/ [user-guide]: /user-guide/setup.md [autogit]: /user-guide/daily-operations.md#automatically-commit-and-push-changes-to-your-repo ================================================ FILE: assets/chezmoi.io/docs/reference/application-order.md ================================================ # Application order chezmoi is deterministic in its order of application. The order is: 1. Read the source state. 2. Read the destination state. 3. Compute the target state. 4. Run `run_before_` scripts in alphabetical order. 5. Update entries in the target state (files, directories, externals, scripts, symlinks, etc.) in alphabetical order of their target name. Directories (including those created by externals) are updated before the files they contain. 6. Run `run_after_` scripts in alphabetical order. Target names are considered after all attributes are stripped. !!! example Given `create_alpha` and `modify_dot_beta` in the source state, `.beta` will be updated before `alpha` because `.beta` sorts before `alpha`. chezmoi assumes that the source or destination states are not modified while chezmoi is being executed. This assumption permits significant performance improvements, including allowing chezmoi to only read files from the source and destination states if they are needed to compute the target state. chezmoi's behavior when the above assumptions are violated is undefined. For example, using a `run_before_` script to update files in the source or destination states violates the assumption that the source and destination states do not change while chezmoi is running. !!! note External sources are updated during the update phase; it is inadvisable for a `run_before_` script to depend on an external applied *during* the update phase. `run_after_` scripts may freely depend on externals. ================================================ FILE: assets/chezmoi.io/docs/reference/command-line-flags/common.md ================================================ # Common command line flags The following flags apply to multiple commands where they are relevant. ## Flags ### `--age-recipient` *recipient* Temporarily override the age recipient for this command. This only has an effect if age encryption is configured and the `--encrypt` flag is passed, and cannot be combined with `--age-recipient-file`. ### `--age-recipient-file` *recipient-file* Temporarily override the age recipient for this command. This only has an effect if age encryption is configured and the `--encrypt` flag is passed, and cannot be combined with `--age-recipient`. ### `-x`, `--exclude` *types* --8<-- "common-flags/exclude.md" ### `-f`, `--format` `json`|`yaml` --8<-- "common-flags/format.md" ### `-h`, `--help` Print help. ### `-i`, `--include` *types* --8<-- "common-flags/include.md" ### `--init` --8<-- "common-flags/init.md" ### `--override-data` *json-data* --8<-- "common-flags/override-data.md" ### `--override-data-file` *filename* --8<-- "common-flags/override-data-file.md" ### `-P`, `--parent-dirs` --8<-- "common-flags/parent-dirs.md" ### `-p`, `--path-style` *style* --8<-- "common-flags/path-style.md:all" ### `-r`, `--recursive` --8<-- "common-flags/recursive.md:default-false" ### `--tree` --8<-- "common-flags/tree.md" ## Available entry types You can provide a list of entry types, separated by commas. Types can be preceded with `no` to remove them, e.g. `scripts,noalways`. | Type | Description | | ----------- | --------------------------- | | `all` | All entries | | `none` | No entries | | `dirs` | Directories | | `files` | Files | | `remove` | Removes | | `scripts` | Scripts | | `symlinks` | Symbolic links | | `always` | Scripts that are always run | | `encrypted` | Encrypted entries | | `externals` | External entries | | `templates` | Templates | ================================================ FILE: assets/chezmoi.io/docs/reference/command-line-flags/developer.md ================================================ # Developer command line flags The following flags are global but only relevant for developers and debugging. ## Flags ### `--debug` Log information helpful for debugging. [profile]: https://blog.golang.org/pprof ================================================ FILE: assets/chezmoi.io/docs/reference/command-line-flags/global.md ================================================ # Global command line flags The following flags are available for all chezmoi commands. Note that some flags may not have any effect on certain commands. ## Flags ### `--cache` *directory* > Configuration: `cacheDir` Use *directory* as the cache directory. ### `--color` *value* > Configuration: `color` Colorize diffs, *value* can be `on`, `off`, `auto`, or any boolean-like value recognized by `promptBool`. The default is `auto` which will colorize diffs only if the environment variable `$NO_COLOR` is not set and stdout is a terminal. ### `-c`, `--config` *filename* Read the [configuration][configuration] from *filename*. ### `--config-format` *format* Assume the configuration file is in the given format. This is only needed if the config filename does not have an extension, for example when it is `/dev/stdin`. Supported formats: `json`, `jsonc`, `toml`, `yaml`. ### `-D`, `--destination` *directory* > Configuration: `destDir` Use *directory* as the destination directory. ### `-n`, `--dry-run` Set dry run mode. In dry run mode, the destination directory is never modified. This is most useful in combination with the `-v` (verbose) flag to print changes that would be made without making them. ### `--force` Make changes without prompting. ### `--interactive` Prompt before applying each target. ### `--less-interactive` Prompt before applying changed or pre-existing targets. ### `-k`, `--keep-going` Keep going as far as possible after a encountering an error. ### `--mode` `file`|`symlink` Mode of operation. The default is `file`. ### `--no-pager` Do not use the pager. ### `--no-tty` Do not attempt to get a TTY for prompts. Instead, read them from stdin. ### `-o`, `--output` *filename* Write the output to *filename* instead of stdout. ### `--persistent-state` *filename* > Configuration: `persistentState` Read and write the persistent state from *filename*. By default, chezmoi stores its persistent state in `chezmoistate.boltdb` in the same directory as its configuration file. ### `--progress` *value* Show progress when downloading externals. *value* can be `on`, `off`, or `auto`. The default is `auto` which shows progress bars when stdout is a terminal. ### `-R`, `--refresh-externals` [*value*] Control the refresh of the externals cache. *value* can be any of `always`, `auto`, or `never` and defaults to `always` if no *value* is specified. If no `--refresh-externals` flag is specified then chezmoi defaults to `auto`. `always` (or any truthy value as accepted by `parseBool`) causes chezmoi to re-download externals. `auto` means only re-download externals that have not been downloaded within their refresh periods. `never` (or any other falsy value accepted by `parseBool`) means only download if no cached external is available. ### `-S`, `--source` *directory* > Configuration: `sourceDir` Use *directory* as the source directory. ### `--source-path` Interpret *targets* passed to the command as paths in the source directory rather than the destination directory. ### `--use-builtin-age` [*bool*] > Configuration: `useBuiltinAge` Use chezmoi's builtin [age encryption][age] instead of an external `age` command. *value* can be `on`, `off`, `auto`, or any boolean-like value recognized by `promptBool`. The default is `auto` which will only use the builtin age if `age.command` cannot be found in `$PATH`. The builtin `age` command does not support passphrases, symmetric encryption, or the use of SSH keys. ### `--use-builtin-diff` [*bool*] Use chezmoi's builtin diff, even if the `diff.command` configuration variable is set. ### `--use-builtin-git` [*bool*] > Configuration: `useBuiltinGit` Use chezmoi's builtin git instead of `git.command` for the `init` and `update` commands. *value* can be `on`, `off`, `auto`, or any boolean-like value recognized by `promptBool`. The default is `auto` which will only use the builtin git if `git.command` cannot be found in `$PATH`. !!! info chezmoi's builtin git has only supports the HTTP and HTTPS transports and does not support `git-repo` externals. ### `-v`, `--verbose` Set verbose mode. In verbose mode, chezmoi prints the changes that it is making as approximate shell commands, and any differences in files between the target state and the destination set are printed as unified diffs. ### `--version` Print the version of chezmoi, the commit at which it was built, and the build timestamp. ### `-w`, `--working-tree` *directory* Use *directory* as the git working tree directory. By default, chezmoi searches the source directory and then its ancestors for the first directory that contains a `.git` directory. [configuration]: /reference/configuration-file/index.md [age]: https://age-encryption.org ================================================ FILE: assets/chezmoi.io/docs/reference/command-line-flags/index.md ================================================ # Command line flags Command line flags override any values set in the configuration file. ================================================ FILE: assets/chezmoi.io/docs/reference/commands/add.md ================================================ # `add` *target*... Add *target*s to the source state. If any target is already in the source state, then its source state is replaced with its current state in the destination directory. ## Flags ### `-a`, `--autotemplate` Automatically generate a template by replacing strings that match variable values from the `data` section of the config file with their respective config names as a template string. Longer substitutions occur before shorter ones. This implies the `--template` option. !!! warning `--autotemplate` uses a greedy algorithm which occasionally generates templates with unwanted variable substitutions. Carefully review any templates it generates. ### `--create` Add files that should exist, irrespective of their contents. Sets the `create_` source state attribute on the added file. A file will be created with the given contents if the file does not exist. If the file already exists, then its contents will not be changed. This allows for managing files with an initial state but should not be changed by chezmoi afterwards. ### `--encrypt` > Configuration: `add.encrypt` Encrypt files using the defined encryption method. ### `--exact` Set the `exact` attribute on added directories. !!! warning Directories with the `exact` attributes are statefully synced between target and source directories. When running `re-add`, any files deleted from the `exact` target directory will be removed from the source directory. Likewise, any files added to the `exact` target directory, will be added to the source directory ### `--follow` If the last part of a target is a symlink, add the target of the symlink instead of the symlink itself. ### `--new` Create a new file if the target does not exist. ### `-p`, `--prompt` Interactively prompt before adding each file. ### `-q`, `--quiet` Suppress warnings about adding ignored entries. ### `--secrets` `ignore`|`warning`|`error` > Configuration: `add.secrets` Action to take when a secret is found when adding a file. The default is `warning`. ### `-T`, `--template` Set the `template` attribute on added files and symlinks. ### `--template-symlinks` > Configuration: `add.templateSymlinks` When adding symlink to an absolute path in the source directory or destination directory, create a symlink template with `.chezmoi.sourceDir` or `.chezmoi.homeDir`. This is useful for creating portable absolute symlinks. ## Common flags ### `-x`, `--exclude` *types* --8<-- "common-flags/exclude.md" ### `-f`, `--force` Add *target*s, even if doing so would cause a source template to be overwritten. ### `-i`, `--include` *types* --8<-- "common-flags/include.md" ### `-r`, `--recursive` --8<-- "common-flags/recursive.md:default-true" ## Examples ```sh chezmoi add ~/.bashrc chezmoi add ~/.gitconfig --template chezmoi add ~/.ssh/id_rsa --encrypt chezmoi add ~/.vim --recursive chezmoi add ~/.oh-my-zsh --exact --recursive ``` ## Notes !!! bug `chezmoi add` will fail if the entry being added is in a directory implicitly created by an [external][external]. See [issue #1574][issue-1574] for details. !!! warning `chezmoi add --exact --recursive DIR` works in predictable but surprising ways and its use is not recommended for nested directories without taking precautions. If you have not previously added any files from `~/.config` to chezmoi and run `chezmoi add --exact --recursive ~/.config/nvim`, chezmoi will consider all files under `~/.config` to be managed, and any file *not* in `~/.config/nvim` will be removed on your next `chezmoi apply`. This is because `~/.config/nvim` is added as: ```text exact_dot_config/ exact_nvim/ exact_lua/ … … ``` To prevent this, add a `.keep` file *first* before adding the subdirectory recursively. ```sh touch ~/.config/.keep chezmoi add ~/.config/.keep chezmoi add --recursive --exact ~/.config/nvim ``` See [issue #4223][issue-4223] for details. [external]: /reference/special-files/chezmoiexternal-format.md [issue-1574]: https://github.com/twpayne/chezmoi/issues/1574 [issue-4223]: https://github.com/twpayne/chezmoi/issues/4223 ================================================ FILE: assets/chezmoi.io/docs/reference/commands/age-keygen.md ================================================ # `age-keygen` [*identity-file*] Generate an age identity or convert an age identity to an age recipient. ## Flags ### `--pq` Generate a post-quantum key pair. ### `-y`, `--convert` Read an identity file *identity-file* or the standard input and print its recipient instead of generating an age identity. ## Examples ```sh chezmoi age-keygen chezmoi age-keygen -o identity.txt chezmoi age-keygen -y identity.txt ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/age.md ================================================ # `age` Interact with age's passphrase-based encryption. ## Subcommands ### `age encrypt` [*file*...] Encrypt file or standard input. #### `-p`, `--passphrase` Decrypt with a passphrase. ### `age decrypt` [*file*...] Decrypt file or standard input. #### `-p`, `--passphrase` Decrypt with a passphrase. ## Examples ```sh chezmoi age encrypt --passphrase plaintext.txt > ciphertext.txt chezmoi age decrypt --passphrase ciphertext.txt > decrypted-ciphertext.txt ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/apply.md ================================================ # `apply` [*target*...] Ensure that *target*... are in the target state, updating them if necessary. If no targets are specified, the state of all targets are ensured. If a target has been modified since chezmoi last wrote it then the user will be prompted if they want to overwrite the file. ## Common flags ### `-x`, `--exclude` *types* --8<-- "common-flags/exclude.md" ### `-i`, `--include` *types* --8<-- "common-flags/include.md" ### `--init` --8<-- "common-flags/init.md" ### `-P`, `--parent-dirs` --8<-- "common-flags/parent-dirs.md" ### `-r`, `--recursive` --8<-- "common-flags/recursive.md:default-true" ### `--source-path` Specify targets by source path, rather than target path. This is useful for applying changes after editing. ## Examples ```sh chezmoi apply chezmoi apply --dry-run --verbose chezmoi apply ~/.bashrc ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/archive.md ================================================ # `archive` [*target*....] Generate an archive of the target state, or only the targets specified. This can be piped into `tar` to inspect the target state. ## Flags ### `-f`, `--format` *format* Write the archive in *format*. If `--output` is set the format is guessed from the extension, otherwise the default is `tar`. | Supported formats | | ----------------- | | `tar` | | `tar.gz` | | `tgz` | | `zip` | ### `-z`, `--gzip` Compress the archive with gzip. This is automatically set if the format is `tar.gz` or `tgz` and is ignored if the format is `zip`. ## Common flags ### `-x`, `--exclude` *types* --8<-- "common-flags/exclude.md" ### `-i`, `--include` *types* --8<-- "common-flags/include.md" ### `--init` --8<-- "common-flags/init.md" ### `-P`, `--parent-dirs` --8<-- "common-flags/parent-dirs.md" ### `-r`, `--recursive` --8<-- "common-flags/recursive.md:default-true" ## Examples ```sh chezmoi archive | tar tvf - chezmoi archive --output=dotfiles.tar.gz chezmoi archive --output=dotfiles.zip ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/cat-config.md ================================================ # `cat-config` Print the configuration file. ## Examples ```sh chezmoi cat-config ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/cat.md ================================================ # `cat` *target*... Write the target contents of *target*s to stdout. *target*s must be files, scripts, or symlinks. For files, the target file contents are written. For scripts, the script's contents are written. For symlinks, the target is written. ## Examples ```sh chezmoi cat ~/.bashrc ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/cd.md ================================================ # `cd` [*path*] Launch a shell in the working tree (typically the source directory). chezmoi will launch the command set by the `cd.command` configuration variable with any extra arguments specified by `cd.args`. If this is not set, chezmoi will attempt to detect your shell and finally fall back to an OS-specific default. If the optional argument *path* is present, the shell will be launched in the source directory corresponding to *path*. The shell will have various `CHEZMOI*` environment variables set, as for scripts. !!! hint This does not change the current directory of the current shell. To do that, instead use: ```console $ cd $(chezmoi source-path) ``` ## Examples ```sh chezmoi cd chezmoi cd ~ chezmoi cd ~/.config ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/chattr.md ================================================ # `chattr` *modifier* *target*... Change the attributes and/or type of *target*s. *modifier* specifies what to modify. See [attributes][attributes] for a description of each attribute. Add attributes by specifying them or their abbreviations directly, optionally prefixed with a plus sign (`+`). Remove attributes by prefixing them or their attributes with the string `no` or a minus sign (`-`). The available attribute modifiers and their abbreviations are: | Attribute modifier | Abbreviation | | ------------------ | ------------ | | `after` | `a` | | `before` | `b` | | `empty` | `e` | | `encrypted` | *none* | | `exact` | *none* | | `executable` | `x` | | `external` | *none* | | `once` | `o` | | `onchange` | *none* | | `private` | `p` | | `readonly` | `r` | | `remove` | *none* | | `template` | `t` | The type of a target can be changed using a type modifier: | Type modifier | | ------------- | | `create` | | `modify` | | `script` | | `symlink` | The negative form of type modifiers, e.g. `nocreate`, changes the target to be a regular file if it is of that type, otherwise the type is left unchanged. Multiple modifications may be specified by separating them with a comma (`,`). If you use the `-`*modifier* form then you must put *modifier* after a `--` to prevent chezmoi from interpreting `-`*modifier* as an option. ## Common flags ### `-r`, `--recursive` --8<-- "common-flags/recursive.md:default-false" ## Examples ```sh chezmoi chattr template ~/.bashrc chezmoi chattr noempty ~/.profile chezmoi chattr private,template ~/.netrc chezmoi chattr -- -x ~/.zshrc chezmoi chattr +create,+private ~/.kube/config ``` [attributes]: /reference/source-state-attributes.md ================================================ FILE: assets/chezmoi.io/docs/reference/commands/commands.go ================================================ // Package commands contains chezmoi's documentation for commands. package commands import "embed" // FS contains all docs. // //go:embed *.md var FS embed.FS ================================================ FILE: assets/chezmoi.io/docs/reference/commands/commands_test.go ================================================ package commands import ( "testing" "github.com/alecthomas/assert/v2" ) func TestFS(t *testing.T) { _, err := FS.ReadFile("add.md") assert.NoError(t, err) } ================================================ FILE: assets/chezmoi.io/docs/reference/commands/completion.md ================================================ # `completion` *shell* Generate shell completion code for the specified shell (`bash`, `fish`, `powershell`, or `zsh`). ## Examples ```sh chezmoi completion bash chezmoi completion fish --output=~/.config/fish/completions/chezmoi.fish ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/data.md ================================================ # `data` Write the computed template data to stdout. ## Common flags ### `-f`, `--format` `json`|`yaml` --8<-- "common-flags/format.md" ## Examples ```sh chezmoi data chezmoi data --format=yaml ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/decrypt.md ================================================ # `decrypt` [*file*...] Decrypt *file*s using chezmoi's configured encryption. If no files are given, decrypt the standard input. The decrypted result is written to the standard output or a file if the `--output` flag is set. ================================================ FILE: assets/chezmoi.io/docs/reference/commands/destroy.md ================================================ # `destroy` *target*... Remove *target* from the source state, the destination directory, and the state. !!! danger The `destroy` command permanently removes files both from your home directory and chezmoi's source directory. Only run `chezmoi destroy` if you have a separate backup of your home directory and your source directory. If you want chezmoi to stop managing the file use [`forget`][forget] instead. If you want to remove all traces of chezmoi from your system use [`purge`][purge] instead. ## Common flags ### `--force` Destroy without prompting. ### `-r`, `--recursive` --8<-- "common-flags/recursive.md:default-false" [forget]: /reference/commands/forget.md [purge]: /reference/commands/purge.md ================================================ FILE: assets/chezmoi.io/docs/reference/commands/diff.md ================================================ # `diff` [*target*...] Print the difference between the target state and the destination state for *target*s. If no targets are specified, print the differences for all targets. If a `diff.pager` command is set in the configuration file then the output will be piped into it. If `diff.command` is set then it will be invoked to show individual file differences with `diff.args` passed as arguments. Each element of `diff.args` is interpreted as a template with the variables `.Destination` and `.Target` available corresponding to the path of the file in the source and target state respectively. The default value of `diff.args` is `["{{ .Destination }}", "{{ .Target }}"]`. If `diff.args` does not contain any template arguments then `{{ .Destination }}` and `{{ .Target }}` will be appended automatically. ## Flags ### `--pager` *pager* > Configuration: `diff.pager` Pager to use for output. ### `--reverse` > Configuration: `diff.reverse` Reverse the direction of the diff, i.e. show the changes to the target required to match the destination. ### `--script-contents` Show script contents, defaults to `true`. ## Common flags ### `-x`, `--exclude` *types* --8<-- "common-flags/exclude.md" ### `-i`, `--include` *types* --8<-- "common-flags/include.md" ### `--init` --8<-- "common-flags/init.md" ### `-P`, `--parent-dirs` --8<-- "common-flags/parent-dirs.md" ### `-r`, `--recursive` --8<-- "common-flags/recursive.md:default-false" ## Examples ```sh chezmoi diff chezmoi diff ~/.bashrc ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/docker.md ================================================ # `docker` !!! Warning `docker` is an experimental command. !!! Hint To use [podman](https://podman.io/) instead of Docker, set the `docker.command` configuration variable to `podman`. You can also use the [`chezmoi podman` command][chezmoi-podman] instead of `chezmoi docker`. Install chezmoi, run `chezmoi init --apply`, and optionally execute your shell in [Docker](https://docker.com/) containers. ## Subcommands ### `exec` *container-id* *init-args*... Install chezmoi, run `chezmoi init --apply *init-args*`, and execute your shell in the existing Docker container *container-id*. #### Flags ##### `-i`, `--interactive` Keep stdin open even if not attached. ##### `-p`, `--package-manager` *package-manager* Install chezmoi using *package-manager*, if possible. Valid values for *package-manager* include `apk`, `apt-get`, `brew`, `dnf`, `nix-env`, `pacman`, `port`, `pkg`, `rpm`, `snap`, `xbps-install`, and `zypper`. Otherwise, fall back to `curl` or `wget` installation. If neither `curl` nor `wget` are installed then install them with *package-manager*. ##### `-s`, `--shell` After installing chezmoi, initializing your dotfiles, execute your shell. This is the default. ### `run` *image-id* *init-args*... Create a new Docker container using *image-id*, and in it, install chezmoi, run `chezmoi init --apply *init-args*`, and execute your shell. #### Flags ##### `-p`, `--package-manager` *package-manager* Install chezmoi using *package-manager*, if possible. Valid values for *package-manager* include `apk`, `apt-get`, `brew`, `dnf`, `nix-env`, `pacman`, `port`, `pkg`, `rpm`, `snap`, `xbps-install`, and `zypper`. Otherwise, fall back to `curl` or `wget` installation. If neither `curl` nor `wget` are installed then install them with *package-manager*. ## Examples ```sh chezmoi docker exec $CONTAINER_ID $GITHUB_USERNAME chezmoi docker run -p apk alpine:latest $GITHUB_USERNAME ``` [chezmoi-podman]: /reference/commands/podman.md ================================================ FILE: assets/chezmoi.io/docs/reference/commands/doctor.md ================================================ # `doctor` Check for potential problems. ## Flags ### `--no-network` Do not use any network connections. ## Examples ```sh chezmoi doctor ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/dump-config.md ================================================ # `dump-config` Dump the configuration. ## Common flags ### `-f`, `--format` `json`|`yaml` --8<-- "common-flags/format.md" ## Examples ```sh chezmoi dump-config ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/dump.md ================================================ # `dump` [*target*...] Dump the target state of *target*s. If no targets are specified, then the entire target state. ## Common flags ### `-x`, `--exclude` *types* --8<-- "common-flags/exclude.md" ### `-f`, `--format` `json`|`yaml` --8<-- "common-flags/format.md" ### `-i`, `--include` *types* --8<-- "common-flags/include.md" ### `--init` --8<-- "common-flags/init.md" ### `-P`, `--parent-dirs` --8<-- "common-flags/parent-dirs.md" ### `-r`, `--recursive` --8<-- "common-flags/recursive.md:default-true" ## Examples ```sh chezmoi dump ~/.bashrc chezmoi dump --format=yaml ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/edit-config-template.md ================================================ # `edit-config-template` Edit the configuration file template. If no configuration file template exists, then a new one is created with the contents of the current config file. ## Examples ```sh chezmoi edit-config-template ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/edit-config.md ================================================ # `edit-config` Edit the configuration file. ## Examples ```sh chezmoi edit-config ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/edit-encrypted.md ================================================ # `edit-encrypted` *filename*... Edit the encrypted files *filename*s. Each *filename* is decrypted to a temporary directory, the editor is invoked on the decrypted files. After the editor returns, each the decrypted file is re-encrypted. ## Examples ```sh chezmoi edit-encrypted encrypted_file ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/edit.md ================================================ # `edit` [*target*...] Edit the source state of *target*s, which must be files or symlinks. If no targets are given then the working tree of the source directory is opened. Encrypted files are decrypted to a private temporary directory and the editor is invoked with the decrypted file. When the editor exits the edited decrypted file is re-encrypted and replaces the original file in the source state. If the operating system supports hard links, then the edit command invokes the editor with filenames which match the target filename, unless the `edit.hardlink` configuration variable is set to `false` or the `--hardlink=false` command line flag is set. Templates preserve their `.tmpl` extension so editors can highlight them as templates. !!! hint Depending on your editor, you can set the format of a file in the file itself using a [modeline][modelines]. This can be useful if you want to syntax highlight a template as a different format. ## Flags ### `-a`, `--apply` > Configuration: `edit.apply` Apply the target immediately after editing. This is ignored if there are no targets, and does not apply scripts. ### `--hardlink` *bool* > Configuration: `edit.hardlink` Invoke the editor with a hard link to the source file with a name matching the target filename. This can help the editor determine the type of the file correctly. This is the default. !!! hint Creating hardlinks is not possible between different filesystems. Hence, if your [`tempDir`][tempdir] resides on a different filesystem (e.g. a [tmpfs][tmpfs], which is sometimes used for `/tmp`), this will not work. ### `--watch` > Configuration: `edit.watch` Automatically apply changes when files are saved, with the following limitations: * Only available when `chezmoi edit` is invoked with arguments (i.e. argument-free `chezmoi edit` is not supported). * All edited files are applied when any file is saved. * Only the edited files are watched, not any dependent files (e.g. `.chezmoitemplates` and `include`d files in templates are not watched). * Only works on operating systems supported by [fsnotify][fsnotify]. * Only works if `edit.hardlink` is enabled and works. ## Common flags ### `-x`, `--exclude` *types* --8<-- "common-flags/exclude.md" ### `-i`, `--include` *types* --8<-- "common-flags/include.md" ### `--init` --8<-- "common-flags/init.md" ## Examples ```sh chezmoi edit ~/.bashrc chezmoi edit ~/.bashrc --apply chezmoi edit ``` [fsnotify]: https://github.com/fsnotify/fsnotify [modelines]: https://vimhelp.org/options.txt.html#auto-setting [tempdir]: /reference/configuration-file/variables.md#tempdir [tmpfs]: https://en.wikipedia.org/wiki/Tmpfs ================================================ FILE: assets/chezmoi.io/docs/reference/commands/encrypt.md ================================================ # `encrypt` [*file*...] Encrypt *file*s using chezmoi's configured encryption. If no files are given, encrypt the standard input. The encrypted result is written to the standard output or a file if the `--output` flag is set. ================================================ FILE: assets/chezmoi.io/docs/reference/commands/execute-template.md ================================================ # `execute-template` [*template*...] Execute *template*s. This is useful for [testing templates][testing] or for calling chezmoi from other scripts. *templates* are interpreted as literal templates, with no whitespace added to the output between arguments. If no templates are specified, the template is read from stdin. ## Flags ### `-f`, `--file` Treat the arguments as filenames, not literal templates. ### `-i`, `--init` Include simulated functions only available during `chezmoi init`. ### `--left-delimiter` *delimiter* Set the left template delimiter. ### `--promptBool` *pairs* Simulate the `promptBool` template function with a function that returns values from *pairs*. *pairs* is a comma-separated list of *prompt*`=`*value* pairs. If `promptBool` is called with a *prompt* that does not match any of *pairs*, then it returns false. ### `--promptChoice` *pairs* Simulate the `promptChoice` template function with a function that returns values from *pairs*. *pairs* is a comma-separated list of *prompt*`=`*value* pairs. If `promptChoice` is called with a *prompt* that does not match any of *pairs*, then it returns *prompt* unchanged. ### `--promptInt` *pairs* Simulate the `promptInt` template function with a function that returns values from *pairs*. *pairs* is a comma-separated list of *prompt*`=`*value* pairs. If `promptInt` is called with a *prompt* that does not match any of *pairs*, then it returns zero. ### `--promptMultichoice` *pairs* Simulate the `promptMultichoice` template function with a function that returns values from *pairs*. *pairs* is a comma-separated list of *prompt*`=`*value* pairs. If `promptMultichoice` is called with a *prompt* that does not match any of *pairs*, then it returns *prompt* as an array. ### `-p`, `--promptString` *pairs* Simulate the `promptString` template function with a function that returns values from *pairs*. *pairs* is a comma-separated list of *prompt*`=`*value* pairs. If `promptString` is called with a *prompt* that does not match any of *pairs*, then it returns *prompt* unchanged. ### `--right-delimiter` *delimiter* Set the right template delimiter. ### `--stdinisatty` *bool* Simulate the `stdinIsATTY` function by returning *bool*. ### `--with-stdin` If run with arguments, then set `.chezmoi.stdin` to the contents of the standard input. ## Examples ```sh chezmoi execute-template '{{ .chezmoi.sourceDir }}' chezmoi execute-template '{{ .chezmoi.os }}' / '{{ .chezmoi.arch }}' echo '{{ .chezmoi | toJson }}' | chezmoi execute-template chezmoi execute-template --init --promptString email=me@home.org < ~/.local/share/chezmoi/.chezmoi.toml.tmpl ``` [testing]: /user-guide/templating.md#testing-templates ================================================ FILE: assets/chezmoi.io/docs/reference/commands/forget.md ================================================ # `forget` *target*... Remove *target*s from the source state, i.e. stop managing them. *target*s must have entries in the source state. They cannot be externals. ## Examples ```sh chezmoi forget ~/.bashrc ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/generate.md ================================================ # `generate` *output* Generates *output* for use with chezmoi. The currently supported *output*s are: | Output | Description | | ----------------------- | ----------------------------------------------------------------------------- | | `git-commit-message` | A git commit message, describing the changes to the source directory. | | `install.sh` | An install script, suitable for use with GitHub Codespaces | | `install-init-shell.sh` | A script which installs chezmoi, runs `chezmoi init`, and executes your shell | ## Examples ```sh chezmoi generate install.sh > install.sh chezmoi git -- commit -m "$(chezmoi generate git-commit-message)" chezmoi generate install-init-shell.sh $GITHUB_USERNAME ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/git.md ================================================ # `git` [*arg*...] Run `git` *args* in the working tree (typically the source directory). !!! note Flags in *args* must occur after `--` to prevent chezmoi from interpreting them. ## Examples ```sh chezmoi git add . chezmoi git add dot_gitconfig chezmoi git -- commit -m "Add .gitconfig" ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/help.md ================================================ # `help` [*command*...] Print the help associated with *command*, or general help if no command is given. ================================================ FILE: assets/chezmoi.io/docs/reference/commands/ignored.md ================================================ # `ignored` Print the list of entries ignored by chezmoi. ## Common flags ### `-0`, `--nul-path-separator` --8<-- "common-flags/nul-path-separator.md" ### `-t`, `--tree` --8<-- "common-flags/tree.md" ## Examples ```sh chezmoi ignored ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/import.md ================================================ # `import` *filename* Import the source state from an archive file in to a directory in the source state. This is primarily used to make subdirectories of your home directory exactly match the contents of a downloaded archive. You will generally always want to set the `--destination`, `--exact`, and `--remove-destination` flags. The supported archive formats are `rar`, `tar`, `tar.gz`, `tgz`, `tar.bz2`, `tbz2`, `txz`, `tar.zst`, and `zip`. ## Flags ### `-d`, `--destination` *directory* Set the destination (in the source state) where the archive will be imported. ### `--exact` Set the `exact` attribute on all imported directories. ### `-r`, `--remove-destination` Remove destination (in the source state) before importing. ### `--strip-components` *n* Strip *n* leading components from paths. ## Common flags ### `-x`, `--exclude` *types* --8<-- "common-flags/exclude.md" ### `-i`, `--include` *types* --8<-- "common-flags/include.md" ## Examples ```sh curl -s -L -o ${TMPDIR}/oh-my-zsh-master.tar.gz https://github.com/ohmyzsh/ohmyzsh/archive/master.tar.gz mkdir -p $(chezmoi source-path)/dot_oh-my-zsh chezmoi import --strip-components 1 --destination ~/.oh-my-zsh ${TMPDIR}/oh-my-zsh-master.tar.gz ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/index.md ================================================ # Commands This section provides documentation for chezmoi commands and their arguments. All commands accept [global flags][global], though some flags may have no effect on certain commands. Many commands also share [common flags][common]. [global]: /reference/command-line-flags/global.md [common]: /reference/command-line-flags/common.md ================================================ FILE: assets/chezmoi.io/docs/reference/commands/init.md ================================================ # `init` [*repo*] Setup the source directory, generate the config file, and optionally update the destination directory to match the target state. This is done in the following order: 1. The source directory is initialized. If chezmoi does not detect a Git repository in the source directory, chezmoi will clone the provided *repo* into the source directory. If no *repo* is provided, chezmoi will initialize a new Git repository. 2. If the initialized source directory contains a `.chezmoi.$FORMAT.tmpl` file, a new configuration file will be created using that file as a template. 3. If the `--apply` flag is provided, `chezmoi apply` is run. 4. If the `--purge` flag is provided, chezmoi will remove the source, config, and cache directories. 5. If the `--purge-binary` is passed, chezmoi will attempt to remove its own binary. By default, if *repo* is given, chezmoi will guess the full git repo URL, using HTTPS by default, or SSH if the `--ssh` option is specified, according to the following patterns: | Pattern | HTTPS Repo | SSH repo | | ------------------ | ------------------------------------------- | ---------------------------------- | | `user` | `https://user@github.com/user/dotfiles.git` | `git@github.com:user/dotfiles.git` | | `user/repo` | `https://user@github.com/user/repo.git` | `git@github.com:user/repo.git` | | `site/user/repo` | `https://user@site/user/repo.git` | `git@site:user/repo.git` | | `sr.ht/~user` | `https://user@git.sr.ht/~user/dotfiles` | `git@git.sr.ht:~user/dotfiles.git` | | `sr.ht/~user/repo` | `https://user@git.sr.ht/~user/repo` | `git@git.sr.ht:~user/repo.git` | To disable git repo URL guessing, pass the `--guess-repo-url=false` option. !!! info If you are using a different version control system, there are different steps [required for repo initialization][alt-vcs]. To prevent chezmoi from trying to clone or create a Git repository, add an empty `.git` directory to the source directory. ```sh mkdir -p ~/.local/share/chezmoi/.git ``` --8<-- "config-format.md" ## Flags ### `-a`, `--apply` Run `chezmoi apply` after checking out the repo and creating the config file. ### `--branch` *branch* Check out *branch* instead of the default branch. ### `-C`, `--config-path` *path* Write the generated config file to *path* instead of the default location. ### `--data` *bool* Include existing template data when creating the config file. This defaults to `true`. Set this to `false` to simulate creating the config file with no existing template data. ### `-d`, `--depth` *depth* Clone the repo with depth *depth*. ### `--git-lfs` *bool* Run `git lfs pull` after cloning the repo. ### `-g`, `--guess-repo-url` *bool* Guess the repo URL from the *repo* argument. This defaults to `true`. ### `--one-shot` `--one-shot` is the equivalent of `--apply`, `--depth=1`, `--force`, `--purge`, and `--purge-binary`. It attempts to install your dotfiles with chezmoi and then remove all traces of chezmoi from the system. This is useful for setting up temporary environments (e.g. Docker containers). ### `--prompt` Force the `prompt*Once` template functions to prompt. ### `--promptBool` *pairs* Populate the `promptBool` template function with values from *pairs*. *pairs* is a comma-separated list of *prompt*`=`*value* pairs. If `promptBool` is called with a *prompt* that does not match any of *pairs*, then it prompts the user for a value. ### `--promptChoice` *pairs* Populate the `promptChoice` template function with values from *pairs*. *pairs* is a comma-separated list of *prompt*`=`*value* pairs. If `promptChoice` is called with a *prompt* that does not match any of *pairs*, then it prompts the user for a value. ### `--promptDefaults` Make all `prompt*` template function calls with a default value return that default value instead of prompting. ### `--promptInt` *pairs* Populate the `promptInt` template function with values from *pairs*. *pairs* is a comma-separated list of *prompt*`=`*value* pairs. If `promptInt` is called with a *prompt* that does not match any of *pairs*, then it prompts the user for a value. ### `--promptMultichoice` *pairs* Populate the `promptMultichoice` template function with values from *pairs*. *pairs* is a comma-separated list of *prompt*`=`*value*[`/`*value*] pairs. If `promptMultichoice` is called with a *prompt* that does not match any of *pairs*, then it prompts the user for values. ### `--promptString` *pairs* Populate the `promptString` template function with values from *pairs*. *pairs* is a comma-separated list of *prompt*`=`*value* pairs. If `promptString` is called with a *prompt* that does not match any of *pairs*, then it prompts the user for a value. ### `-p`, `--purge` Remove the source and config directories after applying. ### `-P`, `--purge-binary` Attempt to remove the chezmoi binary after applying. ### `--recurse-submodules` *bool* Recursively clone submodules. This defaults to `true`. ### `--ssh` Guess an SSH repo URL instead of an HTTPS repo. ## Common flags ### `-x`, `--exclude` *types* --8<-- "common-flags/exclude.md" ### `-i`, `--include` *types* --8<-- "common-flags/include.md" ## Examples ```sh chezmoi init user chezmoi init user --apply chezmoi init user --apply --purge chezmoi init user/dots chezmoi init codeberg.org/user chezmoi init gitlab.com/user ``` [alt-vcs]: /user-guide/advanced/customize-your-source-directory.md#use-a-different-version-control-system-to-git ================================================ FILE: assets/chezmoi.io/docs/reference/commands/license.md ================================================ # `license` Print chezmoi's license. ## Examples ```sh chezmoi license ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/list.md ================================================ # `list` `list` is an alias for [`managed`][managed]. [managed]: /reference/commands/managed.md ================================================ FILE: assets/chezmoi.io/docs/reference/commands/manage.md ================================================ # `manage` *target*... `manage` is an alias for [`add`][add] for symmetry with [`unmanage`][unmanage]. [add]: /reference/commands/add.md [unmanage]: /reference/commands/unmanage.md ================================================ FILE: assets/chezmoi.io/docs/reference/commands/managed.md ================================================ # `managed` [*path*...] List all managed entries in the destination directory under all *path*s in alphabetical order. When no *path*s are supplied, list all managed entries in the destination directory in alphabetical order. ## Common flags ### `-x`, `--exclude` *types* --8<-- "common-flags/exclude.md" ### `-f`, `--format` `json`|`yaml` --8<-- "common-flags/format.md" ### `-i`, `--include` *types* --8<-- "common-flags/include.md" ### `-0`, `--nul-path-separator` --8<-- "common-flags/nul-path-separator.md" ### `-p`, `--path-style` *style* --8<-- "common-flags/path-style.md:all" ### `-t`, `--tree` --8<-- "common-flags/tree.md" ## Examples ```sh chezmoi managed chezmoi managed --include=files chezmoi managed --include=files,symlinks chezmoi managed -i dirs chezmoi managed -i dirs,files chezmoi managed -i files ~/.config chezmoi managed --exclude=encrypted --path-style=source-relative ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/merge-all.md ================================================ # `merge-all` Perform a three-way merge for file whose actual state does not match its target state. The merge is performed with `chezmoi merge`. ## Common flags ### `--init` --8<-- "common-flags/init.md" ### `-r`, `--recursive` --8<-- "common-flags/recursive.md:default-true" ## Examples ```sh chezmoi merge-all ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/merge.md ================================================ # `merge` *target*... Perform a three-way merge between the destination state, the target state, and the source state for each *target*. The merge tool is defined by the `merge.command` configuration variable, and defaults to `vimdiff`. If multiple targets are specified the merge tool is invoked separately and sequentially for each target. If the target state cannot be computed (for example if source is a template containing errors or an encrypted file that cannot be decrypted) a two-way merge is performed instead. The order of arguments to `merge.command` is set by `merge.args`. Each argument is interpreted as a template with the variables `.Destination`, `.Source`, and `.Target` available corresponding to the path of the file in the destination state, the source state, and the target state respectively. The default value of `merge.args` is `["{{ .Destination }}", "{{ .Source }}", "{{ .Target }}"]`. If `merge.args` does not contain any template arguments then `{{ .Destination }}`, `{{ .Source }}`, and `{{ .Target }}` will be appended automatically. ## Examples ```sh chezmoi merge ~/.bashrc ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/podman.md ================================================ # `podman` `podman` is an alias for [`docker`][docker]. [docker]: /reference/commands/docker.md ================================================ FILE: assets/chezmoi.io/docs/reference/commands/purge.md ================================================ # `purge` Remove chezmoi's configuration, state, and source directory, but leave the target state intact. ## Flags ### `-P`, `--binary` Purge chezmoi binary. ## Common flags ### `--force` Remove without prompting. ## Examples ```sh chezmoi purge chezmoi purge --force ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/re-add.md ================================================ # `re-add` [*target*...] Re-add modified files in the target state, preserving any `encrypted_` attributes. chezmoi will not overwrite templates, and all entries that are not files are ignored. Directories are recursed into by default. If no *target*s are specified then all modified files are re-added. If one or more *target*s are given then only those targets are re-added. ## Common flags ### `-x`, `--exclude` *types* --8<-- "common-flags/exclude.md" ### `-i`, `--include` *types* --8<-- "common-flags/include.md" ### `--re-encrypt` Re-encrypt encrypted files. ### `-r`, `--recursive` --8<-- "common-flags/recursive.md:default-true" ## Examples ```sh chezmoi re-add chezmoi re-add ~/.bashrc chezmoi re-add --recursive=false ~/.config/git ``` ## Notes !!! hint If you want to re-add a single file unconditionally, use `chezmoi add --force` instead. ================================================ FILE: assets/chezmoi.io/docs/reference/commands/remove.md ================================================ # `remove` The `remove` command has been removed. Use the [`forget` command][forget] or the [`destroy` command][destroy] instead. [forget]: /reference/commands/forget.md [destroy]: /reference/commands/destroy.md ================================================ FILE: assets/chezmoi.io/docs/reference/commands/rm.md ================================================ # `rm` The `rm` command has been removed. Use the [`forget` command][forget] or the [`destroy` command][destroy] instead. [forget]: /reference/commands/forget.md [destroy]: /reference/commands/destroy.md ================================================ FILE: assets/chezmoi.io/docs/reference/commands/secret.md ================================================ # `secret` Run a secret manager's CLI, passing any extra arguments to the secret manager's CLI. This is primarily for verifying chezmoi's integration with a custom secret manager. Normally you would use chezmoi's existing template functions to retrieve secrets. !!! note If you need to pass flags to the secret manager's CLI you must separate them with `--` to prevent chezmoi from interpreting them. ## Subcommands ### `secret keyring delete` #### `--service` *string* Name of the service. #### `--user` *string* Name of the user. ### `secret keyring get` #### `--service` *string* Name of the service. #### `--user` *string* Name of the user. ### `secret keyring set` #### `--service` *string* Name of the service. #### `--user` *string* Name of the user. #### `--value` *string* New value. ## Examples ```sh chezmoi secret keyring set --service=service --user=user --value=password chezmoi secret keyring get --service=service --user=user chezmoi secret keyring delete --service=service --user=user ``` ## Notes !!! warning On FreeBSD, the `secret keyring` command is only available if chezmoi was compiled with cgo enabled. The official release binaries of chezmoi are **not** compiled with cgo enabled, and `secret keyring` command is not available. ================================================ FILE: assets/chezmoi.io/docs/reference/commands/source-path.md ================================================ # `source-path` [*target*...] Print the path to each target's source state. If no targets are specified then print the source directory. ## Examples ```sh chezmoi source-path chezmoi source-path ~/.bashrc ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/ssh.md ================================================ # `ssh` *host* *init-args*... SSH to *host*, install chezmoi, run `chezmoi init --apply *init-args*`, and executes your shell. !!! Warning `ssh` is an experimental, potentially destructive, command. ## Flags ### `-p`, `--package-manager` *package-manager* Install chezmoi using *package-manager*, if possible. Valid values for *package-manager* include `apk`, `apt-get`, `brew`, `dnf`, `nix-env`, `pacman`, `port`, `pkg`, `rpm`, `snap`, `xbps-install`, and `zypper`. Otherwise, fall back to `curl` or `wget` installation. If neither `curl` nor `wget` are installed then install them with *package-manager*. ### `-s`, `--shell` After installing chezmoi, initializing your dotfiles, execute your shell. This is the default. ## Examples ```sh chezmoi ssh $HOSTNAME $GITHUB_USERNAME chezmoi ssh $HOSTNAME -- --one-shot $GITHUB_USERNAME ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/state.md ================================================ # `state` Manipulate the persistent state. !!! hint To get a full list of subcommands run: ```console $ chezmoi state help ``` ## Subcommands ### `data` Print the raw data in the persistent state. ### `delete` Delete a value from the persistent state. ### `delete-bucket` Delete a bucket from the persistent state. ### `dump` Generate a dump of the persistent state. ### `get` Get a value from the persistent state. ### `get-bucket` Get a bucket from the persistent state. ### `reset` Reset the persistent state. ### `set` Set a value from the persistent state ## Examples ```sh chezmoi state data chezmoi state delete --bucket=$BUCKET --key=$KEY chezmoi state delete-bucket --bucket=$BUCKET chezmoi state dump chezmoi state get --bucket=$BUCKET --key=$KEY chezmoi state get-bucket --bucket=$BUCKET chezmoi state set --bucket=$BUCKET --key=$KEY --value=$VALUE chezmoi state reset ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/status.md ================================================ # `status` Print the status of the files and scripts managed by chezmoi in a format similar to [`git status`][git-status]. The first column of output indicates the difference between the last state written by chezmoi and the actual state. The second column indicates the difference between the actual state and the target state, and what effect running [`chezmoi apply`][apply] will have. | Character | Meaning | First column | Second column | | --------- | --------- | ------------------ | ---------------------- | | Space | No change | No change | No change | | `A` | Added | Entry was created | Entry will be created | | `D` | Deleted | Entry was deleted | Entry will be deleted | | `M` | Modified | Entry was modified | Entry will be modified | | `R` | Run | Not applicable | Script will be run | ## Common flags ### `-x`, `--exclude` *types* --8<-- "common-flags/exclude.md" ### `-i`, `--include` *types* --8<-- "common-flags/include.md" ### `--init` --8<-- "common-flags/init.md" ### `-P`, `--parent-dirs` --8<-- "common-flags/parent-dirs.md" ### `-p`, `--path-style` *style* --8<-- "common-flags/path-style.md:all" ### `-r`, `--recursive` --8<-- "common-flags/recursive.md:default-true" ## Examples ```sh chezmoi status ``` [git-status]: https://git-scm.com/docs/git-status [apply]: /reference/commands/apply.md ================================================ FILE: assets/chezmoi.io/docs/reference/commands/target-path.md ================================================ # `target-path` [*source-path*...] Print the target path of each source path. If no source paths are specified then print the target directory. ## Examples ```sh chezmoi target-path chezmoi target-path ~/.local/share/chezmoi/dot_zshrc ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/unmanage.md ================================================ # `unmanage` *target*... `unmanage` is an alias for [`forget`][forget] for symmetry with [`manage`][manage]. [forget]: /reference/commands/forget.md [manage]: /reference/commands/manage.md ================================================ FILE: assets/chezmoi.io/docs/reference/commands/unmanaged.md ================================================ # `unmanaged` [*path*...] List all unmanaged files in *path*s. When no *path*s are supplied, list all unmanaged files in the destination directory. It is an error to supply *path*s that are not found on the file system. ## Common flags ### `-x`, `--exclude` *types* --8<-- "common-flags/exclude.md" ### `-i`, `--include` *types* --8<-- "common-flags/include.md" ### `-0`, `--nul-path-separator` --8<-- "common-flags/nul-path-separator.md" ### `-p`, `--path-style` *style* --8<-- "common-flags/path-style.md:no-source-tree" ### `-t`, `--tree` --8<-- "common-flags/tree.md" ## Examples ```sh chezmoi unmanaged chezmoi unmanaged ~/.config/chezmoi ~/.ssh ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/update.md ================================================ # `update` Pull changes from the source repo and apply any changes. If `update.command` is set then chezmoi will run `update.command` with `update.args` in the working tree. Otherwise, chezmoi will run `git pull --autostash --rebase [--recurse-submodules]` , using chezmoi's builtin git if `useBuiltinGit` is `true` or if `git.command` cannot be found in `$PATH`. ## Flags ### `-a`, `--apply` Apply changes after pulling, `true` by default. Can be disabled with `--apply=false`. ### `--recurse-submodules` Update submodules recursively. This defaults to `true`. Can be disabled with `--recurse-submodules=false`. ## Common flags ### `-x`, `--exclude` *types* --8<-- "common-flags/exclude.md" ### `-i`, `--include` *types* --8<-- "common-flags/include.md" ### `--init` --8<-- "common-flags/init.md" ### `-P`, `--parent-dirs` --8<-- "common-flags/parent-dirs.md" ### `-r`, `--recursive` --8<-- "common-flags/recursive.md:default-true" ## Examples ```sh chezmoi update ``` ================================================ FILE: assets/chezmoi.io/docs/reference/commands/upgrade.md ================================================ # `upgrade` Upgrade chezmoi by downloading and installing the latest released version. This will call the GitHub API to determine if there is a new version of chezmoi available, and if so, download and attempt to install it in the same way as chezmoi was previously installed. If the any of the `$CHEZMOI_GITHUB_ACCESS_TOKEN`, `$CHEZMOI_GITHUB_TOKEN`, `$GITHUB_ACCESS_TOKEN`, or `$GITHUB_TOKEN` environment variables are set, then the first value found will be used to authenticate requests to the GitHub API, otherwise unauthenticated requests are used which are subject to stricter [rate limiting][rate]. Unauthenticated requests should be sufficient for most cases. !!! warning If you installed chezmoi using a package manager, the `upgrade` command might have been removed by the package maintainer. ## Flags ### `--executable` *filename* Set name of executable to replace. ### `--method` *method* Override the upgrade method that was automatically detected by chezmoi. !!! danger This flag should only be used when recommended by chezmoi developers. | Methods | Description | | ---------------------- | ---------------------------------------------------------------------------------- | | `brew-upgrade` | Run `brew upgrade chezmoi`. | | `replace-executable` | Download the latest released executable from Github. | | `snap-refresh` | Run `snap refresh chezmoi`. | | `sudo-upgrade-package` | Same as `upgrade-package` but use `sudo`. | | `upgrade-package` | Download and install `.apk`, `.deb` or `.rpm` package. Run `pacman` on Arch Linux. | [rate]: https://developer.github.com/v3/#rate-limiting ================================================ FILE: assets/chezmoi.io/docs/reference/commands/verify.md ================================================ # `verify` [*target*...] Verify that all *target*s match their target state. chezmoi exits with code 0 (success) if all targets match their target state, or 1 (failure) otherwise. If no targets are specified then all targets are checked. ## Common flags ### `-x`, `--exclude` *types* --8<-- "common-flags/exclude.md" ### `-i`, `--include` *types* --8<-- "common-flags/include.md" ### `--init` --8<-- "common-flags/init.md" ### `-P`, `--parent-dirs` --8<-- "common-flags/parent-dirs.md" ### `-r`, `--recursive` --8<-- "common-flags/recursive.md:default-true" ## Examples ```sh chezmoi verify chezmoi verify ~/.bashrc ``` ================================================ FILE: assets/chezmoi.io/docs/reference/concepts.md ================================================ # Concepts chezmoi computes the target state for the current machine and then updates the destination directory, where: * The *destination directory* is the directory that chezmoi manages, usually your home directory, `~`. * A *target* is a file, directory, or symlink in the destination directory. * The *destination state* is the current state of all the targets in the destination directory. * The *source state* declares the desired state of your home directory, including templates that use machine-specific data. It contains only regular files and directories. * The *source directory* is where chezmoi stores the source state. By default it is `~/.local/share/chezmoi`. * The *config file* contains machine-specific data. By default it is `~/.config/chezmoi/chezmoi.toml`. * The *target state* is the desired state of the destination directory. It is computed from the source state, the config file, and the destination state. The target state includes regular files and directories, and may also include symbolic links, scripts to be run, and targets to be removed. * The *working tree* is the git working tree. Normally it is the same as the source directory, but can be a parent of the source directory. ================================================ FILE: assets/chezmoi.io/docs/reference/configuration-file/editor.md ================================================ # Editor The editor used is the first non-empty string of the `edit.command` configuration variable, the `$VISUAL` environment variable, the `$EDITOR` environment variable. If none are set then chezmoi falls back to `notepad.exe` on Windows systems and `vi` on non-Windows systems. When the `edit.command` configuration variable is used, extra arguments can be passed to the editor with the `edit.args` configuration variable. chezmoi will emit a warning if the editor returns in less than `edit.minDuration` (default `1s`). To disable this warning, set `edit.minDuration` to `0`. ================================================ FILE: assets/chezmoi.io/docs/reference/configuration-file/hooks.md ================================================ # Hooks Hook commands are executed before and after events. Unlike scripts, hooks are always run, even if `--dry-run` is specified. Hooks should be fast and idempotent. The following events are defined: | Event | Trigger | | --------------------- | --------------------------------------------- | | *command*, e.g. `add` | Running `chezmoi command`, e.g. `chezmoi add` | | `git-auto-commit` | Generating an automatic git commit | | `git-auto-push` | Running an automatic git push | | `read-source-state` | Reading the source state | Each event can have a `.pre` and/or a `.post` command. The *event*.`pre` command is executed before *event* occurs and the *event*`.post` command is executed after *event* has occurred. A command contains a `command` or `script` and an optional array of strings `args`. `command`s are executed directly. `script`s are executed with configured interpreter for the script's extension, see the [section on interpreters][interpreters]. !!! example ```toml title="~/.config/chezmoi/chezmoi.toml" [hooks.read-source-state.pre] command = "echo" args = ["pre-read-source-state-hook"] [hooks.apply.post] command = "echo" args = ["post-apply-hook"] [hooks.add.post] script = "post-add-hook.ps1' ``` When running hooks, the `CHEZMOI=1` and `CHEZMOI_*` environment variables will be set. `CHEZMOI_COMMAND` is set to the chezmoi command being run, `CHEZMOI_COMMAND_DIR` is set to the directory where chezmoi was run from, and `CHEZMOI_ARGS` contains the full arguments to chezmoi, starting with the path to chezmoi's executable. [interpreters]: /reference/configuration-file/interpreters.md ================================================ FILE: assets/chezmoi.io/docs/reference/configuration-file/index.md ================================================ # Configuration file chezmoi searches for its configuration file according to the [XDG Base Directory Specification][xdg]. The base name of the config file is `chezmoi`. If multiple configuration file formats are present, chezmoi will report an error. --8<-- "config-format.md" In most installations, the config file will be read from `$HOME/.config/chezmoi/chezmoi.$FORMAT` (`%USERPROFILE%/.config/chezmoi/chezmoi.$FORMAT`), where `$FORMAT` is one of `json`, `jsonc`, `toml`, or `yaml`. The config file can be set explicitly with the `--config` command line option. By default, the format is detected based on the extension of the config file name, but can be overridden with the `--config-format` command line option. ## Examples === "JSON" ```json title="~/.config/chezmoi/chezmoi.json" { "sourceDir": "/home/user/.dotfiles", "git": { "autoPush": true } } ``` === "JSONC" ```jsonc title="~/.config/chezmoi/chezmoi.jsonc" { // The chezmoi source files are stored here "sourceDir": "/home/user/.dotfiles", "git": { "autoPush": true } } ``` === "TOML" ```toml title="~/.config/chezmoi/chezmoi.toml" sourceDir = "/home/user/.dotfiles" [git] autoPush = true ``` === "YAML" ```yaml title="~/.config/chezmoi/chezmoi.yaml" sourceDir: /home/user/.dotfiles git: autoPush: true ``` [xdg]: https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html [json]: https://www.json.org/json-en.html [toml]: https://github.com/toml-lang/toml [yaml]: https://yaml.org/ ================================================ FILE: assets/chezmoi.io/docs/reference/configuration-file/interpreters.md ================================================ # Interpreters The execution of scripts and hooks on Windows depends on the file extension. Windows will natively execute scripts with a `.bat`, `.cmd`, `.com`, and `.exe` extensions. Other extensions require an interpreter, which must be in your `%PATH%`. The default script interpreters are: | Extension | Command | Arguments | | --------- | ------------ | --------------- | | `.nu` | `nu` | *none* | | `.pl` | `perl` | *none* | | `.py` | `python3` | *none* | | `.ps1` | `pwsh` | `-NoLogo -File` | | `.rb` | `ruby` | *none* | Script interpreters can be added or overridden by adding the corresponding extension (without the leading dot) as a key under the `interpreters` section of the configuration file. !!! note The leading `.` is dropped from *extension*, for example to specify the interpreter for `.pl` files you configure `interpreters.pl` (where `.` in this case just means "a child of" in the configuration file, however that is specified in your preferred format). !!! example To change the Python interpreter to `C:\Python39\python3.exe` and add a Tcl/Tk interpreter, include the following in your config file: ```toml title="~/.config/chezmoi/chezmoi.toml" [interpreters.py] command = 'C:\Python39\python3.exe' [interpreters.tcl] command = "tclsh" ``` Or if using YAML: ```yaml title="~/.config/chezmoi/chezmoi.yaml" interpreters: py: command: "C:\Python39\python3.exe" tcl: command: "tclsh" ``` Note that the TOML version can also be written like this, which resembles the YAML version more and makes it clear that the key for each file extension should not have a leading `.`: ```toml title="~/.config/chezmoi/chezmoi.toml" [interpreters] py = { command = 'C:\Python39\python3.exe' } tcl = { command = "tclsh" } ``` !!! info "PowerShell Core Installation" PowerShell Core (`pwsh`) must be installed separately on most systems. If you don't have it installed: - **Windows**: Download from [PowerShell releases](https://github.com/PowerShell/PowerShell/releases) or install via `winget install Microsoft.PowerShell` - **Linux/macOS**: Follow instructions at [Installing PowerShell](https://learn.microsoft.com/powershell/scripting/install/installing-powershell) !!! note chezmoi defaults to PowerShell Core (`pwsh`) for `.ps1` scripts as it provides better UTF-8 support and cross-platform compatibility. On Windows, if `pwsh` is not available, chezmoi will automatically fall back to Windows PowerShell (`powershell`). To explicitly use Windows PowerShell instead of the automatic selection, include the following in your config file: ```toml title="~/.config/chezmoi/chezmoi.toml" [interpreters.ps1] command = "powershell" args = ["-NoLogo"] ``` If the script in the source state is a template (with a `.tmpl` extension), then chezmoi will strip the `.tmpl` extension and use the next remaining extension to determine the interpreter to use. ================================================ FILE: assets/chezmoi.io/docs/reference/configuration-file/pinentry.md ================================================ # pinentry By default, chezmoi will request passwords from the terminal. If the `--no-tty` option is passed, then chezmoi will instead read passwords from the standard input. Otherwise, if the configuration variable `pinentry.command` is set then chezmoi will instead used the given command to read passwords, assuming that it follows the [Assuan protocol (PDF)][assuan] like [GnuPG's pinentry][pinentry]. The configuration variable `pinentry.args` specifies extra arguments to be passed to `pinentry.command` and the configuration variable `pinentry.options` specifies extra options to be set. The default `pinentry.options` is `["allow-external-password-cache"]`. !!! example ```toml title="~/.config/chezmoi/chezmoi.toml" [pinentry] command = "pinentry" ``` [assuan]: https://www.gnupg.org/documentation/manuals/assuan.pdf [pinentry]: https://www.gnupg.org/related_software/pinentry/index.html ================================================ FILE: assets/chezmoi.io/docs/reference/configuration-file/textconv.md ================================================ # textconv A section called `textconv` in the configuration file controls how file contents are modified before being passed to diff. The `textconv` must contain an array of objects where each object has the following properties: | Name | Type | Description | | --------- | -------- | ----------------------------- | | `pattern` | string | Target path pattern to match | | `command` | string | Command to transform contents | | `args` | []string | Extra arguments to command | Files whose target path matches `pattern` are transformed by passing them to the standard input of `command` with `args`, and new contents are read from the command's standard output. If a target path does not match any patterns then the file contents are passed unchanged to diff. If a target path matches multiple patterns then element with the longest `pattern` is used. ================================================ FILE: assets/chezmoi.io/docs/reference/configuration-file/umask.md ================================================ # umask By default, chezmoi uses your current umask as set by your operating system and shell. chezmoi only stores crude permissions in its source state, namely in the `executable` and `private` attributes, corresponding to the umasks of `0o111` and `0o077` respectively. For machine-specific control of umask, set the `umask` configuration variable in chezmoi's configuration file. !!! example ```toml title="~/.config/chezmoi/chezmoi.toml" umask = 0o22 ``` ================================================ FILE: assets/chezmoi.io/docs/reference/configuration-file/variables.md.tmpl ================================================ # Variables {{ $lastSectionValue := "" -}} {{ range $sectionName, $variables := .sections -}} {{ $sectionValue := "" -}} {{ if $sectionName -}} ## `{{ $sectionName }}` {{ else -}} ## Top level {{ end -}} {{ range $variableName, $variable := $variables -}} {{ with $variable -}} {{ $nameValue := $variableName -}} {{ if regexMatch "\\A\\w+\\z" $nameValue -}} {{ $nameValue = printf "`%s`" $nameValue -}} {{ end -}} {{ $defaultValue := "*none*" -}} {{ if eq .type "bool" -}} {{ $defaultValue = "`false`" -}} {{ end -}} {{ $slug := $variableName | slugify -}} {{ if $sectionName -}} {{ $slug = printf "%s.%s" $sectionName $variableName | slugify -}} {{ end -}} {{ $title := printf "`%s.%s`" $sectionName $variableName -}} {{ if not $sectionName -}} {{ $title = $variableName -}} {{ else if contains "`" $variableName -}} {{ $title = printf "`%s.`%s" $sectionName $variableName -}} {{ end -}} {{ $defaultValue := "*none*" -}} {{ if eq .type "bool" -}} {{ $defaultValue = "`false`" -}} {{ end -}} ### {{ $title }}{{ if .deprecated }} (deprecated){{ end }} { #{{$slug}} } | Type | Default | | - | - | | `{{ .type | default "string" }}` | {{ .default | default $defaultValue }} | {{ .description }} {{ end -}} {{ end -}} {{ end -}} ================================================ FILE: assets/chezmoi.io/docs/reference/configuration-file/variables.md.yaml ================================================ sections: '': cacheDir: default: '`$XDG_CACHE_HOME/chezmoi` / `$HOME/.cache/chezmoi` / `%USERPROFILE%/.cache/chezmoi`' description: Cache directory. color: default: '`auto`' description: Colorize output. data: type: object description: Template data. destDir: default: '`$HOME` / `%USERPROFILE%`' description: Destination directory. encryption: description: Encryption type, either `age`, `gpg`, or `transparent`. env: type: object description: Extra environment variables for scripts and commands. format: default: '`json`' description: Format for data output, either `json` or `yaml`. interactive: default: '`false`' description: Prompt for all changes. mode: default: '`file`' description: Mode in target dir, either `file` or `symlink`. pager: default: '`$PAGER`' description: Default pager CLI command. pagerArgs: type: '[]string' description: Extra args to the pager command. persistentState: default: '`$XDG_CONFIG_HOME/chezmoi/chezmoi.boltdb` / `$HOME/.config/chezmoi/chezmoi.boltdb` / `%USERPROFILE%/.config/chezmoi/chezmoi.boltdb`' description: Location of the persistent state file. progress: type: bool description: Display progress bars. scriptEnv: type: object description: Extra environment variables for scripts, hooks, and commands. scriptTempDir: description: Temporary directory for scripts. sourceDir: default: '`$XDG_SHARE_HOME/chezmoi` / `$HOME/.local/share/chezmoi` / `%USERPROFILE%/.local/share/chezmoi`' description: Source directory. tempDir: default: '*from system*' description: Temporary directory. umask: type: int default: '*from system*' description: Umask. useBuiltinAge: default: '`auto`' description: Use builtin age if `age` command is not found in `$PATH`. useBuiltinGit: default: '`auto`' description: Use builtin git if `git` command is not found in `$PATH`. verbose: type: bool description: Make output more verbose. workingTree: default: '*source directory*' description: git working tree directory. add: encrypt: type: bool description: Encrypt by default. secrets: default: '`warning`' description: Action when secrets are found when adding files. templateSymlinks: type: bool description: Template symlinks to source and home dirs. age: args: type: '[]string' description: Extra args to age CLI command. command: default: '`age`' description: age CLI command. identity: description: age identity file. identities: type: '[]string' description: age identity files. passphrase: type: bool description: Use age passphrase instead of identity. recipient: description: age recipient. recipients: type: '[]string' description: age recipients. recipientsFile: description: age recipients file. recipientsFiles: type: '[]string' description: age recipients files. suffix: default: '`.age`' description: Suffix appended to age-encrypted files. symmetric: type: bool description: Use age symmetric encryption. awsSecretsManager: profile: description: AWS shared profile name. region: description: AWS region. azureKeyVault: defaultVault: description: Default Azure Key Vault name. bitwarden: command: default: '`bw`' description: Bitwarden CLI command. unlock: type: bool description: Whether to unlock the Bitwarden CLI. bitwardenSecrets: command: default: '`bws`' description: Bitwarden Secrets CLI command. cd: args: type: '[]string' description: Extra args to shell in `cd` command. command: description: Shell to run in `cd` command. completion: custom: type: bool description: Enable custom shell completions. dashlane: args: type: '[]string' description: Extra args to Dashlane CLI command. command: default: '`dcli`' description: Dashlane CLI command. diff: args: type: '[]string' default: '*see [`diff`](/user-guide/tools/diff.md)*' description: Extra args to external diff command. command: description: External diff command. exclude: type: '[]string' description: Entry types to exclude from diffs. pager: description: Diff-specific pager. pagerArgs: type: '[]string' description: Extra args to the diff-specific pager command. reverse: type: bool description: Reverse order of arguments to diff. scriptContents: type: bool default: '`true`' description: Show script contents. docker: command: default: '`docker`' description: Docker CLI command. doppler: args: type: '[]string' description: Extra args to Doppler CLI command. command: default: '`doppler`' description: Doppler CLI command. config: type: string description: Default config (aka environment) if none is specified. project: type: string description: Default project name if none is specified. edit: apply: type: bool description: Apply changes on exit. args: type: '[]string' description: Extra args to edit command. command: default: '`$EDITOR` / `$VISUAL`' description: Edit command. hardlink: type: bool default: '`true`' description: Invoke editor with a hardlink to the source file. minDuration: type: duration default: '`1s`' description: Minimum duration for edit command. watch: type: bool description: Automatically apply changes when files are saved. ejson: keyDir: type: string default: '`/opt/ejson/keys`' description: Path to directory containing private keys. Setting the `$EJSON_KEYDIR` environment variable will also set this value, with lower precedence. key: type: string description: The private key to use for decryption, will supersede using the `keyDir` if set. git: autoAdd: type: bool description: Add changes to the source state after any change. autoCommit: type: bool description: Commit changes to the source state after any change. autoPush: type: bool description: Push changes to the source state after any change. command: default: '`git`' description: git CLI command. commitMessageTemplate: type: string description: Commit message template. commitMessageTemplateFile: type: string description: Commit message template file (relative to source directory). lfs: type: bool description: Run `git lfs pull` after cloning. gitHub: refreshPeriod: type: duration default: '`1m`' description: Minimum duration between identical GitHub API requests. gopass: command: default: '`gopass`' description: gopass CLI command. mode: description: See [gopass functions](/reference/templates/gopass-functions/index.md). gpg: args: type: '[]string' description: Extra args to GPG CLI command. command: default: '`gpg`' description: GPG CLI command. recipient: description: GPG recipient. recipients: type: '[]string' description: GPG recipients. suffix: default: '`.asc`' description: Suffix appended to GPG-encrypted files. symmetric: type: bool description: Use symmetric GPG encryption. hooks: '*command*`.post.args`': type: '[]string' description: Extra arguments to command to run after *command*. '*command*`.post.command`': type: '[]string' description: Command to run after *command*. '*command*`.pre.args`': type: '[]string' description: Extra arguments to command to run before *command*. '*command*`.pre.command`': type: '[]string' description: Command to run before *command*. interpreters: '*extension*.`args`': type: '[]string' description: See [Interpreters](/reference/configuration-file/interpreters.md). '*extension*.`command`': default: '*special*' description: See [Interpreters](/reference/configuration-file/interpreters.md). keepassxc: args: type: '[]string' description: Extra args to KeePassXC CLI command. command: default: '`keepassxc-cli`' description: KeePassXC CLI command. database: description: KeePassXC database. mode: default: '`cache-password`' description: See [KeePassXC functions](/reference/templates/keepassxc-functions/index.md). prompt: type: bool default: '`true`' description: Prompt for password. keeper: args: type: '[]string' description: Extra args to Keeper CLI command. command: default: '`keeper`' description: Keeper CLI command. lastpass: command: default: '`lpass`' description: LastPass CLI command. merge: args: type: '[]string' default: See [`merge`](/user-guide/tools/merge.md) description: Extra args to three-way merge CLI command. command: description: Three-way merge CLI command. onepassword: cache: type: bool default: '`true`' description: Enable optional caching provided by `op`. command: default: '`op`' description: 1Password CLI command. prompt: type: bool default: '`true`' description: Prompt for sign-in when no valid session is available. mode: default: '`account`' description: See [1Password Secrets Automation](/user-guide/password-managers/1password.md#secrets-automation). pass: command: default: '`pass`' description: Pass CLI command. passhole: args: type: '[]string' description: Extra args to Passhole CLI command. command: default: '`ph`' description: Passhole CLI command. prompt: type: bool default: '`true`' description: Prompt for password. protonPass: command: default: '`pass-cli`' description: Proton Pass CLI command. pinentry: args: type: '[]string' description: Extra args to pinentry CLI command. command: description: pinentry CLI command. options: type: '[]string' default: See [`pinentry`](/reference/configuration-file/pinentry.md) description: Extra options for pinentry. rbw: command: default: '`rbw`' description: Unofficial Bitwarden CLI command. secret: args: type: '[]string' description: Extra args to secret CLI command. command: description: Generic secret CLI command. status: exclude: type: '[]string' description: Entry types to exclude from status. pathStyle: type: string default: '`relative`' description: How to present the path to files in status output. template: options: type: '[]string' default: '`["missingkey=error"]`' description: Template options. textconv: '': type: '[]object' description: See [textconv](/reference/configuration-file/textconv.md). vault: command: default: '`vault`' description: Vault CLI command. update: apply: type: bool default: '`true`' description: Apply after pulling. args: type: '[]string' description: Extra args to update command. command: description: Update command. recurseSubmodules: type: bool default: '`true`' description: Update submodules recursively. verify: exclude: type: '[]string' description: Entry types to exclude from verify. warnings: '': type: object description: See [Warnings](/reference/configuration-file/warnings.md). ================================================ FILE: assets/chezmoi.io/docs/reference/configuration-file/warnings.md ================================================ # Warnings By default, chezmoi will warn you when it encounters potential problems. Some of these warnings can be suppressed by setting values in configuration file. | Variable | Type | Default | Description | | ------------------------------ | ---- | ------- | ---------------------------------------------- | | `configFileTemplateHasChanged` | bool | `true` | Warn when the config file template has changed | !!! example ```toml title="~/.config/chezmoi/chezmoi.toml" [warnings] configFileTemplateHasChanged = false ``` ================================================ FILE: assets/chezmoi.io/docs/reference/index.md ================================================ # Reference Welcome to the reference section of the chezmoi documentation. This reference covers the following topics: - [Key concepts](/reference/concepts.md) - [Source state attributes](/reference/source-state-attributes.md) - [Target types](/reference/target-types.md) - [Application order of changes](/reference/application-order.md) - [Configuration file](/reference/configuration-file/index.md) - [Special files](/reference/special-files/index.md) and [special directories](/reference/special-directories/index.md) - [Command line flags](/reference/command-line-flags/index.md) - [Commands](/reference/commands/index.md) - [Templates](/reference/templates/index.md) - [Variables](/reference/templates/variables.md) - [Directives](/reference/templates/directives.md) - [Functions](/reference/templates/functions/index.md) - [Plugins](/reference/plugins.md) - [Release history](/reference/release-history.md) ================================================ FILE: assets/chezmoi.io/docs/reference/plugins.md ================================================ # Plugins chezmoi supports plugins, similar to git. If you run `chezmoi command` where *command* is not a builtin chezmoi command then chezmoi will look for a binary called `chezmoi-command` in your `$PATH`. If such a binary is found then chezmoi will execute it. Otherwise, chezmoi will report an unknown command error. ================================================ FILE: assets/chezmoi.io/docs/reference/release-history.md.tmpl ================================================ # Release history {{- $releases := gitHubListReleases "twpayne/chezmoi" }} {{- $latestRelease := index $releases 0 }} [Upcoming changes](https://github.com/twpayne/chezmoi/compare/{{ $latestRelease.Name }}...master) {{- $lastReleaseIndex := sub (len $releases) 1 }} {{- range $index, $release := $releases }} ## [{{ $release.Name | trimPrefix "v" }}]({{ $release.HTMLURL }}) ({{ $release.PublishedAt | gitHubTimestampFormat "2006-01-02" }}) { id="{{ $release.Name }}" } {{ $release.Body | replaceAllRegex "(?m)^#+ (Changelog\\r?|What's Changed)$" "" | replaceAllRegex "(?m)^#+ (.*)\\r?$" "\n\n$1\n" | replaceAllRegex "\\*\\*(Full Changelog)\\*\\*.*\n" "" | replaceAllRegex "(https://github\\.com/twpayne/chezmoi/compare/(\\S+))" "[`$2`]($1)" | replaceAllRegex "@(\\S+)" "[**$0**](https://github.com/$1)" | replaceAllRegex "pull request #(\\d+)" "pull request https://github.com/twpayne/chezmoi/pull/$1" | replaceAllRegex "\\(#(\\d+)[)]" "(https://github.com/twpayne/chezmoi/pull/$1)" | replaceAllRegex "(https://github\\.com/twpayne/chezmoi/pull/(\\d+))" "[#$2]($1)" | replaceAllRegex "(?m)^([0-9a-f]{7,})" "* $1" | replaceAllRegex "(?m)^\\* (([0-9a-f]{7})[0-9a-f]*)" "* [`$2`](https://github.com/twpayne/chezmoi/commit/$1)" | replaceAllRegex "\\r\\n" "\\n" | trim | default "Internal changes only" }} {{- if ne $index $lastReleaseIndex }} {{- $prevRelease := index $releases (add $index 1) }} Full changelog: [{{ $prevRelease.Name }}...{{ $release.Name }}](https://github.com/twpayne/chezmoi/compare/{{ $prevRelease.Name }}...{{ $release.Name }}) {{- end }} {{- end }} ================================================ FILE: assets/chezmoi.io/docs/reference/source-state-attributes.md ================================================ # Source state attributes chezmoi stores the source state of files, symbolic links, and directories in regular files and directories in the source directory (`~/.local/share/chezmoi` by default). This location can be overridden with the `-S` flag or by giving a value for `sourceDir` in the configuration file. Directory targets are represented as directories in the source state. All other target types are represented as files in the source state. Some state is encoded in the source file names. Attributes can be changed by renaming the file in the source state or with the [chattr][chattr] command. The following prefixes and suffixes are special, and are collectively referred to as "attributes": | Prefix | Effect | | ------------- | ------------------------------------------------------------------------------------------------ | | `after_` | Run script after updating the destination | | `before_` | Run script before updating the destination | | `create_` | Ensure that the file exists, and create it with contents if it does not | | `dot_` | Rename to use a leading dot, e.g. `dot_foo` becomes `.foo` | | `empty_` | Ensure the file exists, even if is empty. By default, empty files are removed | | `encrypted_` | Encrypt the file in the source state | | `external_` | Ignore attributes in child entries | | `exact_` | Remove anything not managed by chezmoi | | `executable_` | Add executable permissions to the target file | | `literal_` | Stop parsing prefix attributes | | `modify_` | Treat the contents as a script that modifies an existing file | | `once_` | Only run the script if its contents have not been run successfully before | | `onchange_` | Only run the script if its contents have not been run successfully before with the same filename | | `private_` | Remove all group and world permissions from the target file or directory | | `readonly_` | Remove all write permissions from the target file or directory | | `remove_` | Remove the file or symlink if it exists or the directory if it is empty | | `run_` | Treat the contents as a script to run | | `symlink_` | Create a symlink instead of a regular file | | Suffix | Effect | | ---------- | --------------------------------------------------- | | `.literal` | Stop parsing suffix attributes | | `.tmpl` | Treat the contents of the source file as a template | Different target types allow different prefixes and suffixes. The order of prefixes is important. | Target type | Source type | Allowed prefixes in order | Allowed suffixes | | ---------------- | ----------- | --------------------------------------------------------------------------------- | ---------------- | | Directory | Directory | `remove_`, `external_`, `exact_`, `private_`, `readonly_`, `dot_` | *none* | | Regular file | File | `encrypted_`, `private_`, `readonly_`, `empty_`, `executable_`, `dot_` | `.tmpl` | | Create file | File | `create_`, `encrypted_`, `private_`, `readonly_`, `empty_`, `executable_`, `dot_` | `.tmpl` | | Modify file | File | `modify_`, `encrypted_`, `private_`, `readonly_`, `executable_`, `dot_` | `.tmpl` | | Remove file | File | `remove_`, `dot_` | *none* | | Script | File | `run_`, `once_` or `onchange_`, `before_` or `after_` | `.tmpl` | | Symbolic link | File | `symlink_`, `dot_` | `.tmpl` | The `literal_` prefix and `.literal` suffix can appear anywhere and stop attribute parsing. This permits filenames that would otherwise conflict with chezmoi's attributes to be represented. In addition, if the source file is encrypted, the suffix `.age` (when age encryption is used) or `.asc` (when gpg encryption is used) is stripped. These suffixes can be overridden with the `age.suffix` and `gpg.suffix` configuration variables. chezmoi ignores all files and directories in the source directory that begin with a `.` with the exception of files and directories that begin with `.chezmoi`. [chattr]: /reference/commands/chattr.md ================================================ FILE: assets/chezmoi.io/docs/reference/special-directories/chezmoidata.md ================================================ # `.chezmoidata/` If any `.chezmoidata/` directories exist in the source state, all files within them are interpreted as structured static data in the given formats. This data can then be used in templates. See also [`.chezmoidata.$FORMAT`][data-format]. --8<-- "config-format.md" !!! info The files in the `.chezmoidata` directories all *merge* to the root of the data dictionary and are read in lexical (alphabetic) filesystem order. This applies both within `.chezmoidata` directories and between `.chezmoidata` directories. As an example, if I have a `.chezmoidata` directory in my `dot_config` source directory, the files within will be merged according to the sort order of the files: === "JSON" ```json title="dot_config/.chezmoidata/zed.json" { "z": { "z": 3 } } ``` === "JSONC" ```jsonc title="dot_config/.chezmoidata/alpha.jsonc" { "z": { "z": 4 } } ``` === "TOML" ```toml title="dot_config/.chezmoidata/beta.toml" z.x = 1 ``` === "YAML" ```toml title="dot_config/.chezmoidata/gamma.yaml" z: y: 2 ``` The output of `chezmoi data` will include the following merged `z` dictionary. Note that the value in `.chezmoidata/zed.json` overwrote the value in `.chezmoidata/alpha.jsonc` because of the lexical file sorting. ```json { "z": { "x": 1, "y": 2, "z": 3 } } ``` Only dictionaries are merged; all other values (in particular lists) are replaced. !!! warning Files in `.chezmoidata` directories cannot be templates because they must be present prior to the start of the template engine. Dynamic machine data should be set in the `data` section of [`.chezmoi.$FORMAT.tmpl`][config]. Dynamic environment data should be read from templates using the [`output`][output], [`fromJson`][fromjson], [`fromYaml`][fromyaml], or similar functions. [data-format]: /reference/special-files/chezmoidata-format.md [config]: /reference/special-files/chezmoi-format-tmpl.md [fromjson]: /reference/templates/functions/fromJson.md [fromyaml]: /reference/templates/functions/fromYaml.md [output]: /reference/templates/functions/output.md ================================================ FILE: assets/chezmoi.io/docs/reference/special-directories/chezmoiexternals.md ================================================ # `.chezmoiexternals/` If any `.chezmoiexternals/` directories exist in the source state, then all files in this directory are treated as [`.chezmoiexternal.`][external] files relative to the source directory. !!! warning `.chezmoiexternals/` directories do not support externals for subdirectories within the `.chezmoiexternals/` directories. See [#4274][issue-4274] for details. [external]: /reference/special-files/chezmoiexternal-format.md [issue-4274]: https://github.com/twpayne/chezmoi/issues/4274 ================================================ FILE: assets/chezmoi.io/docs/reference/special-directories/chezmoiscripts.md ================================================ # `.chezmoiscripts/` If a directory called `.chezmoiscripts/` exists in the root of the source directory, then any scripts in it are executed as normal scripts without creating a corresponding directory in the target state. The `run_` attribute is still required. ================================================ FILE: assets/chezmoi.io/docs/reference/special-directories/chezmoitemplates.md ================================================ # `.chezmoitemplates/` If any directory called `.chezmoitemplates/` exists in the source state, then all files in this directory are available as templates with a name equal to the relative path to the `.chezmoitemplates/` directory. The [`template` action][action] or [`includeTemplate` function][function] can be used to include these templates in another template. The context value (`.`) must be set explicitly if needed, otherwise the template will be executed with `nil` context data. !!! example Given: ``` title="~/.local/share/chezmoi/.chezmoitemplates/foo" {{ if true }}bar{{ end }} ``` ``` title="~/.local/share/chezmoi/dot_file.tmpl" {{ template "foo" . }} ``` The target state of `.file` will be `bar`. While `.chezmoitemplates/` directories can reside anywhere in the source state, it easiest to manage a single directory in the root of your source directory. [action]: https://pkg.go.dev/text/template#hdr-Actions [function]: /reference/templates/functions/includeTemplate.md ================================================ FILE: assets/chezmoi.io/docs/reference/special-directories/index.md ================================================ # Special directories All directories in the source state whose name begins with `.` are ignored by default, unless they are one of the special directories listed here. All of these directories are optional and are evaluated in a specific order described in [special files][special-files]. - The files in [`.chezmoidata/`][data-dir] directories are read in lexical order with any [`.chezmoidata.$FORMAT`][data] files in the source state. - The files in [`.chezmoitemplates/`][templates] are made available for use in source templates. - The files in [`.chezmoiscripts/`][scripts] are read, templated, and according to their phase attributes (`run_after_`, `run_before_`, etc.) and lexical ordering. - Files in [`.chezmoiexternals/`][externals-dir] are read in lexical order with any [`.chezmoiexternal.$FORMAT`][external] files. [data-dir]: /reference/special-directories/chezmoidata.md [data]: /reference/special-files/chezmoidata-format.md [external]: /reference/special-files/chezmoiexternal-format.md [externals-dir]: /reference/special-directories/chezmoiexternals.md [scripts]: /reference/special-directories/chezmoiscripts.md [templates]: /reference/special-directories/chezmoitemplates.md [special-files]: /reference/special-files/index.md ================================================ FILE: assets/chezmoi.io/docs/reference/special-files/chezmoi-format-tmpl.md ================================================ # `.chezmoi.$FORMAT.tmpl` If a file called `.chezmoi.$FORMAT.tmpl` exists in the root of the source state then [`chezmoi init`][init] will use it to create or update the chezmoi config file. `$FORMAT` must be one of the supported config file formats. This template differs from source state templates because this template is executed prior to the reading of the source state. | Feature | Available? | | -------------------------------------------------- | ---------- | | data in the [config file][config] | ✅ | | data in [`.chezmoidata.$FORMAT`][data-files] files | 🚫 | | data in [`.chezmoidata/`][data-dirs] directories | 🚫 | | templates in [`.chezmoitemplates`][templates] | 🚫 | | [template functions][functions] | ✅ | | [init functions][init-functions] | ✅ | !!! example ``` title="~/.local/share/chezmoi/.chezmoi.yaml.tmpl" {{ $email := promptStringOnce . "email" "What is your email address" -}} data: email: {{ $email | quote }} ``` --8<-- "config-format.md" !!! info This file will also be used to update the config file when a command supports the `--init` flag, such as `chezmoi update --init`. [config]: /reference/configuration-file/index.md [data-dirs]: /reference/special-directories/chezmoidata.md [data-files]: /reference/special-files/chezmoidata-format.md [functions]: /reference/templates/functions/index.md [init-functions]: /reference/templates/init-functions/index.md [init]: /reference/commands/init.md [templates]: /reference/special-directories/chezmoitemplates.md ================================================ FILE: assets/chezmoi.io/docs/reference/special-files/chezmoidata-format.md ================================================ # `.chezmoidata.$FORMAT` If `.chezmoidata.$FORMAT` files exist in the source state, they are interpreted as structured static data in the given format. This data can then be used in templates. See also [`.chezmoidata/`][data-dir]. !!! example If `.chezmoidata.toml` contains the following: ```toml title="~/.local/share/chezmoi/.chezmoidata.toml" fontSize = 12 ``` Then the `.fontSize` variable is available in templates, e.g. ``` FONT_SIZE={{ .fontSize }} ``` Will result in: ``` FONT_SIZE=12 ``` --8<-- "config-format.md" !!! info There may be multiple `.chezmoidata.$FORMAT` files in the source state. They all *merge* to the root of the data dictionary and they are read in lexical (alphabetic) filesystem order. As an example, if I have four `.chezmoidata.$FORMAT` files in my `dot_config` source directory, they will be merged according to the sort order of the files: === "JSON" ```json title="dot_config/.chezmoidata.json" { "z": { "z": 3 } } ``` === "JSONC" ```jsonc title="dot_config/.chezmoidata.jsonc" { "z": { "z": 4 } } ``` === "TOML" ```toml title="dot_config/.chezmoidata.toml" z.x = 1 ``` === "YAML" ```toml title="dot_config/.chezmoidata.yaml" z: y: 2 ``` The output of `chezmoi data` will include the following merged `z` dictionary. Note that the value in `.chezmoidata.jsonc` overwrote the value in `.chezmoidata.json` because of the lexical file sorting. ```json { "z": { "x": 1, "y": 2, "z": 4 } } ``` Only dictionaries are merged; all other values (in particular lists) are replaced. !!! warning `.chezmoidata.$FORMAT` files cannot be templates because they must be present prior to the start of the template engine. Dynamic machine data should be set in the `data` section of [`.chezmoi.$FORMAT.tmpl`][config]. Dynamic environment data should be read from templates using the [`output`][output], [`fromJson`][fromjson], [`fromYaml`][fromyaml], or similar functions. [config]: /reference/special-files/chezmoi-format-tmpl.md [data-dir]: /reference/special-directories/chezmoidata.md [fromjson]: /reference/templates/functions/fromJson.md [fromyaml]: /reference/templates/functions/fromYaml.md [output]: /reference/templates/functions/output.md ================================================ FILE: assets/chezmoi.io/docs/reference/special-files/chezmoiexternal-format.md ================================================ # `.chezmoiexternal.$FORMAT{,.tmpl}` If a file called `.chezmoiexternal.$FORMAT` (with an optional `.tmpl` extension) exists anywhere in the source state (either `~/.local/share/chezmoi` or directory defined inside `.chezmoiroot`), it is interpreted as a list of external files and archives to be included as if they were in the source state. See also [`.chezmoiexternals/` directories][external-dir]. `$FORMAT` must be one of chezmoi's supported configuration file formats. --8<-- "config-format.md" `.chezmoiexternal.$FORMAT` is interpreted as a template, whether or not it has a `.tmpl` extension. This allows different externals to be included on different machines. If a `.chezmoiexternal.$FORMAT` file is located in an ignored directory (one listed in [`.chezmoiignore`][ignore]), all entries within the file are also ignored. Entries are indexed by target name relative to the directory of the `.chezmoiexternal.$FORMAT` file, and must have a `type` and a `url` and/or a `urls` field. `type` can be either `file`, `archive`, `archive-file`, or `git-repo`. If the entry's parent directories do not already exist in the source state then chezmoi will create them as regular directories. Entries may have the following fields: | Variable | Type | Default value | Description | | ---------------------------- | -------- | ------------- | ---------------------------------------------------------------- | | `type` | string | *none* | External type (`file`, `archive`, `archive-file`, or `git-repo`) | | `decompress` | string | *none* | Decompression for file | | `encrypted` | bool | `false` | Whether the external is encrypted | | `exact` | bool | `false` | Add `exact_` attribute to directories in archive | | `exclude` | []string | *none* | Patterns to exclude from archive | | `executable` | bool | `false` | Add `executable_` attribute to file | | `private` | bool | `false` | Add `private_` attribute to file | | `readonly` | bool | `false` | Add `readonly_` attribute to file | | `format` | string | *autodetect* | Format of archive | | `path` | string | *none* | Path to file in archive | | `include` | []string | *none* | Patterns to include from archive | | `refreshPeriod` | duration | `0` | Refresh period | | `stripComponents` | int | `0` | Number of leading directory components to strip from archives | | `url` | string | *none* | URL | | `urls` | []string | *none* | Extra URLs to try, in order | | `checksum.sha256` | string | *none* | Expected SHA256 checksum of data | | `checksum.sha384` | string | *none* | Expected SHA384 checksum of data | | `checksum.sha512` | string | *none* | Expected SHA512 checksum of data | | `checksum.size` | int | *none* | Expected size of data | | `clone.args` | []string | *none* | Extra args to `git clone` | | `filter.command` | string | *none* | Command to filter contents | | `filter.args` | []string | *none* | Extra args to command to filter contents | | `pull.args` | []string | *none* | Extra args to `git pull` | | `archive.extractAppleDouble` | bool | `false` | If `true`, AppleDouble files are extracted | | `targetPath` | string | *none* | Target path, overriding the key of the entry | `url` must be an `https://`, `http://`, or `file://` URL. If `urls` is specified then they are tried in order and the first URL that succeeds is used. If any of the optional `checksum.sha256`, `checksum.sha384`, or `checksum.sha512` fields are set, chezmoi will verify that the downloaded data has the given checksum. The optional boolean `encrypted` field specifies whether the file or archive is encrypted. The optional string `decompress` specifies how the file should be decompressed. Supported compression formats are `bzip2`, `gzip`, `xz`, and `zstd`. Note the `.rar` and `.zip` files are archives and you must use the `archive-file` type to extract a single file from a `.rar` or `.zip` archive. If optional string `filter.command` and array of strings `filter.args` are specified, the file or archive is filtered by piping it into the command's standard input and reading the command's standard output. If `type` is `file` then the target is a file with the contents of `url`. The optional boolean field `executable` may be set, in which case the target file will be executable. If `type` is `archive` then the target is a directory with the contents of the archive at `url`. The optional boolean field `exact` may be set, in which case the directory and all subdirectories will be treated as exact directories, i.e. `chezmoi apply` will remove entries not present in the archive. The optional integer field `stripComponents` will remove leading path components from the members of archive. The optional string field `format` sets the archive format. The supported archive formats are `tar`, `tar.gz`, `tgz`, `tar.bz2`, `tbz2`, `xz`, `.tar.zst`, and `zip`. If `format` is not specified then chezmoi will guess the format using firstly the path of the URL and secondly its contents. When `type` is `archive` or `archive-file`, the optional setting `archive.extractAppleDouble` controls whether [AppleDouble][appledouble] files are extracted. It is `false` by default, so AppleDouble files will not be extracted. The optional `include` and `exclude` fields are lists of patterns specify which archive members to include or exclude respectively. Patterns match paths in the archive, not the target state. chezmoi uses the following algorithm to determine whether an archive member is included: 1. If the archive member name matches any `exclude` pattern, then the archive member is excluded. In addition, if the archive member is a directory, then all contained files and sub-directories will be excluded, too (recursively). 2. Otherwise, if the archive member name matches any `include` pattern, then the archive member is included. 3. Otherwise, if only `include` patterns were specified then the archive member is excluded. 4. Otherwise, if only `exclude` patterns were specified then the archive member is included. 5. Otherwise, the archive member is included. Excluded archive members do not generate source state entries, and, if they are directories, all of their children are also excluded. If `type` is `archive-file` then the target is a file or symlink with the contents of the entry `path` in the archive at `url`. The optional integer field `stripComponents` will remove leading path components from the members of the archive before comparing them with `path`. The behavior of `format` is the same as for `archive`. If `executable` is `true` then chezmoi will set the executable bits on the target file, even if they are not set in the archive. !!! info Be sure to check that you have the correct `path` for the file in your archive. You can use your archive tooling to check for the correct paths. For example: ```console $ tar tzf eza_x86_64-unknown-linux-gnu.tar.gz ./eza $ tar tzf zellij-x86_64-unknown-linux-musl.tar.gz zellij ``` Notice how `path = "zellij"` in the configuration does not include a leading `./`. ```toml title="~/.local/share/chezmoi/.chezmoiexternal.toml" [".local/bin/zellij"] type = "archive-file" url = {{ gitHubLatestReleaseAssetURL "zellij-org/zellij" "zellij-x86_64-unknown-linux-musl.tar.gz" | quote }} executable = true path = "zellij" [".local/bin/eza"] type = "archive-file" url = {{ gitHubLatestReleaseAssetURL "eza-community/eza" "eza_x86_64-unknown-linux-gnu.tar.gz" | quote }} executable = true path = "./eza" ``` If `type` is `git-repo` then chezmoi will run `git clone $URL $TARGET_NAME` with the optional `clone.args` if the target does not exist. If the target exists, then chezmoi will run `git pull` with the optional `pull.args` to update the target. For `file` and `archive` externals, chezmoi will cache downloaded URLs. The optional duration `refreshPeriod` field specifies how often chezmoi will re-download the URL. The default is zero meaning that chezmoi will never re-download unless forced. To force chezmoi to re-download URLs, pass the `-R`/`--refresh-externals` flag. Suitable refresh periods include one day (`24h`), one week (`168h`), or four weeks (`672h`). !!! example ```toml title="~/.local/share/chezmoi/.chezmoiexternal.toml" [".vim/autoload/plug.vim"] type = "file" url = "https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim" refreshPeriod = "168h" [".oh-my-zsh"] type = "archive" url = "https://github.com/ohmyzsh/ohmyzsh/archive/master.tar.gz" exact = true stripComponents = 1 refreshPeriod = "168h" [".oh-my-zsh/custom/plugins/zsh-syntax-highlighting"] type = "archive" url = "https://github.com/zsh-users/zsh-syntax-highlighting/archive/master.tar.gz" exact = true stripComponents = 1 refreshPeriod = "168h" [".oh-my-zsh/custom/themes/powerlevel10k"] type = "archive" url = "https://github.com/romkatv/powerlevel10k/archive/v1.15.0.tar.gz" exact = true stripComponents = 1 [".local/bin/age"] type = "archive-file" url = "https://github.com/FiloSottile/age/releases/download/v1.1.1/age-v1.1.1-{{ .chezmoi.os }}-{{ .chezmoi.arch }}.tar.gz" path = "age/age" ["www/adminer/plugins"] type = "archive" url = "https://api.github.com/repos/vrana/adminer/tarball" refreshPeriod = "744h" stripComponents = 2 include = ["*/plugins/**"] ``` Some more examples can be found in the [user guide][elsewhere]. The optional `targetPath` field is a way to specify the target path of the fetched external(s), instead of using the entry key. This allows fetching multiple externals into one target directory in one chezmoiexternal file. !!! example ```toml title="~/.local/share/chezmoi/.chezmoiexternal.toml" [p10k_fonts] type = "archive" url = "https://github.com/romkatv/powerlevel10k-media/archive/master.tar.gz" exact = false stripComponents = 1 refreshPeriod = "168h" include = ["*/*.ttf"] targetPath = "Library/Fonts" [source_code_pro] type = "archive" url = "https://github.com/adobe-fonts/source-code-pro/archive/master.tar.gz" exact = false stripComponents = 2 refreshPeriod = "168h" include = ["**/*.ttf"] targetPath = "Library/Fonts" ``` !!! info Externals may be set up with a private Git repository. This must be done with an SSH URL. The SSH key should be configured in a `before_` script; and possible errors can be avoided by using [`stat`][stat]: ```toml title="~/.local/share/chezmoi/.chezmoiexternal.toml" {{ if stat (joinPath .chezmoi.homeDir ".ssh" "id_rsa") }} [".path/to/private/repo"] type = "git-repo" url = "git@private.com:org/repo.git" {{ end }} ``` [ignore]: /reference/special-files/chezmoiignore.md [external-dir]: /reference/special-directories/chezmoiexternals.md [elsewhere]: /user-guide/include-files-from-elsewhere.md [appledouble]: https://en.wikipedia.org/wiki/AppleSingle_and_AppleDouble_formats [stat]: /reference/templates/functions/stat.md ================================================ FILE: assets/chezmoi.io/docs/reference/special-files/chezmoiignore.md ================================================ # `.chezmoiignore{,.tmpl}` If a file called `.chezmoiignore` (with an optional `.tmpl` extension) exists in the source state then it is interpreted as a set of patterns to ignore. Patterns are matched using [`doublestar.Match`][match] and match against the target path, not the source path. Patterns can be excluded by prefixing them with a `!` character. All excludes take priority over all includes. Comments in `.chezmoiignore` files are introduced with the `#` character and run to the end of the line. If there is a `#` character introduced after the beginning of the line, it must be preceded by whitespace to be recognized as a comment and not part of the file. `.chezmoiignore` is interpreted as a template, whether or not it has a `.tmpl` extension. This allows different files to be ignored on different machines. `.chezmoiignore` files in source state subdirectories apply only to that subdirectory. !!! example ``` title="~/.local/share/chezmoi/.chezmoiignore" README.md *.txt # ignore *.txt in the target directory */*.txt # ignore *.txt in subdirectories of the target directory # but not in subdirectories of subdirectories; # so a/b/c.txt would *not* be ignored */*.org# # Ignore org-mode backup files that end with `#` backups/ # ignore the backups folder, but not its contents backups/** # ignore the contents of backups folder but not the folder itself {{- if ne .email "firstname.lastname@company.com" }} # Ignore .company-directory unless configured with a company email .company-directory # note that the pattern is not dot_company-directory {{- end }} {{- if ne .email "me@home.org" }} .personal-file {{- end }} {{- if eq .chezmoi.os "windows" }} Documents/* !Documents/*PowerShell/ # ignore a folder, except for Windows PowerShell profiles {{- end }} ``` [match]: https://pkg.go.dev/github.com/bmatcuk/doublestar/v4#Match ================================================ FILE: assets/chezmoi.io/docs/reference/special-files/chezmoiremove.md ================================================ # `.chezmoiremove{,.tmpl}` If a file called `.chezmoiremove` (with an optional `.tmpl` extension) exists in the source state then it is interpreted as a list of targets to remove. `.chezmoiremove` is interpreted as a template, whether or not it has a `.tmpl` extension. ================================================ FILE: assets/chezmoi.io/docs/reference/special-files/chezmoiroot.md ================================================ # `.chezmoiroot` If a file called `.chezmoiroot` exists in the root of the source directory then the source state is read from the directory specified in `.chezmoiroot` interpreted as a relative path to the source directory. `.chezmoiroot` is read before all other files in the source directory. !!! warning If you use this approach, you must move all other "source root" files such as [`.chezmoi.$FORMAT.tmpl`][config] into your new root. [config]: /reference/special-files/chezmoi-format-tmpl.md ================================================ FILE: assets/chezmoi.io/docs/reference/special-files/chezmoiversion.md ================================================ # `.chezmoiversion` If a file called `.chezmoiversion` exists anywhere in the source directory (not just the source state), then its contents are interpreted as a semantic version defining the minimum version of chezmoi required to interpret the source state correctly. chezmoi will refuse to interpret the source state if the current version is too old. This file is evaluated before any operation. !!! example ``` title="~/.local/share/chezmoi/.chezmoiversion" 2.50.0 ``` ================================================ FILE: assets/chezmoi.io/docs/reference/special-files/index.md ================================================ # Special files All files in the source directory whose name begins with `.` are ignored by default, unless they are one of the special files listed here. All of these files are optional and are evaluated in a specific order. 1. [`.chezmoiroot`][root] is read from the root of the source directory before anything other file, setting the source state path. The location of all other files, except `.chezmoiversion`, is relative to the source state path. 2. [`.chezmoi.$FORMAT.tmpl`][config] is used by [`chezmoi init`][init] to prepare or update the chezmoi config file. This is also used when the command supports the `--init` flag, such as `chezmoi apply --init`. This will be applied prior to any remaining special files or directories. 3. Data files ([`.chezmoidata.$FORMAT`][data] files or files in [`.chezmoidata/` directories][data-dir]) are read before any templates are processed so that data contained within are available to the templates. 4. [`.chezmoitemplates/`][templates-dir] directories are made available for use in source templates. 5. [`.chezmoiignore`][ignore] determines files and directories that should be ignored. 6. [`.chezmoiremove`][remove] determines files that should be removed during an apply. 7. External sources ([`.chezmoiexternal.$FORMAT`][external] or files in [`.chezmoiexternals/`][externals-dir]) are read in lexical order to include external files and archives as if they were in the source state. 8. [`.chezmoiversion`][version] is processed before any operation is applied, to ensure that the running version of chezmoi is new enough. [config]: /reference/special-files/chezmoi-format-tmpl.md [data-dir]: /reference/special-directories/chezmoidata.md [data]: /reference/special-files/chezmoidata-format.md [external-dir]: /reference/special-directories/chezmoiexternals.md [external]: /reference/special-files/chezmoiexternal-format.md [externals-dir]: /reference/special-directories/chezmoiexternals.md [ignore]: /reference/special-files/chezmoiignore.md [init]: /reference/commands/init.md [remove]: /reference/special-files/chezmoiremove.md [root]: /reference/special-files/chezmoiroot.md [templates-dir]: /reference/special-directories/chezmoitemplates.md [version]: /reference/special-files/chezmoiversion.md ================================================ FILE: assets/chezmoi.io/docs/reference/target-types.md ================================================ # Target types chezmoi will create, update, and delete files, directories, and symbolic links in the destination directory, and run scripts. chezmoi deterministically performs actions in ASCII order of their target name. !!! example Given a file `dot_a`, a script `run_z`, and a directory `exact_dot_c`, chezmoi will first create `.a`, create `.c`, and then execute `run_z`. ## Files Files are represented by regular files in the source state. The `encrypted_` attribute determines whether the file in the source state is encrypted. The `executable_` attribute will set the executable bits in the target state, and the `private_` attribute will clear all group and world permissions. The `readonly_` attribute will clear all write permission bits in the target state. Files with the `.tmpl` suffix will be interpreted as templates. If the target contents are empty then the file will be removed, unless it has an `empty_` prefix. ### Create file Files with the `create_` prefix will be created in the target state with the contents of the file in the source state if they do not already exist. If the file in the destination state already exists then its contents will be left unchanged. ### Modify file Files with the `modify_` prefix are treated as scripts that modify an existing file. If the file contains the string `chezmoi:modify-template`, then all lines containing that string will be removed, and the rest of the file will be interpreted as a template. The template is executed with the existing file's contents passed as a string in `.chezmoi.stdin`. The result of the template execution becomes the new contents of the file. Otherwise, the script receives the current contents of the target file on standard input and must write the new contents to standard output. If the target file does not exist, the script's standard input will be empty, and the script is responsible for generating the complete file contents. ### Remove entry Files with the `remove_` prefix will cause the corresponding entry (file, directory, or symlink) to be removed in the target state. ## Directories Directories are represented by regular directories in the source state. The `exact_` attribute causes chezmoi to remove any entries in the target state that are not explicitly specified in the source state, and the `private_` attribute causes chezmoi to clear all group and world permissions. The `readonly_` attribute will clear all write permission bits. ## Symbolic links Symbolic links are represented by regular files in the source state with the prefix `symlink_`. The contents of the file will have a trailing newline stripped, and the result be interpreted as the target of the symbolic link. Symbolic links with the `.tmpl` suffix in the source state are interpreted as templates. If the target of the symbolic link is empty or consists only of whitespace, then the target is removed. ## Scripts Scripts are represented as regular files in the source state with prefix `run_`. The file's contents (after being interpreted as a template if it has a `.tmpl` suffix) are executed. Scripts are executed on every `chezmoi apply`, unless they have the `once_` or `onchange_` attribute. `run_once_` scripts are only executed if a script with the same contents has not been run successfully before, i.e. if the script is new or if its contents have changed, or if it previously failed. `run_onchange_` scripts are executed whenever their contents change, even if a script with the same contents has run successfully before. Scripts with the `before_` attribute are executed before any files, directories, or symlinks are updated. Scripts with the `after_` attribute are executed after all files, directories, and symlinks have been updated. Scripts without an `before_` or `after_` attribute are executed in ASCII order of their target names with respect to files, directories, and symlinks. Scripts will normally run with their working directory set to their equivalent location in the destination directory. If the equivalent location in the destination directory either does not exist or is not a directory, then chezmoi will walk up the script's directory hierarchy and run the script in the first directory that exists and is a directory. !!! example A script in `~/.local/share/chezmoi/dir/run_script` will be run with a working directory of `~/dir`. chezmoi sets a number of `CHEZMOI*` environment variables when running scripts, corresponding to commonly-used template data variables. Extra environment variables can be set in the `env` or `scriptEnv` configuration variables. Scripts are executed using an interpreter, if configured. See the [section on interpreters][interpreters]. ## `symlink` mode By default, chezmoi will create regular files and directories. Setting `mode = "symlink"` will make chezmoi behave more like a dotfile manager that uses symlinks by default, i.e. `chezmoi apply` will make dotfiles symlinks to files in the source directory if the target is a regular file and is not encrypted, executable, private, or a template. [interpreters]: /reference/configuration-file/interpreters.md ================================================ FILE: assets/chezmoi.io/docs/reference/templates/1password-functions/index.md ================================================ # 1Password functions The `onepassword*` template functions return structured data from [1Password][1p] using the [1Password CLI][op] (`op`). !!! info When using the 1Password CLI with biometric authentication, chezmoi derives values from `op account list` that can resolves into the appropriate 1Password *account-uuid*. As an example, if `op account list --format=json` returns the following structure: ```json [ { "url": "account1.1password.ca", "email": "my@email.com", "user_uuid": "some-user-uuid", "account_uuid": "some-account-uuid" } ] ``` The following values can be used in the `account` parameter and the value `some-account-uuid` will be passed as the `--account` parameter to `op`. - `some-account-uuid` - `some-user-uuid` - `account1.1password.ca` - `account1` - `my@email.com` - `my` - `my@account1.1password.ca` - `my@account1` If there are multiple accounts and any value exists more than once, that value will be removed from the account mapping. That is, if you are signed into `my@email.com` and `your@email.com` for `account1.1password.ca`, then `account1.1password.ca` will not be a valid lookup value, but `my@account1`, `my@account1.1password.ca`, `your@account1`, and `your@account1.1password.ca` would all be valid lookups. !!! warning chezmoi has experimental support for [1Password secrets automation][automation] modes. These modes change how the 1Password CLI works and affect all functions. Most notably, `account` parameters are not allowed on all 1Password template functions. [1p]: https://1password.com/ [op]: https://developer.1password.com/docs/cli [automation]: /user-guide/password-managers/1password.md#secrets-automation ================================================ FILE: assets/chezmoi.io/docs/reference/templates/1password-functions/onepassword.md ================================================ # `onepassword` *uuid* [*vault* [*account*]] `onepassword` returns structured data from [1Password][1p] using the [1Password CLI][op] (`op`). *uuid* is passed to `op item get $UUID --format json` and the output from `op` is parsed as JSON. The output from `op` is cached so calling `onepassword` multiple times with the same *uuid* will only invoke `op` once. If the optional *vault* is supplied, it will be passed along to the `op item get` call, which can significantly improve performance. If the optional *account* is supplied, it will be passed along to the `op item get` call, which will help it look in the right account, in case you have multiple accounts (e.g., personal and work accounts). If there is no valid session in the environment, by default you will be interactively prompted to sign in. The 1password CLI command can be set with the `onePassword.command` config variable, and extra arguments can be specified with the `onePassword.args` config variable. !!! example ``` {{ (onepassword "$UUID").fields[1].value }} {{ (onepassword "$UUID" "$VAULT_UUID").fields[1].value }} {{ (onepassword "$UUID" "$VAULT_UUID" "$ACCOUNT_NAME").fields[1].value }} {{ (onepassword "$UUID" "" "$ACCOUNT_NAME").fields[1].value }} ``` A more robust way to get a password field would be something like: ``` {{ range (onepassword "$UUID").fields -}} {{ if and (eq .label "password") (eq .purpose "PASSWORD") -}} {{ .value -}} {{ end -}} {{ end }} ``` !!! warning When using [1Password secrets automation][automation], the *account* parameter is not allowed. [1p]: https://1password.com/ [op]: https://support.1password.com/command-line-getting-started/ [automation]: /user-guide/password-managers/1password.md#secrets-automation ================================================ FILE: assets/chezmoi.io/docs/reference/templates/1password-functions/onepasswordDetailsFields.md ================================================ # `onepasswordDetailsFields` *uuid* [*vault* [*account*]] `onepasswordDetailsFields` returns structured data from [1Password][1p] using the [1Password CLI][op] (`op`). *uuid* is passed to `op get item $UUID`, the output from `op` is parsed as JSON, and elements of `details.fields` are returned as a map indexed by each field's `id` (if set) or `label` (if set and `id` is not present). If there is no valid session in the environment, by default you will be interactively prompted to sign in. The output from `op` is cached so calling `onepasswordDetailsFields` multiple times with the same *uuid* will only invoke `op` once. If the optional *vault* is supplied, it will be passed along to the `op get` call, which can significantly improve performance. If the optional *account* is supplied, it will be passed along to the `op get` call, which will help it look in the right account, in case you have multiple accounts (e.g. personal and work accounts). !!! example ``` {{ (onepasswordDetailsFields "$UUID").password.value }} {{ (onepasswordDetailsFields "$UUID" "$VAULT_UUID").password.value }} {{ (onepasswordDetailsFields "$UUID" "$VAULT_UUID" "$ACCOUNT_NAME").password.value }} {{ (onepasswordDetailsFields "$UUID" "" "$ACCOUNT_NAME").password.value }} ``` !!! example Given the output from `op`: ```json { "uuid": "$UUID", "details": { "fields": [ { "designation": "username", "name": "username", "type": "T", "value": "exampleuser" }, { "designation": "password", "name": "password", "type": "P", "value": "examplepassword" } ] } } ``` the return value of `onepasswordDetailsFields` will be the map: ```json { "username": { "designation": "username", "name": "username", "type": "T", "value": "exampleuser" }, "password": { "designation": "password", "name": "password", "type": "P", "value": "examplepassword" } } ``` !!! warning When using [1Password secrets automation][automation], the *account* parameter is not allowed. [1p]: https://1password.com/ [op]: https://support.1password.com/command-line-getting-started/ [automation]: /user-guide/password-managers/1password.md#secrets-automation ================================================ FILE: assets/chezmoi.io/docs/reference/templates/1password-functions/onepasswordDocument.md ================================================ # `onepasswordDocument` *uuid* [*vault* [*account*]] `onepasswordDocument` returns a document from [1Password][1p] using the [1Password CLI][op] (`op`). *uuid* is passed to `op get document $UUID` and the output from `op` is returned. The output from `op` is cached so calling `onepasswordDocument` multiple times with the same *uuid* will only invoke `op` once. If the optional *vault* is supplied, it will be passed along to the `op get` call, which can significantly improve performance. If the optional *account* is supplied, it will be passed along to the `op get` call, which will help it look in the right account, in case you have multiple accounts (e.g., personal and work accounts). If there is no valid session in the environment, by default you will be interactively prompted to sign in. !!! example ``` {{- onepasswordDocument "$UUID" -}} {{- onepasswordDocument "$UUID" "$VAULT_UUID" -}} {{- onepasswordDocument "$UUID" "$VAULT_UUID" "$ACCOUNT_NAME" -}} {{- onepasswordDocument "$UUID" "" "$ACCOUNT_NAME" -}} ``` !!! warning When using [1Password Connect][connect], `onepasswordDocument` is not available. !!! warning When using [1Password Service Accounts][accounts], the *account* parameter is not allowed. [1p]: https://1password.com/ [op]: https://developer.1password.com/docs/cli [connect]: /user-guide/password-managers/1password.md#1password-connect [accounts]: /user-guide/password-managers/1password.md#1password-service-accounts ================================================ FILE: assets/chezmoi.io/docs/reference/templates/1password-functions/onepasswordItemFields.md ================================================ # `onepasswordItemFields` *uuid* [*vault* [*account*]] `onepasswordItemFields` returns structured data from [1Password][1p] using the [1Password CLI][op] (`op`). *uuid* is passed to `op item get $UUID --format json`, the output from `op` is parsed as JSON, and each element of `details.sections` are iterated over and any `fields` are returned as a map indexed by each field's `n`. If there is no valid session in the environment, by default you will be interactively prompted to sign in. !!! example The result of ``` {{ (onepasswordItemFields "abcdefghijklmnopqrstuvwxyz").exampleLabel.value }} ``` is equivalent to calling ```console $ op item get abcdefghijklmnopqrstuvwxyz --fields label=exampleLabel ``` or ```console $ op item get abcdefghijklmnopqrstuvwxyz --fields exampleLabel ``` !!! example Given the output from `op`: ```json { "id": "$UUID", "title": "$TITLE", "version": 1, "vault": { "id": "$vaultUUID" }, "category": "LOGIN", "last_edited_by": "userUUID", "created_at": "2022-01-12T16:29:26Z", "updated_at": "2022-01-12T16:29:26Z", "sections": [ { "id": "$sectionID", "label": "Related Items" } ], "fields": [ { "id": "nerlnqbfzdm5q5g6ydsgdqgdw4", "type": "STRING", "label": "exampleLabel", "value": "exampleValue" } ], } ``` the return value of `onepasswordItemFields` will be the map: ```json { "exampleLabel": { "id": "string", "type": "D4328E0846D2461E8E455D7A07B93397", "label": "exampleLabel", "value": "exampleValue" } } ``` !!! warning When using [1Password secrets automation][automation], the *account* parameter is not allowed. [1p]: https://1password.com/ [op]: https://support.1password.com/command-line-getting-started/ [automation]: /user-guide/password-managers/1password.md#secrets-automation ================================================ FILE: assets/chezmoi.io/docs/reference/templates/1password-functions/onepasswordRead.md ================================================ # `onepasswordRead` *url* [*account*] `onepasswordRead` returns data from [1Password][1p] using the [1Password CLI][op] (`op`). *url* is passed to the `op read --no-newline` command. If *account* is specified, the extra arguments `--account $ACCOUNT` are passed to `op`. If there is no valid session in the environment, by default you will be interactively prompted to sign in. !!! example The result of ``` {{ onepasswordRead "op://vault/item/field" }} ``` is equivalent to calling ```console $ op read --no-newline op://vault/item/field ``` !!! warning When using [1Password secrets automation][automation], the *account* parameter is not allowed. [1p]: https://1password.com/ [op]: https://developer.1password.com/docs/cli [automation]: /user-guide/password-managers/1password.md#secrets-automation ================================================ FILE: assets/chezmoi.io/docs/reference/templates/aws-secrets-manager-functions/awsSecretsManager.md ================================================ # `awsSecretsManager` *arn* `awsSecretsManager` returns structured data retrieved from [AWS Secrets Manager][awssm]. *arn* specifies the `SecretId` passed to [`GetSecretValue`][gsv]. This can either be the full ARN or the [simpler name][name] if applicable. [awssm]: https://aws.amazon.com/secrets-manager/ [gsv]: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html [name]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/troubleshoot.html#ARN_secretnamehyphen ================================================ FILE: assets/chezmoi.io/docs/reference/templates/aws-secrets-manager-functions/awsSecretsManagerRaw.md ================================================ # `awsSecretsManagerRaw` *arn* `awsSecretsManager` returns the raw string value retrieved from [AWS Secrets Manager][awssm]. *arn* specifies the `SecretId` passed to [`GetSecretValue`][gsv]. This can either be the full ARN or the [simpler name][name] if applicable. [awssm]: https://aws.amazon.com/secrets-manager/ [gsv]: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html [name]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/troubleshoot.html#ARN_secretnamehyphen ================================================ FILE: assets/chezmoi.io/docs/reference/templates/aws-secrets-manager-functions/index.md ================================================ # AWS Secrets Manager functions The `awsSecretsManager*` functions return data from [AWS Secrets Manager][awssm] using the [`GetSecretValue`][gsv] API. The profile and region are pulled from the standard environment variables and shared config files but can be overridden by setting `awsSecretsManager.profile` and `awsSecretsManager.region` configuration variables respectively. [awssm]: https://aws.amazon.com/secrets-manager/ [gsv]: https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html ================================================ FILE: assets/chezmoi.io/docs/reference/templates/azure-key-vault-functions/azureKeyVault.md ================================================ # `azureKeyVault` *secret name* [*vault-name*] `azureKeyVault` returns a secret value retrieved from an [Azure Key Vault][azkv]. The mandatory `secret name` argument specifies the *name of the secret* to retrieve. The optional `vault name` argument specifies the *name of the vault*, if not set, the default vault name will be used. !!! warning The current implementation will always return the latest version of the secret. Retrieving a specific version of a secret is not supported. [azkv]: https://learn.microsoft.com/en-us/azure/key-vault/general/ ================================================ FILE: assets/chezmoi.io/docs/reference/templates/bitwarden-functions/bitwarden.md ================================================ # `bitwarden` [*arg*...] `bitwarden` returns structured data retrieved from [Bitwarden][bitwarden] using the [Bitwarden CLI][cli] (`bw`). *arg*s are passed to `bw get` unchanged and the output from `bw get` is parsed as JSON. The output from `bw get` is cached so calling `bitwarden` multiple times with the same arguments will only invoke `bw` once. !!! example ``` username = {{ (bitwarden "item" "$ITEMID").login.username }} password = {{ (bitwarden "item" "$ITEMID").login.password }} ``` [bitwarden]: https://bitwarden.com [cli]: https://bitwarden.com/help/cli ================================================ FILE: assets/chezmoi.io/docs/reference/templates/bitwarden-functions/bitwardenAttachment.md ================================================ # `bitwardenAttachment` *filename* *itemid* `bitwardenAttachment` returns a document from [Bitwarden][bitwarden] using the [Bitwarden CLI][cli] (`bw`). *filename* and *itemid* are passed to `bw get attachment $FILENAME --itemid $ITEMID` and the output is returned. The output from `bw` is cached so calling `bitwardenAttachment` multiple times with the same *filename* and *itemid* will only invoke `bw` once. !!! example ``` {{- bitwardenAttachment "$FILENAME" "$ITEMID" -}} ``` [bitwarden]: https://bitwarden.com/ [cli]: https://bitwarden.com/help/article/cli/ ================================================ FILE: assets/chezmoi.io/docs/reference/templates/bitwarden-functions/bitwardenAttachmentByRef.md ================================================ # `bitwardenAttachmentByRef` *filename* *args* `bitwardenAttachmentByRef` returns a document from [Bitwarden][bitwarden] using the [Bitwarden CLI][cli] (`bw`). This method requires two calls to `bw` to complete: 1. First, *args* are passed to `bw get` in order to retrieve the item's *itemid*. 2. Then, *filename* and *itemid* are passed to `bw get attachment $FILENAME --itemid $ITEMID` and the output from `bw` is returned. The output from `bw` is cached so calling `bitwardenAttachmentByRef` multiple times with the same *filename* and *itemid* will only invoke `bw` once. !!! example ``` {{- bitwardenAttachmentByRef "$FILENAME" "$ARGS" -}} ``` !!! example ``` {{- bitwardenAttachmentByRef "id_rsa" "item" "example.com" -}} ``` [bitwarden]: https://bitwarden.com/ [cli]: https://bitwarden.com/help/article/cli/ ================================================ FILE: assets/chezmoi.io/docs/reference/templates/bitwarden-functions/bitwardenFields.md ================================================ # `bitwardenFields` [*arg*...] `bitwardenFields` returns structured data retrieved from [Bitwarden][bitwarden] using the [Bitwarden CLI][cli] (`bw`). *arg*s are passed to `bw get` unchanged, the output from `bw get` is parsed as JSON, and the elements of `fields` are returned as a dict indexed by each field's `name`. The output from `bw get` is cached so calling `bitwardenFields` multiple times with the same arguments will only invoke `bw get` once. !!! example ``` {{ (bitwardenFields "item" "$ITEMID").token.value }} ``` !!! example Given the output from `bw get`: ```json { "object": "item", "id": "bf22e4b4-ae4a-4d1c-8c98-ac620004b628", "organizationId": null, "folderId": null, "type": 1, "name": "example.com", "notes": null, "favorite": false, "fields": [ { "name": "hidden", "value": "hidden-value", "type": 1 }, { "name": "token", "value": "token-value", "type": 0 } ], "login": { "username": "username-value", "password": "password-value", "totp": null, "passwordRevisionDate": null }, "collectionIds": [], "revisionDate": "2020-10-28T00:21:02.690Z" } ``` the return value if `bitwardenFields` will be the map: ```json { "hidden": { "name": "hidden", "type": 1, "value": "hidden-value" }, "token": { "name": "token", "type": 0, "value": "token-value" } } ``` [bitwarden]: https://bitwarden.com [cli]: https://bitwarden.com/help/cli ================================================ FILE: assets/chezmoi.io/docs/reference/templates/bitwarden-functions/bitwardenSecrets.md ================================================ # `bitwardenSecrets` *secret-id* [*access-token*] `bitwardenSecrets` returns structured data from [Bitwarden][bitwarden] using the [Bitwarden Secrets CLI][secrets] (`bws`). *secret-id* is passed to `bws secret get` and the output from `bws secret get` is parsed as JSON and returned. If the additional *access-token* argument is given, it is passed to `bws secret get` with the `--access-token` flag. The output from `bws secret get` is cached so calling `bitwardenSecrets` multiple times with the same *secret-id* and *access-token* will only invoke `bws secret get` once. !!! ``` {{ (bitwardenSecrets "be8e0ad8-d545-4017-a55a-b02f014d4158").value }} ``` [bitwarden]: https://bitwarden.com [secrets]: https://bitwarden.com/help/secrets-manager-cli/ ================================================ FILE: assets/chezmoi.io/docs/reference/templates/bitwarden-functions/index.md ================================================ # Bitwarden functions The `bitwarden*` and `rbw*` functions return data from [Bitwarden][bitwarden] using the [Bitwarden CLI][bw] (`bw`), [Bitwarden Secrets CLI][bws] (`bws`), and [`rbw`][rbw] commands. ## Automatic Bitwarden CLI unlock By default, you must have unlocked your Bitwarden CLI session with the command ```bash export BW_SESSION="$(bw unlock --raw)" ``` before running chezmoi. Optionally, you can tell chezmoi to automatically run `bw unlock --raw` and set the `BW_SESSION` environment variable by setting the `bitwarden.unlock` configuration variable. Valid values are: | Value | Effect | | -------- | ------------------------------------------------------------------------------- | | `false` | Never run `bw unlock --raw` automatically. | | `true` | Always run `bw unlock --raw` automatically. | | `"auto"` | Only run `bw unlock --raw` if the `BW_SESSION` environment variable is not set. | Additionally, if chezmoi runs `bw unlock raw` automatically, then chezmoi will also run `bw lock` before terminating. [bitwarden]: https://bitwarden.com [bw]: https://bitwarden.com/help/article/cli/ [bws]: https://bitwarden.com/help/secrets-manager-cli/ [rbw]: https://github.com/doy/rbw ================================================ FILE: assets/chezmoi.io/docs/reference/templates/bitwarden-functions/rbw.md ================================================ # `rbw` *name* [*arg*...] `rbw` returns structured data retrieved from [Bitwarden][bitwarden] using [`rbw`][rbw]. *name* is passed to `rbw get --raw`, along with any extra *arg*s, and the output is parsed as JSON. The output from `rbw get --raw` is cached so calling `rbw` multiple times with the same arguments will only invoke `rbw` once. !!! example ``` username = {{ (rbw "test-entry").data.username }} password = {{ (rbw "test-entry" "--folder" "my-folder").data.password }} ``` [bitwarden]: https://bitwarden.com [rbw]: https://github.com/doy/rbw ================================================ FILE: assets/chezmoi.io/docs/reference/templates/bitwarden-functions/rbwFields.md ================================================ # `rbwFields` *name* [*arg*...] `rbw` returns structured data retrieved from [Bitwarden][bitwarden] using [`rbw`][rbw]. *name* is passed to `rbw get --raw`, along with any extra *arg*s, the output is parsed as JSON, and the elements of `fields` are returned as a dict indexed by each field's `name`. The output from `rbw get --raw` is cached so calling `rbwFields` multiple times with the same arguments will only invoke `rbwFields` once. !!! example ``` {{ (rbwFields "item").name.value }} {{ (rbwFields "item" "--folder" "my-folder").name.value }} ``` [bitwarden]: https://bitwarden.com [rbw]: https://github.com/doy/rbw ================================================ FILE: assets/chezmoi.io/docs/reference/templates/dashlane-functions/dashlaneNote.md ================================================ # `dashlaneNote` *filter* `dashlaneNote` returns the content of a secure note from [Dashlane][dashlane] using the [Dashlane CLI][cli] (`dcli`). *filter* is passed to `dcli note`, and the output from `dcli note` is just read as a multi-line string. The output from `dcli note` is cached so calling `dashlaneNote` multiple times with the same *filter* will only invoke `dcli note` once. !!! example ``` {{ dashlaneNote "filter" }} ``` [dashlane]: https://dashlane.com [cli]: https://github.com/Dashlane/dashlane-cli ================================================ FILE: assets/chezmoi.io/docs/reference/templates/dashlane-functions/dashlanePassword.md ================================================ # `dashlanePassword` *filter* `dashlanePassword` returns structured data from [Dashlane][dashlane] using the [Dashlane CLI][cli] (`dcli`). *filter* is passed to `dcli password --output json`, and the output from `dcli password` is parsed as JSON. The output from `dcli password` cached so calling `dashlanePassword` multiple times with the same *filter* will only invoke `dcli password` once. !!! example ``` {{ (index (dashlanePassword "filter") 0).password }} ``` [dashlane]: https://dashlane.com [cli]: https://github.com/Dashlane/dashlane-cli ================================================ FILE: assets/chezmoi.io/docs/reference/templates/dashlane-functions/index.md ================================================ # Dashlane functions The `dashlane*` functions return data from [Dashlane][dashlane] using the [Dashlane CLI][cli]. [dashlane]: https://dashlane.com [cli]: https://github.com/Dashlane/dashlane-cli ================================================ FILE: assets/chezmoi.io/docs/reference/templates/directives.md ================================================ # Directives File-specific template options can be set using template directives in the template of the form: chezmoi:template:$KEY=$VALUE which sets the template option `$KEY` to `$VALUE`. `$VALUE` must be quoted if it contains spaces or double quotes. Multiple key/value pairs may be specified on a single line. Lines containing template directives are removed to avoid parse errors from any delimiters. If multiple directives are present in a file, later directives override earlier ones. ## Delimiters By default, chezmoi uses the standard `text/template` delimiters `{{` and `}}`. If a template contains the string: chezmoi:template:left-delimiter=$LEFT right-delimiter=$RIGHT Then the delimiters `$LEFT` and `$RIGHT` are used instead. Either or both of `left-delimiter=$LEFT` and `right-delimiter=$RIGHT` may be omitted. If either `$LEFT` or `$RIGHT` is empty then the default delimiter (`{{` and `}}` respectively) is set instead. The delimiters are specific to the file in which they appear and are not inherited by templates called from the file. !!! example ```sh #!/bin/sh # chezmoi:template:left-delimiter="# [[" right-delimiter=]] # [[ "true" ]] ``` ## Encoding Templates are always written in UTF-8 with no byte order mark. By default, the result of executing a template is also UTF-8 with no byte order mark but this can be transformed into another encoding with the template directive: chezmoi:template:encoding=$ENCODING where `$ENCODING` is one of: | Encoding | Description | | --------------- | -------------------------------------------- | | `utf-8` | UTF-8 with no byte order mark | | `utf-8-bom` | UTF-8 with a byte order mark | | `utf-16-be` | Big-endian UTF-16 with no byte order mark | | `utf-16-be-bom` | Big-endian UTF-16 with a byte order mark | | `utf-16-le` | Little-endian UTF-16 with no byte order mark | | `utf-16-le-bom` | Little-endian UTF-16 with a byte order mark | !!! example ``` {{/* chezmoi:template:encoding=utf-16-le */}} ``` ## Format indent By default, chezmoi's `toJson`, `toToml`, and `toYaml` template functions use the default indent of two spaces. The indent can be overridden with: chezmoi:template:format-indent=$STRING to set the indent to be the literal `$STRING`, or chezmoi:template:format-indent-width=$WIDTH to set the indent to be `$WIDTH` spaces. !!! example ``` {{/* chezmoi:template:format-indent="\t" */}} {{ dict "key" "value" | toJson }} ``` !!! example ``` {{/* chezmoi:template:format-indent-width=4 */}} {{ dict "key" "value" | toYaml }} ``` ## Line endings Many of the template functions available in chezmoi primarily use UNIX-style line endings (`lf`/`\n`), which may result in unexpected output when running `chezmoi diff` on a `modify_` template. These line endings can be overridden with a template directive: chezmoi:template:line-endings=$VALUE `$VALUE` can be an arbitrary string or one of: | Value | Effect | | -------- | -------------------------------------------------------------------- | | `crlf` | Use Windows line endings (`\r\n`) | | `lf` | Use UNIX-style line endings (`\n`) | | `native` | Use platform-native line endings (`crlf` on Windows, `lf` elsewhere) | ## Missing keys By default, chezmoi will return an error if a template indexes a map with a key that is not present in the map. This behavior can be changed globally with the `template.options` configuration variable or with a template directive: chezmoi:template:missing-key=$VALUE `$VALUE` can be one of: | Value | Effect | | --------- | --------------------------------------------------------------------------------------------- | | `error` | Return an error on any missing key (default) | | `invalid` | Ignore missing keys. If printed, the result of the index operation is the string `` | | `zero` | Ignore missing keys. If printed, the result of the index operation is the zero value | ================================================ FILE: assets/chezmoi.io/docs/reference/templates/doppler-functions/doppler.md ================================================ # `doppler` *key* [*project* [*config*]] `doppler` returns the secret for the specified project and configuration from [Doppler][doppler] using `doppler secrets download --json --no-file`. If either of *project* or *config* are empty or omitted, then chezmoi will use the value from the `doppler.project` and `doppler.config` config variables if they are set and not empty. !!! example ``` {{ doppler "SECRET_NAME" "project_name" "configuration_name" }} ``` [doppler]: https://www.doppler.com ================================================ FILE: assets/chezmoi.io/docs/reference/templates/doppler-functions/dopplerProjectJson.md ================================================ # `dopplerProjectJson` [*project* [*config*]] `dopplerProjectJson` returns the secret for the specified project and configuration from [Doppler][doppler] using `doppler secrets download --json --no-file` as `json` structured data. If either of *project* or *config* are empty or omitted, then chezmoi will use the value from the `doppler.project` and `doppler.config` config variables if they are set and not empty. !!! example ``` {{ (dopplerProjectJson "project_name" "configuration_name").SECRET_NAME }} ``` [doppler]: https://www.doppler.com ================================================ FILE: assets/chezmoi.io/docs/reference/templates/doppler-functions/index.md ================================================ # Doppler chezmoi includes support for [Doppler][doppler] using the `doppler` CLI to expose data through the `doppler` and `dopplerProjectJson` template functions. [doppler]: https://www.doppler.com ================================================ FILE: assets/chezmoi.io/docs/reference/templates/ejson-functions/ejsonDecrypt.md ================================================ # `ejsonDecrypt` *filePath* `ejsonDecrypt` returns the decrypted content of an [ejson][ejson]-encrypted file. *filePath* indicates where the encrypted file is located. The decrypted file is cached so calling `ejsonDecrypt` multiple times with the same *filePath* will only run through the decryption process once. The cache is shared with `ejsonDecryptWithKey`. !!! example ``` {{ (ejsonDecrypt "my-secrets.ejson").password }} ``` [ejson]: https://github.com/Shopify/ejson ================================================ FILE: assets/chezmoi.io/docs/reference/templates/ejson-functions/ejsonDecryptWithKey.md ================================================ # `ejsonDecryptWithKey` *filePath* *key* `ejsonDecryptWithKey` returns the decrypted content of an [ejson][ejson]-encrypted file. *filePath* indicates where the encrypted file is located, and *key* is used to decrypt the file. The decrypted file is cached so calling `ejsonDecryptWithKey` multiple times with the same *filePath* will only run through the decryption process once. The cache is shared with `ejsonDecrypt`. !!! example ``` {{ (ejsonDecryptWithKey "my-secrets.ejson" "top-secret-key").password }} ``` [ejson]: https://github.com/Shopify/ejson ================================================ FILE: assets/chezmoi.io/docs/reference/templates/ejson-functions/index.md ================================================ # ejson functions The `ejson*` functions return data from [ejson][ejson]-encrypted files. [ejson]: https://github.com/Shopify/ejson ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/abortEmpty.md ================================================ # `abortEmpty` `abortEmpty` causes template execution to immediately stop and return the empty string. If `abortEmpty` is called in a sub-template executed by `includeTemplate` then all template execution stops and returns the empty string, not just the sub-template. !!! example ``` {{ abortEmpty }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/comment.md ================================================ # `comment` *prefix* *text* `comment` returns *text* with each line prefixed with *prefix*. `comment` is typically used to comment out blocks of multi-line text unconditionally. In contrast, the [`ensureLinePrefix` template function](ensureLinePrefix.md) can be used to only comment out lines that are not already comments. !!! example ``` {{ "Line 1\nLine 2\n" | comment "# " }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/completion.md ================================================ # `completion` *shell* `completion` returns chezmoi's shell completion for *shell*. *shell* can be one of `bash`, `fish`, `powershell`, or `zsh`. !!! example ``` {{ completion "zsh" }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/decrypt.md ================================================ # `decrypt` *ciphertext* `decrypt` decrypts *ciphertext* using chezmoi's configured encryption method. !!! example ``` {{ joinPath .chezmoi.sourceDir ".ignored-encrypted-file.age" | include | decrypt }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/deleteValueAtPath.md ================================================ # `deleteValueAtPath` *path* *dict* `deleteValueAtPath` modifies *dict* to delete the value at *path* and returns *dict*. *path* can be either a string containing a `.`-separated list of keys or a list of keys. If *path* does not exist in *dict* then `deleteValueAtPath` returns *dict* unchanged. !!! example ``` {{ dict "outer" (dict "inner" "value") | deleteValueAtPath "outer.inner" | toJson }} {{ dict | setValueAtPath "key1" "value1" | setValueAtPath "key2.nestedKey" "value2" | toJson }} {{ dict | setValueAtPath (list "key2" "nestedKey") "value2" | toJson }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/encrypt.md ================================================ # `encrypt` *plaintext* `encrypt` encrypts *plaintext* using chezmoi's configured encryption method. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/ensureLinePrefix.md ================================================ # `ensureLinePrefix` *prefix* [*prefix-to-add*] *text* `ensureLinePrefix` ensures that each line of *text* starts with *prefix*. If any line does not start with *prefix* then *prefix-to-add* is prepended to that line. Typically, `ensureLinePrefix` is used to ensure that lines are commented out, similar to the [`comment` template function](comment.md). `ensureLinePrefix` only modifies lines that are not already comments, whereas `comment` modifies all lines, even if they are already comments. !!! example ``` {{ "### Heading\nBody\n" | ensureLinePrefix "#" }} {{ "### Heading\nBody\n" | ensureLinePrefix "#" "# " }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/eqFold.md ================================================ # `eqFold` *string1* *string2* [*extraString*...] `eqFold` returns the boolean truth of comparing *string1* with *string2* and any number of *extraString*s under Unicode case-folding. !!! example ``` {{ $commandOutput := output "path/to/output-FOO.sh" }} {{ if eqFold "foo" $commandOutput }} # $commandOutput is "foo"/"Foo"/"FOO"... {{ else if eqFold "bar" $commandOutput }} # $commandOutput is "bar"/"Bar"/"BAR"... {{ end }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/exec.md ================================================ # `exec` *name* [*arg*...] `exec` executes the command *name* with *arg*s and returns `true` if the command succeeded, `false` if it failed, or an error if the command cannot be found. The command's output is ignored. The execution occurs every time that the template is executed. It is the user's responsibility to ensure that executing the command is both idempotent and fast. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/findExecutable.md ================================================ # `findExecutable` *file* *path-list* `findExecutable` searches for an executable named *file* in directories identified by *path-list*. The result will be the executable file concatenated with the matching path. If an executable *file* cannot be found in *path-list*, `findExecutable` returns an empty string. `findExecutable` is provided as an alternative to [`lookPath`][site-lookpath] so that you can interrogate the system PATH as it would be configured after `chezmoi apply`. Like `lookPath`, `findExecutable` is not hermetic: its return value depends on the state of the file system at the moment the template is executed. Exercise caution when using it in your templates. The return value of the first successful call to `findExecutable` is cached, and future calls to `findExecutable` with the same parameters will return this path. !!! info On Windows, the resulting path will contain the first found executable extension as identified by the environment variable `%PathExt%`. !!! example ``` {{ if findExecutable "mise" (list "bin" "go/bin" ".cargo/bin" ".local/bin") }} # $HOME/.cargo/bin/mise exists and will probably be in $PATH after apply {{ end }} ``` [site-lookpath]: /reference/templates/functions/lookPath.md ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/findOneExecutable.md ================================================ # `findOneExecutable` *file-list* *path-list* `findOneExecutable` searches for an executable from *file-list* in directories identified by *path-list*, finding the first matching executable in the first matching directory (each directory is searched for matching executables in turn). The result will be the executable file concatenated with the matching path. If an executable from *file-list* cannot be found in *path-list*, `findOneExecutable` returns an empty string. `findOneExecutable` is provided as an alternative to [`lookPath`][lookpath] so that you can interrogate the system PATH as it would be configured after `chezmoi apply`. Like `lookPath`, `findOneExecutable` is not hermetic: its return value depends on the state of the file system at the moment the template is executed. Exercise caution when using it in your templates. The return value of the first successful call to `findOneExecutable` is cached, and future calls to `findOneExecutable` with the same parameters will return this path. !!! info On Windows, the resulting path will contain the first found executable extension as identified by the environment variable `%PathExt%`. !!! example ``` {{ if findOneExecutable (list "eza" "exa") (list "bin" "go/bin" ".cargo/bin" ".local/bin") }} # $HOME/.cargo/bin/exa exists and will probably be in $PATH after apply {{ end }} ``` [lookpath]: /reference/templates/functions/lookPath.md ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/fromIni.md ================================================ # `fromIni` *initext* `fromIni` returns the parsed value of *initext*. !!! example ``` {{ (fromIni "[section]\nkey = value").section.key }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/fromJson.md ================================================ # `fromJson` *jsontext* `fromJson` parses *jsontext* as JSON and returns the parsed value. JSON numbers that can be represented exactly as 64-bit signed integers are returned as such. Otherwise, if the number is in the range of 64-bit IEEE floating point values, it is returned as such. Otherwise, the number is returned as a string. See [RFC7159 Section 6][rfc7159s6]. [rfc7159S6]: https://www.rfc-editor.org/rfc/rfc7159#section-6 ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/fromJsonc.md ================================================ # `fromJsonc` *jsonctext* `fromJsonc` parses *jsonctext* as JSONC using [`github.com/tailscale/hujson`][hujson] and returns the parsed value. [hujson]: https://github.com/tailscale/hujson ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/fromToml.md ================================================ # `fromToml` *tomltext* `fromToml` returns the parsed value of *tomltext*. !!! example ``` {{ (fromToml "[section]\nkey = \"value\"").section.key }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/fromYaml.md ================================================ # `fromYaml` *yamltext* `fromYaml` returns the parsed value of *yamltext*. !!! example ``` {{ (fromYaml "key1: value\nkey2: value").key2 }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/getRedirectedURL.md ================================================ # `getRedirectedURL` *url* `getRedirectedURL` returns the final URL after following any HTTP redirects from the given *url*. If the *url* does not redirect, it returns the original *url*. `getRedirectedURL` is not hermetic: its return value depends on the state of the network and the remote server at the moment the template is executed. Exercise caution when using it in your templates. !!! example ``` {{ getRedirectedURL "https://github.com/twpayne/chezmoi/releases/latest" }} {{ getRedirectedURL "https://github.com/twpayne/chezmoi/raw/HEAD/README.md" }} {{ getRedirectedURL "https://git.io/chezmoi" }} ``` This will return something like: ``` https://github.com/twpayne/chezmoi/releases/tag/v2.62.7 https://raw.githubusercontent.com/twpayne/chezmoi/aa57d1d773715e02103e87f78c58b99f9b91fc0c/README.md https://raw.githubusercontent.com/twpayne/chezmoi/master/assets/scripts/install.sh ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/glob.md ================================================ # `glob` *pattern* `glob` returns the list of files matching *pattern* according to [`doublestar.Glob`][glob]. Relative paths are interpreted relative to the destination directory. [glob]: https://pkg.go.dev/github.com/bmatcuk/doublestar/v4#Glob ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/hexDecode.md ================================================ # `hexDecode` *hextext* `hexDecode` returns *hextext* decoded from a hex-encoding string. !!! example ``` {{ hexDecode "68656c6c6f" }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/hexEncode.md ================================================ # `hexEncode` *string* `hexEncode` returns *string* encoded as a hex string. !!! example ``` {{ hexEncode "example" }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/include.md ================================================ # `include` *filename* `include` returns the literal contents of the file named `*filename*`. Relative paths are interpreted relative to the source directory. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/includeTemplate.md ================================================ # `includeTemplate` *filename* [*data*] `includeTemplate` returns the result of executing the contents of *filename* with the optional *data*. Relative paths are first searched for in `.chezmoitemplates` and, if not found, are interpreted relative to the source directory. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/index.md ================================================ # Functions All standard [`text/template`][go-template] and [text template functions from `sprig`][sprig] are included. chezmoi provides some additional functions. [go-template]: https://pkg.go.dev/text/template [sprig]: http://masterminds.github.io/sprig/ ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/ioreg.md ================================================ # `ioreg` On macOS, `ioreg` returns the structured output of the `ioreg -a -l` command, which includes detailed information about the I/O Kit registry. On non-macOS operating systems, `ioreg` returns `nil`. The output from `ioreg` is cached so multiple calls to the `ioreg` function will only execute the `ioreg -a -l` command once. !!! example ``` {{ if eq .chezmoi.os "darwin" }} {{ $serialNumber := index ioreg "IORegistryEntryChildren" 0 "IOPlatformSerialNumber" }} {{ end }} ``` !!! warning The `ioreg` function can be very slow and should not be used. It will be removed in a later version of chezmoi. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/isExecutable.md ================================================ # `isExecutable` *file* `isExecutable` returns true if a file is executable. !!! example ``` {{ if isExecutable "/bin/echo" }} # echo is executable {{ end }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/joinPath.md ================================================ # `joinPath` *element*... `joinPath` joins any number of path elements into a single path, separating them with the OS-specific path separator. Empty elements are ignored. The result is cleaned. If the argument list is empty or all its elements are empty, `joinPath` returns an empty string. On Windows, the result will only be a UNC path if the first non-empty element is a UNC path. !!! example ```text {{ joinPath .chezmoi.homeDir ".zshrc" }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/jq.md ================================================ # `jq` *query* *input* `jq` runs the [jq][jq] query *query* against *input* and returns a list of results. !!! example ``` {{ dict "key" "value" | jq ".key" | first }} ``` !!! warning `jq` uses [`github.com/itchyny/gojq`][gojq], which behaves slightly differently to the `jq` command in some [edge cases][cases]. [jq]: https://jqlang.org [gojq]: https://github.com/itchyny/gojq [cases]: https://github.com/itchyny/gojq#difference-to-jq ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/lookPath.md ================================================ # `lookPath` *file* `lookPath` searches for an executable named *file* in the directories named by the `PATH` environment variable. If file contains a slash, it is tried directly and the `PATH` is not consulted. The result may be an absolute path or a path relative to the current directory. If *file* is not found, `lookPath` returns an empty string. `lookPath` is not hermetic: its return value depends on the state of the environment and the file system at the moment the template is executed. Exercise caution when using it in your templates. The return value of the first successful call to `lookPath` is cached, and future calls to `lookPath` for the same *file* will return this path. !!! example ``` {{ if lookPath "diff-so-fancy" }} # diff-so-fancy is in $PATH {{ end }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/lstat.md ================================================ # `lstat` *name* `lstat` runs [`os.Lstat`][lstat] on *name*. If *name* exists it returns structured data. If *name* does not exist then it returns a false value. If `os.Lstat` returns any other error then it raises an error. The structured value returned if *name* exists contains the fields `name`, `size`, `mode`, `perm`, `modTime`, `isDir`, and `type`. `lstat` is not hermetic: its return value depends on the state of the file system at the moment the template is executed. Exercise caution when using it in your templates. !!! example ``` {{ if eq (joinPath .chezmoi.homeDir ".xinitrc" | lstat).type "symlink" }} # ~/.xinitrc exists and is a symlink {{ end }} ``` [lstat]: https://pkg.go.dev/os#File.Lstat ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/mozillaInstallHash.md ================================================ # `mozillaInstallHash` *path* `mozillaInstallHash` returns the Mozilla install hash for *path*. This is a convenience function to assist the management of Firefox profiles. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/output.md ================================================ # `output` *name* [*arg*...] `output` returns the output of executing the command *name* with *arg*s. If executing the command returns an error then template execution exits with an error. The execution occurs every time that the template is executed. It is the user's responsibility to ensure that executing the command is both idempotent and fast. !!! example ``` current-context: {{ output "kubectl" "config" "current-context" | trim }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/outputList.md ================================================ # `outputList` *name* [*argList*] `outputList` returns the output of executing the command *name* with the *argList*. If executing the command returns an error then template execution exits with an error. The execution occurs every time that the template is executed. It is the user's responsibility to ensure that executing the command is both idempotent and fast. This differs from [`output`][output] in that it allows for the *argsList* to be created programmatically. !!! example ``` {{- $args := (list "config" "current-context") }} current-context: {{ outputList "kubectl" $args | trim }} ``` [output]: /reference/templates/functions/output.md ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/pruneEmptyDicts.md ================================================ # `pruneEmptyDicts` *dict* `pruneEmptyDicts` modifies *dict* to remove nested empty dicts. Properties are pruned from the bottom up, so any nested dicts that themselves only contain empty dicts are pruned. !!! example ``` {{ dict "key" "value" "inner" (dict) | pruneEmptyDicts | toJson }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/quoteList.md ================================================ # `quoteList` *list* `quoteList` returns a list where each element is the corresponding element in *list* quoted. !!! example ``` {{ $args := list "alpha" "beta" "gamma" }} command {{ $args | quoteList }} ``` ``` [section] array = [{{- $list | quoteList | join ", " -}}] ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/replaceAllRegex.md ================================================ # `replaceAllRegex` *expr* *repl* *text* `replaceAllRegex` returns *text* with all substrings matching the regular expression *expr* replaced with *repl*. It is an alternative to [sprig's `regexpReplaceAll` function][sprig] with a different argument order that supports pipelining. !!! example ``` {{ "foo subject string" | replaceAllRegex "foo" "bar" }} ``` [sprig]: http://masterminds.github.io/sprig/strings.html ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/setValueAtPath.md ================================================ # `setValueAtPath` *path* *value* *dict* `setValueAtPath` modifies *dict* to set the value at *path* to *value* and returns *dict*. *path* can be either a string containing a `.`-separated list of keys or a list of keys. The function will create new key/value pairs in *dict* if needed. This is an alternative to [sprig's `set` function][dict] with a different argument order that supports pipelining. !!! example ``` {{ dict | setValueAtPath "key1" "value1" | setValueAtPath "key2.nestedKey" "value2" | toJson }} {{ dict | setValueAtPath (list "key2" "nestedKey") "value2" | toJson }} ``` [dict]: http://masterminds.github.io/sprig/dicts.html ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/stat.md ================================================ # `stat` *name* `stat` runs [`os.Stat`][stat] on *name*. If *name* exists it returns structured data. If *name* does not exist then it returns a false value. If `os.Stat` returns any other error then it raises an error. The structured value returned if *name* exists contains the fields `name`, `size`, `mode`, `perm`, `modTime`, `isDir`, and `type`. `stat` is not hermetic: its return value depends on the state of the file system at the moment the template is executed. Exercise caution when using it in your templates. !!! example ``` {{ if stat (joinPath .chezmoi.homeDir ".pyenv") }} # ~/.pyenv exists {{ end }} ``` [stat]: https://pkg.go.dev/os#File.Stat ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/toIni.md ================================================ # `toIni` *value* `toIni` returns the ini representation of *value*, which must be a dict. !!! example ``` {{ dict "key" "value" "section" (dict "subkey" "subvalue") | toIni }} ``` !!! warning The ini format is not well defined, and the particular variant generated by `toIni` might not be suitable for you. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/toPrettyJson.md ================================================ # `toPrettyJson` [*indent*] *value* `toPrettyJson` returns the JSON representation of *value*. The optional *indent* specifies how much nested elements are indented relative to their parent. *indent* defaults to two spaces. !!! examples ``` {{ dict "a" (dict "b" "c") | toPrettyJson "\t" }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/toString.md ================================================ # `toString` *value* `toString` returns the string representation of *value*. Notably, if *value* is a pointer, then it is safely dereferenced. If *value* is a nil pointer, then `toString` returns the string representation of the zero value of the pointee's type. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/toStrings.md ================================================ # `toStrings` [*value*...] `toStrings` converts each argument to a string and returns the list of strings. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/toToml.md ================================================ # `toToml` *value* `toToml` returns the TOML representation of *value*. !!! example ``` {{ dict "key" "value" | toToml }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/toYaml.md ================================================ # `toYaml` *value* `toYaml` returns the YAML representation of *value*. !!! example ``` {{ dict "key" "value" | toYaml }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/functions/warnf.md ================================================ # `warnf` *format* [*arg*...] `warnf` prints a message to stderr prefixed by `chezmoi: warning:` and returns the empty string. *format* is interpreted as a [printf-style format string][fmt] with the given *arg*s. [fmt]: https://pkg.go.dev/fmt#hdr-Printing ================================================ FILE: assets/chezmoi.io/docs/reference/templates/github-functions/gitHubKeys.md ================================================ # `gitHubKeys` *user* `gitHubKeys` returns *user*'s public SSH keys from GitHub using the GitHub API. The returned value is a slice of structs with `.ID` and `.Key` fields. !!! warning If you use this function to populate your `~/.ssh/authorized_keys` file then you potentially open SSH access to anyone who is able to modify or add to your GitHub public SSH keys, possibly including certain GitHub employees. You should not use this function on publicly-accessible machines and should always verify that no unwanted keys have been added, for example by using the `-v` / `--verbose` option when running `chezmoi apply` or `chezmoi update`. Additionally, GitHub automatically [removes keys which haven't been used in the last year][timeout]. This may cause your keys to be removed from `~/.ssh/authorized_keys` suddenly, and without any warning or indication of the removal. You should provide one or more keys in plain text alongside this function to avoid unknowingly losing remote access to your machine. !!! example ``` {{ range gitHubKeys "user" }} {{- .Key }} {{ end }} ``` [timeout]: https://docs.github.com/en/authentication/troubleshooting-ssh/deleted-or-missing-ssh-keys ================================================ FILE: assets/chezmoi.io/docs/reference/templates/github-functions/gitHubLatestRelease.md ================================================ # `gitHubLatestRelease` *owner-repo* `gitHubLatestRelease` calls the GitHub API to retrieve the latest release about the given *owner-repo*, returning structured data as defined by the [GitHub Go API bindings][bindings]. Calls to `gitHubLatestRelease` are cached so calling `gitHubLatestRelease` with the same *owner-repo* will only result in one call to the GitHub API. !!! example ``` {{ (gitHubLatestRelease "docker/compose").TagName }} ``` !!! hint Some fields in the returned object have type `*string`. Use the [`toString` template function][toString] to convert these to strings. [bindings]: https://pkg.go.dev/github.com/google/go-github/v61/github#RepositoryRelease [toString]: ../functions/toString.md ================================================ FILE: assets/chezmoi.io/docs/reference/templates/github-functions/gitHubLatestReleaseAssetURL.md ================================================ # `gitHubLatestReleaseAssetURL` *owner-repo* *pattern* `gitHubLatestReleaseAssetURL` calls the GitHub API to retrieve the latest release about the given *owner-repo*, returning structured data as defined by the [GitHub Go API bindings][bindings]. It then iterates through all the release's assets, returning the first one that matches *pattern*. *pattern* is a shell pattern as [described in `path.Match`][match]. Calls to `gitHubLatestReleaseAssetURL` are cached so calling `gitHubLatestReleaseAssetURL` with the same *owner-repo* will only result in one call to the GitHub API. !!! example ``` {{ gitHubLatestReleaseAssetURL "FiloSottile/age" (printf "age-*-%s-%s.tar.gz" .chezmoi.os .chezmoi.arch) }} {{ gitHubLatestReleaseAssetURL "twpayne/chezmoi" (printf "chezmoi-%s-%s" .chezmoi.os .chezmoi.arch) }} ``` !!! hint Some fields in the returned object have type `*string`. Use the [`toString` template function][toString] to convert these to strings. [bindings]: https://pkg.go.dev/github.com/google/go-github/v61/github#RepositoryRelease [match]: https://pkg.go.dev/path#Match [toString]: ../functions/toString.md ================================================ FILE: assets/chezmoi.io/docs/reference/templates/github-functions/gitHubLatestTag.md ================================================ # `gitHubLatestTag` *owner-repo* `gitHubLatestTag` calls the GitHub API to retrieve the latest tag for the given *owner-repo*, returning structured data as defined by the [GitHub Go API bindings][bindings]. Calls to `gitHubLatestTag` are cached the same as [`githubTags`][tags], so calling `gitHubLatestTag` with the same *owner-repo* will only result in one call to the GitHub API. !!! example ``` {{ (gitHubLatestTag "docker/compose").Name }} ``` !!! warning `gitHubLatestTag` returns the first tag returned by the [list repository tags GitHub API endpoint][endpoint]. Although this seems to be the most recent tag, the GitHub API documentation does not specify the order of the returned tags. !!! hint Some fields in the returned object have type `*string`. Use the [`toString` template function][toString] to convert these to strings. [bindings]: https://pkg.go.dev/github.com/google/go-github/v61/github#RepositoryTag [endpoint]: https://docs.github.com/en/rest/repos/repos#list-repository-tags [tags]: /reference/templates/github-functions/gitHubTags.md [toString]: ../functions/toString.md ================================================ FILE: assets/chezmoi.io/docs/reference/templates/github-functions/gitHubRelease.md ================================================ # `gitHubRelease` *owner-repo* *version* `gitHubRelease` calls the GitHub API to retrieve the latest releases about the given *owner-repo*, It iterates through all the versions of the release, fetching the first entry equal to *version*. It then returns structured data as defined by the [GitHub Go API bindings][bindings]. Calls to `gitHubRelease` are cached so calling `gitHubRelease` with the same *owner-repo* *version* will only result in one call to the GitHub API. !!! example ``` {{ (gitHubRelease "docker/compose" "v2.29.1").TagName }} ``` !!! hint Some fields in the returned object have type `*string`. Use the [`toString` template function][toString] to convert these to strings. [bindings]: https://pkg.go.dev/github.com/google/go-github/v61/github#RepositoryRelease [toString]: ../functions/toString.md ================================================ FILE: assets/chezmoi.io/docs/reference/templates/github-functions/gitHubReleaseAssetURL.md ================================================ # `gitHubReleaseAssetURL` *owner-repo* *version* *pattern* `gitHubReleaseAssetURL` calls the GitHub API to retrieve the latest releases about the given *owner-repo*, returning structured data as defined by the [GitHub Go API bindings][bindings]. It iterates through all the versions of the release, returning the first entry equal to *version*. It then iterates through all the release's assets, returning the first one that matches *pattern*. *pattern* is a shell pattern as [described in `path.Match`][match]. Calls to `gitHubReleaseAssetURL` are cached so calling `gitHubReleaseAssetURL` with the same *owner-repo* will only result in one call to the GitHub API. !!! example ``` {{ gitHubReleaseAssetURL "FiloSottile/age" "age v1.2.0" (printf "age-*-%s-%s.tar.gz" .chezmoi.os .chezmoi.arch) }} {{ gitHubReleaseAssetURL "twpayne/chezmoi" "v2.50.0" (printf "chezmoi-%s-%s" .chezmoi.os .chezmoi.arch) }} ``` [bindings]: https://pkg.go.dev/github.com/google/go-github/v61/github#RepositoryRelease [match]: https://pkg.go.dev/path#Match ================================================ FILE: assets/chezmoi.io/docs/reference/templates/github-functions/gitHubReleases.md ================================================ # `gitHubReleases` *owner-repo* `gitHubReleases` calls the GitHub API to retrieve the first page of releases for the given *owner-repo*, returning structured data as defined by the [GitHub Go API bindings][github-go]. Calls to `gitHubReleases` are cached so calling `gitHubReleases` with the same *owner-repo* will only result in one call to the GitHub API. !!! example ``` {{ (index (gitHubReleases "docker/compose") 0).TagName }} ``` !!! note The maximum number of items returned by `gitHubReleases` is determined by default page size for the GitHub API. !!! warning The values returned by `gitHubReleases` are not directly queryable via the [`jq`][jq] function and must instead be converted through JSON: ``` {{ gitHubReleases "docker/compose" | toJson | fromJson | jq ".[0].tag_name" }} ``` !!! hint Some fields in the returned object have type `*string`. Use the [`toString` template function][toString] to convert these to strings. [github-go]: https://pkg.go.dev/github.com/google/go-github/v61/github#RepositoryRelease [jq]: /reference/templates/functions/jq.md [toString]: ../functions/toString.md ================================================ FILE: assets/chezmoi.io/docs/reference/templates/github-functions/gitHubTags.md ================================================ # `gitHubTags` *owner-repo* `gitHubTags` calls the GitHub API to retrieve the first page of tags for the given *owner-repo*, returning structured data as defined by the [GitHub Go API bindings][github-go]. Calls to `gitHubTags` are cached so calling `gitHubTags` with the same *owner-repo* will only result in one call to the GitHub API. !!! example ``` {{ (index (gitHubTags "docker/compose") 0).Name }} ``` !!! note The maximum number of items returned by `gitHubReleases` is determined by default page size for the GitHub API. !!! warning The values returned by `gitHubTags` are not directly queryable via the [`jq`][jq] function and must instead be converted through JSON: ``` {{ gitHubTags "docker/compose" | toJson | fromJson | jq ".[0].name" }} ``` !!! hint Some fields in the returned object have type `*string`. Use the [`toString` template function][toString] to convert these to strings. [github-go]: https://pkg.go.dev/github.com/google/go-github/v61/github#RepositoryTag [jq]: /reference/templates/functions/jq.md [toString]: ../functions/toString.md ================================================ FILE: assets/chezmoi.io/docs/reference/templates/github-functions/index.md ================================================ # GitHub functions The `gitHub*` template functions return data from the GitHub API. By default, chezmoi makes anonymous GitHub API requests, which are subject to [GitHub's rate limits][limits] (currently 60 requests per hour per source IP address). chezmoi caches results from identical GitHub API requests for the period defined in `gitHub.refreshPeriod` (default one minute). If any of the environment variables `$CHEZMOI_GITHUB_ACCESS_TOKEN`, `$GITHUB_ACCESS_TOKEN`, or `$GITHUB_TOKEN` are found, then the first one found will be used to authenticate the GitHub API requests which have a higher rate limit (currently 5,000 requests per hour per user). In practice, GitHub API rate limits are high enough chezmoi's caching of results mean that you should rarely need to set a token, unless you are sharing a source IP address with many other GitHub users. If needed, the GitHub documentation describes how to [create a personal access token][pat]. [limits]: https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting [pat]: https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token ================================================ FILE: assets/chezmoi.io/docs/reference/templates/gopass-functions/gopass.md ================================================ # `gopass` *gopass-name* `gopass` returns passwords stored in [gopass][gopass] using the gopass CLI (`gopass`). *gopass-name* is passed to `gopass show --password $GOPASS_NAME` and the first line of the output of `gopass` is returned with the trailing newline stripped. The output from `gopass` is cached so calling `gopass` multiple times with the same *gopass-name* will only invoke `gopass` once. !!! example ``` {{ gopass "$PASS_NAME" }} ``` [gopass]: https://www.gopass.pw/ ================================================ FILE: assets/chezmoi.io/docs/reference/templates/gopass-functions/gopassRaw.md ================================================ # `gopassRaw` *gopass-name* `gopass` returns raw passwords stored in [gopass][gopass] using the gopass CLI (`gopass`). *gopass-name* is passed to `gopass show --noparsing $GOPASS_NAME` and the output is returned. The output from `gopassRaw` is cached so calling `gopassRaw` multiple times with the same *gopass-name* will only invoke `gopass` once. [gopass]: https://www.gopass.pw/ ================================================ FILE: assets/chezmoi.io/docs/reference/templates/gopass-functions/index.md ================================================ # gopass functions The `gopass*` template functions return data stored in [gopass][gopass] using the gopass CLI (`gopass`) or builtin code. By default, chezmoi will use the gopass CLI (`gopass`). Depending on your gopass configuration, you may have to enter your passphrase once for each secret. When setting `gopass.mode` to `builtin`, chezmoi use builtin code to access the goapass database and caches your passphrase in plaintext in memory until chezmoi terminates. !!! warning Using the builtin code is experimental and may be removed. [gopass]: https://www.gopass.pw/ ================================================ FILE: assets/chezmoi.io/docs/reference/templates/index.md ================================================ # Templates chezmoi executes templates using [`text/template`][go-template]. The result is treated differently depending on whether the target is a file or a symlink. If target is a file, then: - If the result is an empty string, then the file is removed. - Otherwise, the target file contents are result. If the target is a symlink, then: - Leading and trailing whitespace are stripped from the result. - If the result is an empty string, then the symlink is removed. - Otherwise, the target symlink target is the result. chezmoi executes templates using `text/template`'s `missingkey=error` option, which means that misspelled or missing keys will raise an error. This can be overridden by setting a list of options in the configuration file. !!! hint For a full list of template options, see [`Template.Option`][option]. !!! example ```toml title="~/.config/chezmoi/chezmoi.toml" [template] options = ["missingkey=zero"] ``` [go-template]: https://pkg.go.dev/text/template [option]: https://pkg.go.dev/text/template?tab=doc#Template.Option ================================================ FILE: assets/chezmoi.io/docs/reference/templates/init-functions/exit.md ================================================ # `exit` *code* `exit` stops template execution and causes chezmoi to exit with *code*. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/init-functions/index.md ================================================ # Init functions These template functions are only available when generating a config file with `chezmoi init`. For testing with `chezmoi execute-template`, pass the `--init` flag to enable them. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/init-functions/promptBool.md ================================================ # `promptBool` *prompt* [*default*] `promptBool` prompts the user with *prompt* and returns the user's response interpreted as a boolean. If *default* is passed and the user's response is empty then it returns *default*. The user's response is interpreted as follows (case insensitive): | Response | Result | | ----------------------- | ------- | | 1, on, t, true, y, yes | `true` | | 0, off, f, false, n, no | `false` | ================================================ FILE: assets/chezmoi.io/docs/reference/templates/init-functions/promptBoolOnce.md ================================================ # `promptBoolOnce` *map* *path* *prompt* [*default*] `promptBoolOnce` returns the value of *map* at *path* if it exists and is a boolean value, otherwise it prompts the user for a boolean value with *prompt* and an optional *default* using `promptBool`. !!! example ``` {{ $hasGUI := promptBoolOnce . "hasGUI" "Does this machine have a GUI" }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/init-functions/promptChoice.md ================================================ # `promptChoice` *prompt* *choices* [*default*] `promptChoice` prompts the user with *prompt* and *choices* and returns the user's response. *choices* must be a list of strings. If *default* is passed and the user's response is empty then it returns *default*. !!! example ``` {{- $choices := list "desktop" "server" -}} {{- $hosttype := promptChoice "What type of host are you on" $choices -}} [data] hosttype = {{- $hosttype | quote -}} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/init-functions/promptChoiceOnce.md ================================================ # `promptChoiceOnce` *map* *path* *prompt* *choices* [*default*] `promptChoiceOnce` returns the value of *map* at *path* if it exists and is a string, otherwise it prompts the user for one of *choices* with *prompt* and an optional *default* using [`promptChoice`][pc]. !!! example ``` {{- $choices := list "desktop" "laptop" "server" "termux" -}} {{- $hosttype := promptChoiceOnce . "hosttype" "What type of host are you on" $choices -}} [data] hosttype = {{- $hosttype | quote -}} ``` [pc]: /reference/templates/init-functions/promptChoice.md ================================================ FILE: assets/chezmoi.io/docs/reference/templates/init-functions/promptInt.md ================================================ # `promptInt` *prompt* [*default*] `promptInt` prompts the user with *prompt* and returns the user's response interpreted as an integer. If *default* is passed and the user's response is empty then it returns *default*. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/init-functions/promptIntOnce.md ================================================ # `promptIntOnce` *map* *path* *prompt* [*default*] `promptIntOnce` returns the value of *map* at *path* if it exists and is an integer value, otherwise it prompts the user for a integer value with *prompt* and an optional *default* using `promptInt`. !!! example ``` {{ $monitors := promptIntOnce . "monitors" "How many monitors does this machine have" }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/init-functions/promptMultichoice.md ================================================ # `promptMultichoice` *prompt* *choices* [*default*] `promptMultichoice` prompts the user with *prompt* and *choices* and returns the user's response. *choices* must be a list of strings. If a *default* list is passed and the user's response is empty then it returns *default*. !!! example ``` {{- $choices := list "chocolate" "strawberry" "vanilla" "pistachio" -}} {{- $icecream := promptMultichoice "What type of ice cream do you like" $choices (list "pistachio" "chocolate") -}} [data] icecream = {{- $icecream | toToml -}} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/init-functions/promptMultichoiceOnce.md ================================================ # `promptMultichoiceOnce` *map* *path* *prompt* *choices* [*default*] `promptMultichoiceOnce` returns the value of *map* at *path* if it exists and is a string, otherwise it prompts the user for one of *choices* with *prompt* and an optional list *default* using [`promptMultichoice`][pm]. !!! example ``` {{- $choices := list "chocolate" "strawberry" "vanilla" "pistachio" -}} {{- $icecream := promptMultichoiceOnce . "icecream" "What type of ice cream do you like" $choices (list "pistachio" "chocolate") -}} [data] icecream = {{- $icecream | toToml -}} ``` [pm]: /reference/templates/init-functions/promptMultichoice.md ================================================ FILE: assets/chezmoi.io/docs/reference/templates/init-functions/promptString.md ================================================ # `promptString` *prompt* [*default*] `promptString` prompts the user with *prompt* and returns the user's response with all leading and trailing spaces stripped. If *default* is passed and the user's response is empty then it returns *default*. !!! example ``` {{ $email := promptString "email" -}} [data] email = {{ $email | quote }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/init-functions/promptStringOnce.md ================================================ # `promptStringOnce` *map* *path* *prompt* [*default*] `promptStringOnce` returns the value of *map* at *path* if it exists and is a string value, otherwise it prompts the user for a string value with *prompt* and an optional *default* using `promptString`. !!! example ``` {{ $email := promptStringOnce . "email" "What is your email address" }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/init-functions/stdinIsATTY.md ================================================ # `stdinIsATTY` `stdinIsATTY` returns `true` if chezmoi's standard input is a TTY. It is primarily useful for determining whether `prompt*` functions should be called or default values be used. !!! example ``` {{ $email := "" }} {{ if stdinIsATTY }} {{ $email = promptString "email" }} {{ else }} {{ $email = "user@example.com" }} {{ end }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/init-functions/writeToStdout.md ================================================ # `writeToStdout` *string*... `writeToStdout` writes each *string* to stdout. !!! example ```text {{- writeToStdout "Hello, world\n" -}} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/keepassxc-functions/index.md ================================================ # KeePassXC functions The `keepassxc*` template functions return structured data retrieved from a [KeePassXC][keepassxc] database using the KeePassXC CLI (`keepassxc-cli`) The database is configured by setting `keepassxc.database` in the configuration file. You will be prompted for the database password the first time `keepassxc-cli` is run, and the password is cached, in plain text, in memory until chezmoi terminates. The command used can be changed by setting the `keepassxc.command` configuration variable, and extra arguments can be added by setting `keepassxc.args`. The password prompt can be disabled by setting `keepassxc.prompt` to `false`. By default, chezmoi will prompt for the KeePassXC password when required and cache it for the duration of chezmoi's execution. Setting `keepassxc.mode` to `open` will tell chezmoi to instead open KeePassXC's console with `keepassxc-cli open` followed by `keepassxc.args`. chezmoi will use this console to request values from KeePassXC. When setting `keepassxc.mode` to `builtin`, chezmoi uses a builtin library to access a keepassxc database, which can be handy if `keepassxc-cli` is not available. Some KeePassXC features (such as Yubikey-enhanced encryption) may not be available with builtin support. [keepassxc]: https://keepassxc.org/ ================================================ FILE: assets/chezmoi.io/docs/reference/templates/keepassxc-functions/keepassxc.md ================================================ # `keepassxc` *entry* `keepassxc` returns structured data for *entry* using `keepassxc-cli`. The output from `keepassxc-cli` is parsed into key-value pairs and cached so calling `keepassxc` multiple times with the same *entry* will only invoke `keepassxc-cli` once. !!! example ``` username = {{ (keepassxc "example.com").UserName }} password = {{ (keepassxc "example.com").Password }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/keepassxc-functions/keepassxcAttachment.md ================================================ # `keepassxcAttachment` *entry* *name* `keepassxcAttachment` returns the attachment with *name* of *entry* using `keepassxc-cli`. !!! info `keepassxcAttachment` requires `keepassxc-cli` version 2.7.0 or later. !!! example ``` {{- keepassxcAttachment "SSH Config" "config" -}} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/keepassxc-functions/keepassxcAttribute.md ================================================ # `keepassxcAttribute` *entry* *attribute* `keepassxcAttribute` returns the attribute *attribute* of *entry* using `keepassxc-cli`, with any leading or trailing whitespace removed. !!! example ``` {{ keepassxcAttribute "SSH Key" "private-key" }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/keeper-functions/index.md ================================================ # Keeper functions The `keeper*` functions return data from [Keeper][keeper] [Commander CLI][cli] (`keeper`). The command used can by changed by setting the `keeper.command` configuration variable, and extra arguments can be added by setting `keeper.args`. [keeper]: https://www.keepersecurity.com/ [cli]: https://docs.keeper.io/secrets-manager/commander-cli ================================================ FILE: assets/chezmoi.io/docs/reference/templates/keeper-functions/keeper.md ================================================ # `keeper` *uid* `keeper` returns structured data retrieved from [Keeper][keeper] using the [Commander CLI][cli]. *uid* is passed to `keeper get --format=json` and the output is parsed as JSON. [keeper]: https://www.keepersecurity.com/ [cli]: https://docs.keeper.io/secrets-manager/commander-cli ================================================ FILE: assets/chezmoi.io/docs/reference/templates/keeper-functions/keeperDataFields.md ================================================ # `keeperDataFields` *uid* `keeperDataFields` returns the `.data.fields` elements of `keeper get --format=json *uid*` indexed by `type`. ## Examples ```text url = {{ (keeperDataFields "$UID").url }} login = {{ index (keeperDataFields "$UID").login 0 }} password = {{ index (keeperDataFields "$UID").password 0 }} ``` ================================================ FILE: assets/chezmoi.io/docs/reference/templates/keeper-functions/keeperFindPassword.md ================================================ # `keeperFindPassword` *query* `keeperFindPassword` returns the output of `keeper find-password query`. *query* can be a UID or a path. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/keyring-functions/keyring.md ================================================ # `keyring` *service* *user* `keyring` retrieves the value associated with *service* and *user* from the user's keyring. | OS | Keyring | | ------- | --------------------------- | | macOS | Keychain | | Linux | GNOME Keyring | | Windows | Windows Credentials Manager | | FreeBSD | GNOME Keyring | !!! example ``` [github] user = {{ .github.user | quote }} token = {{ keyring "github" .github.user | quote }} ``` !!! warning On FreeBSD, the `keyring` template function is only available if chezmoi was compiled with cgo enabled. The official release binaries of chezmoi are **not** compiled with cgo enabled, and `keyring` will always return an empty string. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/lastpass-functions/index.md ================================================ # LastPass functions The `lastpass*` template functions return structured data from [LastPass][lastpass] using the [LastPass CLI][cli] (`lpass`). [lastpass]: https://lastpass.com/ [cli]: https://lastpass.github.io/lastpass-cli/lpass.1.html ================================================ FILE: assets/chezmoi.io/docs/reference/templates/lastpass-functions/lastpass.md ================================================ # `lastpass` *id* `lastpass` returns structured data from [LastPass][lastpass] using the [LastPass CLI][cli] (`lpass`). *id* is passed to `lpass show --json $ID` and the output from `lpass` is parsed as JSON. In addition, the `note` field, if present, is further parsed as colon-separated key-value pairs. The structured data is an array so typically the `index` function is used to extract the first item. The output from `lastpass` is cached so calling `lastpass` multiple times with the same *id* will only invoke `lpass` once. !!! example ``` githubPassword = {{ (index (lastpass "GitHub") 0).password | quote }} {{ (index (lastpass "SSH") 0).note.privateKey }} ``` [lastpass]: https://lastpass.com/ [cli]: https://lastpass.github.io/lastpass-cli/lpass.1.html ================================================ FILE: assets/chezmoi.io/docs/reference/templates/lastpass-functions/lastpassRaw.md ================================================ # `lastpassRaw` *id* `lastpassRaw` returns structured data from [LastPass][lastpass] using the [LastPass CLI][cli] (`lpass`). It behaves identically to the `lastpass` function, except that no further parsing is done on the `note` field. !!! example ``` {{ (index (lastpassRaw "SSH Private Key") 0).note }} ``` [lastpass]: https://lastpass.com/ [cli]: https://lastpass.github.io/lastpass-cli/lpass.1.html ================================================ FILE: assets/chezmoi.io/docs/reference/templates/pass-functions/index.md ================================================ # pass functions The `pass` template functions return passwords stored in [pass][pass] using the pass CLI (`pass`). !!! hint To use a pass-compatible password manager like [passage][passage], set `pass.command` to the name of the binary and use chezmoi's `pass*` template functions as if you were using pass. ```toml title="~/.config/chezmoi/chezmoi.toml" [pass] command = "passage" ``` [pass]: https://www.passwordstore.org/ [passage]: https://github.com/FiloSottile/passage, ================================================ FILE: assets/chezmoi.io/docs/reference/templates/pass-functions/pass.md ================================================ # `pass` *pass-name* `pass` returns passwords stored in [pass][pass] using the pass CLI (`pass`). *pass-name* is passed to `pass show $PASS_NAME` and the first line of the output of `pass` is returned with the trailing newline stripped. The output from `pass` is cached so calling `pass` multiple times with the same *pass-name* will only invoke `pass` once. !!! example ``` {{ pass "$PASS_NAME" }} ``` [pass]: https://www.passwordstore.org/ ================================================ FILE: assets/chezmoi.io/docs/reference/templates/pass-functions/passFields.md ================================================ # `passFields` *pass-name* `passFields` returns structured data stored in [pass][pass] using the pass CLI (`pass`). *pass-name* is passed to `pass show $PASS_NAME` and the output is parsed as colon-separated key-value pairs, one per line. The return value is a map of keys to values. !!! example Given the output from `pass`: ``` GitHub login: username password: secret ``` the return value will be the map: ```json { "login": "username", "password": "secret" } ``` !!! example ``` {{ (passFields "GitHub").password }} ``` [pass]: https://www.passwordstore.org ================================================ FILE: assets/chezmoi.io/docs/reference/templates/pass-functions/passRaw.md ================================================ # `passRaw` *pass-name* `passRaw` returns passwords stored in [pass][pass] using the pass CLI (`pass`). *pass-name* is passed to `pass show $PASS_NAME` and the output is returned. The output from `pass` is cached so calling `passRaw` multiple times with the same *pass-name* will only invoke `pass` once. [pass]: https://www.passwordstore.org/ ================================================ FILE: assets/chezmoi.io/docs/reference/templates/passhole-functions/index.md ================================================ # Passhole chezmoi includes support for [KeePass][keepass] using the [Passhole CLI][cli] (`ph`). [keepass]: https://keepass.info/ [cli]: https://github.com/Evidlo/passhole ================================================ FILE: assets/chezmoi.io/docs/reference/templates/passhole-functions/passhole.md ================================================ # passhole *path* *field* `passhole` returns the *field* of *path* from a [KeePass][keepass] database using [passhole][passhole]'s `ph` command. !!! example ``` {{ passhole "example.com" "password" }} ``` [keepass]: https://keepass.info/ [passhole]: https://github.com/Evidlo/passhole ================================================ FILE: assets/chezmoi.io/docs/reference/templates/protonpass-functions/index.md ================================================ # Proton Pass The `protonPass*` template functions return structured data from [Proton Pass][protonpass] using the [Proton Pass CLI][protonpass-cli] (`pass-cli`). [protonpass]: https://proton.me/pass [protonpass-cli]: https://protonpass.github.io/pass-cli/ ================================================ FILE: assets/chezmoi.io/docs/reference/templates/protonpass-functions/protonPass.md ================================================ # `protonPass` *uri* `protonPass` returns the item associated with *uri* from [Proton Pass][protonpass] using the [Proton Pass CLI][protonpass-cli]. !!! example ``` {{ protonPass "pass://$SHARE_ID/$ITEM_ID/$FIELD" }} ``` [protonpass]: https://proton.me/pass [protonpass-cli]: https://protonpass.github.io/pass-cli/ ================================================ FILE: assets/chezmoi.io/docs/reference/templates/protonpass-functions/protonPassJSON.md ================================================ # `protonPassJSON` *uri* `protonPassJSON` returns the structured data associated with *uri* from [Proton Pass][protonpass] using the [Proton Pass CLI][protonpass-cli]. !!! example ``` {{ (protonPassJSON "pass://$SHARE_ID/$ITEM_ID").item.content.content.key.password }} ``` [protonpass]: https://proton.me/pass [protonpass-cli]: https://protonpass.github.io/pass-cli/ ================================================ FILE: assets/chezmoi.io/docs/reference/templates/secret-functions/index.md ================================================ # Generic secret functions The `secret*` template functions return the output of the generic secret command defined by the `secret.command` configuration variable. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/secret-functions/secret.md ================================================ # `secret` [*arg*...] `secret` returns the output of the generic secret command defined by the `secret.command` configuration variable with `secret.args` and *arg*s with leading and trailing whitespace removed. The output is cached so multiple calls to `secret` with the same *arg*s will only invoke the generic secret command once. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/secret-functions/secretJSON.md ================================================ # `secretJSON` [*arg*...] `secretJSON` returns structured data from the generic secret command defined by the `secret.command` configuration variable with `secret.args` and *arg*s. The output is parsed as JSON. The output is cached so multiple calls to `secret` with the same *args* will only invoke the generic secret command once. ================================================ FILE: assets/chezmoi.io/docs/reference/templates/variables.md ================================================ # Variables chezmoi provides the following automatically-populated variables: | Variable | Type | Value | | ---------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | `.chezmoi.arch` | string | Architecture, e.g. `amd64`, `arm`, etc. as returned by [runtime.GOARCH][constants] | | `.chezmoi.args` | []string | The arguments passed to the `chezmoi` command, starting with the program command | | `.chezmoi.cacheDir` | string | The cache directory | | `.chezmoi.config` | object | The configuration, as read from the config file | | `.chezmoi.configFile` | string | The path to the configuration file used by chezmoi | | `.chezmoi.destDir` | string | The destination directory | | `.chezmoi.executable` | string | The path to the `chezmoi` executable, if available | | `.chezmoi.fqdnHostname` | string | The fully-qualified domain name hostname of the machine chezmoi is running on | | `.chezmoi.gid` | string | The primary group ID | | `.chezmoi.group` | string | The group of the user running chezmoi | | `.chezmoi.homeDir` | string | The home directory of the user running chezmoi | | `.chezmoi.hostname` | string | The hostname of the machine chezmoi is running on, up to the first `.` | | `.chezmoi.kernel` | object | Contains information from `/proc/sys/kernel`. Linux only, useful for detecting specific kernels (e.g. Microsoft's WSL kernel) | | `.chezmoi.os` | string | Operating system, e.g. `darwin`, `linux`, etc. as returned by [runtime.GOOS][constants] | | `.chezmoi.osRelease` | object | The information from `/etc/os-release`, Linux only, run `chezmoi data` to see its output | | `.chezmoi.pathListSeparator` | string | The path list separator, typically `;` on Windows and `:` on other systems. Used to separate paths in environment variables. i.e., `/bin:/sbin:/usr/bin` | | `.chezmoi.pathSeparator` | string | The path separator, typically `\` on windows and `/` on unix. Used to separate files and directories in a path. i.e., `c:\see\dos\run` | | `.chezmoi.sourceDir` | string | The source directory | | `.chezmoi.sourceFile` | string | The path of the template relative to the source directory | | `.chezmoi.targetFile` | string | The absolute path of the target file for the template | | `.chezmoi.uid` | string | The user ID | | `.chezmoi.username` | string | The username of the user running chezmoi | | `.chezmoi.version.builtBy` | string | The program that built the `chezmoi` executable, if set | | `.chezmoi.version.commit` | string | The git commit at which the `chezmoi` executable was built, if set | | `.chezmoi.version.date` | string | The timestamp at which the `chezmoi` executable was built, if set | | `.chezmoi.version.version` | string | The version of chezmoi | | `.chezmoi.windowsVersion` | object | Windows version information, if running on Windows | | `.chezmoi.workingTree` | string | The working tree of the source directory | `.chezmoi.windowsVersion` contains the following keys populated from the registry key `Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion`. | Key | Type | | --------------------------- | ------- | | `currentBuild` | string | | `currentMajorVersionNumber` | integer | | `currentMinorVersionNumber` | integer | | `currentVersion` | string | | `displayVersion` | string | | `editionID` | string | | `productName` | string | Additional variables can be defined in the config file in the `data` section. Variable names must consist of a letter and be followed by zero or more letters and/or digits. [constants]: https://pkg.go.dev/runtime?tab=doc#pkg-constants ================================================ FILE: assets/chezmoi.io/docs/reference/templates/vault-functions/vault.md ================================================ # `vault` *key* `vault` returns structured data from [Vault][vault] using the [Vault CLI][cli] (`vault`). *key* is passed to `vault kv get -format=json $KEY` and the output from `vault` is parsed as JSON. The output from `vault` is cached so calling `vault` multiple times with the same *key* will only invoke `vault` once. !!! example ``` {{ (vault "$KEY").data.data.password }} ``` [vault]: https://www.vaultproject.io/ [cli]: https://www.vaultproject.io/docs/commands/ ================================================ FILE: assets/chezmoi.io/docs/user-guide/advanced/customize-your-source-directory.md ================================================ # Customize your source directory ## Use a subdirectory of your dotfiles repo as the root of the source state By default, chezmoi uses the root of your dotfiles repo as the root of the source state. If your source state contains many entries in its root, then your target directory (usually your home directory) will in turn be filled with many entries in its root as well. You can reduce the number of entries by keeping `.chezmoiignore` up to date, but this can become tiresome. Instead, you can specify that chezmoi should read the source state from a subdirectory of the source directory instead by creating a file called `.chezmoiroot` containing the relative path to this subdirectory. For example, given: ``` title="~/.local/share/chezmoi/.chezmoiroot" home ``` Then chezmoi will read the source state from the `home` subdirectory of your source directory, for example the desired state of `~/.gitconfig` will be read from `~/.local/share/chezmoi/home/dot_gitconfig` (instead of `~/.local/share/chezmoi/dot_gitconfig`). When migrating an existing chezmoi dotfiles repo to use `.chezmoiroot` you will need to move the relevant files in to the new root subdirectory manually, including special "root directory" files such as `.chezmoi.$FORMAT.tmpl`. You do not need to move files that are ignored by chezmoi in all cases (i.e. are listed in `.chezmoiignore` when executed as a template on all machines), and you can afterwards remove their entries from `home/.chezmoiignore`. ## Use a different version control system to git Although chezmoi is primarily designed to use a git repo for the source state, it does not require git and can be used with other version control systems, such as [fossil][fossil] or [pijul][pijul]. The version control system is used in only three places: * `chezmoi init` will use `git clone` to clone the source repo if it does not already exist. * `chezmoi update` will use `git pull` by default to pull the latest changes. * chezmoi's auto add, commit, and push functionality use `git status`, `git add`, `git commit` and `git push`. Using a different version control system (VCS) to git can be achieved in two ways. Firstly, if your VCS is compatible with git's CLI, then you can set the `git.command` configuration variable to your VCS command and set `useBuiltinGit` to `false`. Otherwise, you can use your VCS to create the source directory before running `chezmoi init`, for example: ```sh fossil clone https://dotfiles.example.com/ dotfiles.fossil mkdir -p .local/share/chezmoi/.git cd .local/share/chezmoi fossil open ~/dotfiles.fossil chezmoi init --apply ``` !!! note The creation of an empty `.git` directory in the source directory is required for chezmoi to be able to identify the work tree. For updates, you can set the `update.command` and `update.args` configuration variables and `chezmoi update` will use these instead of `git pull`, for example: ```toml title="~/.config/chezmoi/chezmoi.toml" [update] command = "fossil" args = ["update"] ``` Currently, it is not possible to override the auto add, commit, and push behavior for non-git VCSs, so you will have to commit changes manually, for example: ```sh chezmoi cd fossil add . fossil commit ``` [fossil]: https://www.fossil-scm.org/ [pijul]: https://pijul.org/ ================================================ FILE: assets/chezmoi.io/docs/user-guide/advanced/install-packages-declaratively.md ================================================ # Install packages declaratively chezmoi uses a declarative approach for the contents of dotfiles, but package installation requires running imperative commands. However, you can simulate declarative package installation with a combination of a `.chezmoidata` file and a `run_onchange_` script. The following example uses [homebrew][homebrew] on macOS, but should be adaptable to other operating systems and package managers. First, create `.chezmoidata/packages.yaml` declaring the packages that you want installed, for example: ```yaml title="~/.local/share/chezmoi/.chezmoidata/packages.yaml" packages: darwin: brews: - 'git' casks: - 'google-chrome' ``` Second, create a `run_onchange_darwin-install-packages.sh.tmpl` script that uses the package manager to install those packages, for example: ``` title="~/.local/share/chezmoi/run_onchange_darwin-install-packages.sh.tmpl" {{ if eq .chezmoi.os "darwin" -}} #!/bin/bash brew bundle --file=/dev/stdin </dev/null 2>&1 && exit case "$(uname -s)" in Darwin) # commands to install password-manager-binary on Darwin ;; Linux) # commands to install password-manager-binary on Linux ;; *) echo "unsupported OS" exit 1 ;; esac ``` !!! note The leading `.` in `.install-password-manager.sh` is important because it tells chezmoi to ignore `.install-password-manager.sh` when declaring the state of files in your home directory. Finally, tell chezmoi to run your password manager install hook before reading the source state: ```toml title=".config/chezmoi/chezmoi.toml" [hooks.read-source-state.pre] command = ".local/share/chezmoi/.install-password-manager.sh" ``` ================================================ FILE: assets/chezmoi.io/docs/user-guide/advanced/migrate-away-from-chezmoi.md ================================================ # Migrate away from chezmoi chezmoi provides several mechanisms to help you move to an alternative dotfile manager (or even no dotfile manager at all) in the future: chezmoi creates your dotfiles just as if you were not using a dotfile manager at all. Your dotfiles are regular files, directories, and symlinks. You can run [`chezmoi purge`][purge] to delete all traces of chezmoi and then, if you're migrating to a new dotfile manager, then you can use whatever mechanism it provides to add your dotfiles to your new system. chezmoi has a [`chezmoi archive`][archive] command that generates a tarball of your dotfiles. You can replace the contents of your dotfiles repo with the contents of the archive and you've effectively immediately migrated away from chezmoi. chezmoi has a [`chezmoi dump`][dump] command that dumps the interpreted (target) state in a machine-readable form, so you can write scripts around chezmoi. [purge]: /reference/commands/purge.md [archive]: /reference/commands/archive.md [dump]: /reference/commands/dump.md ================================================ FILE: assets/chezmoi.io/docs/user-guide/advanced/use-chezmoi-with-watchman.md ================================================ # Use chezmoi with Watchman chezmoi can be used with [Watchman][watchman] to automatically run `chezmoi apply` whenever your source state changes, but there are some limitations because Watchman runs actions in the background without a terminal. Firstly, Watchman spawns a server which runs actions when file systems change. This server reads its environment variables when it is started, typically on the first invocation of the `watchman` command. If you use a password manager that uses environment variables to persist login sessions, then you must login to your password manager before you run the first `watchman` command, and your session might eventually time out. Secondly, Watchman runs processes without a terminal, and so cannot run interactive processes. For `chezmoi apply`, you can use the `--force` flag to suppress prompts to overwrite files that have been modified since chezmoi last wrote them. However, if any other part of `chezmoi apply` is interactive, for example if your password manager prompts for a password, then it will not work with Watchman. 1. Tell watchman to watch your source directory: ```sh CHEZMOI_SOURCE_PATH="$(chezmoi source-path)" watchman watch "${CHEZMOI_SOURCE_PATH}" ``` 2. Tell watchman to run `chezmoi apply --force` whenever your source directory changes: ```sh watchman -j <>W: chezmoi add $FILE W->>W: chezmoi edit $FILE W-->>H: chezmoi status W-->>H: chezmoi diff W->>H: chezmoi apply W->>H: chezmoi edit --apply $FILE H-->>W: chezmoi cd ``` ## Using chezmoi across multiple machines - [`chezmoi init $GITHUB_USERNAME`](/reference/commands/init.md) clones your dotfiles from GitHub into the source directory. - [`chezmoi init --apply $GITHUB_USERNAME`](/reference/commands/init.md) clones your dotfiles from GitHub into the source directory and runs `chezmoi apply`. - [`chezmoi update`](/reference/commands/update.md) pulls the latest changes from your remote repo and runs `chezmoi apply`. - Use normal git commands to add, commit, and push changes to your remote repo. ```mermaid sequenceDiagram participant H as home directory participant W as working copy participant L as local repo participant R as remote repo R->>W: chezmoi init $GITHUB_USERNAME R->>H: chezmoi init --apply $GITHUB_USERNAME R->>H: chezmoi update $GITHUB_USERNAME W->>L: git commit L->>R: git push ``` ## Working with templates - [`chezmoi data`](/reference/commands/data.md) prints the available template data. - [`chezmoi add --template $FILE`](/reference/commands/add.md) adds `$FILE` as a template. - [`chezmoi chattr +template $FILE`](/reference/commands/chattr.md) makes an existing file a template. - [`chezmoi cat $FILE`](/reference/commands/cat.md) prints the target contents of `$FILE`, without changing `$FILE`. - [`chezmoi execute-template`](/reference/commands/execute-template.md) is useful for testing and debugging templates. ================================================ FILE: assets/chezmoi.io/docs/user-guide/daily-operations.md ================================================ # Daily operations ## Edit your dotfiles Edit a dotfile with: ```sh chezmoi edit $FILENAME ``` This will edit `$FILENAME`'s source file in your source directory. chezmoi will not make any changes to the actual dotfile until you run `chezmoi apply`. To automatically run `chezmoi apply` when you quit your editor, run: ```sh chezmoi edit --apply $FILENAME ``` To automatically run `chezmoi apply` whenever you save the file in your editor, run: ```sh chezmoi edit --watch $FILENAME ``` You don't have to use `chezmoi edit` to edit your dotfiles. For more information, see [Do I have to use `chezmoi edit` to edit my dotfiles?][faq-edit]. ```mermaid sequenceDiagram participant H as home directory participant W as working copy participant L as local repo participant R as remote repo W->>W: chezmoi edit W->>H: chezmoi apply W->>H: chezmoi edit --apply W->>H: chezmoi edit --watch ``` ## Pull the latest changes from your repo and apply them You can pull the changes from your repo and apply them in a single command: ```sh chezmoi update ``` This runs `git pull --autostash --rebase` in your source directory and then `chezmoi apply`. ```mermaid sequenceDiagram participant H as home directory participant W as working copy participant L as local repo participant R as remote repo R->>H: chezmoi update ``` ## Pull the latest changes from your repo and see what would change, without actually applying the changes Run: ```sh chezmoi git pull -- --autostash --rebase && chezmoi diff ``` This runs `git pull --autostash --rebase` in your source directory and `chezmoi diff` then shows the difference between the target state computed from your source directory and the actual state. If you're happy with the changes, then you can run ```sh chezmoi apply ``` to apply them. ```mermaid sequenceDiagram participant H as home directory participant W as working copy participant L as local repo participant R as remote repo R->>W: chezmoi git pull W-->>H: chezmoi diff W->>H: chezmoi apply ``` ## Automatically commit and push changes to your repo chezmoi can automatically commit and push changes to your source directory to your repo. This feature is disabled by default. To enable it, add the following to your config file: ```toml title="~/.config/chezmoi/chezmoi.toml" [git] autoCommit = true autoPush = true ``` Whenever a change is made to your source directory, chezmoi will commit the changes with an automatically-generated commit message (if `autoCommit` is true) and push them to your repo (if `autoPush` is true). `autoPush` implies `autoCommit`, i.e. if `autoPush` is true then chezmoi will auto-commit your changes. If you only set `autoCommit` to true then changes will be committed but not pushed. By default, `autoCommit` will generate a commit message based on the files changed. You can override this by setting the `git.commitMessageTemplate` configuration variable. For example, to have chezmoi prompt you for a commit message each time, use: ```toml title="~/.config/chezmoi/chezmoi.toml" [git] autoCommit = true commitMessageTemplate = "{{ promptString \"Commit message\" }}" ``` If your commit message is longer than fits in a string then you can set `git.commitMessageTemplateFile` to specify a path to the commit message template relative to the source directory, for example: ```toml title="~/.config/chezmoi/chezmoi.toml" [git] autoCommit = true commitMessageTemplateFile = ".commit_message.tmpl" ``` Be careful when using `autoPush`. If your dotfiles repo is public and you accidentally add a secret in plain text, that secret will be pushed to your public repo. ```mermaid sequenceDiagram participant H as home directory participant W as working copy participant L as local repo participant R as remote repo W->>L: autoCommit W->>R: autoPush ``` ## Install chezmoi and your dotfiles on a new machine with a single command chezmoi's install script can run `chezmoi init` for you by passing extra arguments to the newly installed chezmoi binary. If your dotfiles repo is `github.com/$GITHUB_USERNAME/dotfiles` then installing chezmoi, running `chezmoi init`, and running `chezmoi apply` can be done in a single line of shell: ```sh sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply $GITHUB_USERNAME ``` If your dotfiles repo has a different name to `dotfiles`, or if you host your dotfiles on a different service, then see the [reference manual for `chezmoi init`][init]. For setting up transitory environments (e.g. short-lived Linux containers) you can install chezmoi, install your dotfiles, and then remove all traces of chezmoi, including the source directory and chezmoi's configuration directory, with a single command: ```sh sh -c "$(curl -fsLS get.chezmoi.io)" -- init --one-shot $GITHUB_USERNAME ``` ```mermaid sequenceDiagram participant H as home directory participant W as working copy participant L as local repo participant R as remote repo R->>W: chezmoi init $GITHUB_USERNAME R->>H: chezmoi init --apply $GITHUB_USERNAME R->>H: chezmoi init --one-shot $GITHUB_USERNAME ``` [faq-edit]: /user-guide/frequently-asked-questions/usage.md#how-do-i-edit-my-dotfiles-with-chezmoi [init]: /reference/commands/init.md ================================================ FILE: assets/chezmoi.io/docs/user-guide/encryption/age.md ================================================ # age chezmoi supports encrypting files with [age][age]. Generate a key using `chezmoi age-keygen`: ```console $ chezmoi age-keygen --output=$HOME/key.txt Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p ``` Specify age encryption in your configuration file, being sure to specify at least the identity and one recipient: ```toml title="~/.config/chezmoi/chezmoi.toml" encryption = "age" [age] identity = "/home/user/key.txt" recipient = "age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p" ``` chezmoi supports multiple identities and multiple recipients: ```toml title="~/.config/chezmoi/chezmoi.toml" encryption = "age" [age] identities = ["/home/user/key1.txt", "/home/user/key2.txt"] recipients = ["recipient1", "recipient2"] ``` !!! note Make sure `encryption` is added to the top level section at the beginning of the config, before any other sections. ## Symmetric encryption To use age's symmetric encryption, specify a single identity and enable symmetric encryption in your config file, for example: ```toml title="~/.config/chezmoi/chezmoi.toml" encryption = "age" [age] identity = "~/.ssh/id_rsa" symmetric = true ``` ## Symmetric encryption with a passphrase To use age's symmetric encryption with a passphrase, set `age.passphrase` to `true` in your config file, for example: ```toml title="~/.config/chezmoi/chezmoi.toml" encryption = "age" [age] passphrase = true ``` You will be prompted for the passphrase whenever you run `chezmoi add --encrypt` and whenever chezmoi needs to decrypt the file, for example when you run `chezmoi apply`, `chezmoi diff`, or `chezmoi status`. ## Builtin age encryption chezmoi has builtin support for age encryption which is automatically used if the `age` command is not found in `$PATH`. !!! info The builtin age encryption does not support passphrases, symmetric encryption, or SSH keys. Passphrases are not supported because chezmoi needs to decrypt files regularly, e.g. when running a `chezmoi diff` or a `chezmoi status` command, not just when running `chezmoi apply`. Prompting for a passphrase each time would quickly become tiresome. Symmetric encryption may be supported in the future. Please [open an issue][issue] if you want this. SSH keys are not supported as the [age documentation explicitly recommends not using them][nossh]: > When integrating age into a new system, it's recommended that you only > support X25519 keys, and not SSH keys. The latter are supported for > manual encryption operations. [age]: https://age-encryption.org/ [issue]: https://github.com/twpayne/chezmoi/issues/new?assignees=&labels=enhancement&template=02_feature_request.md&title= [nossh]: https://pkg.go.dev/filippo.io/age#hdr-Key_management ================================================ FILE: assets/chezmoi.io/docs/user-guide/encryption/gpg.md ================================================ # gpg chezmoi supports encrypting files with [gpg][gpg]. Encrypted files are stored in the source state and automatically be decrypted when generating the target state or editing a file contents with `chezmoi edit`. ## Asymmetric (private/public-key) encryption Specify the encryption key to use in your configuration file (`chezmoi.toml`) with the `gpg.recipient` key: ```toml title="~/.config/chezmoi/chezmoi.toml" encryption = "gpg" [gpg] recipient = "..." ``` chezmoi will encrypt files: ```sh gpg --armor --recipient $RECIPIENT --encrypt ``` and store the encrypted file in the source state. The file will automatically be decrypted when generating the target state. !!! note Make sure `encryption` is added to the top level section at the beginning of the config, before any other sections. ## Symmetric encryption Specify symmetric encryption in your configuration file: ```toml title="~/.config/chezmoi/chezmoi.toml" encryption = "gpg" [gpg] symmetric = true ``` chezmoi will encrypt files: ```sh gpg --armor --symmetric ``` ## Encrypting files with a passphrase If you want to encrypt your files with a passphrase, but don't mind the passphrase being stored in plaintext on your machines, then you can use the following configuration: ``` title="~/.local/share/chezmoi/.chezmoi.toml.tmpl" {{ $passphrase := promptStringOnce . "passphrase" "passphrase" -}} encryption = "gpg" [data] passphrase = {{ $passphrase | quote }} [gpg] symmetric = true args = ["--batch", "--passphrase", {{ $passphrase | quote }}, "--no-symkey-cache"] ``` This will prompt you for the passphrase the first time you run `chezmoi init` on a new machine, and then remember the passphrase in your configuration file. ## Muting gpg output Since gpg sends some info messages to stderr instead of stdout, you will see some output even if you redirect stdout to `/dev/null`. You can mute this by adding `--quiet` to the `gpg.args` key in your configuration: ```toml title="~/.local/share/chezmoi/.chezmoi.toml.tmpl" [gpg] args = ["--quiet"] ``` [gpg]: https://www.gnupg.org/ ================================================ FILE: assets/chezmoi.io/docs/user-guide/encryption/index.md ================================================ # Encryption chezmoi supports encrypting files with [age][age], [git-crypt][gitcrypt], [gpg][gpg], and [transcrypt][transcrypt]. Encrypted files are stored in ASCII-armored format in the source directory with the `encrypted_` attribute and are automatically decrypted when needed. Add files to be encrypted with the `--encrypt` flag, for example: ```sh chezmoi add --encrypt ~/.ssh/id_rsa ``` `chezmoi edit` will transparently decrypt the file before editing and re-encrypt it afterwards. [age]: https://age-encryption.org [gitcrypt]: https://github.com/AGWA/git-crypt [gpg]: https://www.gnupg.com/ [transcrypt]: https://github.com/elasticdog/transcrypt ================================================ FILE: assets/chezmoi.io/docs/user-guide/encryption/rage.md ================================================ # rage chezmoi supports encrypting files with [rage][rage]. To use rage, set `age.command` to `rage` in your configuration file, for example: ```toml title="~/.config/chezmoi/chezmoi.toml" encryption = "age" [age] command = "rage" ``` !!! note Make sure `encryption` is added to the top level section at the beginning of the config, before any other sections. Then, configure chezmoi as you would for [age][age]. [rage]: https://str4d.xyz/rage [age]: /user-guide/encryption/age.md ================================================ FILE: assets/chezmoi.io/docs/user-guide/encryption/transparent.md ================================================ # Transparent chezmoi supports encrypting files with transparent git encryption tools like [transcrypt][transcrypt] and [git-crypt][gitcrypt]. ## transcrypt In your configuration file, set `encryption` to `transparent`: ```toml title="~/.config/chezmoi/chezmoi.toml" encryption = "transparent" ``` Initialize transcrypt: ```console $ chezmoi cd $ transcrypt ``` Edit `.gitattributes` to use transcrypt for files with the `encrypted_` prefix: ```gitattributes title="~/.local/share/chezmoi/.gitattributes" encrypted_* filter=crypt diff=crypt merge=crypt ``` Add an encrypted file to both chezmoi and git: ```console $ chezmoi add ~/.config/sensitive_file $ git add dot_config/encrypted_sensitive_file $ git commit -m "Add .config/sensitive_file" ``` Verify that the file is handled by transcrypt: ```console $ git ls-crypt dot_config/encrypted_sensitive_file ``` Note that commands like `git show`, `git diff`, etc. will also show the cleartext form of the file. Use `transcrypt --display` to show instructions for how to setup transcrypt after cloning the repository elsewhere. It will involve running a command like: ```console $ transcrypt -c aes-256-cbc -p $PASSWORD ``` [gitcrypt]: https://github.com/AGWA/git-crypt [transcrypt]: https://github.com/elasticdog/transcrypt ================================================ FILE: assets/chezmoi.io/docs/user-guide/frequently-asked-questions/design.md ================================================ # Design ## Do I have to use `chezmoi edit` to edit my dotfiles? No. `chezmoi edit` is a convenience command that has a couple of useful features, but you don't have to use it. You can also run `chezmoi cd` and then just edit the files in the source state directly. After saving an edited file you can run `chezmoi diff` to check what effect the changes would have, and run `chezmoi apply` if you're happy with them. If there are inconsistencies that you want to keep, then `chezmoi merge-all` will help you resolve any differences. `chezmoi edit` provides the following useful features: * The arguments to `chezmoi edit` are the files in their target location, so you don't have to think about source state attributes and your editor's syntax highlighting will work. * If the dotfile is encrypted in the source state, then `chezmoi edit` will decrypt it to a private directory, open that file in your `$EDITOR`, and then re-encrypt the file when you quit your editor. This makes encryption transparent. * With the `--diff` and `--apply` options you can see what would change and apply those changes without having to run `chezmoi diff` or `chezmoi apply`. * If you have configured git auto commits or git auto pushes then `chezmoi edit` will create commits and push them for you. If you chose to edit files in the source state and you're using VIM then [`github.com/alker0/chezmoi.vim`][chezmoi.vim] gives you syntax highlighting, however you edit your files. Besides using the plugin, you can use modeline to tell VIM the correct filetype. For example, put `# vim: filetype=zsh` at the top of `dot_zshrc`, and VIM will treat `dot_zshrc` as zsh file. ## Why doesn't chezmoi use symlinks like GNU Stow? Symlinks are first class citizens in chezmoi: chezmoi supports creating them, updating them, removing them, and even more advanced features not found in other dotfile managers like having the same symlink point to different targets on different machines by using a template. With chezmoi, you only use a symlink where you really need a symlink, in contrast to some other dotfile managers (e.g. GNU Stow) which require the use of symlinks as a layer of indirection between a dotfile's location (which can be anywhere in your home directory) and a dotfile's content (which needs to be in a centralized directory that you manage with version control). chezmoi solves this problem in a different way. Instead of using a symlink to redirect from the dotfile's location to the centralized directory, chezmoi generates the dotfile as a regular file in its final location from the contents of the centralized directory. This approach allows chezmoi to provide features that are not possible when using symlinks, for example having files that are encrypted, executable, private, or templates. There is nothing special about dotfiles managed by chezmoi whereas dotfiles managed with GNU Stow are special because they're actually symlinks to somewhere else. The only advantage to using GNU Stow-style symlinks is that changes that you make to the dotfile's contents in the centralized directory are immediately visible whenever you save them, whereas chezmoi currently requires you to pass the `--watch` flag to `chezmoi edit` or set `edit.watch` to `true` in your configuration file. If you really want to use symlinks, then chezmoi provides a [symlink mode][symlink] which uses symlinks where possible. This configures chezmoi to work like GNU Stow and have it create a set of symlinks back to a central directory, but this currently requires a bit of manual work (as described in [issue #167][#167]). chezmoi might get some automation to help (see [issue #886][#886] for example) but it does need some convincing use cases that demonstrate that a symlink from a dotfile's location to its contents in a central directory is better than just having the correct dotfile contents. ## What are the limitations of chezmoi's symlink mode? In symlink mode chezmoi replaces targets with symlinks to the source directory if the target is a regular file and is not encrypted, executable, private, or a template. Symlinks cannot be used for encrypted files because the source state contains the ciphertext, not the plaintext. Symlinks cannot be used for executable files as the executable bit would need to be set on the file in the source directory and chezmoi uses only regular files and directories in its source state for portability across operating systems. This may change in the future. Symlinks cannot be used for private files because git does not persist group and world permission bits. Symlinks cannot be used for templated files because the source state contains the template, not the result of executing the template. Symlinks cannot be used for entire directories because of chezmoi's use of attributes in the filename mangles entries in the directory, directories might have the `exact_` attribute and contain empty files, and the directory's entries might not be usable with symlinks. In symlink mode, running `chezmoi add` does not immediately replace the targets with a symlink. You must run `chezmoi apply` to create the symlinks. ## Why does chezmoi use weird filenames? There are a number of criticisms of how chezmoi uses filenames: 1. The long source file names are weird and verbose. 2. Not all possible file permissions can be represented. 3. Everything is in a single directory, which can end up containing many entries. chezmoi's decision to store metadata in filenames is a deliberate, practical, compromise. Firstly, almost all programs store metadata in filenames: the filename's extension. chezmoi extends the filename to storing metadata in attributes in the filename's prefix as well. The `dot_` attribute makes it transparent which dotfiles are managed by chezmoi and which files are ignored by chezmoi. chezmoi ignores all files and directories that start with `.` so no special whitelists are needed for version control systems and their control files (e.g. `.git` and `.gitignore`). chezmoi needs per-file metadata to know how to interpret the source file's contents, for example to know when the source file is a template or if the file's contents are encrypted. By storing this metadata in the filename, the metadata is unambiguously associated with a single file and adding, updating, or removing a single file touches only a single file in the source state. Changes to the metadata (e.g. `chezmoi chattr +template $TARGET`) are simple file renames and isolated to the affected file. If chezmoi were to, say, use a common configuration file listing which files were templates and/or encrypted, then changes to any file would require updates to the common configuration file. Automating updates to configuration files requires a round trip (read config file, update config, write config) and it is not always possible preserve comments and formatting. chezmoi's attributes of `executable_`, `private_`, and `readonly_` allow the file permissions `0o644`, `0o755`, `0o600`, `0o700`, `0o444`, `0o555`, `0o400`, and `0o500` to be represented. Directories can only have permissions `0o755`, `0o700`, or `0o500`. In practice, these cover all permissions typically used for dotfiles. If this does cause a genuine problem for you, please [open an issue on GitHub][choose]. File permissions and modes like `executable_`, `private_`, `readonly_`, and `symlink_` could also be stored in the file system, rather than in the filename. However, this requires the permissions to be preserved and handled by the underlying version control system and file system. chezmoi provides first-class support for Windows, where the `executable_` and `private_` attributes have no direct equivalents and symbolic links are not always permitted. By using regular files and directories, chezmoi avoids variations in the operating system, version control system, and file system making it both more robust and more portable. chezmoi uses a 1:1 mapping between entries in the source state and entries in the target state. This mapping is bi-directional and unambiguous. However, this also means that dotfiles that in the same directory in the target state must be in the same directory in the source state. In particular, every entry managed by chezmoi in the root of your home directory has a corresponding entry in the root of your source directory, which can mean that you end up with a lot of entries in the root of your source directory. This can be mitigated by using `.chezmoiroot` file. If chezmoi were to permit, say, multiple separate source directories (so you could, say, put `dot_bashrc` in a `bash/` subdirectory, and `dot_vimrc` in a `vim/` subdirectory, but have `chezmoi apply` map these to `~/.bashrc` and `~/.vimrc` in the root of your home directory) then the mapping between source and target states is no longer bidirectional nor unambiguous, which significantly increases complexity and requires more user interaction. For example, if both `bash/dot_bashrc` and `vim/dot_bashrc` exist, what should be the contents of `~/.bashrc`? If you run `chezmoi add ~/.zshrc`, should `dot_zshrc` be stored in the source `bash/` directory, the source `vim/` directory, or somewhere else? How does the user communicate their preferences? chezmoi has many users and any changes to the source state representation must be backwards-compatible. In summary, chezmoi's source state representation is a compromise with both advantages and disadvantages. Changes to the representation will be considered, but must meet the following criteria, in order of importance: 1. Be fully backwards-compatible for existing users. 2. Fix a genuine problem encountered in practice. 3. Be independent of the underlying operating system, version control system, and file system. 4. Not add significant extra complexity to the user interface or underlying implementation. ## Can chezmoi support multiple sources or multiple source states? With some dotfile managers, dotfiles can be distributed across multiple directories or even multiple repos. For example, the user might have one directory per application, or separate repos for home and work configurations, or even separate git submodules for different applications. These can be considered multiple sources of truth for the target state. This, however, comes with complications: 1. Multiple sources of truth complicate the user interface. When running `chezmoi add $FILE`, which source should `$FILE` be added to? 2. Multiple sources of truth do not compose easily if target files overlap. For example, if you have two sources, both of which need to set an environment variable in `.bashrc`, how do you handle this when both, only one, or neither source might be activated? What if the sources are mutually exclusive, e.g. if the VIM source and the Emacs source both want to set the `$EDITOR` environment variable? 3. Multiple sources of truth are not always independent. Related to the previous point, consider a source that adds an applications's configuration files and shell completions. Should the shell completions be part of the applications's source or of the shell's source? chezmoi instead makes the opinionated choice to use a single source of truth, i.e. a single branch in a single git repo. Using a single source of truth avoids the inherent complexity and ambiguity of multiple sources. chezmoi provides mechanisms like templates (for minor differences), `.chezmoiignore` (for controlling the presence or otherwise of complete files and directories), and password manager integration (so secrets never need to be stored in a repo) handle machine-to-machine differences. Externals make it easy to pull in dotfiles from third-party sources. That said, if you are keen to use multiple sources of truth with chezmoi, you have a number of options with some scripting around chezmoi. Firstly, you can run `chezmoi apply` with different arguments to the `--config` and `--source` flags which will apply to the same destination. So that you only have to type one command you can wrap this in a shell function, for example: ```bash chezmoi-apply() { chezmoi apply --config ~/.config/chezmoi-home/chezmoi.toml \ --source ~/.local/share/chezmoi-home && \ chezmoi apply --config ~/.config/chezmoi-work/chezmoi.toml \ --source ~/.local/share/chezmoi-work } ``` If you want to generate multiple configuration files with `chezmoi init` then you will need the `--config-path` flag. For more advanced use, use the `--destination`, `--cache`, and `--persistent-state` flags. Secondly, you can assemble a single source state from multiple sources and then use `chezmoi apply`. For example, if you have multiple source states in subdirectories of `~/.dotfiles`: ```bash #!/bin/bash # create a combined source state in a temporary directory combined_source="$(mktemp -d)" # remove the temporary source state on exit trap 'rm -rf -- "${combined_source}"' INT TERM # copy files from multiple sources into the temporary source state for source in $HOME/.dotfiles/*; do cp -r "${source}"/* "${combined_source}" done # apply the temporary source state chezmoi apply --source "${combined_source}" ``` Thirdly, you can use a `run_` script to invoke a second instance of chezmoi, [as used by @felipecrs][rootmoi]. ## Why does `chezmoi cd` spawn a shell instead of just changing directory? `chezmoi cd` spawns a shell because it is not possible for a program to change the working directory of its parent process. You can add a shell function instead: ```bash chezmoi-cd() { cd $(chezmoi source-path) } ``` Typing `chezmoi-cd` will then change the directory of your current shell to chezmoi's source directory. ## Why are the `prompt*` functions only available in config file templates? chezmoi regularly needs to execute templates to determine the target contents of files. For example, templates are executed for the `apply`, `diff`, and `status` commands, amongst many others. Having to interactively respond each time would quickly become tiresome. Therefore, chezmoi only provides these functions when generating a config file from a config file template (e.g. when you run `chezmoi init` or `chezmoi --init apply`). ## Why not use Ansible/Chef/Puppet/Salt, or similar to manage my dotfiles instead? Whole system management tools are more than capable of managing your dotfiles, but they are large systems that entail several disadvantages. Compared to whole system management tools, chezmoi offers: * Small, focused feature set designed for dotfiles. There's simply less to learn with chezmoi compared to whole system management tools. * Easy installation and execution on every platform, without root access. Installing chezmoi requires only copying a single binary file with no external dependencies. Executing chezmoi just involves running the binary. In contrast, installing and running a whole system management tool typically requires installing a scripting language runtime, several packages, and running a system service, all typically requiring root access. chezmoi's focus and simple installation means that it runs almost everywhere: from tiny ARM-based Linux systems to Windows desktops, from inside lightweight containers to FreeBSD-based virtual machines in the cloud. ## Can I use chezmoi to manage files outside my home directory? In practice, yes, you can, but this usage is strongly discouraged beyond using your system's package manager to install the packages you need. chezmoi is designed to operate on your home directory, and is explicitly not a full system configuration management tool. That said, there are some ways to have chezmoi manage a few files outside your home directory. chezmoi's scripts can execute arbitrary commands, so you can use a `run_` script that is run every time you run `chezmoi apply`, to, for example: * Make the target file outside your home directory a symlink to a file managed by chezmoi in your home directory. * Copy a file managed by chezmoi inside your home directory to the target file. * Execute a template with `chezmoi execute-template --output=$FILENAME template` where `$FILENAME` is outside the target directory. chezmoi executes all scripts as the user executing chezmoi, so you may need to add extra privilege elevation commands like `sudo` or `PowerShell start -verb runas -wait` to your script. chezmoi, by default, operates on your home directory but this can be overridden with the `--destination` command line flag or by specifying `destDir` in your config file, and could even be the root directory (`/` or `C:\`). This allows you, in theory, to use chezmoi to manage any file in your file system, but this usage is extremely strongly discouraged. If your needs extend beyond modifying a handful of files outside your target system, then existing configuration management tools like [Puppet][puppet], [Chef][chef], [Ansible][ansible], and [Salt][salt] are much better suited - and of course can be called from a chezmoi `run_` script. Put your Puppet Manifests, Chef Recipes, Ansible Modules, and Salt Modules in a directory ignored by `.chezmoiignore` so they do not pollute your home directory. ## What inspired chezmoi? chezmoi was inspired by [Puppet][puppet], but was created because Puppet is an overkill for managing your personal configuration files. The focus of chezmoi will always be personal home directory management. If your needs grow beyond that, switch to a whole system configuration management tool. ## Where does the name "chezmoi" come from? "chezmoi" splits to "chez moi" and pronounced /ʃeɪ mwa/ (shay-mwa) meaning "at my house" in French. It's seven letters long, which is an appropriate length for a command that is only run occasionally. If you prefer a shorter command, add an alias to your shell configuration, for example: ```sh alias cz=chezmoi ``` [#167]: https://github.com/twpayne/chezmoi/issues/167 [#886]: https://github.com/twpayne/chezmoi/issues/886 [ansible]: https://www.ansible.com/ [chef]: https://chef.io/ [chezmoi.vim]: https://github.com/alker0/chezmoi.vim [choose]: https://github.com/twpayne/chezmoi/issues/new/choose [puppet]: https://puppet.com/ [rootmoi]: https://github.com/felipecrs/dotfiles/blob/8a7840efdeff1a45069f47e5b2e558dc9812712d/home/.chezmoiscripts/run_after_20-run-rootmoi.sh.tmpl [salt]: https://www.saltstack.com/ [symlink]: /reference/target-types.md#symlink-mode ================================================ FILE: assets/chezmoi.io/docs/user-guide/frequently-asked-questions/encryption.md ================================================ # Encryption ## How do I configure chezmoi to encrypt files but only request a passphrase the first time `chezmoi init` is run? The following steps use [age][age] for encryption. This can be achieved with the following process: 1. Generate an age private key. 2. Encrypt the private key with a passphrase. 3. Configure chezmoi to decrypt the private key if needed. 4. Configure chezmoi to use the private key. 5. Add encrypted files. First, change to chezmoi's root directory: ```sh chezmoi cd ``` Generate an age private key encrypted with a passphrase in the file `key.txt.age` with the command: ```console $ chezmoi age-keygen | chezmoi age encrypt --passphrase --output=key.txt.age Public key: age193wd0hfuhtjfsunlq3c83s8m93pde442dkcn7lmj3lspeekm9g7stwutrl Enter passphrase (leave empty to autogenerate a secure one): Confirm passphrase: ``` Use a strong passphrase and make a note of the public key (`age193wd0hfuhtjfsunlq3c83s8m93pde442dkcn7lmj3lspeekm9g7stwutrl` in this case). Add `key.txt.age` to `.chezmoiignore` so that chezmoi does not try to create it: ```sh echo key.txt.age >> .chezmoiignore ``` Configure chezmoi to decrypt the passphrase-encrypted private key if needed: ```console $ cat > run_onchange_before_decrypt-private-key.sh.tmpl <..." to include in what will be committed) .chezmoi.toml.tmpl .chezmoiignore key.txt.age run_onchange_before_decrypt-private-key.sh.tmpl nothing added to commit but untracked files present (use "git add" to track) ``` If you're happy with the changes you can commit them. All four files should be committed. Add files that you want to encrypt using the `--encrypt` argument to `chezmoi add`, for example: ```sh chezmoi add --encrypt ~/.ssh/id_rsa ``` When you run `chezmoi init` on a new machine you will be prompted to enter your passphrase once to decrypt `key.txt.age`. Your decrypted private key will be stored in `~/.config/chezmoi/key.txt`. ## How to re-encrypt encrypted files To rotate from an expired GPG key to its replacement, or change from GPG to age encryption, the following steps can be used: 1. Make sure you have applied all encrypted files (e.g. `chezmoi apply` decrypts files and places them in their destinations). 2. Update chezmoi configuration to use the new encryption method (examples: [gpg][site-gpg], [age][site-age], [age with one-time passphrase][age-passphrase]). 3. Remove all encrypted files from the state via `chezmoi forget` or `chezmoi unmanage`. 4. Add them back with `chezmoi add --encrypt`. ### Example: Migrate from GPG to age Update chezmoi configuration to use age encryption (with `chezmoi edit-config` or manually editing the corresponding template): ```diff - encryption = "gpg" - [gpg] - recipient = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + encryption = "age" + [age] + recipient = "age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + identity = "/home/user/key.txt" ``` Re-encrypt the files with a script like this: ```bash for encrypted_file in $(chezmoi managed --include encrypted --path-style absolute) do # optionally, add --force to avoid prompts chezmoi forget "$encrypted_file" # strip the .asc extension decrypted_file="${encrypted_file%.asc}" chezmoi add --encrypt "$decrypted_file" done ``` [site-gpg]: /user-guide/encryption/gpg.md [site-age]: /user-guide/encryption/age.md [age-passphrase]: #how-do-i-configure-chezmoi-to-encrypt-files-but-only-request-a-passphrase-the-first-time-chezmoi-init-is-run [age]: https://age-encryption.org/ ================================================ FILE: assets/chezmoi.io/docs/user-guide/frequently-asked-questions/general.md ================================================ # General ## What other questions have been asked about chezmoi? See the [issues][support-q] and [discussions][discussions]. ## Where do I ask a question that isn't answered here? Please [open an issue on GitHub][choose] or [start a discussion][discuss]. ## I like chezmoi. How do I say thanks? Thank you! chezmoi was written to scratch a personal itch, and I'm very happy that it's useful to you. Please give [chezmoi a star on GitHub][star], and if you're happy to share your public dotfile repo then [tag it with `chezmoi`][tag]. If you write an article or give a talk on chezmoi please inform the author (e.g. by [opening an issue][choose]) so it can be added to chezmoi's [articles][articles], [podcasts][podcasts], or [videos][videos] pages. [Contributions are very welcome][contributions] and every [bug report, support request, and feature request][choose] helps make chezmoi better. Thank you :) [articles]: /links/articles.md [choose]: https://github.com/twpayne/chezmoi/issues/new/choose [discuss]: https://github.com/twpayne/chezmoi/discussions/new [discussions]: https://github.com/twpayne/chezmoi/discussions [podcasts]: /links/podcasts.md [star]: https://github.com/twpayne/chezmoi/stargazers [support-q]: https://github.com/twpayne/chezmoi/issues?utf8=%E2%9C%93&q=is%3Aissue+sort%3Aupdated-desc+label%3Asupport [tag]: https://github.com/topics/chezmoi?o=desc&s=updated [videos]: /links/videos.md [contributions]: /developer-guide/contributing-changes.md ================================================ FILE: assets/chezmoi.io/docs/user-guide/frequently-asked-questions/troubleshooting.md ================================================ # Troubleshooting ## How can I quickly check for problems with chezmoi on my machine? Run: ```sh chezmoi doctor ``` Anything `ok` is fine, anything `warning` is only a problem if you want to use the related feature, and anything `error` indicates a definite problem. ## A specific command is not behaving as I expect. How can I debug it? The `--verbose` flag makes chezmoi to print extra information about what it is doing. The `--debug` flag makes chezmoi print very detailed step by step information. ## The output of `chezmoi diff` is broken and does not contain color. What could be wrong? By default, chezmoi's diff output includes ANSI color escape sequences (e.g. `ESC[37m`) and is piped into your pager (by default `less`). chezmoi assumes that your pager passes through the ANSI color escape sequences, as configured on many systems, but not all. If your pager does not pass through ANSI color escape sequences then you will see monochrome diff output with uninterpreted ANSI color escape sequences. This can typically by fixed by setting the environment variable ```sh export LESS=-R ``` which instructs `less` to display "raw" control characters via the `-R` / `--RAW-CONTROL-CHARS` option. You can also set the `pager` configuration variable in your config file, for example: ```toml title="~/.config/chezmoi/chezmoi.toml" pager = "less -R" ``` If you have set a different pager (via the `pager` configuration variable or `PAGER` environment variable) then you must ensure that it passes through raw control characters. Alternatively, you can use the `--color=false` option to chezmoi to disable colors or the `--no-pager` option to chezmoi to disable the pager. ## Why do I get a blank buffer or empty file when running `chezmoi edit`? In this case, `chezmoi edit` typically prints a warning like: ```text chezmoi: warning: $EDITOR $TMPDIR/$FILENAME: returned in less than 1s ``` `chezmoi edit` performs a bit of magic to improve the experience of editing files in the source state by invoking your editor with filenames in a temporary directory that look like filenames in your home directory. What's happening here is that your editor command is exiting immediately, so chezmoi thinks you've finished editing and so removes the temporary directory, but actually your editor command has forked a edit process in the background, and that edit process opens a now non-existent file. To fix this you have to configure your editor command to remain in the foreground until you have finished editing the file, so chezmoi knows when to remove the temporary directory. === "VIM" Pass the `-f` flag, e.g. by setting the `edit.args` configuration variable to `["-f"]`, or by setting the `EDITOR` environment variable to include the `-f` flag, e.g. `export EDITOR="vim -f"`. === "VSCode" Pass the `--wait` flag, e.g. by setting the `edit.args` configuration variable to `["--wait"]` or by setting the `EDITOR` environment variable to include the `--wait` flag, e.g. `export EDITOR="code --wait"`. The "bit of magic" that `chezmoi edit` performs includes: * `chezmoi edit` makes the filename opened by your editor more closely match the target filename, which can help your editor choose the correct syntax highlighting. For example, if you run `chezmoi edit ~/.zshrc`, your editor is be opened with `$TMPDIR/.zshrc` but you'll actually be editing `~/.local/share/chezmoi/dot_zshrc`. Under the hood, chezmoi creates a hardlink in a temporary directory to the file in your source directory, so even though your editor thinks it's editing `.zshrc`, it is really editing `dot_zshrc` in your source directory. * If the source file is encrypted then `chezmoi edit` transparently decrypts and re-encrypts the file for you. Specifically, chezmoi decrypts the file into a private temporary directory and open your editor with the decrypted file, and re-encrypts the file when you exit your editor. * If the source file is a template, then `chezmoi edit` preserves the `.tmpl` extension. ## chezmoi makes `~/.ssh/config` group writeable. How do I stop this? By default, chezmoi uses your system's umask when creating files. On most systems the default umask is `022` but some systems use `002`, which means that files and directories are group writeable by default. You can override this for chezmoi by setting the `umask` configuration variable in your configuration file, for example: ```toml title="~/.config/chezmoi/chezmoi.toml" umask = 0o022 ``` !!! note This will apply to all files and directories that chezmoi manages and will ensure that none of them are group writeable. It is not currently possible to control group write permissions for individual files or directories. Please [open an issue on GitHub][enhancement] if you need this. ## chezmoi reports `chezmoi: user: lookup userid NNNNN: input/output error` This is likely because the chezmoi binary you are using was statically compiled with [musl][musl] and the machine you are running on uses LDAP or NIS. The immediate fix is to use a package built for your distribution (e.g a `.deb` or `.rpm`) which is linked against glibc and includes LDAP/NIS support instead of the statically-compiled binary. If the problem still persists, then please [open an issue on GitHub][choose]. ## chezmoi reports `chezmoi: timeout` or `chezmoi: timeout obtaining persistent state lock` chezmoi will report this when it is unable to lock its persistent state (`~/.config/chezmoi/chezmoistate.boltdb`), typically because another instance of chezmoi is currently running and holding the lock. This can happen, for example, if you have a `run_` script that invokes `chezmoi`, or are running chezmoi in another window. Under the hood, chezmoi uses [bbolt][bbolt] which permits multiple simultaneous readers, but only one writer (with no readers). Commands that take a write lock include `add`, `apply`, `edit`, `forget`, `import`, `init`, `state`, `unmanage`, and `update`. Commands that take a read lock include `diff`, `status`, and `verify`. ## chezmoi reports `chezmoi: fork/exec /tmp/XXXXXXXXXX.XX: exec format error` when executing a template script This error occurs when you have a newline before the `#!` in your script. Suppress the newline by including a `-` before the closing `}}` on the first line. For example, if your template script begins with ```text {{ if eq .chezmoi.os "linux" }} #!/bin/sh ``` change this to ```text {{ if eq .chezmoi.os "linux" -}} #!/bin/sh ``` ## chezmoi reports `chezmoi: fork/exec /tmp/XXXXXXXXXX.XX: permission denied` when executing a script This error occurs when your temporary directory is mounted with the `noexec` option. As chezmoi scripts can be templates, encrypted, or both, chezmoi needs to write the final script's contents to a file so that it can be executed by the operating system. By default, chezmoi will use `$TMPDIR` for this. You can change the temporary directory into which chezmoi writes and executes scripts with the `scriptTempDir` configuration variable. For example, to use a subdirectory of your home directory you can use: ```toml title="~/.config/chezmoi/chezmoi.toml" scriptTempDir = "~/tmp" ``` ## chezmoi reports `chezmoi: mkdir xxxxx: no such file or directory` when trying to manage file or directory This error occurs when you try to add directory/file to be managed via chezmoi but the same directory is only listed in `.chezmoiexternal.$FORMAT`. A workaround can be applied in a such case via manually creating import directory in chezmoi source directory (typically `~/.local/share/chezmoi`) and create `.keep` file. For example, if `.chezmoiexternal.toml` has the configuration: ```toml [".config/nvim"] type = "git-repo" url = "https://github.com/NvChad/NvChad.git" refreshPeriod = "168h" [".config/nvim".pull] args = ["--ff-only"] ``` Now `chezmoi add ~/.config/direnv/direnvrc` will raise the error: ```text chezmoi: mkdir /home//.local/share/chezmoi/dot_config/direnv: no such file or directory ``` But the workaround can be applied: ```sh chezmoi cd mkdir -p dot_config/ touch dot_config/.keep ``` Now once that done `chezmoi add ~/.config/direnv/direnvrc` should work. For reference see [issue #2006][#2006]. ## chezmoi reports `read /dev/stdin: permission denied` or `write /dev/stdout: permission denied` when I redirect standard input or standard output This error occurs when you [installed chezmoi with snap][snap] and is caused by a long-standing [bug in snap][snap-bug]. This is not a bug in chezmoi and there is nothing that chezmoi can do about this. However, there are two workarounds: Firstly, you can use alternatives to shell redirection. For standard input: ```sh chezmoi $COMMAND <$FILENAME # fails cat $FILENAME | chezmoi $COMMAND # succeeds ``` For standard output: ```sh chezmoi $COMMAND >$FILENAME # fails chezmoi $COMMAND -o $FILENAME # succeeds chezmoi $COMMAND --output=$FILENAME # succeeds chezmoi $COMMAND | tee $FILENAME >/dev/null # succeeds ``` Secondly, you can install chezmoi with any of the [many supported install methods][install] instead of snap. ## chezmoi reports `fork/exec ...: no such file or directory` when running scripts on Nix or Termux You are likely using a hardcoded script interpreter in the shebang line of your scripts, e.g. ```bash #!/bin/bash ``` `/bin/bash` does not exist on Nix or Termux. You must update the shebang line to point to the actual bash interpreter. The easiest way to do this is make the script a template and use the `lookPath` template function, for example: ```bash #!{{ lookPath "bash" }} ``` Alternatively, you can use the actual path to `bash` on your system, for example: === "Nix" ```bash #!/usr/bin/env bash ``` === "Termux" ```bash #!/data/data/com.termux/files/usr/bin/bash ``` [choose]: https://github.com/twpayne/chezmoi/issues/new/choose [enhancement]: https://github.com/twpayne/chezmoi/issues/new?assignees=&labels=enhancement&template=02_feature_request.md&title= [musl]: https://musl.libc.org/ [bbolt]: https://github.com/etcd-io/bbolt [#2006]: https://github.com/twpayne/chezmoi/issues/2006 [snap]: https://snapcraft.io/chezmoi [snap-bug]: https://bugs.launchpad.net/ubuntu/+source/snapd/+bug/1849753 [install]: /install.md ================================================ FILE: assets/chezmoi.io/docs/user-guide/frequently-asked-questions/usage.md ================================================ # Usage ## How do I edit my dotfiles with chezmoi? There are five popular approaches: 1. Use `chezmoi edit $FILE`. This will open the source file for `$FILE` in your editor, including opening the template if the file is templated and transparently decrypting and re-encrypting it if it is encrypted. For extra ease, use `chezmoi edit --apply $FILE` to apply the changes when you quit your editor, and `chezmoi edit --watch $FILE` to apply the changes whenever you save the file. 2. Use `chezmoi cd` and edit the files in the source directory directly. Run `chezmoi diff` to see what changes would be made, and `chezmoi apply` to make the changes. 3. If your editor supports opening directories, run `chezmoi edit` with no arguments to open the source directory. 4. Edit the file in your home directory, and then either re-add it by running `chezmoi add $FILE` or `chezmoi re-add`. 5. Edit the file in your home directory, and then merge your changes with source state by running `chezmoi merge $FILE`. !!! note `re-add` doesn't work with templates. ## What are the consequences of "bare" modifications to the target files? If my `.zshrc` is managed by chezmoi and I edit `~/.zshrc` without using `chezmoi edit`, what happens? Until you run `chezmoi apply` your modified `~/.zshrc` will remain in place. When you run `chezmoi apply` chezmoi will detect that `~/.zshrc` has changed since chezmoi last wrote it and prompt you what to do. You can resolve differences with a merge tool by running `chezmoi merge ~/.zshrc`. ## How can I tell what dotfiles in my home directory aren't managed by chezmoi? Is there an easy way to have chezmoi manage a subset of them? `chezmoi unmanaged` will list everything not managed by chezmoi. You can add entire directories with `chezmoi add`. ## How can I tell what dotfiles in my home directory are currently managed by chezmoi? `chezmoi managed` will list everything managed by chezmoi. ## If there's a mechanism in place for the above, is there also a way to tell chezmoi to ignore specific files or groups of files (e.g. by directory name or by glob)? By default, chezmoi ignores everything that you haven't explicitly added. If you have files in your source directory that you don't want added to your destination directory when you run `chezmoi apply` add their names to a file called `.chezmoiignore` in the source state. Patterns are supported, and you can change what's ignored from machine to machine. The full usage and syntax is described in the [reference manual][ignore]. ## If the target already exists, but is "behind" the source, can chezmoi be configured to preserve the target version before replacing it with one derived from the source? Yes. Running `chezmoi add` will update the source state with the target. To see diffs of what would change, without actually changing anything, use `chezmoi diff`. ## Once I've made a change to the source directory, how do I commit it? You have several options: * `chezmoi cd` opens a shell in the source directory, where you can run your usual version control commands, like `git add` and `git commit`. * `chezmoi git` runs `git` in the source directory and pass extra arguments to the command. If you're passing any flags, you'll need to use `--` to prevent chezmoi from consuming them, for example `chezmoi git -- commit -m "Update dotfiles"`. * You can configure chezmoi to automatically commit and push changes to your source state, as [described in the how-to guide][auto-commit]. ## I've made changes to both the destination state and the source state that I want to keep. How can I keep them both? `chezmoi merge` will open a merge tool to resolve differences between the source state, target state, and destination state. Copy the changes you want to keep in to the source state. ## Can I use chezmoi to manage my shell history across multiple machines? No. Every change in a file managed by chezmoi requires an explicit command to record it (e.g. `chezmoi add`) or apply it somewhere else (e.g. `chezmoi update`), and is recorded as a commit in your dotfiles repository. Creating a commit every time a command is entered would quickly become cumbersome. This makes chezmoi unsuitable for sharing changes to rapidly-changing files like shell histories. Instead, consider using a dedicated tool for sharing shell history across multiple machines, like [`atuin`][atuin]. You can use chezmoi to install and configure atuin. ## How do I install pre-requisites for templates? If you have a template that depends on some other tool, like `curl`, you may need to install it before chezmoi renders the template. To do so, use a `run_before` script that is **not** a template. Something like: ```bash title="run_before_00-install-pre-requisites.sh" #!/bin/bash set -eu # Install curl if it's not already installed if ! command -v curl >/dev/null; then sudo apt update sudo apt install -y curl fi ``` chezmoi will make sure to execute it before templating other files. !!! tip You can [use `scriptEnv` to inject data into your scripts and hooks through environment variables][scriptenv]. ## How do I write a literal `{{` or `}}` in a template? `{{` and `}}` are chezmoi's default template delimiters, and so need escaping, for example: ```text {{ "{{" }} {{ "}}" }} ``` results in ```text {{ }} ``` For longer tokens containing a `{{` and a `}}` you can use a longer literal, for example: ```text {{ "{{ .Target }}" }} ``` results in ```text {{ .Target }} ``` ## How do I run a script when a `git-repo` external changes? Use a `run_onchange_after_*.tmpl` script that includes the HEAD commit. For example, if `~/.emacs.d` is a `git-repo` external, then create: ```text title="~/.local/share/chezmoi/run_onchange_after_emacs.d.tmpl" #!/bin/sh # {{ output "git" "-C" (joinPath .chezmoi.homeDir ".emacs.d") "rev-parse" "HEAD" }} echo "~/emacs.d updated" ``` ## How do I run a script periodically? Use a `run_onchange_*.tmpl` script that includes the current time truncated to a suitable unit. For example, to run a script daily: ```text title="~/.local/share/chezmoi/run_onchange_daily.tmpl" #!/bin/sh # {{ now | date "2006-01-02" }} echo "new day" ``` For weekly, use the week number from the output of `date`, for example: ```text title="~/.local/share/chezmoi/run_onchange_weekly.tmpl" #!/bin/sh # {{ output "date" "+%V" | trim }} echo "new week" ``` Or, approximate the week number with template functions: ```text title="~/.local/share/chezmoi/run_onchange_weekly.tmpl" #!/bin/sh # {{ div now.YearDay 7 }} echo "new week" ``` ## How do I enable shell completions? chezmoi includes shell completions for [`bash`][bash], [`fish`][fish], [`powershell`][powershell], and [`zsh`][zsh]. If you have installed chezmoi via your package manager then the shell completion should already be installed. For PowerShell, you need to manually add the completion script to your profile. Please [open an issue][choose] if this is not working correctly. chezmoi provides a [`completion`][completion-cmd] command and a [`completion`][completion-fun] template function which return the shell completions for the given shell. These can be used either as a one-off or as part of your dotfiles repo. The details of how to use these depend on your shell. ## How do I use tools that I installed with Flatpak? Command line programs installed with [Flatpak][flatpak] cannot be run directly. Instead, they must be run with `flatpak run`. This can either be added by using a wrapper script or by configuring chezmoi to invoke `flatpak run` with the correct arguments directly. Wrapper scripts are recommended, as they work with the [`doctor` command][doctor]. ### Use a wrapper script Create a wrapper script with the exact same name as the command that invokes `flatpak run` and passes all arguments to the wrapped command. For example, to wrap KeePassXC installed with Flatpak, create the script: ```bash title="keepassxc-cli" #!/bin/bash flatpak run --command=keepassxc-cli org.keepassxc.KeePassXC -- "$@" ``` Note that the script is called `keepassxc-cli` without any `.sh` extension, so it has the exact same name as the `keepassxc-cli` command that chezmoi invokes by default. Ensure that this script is in your path and is executable. ### Configure chezmoi to invoke `flatpak run` For tools that chezmoi invokes with `.command` and `.args` configuration variables, you can configure chezmoi to invoke `flatpak` directly with the correct arguments. For example, to use VSCodium installed with Flatpak as your diff command, add the following to your config file: ```toml title="~/.config/chezmoi/chezmoi.toml" [diff] command = "flatpak" args = ["run", "com.vscodium.codium", "--wait", "--diff"] ``` Note that the command is `flatpak`, the first two arguments are `run` and the name of app, and any further arguments are passed to the app. [atuin]: https://atuin.sh/ [auto-commit]: /user-guide/daily-operations.md#automatically-commit-and-push-changes-to-your-repo [bash]: https://www.gnu.org/software/bash/ [choose]: https://github.com/twpayne/chezmoi/issues/new/choose [completion-cmd]: /reference/commands/completion.md [completion-fun]: /reference/templates/functions/completion.md [fish]: https://fishshell.com/ [ignore]: /reference/special-files/chezmoiignore.md [powershell]: https://learn.microsoft.com/en-us/powershell/ [scriptenv]: /user-guide/use-scripts-to-perform-actions.md#set-environment-variables [zsh]: https://zsh.sourceforge.io/ [flatpak]: https://flatpak.org/ [doctor]: /reference/commands/doctor.md ================================================ FILE: assets/chezmoi.io/docs/user-guide/include-files-from-elsewhere.md ================================================ # Include dotfiles from elsewhere The sections below contain examples of how to use `.chezmoiexternal.toml` to include files from external sources. For more details, check the [reference manual][external]. ## Include a subdirectory from a URL To include a subdirectory from another repository, e.g. [Oh My Zsh][ohmyzsh], you cannot use git submodules because chezmoi uses its own format for the source state and Oh My Zsh is not distributed in this format. Instead, you can use the `.chezmoiexternal.$FORMAT` file to tell chezmoi to import dotfiles from an external source. For example, to import Oh My Zsh, the [zsh-syntax-highlighting plugin][hlplug], and [powerlevel10k][p10k], put the following in `~/.local/share/chezmoi/.chezmoiexternal.toml`: ```toml title="~/.local/share/chezmoi/.chezmoiexternal.toml" [".oh-my-zsh"] type = "archive" url = "https://github.com/ohmyzsh/ohmyzsh/archive/master.tar.gz" exact = true stripComponents = 1 refreshPeriod = "168h" [".oh-my-zsh/custom/plugins/zsh-syntax-highlighting"] type = "archive" url = "https://github.com/zsh-users/zsh-syntax-highlighting/archive/master.tar.gz" exact = true stripComponents = 1 refreshPeriod = "168h" [".oh-my-zsh/custom/themes/powerlevel10k"] type = "archive" url = "https://github.com/romkatv/powerlevel10k/archive/v1.15.0.tar.gz" exact = true stripComponents = 1 ``` To apply the changes, run: ```sh chezmoi apply ``` chezmoi will download the archives and unpack them as if they were part of the source state. chezmoi caches downloaded archives locally to avoid re-downloading them every time you run a chezmoi command, and will only re-download them at most every `refreshPeriod` (default never). In the above example `refreshPeriod` is set to `168h` (one week) for `.oh-my-zsh` and `.oh-my-zsh/custom/plugins/zsh-syntax-highlighting` because the URL point to tarballs of the `master` branch, which changes over time. No refresh period is set for `.oh-my-zsh/custom/themes/powerlevel10k` because the URL points to a tarball of a tagged version, which does not change over time. To bump the version of powerlevel10k, change the version in the URL. To force a refresh the downloaded archives, use the `--refresh-externals` flag to `chezmoi apply`: ```sh chezmoi --refresh-externals apply ``` `--refresh-externals` can be shortened to `-R`: ```sh chezmoi -R apply ``` When using Oh My Zsh, make sure you disable built-in auto-updates by setting `DISABLE_AUTO_UPDATE="true"` in `~/.zshrc`. Auto updates will cause the `~/.oh-my-zsh` directory to drift out of sync with chezmoi's source state. To update Oh My Zsh and its plugins, refresh the downloaded archives. !!! note If your external dependency target directory can contain cache files that are added during normal use, chezmoi will report that files have changed on `chezmoi apply`. To avoid this, add the cache directory to your [`.chezmoiignore`][ignore] file. For example, Oh My Zsh may cache completions in `.oh-my-zsh/cache/completions/`, which should be added to your `.chezmoiignore` file. !!! warning Do not use externals for large files or archives. chezmoi validates the exact contents of externals every time you run `chezmoi diff`, `chezmoi apply`, or `chezmoi verify`. For large externals, use a `run_onchange_` script to unpack the archive or file once instead. ## Include a subdirectory with selected files from a URL Use `include` pattern filters to include only selected files from an archive URL. For example, to import just the required source files of the [zsh-syntax-highlighting plugin][hlplug] in the example above, add in `include` filter to the `zsh-syntax-highlighting` section as shown below: ```toml title="~/.local/share/chezmoi/.chezmoiexternal.toml" [".oh-my-zsh/custom/plugins/zsh-syntax-highlighting"] type = "archive" url = "https://github.com/zsh-users/zsh-syntax-highlighting/archive/master.tar.gz" exact = true stripComponents = 1 refreshPeriod = "168h" include = ["*/*.zsh", "*/.version", "*/.revision-hash", "*/highlighters/**"] ``` ## Include a single file from a URL Including single files uses the same mechanism as including a subdirectory above, except with the external type `file` instead of `archive`. For example, to include [`plug.vim`][plug.vim] from [`github.com/junegunn/vim-plug`][vim-plug] in `~/.vim/autoload/plug.vim` put the following in `~/.local/share/chezmoi/.chezmoiexternal.toml`: ```toml title="~/.local/share/chezmoi/.chezmoiexternal.toml" [".vim/autoload/plug.vim"] type = "file" url = "https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim" refreshPeriod = "168h" ``` ## Extract a single file from an archive You can extract a single file from an archive using the `archive-file` type in `.chezmoiexternal.$FORMAT`, for example: ```toml title="~/.local/share/chezmoi/.chezmoiexternal.toml" {{ $ageVersion := "1.1.1" -}} [".local/bin/age"] type = "archive-file" url = "https://github.com/FiloSottile/age/releases/download/v{{ $ageVersion }}/age-v{{ $ageVersion }}-{{ .chezmoi.os }}-{{ .chezmoi.arch }}.tar.gz" path = "age/age" ``` This will extract the single archive member `age/age` from the given URL (which is computed for the current OS and architecture) to the target `./local/bin/age`. ## Import archives It is occasionally useful to import entire archives of configuration into your source state. The `import` command does this. For example, to import the latest version [`github.com/ohmyzsh/ohmyzsh`][ohmyzsh] to `~/.oh-my-zsh` run: ```sh curl -s -L -o ${TMPDIR}/oh-my-zsh-master.tar.gz https://github.com/ohmyzsh/ohmyzsh/archive/master.tar.gz mkdir -p $(chezmoi source-path)/dot_oh-my-zsh chezmoi import --strip-components 1 --destination ~/.oh-my-zsh ${TMPDIR}/oh-my-zsh-master.tar.gz ``` !!! note This only updates the source state. You will need to run: ```sh chezmoi apply ``` to update your destination directory. ## Handle tar archives in an unsupported compression format chezmoi natively understands tar archives. tar archives can be uncompressed or compressed in the bzip2, gzip, xz, or zstd formats. If you have a tar archive in an unsupported compression format then you can use a filter to decompress it. For example, before chezmoi natively supported the zstd compression format, you could handle `.tar.zst` external archives with, for example: ```toml title="~/.local/share/chezmoi/.chezmoiexternal.toml" [".Software/anki/2.1.54-qt6"] type = "archive" url = "https://github.com/ankitects/anki/releases/download/2.1.54/anki-2.1.54-linux-qt6.tar.zst" filter.command = "zstd" filter.args = ["-d"] format = "tar" ``` Here `filter.command` and `filter.args` together tell chezmoi to filter the downloaded data through `zstd -d`. The `format = "tar"` line tells chezmoi that output of the filter is an uncompressed tar archive. ## Include a subdirectory from a git repository You can configure chezmoi to keep a git repository up to date in a subdirectory by using the external type `git-repo`, for example: ```toml title="~/.local/share/chezmoi/.chezmoiexternal.toml" [".vim/pack/alker0/chezmoi.vim"] type = "git-repo" url = "https://github.com/alker0/chezmoi.vim.git" refreshPeriod = "168h" ``` If the directory does not exist then chezmoi will run `git clone` to clone it. If the directory does exist then chezmoi will run `git pull` to pull the latest changes, but not more often than every `refreshPeriod`. In the above example the `refreshPeriod` is `168h` which is one week. The default `refreshPeriod` is zero, which disables refreshes. You can force a refresh (i.e. force a `git pull`) by passing the `--refresh-externals`/`-R` flag to `chezmoi apply`. !!! warning chezmoi's support for `git-repo` externals is limited to running `git clone` and/or `git pull` in a directory. You must have a `git` binary in your `$PATH`. Using a `git-repo` external delegates management of the directory to git. chezmoi cannot manage any other files in that directory. The contents of `git-repo` externals will not be manifested in commands like `chezmoi diff` or `chezmoi dump`, and will be listed by `chezmoi unmanaged`. !!! hint If you need to manage extra files in a `git-repo` external, use an `archive` external instead with the URL pointing to an archive of the git repo's `master` or `main` branch. You can customize the arguments to `git clone` and `git pull` by setting the `$DIR.clone.args` and `$DIR.pull.args` variables in `.chezmoiexternal.$FORMAT`, for example: ```toml title="~/.local/share/chezmoi/.chezmoiexternal.toml" [".vim/pack/alker0/chezmoi.vim"] type = "git-repo" url = "https://github.com/alker0/chezmoi.vim.git" refreshPeriod = "168h" [".vim/pack/alker0/chezmoi.vim".pull] args = ["--ff-only"] ``` ## Use git submodules in your source directory !!! important If you use git submodules, then you should set the `external_` attribute on the subdirectory containing the submodule. You can include git repos from elsewhere as git submodules in your source directory. `chezmoi init` and `chezmoi update` are aware of git submodules and will run git with the `--recurse-submodules` flag by default. chezmoi assumes that all files and directories in its source state are in chezmoi's format, i.e. their filenames include attributes like `private_` and `run_`. Most git submodules are not in chezmoi's format and so files like `run_test.sh` will be interpreted by chezmoi as a `run_` script. To avoid this problem, set the `external_` attribute on all subdirectories that contain submodules. You can stop chezmoi from handling git submodules by passing the `--recurse-submodules=false` flag or setting the `update.recurseSubmodules` configuration variable to `false`. [external]: /reference/special-files/chezmoiexternal-format.md [ohmyzsh]: https://github.com/ohmyzsh/ohmyzsh [hlplug]: https://github.com/zsh-users/zsh-syntax-highlighting [p10k]: https://github.com/romkatv/powerlevel10k [ignore]: /reference/special-files/chezmoiignore.md [plug.vim]: https://github.com/junegunn/vim-plug/blob/master/plug.vim [vim-plug]: https://github.com/junegunn/vim-plug ================================================ FILE: assets/chezmoi.io/docs/user-guide/machines/containers-and-vms.md ================================================ # Containers and VMs You can use chezmoi to manage your dotfiles in [GitHub Codespaces][ghc], [Visual Studio Codespaces][vsc], and [Visual Studio Code Remote - Containers][vscrc]. For a quick start, you can clone the [`chezmoi/dotfiles` repository][czdot] which supports Codespaces out of the box. The workflow is different to using chezmoi on a new machine, notably: * These systems will automatically clone your `dotfiles` repo to `~/dotfiles`, so there is no need to clone your repo yourself. * The installation script must be non-interactive. * When running in a Codespace, the environment variable `CODESPACES` will be set to `true`. You can read its value with the [`env` template function][sprig-os]. First, if you are using a chezmoi configuration file template, ensure that it is non-interactive when running in Codespaces, for example, `.chezmoi.toml.tmpl` might contain: ```text {{- $codespaces:= env "CODESPACES" | not | not -}} sourceDir = {{ .chezmoi.sourceDir | quote }} [data] name = "Your name" codespaces = {{ $codespaces }} {{- if $codespaces }}{{/* Codespaces dotfiles setup is non-interactive, so set an email address */}} email = "your@email.com" {{- else }}{{/* Interactive setup, so prompt for an email address */}} email = {{ promptString "email" | quote }} {{- end }} ``` !!! info Setting the `sourceDir` configuration variable to `.chezmoi.sourceDir` is required because Codespaces clones your dotfiles repo to a different one to chezmoi's default. This sets the `codespaces` template variable, so you don't have to repeat `(env "CODESPACES")` in your templates. It also sets the `sourceDir` configuration to the `--source` argument passed in `chezmoi init`. Second, create an `install.sh` script that installs chezmoi and your dotfiles and add it to `.chezmoiignore` and your dotfiles repo: ```sh chezmoi generate install.sh > install.sh chmod a+x install.sh echo install.sh >> .chezmoiignore git add install.sh .chezmoiignore git commit -m "Add install.sh" ``` The generated script installs the latest version of chezmoi in `~/.local/bin` if needed, and then `chezmoi init ...` invokes chezmoi to create its configuration file and initialize your dotfiles. `--apply` tells chezmoi to apply the changes immediately, and `--source=...` tells chezmoi where to find the cloned `dotfiles` repo, which in this case is the same folder in which the script is running from. Finally, modify any of your templates to use the `codespaces` variable if needed. For example, to install `vim-gtk` on Linux but not in Codespaces, your `run_onchange_install-packages.sh.tmpl` might contain: ```text {{- if (and (eq .chezmoi.os "linux") (not .codespaces)) -}} #!/bin/sh sudo apt install -y vim-gtk {{- end -}} ``` [ghc]: https://docs.github.com/en/github/developing-online-with-codespaces/personalizing-codespaces-for-your-account [vsc]: https://code.visualstudio.com/docs/remote/codespaces [vscrc]: https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories [czdot]: https://github.com/chezmoi/dotfiles [sprig-os]: http://masterminds.github.io/sprig/os.html ================================================ FILE: assets/chezmoi.io/docs/user-guide/machines/general.md ================================================ # General ## Determine whether the current machine is a laptop or desktop The following template sets the `$chassisType` variable to `"desktop"` or `"laptop"` on macOS, Linux, and Windows. ```text {{- $chassisType := "desktop" }} {{- if eq .chezmoi.os "darwin" }} {{- if contains "MacBook" (output "system_profiler" "SPHardwareDataType") }} {{- $chassisType = "laptop" }} {{- else }} {{- $chassisType = "desktop" }} {{- end }} {{- else if eq .chezmoi.os "linux" }} {{- $chassisType = (output "hostnamectl" "--json=short" | mustFromJson).Chassis }} {{- else if eq .chezmoi.os "windows" }} {{- $chassisType = (output "pwsh.exe" "-NoProfile" "-NonInteractive" "-Command" "if ((Get-CimInstance -Class Win32_Battery | Measure-Object).Count -gt 0) { Write-Output 'laptop' } else { Write-Output 'desktop' }") | trim }} {{- end }} ``` ## Determine how many CPU cores and threads the current machine has The following template sets the `$cpuCores` and `$cpuThreads` variables to the number of CPU cores and threads on the current machine respectively on macOS, Linux and Windows. ```text {{- $cpuCores := 1 }} {{- $cpuThreads := 1 }} {{- if eq .chezmoi.os "darwin" }} {{- $cpuCores = (output "sysctl" "-n" "hw.physicalcpu_max") | trim | atoi }} {{- $cpuThreads = (output "sysctl" "-n" "hw.logicalcpu_max") | trim | atoi }} {{- else if eq .chezmoi.os "linux" }} {{- $cpuCores = (output "sh" "-c" "lscpu --online --parse | grep --invert-match '^#' | sort --field-separator=',' --key='2,4' --unique | wc --lines") | trim | atoi }} {{- $cpuThreads = (output "sh" "-c" "lscpu --online --parse | grep --invert-match '^#' | wc --lines") | trim | atoi }} {{- else if eq .chezmoi.os "windows" }} {{- $cpuCores = (output "pwsh.exe" "-NoProfile" "-NonInteractive" "-Command" "(Get-CimInstance -ClassName 'Win32_Processor').NumberOfCores") | trim | atoi }} {{- $cpuThreads = (output "pwsh.exe" "-NoProfile" "-NonInteractive" "-Command" "(Get-CimInstance -ClassName 'Win32_Processor').NumberOfLogicalProcessors") | trim | atoi }} {{- end }} ``` !!! note The Windows examples above use `pwsh.exe` (PowerShell Core). If you don't have PowerShell Core installed, you can use `powershell.exe` instead (the built-in Windows PowerShell). !!! example ```text title="~/.local/share/chezmoi/.chezmoi.toml.tmpl" [data.cpu] cores = {{ $cpuCores }} threads = {{ $cpuThreads }} ``` ```text title="~/.local/share/chezmoi/is_hyperthreaded.txt.tmpl" {{- if gt .cpu.threads .cpu.cores -}} Hyperthreaded! {{- else -}} Not hyperthreaded! {{- end -}} ``` ================================================ FILE: assets/chezmoi.io/docs/user-guide/machines/linux.md ================================================ # Linux ## Combine operating system and Linux distribution conditionals There can be as much variation between Linux distributions as there is between operating systems. Due to `text/template`'s eager evaluation of conditionals, this means you often have to write templates with nested conditionals: ```text {{ if eq .chezmoi.os "darwin" }} # macOS-specific code {{ else if eq .chezmoi.os "linux" }} {{ if eq .chezmoi.osRelease.id "debian" }} # Debian-specific code {{ else if eq .chezmoi.osRelease.id "fedora" }} # Fedora-specific code {{ end }} {{ end }} ``` This can be simplified by combining the operating system and distribution into a single custom template variable. Put the following in your configuration file template: ```text {{- $osid := .chezmoi.os -}} {{- if hasKey .chezmoi.osRelease "id" -}} {{- $osid = printf "%s-%s" .chezmoi.os .chezmoi.osRelease.id -}} {{- end -}} [data] osid = {{ $osid | quote }} ``` This defines the `.osid` template variable to be `{{ .chezmoi.os }}` on machines without an [`os-release` file][os-release], or to be `{{ .chezmoi.os }}-{{ .chezmoi.osRelease.id }}` on machines with an `os-release` file. You can then simplify your conditionals to be: ```text {{ if eq .osid "darwin" }} # macOS-specific code {{ else if eq .osid "linux-debian" }} # Debian-specific code {{ else if eq .osid "linux-fedora" }} # Fedora-specific code {{ end }} ``` [os-release]: https://www.freedesktop.org/software/systemd/man/os-release.html ================================================ FILE: assets/chezmoi.io/docs/user-guide/machines/macos.md ================================================ # macOS ## Use `brew bundle` to manage your brews and casks Homebrew's [`brew bundle` subcommand][bundle] allows you to specify a list of brews and casks to be installed. You can integrate this with chezmoi by creating a `run_onchange_` script. For example, create a file in your source directory called `run_onchange_before_install-packages-darwin.sh.tmpl` containing: ```text {{- if eq .chezmoi.os "darwin" -}} #!/bin/bash brew bundle --file=/dev/stdin < "${tempfile}" # modify ${tempfile} cat "${tempfile}" ``` !!! note If the file does not exist then the standard input to the `modify_` script will be empty and it is the script's responsibility to write a complete file to the standard output. `modify_` scripts that contain the string `chezmoi:modify-template` will have all lines containing that string removed, and the rest of the file will be interpreted as a template. The template is executed with the current contents of the file passed as `.chezmoi.stdin`, and the result of the template execution becomes the new contents of the file. !!! example To replace the string `old` with `new` in a file while leaving the rest of the file unchanged, use the modify script: ```text {{- /* chezmoi:modify-template */ -}} {{- .chezmoi.stdin | replaceAllRegex "old" "new" }} ``` To set individual values in JSON, JSONC, TOML, and YAML files you can use the `setValueAtPath` template function, for example: ```text {{- /* chezmoi:modify-template */ -}} {{ fromJson .chezmoi.stdin | setValueAtPath "key.nestedKey" "value" | toPrettyJson }} ``` !!! warning Modify templates **must not** have a `.tmpl` extension. Secondly, if only a small part of the file changes then consider using a template to re-generate the full contents of the file from the current state. For example, Kubernetes configurations include a current context that can be substituted with: ```title="~/.local/share/chezmoi/dot_kube/config.tmpl" current-context: {{ output "kubectl" "config" "current-context" | trim }} ``` !!! hint For managing ini files with a mix of settings and state (such as recently used files or window positions), there is a third party tool called `chezmoi_modify_manager` that builds upon `modify_` scripts. See [related software][chezmoi_modify_manager] for more information. ## Manage a file's permissions, but not its contents chezmoi's `create_` attributes allows you to tell chezmoi to create a file if it does not already exist. chezmoi, however, will apply any permission changes from the `executable_`, `private_`, and `readonly_` attributes. This can be used to control a file's permissions without altering its contents. For example, if you want to ensure that `~/.kube/config` always has permissions 600 then if you create an empty file called `dot_kube/private_config` in your source state, chezmoi will ensure `~/.kube/config`'s permissions are 0600 when you run `chezmoi apply` without changing its contents. This approach does have the downside that chezmoi will create the file if it does not already exist. If you only want `chezmoi apply` to set a file's permissions if it already exists and not create the file otherwise, you can use a `run_` script. For example, create a file in your source state called `run_set_kube_config_permissions.sh` containing: ```bash #!/bin/sh FILE="$HOME/.kube/config" if [ -f "$FILE" ]; then if [ "$(stat -c %a "$FILE")" != "600" ] ; then chmod 600 "$FILE" fi fi ``` ## Handle configuration files which are externally modified Some programs modify their configuration files. When you next run `chezmoi apply`, any modifications made by the program will be lost. You can track changes to these files by replacing with a symlink back to a file in your source directory, which is under version control. Here is a worked example for VSCode's `settings.json` on Linux: Copy the configuration file to your source directory: ```sh cp ~/.config/Code/User/settings.json $(chezmoi source-path) ``` Tell chezmoi to ignore this file: ```sh echo settings.json >> $(chezmoi source-path)/.chezmoiignore ``` Tell chezmoi that `~/.config/Code/User/settings.json` should be a symlink to the file in your source directory: ```sh mkdir -p $(chezmoi source-path)/private_dot_config/private_Code/User echo -n "{{ .chezmoi.sourceDir }}/settings.json" > $(chezmoi source-path)/private_dot_config/private_Code/User/symlink_settings.json.tmpl ``` The prefix `private_` is used because the `~/.config` and `~/.config/Code` directories are private by default. Apply the changes: ```sh chezmoi apply -v ``` Now, when the program modifies its configuration file it will modify the file in the source state instead. ## Populate `~/.ssh/authorized_keys` with your public SSH keys from GitHub chezmoi can retrieve your public SSH keys from GitHub, which can be useful for populating your `~/.ssh/authorized_keys`. Put the following in your `~/.local/share/chezmoi/dot_ssh/authorized_keys.tmpl`: ```text {{ range gitHubKeys "$GITHUB_USERNAME" -}} {{ .Key }} {{ end -}} ``` [chezmoi_modify_manager]: /links/related-software.md#vorpalblade/chezmoi_modify_manager ================================================ FILE: assets/chezmoi.io/docs/user-guide/manage-machine-to-machine-differences.md ================================================ # Manage machine-to-machine differences ## Use templates The primary goal of chezmoi is to manage configuration files across multiple machines, for example your personal macOS laptop, your work Ubuntu desktop, and your work Linux laptop. You will want to keep much configuration the same across these, but also need machine-specific configurations for email addresses, credentials, etc. chezmoi achieves this functionality by using [`text/template`][go-template] for the source state where needed. For example, your home `~/.gitconfig` on your personal machine might look like: ```toml title="~/.gitconfig" [user] email = "me@home.org" ``` Whereas at work it might be: ```toml title="~/.gitconfig" [user] email = "firstname.lastname@company.com" ``` To handle this, on each machine create a configuration file called `~/.config/chezmoi/chezmoi.toml` defining variables that might vary from machine to machine. For example, for your home machine: ```toml title="~/.config/chezmoi/chezmoi.toml" [data] email = "me@home.org" ``` If you intend to store private data (e.g. access tokens) in `~/.config/chezmoi/chezmoi.toml`, make sure it has permissions `0600`. If you prefer, you can use JSON, JSONC, or YAML for your configuration file. Variable names must start with a letter and be followed by zero or more letters or digits. Then, add `~/.gitconfig` to chezmoi using the `--template` flag to turn it into a template: ```sh chezmoi add --template ~/.gitconfig ``` You can then open the template (which will be saved in the file `~/.local/share/chezmoi/dot_gitconfig.tmpl`): ```sh chezmoi edit ~/.gitconfig ``` Edit the file so it looks something like: ```toml title="~/.local/share/chezmoi/dot_gitconfig.tmpl" [user] email = {{ .email | quote }} ``` Templates are often used to capture machine-specific differences. For example, in your `~/.local/share/chezmoi/dot_bashrc.tmpl` you might have: ```text title="~/.local/share/chezmoi/dot_bashrc.tmpl" # common config export EDITOR=vi # machine-specific configuration {{- if eq .chezmoi.hostname "work-laptop" }} # this will only be included in ~/.bashrc on work-laptop {{- end }} ``` For a full list of variables, run: ```sh chezmoi data ``` For more advanced usage, you can use the full power of the [`text/template`][go-template] language. chezmoi includes all of the text functions from [sprig][sprig] and its own [functions][functions] for interacting with [password managers][passman]. Templates can be executed directly from the command line, without the need to create a file on disk, with the `execute-template` command, for example: ```sh chezmoi execute-template "{{ .chezmoi.os }}/{{ .chezmoi.arch }}" ``` This is useful when developing or [debugging templates][debugging]. Some password managers allow you to store complete files. The files can be retrieved with chezmoi's template functions. For example, if you have a file stored in 1Password with the UUID `uuid` then you can retrieve it with the template: ```text {{- onepasswordDocument "uuid" -}} ``` The `-`s inside the brackets remove any whitespace before or after the template expression, which is useful if your editor has added any newlines. If, after executing the template, the file contents are empty, the target file will be removed. This can be used to ensure that files are only present on certain machines. If you want an empty file to be created anyway, you will need to give it an `empty_` prefix. ## Ignore files or a directory on different machines For coarser-grained control of files and entire directories managed on different machines, or to exclude certain files completely, you can create `.chezmoiignore` files in the source directory. These specify a list of patterns that chezmoi should ignore, and are interpreted as templates. An example `.chezmoiignore` file might look like: ```text title="~/.local/share/chezmoi/.chezmoiignore" README.md {{- if ne .chezmoi.hostname "work-laptop" }} .work # only manage .work on work-laptop {{- end }} ``` The use of `ne` (not equal) is deliberate. What we want to achieve is "only install `.work` if hostname is `work-laptop`" but chezmoi installs everything by default, so we have to turn the logic around and instead write "ignore `.work` unless the hostname is `work-laptop`". Patterns can be excluded by starting the line with a `!`, for example: ```text title="~/.local/share/chezmoi/.chezmoiignore" dir/f* !dir/foo ``` will ignore all files beginning with an `f` in `dir` except for `dir/foo`. You can see what files chezmoi ignores with the command ```sh chezmoi ignored ``` ## Handle different file locations on different systems with the same contents If you want to have the same file contents in different locations on different systems, but maintain only a single file in your source state, you can use a shared template. Create the common file in the `.chezmoitemplates` directory in the source state. For example, create `.chezmoitemplates/file.conf`. The contents of this file are available in templates with the `template $NAME .` function where `$NAME` is the name of the file (`.` passes the current data to the template code in `file.conf`; see [`template` action][go-template-actions] for details). Then create files for each system, for example `Library/Application Support/App/file.conf.tmpl` for macOS and `dot_config/app/file.conf.tmpl` for Linux. Both template files should contain `{{- template "file.conf" . -}}`. Finally, tell chezmoi to ignore files where they are not needed by adding lines to your `.chezmoiignore` file, for example: ```text title="~/.local/share/chezmoi/.chezmoiignore" {{ if ne .chezmoi.os "darwin" }} Library/Application Support/App/file.conf {{ end }} {{ if ne .chezmoi.os "linux" }} .config/app/file.conf {{ end }} ``` ## Use completely different dotfiles on different machines chezmoi's template functionality allows you to change a file's contents based on any variable. For example, if you want `~/.bashrc` to be different on Linux and macOS you would create a file in the source state called `dot_bashrc.tmpl` containing: ```text title="~/.local/share/chezmoi/dot_bashrc.tmpl" {{ if eq .chezmoi.os "darwin" -}} # macOS .bashrc contents {{ else if eq .chezmoi.os "linux" -}} # Linux .bashrc contents {{ end -}} ``` However, if the differences between the two versions are so large that you'd prefer to use completely separate files in the source state, you can achieve this with the `include` template function. Create the following files: ```bash title="~/.local/share/chezmoi/.bashrc_darwin" # macOS .bashrc contents ``` ```bash title="~/.local/share/chezmoi/.bashrc_linux" # Linux .bashrc contents ``` ```text title="~/.local/share/chezmoi/dot_bashrc.tmpl" {{- if eq .chezmoi.os "darwin" -}} {{- include ".bashrc_darwin" -}} {{- else if eq .chezmoi.os "linux" -}} {{- include ".bashrc_linux" -}} {{- end -}} ``` This will cause `~/.bashrc` to contain `~/.local/share/chezmoi/.bashrc_darwin` on macOS and `~/.local/share/chezmoi/.bashrc_linux` on Linux. If you want to use templates within your templates, then, instead, create: ```text title="~/.local/share/chezmoi/.chezmoitemplates/bashrc_darwin.tmpl" # macOS .bashrc template contents ``` ```text title="~/.local/share/chezmoi/.chezmoitemplates/bashrc_linux.tmpl" # Linux .bashrc template contents ``` ```text title="~/.local/share/chezmoi/dot_bashrc.tmpl" {{- if eq .chezmoi.os "darwin" -}} {{- template "bashrc_darwin.tmpl" . -}} {{- else if eq .chezmoi.os "linux" -}} {{- template "bashrc_linux.tmpl" . -}} {{- end -}} ``` [go-template]: https://pkg.go.dev/text/template [sprig]: http://masterminds.github.io/sprig/ [functions]: /reference/templates/functions/index.md [debugging]: /user-guide/templating.md#testing-templates [go-template-actions]: https://pkg.go.dev/text/template#hdr-Actions [passman]: /user-guide/password-managers/index.md ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/1password.md ================================================ # 1Password chezmoi includes support for [1Password][1p] using the [1Password CLI][op] to expose data as a template function. Log in and get a session using: ```sh op account add --address $SUBDOMAIN.1password.com --email $EMAIL eval $(op signin --account $SUBDOMAIN) ``` This is not necessary if you are using biometric authentication. The output of `op read $URL` is available as the `onepasswordRead` template function, for example: ```text {{ onepasswordRead "op://app-prod/db/password" }} ``` returns the output of ```sh op read op://app-prod/db/password ``` Documents can be retrieved with: ```text {{- onepasswordDocument "$UUID" -}} ``` The output of `op item get $UUID --format json` is available as the `onepassword` template function. chezmoi parses the JSON output and returns it as structured data. For example, if the output is: ```json { "id": "$UUID", "title": "$TITLE", "version": 2, "vault": { "id": "$vaultUUID" }, "category": "LOGIN", "last_edited_by": "$userUUID", "created_at": "2010-08-23T13:18:43Z", "updated_at": "2014-07-20T04:40:11Z", "fields": [ { "id": "username", "type": "STRING", "purpose": "USERNAME", "label": "username", "value": "$USERNAME" }, { "id": "password", "type": "CONCEALED", "purpose": "PASSWORD", "label": "password", "value": "$PASSWORD", "password_details": { "strength": "FANTASTIC", "history": [] } } ], "urls": [ { "primary": true, "href": "$URL" } ] } ``` Then you can access the password field with the syntax ```text {{ (index (onepassword "$UUID").fields 1).value }} ``` or: ```text {{ range (onepassword "$UUID").fields -}} {{ if and (eq .label "password") (eq .purpose "PASSWORD") -}} {{ .value -}} {{ end -}} {{ end }} ``` `onepasswordDetailsFields` returns a reworked version of the structure that allows the fields to be queried by key: ```json { "password": { "id": "password", "label": "password", "password_details": { "history": [], "strength": "FANTASTIC" }, "purpose": "PASSWORD", "type": "CONCEALED", "value": "$PASSWORD" }, "username": { "id": "username", "label": "username", "purpose": "USERNAME", "type": "STRING", "value": "$USERNAME" } } ``` ```text {{- (onepasswordDetailsFields "$UUID").password.value }} ``` Additional fields may be obtained with `onepasswordItemFields`; not all objects in 1Password have item fields. This can be tested with: ```sh chezmoi execute-template "{{ onepasswordItemFields \"$UUID\" | toJson }}" | jq . ``` ## Sign-in prompt chezmoi will verify the availability and validity of a session token in the current environment. If it is missing or expired, you will be interactively prompted to sign-in again. In the past chezmoi used to exit with an error when no valid session was available. If you'd like to restore this behavior, set the `onepassword.prompt` configuration variable to `false`, for example: ```toml title="~/.config/chezmoi/chezmoi.toml" [onepassword] prompt = false ``` !!! danger Do not use `prompt` on shared machines. A session token verified or acquired interactively will be passed to the 1Password CLI through a command line parameter, which is visible to other users of the same system. ## Secrets Automation chezmoi has experimental support for secrets automation with [1Password Connect][connect] and [1Password Service Accounts][service]. These might be used on restricted machines where you cannot or do not wish to install a full 1Password desktop application. When these features are used, the behavior of the 1Password CLI changes, so chezmoi requires explicit configuration for either connect or service account modes using the `onepassword.mode` configuration option. The default, if not specified, is `account`: ```toml title="~/.config/chezmoi/chezmoi.toml" [onepassword] mode = "account" ``` In `account` mode, chezmoi will stop with an error if the environment variable `OP_SERVICE_ACCOUNT_TOKEN` is set, or if both environment variables `OP_CONNECT_HOST` and `OP_CONNECT_TOKEN` are set. !!! info Both 1Password Connect and Service Accounts prevent the CLI from working with multiple accounts. If you need access to secrets from more than one 1Password account, do not use these features with chezmoi. ### 1Password Connect Once 1Password Connect is [configured][configured], and `OP_CONNECT_HOST` and `OP_CONNECT_TOKEN` are properly set, set `onepassword.mode` to `connect`. ```toml title="~/.config/chezmoi/chezmoi.toml" [onepassword] mode = "connect" ``` In `connect` mode: - the `onepasswordDocument` template function is not available, - `account` parameters are not allowed in 1Password template functions, - chezmoi will stop with an error if one or both of `OP_CONNECT_HOST` and `OP_CONNECT_TOKEN` are unset, or if `OP_SERVICE_ACCOUNT_TOKEN` is set. ### 1Password Service Accounts Once a 1Password service account has been [created][created] and `OP_SERVICE_ACCOUNT_TOKEN` is properly set, set `onepassword.mode` to `service`. ```toml title="~/.config/chezmoi/chezmoi.toml" [onepassword] mode = "service" ``` In `service` mode: - `account` parameters are not allowed in 1Password template functions, - chezmoi will stop with an error if `OP_SERVICE_ACCOUNT_TOKEN` is unset, or if both of `OP_CONNECT_HOST` and `OP_CONNECT_TOKEN` are set. [1p]: https://1password.com/ [op]: https://support.1password.com/command-line-getting-started/ [connect]: https://developer.1password.com/docs/connect/ [service]: https://developer.1password.com/docs/service-accounts [configured]: https://developer.1password.com/docs/connect/connect-cli#requirements [created]: https://developer.1password.com/docs/service-accounts/use-with-1password-cli/#requirements ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/aws-secrets-manager.md ================================================ # AWS Secrets Manager chezmoi includes support for [AWS Secrets Manager][awssm]. Structured data can be retrieved with the `awsSecretsManager` template function, for example: ```text exampleUsername = {{ (awsSecretsManager "my-secret-name").username }} examplePassword = {{ (awsSecretsManager "my-secret-name").password }} ``` For retrieving unstructured data, the `awsSecretsManagerRaw` template function can be used. For example: ```text exampleSecretString = {{ awsSecretsManagerRaw "my-secret-string" }} ``` The AWS shared profile name and region can be specified in chezmoi's config file with `awsSecretsManager.profile` and `awsSecretsManager.region` respectively. By default, these values will be picked up from the standard environment variables and config files used by the standard AWS tooling. ```toml title="~/.config/chezmoi/chezmoi.toml" [awsSecretsManager] profile = myWorkProfile region = us-east-2 ``` [awssm]: https://aws.amazon.com/secrets-manager/ ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/azure-key-vault.md ================================================ # Azure Key Vault chezmoi includes support for [Azure Key Vault secrets][azure-key]. A default Azure Key Vault name can be set in `~/.config/chezmoi/chezmoi.toml` with `azureKeyVault.defaultVault`. Ensure [Azure CLI][cli] is installed and [log in][login]. The logged in user must have the `Key Vault Secrets User` RBAC role on the Azure Key Vault resource. Alternatively, use alternate [authentication options][auth]. ```toml title="~/.config/chezmoi/chezmoi.toml" [azureKeyVault] defaultVault = "contoso-vault2" ``` A secret value can be retrieved with the `azureKeyVault` template function. Retrieve the secret `my-secret-name` from the default configured vault. ```text exampleSecret = {{ azureKeyVault "my-secret-name" }} ``` Retrieve the secret `my-secret-name` from the vault named `contoso-vault2`. ```text exampleSecret = {{ azureKeyVault "my-secret-name" "contoso-vault2" }} ``` It is also possible to define an alias in the configuration file for an additional vault. ```toml title="~/.config/chezmoi/chezmoi.toml" [data] vault42 = "contoso-vault42" [azureKeyVault] defaultVault = "contoso-vault2" ``` Retrieve the secret `my-secret-name` from the vault named `contoso-vault42` through the alias. ```text exampleSecret = {{ azureKeyVault "my-secret-name" .vault42 }} ``` [azure-key]: https://learn.microsoft.com/en-us/azure/key-vault/secrets/about-secrets [cli]: https://learn.microsoft.com/en-us/cli/azure/install-azure-cli [login]: https://learn.microsoft.com/en-us/azure/developer/go/azure-sdk-authentication?tabs=bash#azureCLI [auth]: https://learn.microsoft.com/en-us/azure/developer/go/azure-sdk-authentication?tabs=bash#2-authenticate-with-azure ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/bitwarden.md ================================================ # Bitwarden chezmoi includes support for [Bitwarden][bitwarden] using the [Bitwarden CLI][cli] (`bw`), [Bitwarden Secrets CLI][secrets] (`bws`), and [`rbw`][rbw] commands to expose data as a template function. ## Bitwarden CLI Log in to Bitwarden using a normal method ```sh bw login $BITWARDEN_EMAIL # or bw login --apikey # or bw login --sso ``` If required, unlock your Bitwarden vault (API key and SSO logins always require an explicit unlock step): ```sh bw unlock ``` Set the `BW_SESSION` environment variable, as instructed. !!! tip "Bitwarden Session One-liner" The `BW_SESSION` value can be set directly. The exact combination differs based on whether you are currently logged into Bitwarden and how you log into Bitwarden. ```sh export BW_SESSION=$(bw unlock --raw) # You are already logged in with any method export BW_SESSION=$(bw login $BITWARDEN_EMAIL --raw) # You are not logged in and log in with an email export BW_SESSION=$(bw login --sso && bw unlock --raw) # You are not logged in and login with SSO or API key ``` !!! tip "Bitwarden automatic unlock" If you set the `bitwarden.unlock` configuration variable to `"auto"` in your config file, chezmoi will automatically call `bw unlock` if the `BW_SESSION` environment variable is not set. The structured data from `bw get` is available as the `bitwarden` template function in your config files, for example: ```text username = {{ (bitwarden "item" "example.com").login.username }} password = {{ (bitwarden "item" "example.com").login.password }} ``` Custom fields can be accessed with the `bitwardenFields` template function. For example, if you have a custom field named `token` you can retrieve its value with: ```text {{ (bitwardenFields "item" "example.com").token.value }} ``` Attachments can be accessed with the `bitwardenAttachment` and `bitwardenAttachmentByRef` template function. For example, if you have an attachment named `id_rsa`, you can retrieve its value with: ```text {{ bitwardenAttachment "id_rsa" "bf22e4b4-ae4a-4d1c-8c98-ac620004b628" }} ``` or ```text {{ bitwardenAttachmentByRef "id_rsa" "item" "example.com" }} ``` ## Bitwarden Secrets CLI Generate an [access token][token] for a specific [service account][account]. Either set the `BWS_ACCESS_TOKEN` environment variable or store the access token in a template variable, e.g. ```toml title="~/.config/chezmoi/chezmoi.toml" [data] accessToken = "0.48c78342-1635-48a6-accd-afbe01336365.C0tMmQqHnAp1h0gL8bngprlPOYutt0:B3h5D+YgLvFiQhWkIq6Bow==" ``` You can then retrieve secrets using the `bitwardenSecrets` template function, for example: ```text {{ (bitwardenSecrets "be8e0ad8-d545-4017-a55a-b02f014d4158" .accessToken).value }} ``` [bitwarden]: https://bitwarden.com/ [cli]: https://bitwarden.com/help/cli [secrets]: https://bitwarden.com/help/secrets-manager-cli/ [rbw]: https://github.com/doy/rbw [token]: https://bitwarden.com/help/access-tokens/ [account]: https://bitwarden.com/help/service-accounts/ ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/custom.md ================================================ # Custom You can use any command line tool that outputs secrets either as a string or in JSON format. Choose the binary by setting `secret.command` in your configuration file. You can then invoke this command with the `secret` and `secretJSON` template functions which return the raw output and JSON-decoded output respectively. All of the above secret managers can be supported in this way: | Secret Manager | `secret.command` | Template skeleton | | --------------- | ---------------- | ---------------------------------------------------------------- | | 1Password | `op` | `{{ secretJSON "get" "item" "$ID" }}` | | Bitwarden | `bw` | `{{ secretJSON "get" "$ID" }}` | | Doppler | `doppler` | `{{ secretJSON "secrets" "download" "--json" "--no-file" }}` | | HashiCorp Vault | `vault` | `{{ secretJSON "kv" "get" "-format=json" "$ID" }}` | | LastPass | `lpass` | `{{ secretJSON "show" "--json" "$ID" }}` | | KeePassXC | `keepassxc-cli` | Not possible (interactive command only) | | Keeper | `keeper` | `{{ secretJSON "get" "--format=json" "$ID" }}` | | pass | `pass` | `{{ secret "show" "$ID" }}` | | passhole | `ph` | `{{ secret "$ID" "password" }}` | | Proton Pass | `pass-cli` | `{{ secretJSON "item" "view" "$ID" "--output=json" }}` | ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/dashlane.md ================================================ # Dashlane chezmoi includes support for [Dashlane][dashlane]. Structured data can be retrieved with the `dashlanePassword` template function, for example: ```text examplePassword = {{ (index (dashlanePassword "filter") 0).password }} ``` Secure notes can be retrieved with the `dashlaneNote` template function, for example: ```text exampleNote = {{ dashlaneNote "filter" }} ``` [dashlane]: https://dashlane.com ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/doppler.md ================================================ # Doppler chezmoi includes support for [Doppler][doppler] using the `doppler` CLI to expose data through the `doppler` and `dopplerProjectJson` template functions. Log in using: ```sh doppler login ``` It is now possible to interact with the `doppler` CLI in two different, but similar, ways. Both make use of the command `doppler secrets download --json --no-file` behind the scenes but present a different experience. The `doppler` function is used in the following way: ```text {{ doppler "SECRET_NAME" "project name" "config" }} ``` All secrets from the specified project/config combination are cached for subsequent access and will not requery the `doppler` CLI for another secret in the same project/config. This caching mechanism enhances performance and reduces unnecessary CLI calls. The `dopplerProjectJson` presents the secrets as `json` structured data and is used in the following way: ```text {{ (dopplerProjectJson "project" "config").PASSWORD }} ``` Additionally one can set the default values for the project and config (aka environment) in your config file, for example: ```toml title="~/.config/chezmoi/chezmoi.toml" [doppler] project = "my-project" config = "dev" ``` With these default values, you can omit them in the call to both `doppler` and `dopplerProjectJson`, for example: ```text {{ doppler "SECRET_NAME" }} {{ dopplerProjectJson.SECRET_NAME }} ``` It is important to note that neither of the above parse any individual secret as `json`. This can be achieved by using the `fromJson` function, for example: ```text {{ (doppler "SECRET_NAME" | fromJson).created_by.email_address }} {{ (dopplerProjectJson.SECRET_NAME | fromJson).created_by.email_address }} ``` Obviously the secret would have to be saved in `json` format for this to work as expected. [doppler]: https://www.doppler.com ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/ejson.md ================================================ # ejson chezmoi includes support for [ejson][ejson]. Structured data can be retrieved with the `ejsonDecrypt` template function, for example: ```text examplePassword = {{ (ejsonDecrypt "my-secrets.ejson").password }} ``` If you want to specify the private key to use for the decryption, structured data can be retrieved with the `ejsonDecryptWithKey` template function, for example: ```text examplePassword = {{ (ejsonDecryptWithKey "my-secrets.ejson" "top-secret-key").password }} ``` [ejson]: https://github.com/Shopify/ejson ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/gopass.md ================================================ # gopass chezmoi includes support for [gopass][gopass] using the `gopass` CLI. The first line of the output of `gopass show $PASS_NAME` is available as the `gopass` template function, for example: ```text {{ gopass "$PASS_NAME" }} ``` [gopass]: https://www.gopass.pw/ ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/index.md ================================================ # Password Manager Integration Using a password manager with chezmoi enables you to maintain a public dotfiles repository while keeping your secrets secure. chezmoi extends its [templating capabilities][templating] by providing password manager specific *template functions* for many popular password managers. When chezmoi applies a template with a secret referenced from a password manager, it will automatically fetch the secret value and insert it into the generated destination file. !!! example Here's a practical example of a `.zshrc.tmpl` file that retrieves an CloudFlare API token from 1Password while maintaining other standard shell configurations: ```zsh # set up $PATH # … # Cloudflare API Token retrieved from 1Password for use with flarectl export CF_API_TOKEN='{{ onepasswordRead "op://Personal/cloudlfare-api-token/password" }}' # set up aliases and useful functions ``` In this example, the `CF_API_TOKEN` is retrieved from a 1Password vault named `Personal`, an item called `cloudflare-api-token`, and the `password` field. [templating]: /user-guide/templating.md ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/keepassxc.md ================================================ # KeePassXC chezmoi includes support for [KeePassXC][keepassxc] using the KeePassXC CLI (`keepassxc-cli`) to expose data as a template function. Provide the path to your KeePassXC database in your configuration file: ```toml title="~/.config/chezmoi/chezmoi.toml" [keepassxc] database = "/home/user/Passwords.kdbx" ``` The structured data from `keepassxc-cli show $database` is available as the `keepassxc` template function in your config files, for example: ```text username = {{ (keepassxc "example.com").UserName }} password = {{ (keepassxc "example.com").Password }} ``` Additional attributes are available through the `keepassxcAttribute` function. For example, if you have an entry called `SSH Key` with an additional attribute called `private-key`, its value is available as: ```text {{ keepassxcAttribute "SSH Key" "private-key" }} ``` ## Non-password-protected databases If your database is not password protected, add `--no-password` to `keepassxc.args` and `keepassxc.prompt = false`: ```toml title="~/.config/chezmoi/chezmoi.toml" [keepassxc] database = "/home/user/Passwords.kdbx" args = ["--no-password"] prompt = false ``` ## YubiKey support chezmoi includes an experimental mode to support using KeePassXC with YubiKeys. Set `keepassxc.mode` to `open` and `keepassxc.args` to the arguments required to set your YubiKey, for example: ```toml title="~/.config/chezmoi/chezmoi.toml" [keepassxc] database = "/home/user/Passwords.kdbx" args = ["--no-password", "--yubikey", "2:7370001"] mode = "open" ``` [keepassxc]: https://keepassxc.org ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/keeper.md ================================================ # Keeper chezmoi includes support for [Keeper][keeper] using the [Commander CLI][commander] to expose data as a template function. Create a persistent login session as [described in the Command CLI documentation][clidocs]. Passwords can be retrieved with the `keeperFindPassword` template function, for example: ```text examplePasswordFromPath = {{ keeperFindPassword "$PATH" }} examplePasswordFromUid = {{ keeperFindPassword "$UID" }} ``` For retrieving more complex data, use the `keeper` template function with a UID to retrieve structured data from [`keeper get`][get] or the `keeperDataFields` template function which restructures the output of `keeper get` in to a more convenient form, for example: ```text keeperDataTitle = {{ (keeper "$UID").data.title }} examplePassword = {{ index (keeperDataFields "$UID").password 0 }} ``` Extra arguments can be passed to the Keeper CLI command by setting the `keeper.args` variable in chezmoi's config file, for example: ```toml title="~/.config/chezmoi/chezmoi.toml" [keeper] args = ["--config", "/path/to/config.json"] ``` [keeper]: https://www.keepersecurity.com/ [commander]: https://docs.keeper.io/secrets-manager/commander-cli [clidocs]: https://docs.keeper.io/secrets-manager/commander-cli/using-commander/logging-in#persistent-login-sessions [get]: https://docs.keeper.io/secrets-manager/commander-cli/using-commander/command-reference/record-commands#get-command ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/keychain-and-windows-credentials-manager.md ================================================ # Keychain and Windows Credentials Manager chezmoi includes support for Keychain (on macOS), GNOME Keyring (on Linux and FreeBSD), and Windows Credentials Manager (on Windows) via the [`zalando/go-keyring`][keyring] library. Set values with: ```console $ chezmoi secret keyring set --service=$SERVICE --user=$USER Value: xxxxxxxx ``` The value can then be used in templates using the `keyring` function which takes the service and user as arguments. For example, save a GitHub access token in keyring with: ```console $ chezmoi secret keyring set --service=github --user=$GITHUB_USERNAME Value: xxxxxxxx ``` and then include it in your `~/.gitconfig` file with: ```text [github] user = {{ .github.user | quote }} token = {{ keyring "github" .github.user | quote }} ``` You can query the keyring from the command line: ```sh chezmoi secret keyring get --service=github --user=$GITHUB_USERNAME ``` [keyring]: https://github.com/zalando/go-keyring ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/lastpass.md ================================================ # LastPass chezmoi includes support for [LastPass][lastpass] using the [LastPass CLI][cli] to expose data as a template function. Log in to LastPass using: ```sh lpass login $LASTPASS_USERNAME ``` Check that `lpass` is working correctly by showing password data: ```sh lpass show --json $LASTPASS_ENTRY_ID ``` where `$LASTPASS_ENTRY_ID` is a [LastPass Entry Specification][spec]. The structured data from `lpass show --json id` is available as the `lastpass` template function. The value will be an array of objects. You can use the `index` function and `.Field` syntax of the `text/template` language to extract the field you want. For example, to extract the `password` field from first the "GitHub" entry, use: ```text githubPassword = {{ (index (lastpass "GitHub") 0).password | quote }} ``` chezmoi automatically parses the `note` value of the LastPass entry as colon-separated key-value pairs, so, for example, you can extract a private SSH key like this: ```text {{ (index (lastpass "SSH") 0).note.privateKey }} ``` Keys in the `note` section written as `CamelCase Words` are converted to `camelCaseWords`. If the `note` value does not contain colon-separated key-value pairs, then you can use `lastpassRaw` to get its raw value, for example: ```text {{ (index (lastpassRaw "SSH Private Key") 0).note }} ``` [lastpass]: https://lastpass.com/ [cli]: https://lastpass.github.io/lastpass-cli/lpass.1.html [spec]: https://lastpass.github.io/lastpass-cli/lpass.1.html#_entry_specification ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/pass.md ================================================ # pass chezmoi includes support for [pass][pass] using the pass CLI. The first line of the output of `pass show $PASS_NAME` is available as the `pass` template function, for example: ```text {{ pass "$PASS_NAME" }} ``` [pass]: https://www.passwordstore.org/ ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/passhole.md ================================================ # Passhole chezmoi includes support for [KeePass][keepass] using the [passhole CLI][cli] (`ph`) to expose data as a template function. [keepass]: https://keepass.info/ [cli]: https://github.com/Evidlo/passhole ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/proton-pass.md ================================================ # Proton Pass chezmoi includes support for [Proton Pass][protonpass] using the [Proton Pass CLI][cli]. Log in to Proton Pass using ```shell pass-cli login ``` The output of `pass-cli item view pass://$SHARE_ID/$ITEM_ID/$FIELD` is available as the `protonPass` template function, for example: ```text {{ protonPass "pass://$SHARE_ID/$ITEM_ID/$FIELD" }} ``` The output of `pass-cli item view --output=json pass://$SHARE_ID/$ITEM_ID` is available as `protonPassJSON` and returns the structured data the item holds. For example: ```text {{ (protonPassJSON "pass://$SHARE_ID/$ITEM_ID").item.content.content.key.password }} ``` [protonpass]: https://proton.me/pass [cli]: https://protonpass.github.io/pass-cli ================================================ FILE: assets/chezmoi.io/docs/user-guide/password-managers/vault.md ================================================ # Vault chezmoi includes support for [Vault][vault] using the [Vault CLI][cli] to expose data as a template function. The vault CLI needs to be correctly configured on your machine, e.g. the `VAULT_ADDR` and `VAULT_TOKEN` environment variables must be set correctly. Verify that this is the case by running: ```sh vault kv get -format=json $KEY ``` The structured data from `vault kv get -format=json` is available as the `vault` template function. You can use the `.Field` syntax of the `text/template` language to extract the data you want. For example: ```text {{ (vault "$KEY").data.data.password }} ``` [vault]: https://www.vaultproject.io/ [cli]: https://www.vaultproject.io/docs/commands/ ================================================ FILE: assets/chezmoi.io/docs/user-guide/setup.md ================================================ # Setup ## Understand chezmoi's files and directories chezmoi generates your dotfiles for your local machine. It combines two main sources of data: The *source directory*, `~/.local/share/chezmoi`, is common to all your machines, and is a clone of your dotfiles repo. Each file that chezmoi manages has a corresponding file in the source directory. The *config file*, typically `~/.config/chezmoi/chezmoi.toml` (although you can use JSON or YAML if you prefer), is specific to the local machine. Files whose contents are the same on all of your machines are copied verbatim from the source directory. Files which vary from machine to machine are executed as templates, typically using data from the local machine's config file to tune the final contents specific to the local machine. ## Use a hosted repo to manage your dotfiles across multiple machines chezmoi relies on your version control system and hosted repo to share changes across multiple machines. You should create a repo on the source code repository of your choice (e.g. [Bitbucket][bitbucket], [GitHub][github], or [GitLab][gitlab]; many people call their repo `dotfiles`) and push the repo in the source directory here. For example: ```sh chezmoi cd git remote add origin https://github.com/$GITHUB_USERNAME/dotfiles.git git push -u origin main exit ``` On another machine you can checkout this repo: ```sh chezmoi init https://github.com/$GITHUB_USERNAME/dotfiles.git ``` You can then see what would be changed: ```sh chezmoi diff ``` If you're happy with the changes then apply them: ```sh chezmoi apply ``` The above commands can be combined into a single command to initialize, checkout, and apply: ```sh chezmoi init --apply --verbose https://github.com/$GITHUB_USERNAME/dotfiles.git ``` These commands are summarized in this sequence diagram: ```mermaid sequenceDiagram participant H as home directory participant W as working copy participant L as local repo participant R as remote repo R->>W: chezmoi init $REPO W-->>H: chezmoi diff W->>H: chezmoi apply R->>H: chezmoi init --apply $REPO ``` ## Use a private repo to store your dotfiles chezmoi supports storing your dotfiles in both public and private repos. chezmoi is designed so that your dotfiles repo can be public by making it easy for you to store your secrets either in your password manager, in encrypted files, or in private configuration files. Your dotfiles repo can still be private, if you choose. If you use a private repo for your dotfiles then you will typically need to enter your credentials (e.g. your username and password) each time you interact with the repo, for example when pulling or pushing changes. chezmoi itself does not store any credentials, but instead relies on your local git configuration for these operations. When using a private repo on GitHub without `--ssh`, when prompted for a password you will need to enter a [GitHub personal access token][gh-pat]. For more information on these changes, read the [GitHub blog post on Token authentication requirements for Git operations][gh-token-auth]. ## Create a config file on a new machine automatically `chezmoi init` can also create a config file automatically, if one does not already exist. If your repo contains a file called `.chezmoi.$FORMAT.tmpl` where `$FORMAT` is one of the supported config file formats (`json`, `jsonc`, `toml`, or `yaml`) then `chezmoi init` will execute that template to generate your initial config file. Specifically, if you have `.chezmoi.toml.tmpl` that looks like this: ``` title="~/.local/share/chezmoi/.chezmoi.toml.tmpl" {{- $email := promptStringOnce . "email" "Email address" -}} [data] email = {{ $email | quote }} ``` Then `chezmoi init` will create an initial `chezmoi.toml` using this template. `promptStringOnce` is a special function that prompts the user (you) for a value if it is not already set in your `data`. To test this template, use `chezmoi execute-template` with the `--init` and `--promptString` flags, for example: ```sh chezmoi execute-template --init --promptString "Email address=me@home.org" < ~/.local/share/chezmoi/.chezmoi.toml.tmpl ``` ## Re-create your config file If you change your config file template, chezmoi will warn you if your current config file was not generated from that template. You can re-generate your config file by running: ```sh chezmoi init ``` If you are using any `prompt*` template functions in your config file template you will be prompted again. However, you can avoid this with the following example template logic: ```text {{- $email := promptStringOnce . "email" "Email address" -}} [data] email = {{ $email | quote }} ``` This will cause chezmoi use the `email` variable from your `data` and fallback to `promptString` only if it is not set. [bitbucket]: https://bitbucket.org [github]: https://github.com/ [gitlab]: https://gitlab.com [gh-pat]: https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token [gh-token-auth]: https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ ================================================ FILE: assets/chezmoi.io/docs/user-guide/templating.md ================================================ # Templating ## Introduction Templates are used to change the contents of a file depending on the environment. For example, you can use the hostname of the machine to create different configurations on different machines. chezmoi uses the [`text/template`][go-template] syntax from Go extended with [text template functions from `sprig`][sprig]. When reading files from the source state, chezmoi interprets them as a template if either of the following is true: * The file name has a `.tmpl` suffix. * The file is in the `.chezmoitemplates` directory, or a subdirectory of `.chezmoitemplates`. ## Template data chezmoi provides a variety of template variables. For a full list, run ```sh chezmoi data ``` These come from a variety of sources (later data overwrite earlier ones): * Variables populated by chezmoi are in `.chezmoi`, for example `.chezmoi.os`. * Variables created by you in `.chezmoidata.$FORMAT` configuration files. The various supported formats (`json`, `jsonc`, `toml` and `yaml`) are read in alphabetical order. * Variables created by you in the `data` section of the configuration file. Furthermore, chezmoi provides a variety of functions to retrieve data at runtime from password managers, environment variables, and the file system. ## Creating a template file There are several ways to create a template: * When adding a file for the first time, pass the `--template` argument, for example: ```sh chezmoi add --template ~/.zshrc ``` * If a file is already managed by chezmoi, but is not a template, you can make it a template by running, for example: ```sh chezmoi chattr +template ~/.zshrc ``` * You can create a template manually in the source directory by giving it a `.tmpl` extension, for example: ```sh chezmoi cd $EDITOR dot_zshrc.tmpl ``` * Templates in `.chezmoitemplates` must be created manually, for example: ```sh chezmoi cd mkdir -p .chezmoitemplates cd .chezmoitemplates $EDITOR mytemplate ``` ## Editing a template file The easiest way to edit a template is to use `chezmoi edit`, for example: ```sh chezmoi edit ~/.zshrc ``` This will open the source file for `~/.zshrc` in `$EDITOR`. When you quit the editor, chezmoi will check the template syntax. If you want the changes you make to be immediately applied after you quit the editor, use the `--apply` option, for example: ```sh chezmoi edit --apply ~/.zshrc ``` ## Testing templates Templates can be tested and debugged with `chezmoi execute-template`, which treats each of its arguments as a template and executes it. The templates are interpreted and the results are output to standard output, making it useful for testing small template fragments: ```sh chezmoi execute-template '{{ .chezmoi.hostname }}' ``` Without arguments, `chezmoi execute-template` will read the template from standard input, which is useful for testing whole files: ```sh chezmoi cd chezmoi execute-template < dot_zshrc.tmpl ``` If file redirection does not work (as when using PowerShell), the contents of a file can be piped into `chezmoi execute-template`: ```sh cat foo.txt | chezmoi execute-template ``` ## Template syntax Template actions are written inside double curly brackets, `{{` and `}}`. Actions can be variables, pipelines, or control statements. Text outside actions is copied literally. Variables are written literally, for example: ```text {{ .chezmoi.hostname }} ``` Conditional expressions can be written using `if`, `else if`, `else`, and `end`, for example: ```text {{ if eq .chezmoi.os "darwin" }} # darwin {{ else if eq .chezmoi.os "linux" }} # linux {{ else }} # other operating system {{ end }} ``` For a full description of the template syntax, see the [`text/template` documentation][go-template]. ### Removing whitespace For formatting reasons you might want to leave some whitespace after or before the template code. This whitespace will remain in the final file, which you might not want. A solution for this is to place a minus sign and a space next to the brackets. So `{{-` for the left brackets and `-}}` for the right brackets. Here's an example: ```text HOSTNAME={{- .chezmoi.hostname }} ``` This will result in ```text HOSTNAME=myhostname ``` Notice that this will remove any number of tabs, spaces and even newlines and carriage returns. ## Simple logic A very useful feature of chezmoi templates is the ability to perform logical operations. ```text # common config export EDITOR=vi # machine-specific configuration {{- if eq .chezmoi.hostname "work-laptop" }} # this will only be included in ~/.bashrc on work-laptop {{- end }} ``` In this example chezmoi will look at the hostname of the machine and if that is equal to "work-laptop", the text between the `if` and the `end` will be included in the result. ### Boolean functions | Function | Return value | | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `eq` | Returns true if the first argument is equal to any of the other arguments | | `not` | Returns the boolean negation of its single argument | | `and` | Returns the boolean AND of its arguments by returning the first empty argument or the last argument, that is, `and x y` behaves as `if x then y else x`. All the arguments are evaluated | | `or` | Returns the boolean OR of its arguments by returning the first non-empty argument or the last argument, that is, `or x y` behaves as `if x then x else y` All the arguments are evaluated | ### Integer functions | Function | Return value | | -------- | ------------------------------------------ | | `len` | Returns the integer length of its argument | | `eq` | Returns the boolean truth of arg1 == arg2 | | `ne` | Returns the boolean truth of arg1 != arg2 | | `lt` | Returns the boolean truth of arg1 < arg2 | | `le` | Returns the boolean truth of arg1 <= arg2 | | `gt` | Returns the boolean truth of arg1 > arg2 | | `ge` | Returns the boolean truth of arg1 >= arg2 | ## More complicated logic Up until now, we have only seen if statements that can handle at most two variables. In this part we will see how to create more complicated expressions. You can also create more complicated expressions. The `eq` command can accept multiple arguments. It will check if the first argument is equal to any of the other arguments. ```text {{ if eq "foo" "foo" "bar" }}hello{{end}} {{ if eq "foo" "bar" "foo" }}hello{{end}} {{ if eq "foo" "bar" "bar" }}hello{{end}} ``` The first two examples will output `hello` and the last example will output nothing. The operators `or` and `and` can also accept multiple arguments. ### Chaining operators You can perform multiple checks in one if statement. ```text {{ if (and (eq .chezmoi.os "linux") (ne .email "me@home.org")) }} ... {{ end }} ``` This will check if the operating system is Linux and the configured email is not the home email. The brackets are needed here, because otherwise all the arguments will be give to the `and` command. This way you can chain as many operators together as you like. ## Helper functions chezmoi has added multiple helper functions to the [`text/template`][go-template] syntax. chezmoi includes [`sprig`][sprig], an extension to the `text/template` format that contains many helper functions. Take a look at their documentation for a list. chezmoi adds a few functions of its own as well. Take a look at the [reference][reference] for a complete list. ## Template variables chezmoi defines a few useful template variables that depend on the system you are currently on. The variables defined by chezmoi are described in [Variables][variables]. There are, however more variables than that. To view the variables available on your system, execute: ```sh chezmoi data ``` This outputs the variables in JSON format by default. To access the variable `chezmoi.kernel.osrelease` in a template, use ```text {{ .chezmoi.kernel.osrelease }} ``` This way you can also access the variables you defined yourself. ## Using `.chezmoitemplates` Files in the `.chezmoitemplates` subdirectory are parsed as templates and are available to be included in other templates using the [`template` action][action] with a name equal to their relative path to the `.chezmoitemplates` directory. By default, such templates will be executed with `nil` data. If you want to access template variables (e.g. `.chezmoi.os`) in the template you must pass the data explicitly. For example: ```text .chezmoitemplates/part.tmpl: {{ if eq .chezmoi.os "linux" }} # linux config {{ else }} # non-linux config {{ end }} dot_file.tmpl: {{ template "part.tmpl" . }} ``` ## Using `.chezmoitemplates` for creating similar files When you have multiple similar files, but they aren't quite the same, you can create a template file in the directory `.chezmoitemplates`. This template can be inserted in other template files, for example: Create `.local/share/chezmoi/.chezmoitemplates/alacritty`: ```text some: config fontsize: {{ . }} more: config ``` Notice the file name doesn't have to end in `.tmpl`, as all files in the directory `.chezmoitemplates` are interpreted as templates. Create other files using the template `~/.local/share/chezmoi/small-font.yml.tmpl` ```text {{- template "alacritty" 12 -}} ``` `~/.local/share/chezmoi/big-font.yml.tmpl` ```text {{- template "alacritty" 18 -}} ``` Here we're calling the shared `alacritty` template with the font size as the `.` value passed in. You can test this with `chezmoi cat`: ```console $ chezmoi cat ~/small-font.yml some: config fontsize: 12 more: config $ chezmoi cat ~/big-font.yml some: config fontsize: 18 more: config ``` ### Passing multiple arguments In the example above only one arguments is passed to the template. To pass more arguments to the template, you can do it in two ways. #### Via the config file This method is useful if you want to use the same template arguments multiple times, because you don't specify the arguments every time. Instead you specify them in the file `~/.config/chezmoi/chezmoi.toml`: ```toml title="~/.config/chezmoi/chezmoi.toml" [data.alacritty.big] fontsize = 18 font = "DejaVu Serif" [data.alacritty.small] fontsize = 12 font = "DejaVu Sans Mono" ``` Use the variables in `~/.local/share/chezmoi/.chezmoitemplates/alacritty`: ```text title="~/.local/share/chezmoi/.chezmoitemplates/alacritty" some: config fontsize: {{ .fontsize }} font: {{ .font }} more: config ``` And connect them with `~/.local/share/chezmoi/small-font.yml.tmpl`: ```text title="~/.local/share/chezmoi/small-font.yml.tmpl" {{- template "alacritty" .alacritty.small -}} ``` At the moment, this means that you'll have to duplicate the alacritty data in the config file on every machine, but a feature will be added to avoid this. #### By passing a dictionary Using the same alacritty configuration as above, you can pass the arguments to it with a dictionary, for example `~/.local/share/chezmoi/small-font.yml.tmpl`: ```text title="~/.local/share/chezmoi/small-font.yml.tmpl" {{- template "alacritty" dict "fontsize" 12 "font" "DejaVu Sans Mono" -}} ``` [go-template]: https://pkg.go.dev/text/template [sprig]: http://masterminds.github.io/sprig/ [reference]: /reference/templates/functions/index.md [variables]: /reference/templates/variables.md [action]: https://pkg.go.dev/text/template#hdr-Actions ================================================ FILE: assets/chezmoi.io/docs/user-guide/tools/diff.md ================================================ # Diff ## Use a custom diff tool By default, chezmoi uses a built-in diff. You can use a custom tool by setting the `diff.command` and `diff.args` configuration variables. The elements of `diff.args` are interpreted as templates with the variables `.Destination` and `.Target` containing filenames of the file in the destination state and the target state respectively. For example, to use [meld][meld], specify: ```toml title="~/.config/chezmoi/chezmoi.toml" [diff] command = "meld" args = ["--diff", "{{ .Destination }}", "{{ .Target }}"] ``` !!! hint If you generate your config file from a config file template, then you'll need to escape the `{{` and `}}` as `{{ "{{" }}` and `{{ "}}" }}`. That way your generated config file contains the `{{` and `}}` you expect. ## Use VSCode as the diff tool To use [VSCode][vscode] as the diff tool, add the following to your config: === "TOML" ```toml title="~/.config/chezmoi/chezmoi.toml" [diff] command = "code" args = ["--wait", "--diff"] ``` === "YAML" ```yaml title="~/.config/chezmoi/chezmoi.yaml" diff: command: "code" args: - "--wait" - "--diff" ``` ## Use delta as the diff tool To use [delta][delta] as the diff tool you must set `diff.pager` to delta, for example: === "TOML" ```toml title="~/.config/chezmoi/chezmoi.toml" [diff] pager = "delta" ``` === "YAML" ```yaml title="~/.config/chezmoi/chezmoi.yaml" diff: pager: "delta" ``` ## Don't show scripts in the diff output By default, `chezmoi diff` will show all changes, including the contents of scripts that will be run. You can exclude scripts from the diff output by setting the `diff.exclude` configuration variable in your configuration file, for example: ```toml title="~/.config/chezmoi/chezmoi.toml" [diff] exclude = ["scripts"] ``` ## Don't show externals in the diff output To exclude diffs from externals, either pass the `--exclude=externals` flag or set `diff.exclude` to `["externals"]` in your config file. ## Customize the diff pager You can change the diff format, and/or pipe the output into a pager of your choice by setting `diff.pager` configuration variable. For example, to use [`diff-so-fancy`][fancy] specify: ```toml title="~/.config/chezmoi/chezmoi.toml" [diff] pager = "diff-so-fancy" ``` The pager can be disabled using the `--no-pager` flag or by setting `diff.pager` to an empty string. ## Show human-friendly diffs for binary files Similar to git, chezmoi includes a "textconv" feature that can transform file contents before passing them to the diff program. This is primarily useful for generating human-readable diffs of binary files. For example, to show diffs of macOS `.plist` files, add the following to your configuration file: === "JSON" ```json title="~/.config/chezmoi/chezmoi.json" { "textconv": [ "pattern": "**/*.plist", "command": "plutil", "args": [ "-convert", "xml1", "-o", "-", "-" ] ] } ``` === "TOML" ```toml title="~/.config/chezmoi/chezmoi.toml" [[textconv]] pattern = "**/*.plist" command = "plutil" args = ["-convert", "xml1", "-o", "-", "-"] ``` === "YAML" ```yaml title="~/.config/chezmoi/chezmoi.yaml" textconv: - pattern: "**/*.plist" command: "plutil" args: - "-convert" - "xml1" - "-o" - "-", - "-" ``` This will pipe all `.plist` files through `plutil -convert xml1 -o - -` before showing differences. [meld]: https://meldmerge.org/ [vscode]: https://code.visualstudio.com/ [delta]: https://dandavison.github.io/delta/ [fancy]: https://github.com/so-fancy/diff-so-fancy ================================================ FILE: assets/chezmoi.io/docs/user-guide/tools/editor.md ================================================ # Editor ## Use your preferred editor with `chezmoi edit` and `chezmoi edit-config` By default, chezmoi will use your preferred editor as defined by the `$VISUAL` or `$EDITOR` environment variables, falling back to a default editor depending on your operating system (`vi` on UNIX-like operating systems, `notepad.exe` on Windows). You can configure chezmoi to use your preferred editor by either setting the `$EDITOR` environment variable or setting the `edit.command` variable in your configuration file. The editor command must only return when you have finished editing the files. chezmoi will emit a warning if your editor command returns too quickly. In the specific case of using [VSCode][vscode] or [Codium][codium] as your editor, you must pass the `--wait` flag, for example, in your shell config: ```bash export EDITOR="code --wait" ``` Or in chezmoi's configuration file: ```toml title="~/.config/chezmoi/chezmoi.toml" [edit] command = "code" args = ["--wait"] ``` !!! warning If you use [Helix][helix], you must use Helix 25.01 or later. ## Use chezmoi with VIM [`github.com/alker0/chezmoi.vim`][alker0] provides syntax highlighting for files managed by chezmoi, including for templates. [`github.com/Lilja/vim-chezmoi`][lilja] works with `chezmoi edit` to apply the edited dotfile on save. [`github.com/xvzc/chezmoi.nvim`][xvzc] allows you to edit your chezmoi-managed files and automatically apply. Alternatively, you can use an `autocmd` to run `chezmoi apply` whenever you save a dotfile, but you must disable `chezmoi edit`'s hardlinking: ```toml title="~/.config/chezmoi/chezmoi.toml" [edit] hardlink = false ``` ```vim title="~/.vimrc" autocmd BufWritePost ~/.local/share/chezmoi/* ! chezmoi apply --source-path "%" ``` ## Use chezmoi with emacs [`github.com/tuh8888/chezmoi.el`][tuh8888] provides convenience functions for interacting with chezmoi from Emacs, and is available in [MELPA][melpa]. [vscode]: https://code.visualstudio.com/ [codium]: https://vscodium.com/ [alker0]: https://github.com/alker0/chezmoi.vim [lilja]: https://github.com/Lilja/vim-chezmoi [xvzc]: https://github.com/xvzc/chezmoi.nvim [tuh8888]: https://github.com/tuh8888/chezmoi.el [melpa]: https://melpa.org/#/chezmoi [helix]: https://helix-editor.com/ ================================================ FILE: assets/chezmoi.io/docs/user-guide/tools/http-or-socks5-proxy.md ================================================ # HTTP or SOCKS5 proxy chezmoi supports HTTP, HTTPS, and SOCKS5 proxies. Set the `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` environment variables, or their lowercase equivalents, for example: ```sh HTTP_PROXY=socks5://127.0.0.1:1080 chezmoi apply --refresh-externals ``` ================================================ FILE: assets/chezmoi.io/docs/user-guide/tools/merge.md ================================================ # Merge ## Use a custom merge command By default, chezmoi uses `vimdiff`. You can use a custom command by setting the `merge.command` and `merge.args` configuration variables. The elements of `merge.args` are interpreted as templates with the variables `.Destination`, `.Source`, and `.Target` containing filenames of the file in the destination state, source state, and target state respectively. For example, to use [neovim's diff mode][nvim], specify: ```toml title="~/.config/chezmoi/chezmoi.toml" [merge] command = "nvim" args = ["-d", "{{ .Destination }}", "{{ .Source }}", "{{ .Target }}"] ``` !!! hint If you generate your config file from a config file template, then you'll need to escape the `{{` and `}}`. That way your generated config file contains the `{{` and `}}` you expect. ```toml title="~/.local/share/chezmoi/chezmoi.toml.tmpl" [merge] command = "nvim" args = [ "-d", {{ printf "%q" "{{ .Destination }}" }}, {{ printf "%q" "{{ .Source }}" }}, {{ printf "%q" "{{ .Target }}" }}, ] ``` ## Use Beyond Compare as the merge tool To use [Beyond Compare][bcomp] as the merge tool, add the following to your config: === "TOML" ```toml title="~/.config/chezmoi/chezmoi.toml" [merge] command = "bcomp" args = ["{{ .Destination }}", "{{ .Source }}", "{{ .Target }}", "{{ .Source }}"] ``` === "YAML" ```yaml title="~/.config/chezmoi/chezmoi.yaml" merge: command: "bcomp" args: - "{{ .Destination }}" - "{{ .Source }}" - "{{ .Target }}" - "{{ .Source }}" ``` ## Use VSCode as the merge tool To use [VSCode][vscode] as the merge tool, add the following to your config: === "TOML" ```toml title="~/.config/chezmoi/chezmoi.toml" [merge] command = "bash" args = [ "-c", "cp {{ .Target | quote }} {{ printf \"%s.base\" .Target | quote }} && code --new-window --wait --merge {{ .Destination | quote }} {{ .Target | quote }} {{ printf \"%s.base\" .Target | quote }} {{ .Source | quote }}", ] ``` === "YAML" ```yaml title="~/.config/chezmoi/chezmoi.yaml" merge: command: "bash" args: - "-c" - "cp {{ .Target | quote }} {{ printf \"%s.base\" .Target | quote }} && code --new-window --wait --merge {{ .Destination | quote }} {{ .Target | quote }} {{ printf \"%s.base\" .Target | quote }} {{ .Source | quote }}" ``` [bcomp]: https://www.scootersoftware.com/ [nvim]: https://neovim.io/doc/user/diff.html [vscode]: https://code.visualstudio.com/ ================================================ FILE: assets/chezmoi.io/docs/user-guide/use-scripts-to-perform-actions.md ================================================ # Use scripts to perform actions ## Understand how scripts work chezmoi supports scripts that are executed when you run [`chezmoi apply`][apply]. These scripts can be configured to run every time, only when their contents have changed, or only if they haven't been run before. Scripts are any file in the source directory with the prefix `run_`, and they are executed in alphabetical order. - **`run_` scripts**: These scripts are executed every time you run `chezmoi apply`. - **`run_onchange_` scripts**: These scripts are only executed if their content has changed since the last time they were run successfully. - **`run_once_` scripts**: These scripts are executed once for each unique version of the content. If the script is a template, the content is hashed after template execution. chezmoi tracks the content's SHA256 hash and stores it in a database. If the content has been run successfully before (even under a different filename), the script will not run again unless the content itself changes. Scripts break chezmoi's declarative approach and should be used sparingly. All scripts should be idempotent, including `run_onchange_` and `run_once_` scripts. Scripts are normally run while chezmoi updates your dotfiles. For example, `run_b.sh` will be run after updating `a.txt` and before updating `c.txt`. To run scripts before or after the updates, use the `before_` or `after_` attributes, respectively, e.g., `run_once_before_install-password-manager.sh`. Scripts must be created manually in the source directory, typically by running [`chezmoi cd`][cd] and then creating a file with a `run_` prefix. There is no need to set the executable bit on the script. Scripts with the `.tmpl` suffix are treated as templates, with the usual template variables available. If the template resolves to only whitespace or an empty string, the script will not be executed, which is useful for disabling scripts dynamically. When executing a script, chezmoi generates the script contents in a file in a temporary directory with the executable bit set and then executes it using `exec(3)`. As a result, the script must either include a `#!` line or be an executable binary. Script working directory is set to the first existing parent directory in the destination tree. If a `.chezmoiscripts` directory exists at the root of the source directory, scripts in this directory are executed as normal scripts, without creating a corresponding directory in the target state. In verbose mode, the scripts' contents are printed before execution. In dry-run mode, scripts are not executed. ## Set environment variables You can set extra environment variables for your scripts, hooks, and commands in the `scriptEnv` section of your config file. For example, to set the `MY_VAR` environment variable to `my_value`, specify: ```toml title="~/.config/chezmoi/chezmoi.toml" [scriptEnv] MY_VAR = "my_value" ``` chezmoi sets a number of environment variables when running scripts, including `CHEZMOI=1` and common template data like `CHEZMOI_OS` and `CHEZMOI_ARCH`. ## Don't show scripts that would run in `chezmoi diff`/`chezmoi status` By default, `chezmoi diff` will print the contents of scripts that would be run by `chezmoi apply`. To exclude scripts from the output of `chezmoi diff`, set `diff.exclude` in your configuration file, for example: ```toml title="~/.config/chezmoi/chezmoi.toml" [diff] exclude = ["scripts"] ``` Similarly, `chezmoi status` will print the names of the scripts that it will execute with the status `R`. This can similarly disabled by setting `status.exclude` to `["scripts"]` in your configuration file. ## Install packages with scripts Change to the source directory and create a file called `run_onchange_install-packages.sh`. In this file create your package installation script, e.g. ```sh #!/bin/sh sudo apt install ripgrep ``` The next time you run [`chezmoi apply`][apply] or [`chezmoi update`][update] this script will be run. As it has the `run_onchange_` prefix, it will not be run again unless its contents change, for example if you add more packages to be installed. This script can also be a template. For example, if you create `run_onchange_install-packages.sh.tmpl` with the contents: ``` title="~/.local/share/chezmoi/run_onchange_install-packages.sh.tmpl" {{ if eq .chezmoi.os "linux" -}} #!/bin/sh sudo apt install ripgrep {{ else if eq .chezmoi.os "darwin" -}} #!/bin/sh brew install ripgrep {{ end -}} ``` This will install `ripgrep` on both Debian/Ubuntu Linux systems and macOS. ## Run a script when the contents of another file changes chezmoi's `run_` scripts are run every time you run [`chezmoi apply`][apply], whereas `run_onchange_` scripts are run only when their contents have changed, after executing them as templates. You can use this to cause a `run_onchange_` script to run when the contents of another file has changed by including a checksum of the other file's contents in the script. For example, if your [dconf][dconf] settings are stored in `dconf.ini` in your source directory then you can make `chezmoi apply` only load them when the contents of `dconf.ini` has changed by adding the following script as `run_onchange_dconf-load.sh.tmpl`: ``` title="~/.local/share/chezmoi/run_onchange_dconf-load.sh.tmpl" #!/bin/bash # dconf.ini hash: {{ include "dconf.ini" | sha256sum }} dconf load / < {{ joinPath .chezmoi.sourceDir "dconf.ini" | quote }} ``` As the SHA256 sum of `dconf.ini` is included in a comment in the script, the contents of the script will change whenever the contents of `dconf.ini` are changed, so chezmoi will re-run the script whenever the contents of `dconf.ini` change. In this example you should also add `dconf.ini` to [`.chezmoiignore`][ignore] so chezmoi does not create `dconf.ini` in your home directory. ## Clear the state of all `run_onchange_` and `run_once_` scripts chezmoi stores whether and when `run_onchange_` and `run_once_` scripts have been run successfully in its persistent state. To clear the state of `run_onchange_` scripts, run: ```sh chezmoi state delete-bucket --bucket=entryState ``` To clear the state of `run_once_` scripts, run: ```sh chezmoi state delete-bucket --bucket=scriptState ``` [apply]: /reference/commands/apply.md [cd]: /reference/commands/cd.md [update]: /reference/commands/update.md [dconf]: https://wiki.gnome.org/Projects/dconf [ignore]: /reference/special-files/chezmoiignore.md ================================================ FILE: assets/chezmoi.io/docs/what-does-chezmoi-do.md ================================================ # What does chezmoi do? chezmoi helps you manage your personal configuration files (dotfiles, like `~/.gitconfig`) across multiple machines. chezmoi is helpful if you have spent time customizing the tools you use (e.g. shells, editors, and version control systems) and want to keep machines running different accounts (e.g. home and work) and/or different operating systems (e.g. Linux, macOS, and Windows) in sync, while still being able to easily cope with differences from machine to machine. chezmoi scales from the trivial (e.g. copying a few dotfiles onto a Raspberry Pi, development container, or virtual machine) to complex long-lived multi-machine development environments (e.g. keeping any number of home and work, Linux, macOS, and Windows machines in sync). In all cases you only need to maintain a single source of truth (a single branch in git) and getting started only requires adding a single binary to your machine (which you can do with `curl`, `wget`, or `scp`). chezmoi has strong support for security, allowing you to manage secrets (e.g. passwords, access tokens, and private keys) securely and seamlessly using a password manager and/or encrypt whole files with your favorite encryption tool. If you do not personalize your configuration or only ever use a single operating system with a single account and none of your dotfiles contain secrets then you don't need chezmoi. Otherwise, read on... ## What are chezmoi's key features? ### Flexible You can share as much configuration across machines as you want, while still being able to control machine-specific details.Your dotfiles can be templates (using Go [`text/template`][template] syntax). Predefined variables allow you to change behavior depending on operating system, architecture, and hostname. chezmoi runs on all commonly-used platforms, like Linux, macOS, and Windows. It also runs on less commonly-used platforms, like FreeBSD, OpenBSD, and Termux. ### Personal and secure Nothing leaves your machine, unless you want it to. Your configuration remains in a git repo under your control. You can write the configuration file in the format of your choice. chezmoi can retrieve secrets from [1Password][1p], [AWS Secrets Manager][aws-secrets-manager], [Azure Key Vault][azure-key-vault], [Bitwarden][bitwarden], [Dashlane][dashlane], [Doppler][doppler], [gopass][gopass], [KeePassXC][keepass], [Keeper][keeper], [LastPass][lastpass], [pass][pass], [passage][passage], [passhole][passhole], [Proton Pass][protonpass], [Vault][vault], macOS Keychain, GNOME [Keyring][keyring], or any command-line utility of your choice. You can encrypt individual files with [GnuPG][gnupg] or [age][age]. You can checkout your dotfiles repo on as many machines as you want without revealing any secrets to anyone. ### Transparent chezmoi includes verbose and dry run modes so you can review exactly what changes it will make to your home directory before making them. chezmoi's source format uses only regular files and directories that map one-to-one with the files, directories, and symlinks in your home directory that you choose to manage. If you decide not to use chezmoi in the future, it is easy to move your data elsewhere. ### Declarative and robust You declare the desired state of files, directories, and symbolic links in your source of truth and chezmoi updates your home directory to match that state. What you want is what you get. chezmoi updates all files and symbolic links atomically. You will never be left with incomplete files that could lock you out, even if the update process is interrupted. ### Fast and easy to use Using chezmoi feels like using git: the commands are similar and chezmoi runs in fractions of a second. chezmoi makes most day-to-day operations one line commands, including installation, initialization, and keeping your machines up-to-date. chezmoi can pull and apply changes from your dotfiles repo in a single command, and automatically commit and push changes. [1p]: https://1password.com/ [age]: https://age-encryption.org [aws-secrets-manager]: https://aws.amazon.com/secrets-manager/ [azure-key-vault]: https://learn.microsoft.com/en-us/azure/key-vault/general/ [bitwarden]: https://bitwarden.com/ [dashlane]: https://www.dashlane.com/ [doppler]: https://www.doppler.com [gnupg]: https://www.gnupg.org [gopass]: https://www.gopass.pw/ [keepass]: https://keepassxc.org/ [keeper]: https://www.keepersecurity.com/ [keyring]: https://wiki.gnome.org/Projects/GnomeKeyring [lastpass]: https://lastpass.com/ [pass]: https://www.passwordstore.org/ [passage]: https://github.com/FiloSottile/passage [passhole]: https://github.com/Evidlo/passhole [protonpass]: https://proton.me/pass [template]: https://pkg.go.dev/text/template [vault]: https://www.vaultproject.io/ ================================================ FILE: assets/chezmoi.io/docs/why-use-chezmoi.md ================================================ # Why use chezmoi? ## Why should I use a dotfile manager? Dotfile managers give you the combined benefit of a consistent environment everywhere with an undo command and a restore from backup. As the core of our development environments become increasingly standardized (e.g. using git at both home and work), and we further customize them, at the same time we increasingly work in ephemeral environments like Docker containers, virtual machines, and GitHub Codespaces. In the same way that nobody would use an editor without an undo command, or develop software without a version control system, chezmoi brings the investment that you have made in mastering your tools to every environment that you work in. ## I already have a system to manage my dotfiles, why should I use chezmoi? !!! quote I’ve been using Chezmoi for more than a year now, across at least 3 computers simultaneously, and I really love it. Most of all, I love how fast I can configure a new machine when I use it. In just a couple minutes of work, I can kick off a process on a brand-new computer that will set up my dotfiles and install all my usual software so it feels like a computer I’ve been using for years. I also appreciate features like secrets management, which allow me to share my dotfiles while keeping my secrets safe. Overall, I love the way Chezmoi fits so perfectly into the niche of managing dotfiles. — [@mike_kasberg][kasberg] !!! quote I had initially been turned off when I first encountered [chezmoi], because [chezmoi] seemed overkill for (what appeared to me) a simple task. But the problem of managing a relatively small number of dotfiles across a relatively small number of machines with small differences between them and keeping them up to date proved to be _MUCH_ more complex than I imagined. Copy things around by hand, and then later distributing them via source control got hairy very quickly. I finally realized all those features were absolutely necessary to manage things sanely, and once I took some time to learn how to do things with chezmoi, I have never looked back. — [njt][njt] !!! quote Regular reminder that chezmoi is the best dotfile manager utility I've used and you can too — @mbbroberg If you're using any of the following methods: * A custom shell script. * An existing dotfile manager like [dotbot][dotbot], [rcm][rcm], [homesick][homesick], [vcsh][vcsh], [yadm][yadm], or [GNU Stow][stow]. * A [bare git repo][baregit]. Then you've probably run into at least one of the following problems. ### ...if coping with differences between machines requires extra effort If you want to synchronize your dotfiles across multiple operating systems or distributions, then you may need to manually perform extra steps to cope with differences from machine to machine. You might need to run different commands on different machines, maintain separate per-machine files or branches (with the associated hassle of merging, rebasing, or copying each change), or hope that your custom logic handles the differences correctly. chezmoi uses a single source of truth (a single branch) and a single command that works on every machine. Individual files can be templates to handle machine to machine differences, if needed. ### ...if you have to keep your dotfiles repo private !!! quote And regarding dotfiles, I saw that. It's only public dotfiles repos so I have to evaluate my dotfiles history to be sure. I have secrets scanning and more, but it was easier to keep it private for security, I'm ok mostly though. I'm using chezmoi and it's easier now — @sheldon_hull If your system stores secrets in plain text, then you must be very careful about where you clone your dotfiles. If you clone them on your work machine then anyone with access to your work machine (e.g. your IT department) will have access to your home secrets. If you clone it on your home machine then you risk leaking work secrets. With chezmoi you can store secrets in your password manager or encrypt them, and even store passwords in different ways on different machines. You can clone your dotfiles repository anywhere, and even make your dotfiles repo public, without leaving personal secrets on your work machine or work secrets on your personal machine. ### ...if you have to maintain your own tool !!! quote I've offloaded my dotfiles deployment from a homespun shell script to chezmoi. I'm quite happy with this decision. — @gotgenes !!! quote I discovered chezmoi and it's pretty cool, just migrated my old custom multi-machine sync dotfile setup and it's so much simpler now in case you're wondering I have written 0 code — @buritica !!! quote Chezmoi is like what you might get if you re-wrote my bash script in Go, came up with better solutions than `diff` for managing config on multiple machines, added in secrets management and other useful dotfile tools, and tweaked and perfected it over years. - [@mike_kasberg][kasberg] If your system was written by you for your personal use, then it probably has the functionality that you needed when you wrote it. If you need more functionality then you have to implement it yourself. chezmoi includes a huge range of battle-tested functionality out-of-the-box, including dry-run and diff modes, script execution, conflict resolution, Windows support, and much, much more. chezmoi is [used by thousands of people][stars] and has a rich suite of both unit and integration tests. When you hit the limits of your existing dotfile management system, chezmoi already has a tried-and-tested solution ready for you to use. ### ...if setting up your dotfiles requires more than one short command If your system is written in a scripting language like Python, Perl, or Ruby, then you also need to install a compatible version of that language's runtime before you can use your system. chezmoi is distributed as a single stand-alone statically-linked binary with no dependencies that you can simply copy onto your machine and run. You don't even need git installed. chezmoi provides one-line installs, pre-built binaries, packages for Linux and BSD distributions, Homebrew formulae, Scoop and Chocolatey support on Windows, and a initial config file generation mechanism to make installing your dotfiles on a new machine as painless as possible. [baregit]: https://www.atlassian.com/git/tutorials/dotfiles [dotbot]: https://github.com/anishathalye/dotbot [homesick]: https://github.com/technicalpickles/homesick [kasberg]: https://www.mikekasberg.com/blog/2021/05/12/my-dotfiles-story.html [njt]: https://news.ycombinator.com/item?id=31015669 [rcm]: https://github.com/thoughtbot/rcm [stars]: https://github.com/twpayne/chezmoi/stargazers [stow]: https://www.gnu.org/software/stow/ [vcsh]: https://github.com/RichiH/vcsh [yadm]: https://yadm.io/ ================================================ FILE: assets/chezmoi.io/mkdocs.yml ================================================ site_name: chezmoi site_url: https://chezmoi.io site_description: Manage your dotfiles across multiple machines, securely. site_author: Tom Payne copyright: Copyright © Tom Payne 2018 repo_name: twpayne/chezmoi repo_url: https://github.com/twpayne/chezmoi edit_uri: edit/master/assets/chezmoi.io/docs/ strict: true validation: omitted_files: warn absolute_links: relative_to_docs unrecognized_links: warn anchors: warn theme: name: material logo: logo.svg language: en palette: - media: '(prefers-color-scheme: light)' scheme: default primary: indigo accent: indigo toggle: icon: material/toggle-switch name: Switch to dark mode - media: '(prefers-color-scheme: dark)' scheme: slate primary: indigo accent: indigo toggle: icon: material/toggle-switch-off-outline name: Switch to light mode features: - navigation.expand - navigation.indexes - navigation.sections - navigation.tabs - navigation.top - navigation.tracking nav: - Home: - index.md - Install: install.md - Quick start: quick-start.md - What does chezmoi do?: what-does-chezmoi-do.md - Why use chezmoi?: why-use-chezmoi.md - Comparison table: comparison-table.md - Migrating from another dotfile manager: migrating-from-another-dotfile-manager.md - User guide: - Command overview: user-guide/command-overview.md - Setup: user-guide/setup.md - Daily operations: user-guide/daily-operations.md - Manage different types of file: user-guide/manage-different-types-of-file.md - Include files from elsewhere: user-guide/include-files-from-elsewhere.md - Manage machine-to-machine differences: user-guide/manage-machine-to-machine-differences.md - Use scripts to perform actions: user-guide/use-scripts-to-perform-actions.md - Templating: user-guide/templating.md - Tools: - Editor: user-guide/tools/editor.md - Diff: user-guide/tools/diff.md - Merge: user-guide/tools/merge.md - HTTP or SOCKS5 proxy: user-guide/tools/http-or-socks5-proxy.md - Password managers: - user-guide/password-managers/index.md - 1Password: user-guide/password-managers/1password.md - AWS Secrets Manager: user-guide/password-managers/aws-secrets-manager.md - Azure Key Vault: user-guide/password-managers/azure-key-vault.md - Bitwarden: user-guide/password-managers/bitwarden.md - Dashlane: user-guide/password-managers/dashlane.md - Doppler: user-guide/password-managers/doppler.md - ejson: user-guide/password-managers/ejson.md - gopass: user-guide/password-managers/gopass.md - KeePassXC: user-guide/password-managers/keepassxc.md - Keychain and Windows Credentials Manager: user-guide/password-managers/keychain-and-windows-credentials-manager.md - Keeper: user-guide/password-managers/keeper.md - LastPass: user-guide/password-managers/lastpass.md - pass: user-guide/password-managers/pass.md - passhole: user-guide/password-managers/passhole.md - Proton Pass: user-guide/password-managers/proton-pass.md - Vault: user-guide/password-managers/vault.md - Custom: user-guide/password-managers/custom.md - Encryption: - user-guide/encryption/index.md - age: user-guide/encryption/age.md - gpg: user-guide/encryption/gpg.md - rage: user-guide/encryption/rage.md - Transparent: user-guide/encryption/transparent.md - Machines: - General: user-guide/machines/general.md - Linux: user-guide/machines/linux.md - macOS: user-guide/machines/macos.md - Windows: user-guide/machines/windows.md - Containers and VMs: user-guide/machines/containers-and-vms.md - Advanced: - Customize your source directory: user-guide/advanced/customize-your-source-directory.md - Install packages declaratively: user-guide/advanced/install-packages-declaratively.md - Install your password manager on init: user-guide/advanced/install-your-password-manager-on-init.md - Use chezmoi with Watchman: user-guide/advanced/use-chezmoi-with-watchman.md - Migrate away from chezmoi: user-guide/advanced/migrate-away-from-chezmoi.md - Frequently asked questions: - Usage: user-guide/frequently-asked-questions/usage.md - Encryption: user-guide/frequently-asked-questions/encryption.md - Troubleshooting: user-guide/frequently-asked-questions/troubleshooting.md - Design: user-guide/frequently-asked-questions/design.md - General: user-guide/frequently-asked-questions/general.md - Reference: - reference/index.md - Concepts: reference/concepts.md - Source state attributes: reference/source-state-attributes.md - Target types: reference/target-types.md - Application order: reference/application-order.md - Configuration file: - reference/configuration-file/index.md - Variables: reference/configuration-file/variables.md - Editor: reference/configuration-file/editor.md - Hooks: reference/configuration-file/hooks.md - Interpreters: reference/configuration-file/interpreters.md - pinentry: reference/configuration-file/pinentry.md - textconv: reference/configuration-file/textconv.md - umask: reference/configuration-file/umask.md - Warnings: reference/configuration-file/warnings.md - Special files: - reference/special-files/index.md - .chezmoi.<format>.tmpl: reference/special-files/chezmoi-format-tmpl.md - .chezmoidata.<format>: reference/special-files/chezmoidata-format.md - .chezmoiexternal.<format>: reference/special-files/chezmoiexternal-format.md - .chezmoiignore: reference/special-files/chezmoiignore.md - .chezmoiremove: reference/special-files/chezmoiremove.md - .chezmoiroot: reference/special-files/chezmoiroot.md - .chezmoiversion: reference/special-files/chezmoiversion.md - Special directories: - reference/special-directories/index.md - .chezmoidata/: reference/special-directories/chezmoidata.md - .chezmoiexternals/: reference/special-directories/chezmoiexternals.md - .chezmoiscripts/: reference/special-directories/chezmoiscripts.md - .chezmoitemplates/: reference/special-directories/chezmoitemplates.md - Command line flags: - reference/command-line-flags/index.md - Global: reference/command-line-flags/global.md - Common: reference/command-line-flags/common.md - Developer: reference/command-line-flags/developer.md - Commands: - reference/commands/index.md - add: reference/commands/add.md - age: reference/commands/age.md - age-keygen: reference/commands/age-keygen.md - apply: reference/commands/apply.md - archive: reference/commands/archive.md - cat: reference/commands/cat.md - cat-config: reference/commands/cat-config.md - cd: reference/commands/cd.md - chattr: reference/commands/chattr.md - completion: reference/commands/completion.md - data: reference/commands/data.md - decrypt: reference/commands/decrypt.md - destroy: reference/commands/destroy.md - diff: reference/commands/diff.md - docker: reference/commands/docker.md - doctor: reference/commands/doctor.md - dump: reference/commands/dump.md - dump-config: reference/commands/dump-config.md - edit: reference/commands/edit.md - edit-config: reference/commands/edit-config.md - edit-config-template: reference/commands/edit-config-template.md - edit-encrypted: reference/commands/edit-encrypted.md - encrypt: reference/commands/encrypt.md - execute-template: reference/commands/execute-template.md - forget: reference/commands/forget.md - generate: reference/commands/generate.md - git: reference/commands/git.md - help: reference/commands/help.md - ignored: reference/commands/ignored.md - import: reference/commands/import.md - init: reference/commands/init.md - license: reference/commands/license.md - list: reference/commands/list.md - manage: reference/commands/manage.md - managed: reference/commands/managed.md - merge: reference/commands/merge.md - merge-all: reference/commands/merge-all.md - podman: reference/commands/podman.md - purge: reference/commands/purge.md - re-add: reference/commands/re-add.md - remove: reference/commands/remove.md - rm: reference/commands/rm.md - secret: reference/commands/secret.md - source-path: reference/commands/source-path.md - ssh: reference/commands/ssh.md - state: reference/commands/state.md - status: reference/commands/status.md - target-path: reference/commands/target-path.md - unmanage: reference/commands/unmanage.md - unmanaged: reference/commands/unmanaged.md - update: reference/commands/update.md - upgrade: reference/commands/upgrade.md - verify: reference/commands/verify.md - Templates: - reference/templates/index.md - Variables: reference/templates/variables.md - Directives: reference/templates/directives.md - Functions: - reference/templates/functions/index.md - abortEmpty: reference/templates/functions/abortEmpty.md - comment: reference/templates/functions/comment.md - completion: reference/templates/functions/completion.md - decrypt: reference/templates/functions/decrypt.md - deleteValueAtPath: reference/templates/functions/deleteValueAtPath.md - encrypt: reference/templates/functions/encrypt.md - ensureLinePrefix: reference/templates/functions/ensureLinePrefix.md - eqFold: reference/templates/functions/eqFold.md - exec: reference/templates/functions/exec.md - findExecutable: reference/templates/functions/findExecutable.md - findOneExecutable: reference/templates/functions/findOneExecutable.md - fromIni: reference/templates/functions/fromIni.md - fromJson: reference/templates/functions/fromJson.md - fromJsonc: reference/templates/functions/fromJsonc.md - fromToml: reference/templates/functions/fromToml.md - fromYaml: reference/templates/functions/fromYaml.md - getRedirectedURL: reference/templates/functions/getRedirectedURL.md - glob: reference/templates/functions/glob.md - hexDecode: reference/templates/functions/hexDecode.md - hexEncode: reference/templates/functions/hexEncode.md - include: reference/templates/functions/include.md - includeTemplate: reference/templates/functions/includeTemplate.md - ioreg: reference/templates/functions/ioreg.md - isExecutable: reference/templates/functions/isExecutable.md - joinPath: reference/templates/functions/joinPath.md - jq: reference/templates/functions/jq.md - lookPath: reference/templates/functions/lookPath.md - lstat: reference/templates/functions/lstat.md - mozillaInstallHash: reference/templates/functions/mozillaInstallHash.md - output: reference/templates/functions/output.md - outputList: reference/templates/functions/outputList.md - pruneEmptyDicts: reference/templates/functions/pruneEmptyDicts.md - quoteList: reference/templates/functions/quoteList.md - replaceAllRegex: reference/templates/functions/replaceAllRegex.md - setValueAtPath: reference/templates/functions/setValueAtPath.md - stat: reference/templates/functions/stat.md - toIni: reference/templates/functions/toIni.md - toPrettyJson: reference/templates/functions/toPrettyJson.md - toString: reference/templates/functions/toString.md - toStrings: reference/templates/functions/toStrings.md - toToml: reference/templates/functions/toToml.md - toYaml: reference/templates/functions/toYaml.md - warnf: reference/templates/functions/warnf.md - GitHub functions: - reference/templates/github-functions/index.md - gitHubKeys: reference/templates/github-functions/gitHubKeys.md - gitHubLatestRelease: reference/templates/github-functions/gitHubLatestRelease.md - gitHubRelease: reference/templates/github-functions/gitHubRelease.md - gitHubLatestReleaseAssetURL: reference/templates/github-functions/gitHubLatestReleaseAssetURL.md - gitHubReleaseAssetURL: reference/templates/github-functions/gitHubReleaseAssetURL.md - gitHubLatestTag: reference/templates/github-functions/gitHubLatestTag.md - gitHubReleases: reference/templates/github-functions/gitHubReleases.md - gitHubTags: reference/templates/github-functions/gitHubTags.md - Init functions: - reference/templates/init-functions/index.md - exit: reference/templates/init-functions/exit.md - promptBool: reference/templates/init-functions/promptBool.md - promptBoolOnce: reference/templates/init-functions/promptBoolOnce.md - promptChoice: reference/templates/init-functions/promptChoice.md - promptChoiceOnce: reference/templates/init-functions/promptChoiceOnce.md - promptInt: reference/templates/init-functions/promptInt.md - promptIntOnce: reference/templates/init-functions/promptIntOnce.md - promptMultichoice: reference/templates/init-functions/promptMultichoice.md - promptMultichoiceOnce: reference/templates/init-functions/promptMultichoiceOnce.md - promptString: reference/templates/init-functions/promptString.md - promptStringOnce: reference/templates/init-functions/promptStringOnce.md - stdinIsATTY: reference/templates/init-functions/stdinIsATTY.md - writeToStdout: reference/templates/init-functions/writeToStdout.md - 1Password functions: - reference/templates/1password-functions/index.md - onepassword: reference/templates/1password-functions/onepassword.md - onepasswordDocument: reference/templates/1password-functions/onepasswordDocument.md - onepasswordDetailsFields: reference/templates/1password-functions/onepasswordDetailsFields.md - onepasswordItemFields: reference/templates/1password-functions/onepasswordItemFields.md - onepasswordRead: reference/templates/1password-functions/onepasswordRead.md - AWS Secrets Manager functions: - reference/templates/aws-secrets-manager-functions/index.md - awsSecretsManager: reference/templates/aws-secrets-manager-functions/awsSecretsManager.md - awsSecretsManagerRaw: reference/templates/aws-secrets-manager-functions/awsSecretsManagerRaw.md - Azure Key Vault functions: - azureKeyVault: reference/templates/azure-key-vault-functions/azureKeyVault.md - Bitwarden functions: - reference/templates/bitwarden-functions/index.md - bitwarden: reference/templates/bitwarden-functions/bitwarden.md - bitwardenAttachment: reference/templates/bitwarden-functions/bitwardenAttachment.md - bitwardenAttachmentByRef: reference/templates/bitwarden-functions/bitwardenAttachmentByRef.md - bitwardenFields: reference/templates/bitwarden-functions/bitwardenFields.md - bitwardenSecrets: reference/templates/bitwarden-functions/bitwardenSecrets.md - rbw: reference/templates/bitwarden-functions/rbw.md - rbwFields: reference/templates/bitwarden-functions/rbwFields.md - Dashlane functions: - reference/templates/dashlane-functions/index.md - dashlaneNote: reference/templates/dashlane-functions/dashlaneNote.md - dashlanePassword: reference/templates/dashlane-functions/dashlanePassword.md - Doppler functions: - reference/templates/doppler-functions/index.md - doppler: reference/templates/doppler-functions/doppler.md - dopplerProjectJson: reference/templates/doppler-functions/dopplerProjectJson.md - ejson functions: - reference/templates/ejson-functions/index.md - ejsonDecrypt: reference/templates/ejson-functions/ejsonDecrypt.md - ejsonDecryptWithKey: reference/templates/ejson-functions/ejsonDecryptWithKey.md - gopass functions: - reference/templates/gopass-functions/index.md - gopass: reference/templates/gopass-functions/gopass.md - gopassRaw: reference/templates/gopass-functions/gopassRaw.md - KeePassXC functions: - reference/templates/keepassxc-functions/index.md - keepassxc: reference/templates/keepassxc-functions/keepassxc.md - keepassxcAttachment: reference/templates/keepassxc-functions/keepassxcAttachment.md - keepassxcAttribute: reference/templates/keepassxc-functions/keepassxcAttribute.md - Keeper functions: - reference/templates/keeper-functions/index.md - keeper: reference/templates/keeper-functions/keeper.md - keeperDataFields: reference/templates/keeper-functions/keeperDataFields.md - keeperFindPassword: reference/templates/keeper-functions/keeperFindPassword.md - Keyring functions: - keyring: reference/templates/keyring-functions/keyring.md - LastPass functions: - reference/templates/lastpass-functions/index.md - lastpass: reference/templates/lastpass-functions/lastpass.md - lastpassRaw: reference/templates/lastpass-functions/lastpassRaw.md - pass functions: - reference/templates/pass-functions/index.md - pass: reference/templates/pass-functions/pass.md - passFields: reference/templates/pass-functions/passFields.md - passRaw: reference/templates/pass-functions/passRaw.md - Passhole functions: - reference/templates/passhole-functions/index.md - passhole: reference/templates/passhole-functions/passhole.md - Proton Pass functions: - reference/templates/protonpass-functions/index.md - protonPass: reference/templates/protonpass-functions/protonPass.md - protonPassJSON: reference/templates/protonpass-functions/protonPassJSON.md - Vault functions: - vault: reference/templates/vault-functions/vault.md - Generic secret functions: - reference/templates/secret-functions/index.md - secret: reference/templates/secret-functions/secret.md - secretJSON: reference/templates/secret-functions/secretJSON.md - Plugins: reference/plugins.md - Release history: reference/release-history.md - Developer guide: - developer-guide/index.md - Testing: developer-guide/testing.md - Contributing changes: developer-guide/contributing-changes.md - Website: developer-guide/website.md - Install script: developer-guide/install-script.md - Using make: developer-guide/using-make.md - Releases: developer-guide/releases.md - Packaging: developer-guide/packaging.md - Security: developer-guide/security.md - Architecture: developer-guide/architecture.md - Building on top of chezmoi: developer-guide/building-on-top-of-chezmoi.md - Links: - Articles: links/articles.md - Podcasts: links/podcasts.md - Videos: links/videos.md - Dotfile repos: links/dotfile-repos.md - Related software: links/related-software.md - Social media: links/social-media.md - License: license.md markdown_extensions: - admonition - attr_list - meta - pymdownx.details - pymdownx.snippets: base_path: - assets/chezmoi.io/snippets - snippets check_paths: true - pymdownx.superfences: custom_fences: - name: mermaid class: mermaid format: !!python/name:mermaid2.fence_mermaid - pymdownx.tabbed: alternate_style: true hooks: - docs/hooks.py plugins: - mermaid2: version: 11.4.1 arguments: theme: | ^(JSON.parse(__md_get("__palette").index == 1)) ? 'dark' : 'light' - search extra_javascript: - extra/refresh_on_toggle_dark_light.js watch: - snippets ================================================ FILE: assets/chezmoi.io/snippets/common-flags/exclude.md ================================================ Exclude target state entries of specific [*types*][types]. The default is `none`. Types can be explicitly included with the `--include` flag. !!! example `--exclude=scripts` will cause the command to not run scripts and `--exclude=encrypted` will exclude encrypted files. [types]: /reference/command-line-flags/common.md#available-entry-types ================================================ FILE: assets/chezmoi.io/snippets/common-flags/format.md ================================================ Set the output format, `json` by default. ================================================ FILE: assets/chezmoi.io/snippets/common-flags/include.md ================================================ Include target state entries of specific [*types*][types]. The default is `all`. Types can be explicitly excluded with the `--exclude` flag. !!! example `--include=files` specifies all files. [types]: /reference/command-line-flags/common.md#available-entry-types ================================================ FILE: assets/chezmoi.io/snippets/common-flags/init.md ================================================ Regenerate and reload the config file from its template before computing the target state. ================================================ FILE: assets/chezmoi.io/snippets/common-flags/nul-path-separator.md ================================================ Separate paths with the NUL character instead of a newline. ================================================ FILE: assets/chezmoi.io/snippets/common-flags/override-data-file.md ================================================ Override template data with JSON data read from *filename*. ================================================ FILE: assets/chezmoi.io/snippets/common-flags/override-data.md ================================================ Override template data with *json-data*. ================================================ FILE: assets/chezmoi.io/snippets/common-flags/parent-dirs.md ================================================ Execute the command on *target* and all its parent directories. ================================================ FILE: assets/chezmoi.io/snippets/common-flags/path-style.md ================================================ --8<-- [start:all] --8<-- [start:no-source-tree] Print paths in the given style. The default is `relative`. | Style | Description | | ----------------- | ------------------------------------------- | | `absolute` | Absolute paths in the destination directory | | `relative` | Relative paths to the destination directory | --8<-- [end:no-source-tree] | `source-absolute` | Absolute paths in the source tree directory | | `source-relative` | Relative paths to the source tree directory | | `all` | All path styles, indexed by relative | --8<-- [end:all] ================================================ FILE: assets/chezmoi.io/snippets/common-flags/recursive.md ================================================ --8<-- [start:default-true] --8<-- [start:default-false] Recurse into subdirectories. --8<-- [end:default-false] Enabled by default. Can be disabled with `--recursive=false`. --8<-- [end:default-true] ================================================ FILE: assets/chezmoi.io/snippets/common-flags/tree.md ================================================ Print paths as a tree instead of a list. ================================================ FILE: assets/chezmoi.io/snippets/config-format.md ================================================ !!! info chezmoi supports multiple file `$FORMAT`s for configuration and data: [JSON][json], JSONC, [TOML][toml], and [YAML][yaml]. [json]: https://www.json.org/json-en.html [toml]: https://github.com/toml-lang/toml [yaml]: https://yaml.org/ ================================================ FILE: assets/cosign/cosign.key ================================================ -----BEGIN ENCRYPTED COSIGN PRIVATE KEY----- eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6 OCwicCI6MX0sInNhbHQiOiJ0Ris3cmtnL2RPZ0YxZzcwSTA4dVQ0aWdPYisxcTkv SjRNaDIvd1RPQ2VNPSJ9LCJjaXBoZXIiOnsibmFtZSI6Im5hY2wvc2VjcmV0Ym94 Iiwibm9uY2UiOiJWMGpoNGxmenMyNFNRMEpyUExzeFNhbHZacGd5ZGJpbiJ9LCJj aXBoZXJ0ZXh0IjoiTGMrTGsvQTRTdVBmblcyYWJnT2VFbnZDbkxNNG9QRnBFTnpZ ZG04b1BVSlRSdmVsTWxJcjR4Slc1SjNJaXRLYlREOVFUMGRjZ1ozOE0zenh4WGNm TVFxNFBmYXpsZHIwTHlFOE5LREx5bmtlMEcrT2ZwQ2RsbExhb1ZWWW5Uck9RWi9D Y09Xanp4T3FXQ1VGWjlBK0UwVzJLSUJ2VHBjSHZNWVdDOVN3SDBwTUNja2dYc21p SitOTzhYbmwwWTJ4T3N2My84SWJ4TXRzMmc9PSJ9 -----END ENCRYPTED COSIGN PRIVATE KEY----- ================================================ FILE: assets/cosign/cosign.pub ================================================ -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJDy2Dn3u5hqjQkTrcAukXwJty9Ke oquP+qONwiD4r+cjO8yrhoELoUk1ogXzvpM7f9bOS/YS5pdx2snCmMudDg== -----END PUBLIC KEY----- ================================================ FILE: assets/docker/alpine.Dockerfile ================================================ FROM alpine:latest RUN apk --no-cache add age git go unzip zip COPY assets/docker/entrypoint.sh /entrypoint.sh ENTRYPOINT /entrypoint.sh ================================================ FILE: assets/docker/archlinux.Dockerfile ================================================ FROM archlinux:latest RUN pacman -Syu --noconfirm age gcc git go unzip zip COPY assets/docker/entrypoint.sh /entrypoint.sh ENTRYPOINT /entrypoint.sh ================================================ FILE: assets/docker/entrypoint.sh ================================================ #!/bin/sh set -euf git config --global --add safe.directory /chezmoi export GO="${GO:-go}" export GOTOOLCHAIN=auto if [ -d "/go-cache" ]; then export GOCACHE="/go-cache/cache" echo "Set GOCACHE to ${GOCACHE}" export GOMODCACHE="/go-cache/modcache" echo "Set GOMODCACHE to ${GOMODCACHE}" fi cd /chezmoi ${GO} tool chezmoi doctor || true ${GO} test ./... sh assets/scripts/install.sh bin/chezmoi --version ================================================ FILE: assets/docker/fedora.Dockerfile ================================================ FROM fedora:latest ENV GOPROXY=https://proxy.golang.org/ RUN dnf update -y && \ dnf install -y bzip2 git gnupg golang COPY assets/docker/entrypoint.sh /entrypoint.sh ENTRYPOINT /entrypoint.sh ================================================ FILE: assets/docker/test.sh ================================================ #!/bin/bash set -eufo pipefail SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) REPO_ROOT=$(cd "${SCRIPT_DIR}/../.." &>/dev/null && pwd) cd "${REPO_ROOT}" if [ "$#" -eq 0 ]; then echo "Usage: $0 distribution1 [distribution2 ... distributionN]" exit 1 fi for distribution in "$@"; do echo "${distribution}" dockerfile="assets/docker/${distribution}.Dockerfile" if [ ! -f "${dockerfile}" ]; then echo "${dockerfile} not found" exit 1 fi image="$(docker build . -f "assets/docker/${distribution}.Dockerfile" -q)" docker_command=( docker run --env "CHEZMOI_GITHUB_ACCESS_TOKEN=${CHEZMOI_GITHUB_ACCESS_TOKEN-}" --env "CHEZMOI_GITHUB_TOKEN=${CHEZMOI_GITHUB_TOKEN-}" --env "GITHUB_ACCESS_TOKEN=${GITHUB_ACCESS_TOKEN-}" --env "GITHUB_TOKEN=${GITHUB_TOKEN-}" --rm --volume "${PWD}:/chezmoi" ) if [ -n "${DOCKER_GOCACHE-}" ]; then mkdir -p "${DOCKER_GOCACHE}" docker_command+=(--volume "${DOCKER_GOCACHE}:/go-cache") fi docker_command+=("${image}") # Run docker "${docker_command[@]}" done ================================================ FILE: assets/docker/voidlinux.Dockerfile ================================================ FROM ghcr.io/void-linux/void-linux:20220211rc01-full-x86_64 RUN \ xbps-install --sync --update --yes ; \ xbps-install --update --yes xbps && \ xbps-install --sync --update --yes && \ xbps-install --yes age curl gcc git go unzip zip COPY assets/docker/entrypoint.sh /entrypoint.sh ENTRYPOINT /entrypoint.sh ================================================ FILE: assets/get.chezmoi.io/.nojekyll ================================================ ================================================ FILE: assets/get.chezmoi.io/CNAME ================================================ get.chezmoi.io ================================================ FILE: assets/images/logotype_black.ai ================================================ %PDF-1.5 4 0 obj << /Type /Page /Parent 2 0 R /Contents 5 0 R /PieceInfo << /Illustrator 6 0 R>> /MediaBox [-0.0000 -0.0000 245.7601 70.8001] /TrimBox [0.0000 0.0000 245.7601 70.8001] /CropBox [-0.0000 -0.0000 245.7601 70.8001] /Resources << /ProcSet [/PDF] /ExtGState << /GS11 11 0 R >> >> >> endobj 6 0 obj << /Private 7 0 R>> endobj 7 0 obj << /AIMetaData 9 0 R /AIPrivateData1 10 0 R /AIPrivateData2 8 0 R /CreatorVersion 15 /ContainerVersion 9 /RoundtripVersion 15 /NumBlock 2 >> endobj 9 0 obj << /Length 476 >> stream %!PS-Adobe-3.0 %%Creator:Adobe Illustrator (TM) 15.000000 Exported from CorelDRAW X7 %%Title:logotype_black.ai %%CreationDate: %%Canvassize:16383 %%BoundingBox:0 0 246 71 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI3_ColorUsage: Color %AI3_Cropmarks:0 0 246 -71 %AI3_TileBox:0 0 246 -71 %AI3_TemplateBox:123 -35 123 -35 %AI3_DocumentPreview: None %AI5_ArtSize: 246 71 %AI5_NumLayers: 1 %AI5_FileFormat 2.0 %AI9_ColorModel: 2 %AI12_CMSettings: 00.MS endstream endobj 10 0 obj << /Length 3802 >> stream %AI7_Thumbnail: 128 36 8 %%BeginData: 3556 Hex Bytes %FEFEFE332C2B000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000 %524C45FD1500FD0301FD7C00FD0501FD7A00FD0701FD7800FD0901FD7500 %FD0D01FD7200FD0601FD0300FD0601FD7000FD0601FD0500FD0601FD6E00 %FD0601FD0700FD0601FD6C00FD0601FD0900FD0601FD6900FD0701FD0B00 %FD0701FD6600FD0601FD0F00FD0601FD6400FD0601FD1100FD0601FD6200 %FD0601FD1300FD0601FD1600FD0701FD0300FD0301FD0300FD030100FD08 %0100FD08010000FD0301FD0400FD0401FD0500FD0601FD0400FD0301FD05 %00FD0701FD0900FD0301FD0900FD0701FD1300FD09010000FD0301FD0300 %FD030100FD080100FD0701FD0300FD0301FD0400FD0401FD0300FD0901FD %0300FD0301FD0400FD0601FD0A00FD0501FD0A00FD0601FD1100FD0B0100 %FD0301FD0300FD030100FD080100FD0701FD0300FD0401FD0300FD0401FD %0300FD0A010000FD0301FD0300FD0B01FD0500FD0601FD0600FD0B01FD0F %00FD0401FD050001010000FD0301FD0300FD030100FD0301FD0900FD0301 %FD0300FD05010000FD05010000FD0401FD0400FD040100FD03010000FD0C %01FD0500FD0701FD0500FD0C01FD0E00FD0301FD0A00FD090100FD0801FD %0300FD0401FD0300FD05010000FD05010000FD0301FD0600FD030100FD03 %0100FD0D01FD0600FD0501FD0600FD0D01FD0D00FD0301FD0A00FD090100 %FD0801FD0300FD0301FD0400FD060100FD0A01FD0600FD030100FD1101FD %0700FD0301FD0700FD0E01FD0C00FD0301FD0A00FD090100FD0801FD0300 %FD0301FD0400FD1101FD0600FD030100FD0301FD0A00FD0401FD0600FD05 %01FD0600FD0401FD1600FD0301FD0A00FD0301FD0300FD030100FD0301FD %0700FD0301FD0500FD090100FD030100FD0301FD0600FD030100FD0301FD %0A00FD0401FD0500FD0701FD0500FD0401FD1600FD0401FD050001010000 %FD0301FD0300FD030100FD0301FD0700FD0301FD0400FD040100FD050100 %FD030100FD0401FD0400FD040100FD0301FD0A00FD0401FD0500FD0701FD %0500FD0401FD1700FD0B0100FD0301FD0300FD030100FD080100FD070100 %FD03010000FD04010000FD03010000FD0A010000FD0301FD0A00FD0401FD %1100FD0401FD1800FD09010000FD0301FD0300FD030100FD080100FD0701 %00FD03010000FD04010000FD03010000FD0A010000FD0301FD0A00FD0401 %FD1100FD0401FD1900FD0701FD0300FD0301FD0300FD030100FD100100FD %0301FD0300FD03010000FD0401FD0300FD0601FD0400FD0301FD0A00FD04 %01FD1100FD0401FD6700FD0401FD1100FD0401FD6700FD0401FD0800FD07 %010000FD0401FD6700FD04010000FD1401FD6600FD1D01FD6300FD1F01FD %5F00FD0D01FD0F00FD0801FD5A00FD0901FD1B00FD0401FD5700FD0601FD %7800FD0501FD7A00FD0301FD7B00FD0301FD7D00 %%EndData endstream endobj 8 0 obj << /Filter [/ASCIIHexDecode /FlateDecode ] /Length 949853 >> stream 78daecbc67d7e2c8962ef87dd69aff90782f849084072181bcf71e04082b3c74f739bffe8a37b32a b38ee93e3d73baefdc594dad4a42113b623fb163c736a178296464ad31dd5c5771a30d80fff7ff55 28108f387a5d1ffdafca6f4c92bc9fafc7a7e65b59172adf5a08007e7dbecdffed767dbce2cdb7ed e37afe465c1f713253a7d637bb938ea21f5e49dc4faebbebeb4fb778b14aa2f509880ebf33385c2f b3e815f7bf2aa2cbbf44cfe7e1cf71bf85b6bbed4f1d7e7d5f3687cb0ebffe5b1ffc067e8360f45b a7f569995dd7ef737c79c98feb3a7e3e896b727d3cfbdf883f45976f42b44b5ba26f4e9c24d77ffd 867fb8a67da64c7bf145673c538294f653feadfe71bd9da3c7e9f93b9bc6179f4f9b7e48e25f01fc d2129f6f498affd3da82dadf1a6de4db8fef1f143f61c6ff7288ffb5ff4dbc5ee2af3664317dbcb4 cf6c7fcceadb8f6af17de6a33fc59fd9b47e54912902f2fa3847af6fd0d7f24c99def7a908d74d9c a4237cd5b5a0052168f1eb954a2ced0d8280a07d357416fafe7d5e5da2434adb82badfdae8b7ee97 7ce3dde1b30251ff5b1b41d06f74fc6fdff03fbde267da48ce3fffb5db1001e1e07ff2937607ff5f 7cfea7fbff74ff9feeffd3fd7fbaffffa37b4a8640300123e4ac8580203903db608b9c7588af32f2 559e7e953b5fe5ee57b9f75546bebaa78fb3af47e8ab09fd94d3417e2f77c05fea919f6574fe5bf7 ef4d9d5f9a885fbaf47ea9effd4402e2bf75ff7a44d15fba90bf74817f965bad5feaa13f706ffd02 b885fec2a5fd53263fcb5fdf1f2ffd5bf1f3fd0be1779ef05fcef847fd771ae46b817ee7d3fba5e9 9759fec0d5fb0b06eddfc0b77f45f03741ff8aa5fd73f13edcff9aec3bcce9cfb5ff51fe557cf887 d5efdcff5a2c7f0309fc97e59fdca77f3db3ef3cfe4274e82ff5e46fa2fb5dc2e9207f6b98df30b5 ff28e2dfc0ff98e58f8e3f4bf01f05f773a8af12f1b5eebfc3ebfc5226becaf35f784e7f2edf77d9 fc3af75fc5f2b770b47f99f9ef53f9a975b35f9a915fcadfeb677f1bc5f7b977ff8e9aa03f869efe 35e774f57f80effcd2ed7b79fe5526fefd79ff41697fe3f87dd4bf9ce32f43c0bf4ef237c9a33f9b 5ae85f75f9bb1af097e0913f80fc2377f46f779ffed5cefe75fd7f45f457baf9f7b7ccafa26cff0d dd43feb0e3fe3d15fd0bb1fd82f003fe57909d9f7bf93fdec87fe0fe93e75f6c8cbfdccad33fecf7 d62fc05bddbf67d5fe36ffdff6fbff43fe5fe0ffc0bff78f58f816f8fdfbfb86fd8be6bfdac07f6d e17ff0ffa1b4bff2473bbf60f93bf5e07719757eecf7bf45f4bdb605fff47fadafbd8f7e216a913f 248f90bf9805f2e78644a63fdd410bffc900f9c513a7dd7fb8fc5f4381ef2102fe4b79f6a52285f9 65f349597fcf5fe5479a09ef7eb4fc7cf86a4bb3e1f7ed47c2fc3dd9fd9190ff1861d1fcade25bff fbe361fd3995881e7ffa51610bbc9826d97fa779f0adfc6fe7e4921234a2d7eb7158bdd3e4b9f2ad fe453c7d3ca2ffd661fe495c7ea15bef0fc9e6115f7e50fd6879fef1f1fff3b0ff0b862cff4bf448 954b8cce71e55b534bc7b8ec7e127ffeb97c35fd26a75fc9ebff1b34e73f219cff728edf75a8ff2f ff90e07ea7adff3762fbc791fd2757f3df53a8fe333adf92f863995255f98730d4ff63253d44ab24 4ec7fb270ff78fc3fbdfbdf9ff8b57f4bf1eff3339ace3ff93e4fd8fc9fabf6d47ffeb61f3daff43 987e50fe37e1dac787ddfe1fda97bf93d6ff4f759769cbeafa7a5dcf7cbc7d498f431a89fdd32ce7 7f6cb79edb7ffd6798bf73fc8a36a975fe678c95dbfc883bffa1f5ff853aadfa54b6be359934682d 9fa68c1c3d52e257fc78cecfabf813016f0f49ccc57ffad11ffa5ba4d6feb0deff1569a705b55add 1ff49f174adf7e0f8f7fd07efbbcb2fa6d79f0c3771df9d69c6a04c3749159bcfe28e6a73593b954 afc82b5b0032ce78dc9ed76f450c358dc0ec4926aee4784b7332996a299b493f59ac2f612fe4ddc7 a53f7f75f973fac9642acbd90f92329bfdbdfec7e7d3aff689ec3bc12e49cbb9e767a031f6509856 5a2a8c3e043c856331f5793c7c1e11e4515ef969a1584eff5fbc3e8fe11f1f83b4147f7f44bbc43e 25ae4be5afe9208df8539312605ef2fb63da5ddf7b5f8f388ca453caecdf6cdabd467c01fb82d9eb 9edf1e33c385c18a388cc6fd56bd4cf46b18513248227e2d7e9f52cae6970992c43618fd1975d1db 7ebed2c12c399c8fce206baffad3eeeab6abe155a59ed20c5b4d90110bd8895906235cdb0a29cb52 920aae5c4b2552cabe668097907fcef6b447eb4b9adfd97c2ffef8ce0c87d5fc477c5a0603c24ae6 78285eb2f3574ecb659b3e97d347192d0fd516db7c9c136f85290cc585c7015815a5cd705daadfcd 6d2974ed7d79c8e0c5f2b55b432b42f3caa66caab5de715d0df715a036d268ad76770be5bafc5819 0d70a2808dcd93db00b38d4a370bf97bbbe9f254161cb4ca3bf0060ff896dc116108341e6f68db12 776df2850670b90cda7048a02a32019f14f20677d3940d6ae2bb49a773cbe39dcb9ae6bb4ab1e9f7 da707ce81dfbd34c9ff70064d0e4daca60b790df438e68f1a32657ad8df67e3918f39bcc60d282ea 99c9a9a1c7982c60fe141dcebce95d5aae702b69ec887186485236b3bcf4c8cd96030b9e539cce93 cdccfa489ecc3c4a693267d3fd87fe64b293b0cf84cfa7c3927ebfc0013383e24e68e6c86b25a22b f43d5b13332b692f06cb69519a85504faef9395ede3987501145fd93aaaa084f02ea9ded4c346beb ccf4d1ee3037728f2a632c405234e7fcdeb06a8fc1c2da7187a32d024cde417230eadc3305dab59e 27cf9b145677bf882c617f25aec580cd3f4e614b6eb4c34b135717c6dabaa76c9623fcdc8ff29792 1e2d656fb1a232c06edda8cd8eeb7d5ddf6f84905fc6505297e2c460d0adbca5763b982df7774938 77f792c8de0fada05e3aeca6ab540af4a47dca7b1873b280444edaeb3d996c28a09ab2394ff29176 bec956701127d8ee5a5d81f6d5dd949a37d802a9db6ab191ee7d640cdf37bdcee4813ac4f1e1eee7 feb394e4818f461633e8493232ceebb8cf02810a64dd897cc841387dc82df16c36df8bf768ca26bf ebe6c0c2a4bc29171278552ccebc47be78e7835789e53b60393fc198b2aa14bc4abd7c7f56bc427d 5445e65a5c8debdd710d038047ed86b7d43a67c248a3d4135f0d7b5adf01f0fabe04b6663e6a12c9 6c9bb269be897e002a57d06e01c7b1df8a46c70334192f6bd0637d4e352c222e7073d503e1f58057 111caf94d16c2617a0566348773adbe7a873be9ea1aed81bbe7bcd52fedc8b75f4dea7e4a832a802 ee74b0024fab94cd702ef66ba3d2b534192d16757f4c94e4faa47840ddc9e2deec633385cc4e2bfc 259cae4a0c8493c5c186a86218456c2a0134633aa3c21c5802cff9211abf49a95e5d53eda7a05017 a44dd2daad45307d40605236cc3b285aacbf7e5cb859bf81f235e08cf35b1d130481a92d45f88494 c57baadb92b36c66641c7aa94aad5a469543065cab720b286a1dad836b2f885fe97eeed2326625d2 36eb70a16e1e30dbb41473d448d9d8bd77c97232db5451c37948b864feb9f21a0e5cf78e5d86f395 243c063deed50e3359400903617459cceb5a7759b756fc72375ad291801cc9553b7f99ad2ec5ca7c ad3ddbf34dbfccd09b37b4e0634faaeb299b2dde5aae769522f7dac50d02d9f3234c3840b23f3e1c e3fcf228ecd0f209b406d8699f2bab093f6accce104590e7334ae117753fe95dbbd542e3fa60b699 9b356cdbf77ebdbbbd5f1e13f8c11282ffcc4a9cf2e50832d96c2655542be331c74eb675f16fd9d0 c4f7b94ecbbee6e27eb79c1f83cb5afe789ebf0bf8ae73295c9fc8a948b77bdbe23b5f884a624ebb 964b48af56369c2a5669b6325625cc575fd56e059b5677cc6e9fb2a94d9fd8203598f97d9d578ff3 46697b2e7d947203c00d410636f1bed3c41820dbbc8ff52d28d8a93256bb7da7e5b7691feaa9970d 743af49236334f8a7069e40d6057f64da4d7cee49033c8f328df29bf3fe76e35692b76969d1dd8c5 06f973f7ed305ecfa45a4abfe3b484fe7955a30752abc30cc19d240e779bbc3b628bcbd3b82eebb5 f1a6a31113f2c83b58c5555e58e4afc7d3d9bdb6c2cb9c87e0517310117300187d4c67a5fe4e66ab da9e9e93c35d99ac1eb26b72a34e048a11f6431ad888207d989255467c8eb32c4c356fec75d2bb71 0623bdf961ae5213b2ebf550088fae2a924070961ace19920e5648caf2ce0b952e1ce75336ca6bf1 c2559feeafb51968b6f47aad6cea87825c34944a9937bb57e662bead78600558cdb7e765aae4d47d 7fee1ca0d3ca95f577d9eb662bb8f71ad43ddfe32a8f0057336058a949bd7043ddb105bb19531fd3 d982b7f23251da76a465bc68d593e164f5860ef9b577a45b1bdc6ce07125c8dbf1b6da786f851d83 ef1032b47737eae2edcdcd607518d582c33137c927c71028ef4e8470f0931a3d2492e361363d1b75 7298b2b94ca061fb5a29164bd77dedf6ba29326edcbbf38671bf5edefc83cf68d967aef7b63f21c6 2b83b0663363e05b299b3f179c2c3bbe3eb337072373783f253bd4b7727e409cdbf9d5b97a2b201a e31702eef5b102c5e61b428ab6546f97eac2b85b32b6e90256494229eb5d30ae5486d54a45f310aa 5ae9f087aa5eb8f56bd5221bd70cbc83d56bb7e6ab6e6dfa760388b849c3bdedab00341a5c81f076 5b35d1e3ca6baecfaef9395f1ea21b0b3c042fb78573d375eb363d5c204693aaedeca53f6a2b52c7 846bbdfe1d76c7f208818dfb1ad9b4e51e3ac9f50fe815e8d01d96c31add22e0265dab01a50e9ebc 6bbdcd6327f6a7e7b7d27f353127653350968fddb069af4ac3d5698d8fa662291ebd75673036aac2 7902df7465722c9dfb182b11d5694deae4a651d4c9e20467548882027608fffee06613e9b49a6799 6c65eeea3d8e1c17564f2ab36358cadd0e4b291b7a92edfb4c4ee4702628f82576ea3ed65c71d617 b8251af4f8f96c5415aacafd21c485f5516497512c8152bc964ef23b9695cdf094aaece6ad3c9359 53758fc84cc33be3a55e79b90d7d97832c43d22b9f17516657efdcac6c7db3b4a28a6ed95c57d61d b47cf3dd0ce9dcdcd57cd7f7a426b3f38724cb0455eadd0b2e99433b0c06507f21e4f6ec72d4db6d 22a08522d16b0f6c56dbd46dad6da458d8b077f71c8f5bf6e732deb615ed8fbb82d4bcec2ed5c969 1f355ae1419b8bf4913bbfc1138b8ec4846ee29bf3dc69de2f78e816aed850e8dc866d787387b2e7 fdfd2911e24379c9d0b3f088ee8f07a6f06cfde2226cc579be5799f2e813aab3b9526ffaa951e478 20286c9136c72c4244aff175da42d96ea1bd647ddabb571a8b0dc3310dd965a830231e82fbdd17dc fe926104cc06ed003f33fc914256a9e89f21451efb657ed49b4ed8c26df14915e18bd43379c8cf0f b92984f4ee336bc3a99db758a51fca4c5e7263b927734636cb34b9da415216b59e041014cdf077a1 306e886c5d04a24996050a835e65e2e83b516814e63453a4c17d77a44a4689d58b1f1570b852dfa2 e72aa7464cabc16401be476ac61897ef8dec8e11d8020c2bf75e413a4790cd1eae2072cb7b2dc995 2b799246c44d76d13fb6d756a7608c6869a208a2bd9b533a9f71764cf6d2dc8dac6569f00907b1c4 7d31916d542bbcd7aa4b678833533bf222777518087ca7b19cd30dd1334d856a9af62d6c6f6925e8 ade74faf7d340e609a1734f585074fd86c4647f79cc5b236a35f1e3e28adf3712747ed46fd66ca26 9c5f8053d85e0704dd7c337531e735672e145f793a70c6cd517f7be22cf01ab10ce77258c590727d e9094e62ca8072d176bc2cb21bb6cf57a87d38a18d1b02bd16f3e1d2a7078b6961ae1c56776f8a9e 3f2f6fe8470758c1ed43776bd5450f67d1ce33b9b42b19f7f4b46b0bcaa894bde0c0e1b3d8482747 3d757f24a880412c7573d0a0d365ba8f0a4ce0f9b8073e99f1a97aa97865af26672eee492847834d dcca431f15605843d9f27c6e021bed7b7060647727f165ffb1991d7a238dd9f0b533773bf304f408 148b1d64edaec0d9e623e91efda740a03b97876691e96b5eb1ce2b2c78e236a6a9f3121c5cf92295 ac38dd787ddcda205969736ec7840e7704bbbd525798237cb03ed0dc532d13ebcd7983cbb430bd72 62c40ff44c044fa44a8901b961bfa110b7266b8ad5abbce5c6edd10c520bc7abb0aae921bf2646cf e36dfd5e19c8999fa76c52bbd7af79833eb355afeb86cc95aed93ca74046a8d48868c3e5dae17130 d8946a521c9d2b1cf69e6e4bf21cca884a889b5c87affb2b4d4e1e96b12108f6ba5977d5371413fa d9645976bdd70a044a36171f4d5b0881cf9ec968008d2bda4e49e89dcaa31d2e7738f51e87007c6e a7ece146b55dac33651d4e9c12ac697453d58ea58249ab81caa65110d0bf4395968e2ef106979386 fb5204a28118d48f307bb291cf5bd268556f2ae12333b9b2d2a64fa803865bb8afd7a0cb526bed82 c7e443b7e211c4b13a2076a0b6a8640cc15836b9c4829c43a55ebac5c3ce65c42abd5bdf89c07d71 39ba587d7632919e2cbe3431bf8b1c3eb361716722f743887938e4c63bb0b1742f969222d91637b8 7666bd67a4463075b2b7fa635961c786df568ecdb213d99054623bef1d8973c6ae193c5e08c49246 b10455f3286df8a7e3867344b9b5237aa3de47d33287232249acdeb495fa64cb14dbd58728168637 86f55f1da677e32bc2c678e77a061fa64e378e0461d9284305602b59ecabc532924e74ca0bbc16bd f8dee68889c29e6365e55c66b855d35a09cbb5fb716bd37e60129c30ae358441d55740512a81dc52 b00571557db2f17d8b23f2fc99214575781a5af4912a882f8c590be1082bd0d2bbb812b3ba791380 7d3cee6577455f980fb9406895bb616a9f705068d3f4271610431cab8410fbc2b4c22901840d644b 92f8584c947665220ab5a82861ea7b8849a7f9b8c9af796506a6e96e4b52976747ac961fd9cded32 9d5ab78db1111ebb9d6c9279b06eb4c05657009d6042ad768095b2d1b2c719c0bbaf76beabe322a7 c88b69c02fdeccb3b01f09a630cb0c7762fb96bf0643acee3a8f29f710c62364206e507a6ade70ad c107245cc0cafdd75557865e8dc78f33096c6a34a511977924583d0d48d96c1aefb7bae0f14a41d0 abb06044f7bee8a909a3f0fbe7a84075c7cf9b8d078d1d3f5985ebee440d1093edacf2fc7cdec48a f9344910a4c1722d3c9660db5f6aeb5594d3afa850e27675b1ff284efcb791fdb8359e8656f164b5 9f6ddd1273bbf240b76f81d57cc4ebe33b571046e8b8bb56624cdb0dcfd453980de5ad312ef39b15 211b3bde8e7a31798b3285d04ef619bed589d86e93c99c9d78168d78541b8145c096d7291bc1a43b a2866427a87bcf4b6bda09e0aba2596f23f55a479adebd014646275d6d6c0e009a81c67d4bc2a224 00f02e37668e0894c83cfcec45d56852e4eb6bce55927daeaae906dce704159fc94cfdf5d1b4b9b4 871f1c328a5a1271ac0f509f17100eb120586a578b879cb78c44ee82f52975acd61ce74c2f77e273 506dc96b50d9f120684e85cb1373248d0c0ba3c770680a1db7961177f56902b42a85ac30173a42ca 465a943aeb65e541143574585095a60f79aa01412b65d85c75a55477d8d97a9943a4dbb8ce8a47d8 8bd07ede90a4ea72d0111d6fa1e74b3b7dc7b76fdba3e254819e937d0dce06db01ca72db8f90d40a 629f344aeb8ab02a15ef11352269a8a93845b9248e3b510e283b04a90c80d543025a6776319d8a8e 3b4564510e9ee856395fa0bc358c6b9c3467f0c60cc96410fd2ac21d91a2ea4f349bc04dcd58d617 220a48fee7f86132a85e7962d166958133bbdbb32351f5cbe7a12925cb33cc1eb685879dd40a3931 eadacd51657ebd9b6a51698b99846680bcee46ea63ba10c5e87c40c32d6a142365bb9fa5ae635d52 a8ae420452b2f89c4049603de91127756bb8e4f03c11db81d3460ede06b12ed358159ee7069d17dd 8ecb8badf25d019edb869dbd26ebd5cd4f3029082c8c15aad9caa27aa8d8a2726dcc87c78e54f573 e63a14f6dd6cbbf1bc5fc61f477091204ae49df5d69fc22f849edb00658c602a2b99238ca3b91e5b d246a3e301a7ade5905e0760a2ac006b070f7b0b99e9c832adf06043cd3c6bcb2b2b44fb819969ab 55c32d5c588e209f88b69737f2271658dfcf7b2e4b391db581b7d5414feeb1ec2db06f4a462f39b5 7b6f9fe766b9d95381a702e24d986922957c25a7735a6f229e886b4548f683b36a62f67d1a870628 8090735240bbc3b4af8dbccc9f7bc027f1908f6497ccd2ec12e46a2f1f347a7ca9adaf6f858ec291 e04c431c6745671fab8e9cc90c728a0653cb7e9c0f2e523ec9cb32d3c016b50b7fcb4b080f5e64f1 bc11dd45e67437f6896f6ab7f30414eb32847f344daeb50db5a61ed1292af7ebcaf68ee8b257e277 6d3f496225e71745b9ff00dbd95d4400dcb46a69fa2a3b8c749814646bd1846875b3695529ae91ed 18e816af2840055bf6e9e3dbd7e441a32ee7fc42ee732e70555547118c534b4ee3828b0b0fa5b72f 5fab6d4d9f0123c1bd2a0567c00b8eb259bc47d859995ecd7d5cabc8939ac0b7c5987f1944ec71d2 35ddddb972855b7192093d74b17909b43dca796166b1f96404aabc903214505deedd58cf8fe42b1a 9ffa08fc98dadb6cab22ad7023ae5d3ba38d221051ba977b9396139f0bdbf55443df1a45c047615a 997b8bf9bc34524caa196276ad71f7236ba2cbe0cdbab7f16ae9f2c96f1413cc4841c70172884d9d 397bf3aa3a51f008957e61a9d2e3664eb5ca7bed30ef45e89166dc81aeef6f95495781e00e7d8408 53c39ec35b85f75b6786260450ad1c37b6b9ec9f148e56e4a675bd663e47435c2dab4aec45af1286 f08aa3f126846feca24a859a155b718b8df81d7beca3756d5e1f7dcefe67015b4fa8b3fddc0e8ab2 6bdf2662965051738493c92c386df242239a137a5b85a06ebd69387ca8643eaf3cd4f596252ae96a a202645294b27d3c39b37e0cc8d4faab556bb02c36d8f933efc92d785ad71f701619f7d15c56aa30 12ac950fcf71ab5f9da9e2d28319d57fb7f2190a5b842cb9d5c6f6f4f52acb55e1f539ebd48ec052 368e537e336bb103432da816ab059b11de39145454be50c78e8a36dc6bc51b2c067236c7b10aac19 8cb1628fa9f511c0937984c13c9bd9643dc3804f1d5dc4e2cee8d5c81cb5d4d19baac94e3edbb305 5306ade5abab446d941432b38eb836ebe67a33ebedebb1b439a05b6701cc2583b3972c918ceed334 2d1f6db5c9b56876dcaad53056b5495d393c2b58653d2f5ce51253b7e51d7c9c1be41048424a287d 84661a502230cb693df16abdd451776fcdf628525cce7e36c65515af4e87adba33cf18051000948d 59481399f91e61b786165847eee44aa22d66160eeb648db1e86a84bb406ffe3effc6b4d6a293eff0 b25549d938136b172ace43c02babc455e452e38ec80a4dd5d4e6f37aa5a7d800f194a603511de3f1 a007d3eac5d69f607f1079c293961ee4cdec32cea299ad820bfaa992679391f3835455fa77b67e7a 36fc15bffddc63e3ad4d89671f3577e8e84d8498ce4b2ec1eab0165b99822b21745fcab0fab0db36 86179629653aaf1b5763b30b054ac8ae12cad3aed8c587a1bb2eac9ae4a1853f05c879c6562ad1dd 60f2ac42bc938d3eded3b0e50c071c1ca8ccdba83f3324362ba560865c0ae6aafba59b5de081e2a4 22530fafe464b46e197b66435e82c8bd6cce32cf0502d7aba0982ece4677e78d4da9d3c00b22b000 2165d2b12c853c1a9f1457ef2c8e6317d9e26592dc29bc3a5cf6708b68970b838ad309e4b7915b1a 157ddc060ea72e2e6f8f51415ff633e714c9c84e919c094fe7b82197647394f12a2988ad0ab70216 56b6b0f6ea6e64e3313df29fbb83b5580cb40c3257f4867e644b7c13798be2815e2ad405d3147499 886eab2dd69ca31f4064871e4f2cc6179b56ee7e06fb2f81af18a14e49ba17841270c9bd1c4dca3c 4d1d6cd6b329923199223919291b6f60393cb798d9036f2e8659bb479d1c4c551b75fbd59e6186ed 626ba4d8888656bdfa1a695b9b744b0eb81b8b96de792b0c31b92b99f07659bce2d7caf1f75583ac be0a633f6aaf27e6fe7e9af60fcbf147682e5783fa3a77e56fc06e7fcc69eca1ad6849991732554a 6eb3ed6e705b46d17846b3f9dc9a1eed6e7a50de69bd7171f7aad16377603aaf52fd0a45249fa3ed 0bd6b09b3cd1ce6bddf98181c7eda73c508fc74f38383bccc71cd48806219153c644df38cf59ad09 805ec1ca5b5deaea8f58f2b5bfd9334b6bd4c748c161ad70c6d8fd92997aba224ca462a11e8bf32b ecd283014808f3d5a1eaf7b75e348aa1f39277cb9bcf354aa77b185e206d527ef073f54c5a389db7 f277a036e407d560270b84bb13ba9b494b3ea0573d58601a883f916c579ad7d8d04d33e97a178512 5454b938b61697e85a9f772d500c4914b206f7c84b91a09fd7441f30b545b7622274cd0841d52be8 5d6f59333a23038205a5e579a8edb8d01ad272f15dd60799abe9b8fcaa3010b69a50d0373bd915a7 5d01eabe3c7366d987007986087e82ed83de218a63e7622d3f097bb76dd78beafd78af5be0f295d4 5583d829670b2b9aaa40242998ce2045420ec224d7bfd1c5e2fb696d5821f02685b63f520fa664c4 3038b53bd0690499af29ac87c7c9d16c5668b6a09e11434088cee71d81bce6861da1e6c8776f6fb7 46fe55ae2cf1e3b4e03b95f72172520ffeee228d326069dba0661e183d5ff78f404f2d9027d3ac96 8c0f926e3545322f865ed1ce516f13ebfaeb2446bdea7a888e94f5b9f0d13447ac9cadf745cc42d6 ed75328bb7e7c63839dd7c21ac988c307cf508f9001413fa96e07d7a6d94e6ab09374826ccb9ccd3 84ba264231ab3e91b17ff369fe10953d59ec421599645c3a8477841bd208f6f5c6a358dfb1f5e3bb be3e1fbdc98cc84c30f6d8d6f1c5193ed4fa02309459baf37afb14114d9bf1ca5459e6905a7083a3 6ad94361e4b301b63c4a4b25af308da25b126e13888fc8e2b13da16f931a7f3cd4dd944de0990b10 e9a969e44992fba71b26a75c657131113e60c292a3bea57b8aa4d948c592a9ae2b9b0635c3e0f94e da4da7ef45dd55b03e5955d2183abb287a6ea30734cfb9b12ff2c4f6e258debb901b2cc5cfa93ae7 affda6f4088a0a031f22516b1bcffe32a2dabd09439b5b45c8e6e7fe4bc6a7c8d45b32b286789a73 9f75a96a99a96ea53bf6f06c47d6a11409b8ff20f157d25e5bcdc81903e96e9fc042c93c4b299bbe 9cd879adbaa4182f473023b0e6f47055b4dcb3bde733782e5c5a67eec5e06bb96a186f0695f7918d 3e4ff07240cba389c6c18cf13e31479f354b36220fa67dfdf5c063076f0cacead45e08325be87c5e 16dbf94a8b49c140990f186e85688bc38cdd8c78a74b986cba9162b7ef0b83d052ab67cfb5aafb05 d87b84759d59d45af63821e1dc9bb1573cb05719b90d746f4cd384eb413efb2e2c4b44844ddcaa1a 7e348d0a14d787087684b8f0d5b12bb75cc3298b78bf9a6e0e45563b0bdd725ac5c607c992ad5f6b 153aa802ef2d27b93dba169e27f425384dcde1c379d25c3878311050ddcf17c66b43df8c4b85b17a eb8fa6b54de6716258980ed867cb0fcfcb5df6c567e1a548a7315f122cdbfd0ec72fb60f9abe3775 4148e3030e4a922b7d9f3eb2c334bba9712579ae32ce705c2c9debd92ab7a8c934d5ab1c1e711165 8c34e41597291b6a21345307a7138088dc56088d026f66b636a1a730dd3217fa4c4a511b120a0bfe c61c48b65d94bc24351c631d60f11b956f0d7d9f691b805adec25b4a79d2555ef4ee843c6b3fdb34 bcee0d878583f6612355870ace94570a50aec6c3bc282072424eafe8731d57b197b96e9510f28115 0b7a71b4cdea6df452a3f46ab934abb8cba65aa6fb343df5b375e8fe082ef2b95bee334e46ae9d8a c8d0f047ad00207124fc9c757a4c136cd8db4b3fa2b2f1f1c46df6b06feca30d4badf6197c601d26 798ddb95153a80e94db9b33d035241e32bf353a9525d1d75530cbbec8427d9494fd4faefead9c57b db06056dcd35a1b950c9aa53eae72e476a0ddb08b4ba0453fde9b80ed34dbad8117c16361ba305c9 64c67faddc550baf2da21db421bdb651e1d07db0f12c6572a4380f1d0ec6a7d5cd0e0ac099a6ad06 57c6264247aa4d90eb5cdbeab515d2687e6cda36536166647b592ea9d15be7a25c9138902f43f589 36ec6a41c31841d4a9099f206312006601c65826afefcdfdde7dbc29c25b9539aed6469dfdc89d53 877151e4603c4defa57111a3537b98708d3af9fa6c4f0ae8bee9e4d458726c0c428555b2e699c399 93d82b0a4f961b8b97b88a704ad8608e398ab31f50ec266fdbacbd1f0c70e0b297585fba9fd8ed56 b9b510c3cab38938baf2ad7533b3a3e0c14802dfd7cf59278b6ab9856d373959ccd7883bdb9a5047 86df3db3427b40cfd9615d507bebe2e4c4bba577cc6ef5d43b558b58835f5c964566efe3c982618b 4fb5327e5598cd1c6bc83a951a8879c880ccc9cb8253124e3e075d12307df26c2b8f55c047a91f8b da09c339ee84bdb7e86c099b8f1c526298f943b554975ce924960518ce6df5e9f02a99ea783c5d32 8bfbbcd89b54e89b1c7717363bb8c64a717c2a5f85687c091908ac7e8e5443de8c600772098a69bf 9eb8b4eb112db35ff2ee0cb601b6d8158bee5ac2ce1d2622b52a187756a2ba4c266d2e9b77dc785c 98b5c265bfb4603273b8691edefcda7d6e4b5da6bc0e11ba95af64add51ab03f47aad3aa58ed9501 b86d28188430afa34e1685221508c7474ba50f95dc26382dde8f25e13e703a01f589d48dc48a8fe3 c09229a3fc11931f6ad569fb6b2e9d66f006830e09eb9129f659cfe0b88db75a7e8e86b6d6a172a7 a3d60331b1b89589d6f7c3863e6e76176a9d9083209e9f5b4c2bc331ddc36234714b580e6336cbc7 a968e6b08170cfe7d3bc038ccf3e86f1450ab90cdec276789e8b9db613530176410430f3f81c104f 8c1e7fa07be46ac21f9fab6e13d48f23fae9b542b138292d563e742fb2ce765f1654bfbdd14f3dc0 64f1b63de7ef17394b2a5364c3f65b8b8457f22ba6c3bfba2b96a0ab1d7e458e903c745d8db9d26d f3f9534501ef4455af5fbb9485fde63ee517cec6117afc52e1df8ff5801f74f5fe7817633d9ebaa7 1e0804211a3837b51e3fdf00b1803e72b3482956966a11686c796beca791c932e4e5b9b16df1354d aacc57e013fcd2345c187327b46b750acddd5c1cc0cf0bdf08e3775eda1f9ebc783bb2bc46a80bb7 abd2b6aeb6db125fb843114f8d385d1569d1e53c057a8d7b97642f5fc55693335fb935b0b845b1cc a5912aef60e2e75ac2d2c624c599e78e4fbe7819795a215b414ddd0e0ccef017a539e8566a3ae2bd 561c2133321a09a4a47a63f5c105c58e91df8ca62aefc74f9d6f0c1bbaa3b32aec61d601e3ac4a1d e5928169d868b6f73952e506a7b537ba46ad97b161788feb2ac40c700e5ea44dc706cda31b6bbe78 4d97d072575da2dcc21a1baa245e64ff96264b6920d5a9cdd60b25748223dae01a85fa0e55fa0e6c 11dd34fc9fdd092a7f5f77329fa4303e5d41ee1a39ba036cd5cbaa1fb4596e966915396eb47c86f8 946e7255f6321ad953cff0484c8ab8325d4900751a66b5e16850e2ded7461c5ccca44306b9fa59c9 ca1d417e861149359b765bda103d3a6543e8994e96f277c4584c94b98ba079cba0c72ff828ded59d 9625d7a4cf282f46528aa547c3aa8e19882d946d47da664c9279ec7c8fb9446e221e33f7c1102115 977927878638f03bd77a283359765a697d1c817816b1ab7fd98879c1ea6ba274a32f37b9d6316bfc 7a576f8889fba6f15d4707f9cfa5021150d6219c2e19c3bd73b99788184c3e9b5043849b0d5e5bb9 52273cb33e9f5d65c23f38e2fb388f9886bbfe841c52e5f22e8875d21e0dbc8bd31121760b08eeb5 50ac9bf0e92a76472b436c5156d92fa3de4e77369c24b5e11c2e0db787896a73322322fd530d472c 6ba7e470a5272856330753fe29cdfd8bb985602be1c70ae4dafdc04b3563bc938482e519e4821e9a bbe7ee2032eaa241cb7b3fcd61956b4eb01bdb7830ac8a0335748881d0ae01a5baf2781f145de671 c13b91356fc6666dcf1c308c18c3e2418cdbc8c2566fbb4f46201cfbe876ba7b18ba5929bf4e02d4 39d3707764637ad8be150474c97339b28fdeb84d667f92da320fea6fe8dc0a7a177d2e8265d6a281 0b08bb552c190bd8fa9a1f148a70dfcee6db187fabfafbba74333ee7d04a9a70f484c9e5da742faa d25859f763206aa65617294916c328327b82036ba3a9284777ef76684d842cb0d9c015fd78b60adc 6828e496d74c4e451b3b2ea9de15fd58b14c75b1d2007229f6102d7b6a7c5e4892e20dce52509e9c 2bbcdeeaf58c59764fc54a259275f8e557678f4a9a5fac6a3599cc878e3dc20f36b3c9b4afda6016 38fc1cdaea8c6e7863658990b5c9c9df151873e12ab21cec0610e61c4ce6883cb94f1a65f7926766 6cac3c16db1f286db9da1b6a3159d779b7fa94d522625af31366597ca1ab5ee530c68fbda29c3cb8 c54609a42b51c857877b6bcc97d47d4932e845c69262b32e4f1bd84b15f688cbd967ea331ba92c12 132557a1f693c1b93816eb03d0932e30c74115832f0a5e9f9848d73e9ccbac3cedcd3adde749cbe8 fe43c1c6485f5d17f79c324f92fabc671913a5c68feb72b5fc6c76170136929ecbea451aa1e8e752 5f15df0ab1244993a794ad2f4cf3450ba895cde59bca5351575ca381bbba4fce45997711727c4326 6ff5c887a444487ebff56c2565b5b6543869e23fc46ca92f54d8a3821d547d124af26df0894c38e2 331ba5900b6bb333a536cd7116394b4b6fec755975c8e9c16550146f1635abce16cb7daa625c4fd4 73ecc0e45fc37e201a72a27874e7c1c6b5dad19d567b8e8c086c716c75d6919db31681d44019aeb5 370f9ffb0286b0a8f05236dfc7b2dd82ba67dfa75d43257110950df3be0fef27ed261fe9dc6b6664 274b3f6310a2247b93b0db85eba1835da381b8983490eaf4b06dc96c0711c55118ec35936c9d49ca 79bccdcb99fbdc52a52594eb913bc46ee9ab7db21e1e63614b8dc8474eabed81a8f9c8eddbd4853f 889ad87ae99f441563cb3ccc5b02bc97c54cee263084a4b346b7dad8a536c09d3223791c68743687 a104d9df319cd0ba7e4e6ea7e3be54da016f8039ed336549ef5dda5a4db4fbbcf1c049e38a576b74 b1d92c7097e3b0ae5daf8bee103b5a574edcad3895c0f6c7a6339dc51cd73b2c54635fcba5481a69 46961475d32b4e1a02468fc5af8c2081267aea425e782183ecc4327a44d4d5dc55d14aef6c095cab 7f533c7b57293d8f034a20319e949c01bc52f5b55a518f516d6f0c32d90e253abaab402b4ad146e3 657998675675e991bcae4acc289f6b704d3dbf1d4b766b3557f6c9e2fd01c3a460b2be59bd3746fc 7316eef5449eecf47cd4684c7d984ab48c5a3aa9e87938476ee6a6adec07774a6938f1b20c1c0f59 f1359b2492bf530015f554df2964a08f93d613f4fea210260d89c41c10aa37c83e0ee2c104d2fd8a 7848539f64d25480c34d9bae3ba132b056d74c75dc2bb16d10808cbd87bb7c108d76aed44a186d05 56e829e9d6cf76a5b59e2b2f8bce22d1e6f6c9084cb18a38f2ee8d2dcaf07d7791726a0395a63b8f 51ae8b7c7b51ca8a4d3df5a12c952dbca6de8bedbb6a745967065a11119c9b19f714e8fcea36c96b 7ea39d1f92a5e42015cd74ad458b1d26b0e571e5d947d39865919c93cad9076cff08cb1390ed22e4 ed5e6a9a18b628b68327d8a5e8034b1b55f2b22df4b6da80da826a2886c5fc555c5a1ac1f42f39c0 619dd569466d189ca9ba15ccca608b4c8f4cbd17033e321f276d543461d7002e67959929d4313530 b36eba40854ffeaecd5dfad25d326cef7ce64ef562c65a8b87ee78bf068a1cf6b86886128e9c3619 2f25ae1dc47d7db0613a050119ab1cd6dbe6c47dd9cf89536cfdb9382641343eb117d6cd9ae5f8b1 205632a66376d208b707b8db8330657b2d7d8af48e8dc6320f0863401de8627ed1ff2079a64864dc 59db0383a921a8a20c7d746581ede9762c675d41ce3cb22dfdb62f7f4e07db939aea48835e13d4f4 b03a2d6cc56c4fc0ad7c4ebccad641acb631cbc86339d586530b42ac9b0d55abda9866ac479b6a2f 231715652d34cfda6e97811b30daeb29cdcad3d662af7e4991143f67c8e2c7a639a3d345a1ef3cf9 367dbbcc99ab79521d4f4300d0237226e9c29e28b58739b6a6edea6a4fab2adcb15888a72f412600 497c294f42b8acc9bdebb703dcba3c6f15c26ab7c676ad362ea401d25cea9ee4afd341d34e363b8d 37666e032d5245751658376d3cab192998522505c3779d56ab12d11bff5ef4926ec534f55c721ab7 67f6c14900b0a80f577aa60d06cf8c394fc4977ae2a134a12b814341eb158f52765d8ebe92c29e69 9314791a05a3c3a88ef9adbc47dabdc7c91d565a0e4adab51255d4c18c5dac655655b17e5d5144cf 17adf704353399c2e39022a979e1bc108673832720fa9d81ce1e9cbcc281558b41fa4ae29f3fe8b7 35065d8011acf04c25bfba5bc38e2ce6b0677c67646257142b2b1c6011bbbce3aea52de48fe1cd16 d3a271cc75d0d071664c52437b953ccf6536c39c058d41b1aab6f81157bba807730f8ae314c96bfc fde4b60a07ea780bcea70e60880dff9e732953cb0f28729813fa4e09b1e246490497d865c5bf71fc 6de2c442cfadb0dd8ecfdc720511e19b145b1df15b851b56769e13220f6cc60177b9280d687b313f 7ff21b1499b08cd42403d16472c36b7523a994f8beb88171bd52600ae64da5482afd20036c73f39e bdd135c43a26cee5b1e707d8f6d456d6502f67c1cd3204c6d734faeb3fb0bee145b2904fc3960b8f d4a38f5b1347b768cae64af6d9dc1dbc9187bc2a4b6cc26c787dd56abeec61e6e2a3d0790b680e10 e48c377329541f8beb5a3e21fda331d4a8cf5ece402992f2c2b74ba431ef98b396dd685c4447f58c f960c45bc147d36684849a092d5fc0dd8c11f4e02c71067a87dcfc0817d27812134c91984e5eccf5 08bebca30b37bddc42ec6003e89e7168e38cd969085f45818a0e5b3621414668ddfdea3b3c35e473 c719ea771551bf9fdc663cb67e34d995579f25531c0a6c52dbd6fa8be26621761b8a8d92714b8dfc cc82b834623926a83a7226bc62e11e6666dbab4ce7e6ee4298e17e4866c744c054730f70894c9edc f06541121d0bd9cf59a77fd6cfa3368c8237fa6876de2eada6499a151b3c7debb863fbd4cce6d305 ba8029127b15e91eb69f6a7acde1464eed1de6b2d8a90b8357863d1358e06533aeddb85ea4843d48 0ae8ec0aeb67e6cc2c3e6cd8251d43827e7324b2e4ee6fe27435592c26d41119be66cfa630a119d1 7b6de35e7b88e235fe60bf5b0e5bdf61e502cbd4f81ddf21acf5f25a4d915cd7291267b8dc55b68b a9a96537322c44cd000db4cfbbb56ebf73b4a5669a89bb60077e024da39948997abf652f9eb54176 626975ae701bd78408da9c49a00f181a3b5e8cc3f07d7f8f4aed624dd9677cca1bb78a9336c39601 252f2576da971895a98a3d969a87f9e7d690958585590ae676f980792e87563b3f0de319a0ef9b23 d85fcd826217d786378d426a9863bf26213009376d95bae6653b7b5d81596f7ac1b86eb06909076b ba23a19ca6d9e09aa986983fe98c1ae1e893ad998df546719fbabe69cb336b68144067600f6d645f 76ded5b934948e2753d01f1f2477fb3b92b2c778d3d5309c385511087c11dd16bbf3337bb5ecfeba ea4c6f2402cc84db4b83eeac61d94ee1f35b45d9fdedd0e7a6c909122ec703712c1f0eec7cc90f3b 14db2c5ddc339f8749f9a09bd40d63716e4e0b276a08f26b9a1577d6a02e0459ba182d170c82d667 c5c94ebbd336a24dc989032ea2fdec6c316b26fcf81bf2691a2bd59c3c43660582734a17c675fcbe 4dbacc517d37e84996645bf1bca8b0252d50185d284df70a7f0f84d3b4e0cc5f476ee1586738e65f 3e722783113065a7efd5869f2ca980e22b99639fec4f3e776eb96bc1acd1e454ac152db3eb720feb a6ce3342a42e39a49cc658e09099ef8ba3b222c5cdab24bffc06e922930087c4032dceda4d9fd267 e1bb25948f84b0c8b125fa54c9ae77f008a81be7e9ad33cf58f5cf552b5b1cd3274d1ee3d4fc5490 434619f01945b3108fdc31a37cef5d7e62722159afa923af10c5432d5e89e5fb939cb59a4eb498d5 9f0d7b485af4bc5e1910b2fb007c435cd43b6489ea35a6c6e57cd3c032fdb9fd40552fdead35842d 507990c1801e5f76f11672ed65705856e0d9ac38132caf22786e7b1dfa73ac10eaf4753d70ada236 6e9004f1c47a964570ba33ef8c287697baa577c9ab88ad223a22c2c2dd0bed4de7eba754ec1acacc fc5e9a7be7d0d6d6f36362390ff34b77dadc1a813d0bd727723b1b73ad26f47c19c5d34ba033893e 8c590b38c40bc06fcf4aea18b1ca7bb011955bc3e5bcb8721c7a5e7ecf83dc146c9080befdfccc69 6fd4bdd08e827216d5e9b7f6a592ed12220a0a4db6c6d8ebc0385addf96a2fbdd9b2b6a7a481c815 49e552a253d58ea61845e3556a8aaf43169fd7fbcdfdfed4a71ba85ae58c0d8e6cf23ac23150107c 8e869821b88d8c57e91c3108fd2a33e4e1cc50cc63e032934b77c30497d6ae8b960192d12fc49b1d f8673cbf185c9b6c53b12d5a16a6737f1e730bde3d713d7add7213b1b3142a7c5141df4c11124f13 0b9a7e5ded758cca9d11ce5bb2c92b7981533aec845db73adc2aacc903f9888421ddaca93b7d7f2b 16259689273495eb29a4259fbbe24430213a89db7a6761f77441429e14a39dde502187d3457e7dd8 ef29051ef35fafbe4b0f453333dc8eba42efbb3035acb6b2a4da7d7a1066a793823a33e49685e8f4 f954bd3447e51090d4b9396401a08347f63d68d9a4ea2414294b90f6da8981b12bba3ab54d7c834c f398b526f6739f5fd2a5c70158ebe0fbfa5239341982a916827701a9361a4266f09a90af7e69ed0a d7fccb55057a42e1e0c1e7175456b7e4ea48a38e7bf630365e55d3c8239921cd035dbf092db643ad 79286a4cf8eab3cb7333fc5c835bbcfa884665969799465444c24747ce8222933a3b176b1060bf71 55a7f38dbad1696c9b9ab95caba3346263ab056cb185854abb01928e000cdc6c33cd3114bd31a22a d5479deff67261e0b0fc9b32f0cbe7bec0b807bcfbae975a3cbac74ed7cd9235bf6af66d5067e846 d3594c566f61f6bad15d7e573bd5d5167c87e67be004f063f45498e98cd226ade73de14b15638a16 8eaf3cc58ef9253fef0749f6b62b38b478ef7e7e6797efe2c8da9eec5586f66655894b0466c4be61 f84e6fb7e09433a3e26a54ba590853bedf0e9cddc8be1af3477bc6301b97e215b5dc093b2180f2dd 3dd4e1a82d172a085223b8405d911cb86a5244e89c3e27b7dcb059d5b9ba3ec5106e4a1c39a0d9d8 735af600e446b5dc9c43d89ecb150fa59dd541e75b6900675bec7ed94bc52618331132f8031bf6d0 f990b92eb6022e43107bda37f38d6ee9b61660f2bce2f6e8eb73021540474ed38434fb61d5572f92 87b50ea0ac3a9b134b949439515e97ba326c2ede2c35462748831305095af2127b6fde929cb1ea0f b80bfde8b0e3602c9a9410af8d7bc5a659a8ba94196a6b1adac2cfa85f51a773d90f8b7a67a9dc86 87363b917b64030c3359253fb7236e580a273ef1a69eaed715d76c61b68da44529975807c38899db f4d4c74deebd3726b2b16673c5f000df1a434b4b98ca9a15daa49edb0a934f38c8e7d75380b937da b27173fcba2f55883db39d0428fd7eee9f0efebc254cac9f91c13220122b7fdcf5d9c200771ab538 98aaa3554cb0af020778bbc2f01c6dd8fb8c59cdcc9b34aaf35658c8334dc62b601fa1e1ddfb74ea 3e1b94c3c4bdd3000e1f17d76294bec676fbca32776fe15bbe9e7b95e4d2be05e9f57924cf5bb334 4849c3209e0ab4849c3f7af4e7eff810bcaf55f9691ad4198190f47776ad73a2ef1487279f9b9022 3a91bb4e1aec5768705fc3c5dd692c0ae6a8b0a6d30430100b3d78811dcfe8835605bb2108aebf6b b7927293be9fd67de1e418fbccf2049f98185c3e45f758ee69836163c54d533b2e9cb1e73ad582c6 d7cdfb77e5ba107a6130ef5dd8f682dde6e981505c5417b562c7bfb0afe5981088ffc5d477a02dcb 05c9ae05242882198c2039e7ac9873ceeeffcafbcd3f7716e0637968fa545507d3ce243d4caf9683 1ce6336b478fb7e6dc9db3365c7d011696416db6b91f2dac56053f98b3ce5e681c17d9d15c75d7ac 8566df5c4983783ff5f4eb675fb2568637f0d5dd18f0a6505cb64a75d190c917da77492e799bce48 3a774dd9379c325a604c0105dae5c21d45ed63329c98eb88b8a5f0cd594697a2d7b12a36979ae5ed f0101ccacfbc9dc7b4b9a7365c8904e1c31a7431abadf2b69118f6ca2b4ebb8169847b0314a86dcb 28aa0067b5f9f9ddbb87df66f2768cba19a5b5a6b4011afbc8ebac28b3cc3daadde6b5bd09d6454d 31de1b655f2e20ca5f97eaed6415cda15c1f245a37da4d42b95e37d7d6d13666a7fd749410e9deac b75bafa134455fc98f210e8c537168377476db8b9afe8130f1115701fdde3c331a93df9182b7ebd0 73cf8134abdb3063b2d62e6fb89494876f6455b9ff362e4bcbe87cea96946eb3e3c75854666decdd 267df73c1261b3182259447ccca918cce58d2f680d493b043540622fe0c18332c7a3df06c5c91566 26b99054bed42ea5fb577e16ba79b79debeec2d98f52bea7ea7bd0bbfb7c6f1e3b8cdd0f957b1f85 3d986eae844ca36f6a193a9e9df7caa3da74afb957d55babe3fc24fda6385a950175732c3cede264 c4872653291b48c31979d50a34d39ace351fc4d4b390f65c00ec9d69fa9e78ba6e8ef64e95d55a35 9500dabaa14c5327a2c2518e84d7f14b43f0d8d67c69db1cd7b48e83c2c4251eb59a00fbd6d682a0 03e4108a32a01e9034312dddbbdb632ccb43a0385902881903f6c646287815ac3f3cedf9dabeec72 443752bd5bc8b80d74d570a8c3e73c7828d38b0315b61b5b23c6971abd4146b6a50992bdc7cd2e80 9d15416f1a16e766b50f6c038a5d090e2337af143a36b6faf0917347fdd28befdbcec7ec53d91980 dc93892776f1ee0ac54d5a993bc2d66c59918fd503b2d01c27cf06b176ae53dc506b33978dc6c352 c1dea126378870f7116c658db1217afdac354f72ee72f8cbfed5b57b36ea01c3e167ac73ec487219 ce3bfe4471168d261c8f39bfa8a8f33de78b24ed73bb6093a27ca5d4551f8ed6b567642d9b77aab8 5f545b8ef47876ad4efa6af9db05319b55f963df19cf6ab9a5aa5c4327c91aaf376507c2cc1d0c89 b73c228ad8d0da8d6ecd1a1a746621cdcc091b285423c0e47c5fd75bad65cc30c5d4285e0a65f1b2 f9964369a7682c636a33c9bf3e037f9f7695d6b41293f270dec827f27d64359c212540edc923c0bc 5a2419ba2ef144af8abd4c27e1d89229291133559935a7bfebee4c2e7ad2a519ab60f5fdf1b6843d ae00cf5ea80ea79fa9efd8dee9172ae79f6c3ccfc3081dd6d67a16d7f3e58aba241c6ec1e047d687 a7973ed66b1587f02ed12469f5e076592734d1f31a85f31ca9b589933ebcb722abdd013a8e0b9165 6b5cad0c82d5ade348c85bbb9b97822ef895757fdd83418634fb1e3676bf8b245fba4a9c9ff78959 e96fef9e830cea3998d30fccb813761573afe37882bb45d66dfa9148fa4355f76af62bbe515ef5fa b8b5b02b7eb55bfb387435a25f46f85937fdf390adcef22b3b45f99e06ab6c950b8fa0229656e284 fba926acbaeb7bde73e775773a6dba6bf522b9bc5de189ab7c845da631adb9dbc6a9fb4372c92dca 380dd6ab0ba71d74f2148d76efab4f14d1cdb0de2a51bf48db39eeacbada34bfd8330f687f0b3f86 ce7b9d08881b88ae751d0c7ef7c27ee2da73e6d919c53d87f2f76af524aa181024d4ed467a948439 dd0091d8281c2f00175ddc06c47efc05fd5df3d670e928b67224b91717b841f0858739550f5f9d63 06cfe5b6f794238f79b58fb5f43eef6aae3934cfcdad649971ea7e38677a6b11886fa696750bab67 aba795632394863d51c1713e55b3b4c89d0da82c956ad628eec060d0d68829216dc2693ef21b8e3e f74a59f1dcaa3c38545a51851772336133fb8169cfd27730a4e45d7f705058f0528c27a7241c8cac 4e4dc91b03c343b8fed6e35ac3541e2d761ba28f975cb81b744b658ec7a5e9a4fbc010be746e77eb 35a3dc4bfaf5678f4b00dad62e02b78b489a2db4ab03bda56d47333a5880df4a991e8c00edc26687 e06945ed1f92adf843d2fca6a041ae652eb84ae6d4429c1f550f95c1b0bd06cde2fa330a3ebd7493 77771bfc9b3276e8570a2a3dea0e35d7efa36173846f4625b96e149617ccf9d46328f69873ca753f a0671ff1d3353426cca70d16e39d5d584e65ff3b689cca724d6e5b9f760f0d4045757324f71f927a 5e8a48786f23ca581a0f7d30f8be22aabe2407f8cd89dd2c83f4809b85e3ba5611ab6ec1f60bbe46 e006645438d234d2716aa68f4fa46fb8011605d61b888bd54a9d2b43881d2cbb834e08d93784bacc ff7aa07cb37eacfbfaa06e97dd66a1e8e1cbf6cbe7e74e1eb7bbce0f4c154b0ad6ae287d7bb35b82 9d7021bc2e56d24f94589b88dddff000bc64833a8f4d3e61a93b377d980fcfd068f46c9a91569b9b 9931bee6cfc6e3a6fdf1be142ca3eb445eb3af6d874efdea6b191ca0c3969a010f3b81c387ec53da 502a6b4138f59a3790f681d30ace91e83f15ba3a4d2b13b8c78fe1ee531cf9c26acc23cfb83b5805 45897a4cb3dc17406bd6b5721ca9a0749c158789ae8435e069935f399a0c6ce346ef43855a8d560a 14ec1a1932e12a0c517edd94daebfd0b74bd386b91d75aef2737924fbc35f94631804552895e2f25 065e13f687643efee3d0a7752b7b41e1989fd4fcba168b5f6044a1954997ec5990a62e8e95a4507b bb952d6f6e34d32f47f1e0987ae0a085fdb8c0fcb9300b707bac20cef669ba61bc1e13954b8b7e66 6fc1b8847b2789f07ede12dfaaf70b2fc3cc9c5a74c09b78718f058011520d23bc14c4f70fcc22c8 c5b991951f0b8137162162cf7cb8966a2eb5eb1275786c1d6750336e3c2ca6720d4f6bcb6adcd611 bd622ee0e49ca246772ce52160620d40951f4c5ff3eedc041a9953a0461f2be4dc75db533dfe2e90 43abe6aa6b670a4c3b11d14ac8128a4b53fb89b197b0240f8a3f244bea8764e18da3d978c02bb00c 07419f2252100dac2ea23ab999e25383e41bcd5e87acf21c190d2f9a3f4a61762763f0837fd786a6 5e21b38ab501f9b4098af1f35bfc8cf08084e8adb5794498cdfa717c2b1b2d1c550e616d85b4c265 bbf228351f27d5f9bd59e520e4d5db9f730b2f73e776be1eb37761c74b4c679996975328d908d3b4 f3ba9fd8f878c2f94809365ce536ecc081d3eadae16019a805ccb59a86d786af66a3d167f8b9a15a 22378edc39b5c5debd7667bd150ff55a2e7127837969500717e7afc4555d61acfa83036cd6ba4549 301e9578e62d951f9271fb87a47f5b5477c88dd9cc99967cf1dac214f9892ff2b21d8e95a2a58dc6 1637e996b7dfee52e9d782e9b8d498b50000b9e45fa3a644e4185aa3690ac0fbb2d0b4c7663d836a afa8a7598b8156fb65ec0c5b76aff52a7841b4ea59f98e1af26c089f3f8d8ed61ac940dcbc2c901f 92ec77359f06d579b575b7985d99b8180bfa309c80db28efb321bf9f4164b40bca7024d1ca152f35 9bb251753a8d7465a93d0085b655bd76a65e465837f2a6f9a3658f1b5432ad8470d6b3da65c39a74 e8e2b881b68ff561817b5ab51ed64f6bd7ed0161d6cac2cc5c2acfd0910b96963f3093fc010d5633 311998cce519bc5db9502c6403ffb0a68a24bc76544ba8a7e312ddc219b753709a37e7956cc7160e 285b49d6e5d79537b2cf2f0560f5ea2650a58a3a8922e4d40b5c32bf3dbdefca7b8cc65a95a8cb21 e378d273774c9c2dd3404617dab1de91fb0defb4b3fb2199fec8ed89e6a65f6a0a311fd12c870b78 7c1d3fb34a44619ba9164cae543b6dbf88322eed0aa9773a8d1649ebd6cca93a909d460f3d8c9e57 6389218480bf3e46322d37971376921e7b112bf6e272f3a08d24259bd4a5647c09a7edce28214778 1359840bdf2e949c7d683ac6254772d0f11b6e4b5bae92cdecf77b223614320f01595901a65b2998 4f89a5ca8242909701476c445a96a160a93c2a4ca726799ea9d07671acd5a368b1b99dde8e9200b5 83d4b8555ff10eb214e5937600b9b0753095aad42e2a57f8519bbbb71d760eeedf8a0975739a6aca f1c41f60eadec4b49a0e5c84f326e6a6a4e5d3061f56dee275d5cc1ce431088d4263dd91cec48664 fbb3da56dff952285f9707b9861483b7bec82441ed2da6f05a49d59b13a81c24acea71dece1325ec a0688ba4188afb4cf71596d1554b333e65e9c9e05c6730a97ccc556fd050c0367d834d4d6c9af5c9 38e523b23198809a51f01d30728503b2ebd9d88c97dcfd725b173f3cd11e8e1f0aeb08c222b7ede4 eaa8fcacfed2306f47daef3d4b6687c3aa5b97370957dfbff8e301df8669557c8517e60e8a089234 e5a4507af833a8be957a46afd67e745f6b0f9c1f3bb2cc0b1e9c1dec85d557d919771a645286494c 3eec374a90062a5485f7c9a2195a8a6743b32e4ab4531a5696d8246c45be2a4dd2d2b83a5e42b21f a426add445d559ca37dc9b7d3ca2c9f7d6b359f058d142c69f273bc12fe1a10c9ba490d6946728de 4ab37c855e3bea1598e84e9ee73232590ee1ed99502dbebbdb734ab95a1b1bcca6fb8ba7e7895fd3 2bdd5c6eb0d164c72c35b1f81a769839b4f8519f3b759544c953ab06123e83c7a1be916f3f280b73 2e3505f2aee57e9a3a5d1095a0b6705851a7cf98fa28494f496e952129387d60ad433f246ad6841a 728ad05bed561ecf0b8a3abb2b624aa64ae00a8b11b75d1615bab2bf295fee2c1b571dae2873a100 a9025fe6e9035ff8eb4c695226a4beeba77e05d591ab3aae51aa4eb59bc8fca796eafab5d0fc28f0 c5defab84dd17a30fd128ad37d7e44a7a0ed75f114432a3e1a7854b9045d740e93aeeaf51db50a9b 2970d46dc1e365ecf5cda7f0520236277685a52c794e6892feec9428ab9dc56d85de4413ba06f9b1 1902bf7b076d3e14e250521573d0dda99af6bbfa6605193ef8c3d35191de3a6479e503c3ba9b4b97 9083fa4e1176e7e3d111b54f5ef452e86cf92067d777db168b7342adb6d42104b3bdb2d9682965a9 be78d792faadb70c5f2050911e2804eab5ceb3ec5f85032a27de3d1b8cd7c6c3ab4cc492a249739e 98508eeeb2f489531f923c9a9677f59cd98cc6d965297553457745baba88bf07b52483c0fe2bb4bb 54187a276d2defaf779e94d70fc2bf9b075ac920b70db51aa8670eda2021a6e5e331f6d2e72b1bf1 6f5f92f1bba9c5a7f23c0d2512945b1a997b9d83aececf63822a710ad6095522e8ba373f55eca2ea 83bdfa44e7d1f762b33c4ec41b16e1ce43854ed366cd07a4b115f4f943126cc6f0a577949d6b8893 b5ca0d4bea05f4abf028aa4374d07e99bc0be5dad3902e64231a4c444848fa52df68fd648e3a1da4 3771f19cbf8d2290397ddeb4b6d2b15fb48dc6fae996df62d2973302ea9be8a2c864000cf4e5208e 4d5dbb7c0c3b663f37a5b8aaae74a978ec717331ce739ae26b2ca65ba2d46c5e5fc1522d975f57a3 34235e601d0865f5830a336d52428590211fa05e0e26bcb63c9e7565870357ed5de883da7159d27b e36b6baa3d9fc59b5ebbaf8be5456966e8c555e166348857be457174ec21036b79ea1a5acdf5542b 75df030b988a7dadbdebd11cc8c192c99cbfbce6d4668fa6c1f9bcf1dc6d15bdfa660be04f0de846 75faadab81b03402269a836ebca54075c39e4a0a3caee30ef398e521a07e5bd749afcec5a9dd671c 57937c4f2ec7bf8c6ba315faa13b1f434c6fc4910d57f6d75187ee4530efa353df7fec958dea5ccc 01ab4d261f8faa1e65f5ea0241b3e57015b7d2189535db99aae02a66f2af31e849b5aedc9b51dd9f 30e02981df95995a8b1b7bd91c278770653e3055ab9276f7c04f52ff6ec933f5691ce4b2fbb4275e fdac1575acef98692b1e93597cb0deca494c255369016a7ae07617157b27f9d4378b9e4933a6374e 49b5d1acd62c68e02e8c9ba6a6e1f5520f7ca0cd8321afb0b5e28648d3ef5401673a242fb072a460 40c6991a3d1e98d3875afb1d45d77969628a5edfa83af620bbacb585afd7ccb2dcebd4f6e63a8b9d d7b1c66f90f9d64646c1c600f6e659781551d7924b8d13f38c3e4b093c5fca16d4be75eb93cb9991 0b9ffed04a76849aab8f8bb25c69a1a5ba05c83515ce97d53e2099ef1e4a89e3cf336f19911fb120 989e3a653ad54ddb510c6b6a9b366c122545f9f694abcc5be66823bad168b63535c76425d3dd3ba0 dee8bf069ade0e3f26f1102ca6d13a2c352ddc6d8d77e12cd61b876b470b2b2dd274677a2e3c8072 513ae8a8b3be98c47ed675acf11c364730d835b65bb1233c5d796f16d4c6c0b0d88ad09e14f08711 9539da08cdd3a224774b0763e2f967130e3fdb7049d10fb7cf754e86670f514d2ddc58a7769ce6b7 a7d15c1a243d398d56366e55df06bedbd2b52fe76d2df7291bc6646e2f00b9c76f7519b92f8d8179 d8db279995fcdd16350cc42ec982307a915ef3b178ea079b11db1c4e1e9c974a14f5f73ce34ba2ba c7ffda47abad92613ee66a88283d373e49e2507f3e2a03f5134072b82346457d7c4e57f4c0ae89fe 932ce87abcdbdc6a076a97795fe90d1884aa6c80f43d9bebfe285f15b527bf766ea6a77ba056d1a3 d322f76c04a8875d62897dfee4a05758b431b3710d77e5b8aa07b3dba124121ee8ec4928332acf73 277041ed306dc891a5c7fbc3f817b3913de61299d2f9d635a0a146014f510e6174b6416bb53117b8 e17ceae5668abeebae65e0c719457d3ceaa21e8fcff6a6e8359b02955c49b74fbd8a9c767a80625b b22087db172172bc0433a9378a6c07d97797c8073ccca539f11378676591787ac565a5c3a42fb860 2d30e575a9971b5df2f02dc70e0e1f477da12db1f2a5c06c6d5be385ca69df9e2a4234601db1f3cd 29e54cd2e2f21971f6b5ded3d89185b70671f399fd4abd097b583e861ab8921636d9bd7164f97af4 3542c2631bee0e72728bdc931da275d5c836b6da2b76e39b53379b37bf69dfaaf79b5c98566bc66c 502dd8e88b167a97bddd367ae041b6027953ae44dbd6dc20eadba74df794fc0d9ae69d57ebccdedd 0dd94075a6673f9e683e8567c37b0a627faf40c57af9b162b1aedd6a9d3a2bc16a8fa8aa45aa7884 224847338fafe5d7587cbfb8f3ad566ddfa6be031ba71f96e43c9195879d0bc1efb3c75ecf2b2263 e75813cbe6ed817b15bb85e71cdae117f2d9720ce39e83c9e79a67a2dd227d571f71c03b1c8be58a 6530c3c9f0f5aaeffc1b313899a7cf1e68a5e432f16668e1614e17af0cc5a16adbee1621c6985e1f 1bc7acbddea9b7ea2eac1fcfc87b0725b24dbf62dea06be607a963bdeed4f6c3e00bfaa6337a4815 896ddefd8b886216bc3af57324d5dca294ade7286ce9ec7e1a8cebe2a367fdf84f77986d76b3e4c5 2caf66085785968230645c6c01f9122053fc2e11141fd26bbbdbabb17f52c80266a702ef8acb43b0 582e2801ddf30521aceb559f464e50c70c9fa8e8c7eedaa347e01ae791cb531aba93955f159e796b 5c72d43acc7814a4cb29a18203214f36d2bede2bfa1d1d2ad293d6eb22f7afabb1876b3f1d65eedf 7bf9148b6777a7bd0cc82be0a1529eec3b46f975e89a1efb2caa1e4c30fed0fa687cbaa4176a50fd dd56a5691deac0f20951b31be0baedfe252fe1e1e55622a887ece3b8877adfcec1e4c6209df9e6b9 822ac3fd4f20b1e964eab5f408a45bafdd4d7f2dc4994bd4f87d83b28b437d65b282b39f1ebbd092 5cb2fa6203920676c5abc6ab37086c6fede7ccc6d3e116cb1376f0b1667df9e95254d668af0d37b2 0028151c59022638563974cc83d6283a07d5307324d10f4917f4e6d397293fd1f9c8bdce9bb06b35 9af4e0b698f2ce84a330477ec574a3624d72fbc1bef7eb99830d13127adfa199b9afdd33032392a2 e1441d34e4cec0cdbda6e89c3b77ca84bf7a568ecec46ab5da1e41fb9ed64efaf625330f78797900 5caf84044e357bae7324ce0f09197b0393ccffd94e9edc06e3b892f215b77c9bd40663b6a287e2a1 d77710d17dd73f9beb32601fe5bb9da1971d8c9eef33f3da7e0d8d52d42dfcb2fb3a1ded7adba99b 36a4369706d375923dab9bdfb5b52fb6874da91a935f2b6f4eb2a3e3e68417e826ee052192d88b67 3b776e13f3a79aef83645d7267c26d73d0f9972b66d1a55bc47be2743e162e454e08bbd8635bddcd 54585ce80b24841a780c1a3a6448bdc191d0ad92745755a9948f2b489454c4e3c279541d36f6de5a 5a5a811f328cff21abefca56ee629f6bd06ac754491d5027d95b5c48ffe3bef3e294dffa21b9f662 b69245c28c9add55fcddc543bf781bf408a5a6a9cd71c20798d2cf0b2bd560b660547e2798fee121 f4c0e5659ea867551cffb8137b51c166fad03fe3b7157e94f782d9256a578f84061b74efdb5debf5 28d5756eb0ecf98d3a6d952c278474b66086de6275cead75dff82139e63d5091dc8749a13f344e96 a89bdd607a7980dd6b137899b3b53df0493e5b57ddf9686b0ea793ab771e0d8802128ece86ad3703 dd076698323a97c7aef38a0a610731178c572c198ef223d07e24d656ad0371fe9b268aaae4c3d3cc 5ba734ba4f2f7673b1e8ba7745c8f3ad3ff981d99dc23d89eb42d97dffde25496282cabc1e77675c b9ed91d651f326bd48fd2109bbeeb81a2cbce660f02cf4fbfdaf3151755c0f8c691e698ad25b29d1 0609dec131f16e8cc4748410ef7560bf7ec1f11f315ed20166a5430f267bafd2bc52deb950c0032e 6f25518e64f743b25985feb234e6bfdfa79c3492efc37f2c30a3ebb4e04eb4f7e8fc22f0c4887f57 c575da0f4e2db2e09ed12f51e0767e682c9e4aac87c30e2ba2ebc0e07b4da23e46c36aab6f9e6f07 a13e3debc9dd7f371b55e4618a70fb388ca35a638258b54b2a7608c40e5ddd7dfd90d8e63fe75611 c7f2194786df97054835b9514b1bdc23a03e89004b63ea528c05ca50f12dbf3dcba5c3e9126be962 0500f7fa456922d362deb40b8aa5c93453d20b91a5d39a63f54d92c194997860e2c8d8e67e5aa354 3c00cab5200e22c62413249b303f1eec9ad540ae2fab7f607e48a4da8896f1f97061d674bd49cdbc 0403dd3375e92f53ed55813f51b7f3ede297f7e4aa5dcb061a55a10606080b7da0a3edcbf57f3a88 41e1dc995b167c1a34924d57fdf4e962fd6172279f8c322cdd3730d6278dc792e4c2a10d76512080 5bc67a2f89be3c9ce9ff211146a923f5d4610c5779077b5fb598efed716a1307b9fd6077a8a114da 1ab1260062c2db851fa7095b838502cc1163a9c74b16d72bed4255585260e6976cf19cb49042a94f 797bdc3db71939225628d880d79ae356f55110ccef4182927259b0875c1ff4368f71fd9f736be7d1 c2df5344197787f9ea97209b3bfbe87dde5bd40cac687e5822d8109b3d32fc8b3e4b9e55fcae8214 6f1781174755f4f5eaebe9b5692a0ba33584c449bf44c5ef3ad0ecd7824335aacac39c0e86e7da3d adbfe7ab7558aaede1806fd702b4f7595c6c01dbcc3d364dd01f1207d2f1e38b987d6cc9efaf8b44 9177f7e470c23808d794c34141186a2573dcf1cc5bc99587bcb0df44d5310847f9661b339f61972f 40e9fbf78086aff4648ac919ee4e0e3a32695f699691d0b1058e452b122ab76b15916ca1b41835d1 455a780c8593342edfd39ffa82acff7940c7cf76ea2bf07b004d3e7d853ca3e2f846e9cadfe0ffee c62aec9103d2fdc20a4a27b8b256d4065248058af9853da2a49acc555fff8304ec7bda3e24f6d928 e437ed172e6a9a5381eaa30a8f30557cac153429198f92adfb6a433f01b1d46cddf9db6ac510adcb ff8289264e09a9fdc09cd62638fd29904be84c9a4bc4f70d47952ac95c2778acb15b1e0d796bc549 756de4830092a5e3bb7cbb6d8ee4dba4a7f662dab6c787d0a43ba07aca6b04d625b88629f3ad25d5 1ed8942dc5edbfe32321d7a0f5aef234b506ccf9d5e77ef01f926f233b463b7d807397952b4d0271 d463264973031b8293a8683dc1c8be84f102b0748ac1d98f3970a5ffebee163bff1f0c0bb7423dc0 d58138e685b4db812f45d87b5bfb5bf2286c8bd5de69ce7a8a334263c6b34fd0a7702c9afbee77e2 c39d25f1bf48eccc622c7650af43d390390939e518c16584692e8e8c1764b3d62ade3e6f3b8c7304 c5d59e837af4f8b67e373bc21bff1fc992d8ef008e45c2bbb8b05435b8234ae1275f84aee4df9e2b 199b5fcfa22e299a6c0e8bc376cf94f2ae21699d2c8a0abb3a4585cf3db4e56196d5855f26be8fbe 8a0d49bba17d151b12239a7ee35192dde85d96a8e3a4ca486f72aa74773e2f0f98c5bc121699aa72 d14b37e5b1b972f39573c0344e46bafc62202b7f3bba3a25531bd6b04478453750aaea6e43932e05 4aaae19645a5e7624ff3aa5b4aaeede936d44d9c405b4ff83dd76a74d769a6ae6fe6f9c0b7f8f475 130daf7750cc16f6c5844f6a2bf47c7e628d05cde79b2ca436ef2615e6f553867a735554ca0e7d9c 293c1d7845b78c71acb6a2bc316452ce94b850fc865b0622377c9e6df3d406c4e60cbe52adf6ec6b 859942497d9c3c42e6026f9a62364b59189866498c2e73721becd19f1a4a3bbb8a9e2167c57b8a8d 93503e9f4cba8a5aa94b57a613d1818640a5caee1cc77fddbbb2a3bc88a9f2e94e521b6e0a3fc0a9 eaee079745f455ef471eb6e775611a509d6012f41642d026fe0a2b9742b7e37dc1fa5a9c32200c8d 96d39619a5e7e9907e4c86f1057b71e3a0621f38e0244df402b1982563728bfe284b6934c8ac2088 9afad3145177f2aa806326f2b71913c9d556c84e161ba63e771d22c847e5bf8bb3ea92fa763ea9a2 aec885e07e2b34b43818211aff161a1172265d64ba8a5d37bb8a8336d583969ff2c0cc1a9aa1145e 8d795ca1d71b1e602557d1fd2fa1699651100c1c3c29afe9fc39809b78be3f4d3c4fc58aea0ada18 0f46f15dba7ec8b176ef71cf6cd38145f1b3875e72ebec628e54e0d7d2a6745bc98746bae4d512d1 901d603b53ac6ba2b55c05592addc9b0ad76a166131c3125452d4d6a1ff1ba7ce6193a828f91a0ae c55224cddc6657b5b946413d3f98beac03cfcd8fa02c361aba06ea4afff448f141d49c6b5d228cd4 ef7bcf8cd58c8c4c217c9fc4983f20f6e0a4bbc69974be920a85203702ce57430ccc9ca7c9eca1dc 6e2166053108d1a694bad83c81ef4e7dab5f497d27e87bea1d3c98eac85157e3ad686aee5b19237a d7e65a9f9a24dda46f6fd92bb896c6792b5954e73a5ed5c5d8aa212f5b1de0d3da48a48e7f4be782 0fa20abee0d396fa4115ef6ef434d17dadab5ca18c965c0e5856a560bcdc34e76954713a6663294b 8b6dbd509bb3a61171f73a1fdeb96f50c54f931878b70c619e0480020e753b38ee8da6b85e88db7c 2fc74073e73e3c642069bfa0de3822d24777961c65e5983acf74745a9ec6f6304af907b309cdcb45 6393b3e45644803db458fb3edd4652a26c24bc04d79bda1ad18353ba3a488f3b2315069bc14ff186 bd3ca7f1187918f82337b027ddc5f3227457e593ec0a3133a293ea58144a8cd97d733e966087e82d fdf8b35ebe8fcd9767cd3e5fc5f51e972472f922fb1d263ded30ecd68c27e091bcc3ec34edbc9adc 870b3dcc9bc88507612c748480860d1ec65ca90ecf2afa2c700100879ebf24d68c30357b80a8c78f 9717f17b8ee7eab35a63246334a3a535d95969b50ffce95c22b22efb22486a912f9431e20aab4a19 91397d792b647f13926bfaa19632f8a47c91f9d1806ecf83da4b0041ed0d2a0cf3d20a6bd52cb768 758efbe7faf92154d4f555286a123133004bfeb435a56e76950230fbdd546da66b882671560c4614 c5f18a3c1885fa2af73a95fbc1233b34e801fa5a8b6eaa39f649ace020b2be76ef77edbddead2385 3a779cc25112e4c571f2d2090133ed72061e94fa7cea31f409662c6a37a79435a514eb4eb9a198b3 f2f5a52a385e07768dfef4afa92fee16a577e52e397af623f933a98dca5e70bc89e0ab12b9bde97b a760951bdbbecbeedb29efb88ab27ac8ebd2ebdbebda7f73a762bd8784e914f844f329844b2f31db 6aaef24082a319c9b2d5aae6bf863e3d6dc42f16bc9a52bddf7bf5c107bb7b58a3922a9be3a10f82 88dcd1f7d01a970618f1b2dfe12149f67af32c7de74557f0bbb81ad95dbf244767546b07013c0a76 d3794719883c51ba532bd8ad6a6ade41ac41773f0e8bd74e7f525edd11c994aba256655fc648b217 0d1927760bda56422b811f7b423eb4a44bbd0cd737e1f42aa68ac0a41688cfb5997eeb493b33fc30 65cbd9592b2e89eb77b37b764abc3c5de5054901e0c7aa09461d98d266f3b23800a88159f69922ba 5e40bef8ceec921e7303d077ef0754ac3df19aa1b7077565d67a952568d798187d3bb90cc8b7a748 8782d233fa98c25543a27596ddeb796e12b560fa8fdcfa949a5cb64f7df29d8ecc993eb39531f742 f591afbdb9f163dc54ee35fda047f337419ea407a9927a73a25f7cdc4547c8ce50f9ad56d7d562a9 e9596b72ad1f91d65087969b857c0c01420f2697fcbed13e2f5feb2747b7a5336ed0d08928f2aafd aa9bea9d2f1be9974c6afc90b8e77cb35f5913a3a7669c6a40cf5ace97b066b1238783b532687e1b a5b9e6575714a91f2e89299069aa3dc16a1dcd0e764e39cc9fee2bebb4881cdca9d59a787d0c52b5 e26af591a9f60575cb85b3af55b01ad66ff0896bbfd9abae71fd6da55aba80639b0fd09a0ec6d3dc d670ae3f249b911af740c118a06c39489d68aeee8abdbc20c9861360ee833fdda3bebe13826c5e23 d55ddd7faf1d7d5d5ed0e9fbccdbe32d8fe854a50db9b83d8912237e19aaa9cd03e964624ca42bb4 a926e327de3bbf452c18977745f5d14284ca6b7a03fd2182e613929aba5ee5fd4e0ef503b340d406 18cff4b7907e476dd2eaa852b9b0657b4e2226c09090d588f6c0d6d75f37236ec83dd5ef68da4633 abd1b447a5f640c7e693a2ddaf5a003b6d4de74eab3d97c47adbca2b853c0e263fb2984e6e5dad60 550561a16236fa341d82d21b9ea8ec1f5da7501cbb7f94526d8d0ba1ed001cf93b57e324f68e196d bd57c69d494ff156427dc4b6f46c386b0edf62493ab140db72712827b770fb6b4d659c7f7e7ebf06 e42c7f9bd6e58f8e8496b50eee821f6157a56f5b570bbcd42f9d8be94c9558c43ee63adcbcf0cbeb d055ee57a364e9d735f53fe4f62c572d8ce8456a6bf5e9ea486197df9ee6f844920cbc8818ede42f 1766f72bd71b5f04bf68db51a3668a237308b3e29bd4ee545bfd23b7e6fa61f72ceaa0754d3e3147 fc7d0b68662225a0f194cbad8edd600c13787ef7c6fa52e4f09dabe7bd1cc6169b7c4cbe89fea755 cf2c6096d6fc49c98618ebac655c33d6e3418f4e4ad7be3ddd3d8786825297c6f60638d66371191a 61dcb061b33dab9bb74fd1f987a4e542b7a071def58d31dacfed07de3d8ab1b7fd314fa3d729e29d e67c7e77cdc3a2615477d0039f17d5c0b95c9b883129a1e27f48fa8e619f3b6b857993e5a826f62a 4687005f341d3c5e8156b98afab7fc2c3682f4c4f8eaa59c97be0dbcb6b6e19f34df58adf7c7fa03 63ec5a819eaee2d2dae817fa34dfdd960ef172b9c2f4d73815db5ff19a46d5c101d50fc74f178f5b 6ecbfbbeae65a343f6b3ff9020aa1ff1212c955d29b7edb8fee975f5ac8f21f7f54607e11db9da73 7db175adad292c102688cfba91d02f1788e647169bf20bfcc9eef7548b3a2c2938b5a7e6e1beff61 a95ac288ec2e5bbb0df4d3a54a0f7424e1ed96eaa44a256f51c4b8a6614ac97759757618b5fb03a3 ddc0a3e35e1b43599c5471589e2e20db7993f0bb0f351e3fda726ee34eed5dddd4840f3951d8783a 733ac0292bf4fb3f6e7d9090f7df99a857e7fdd20c10cbc9ad739a64bd611a7c408dff2e5dfbe5d8 21395ff540adbf3fb56ddd066b5847f5dada403969b675689dfeb3351e1367d95fbf4574d46c9bd5 8337b7ef17d8ecc52f2c3046a881d9d55752a85103f4fb57f76caca736b93777058bfeec8d029b1c fe81e1cbc5a2d379636bfbc500f4b0d356385b1826825d0e4092b42b76d70a596561a56b8fc3064f 796609fa656a8d757bfd1f92cbd6feb6da79b158586bf2da93406a6053151aebb127feeb92ac0e59 f6899ad52af36fc1f1ae8783e5128576215ba890f15260f71f920297c661ba3ddd6d85810f434824 c741d5ad1cad945a37c901a83cbdef07ce97355a0d98f5b1ae049b2e178dc616beeba8ff8139aeec 64ddc705775b18c594d337adc7969df788347e85cb53d7b19a4e72af7e4f4b3ee83ddfa85539969e 8565af989a40cdfaf78024c695f2e10b56be17d0a85461b041f213abdc34538360528dd0660d35bb fcf1b21afab7eb9642cd5b92086c032b79f7ea42fb172a169140e1f9a66d38b4675484e4bea30323 3e68ede7a4af894285defadca7963f1b62e25f8e12fc82537f719f1d01f5bd646560ffdcfd03839c 9c50869c161318e2b332105ea3a92c6417dc6fd4ee64e31ee396bcaa7ffb9eb96b9f513368cbf23e ee30aeec5dd1ffcec4ca1b95039fc82cd643a7897aa41e944f15a6e3f688b8bed54d7a2a7a951e35 20226caba8fbf9e8edf1100d016be06c6b0352f9172a6238511746f48d4d5f26c6eb01a465bad1ac 072baf62264863d350fec6b14f07d87035f29ba0a3fbfbadefeb43db110dfd7fa345cbfc64390ad8 b6374dac7bc7eb7a6297a9b7a5d9b36135239075079f924ac4466c9a47ecd974a5a2dc05a1bed7d1 0f1af6cfa2ccd51a2dbdbeee2ff17cbd9903b3fda38fcace89a023d71cadaa8d51b334708a0f9874 56775c454f90d7b18b32deb797d771fa1f12c5f1519beab1f8c64cfd143eb4dcfb85bfb53b3b1af3 9c2ff1b75a7fb7bcef09b7af21ee8e56eacebef648c126417ff597b1acff3b16ac55df44de9d5c7a f24057fb13a6c3850dae90b90deb366a9866f2d3cc09a63a12b1b8a1bb3eb4b7176a87b6e9fab0fa 7f9cdbd27ed476cb6bbad8a156ecfefcf924f27d75698d3071cdbd02598b41a44c638be079e757cf ca25e6d2e2190090ee5a6abcdef4df6a01aec244077ebf868544c256a72ee4508410795c1aad078d 9c0b543ffadc14078d3d1721219842cbefbe28eac8d3743df2af66f7772c252f49fc768f46fb7228 cd9a94131d05056d857326958b59791f6a23ec1728b5dd4a661cc10ec7a5bfddce8db9da71ff0663 fef94e27f67e529b1a0bc74415333a7b9378aacdcbec1a2afdd5a4fa7eb5de2a754ad460cc220b18 a93113b55759902e381bfce73b9d5034d6cd6373b04b5e777dc46440b86a1cc19605e3f990acce56 0a956058a14cec79c1aa7a4b801701ffdde42bafabe8ff71c0c61b03b568da1d4764fa803a9327bc 3467c3463908d1a1fe43828fccee0bb2fd592573e0c1ad671b9bf10372e6ddbaf43fce6d0e063e44 b385721e2c94f5dce9f41531c422edd41223cdb11d419dfb13f7782917d0f2d36e57a9d4f74b6b39 dfb153fd3f48fc4dd9f73e86720ba7a526dd490c5e77df9b45fe77880136acdcabd7f436707f0a7f efd7cfb33dac7f3dfc27ba679fbfd543ff2181ae116379abc1ccd6c2803fad7bc11e1d69ad3e88f2 fe649e807e7febf5cb805f5cbbd75bb3e123057f0f009db6faf76ceec9bfab799029436cf87c33bd ec81f535b201f4161c943deaa34fc77ce1c1c642b867745046c9654980ad545679a7180e9c490426 ff1983a93a29c504daedf05790df8c716ff42eddee7f5be2214c118c1781a4dae03541e543f521ac f0592f096fc3f6ef58b054396ebfe57f489ea7022a7e8fd17cdc21f8292984eb8bc402bc91763675 1b3f202b50ca0e9b6e32bf6e48702c75fa3241fde534a7d28bfed7a54c81f13cde8bdd965b4f146f 4665a9bdfa56ea5c7b062ad1a782c48ffb6b845ef65544f1264d26068e2192af70f9e5141495fe21 b96a5f4087c474fa5325c58494e26f6e106b6ba9554c2022ce88623dfee9ed1479c4bd193929604c 70d196f0f16a5f31d0ff5f24e551d62c4ebb1d637235b110c892b03ab1ebf2b4b936a634778f1e64 48179b54933642aaab85f70fd4fbe7dcc2adff1f2d8327d68b6cff7180d2811d17494d5397d6adb7 7ac6c352b02588fee363a5169d4672fdce1778a223194bd84aecf9082bfd2f1273844c97932ee38f 3db76a04394f4b30d745ead2312c3aa32df78a4403328bed7999b7435327c376bc627f480a97ff83 e415925f5f3b551ec9f73a6348b5eedfbd090c9c63b87af089a2396b78a0b0bd47103bc10ab208e7 fd69c60632deb603a2edff0173adf4f839337ec519f469b11da9f415d6df01af17b7ab1b5f7bf055 f18834a141041a5be13c2a5da54bff6c10b56e339588c32655563b7a3fc123a02434a91ec91738f9 6f9dee3dacb7441e8456420f7e9404a0d87e49aa4b49a207477b122a4d1bb262915d49f870ed42cd c29f4aad4d922c669fb34881ca23a5ffc9586ebc6f131a744b9bca8c8629010b00afbf833ab65af9 18f9eb294aa5d7193f629caf5aed5145de0207783c179645fddeeeb459707080ede5c523f4e463bc b99874f63ceeb263fd17a9ba501bf74b2debf3e5757a8bc962d16cf50aca9877f5e145d933dd5da5 119270966f83b36b2848b2951a58574be51761d1e5b7c61d9058e8d731646b46686123d0934a014f d73261f6346f21a3a73019795b72ec776a34c58ca9f7cc5a3b75dedd30518fd5bda2c089f5007514 486df0cd48cc8b5e2dc2e187b6b499d704bc776c1452809819f79743d0c47db60de837d10bb725d4 1e12bb52a8d0f89af30f53e5c57ebd8ddbcb1aced4abb602960facc50b374e9bae5bcdd6a61414ee 7e3aef2d951165eaf9b3a1f7e096b68aaf8e142f76cdca70c1ef62f658cda450a52e1b2ec88e68f3 d0bd9ffc4f69a1f17ac17d15566bca33c101430e74f6fdf5e7fd439001acd663ec630f963743f699 361968cbaaadaedeeb88726e4246df5637e4b1cf30c2e9219679db923595d8efa795360b4379887c a49eb458cfaf66e8260007ab9b44e6a50accb2b136e637433151ca58d368bc71fa26d21db7aaac0b 8205c4c2e620d9f86b2916da8f5ce2fa60c876046d598ec51729b4e44aa640a2572928d276e2905d f354bb49f173d995c7b2b5c50e95c142e61101549701822420a006b23dfde28266f39411a9e38e52 0090b7a8d3bdefd0a98796622ca679a550320f43b62129d795f2b9fff44bdd1b55814f3827d42d8e c0dca33aa9b92beb18e8459e24851af999482d7e7ad79e12c0897d546f754e2cb5d16e7d6b2e0d77 efdaef96dab8dabb01282a59bc40d156e9e75f63658cef7357f4bbd043bcfb353fdfefe617f6d698 392cd1923948fa5591d9d59f0d18ec6c8cebf2eb8b5f853f82e44d95f58fbe99b08ea64c9d4301fc a916188bb8afb05988ca87949cdee734173ad756ae3d3bdcaec3daed3dec8b8ad7b031634535addb 943794535426c3fb807743735593d9535b6f6bd76c23fbeb5bf4fb69d20b653acf5ed7abdc0e9ee0 3fb27e7d6d740417efb6876223350d50aa3a5da374f0f29c361cbf94add37c8a429cb1970f47560f 4fe1feecdf439ba4283ed9dc1aedcf7986f9fb1806847307b431319233d744555c896ec229ec19fd 6c7c5a0003963ac0a0267dc575dadbd111379d5d28fa022c72aa1e7db89a21e0d66258b7eaf22ac4 ab6622cc5ffd06689f5fae5163361bb588146a360345abe1fb36f455de5a2d857a43d973d34af656 27e36ebf5d21cd8930b805b4564b43bfe86a9426763cc6cf17ede45d43411bd27fc962e4ef9462b9 e6a9527f600963a32e290e4fd706f789d21597c315ad16774db6d6865f35699e49b0ba845df22fd9 2a819721d21879d42d4ee8b4e42e1468323bb3befcf033fd49b4d035fed6e614df346529d94ca181 a7a6a4de3a2c8a71d356123c4272d58cf8e86c53d604b7d49748ea36fc85d082d28425e44837f7d2 1e90d268ac71772792c7a3c2a37a7e98674d6b5feb4a9f8eb2ffc8adf63cf7ef22c4cc0593ed165c d3fccc86e28bea57b8e0ab6f8db73c62a419c3b354e109770d7bfa99cbd671d52b0e1edfad615941 4d071b3ffe4fc8c5af5bb8b20b1160114a9e85a59153785c11f138fd5baddf9fd6bc9e0d6b83549a f7c96d35e6ca174bdbf80399cb6e00509b5ffb7a5b3ed8fca79661c6ac29d57d8b7fe22244101c87 943e570fa270517cf6ca3b32bbcd51e75410586903f848b1b7351676d22de66d70da382c89aec19a 783c68ca7ba1d5bf4f642af3cc503e8fc762bd0311fdfe7af8f0278b7659aabc276c556e73736f7c 6a29d2ee224e01a67becea6c74927816db6f8cdefafc48f1e201114cececb2139839e791f64487b4 f863870829d2b01a2a216948c3ba87165b42b3ea14a817aca96ab1e90c78fccc5c0bd14edf75c093 8469b70b9b99f6585ff4c851af5b7dd7f9aeabecf5c31c285640f5d116e27d141bd34dc5f88fdc2a a567c668cee473d11e7e10f35b9986b419106843950d75e1527f85daf1156f5aa5dd84161fc64ed3 3b35b4067fa6fc41623a6a1eb737db6691ef58925b15529d05b7b3488b775986efafbff99b47c907 bae1fb5c92a3bd56d4da939f1a0d66c84d29778b57bd44b2f43fd56c9c672765f4da1bea23293dd4 357f79a8c8f08330cbdd1b5477ec945187a70bdf4c1fd2557df030a79ea3ab8c145a7f6d701a3cb3 877f8cc5127f62d9b8c496adb443ee2578c9a16b58b8c32b619fad76cb5e7761608e38578b28b321 b80d7cd79fe9a2adbe188ffbcf779248993f9d872a199d183ba48f53f935d6f2492fa6ee5f7e3940 16860a771caa4d7acfe216ea489cf26ccdda089c61a8a9327af79f1d373f97b11f33cc12b9bfd706 023a79f2eeba7d90e5cd44823bf30efe7522e02c280c084a04e53bb18383edbc0146259f7bed3f30 c3af142e2149d9de142ec4eeec54ee88e29cdebfcc8d6f210d44de827bb0897cab9ca763235919ce e81252fc926dcbb7bdf53f246d7bd04dd2b7c84a6bf6bee39788979bf7d1420a2b32dd5c791d0de5 e410836159beb36787288d9e25bfde5de9caa1b2f2fe435267acd8d278b98992e270227efa560790 d1815cb708ae434723f3d96e0eeae13756f899fa402df2fc67421636c857119829fae395a7535f0f 2fe72d77860e81c90ced0257490d51281c29cbc48a8d39f55cb46111155698596a2d0be53a34af89 4770a59859f5faaf554343fabf50d47ad38fe498765bea3b78e3ef4f514748b97fa49a67e94a5caf 465b87827ac5fbb665ad55618d64aae085f31b76e42358ce07fa4b5d2dc3f5b35a8039489fbec494 159ea54c6d420f560fbac3885238f0a4324c3bd4a78d9788bd375aae6f54cda65c635815a3ff8e05 a4b50f0076c5d5e705e86f7ef1d021da92fb035b3ee86935417460b5a9d76e2b64a1cbb4b7d2c50b cc4230dc5eeb92f721ff1e90060e677bebab4523cd1d8ef335d4c39b77dd5a4cedc96bceac56a708 50d6cd6cb88bb4d9aa8461f77956322de8b1d5d5c7c5fd4f1ebecaea77243c44860b34f7a7367cad dea73bbdd70d2938dd2d61689d4286d5a69bf9c50e4b85fc6f76b4a4370c20c23f36cdf64451fed9 71ce4a9002bb1777d5e3fdb81f06c3cddcafbff492fa44ff1f5957b9a03cb66c9f25102c81008110 7777c5dd09aeef7fc9d7d33373e6fe6e422ff6ae94ac32bd859d1a5837cc5b1a6ed66d6c00a56d6a ea3faccacabc1da4e62f92ab60188b16a514e668e6727409b44a1bf30d7da7a6d8208c97f181330e c7e6d75ef60774345c5408b381cd61b00955670e8594667f9068ae5f85b8fe79160554b75864c793 ef2de1cb4ac78f4607b55db9f388381d69399fdc39d92ebcb20917ee52c836e67e41abc03f19877e ee59f72d43b724d09013a93ab8bcbc8b222744fafc3a040dc80c3c85253d2430d689525161c2bbf9 1f15f8d46645e5ba26328ab220ab0f8e7214a38c662d581ef739256c2748be9fee8df7eeb676815a b3c976a44e4fa7bddb9f82f3324ff15df5933b3e5c172cb2bfc792e0eea1569e8b4cef86ea4e899d ba427b1810720b1dea63ef6b209fafb78b30333f2353f473dc275d59ddb5722d9eae187ef24c7e88 41b272bc586dbfb97673214930d30f9eb34ab22b3b3e1bbf5be6683e374f7dc673d403b92f8be975 6d1e2a23c431c87bef17892f3849a799898070524dd659139cee5095ad4c94ceef93d33add05fbd6 3d7d107ab9a9dafbf34975e8a2e0e47454c06db8301fff306047a33af5875473e3c096e233ea1054 bc437d39b16739336d91d852f34af56ec640d9c98163cbe6abf9bd54b6dbb7d7fa3cf70bc6fed867 558b85de7e990b931857ecf1a815e0877def1bb00a55d0e6168f14a9de2ead00b837c86cdfae930b e1b36c37d7cd9ff85df176abac8f20b13bdec39e54b638d39cacacb865db91cd74b7af5679b23844 141ec776936e8ecaea56a47ce4bc896d7e0a26bf48daef78634d5e5c1b7fbff9ce0255a28575b6b0 c97ada13461efe086f6298d1ddf02a44227157c9f1d1f0bd76bf5173199767772dabc10734a931bf 0962037e685113482e546e75ba48447d780d09955a7e55c16e22b7666f200444282e74876151f676 cba1d76d0ee0bf90a8d72c911feeb7870efbe81c5d85be124a70fe3a0c98ce52b47283dbd3000926 1578581c01aa889ca7c16c9c6475e66f5f2fc883e8cfb188c9916f6b8b32d7084695f99a0c4786a5 e7e049e82f5669a3316397599185de6a3c76be056ef1c2c28f6d1d5dd805f7741aa7bfc752ab05aa 4c09ac614d46a69cd6253fb9636fac91944f26d53ef8dea572abc3fd5172375b3b19f6995d271bc1 7a93cdb9be99fd32b72266a99e93cfa7733f2cbf36241908b0dd3785a1b7e89ed5467c9a356dd234 244f79fbf5c2d59d8b764965c7aebc246ebf4820d7d7d4e692c51592f39a904c79ebd25a6b7fea50 cf0d092df3d33ce8a0b3f06868932e5defd73db96e8540edc81d2dcca07e727622805e9560ff3cba de2dff6e91f0a6fef4dfc5fed6ab2e5f938615dbac2f34b1bbbb79ec2e85cfd9d35db7fc419cf3b8 5cfd17735ba6bc8f1f512c0c8256a454e8b6a7d2f6b0bd1def98b0cb6867775c0d8e70303f0181b1 1a74dd037f65818e934f2d9a6d427f9070f9837be16afbcfa0672589da314650918f35b1de6d7792 2cc4aded0b302eec2e3edcad2dc4305f023943d4cf36e25e0ac5c58f419c1a43b157baedb694f6c4 0391ec4a48b25c253cca413127e173f7111fc744b3dc478fb2748817cfac9922cb9b5c2e9ab15e41 bfcc2dfbc0c99b2c6d0228890bc55c47ba96e64a516f7b31d7ed546b7b011e2a09133de37a48cb79 b1a18d94bb252fdcbae0ee7e79a745317180fe9e2a5d9c9186de936b8ce66107c5c2e59f4501a696 5851475f8ccabb5af8d4422858848fc79f52f3c3d04417c8e607c9a0e12c8d6405b5e3f695b877ea 57ae65243ed48a8065a75d3b5574ce0822631ace27cf477ed8354d63079fcbcee241daff30b7e90c 8dc98658276f813fb4ae78e7eb7b16df4fb45ae206d6a0d0eb870d20dc95dfab6a6039ceb9182ca3 5276a4dbd93f0c186b6f51d2e5bae543c4f7fd59a784e823e728dfb34c61d8b6d695dac67c379da0 e67e8243b97bce9fda60684f9f26e5b80ac7fd229922917b68e7c953c9befa485d8b43aff2e9a279 8000bd015ea183f052ab96ef37b7e395a2e53ec8d7ce936c980cf1e76efe062372ad3834ad7e397c e9ef07f66a9a7cb018ef88608fd9afda6cdfa80694e91402ea0968f957aeda776a559d74b8c899fe 22a18031c0edf21d562857381d385c86b4ddcdaa1f905539daf2e363291d9890a216609a78093885 3c7b828964631ff68106269b8cb9ad5cc9e74adf0bedb6650d737d86435376b4175bae73eb27c274 04e92871121ff703df1f71d32150bfc42739d737b341338e7d2bfed68aa74c7998f34cad6363455c 86aa32d06799de04797066284fc46da9c753834d21c809889cda969550b34266c256a6319ff5c53f 67421efdbda5d2eea7de17d01583a63097799dea205738760fda660a2d8809a9ee2fe1b53b73bf02 d427ac97b63ecf2b4e65d8b57f91d052df9e0f361d8de97ff4d5b8f5554925b5512f0db6077d77a8 08c966859d0b4f6437d267c93776df15ecd78fa4cda7ff5c1079647dc2022bd569af5e0ecfe819a8 86e6092a7c92c9b07c846e8b5d622ebc62379137dfd7e41554eee6353948f6f116b6ff4692f44cac b6e8d8edcdd3fe54377ff67b82080ad74bfe00b2e7e78313df12d72d22581eb64d705e0b8f1327ab f498befe85e434133b1eb2f4f75d472a8fd173ab05b8d3813949a06611822e0e1eb8dd9445e219d3 b8e560e3cf4e2feb22f2df0811d1fe9616daeabecb3dade374478adf7d99d5649c001be4d3058ebe 89d7f7b1adde8a4568daafb8f1d43443925c65266c02ff8364a09df2219bef306f7108415988eb42 1f00e39d7e8d912ec079215cf78baed8f3de0ba526f75a2d6770746453620aca3c3f92728df07e54 868d578be74b9614914bac22eecedb91f0482c584b46ada54c6b3c2069455d205f96f65146f37756 762d4f8bf5162c8eb65d752cdf4c3d55f7521f99b0ac92967609773484a9fdca6d8eeaa8f8280b5d e0eaf0aff1f7bb74c652256cfe28a06747dbe98d72ba937654e390b3cfefb551ad512933021bb5e0 3dfcb3dfd318b8f6889b179a4535d652ced87d9d4d21418615129f0d623337504da9b2219a706d26 57cc362713ba88ba62f738aa930e5ab937987db1a15969f839d9ab628de2ce8979e79b8798b3bf2e 48286cb176c672a0fae679b421a07a122dfba0e416451ab3cedbc9963a1285a27f8777ba8f8e8605 b6b1493d65668dce1e167f863c79547bc4ecd1a8bb5f0b5e10eeefd6057aa7e5d8457b3d5507a7fb 2449d90d1cc3cf4ab66687d6171bc50217181f2652aec2cec6f68e0b509c0a50f160f19b53d544ab d7fcd6c7943c2bf4a398ce5df672d356f1f98c9caced8bdf24c04e5778154306717aa002172c2ad6 1a09cd29db539b205025cb7884c7d3b52ad47c80818e491b0bf8186b687bd01012083f5f867ac4e3 54d76ce2a665ec667d20a200e6ddeb2c39c86b8d93e3deeff1b51b6a350f332217eb6d8413be5fd6 c80351c3b56d6cd893bb7839d3695e6a3c14ae5ddb3b8ac3adbf61e53d3710c40df154257ce0e1d3 fb0691d8db47d3daf0ae50e95c4944eefa2d39cb9c87f1003a8f84f9e6f5927ac4bd6cbcf693ab44 ccbc953c6b8f17ac5bd416f2a830c8c87b65fba2d68d0b62b8ca5dc27c95bd3460a07c18ddbeeeef 0214e2ee967757b83753b603ef22e58be2553a62c458edb5645d26dd8980e73f754fd3f47b45e1d9 975e29e2df50fc398bebe6c4854f11bb3d587f2202af8708e424f7d28fc49634ea4cab29ce2bf539 333d1560431e6b3919b200a121438a664c98ce494ed9b102c84afb621afc9ee40c46de397d66beb6 517e4d08e8711f4b0036dd59d7ab107dbf4acf1a983b56881dad71d496a4877411caf7f1f5622dc2 7bcdec30a55cb6d24af24647a4c80defb2aee7aa7bc93d77d5bec087ce9aa96c24c98567f5a77835 8e60a3d2a8749d8109e6a5b3b8b9033d6a6a58c1619cf9d06ca16e73f6e9f5e042ea1b31721fe31a 8b429873835cbc6865848fd4292bf6db7b03735cfc94f575f9383dc15ea7521d188ffa920cc25519 ec8285ca95ed15aa0b4d3f4a9598760e23de98ac21ba7ff8d34a12ceee8a22d6c466bf7e84666870 d5eeac44b1f71ab01a9d5d6b48a021d3dca3035b028b4cef482b6b0ec73b4de1316da0c96edd9e0b a54f3cc0961f5c8905a75315c5e35d2e2f99a4e88b61993716bd6d16e2fa77a33661dee4a1a479c2 60ab6e9eed1bdfa4694807eb589f7a6ed6bc580b3051efa9cb3532157aa4b41b9d284313821f2641 3d0f88a732dc6cb7961304aed8dea725d56e0f0e42a5735e4a03cacef6146aed6163d89e4f4153fe 5ce0b7f6b07adde2ecfd3a28c30191750d6e735f993f52f2d1895df9fc4ceacaed7cfa28abe3fcac acde0a496183dc4d4d4aa0a70e194d42c8d986d538cd1b6b736c51fa756e8da1a5a712aa6e08538a f8abae38d5ab2ca662ca5345b3a7cfeb435211a3a8d446cfb5ba51ba972f6a6bb9c9154777f86ae0 099835e614fbeef241df2c4b0dde1210be6205e07aa9c53395970caffe14f591bd4f776735c0f549 69d956aff66c2f8656edf35aa9d16da8fe122c46417073dac1084747df154b9b8df838d7065c8f9c dc9d793f2665f8adbc5afbd7d172da65c356f2493d2c0e078d9afd1e7bdb5fe7d6f93ccb9760eedb 86308ff34f599165c11fa7bd9b54144702891c6f356f1c3e2a32eee0a7daeae53e3decec23ca31ff fe756e4f12c2abc911308af93d1d3dd1f750cc575d816b8eb98c1a0a1d84e6256efa215af616d203 4b0c5632be1f27c5e1b271765f8eaafc20313181eb292d6ecdbfcf9222e7559348ba3b7c2daa24e3 10e9ccfd029c049434d955c15aa4dafd70f2312a8abee6847f9cdb13f1314eb775a8e75f259b0357 a969c24aacb018d1d2f8d7d9de981c4d3f51b12cadc5c59b8bcc14bdce4107821229fea4c76cb019 6c33245f10ea5c4ed0fb89e289e765a3230ed67f660d1920b4aee1a3f95d9481ba70f8aa3632859f c3fd5d294d83d84cf6c9e68760410a24aac15bfaa5d55fca543ed54a076d75bcde1877b0ad2a8bd2 dbd0496a336b1e0e5f2b1155b737fd4c8fe6a07babfe19690474b799cf9f6b5b1855ad6ae178b456 ed7c811573c210f86a1703d3cab6cfe3d0f472d085457cd496440b8367247cd627b84c1928bd79ff 5d317856aa588f5349a5d631c75afc51c13b9489007d1eec576637e88dd4d94ad39a563fd731e379 63a109656d0d260b2f30371799f9a1e334e5453923a8c9caefe5b02d88c2f4e360c5ee44d9c211d3 19523464afd6465d0d97200c7b8438b1bb4c2d2bb2d0e95565f51bab7e86d236889b5f2f26f1fc7a 680ee41b84ec68551c6f3d6ae7ed95557ed16ab607ecc31d535e4e1501a4078e664ae084b44afc55 31082152c4d6dfb89cdbde02fecd16feb495563fd78efcf65f52871d5cde4191c9af94c3cbefc3f2 4d237cf9fa8cb4e50edefe227908527e9fdc14c1a1bca4166d11193f801e0d070e154d5279a43427 75a6996b82cb30151b6365ed8199a481e3da60ebf4bc73fa078ceef991cc1c3ffbba9dd84283eb6f c601978a4ad7b6d383defe58c042e8ee3f1f7b3074c88a0a315b31b5ef1fa70dcd7e44451f15e7bc 85e49765e9b2d2196170e86619768b52e022396dea0709c8b5594b69afb8bab87e0de45699546de0 6640b91841ef0ad838b019cdb9d7a6af4645baee29d2449ad713976f84a87cd265de64f38755db8a b8893267a295394cf346a5593bb5feb4609d64d5ea6be94fc5a079dcc905fded555b1250d3ce5a13 23b7063f3023122a0913adc79faa46cf1ae6ebf9cd15d0c1353c30eb56ddc8cd5373af2fb7e31fe6 563dab2dcb3887b3832e6d9dcc10b0aca8148dd7d4aeea33b8cbb5ab136163bc2632672078fb58a9 ead1d8ccd166dbf4a059ff6f6290d1f89327890adab0ed18205ded3d714c62a0f5da36e62d155de0 a10f72246b5b1be8c09959d3d3d7a49abb946b1f6b7398fcd0712acb9fdb9e7a2fc55a3bff1833b7 095d7637e26bae4d975eaeb5d1cf0bb7d18daf3a6a55c795d251b59c6743458d8307ff461fc720a7 f2b2d1130ba09a0d6b0cbc0fbed4aa231224d8e3ede627b83ad47ac9fb857cfdf59d8f8cf9adfeb5 165eee33db1d6dcb4b901f3aee9ec6d358ee355935ccb128d3afbd26117add8fb5ea39c9b72274ee 8744e9186883be9b7136e577fffaf6bdf1f564a84f74fe0b866f79360eb778fcd1f2d8e935e1bcda 761b7706c354e48d135b752f9dc9a13a5cb623e19e2c9adeb6b9c99a28cf3dc9ec7f667f92537265 011cf89011b2ba4e373f7b17a8cb1840857754059d6d05aa35579acb4b6d061e3b4fadb8296af9f2 5eda1cf33367d55ffcbccba65a0363fbb3b9e3dc3cbaf0d2232640fbfca8731d2ce0eaf27e57dddb 5b01e3ab9a8666118112e003c0719bd10728ad5ba07ac6b3552adf639118302aaba4aca376a95006 29225d47ea21d834adc755d19a0a46631ad97f8dedba30a91595451ed4e4dc0bb78333f84b0c1ed1 b3d548b86cbb02572edf55a38eb6628bcc3f436cab049641915dd822767bbd4ad2ac6a887d0ab18e efaf61a75663ccfc5a8dda0f03b616aa2d6ba5e265b37bcd6b64ba9e0356f2d5b4e650654fcd96f0 312c69ca642523e629ff618afea1bdb4b83454ad2954e17fc12067e32c9c0aec425c4b6eb9676f4d d45c4f311f1a324eb2300626b1ba92d50e83c90e5f7cd8e66e48ac00ebc3f72d1315fb3f4814f7cf 4a717f1a8c4ec672574ec958d814bd8fc308c6794bf59b4050b13ca5d97d9ad5d29428facd99ec72 023eb6eaafe6fe1709bc33d01773646d30d44355bdd68cc10237311a11a1a0bbb514630195b2b2eb 6af9cd4efd3b1d25df207b180151ad105a1e10d67f1a7891771f63cc6b0109494b19e34e1525391c a0c16044e71c6493ba1f7e0b9673c1f9ed3ef39bddf12ec4fcaae020f9fd4ffedd7875a18cb30970 6a6fd342ddf1f95370b3fd9912e2ad4ea4f4c51adcb9f9d288862bd129ad48f91371f2c7ad7e963a 99e6d55aa594bdcba52bb76cd23bf1dc9b38debbba5fe3a8f336a4d42cae3d73b8e690be2c66dd44 f28005006f188a0108781ca3e497016953e0f6f41bbfef120fa7972b6add468bca350e58f780dd18 f4b1ace0aaaa4d58d7b7826d25408f2775cecf6bee842a6564784f32e8e556f9656e39ec28e37a7b 12dd5dea525974d64350d72dbe6c3b97857541ec5348ea7d7ec2b945ca2a83f5ae03e8a787cc5bcb 420ffb45b2ba39d3fe57d76be579622ee064e2b83d4d44c743fa638eb0edfbcfe60b5b652a833e0d 9af14c6c3a1a1e644444b4fa1703f6a43f84d304366d074652a6f3f5b060fb6ce74ff67137c210e1 5c0fec3ef67ad9b747a107aae26e6fa3f3bd67d9b7b1f60f739b2ebaf6ccdfefbe2fe037a080f325 c09e37d106ea4fe9823b5ad9655bbd48cf4aff3cbdbb7252a8da0323cafcae60f92f24935cad18a8 db8b6ef76015ebf0dbcad35fa3cdb9cd2dce59892242f6a8a6af1e56339b29e779509dbc68c7c813 1d8b2047c4efb16861f26eb74a787f45c5ccbb504792dcd5de36f24763cdb933b716f728e15c6477 a2c74b43cd8e9241291b7c3a98297e932bfd38b7f9210d83d712372c70bbb831b7fb6d60469afc0b 430b91a68ff7f0e0793084cfbcd6896e836201b8d9b4263e3be1de4aa3cbe717898a44c9b223e2e6 e79688abab91864b8a45ebcf0ff3672545d7c98321d137a862029d79e9854fe2009b12f09f71257a b7dfbdfc39166a524a17f2799eeb8644bae05b938ed7536c41d303533706f0f8e0be94f3c0da072b afd2cd61378252bb8057b6c84e03fc17732bd583b57840f1dabd2a6b2126734139ba0cea474e81b4 5dc778fb51a4e0c5add372b4d5acb2f626abebece7d97cdfb27f9058fcfd6cac7aefb1bf979443cb ab176463bf083306caaf174a47789a4374637b933c9fdcb7be6e5ffb5136dbd5d2e3cfe3bf4884a1 6fbf2c0d2f90c4dabaa9f3975f80678bfa2c711d6b03d01d2feafb42f15adbcdada9f951dda55758 fc2369bfd242710f5c722bd121f09ea71adc5274f7edacf94fcf93ebf61e8e9b61d319eb1bc833d7 87d7d7dbd124bb5587a2ff41c2d7bde3f233e9bcb81eea8d6bddbd17bdfe1c5a3d829bb8672e30ca 7d197caf784b4f8c6bc820ecd6deafc95f8fff22e92cf0e2828e44f339a89257b1815d528eadadca 933ee1ecc3d28d48df1cba5bd37dac324cb346fe4404f3f32cc36eb172f8639acdd351adf5d5450f 6f1d20f0c98d6de4d5a3f30c5ab547ab0ebffe467bbd1a4ec2795f6c2c04536b3cc366511afd5573 6270f91f24d780e80a61d893baeb27f568d04339336ba2498e7bc96147e865623c4b2440e9134970 b5b26594d851a9eebfaaf96bd97fdb0a0eef5a57dc5e5aadc1bc4fc8d25ab792ce1093aafdc7d094 2febf2253eac6aadfcfbbea794666fb10b20a72cfd4a9ae963e0fd07cc9eeaac54e3153bf1a379bd 36c8f93d55f793e636060afb55b95b625cf5bec467d120616b5f24484e8f722dfbdf17747897e259 c5aab4861308d437c4274be4473bc47e5527aa99e8171dc323a635e5416a260f8d4a59e57d6dbe31 fe7b41fb2ea39bbbf9308e560bbdd7201fcf8db929c2ddf0793de4cabdb36c98abb7ad86eaea76cc 328f5944f08fd8ff8279dda245e3b5688da99268f7e4aa1b15ecc2b2da6fde1e762cd36ce8d16b09 2497a3b6dd1130c0bb71f6e9bf4856c42ce7dabdfc2e22c4e5f1fb6f1a28be5bbb1d0b7b8516828e cade07125ca852ad048b7b7a0572ab3af88ddfb9febf919cdbd3afeddf0f7d8cc531466513460885 78aad0b74ac2173ed14cca8b058220fac582c86f9ed9b829b931541e950f4b3d64c058364cc6d6ef f1e2d58ab9b46c715ca1bc354cfae907c29edf97f883701fb1cf43d994e2e2fc225a53f8d1143176 a3d4265059aa8cbe366470943bea8c473b34e90d54f7298cb2cd17325e1250d636d8b5dc964c4ad1 70bfc65b249fc34d583255bfc07584ab3c7f5526bcdfd7d8b16d192b36e222696a5cb5efdbe2d120 fd0d50a17bcfd7ce2684b11c7c84d8d20e4a7565b2b378216a65bb221ac7c2ded21f4ede15bce98b 041e4d533673e6ac429833447116369a9a3b7f86d2df68d99388db15320fdc2c641d48197652b923 9969e9ecf065c36c569c7519b0c0e25935f86f8417b6eccdc345c7cd6c1a1c71ca85b65eef3c0ace f856e8d36e63e133125b1f384d7528b09be567dcb02e006b9ff133c0bbc5979bab99978a759d99a5 8e6f17f2764a3991ff5ac90e69566733319ca7b8f7aadb6b06ca036647d2c96c8c8107bff33b7689 9dd48ae4a184bbabcf44a33428aa01fdbe2ee35e5a4e7199f0739a5b6e635145f59e1401dc3af499 f1a160098700339122bdd1421b7850c27724776ec4698ed808980dc8668cd149231301bb8db75f5d 2b3f2913f2804645a8668fe201005fe8a65edc77c0cfa815b19dcd803960dd59056f0dfafe2c1671 fd62e7d58040862ef562a39e84a2fa4ce33607921d3536ac4c09a640fb0a290879bc966d2b554463 a9d799770d96c05e4356db25e1a78845194a2d95efc1f6c97a8c07237e80cf2a62ad43be85e1d7dd 16d5705f97aca336c1f8c7c390cb69f52d5b620f2d857171af540ed1f3aba3462bbf12e3599a4842 971cc41d3f06a2b2e750940deef4101cfed9a5e61b77a8246fa124be047c871c3eb3b6da73da5dd9 184d7f680d7df95ae18cb687f7e6f0d094b49cd2a972067d84f8199767b4fed1d3859634cfb2b8ed c9cea9e99dc46744e6a1a725112f77f59eddf87cc1bce7eee5d3a7cdfc73a93242ea4b0a4d746a66 03181a9cf0a470aa184b57130fada1000d9a5dc4bcdfeba69aba944416b4bfde65e8d9ccc228aa1e 7e55e59cc5ef4ee504e18c5fc009eeb90607f6bc7075b9703ab8b609a616d82a644cf945d1624bb4 54816c2bddc95665f8debaf93d11faf2f352a2faa2de917bfd53ea85a719c5dcabea9d74e355d624 fb0d5dad80dbc74284e0057cee2ad55953f4b8b80754aee2c142536e4a0c3e2dc580c31d14ce1e97 94ee8cd825670a6621b0f6d5026bf5a05ceb7cf25a7edc84a6dcb009ce4a44e3ca78f8a4ba3653ef 93b5953a6afdfaec4201702443d052e486efcab1262dea4cbe799f918d7544443941e4b87c598991 fc5c988475080a4478b8b800add9a96491e1925110c3eee8835a7dc97089902a6f3cbf64efc5e582 9bd49c2ce3a12e3adcb555523841d8c3c85b53026f58c04fd3a1f8f294d7f782ba157bdd4e744e31 e593747a1430c9dc1c9b02e19455f91ad02d824122417cc60b4c391d726a758c61b82c548cb9160d cacaafcba1d7bfb1a410698bba36c4db90a459ce4c02f81cc7569d31277747bb978c30c514756e09 a4cc55a122df7a46e58bc4f554165d668b116f3d2bae938c5a7c167da1e00ab1686daf98ba03f63b 51f38799bdc107dbb7ade9eece9786564fab728bca44ff82baa9d57de92f82a5f71aa6ec83bb236a 0f3026865caccdf865fb61303c974846b2dc1745b4ae8d5010112bc6365935c4179edc0b98b20a4c 0481c95fe6d61c6d56799b43259f03c69bb9305fe343bbfa3522fcac5677f15a616b5a972e7f13a1 2275aa5689f1d04a8f76516925fde0ef22bd80814efe487951fcc2fd98f588434f3a49ef4228d3d0 2e575200fed21494e66cb3229d63575e09b35ede291065037008ede1fd2029ceca4e0805cc82190c 6b650167992828d9af09179d4fb3ceb2db847c84c88d84b22b45f04b96086ff7b51cf2c07cb3ff6a 287b6ea9cfb59928be1f34a33757e8b144bd5aa44dc8ba87a3d5fcc6570605aa69eade28b8cd6148 c8cbb54d017b02800bac3af20f31484893019d0fda2d3d3ff7405e3bb12756918759f3856e997a01 dba2d517efa5b06780adfe0d622f664bac6372cbd4090efa8344eba6a3503951c8528e12accb57f7 8dbe3a353d93d2c17e5fd84df6942617fd7aa3f65eae25be02e83ab07b73f9ce4ecf2aef65ba34ca 16981c139dd0f3b8c88e0d50be5813861bee3053eaa2d255590dbef174530b4879f928ba6a483fc8 ef29020725f1aaa8be9da0f18fa8d4aae14dbcb9fda7e41513423947b9ba8c4060d68245aeca2f51 8dd3c75b291de95d7db971ce5a2747179581db4df3a2481db4fb96fc696dd318382deaf672828bde 8ab4d8635987f4c307ba4ba36a63db9e2df26da3e2d10fb937ecb42b8714520c8652b31113dad06a fedd67d73ff28bc9b6236eafe9c992dc522432bd802311cd3f5b24944692199b705d7fb559ab352c 76e5d69090f37afe59b744b4b4fce1e286690e75d14f5be6cfefd68c65a34d3680d659563d4da476 b1dda67b5dda61945a43e26e5da3b214c1ba43ce4b05f5f1967e8b4b8fd189a31f9389c87fbd265f a3d0bb00cfbc2ab15ea8456f98a30c51775ba57a5d49210fab899824c69199d5dcba1ad2b00f87f1 8f4fa8b69eeb678497a33677f5d48479c73b39acaff4a76026ceac5d24c571401e96a2185141369f a8d0f289a98ca9676f07fddd33fb3045b27f129ad73cce20db9af9a77c74b2d0f008d558ae736928 e6b9bb776bdd169c0ae5da01b0467d710da8cbd746f43b42a6e2b6a97cc5b623f6d17ec0fab907b7 e8850494f8b1debe1b144d76d07e31ed8bd860753076dbbb54528c5b46424af564f3b670956bfc70 2c03ca7c6b952a31e0bf6e602a86cf02a16dbe3600cfede67b69d9f1eb3acddccd1ace824b79c5cb 45a3d42725a0f768ac940f1967d47ae121ddbdb12ca74c32522924cea6f3508feece55c6f10356ef 88b14391a6b35125ce8abe3a8f4d4a84b3bca873b0f3346e21f9dbda76240a72821a20d71f6d07da 3b8ceb4acfe67b1d73fa24747610dd55ca42d6b5c20c81f5ee80c9c8148d5a8c75603f41ab46986a 3fd4ba840f1657d3f6b6279945ac8492cb6bc90ca027a6b46ed342f34039bce97f8e55e5a27d6334 8232de6602321ba35f553b7f13831731ad6277aeaebd326563bfeffa5bae27cb61a794df9decb13b 6e29f9046a560f97e6c7f6c403a7121db8093c0b2067ad83f4678884b88da7a1171794ab548c6718 058225d8037aeb835cbb1cb0a6b7b18bae37658af2f18066ac7aa925551bce278efa86ce157e27fb 1deba8b07327267bb0583f903b75596204f6846d82face1f4e36aa8c531da43aa4d7679fdea66765 56ae79392045dfd6ee045e7f7a665b8e58a4d2522d2b80719a4b43215cfd0232ef02aa3bf539bbaf 6fd575934b411774baddee191c47fd357feabbbc3596ca3fcad6a857b60d4b33263d8650619a73d6 246f0d6bb8dd6e0ca895507ac41bebf8050b459240fd1931410577bbbbee66f54e5e205fe034db72 0644bcb75ab504edaa9b26b61a3d89bcb3a98993bb7335cd72f2aedb24d293d616229a0f551b80ab 3904feaac764f11bbf9f3ffa92a96444176dae80a63c6e1b3d032bb7e2d6e49cef2846ec054638ee 3090486f0435f75ce7cde083fc44dc3a572fd23f158307e082a88f56d9d3f32d22872bcbce52d33e 494ed725aa516f6b5b4fdbad87e73f039a9c5b15dcf71b351d1c22d924d7e7dffda1fb93da77dc06 f5d24aac315cc31f4d0f28a9c5e40a7363a3d10dedb2ec6990d8db21469a6bd78df6d09cfc350a43 9a083f48a45339abb3b1c1da7ba5ea5dec8197a8f06e5d37645d2b1ddd10f988fcd9dab4a881e62e 7a2cf808fab635dde6afff436b6c788534b7556a98ec3b2e7c97722ab78cc356417cf0ce26bdd8ea 69fca7ce06aa1725c459aade56375be5d57f6255ae5838777cf5d8e7941805bdce26599fbcf42d2f 557a38cf231bb772f002b0a3a893455704afa3e1c9590f63ea7f9084592ac26ff4428d984b1598ee b51b9af7e89050b3d45f7bacbdf870dea727b82559bee1dc1cca5bde69723bfc795c16cd5bf3cf09 3305efe6b32de895ba367a61da0f2fa1b883bc315c99dedbd51ebcca5c0ea1b109ae1e448e4e39ef 3301c4c683ec7d1ff77e549c39af7b39a7565caf08ea1c8382df6ea00ea21fa2c66e7055c55154a3 9d4e075a952c0c1e4a0ccd2e1ca39463ff32eb0468fd32b7b42c947252505a9d6ce0f60d6fb48a7f 92b51e18d8b975e151d534a8a554526a6c876e69909b2f2242595f8a7c36dba3fc7b2cfac65cc5fb 005fa92d4655e2a5671592adddb08f7752bd1e2e594460b5b69d51c9ca2d0cad645d34eb0c267f11 2cc23dfc69cca13ec674a387339b32e196bc6e972e5c554f9b9da7c979185c254e8fbc91bb168ae6 62fdc07357f05636e22af9f9e7827e985b656fe46805c26d610598f700158d249c5d1b78bd8899cf 707c328e43412d457add369fefe7c33c752aafff880a35a35baa537ced42fdbe6598d691b95eed33 d16b1bf8e04fe0514505fb656f072469f487ef723e6faf40bb53c85dfe475a2454afa0e5334e0eee 7777fdac53fa656ad41a255c33ddd8d140a3302f4325eb5d45dcf2aec69894702afd83e4d7b925a0 cfe2495546b412758e2ba4e93e5d9ac9391a143ec1c3b0828f2a35d618e4ba516015be7e7511c6f9 99bcb864e3347efadfcd0e0566f330825efbb9db81acf6e44a81730436b571739d55de73e9fa940f 26f0f773e60ae7f9f113ee79494ef9a94d55efe927cbd9553ef875f2dcf2cdbbbaf7256c376a32c3 654d50d886ebc30f44aa88f5622812a88df972ef96f9270f453a76d34c54b6977f31b725d223b563 abedc825456a756e8957c4da41cd3fe7001928701daf26e5e7e0d401efb294236cb75d247e2a3d8c 350ffef42be121083695263b625da429c44d30d7a094ed7d9df9022e50dfd62a6acb19a8c8b6a7b8 75a19d02b565cbd6a284c8e672947ec707a5e0c569be575c9b2c17756d4a55d60eb97a8d6a62c471 7a6eb13d39024937c0cb7e04ea68abae39b9d9e1f8ff995b9cef3365633298427657a8561aa7fcd2 358ee5af621ae4afab0a733ecc8c2725d6ed352654be4144b567860853fb1f51c99bd6b6bf2db7eb 516f6b4d6f69c146502973076b1df3b8b2568db46877e8d5033cbafcc35a6c8a944d0c49f53f728b d7e7a3c0095f4fdd3ac2cf7c633cb425471eceaa562a1a7245becb8163593cf4fdeb8501fcb1a75a 0ea5be7f25ed9718ec1f7af2c9f79af95dee1bdcbb9f6777f49eb950beda3cd337b222f6f0dda90b ac97d732ab6a9eecd5ddfa8fe657a965353be142bf5de02e1ffa9a739344b87436b5c311cb86ceb1 dca3754d9076ee1b92d5aa370e2bc2e7e8316a641af232921a06f5c856d8fd4e9a3a0cb6b1f0ac85 8df70bc9f3c0adb08db677ed0355ddf2807f16e845f499bd2739aa8c5784cf3e499d23b33dfc4a9a 01e1e88f966bed3f7a4b3849aa1f6e2492aa4d84d1475c360939d464cb2d22fe5b9010e8ba0cda7d 13f83ebb7654277c64329f2bfe7d26af600f3d3f8d53476fc9518dcfc894c088b75d882a6ed74a69 b71f073e4386b921b74094955343ed4f1d6cfce7825a7b3dc5545d680bfe22f0e75f24665e3da9b2 e1fb57ff53148470abe5107ee7594f20ebaa9bfc69f61b74c6c1ff5ed0d62fb455b5715af72cbdd7 14163e70c359a8dd5c01faa1bf39f885e3f998db1f5b90e11c60ca56c85befbf4856df20c3947be5 b697ac767f24add7e8f8a65da624cfc1eaf3221191b239ee6a1bd7d9e3d9ebdcdffcf3f82f925ee8 a1951cd1d8540cd4ce1777754f5ccb0154b8b47236cac08cd74266d3dc32785ad6432bfcc9465d96 50fd3f6002399c2e89237e8fd8951d3db5596ce49832a559bc3edd7b4cae1fb05c2a02a898037761 fd7ea88922e6d7ba525168fd7441ca5e30e12809e6453b1700999fc65eeeaacfc4b3674b78ec4a23 a10a0fdb5cd72fdcb065b07b4bb5facce30f85e3ac8cdfc0a3ec7f7cc5cae34dd607ccce51102751 9534f0b8a99ab3e34b0273d686bedfc43a5de04790eccdbf383f772fb39e75b6e410caa910c302fe 8e7640bb502e6a0fa9ef769092bb31078315aa988566441673d48abf3587806a0c6b0f7a7428bd30 708ea29a20cc51e6d57f16ca853cbfd0c16f986e7aeafeeecef1aaa44bb198299b4e31790b8a2001 8a7ebc15666485395429ea25ac0c02895ab45bc274e4ec7ce3ebb111b5f982408f01b3f832cccda2 0aa2ef0a5b3692819e5a03874b3ba25375f8f2a08f5989dfbb920a74e4dafe05a3fe6c2bcd156bd4 139951a5cb0bc7aceb8a2b9a8d953470fa8768e8a6293d6c05cd862227f7fcc12513ba8de728f04c 4e8bcad5d91ced88ecb7162324698055273e9d8e9c77bf1f81be698956180dd9c6886c937f2a539c 592120616bdd364ed73517ece72f9fde9f7af87a37dab4ab208578fdd9ee484edd5aabb4b369c503 6e2bce78729e606feae83b8ea106db4cadb42297d3411ce573af2ed69d3f262477c4e5e0381b2f33 ba9b1d5edb08b75fdf03b352ac7073e47a0216be39b6faab8dcbb3e7dd46ff6c5993f2f92322ccb1 58e254cf1458fee6cea542971fb77a38d0e74fecf82e1ddc67a59843988b28bdb1ee574759887db3 6b59ff0d1396e0067b40c6ae743ce17b6e1f0e09de773a2e915285a670b2578908504e50133ff2f8 2b190b49f680c74fae59ab9caf7d6a844d1d6dcf3e6ec2691e84cc399e05ac997635492bd4fbdc5a 31b3680d7d6923ecab96e11adf3bf5f9c21d325c65111d33e6762f5b3dd00a640df89054919804e2 143154c5d1e38411d0e18600b1e74d35ed43c0214420562fb8966a4c0f4ba4cae8f65717e40d24b2 dc1a5e0c1a0d75dc6e17b4f7c52b93fb258c33bd5d08e9eeea42318df9f48cea17efa27f46558755 fae4b670a21b1fc344a799f353de9849d13e98c7fa718e3726d847381eef39f3d64049f225c153dc 6c9dd77fe6759236cce46022aef6c5a56c55a53d28debe4efa2f1d07106d359471e5b6c22ace6097 bef064a5d51930c8af1cb2750128da1d092874aa749dfc318c988a488f0ac7d5216f3feae8df6509 c6c9d2bbfef43c3c61754fd205568e0f5ebaee6004ede14bbcc8c496d7584b654adfe698aa36ef16 bdf2eb52116f8bc5df152cf7089d8a955451a577142656b7d3a94ff2263db2e976e0f48bfb8c8182 6b37b29962af9d7f16920dcda3877561374358574a1b3fb6d5e821e7095998e45149b8e71a426ed1 c1e8ebb915cbe2bcee740c68f5e634939e2a4cb5e0c355760109a61f39dad81ef1bf2e876a2e3887 97db735f7edafbcad7f7f99c845b4d21a9f72dd8f0313beb48cdcfead958b3a3be684e3788b4d974 c3fcdd57ead2f0b8c87ecd72a07be8fac28fcf8725d7a937251ec22d5aa4c6becb9fa24a7668d89a 288da5552ad322b4792fa04d4f2cc94f504dd4524c2d7fa4856f774c5ad858676913a6be6c60fd22 6bd9b33cd578208ce20c2e18df0869b441dabd966ab42a3d011faaabfc5b3c631ac69f5f3f2e0758 d5cc7c6fade51113a3def732cfd5cae3a7d6f771942d49ab186bb6ba03bd798d15ce6b9c6a9065ee cffaf7edd2d522ba457f2f6870246877a888cf125231cbfd6e425d42b745da67255b1a64e2a32ecc 3c66af7cfd95044353dbd918b72c755f607e30b998f7cafec7b6aaf35b63609fd8eb9688015766bd a5bab4bb797446972ff1b13d6b9e97b6ba2ca8cc68745d42825e486d7751c494274f38ff626e63b2 d3581a9ca8dc7343af09a52959f1bb2159c6bab2dbafad425a79c9b77a9fe97f1db9dcc0654ff128 05cbd561e8b49e6ff00749875206c1ab3e37f1c2ed7bcdd5e9f4eabf6f83acb398dc0f5e5c5be77b 844f8e058176728407a1fcc0f48ef5aba93c43e7fcf764bf8642be6f23e1a0f69b9483977a6ac5ee 3104cb9a1d16b097a0ba5b5a2aa2f5059c6fb9b5ba66341649ae927a59c1a570985cb31e8709a588 46da615a25e326c5bb4391e1b13dc331d59620f7f7a0dbe26a8db7d0eea79a629378a5ccec674731 ca731d03097f4b3a378ff0c45f878788bf55889700f5a6b1f87511b243c37ba1d5127b0bfa20b90b be5f7b473025bd2c6723cf4e483987152eae82e1482b4b4e0932dd7a4c25c1c13d1e70a126adf92f 4cb64c4617f4e1618eeefabca4584df52dcaa715516ee8f840adae73b53f5d1168eff957edc74528 31b2736971ef18cbabc39eb6e0a46109ebbc4b92a731cfb4c7ef9583581bf2eb99f635c4907899ce d49c200803fdd30e7e1aca246fd2040ca4965b33a8ff2e500be6cf4c48432d7133aef41ee12877df ec8c09e04ff85a410acab94bd534ae7a40ebf3fdf6f17b4158853c90c72b27412e6aad9e98c6b40e 95ef7b259e3bd674771bb1f764f2a9a9e59a68cd1fad8db0c7bdac0c2e67faeacb5a0f6f3f722be5 9a86ecf2115aa670219fa3280d5e3837406b303a02e3286041a0d39fe7f6ec9cf986de375e829dd0 9deb7ab4e3ff9e76d878116d22f2d90f71d7fd56b996958f528733287790625ff50688d560161c90 afa1dd4deab938721588679dcb390cbeb491a854faa1e3def15d20cba356a423b2149002fe42e9a4 bcdee87d71576dcc80e98b3d60cb8dd1592f83429f5f6405fe7cebfb4fb2b6dd1f9244c7afc24509 ba18c4bcd41ec8e69f6f4f3538da6f7f903ac73d8431a4d5173e0adbd3e55af09521ac27cdda4fdd 88349d92f72c391508307192b90b5cefcb6d29c9425c62521cf404838c0a4afd58c11a44ae2e8ac7 f178a61c2a765a88cbbd993440ba4c1635ff441f6658ee19c2f6356ed39f7bc8497cab174a0df2b2 690b833c246bce652d43a675839bd8a9aa50369c8980aadfe6971fcb8e5d3a3f452c3c15d3be927c 2a845078241dfc6e7ba1ca178a13912d30abfa936d84eaa7032ca4b253d9147a52cfd418ca9a67f3 4ae3dff0f0d46757544ad0dc7195b9eaba0157cf7c02945ead2303f6f403dbfd084b5b2ec3c50eb7 31603f582ae5597501e4a91c6bb6b9d6cff0526e317f9be6f6f369b133de49707d4e3ccc74d61ff1 5cfbd2ad7b172b361f77782398f13aebbf2924177765d53ebc67be5b45e86f6290a0c74421a06e97 59c301bce6843d941a72cbc689d44e77578faf974b7968557487f6bdcf9764a62790408de9471656 2bff34e373ec61b4f048603bc81c28dc3816f08a0abeddedb073e4ca151afebadacdd44dc6c53a1f 6f12b2e02e665767b50347e6896ffebd1d66b832bde6bb4d0e6e414235c8edd24c1f3d0b2d04688d 91e61bcfc29f8541d97fe4b343e38a7e2fb55bdf88f4475a506b50cf92537b662f9c36f428099efa a810eeb04d309fb13d80b78cfa131dd422e83ce069575f1be96599e4e9a6ad094601df66eb83801f 51299d5f9c2abf8c6cc4040930a4cbb7eb9fa386952bf5660f5f49c2f0e37cb4edc2ba948d706188 a333c719d367e5679e8caa157bd958804a9dc901f655ac17ba8ed241580c2be38a2d3183fa4ecd8d 4efd5a6b223e64d252ffac0b598ff2625e9cc479250f57330db9aefe1e8bb3920a87e28720523451 5a63c296a9b8d36c56219755dea7fe4dc9b9d35a991de3a8aa024d542f4dd0e18f598f83e8c706d1 86c99a7f7645581b4434016ed40ef75ca4a5d567470a2d9eade5ca7a4bef14eda7bce64330eff376 4e4fe3b492f558fcf212a9e1f156a9d7c28f28b330f6de232f56a9486e6cc7306c16949e2dbe97bd ac18b64c99afc8443deaa481f015fa4f784817555fb25ef5d18d4f1677a18de58a6debd27870c26d 8be6abf3120f592f1c84a405a00cf3defeab8f4e1fcdfc1f8245c96674b1b797fbc2bdbc6d398bed 7aceefbda8d970b1f5d0198cbf11dc8cda6ccb95326f395dd3d0d423c5fc9718241b31dc27fa7c7e e458f71b8826b345853a83ddbb333a8c9bd0f79cb28d98ccb4a9a8ae774053a036689e385f314fdf 0be27ef85223a245c422c4ca003b8fa480417b7dcfea333b1f995a398cbd7fdd791b565acd8229ae 727c0e5846f614247e3a8c94d930ce1830303304c4b87f56d8a3cd6e8dd9952ba0f5f1b6c10bed36 6e42676e0089fda224f88fcfd37c0d473460adb1407cd7533033ebe0efb1d4626d5c6201acd9ee35 c462971ce9fc75c3207cd11e4955d4ce3af2f583b17c14beea0e9126aa83998bfcfb67d8827e67a8 9ff1bf44cd7b7ea4b711086a328782e660c646f2febadf7c5f81600fb54bcbbea23b8bae3ea41f5d 60488e6d75ba64330f10a0fec5dc5662793cea5cdbcb03caa983e5eeac6ca16baff6199470adc9a0 1b95357361a1cb22034dbf930d232d82b7ff32605b478bf421d5ecc81da8e735395081f59777d82a b5839b0d6882802e451bedfdf4ad35ec37059c4be5ad59ea6fc3ff9196e2484c549c6adb5ecb34e7 d1a02717d3f9a6b619f575f336f606f2bc279e0baebadd59b5517b6c8cde80f3ff995bdcb040ccee c2df0fdf6fecba59c4ec976dcf5657e9a28ba3cae95151ec99d4515555d819c0a9e51eadd96b07fe 0fc1b239f8633a06517ed752c9f67460fa492c10b0dcac668d98348cc26a60ed253a373aacbb4c3f 958bf625aae77fa405591cb29cce2787a10f634775bd72cded516fa09ebf3a3423f58c877b62cb50 a9b080793689d77bf7748ca83f5b1745608365154addf6df95906fe908dad34f04a0001915d9cb78 46dbaf612507e36f8ae01bfdd2ddf1c267330f826a53e8f8c3932555b81fbf4b6f6356ebcfef6acf 77e68eef8bf79a8576f757243960a0b079075176684e6e5d2f552e962e5ac3d0b22b64e3a78653e1 ef6d290bc57e07701de6658375a24e33de89a664474bd8b87daa25b87cfb4832413b73537b327e1e 7d97f3f25b1c64ba7ad4fe7be8dc349dffa432db061b9d14fac20bba42e5ee08d7c72aca832ff1fa b361b74ae532f052c9cda267b21d44faf921fbd03efdcf054d76ea2ae97b4d31ccf7b5fe13bf694b 11cd7e0d740ae34407ab86a6874f2ec953f0f0a1af9b49f6433ef27fe4b6dda1e4aaa1cf5d4f7591 418c200a39318630cb6ba84af58b97ddfc655cabeac060d146f1ff4bda2f9811a760bd6bd26c6cd7 9185f8d599ea6ef10d34dd327b4bcedd8f9a417d843c7b2c4cacc0686aff7ef61749e30ce0039c58 459b98af123b185f3d33ce8624ed0f125f86e42e1f76b506b52e2241e0d366c670c667216abc6fd9 d0a0d35fb3da2a8547a85ca23122a6ca9ceafb033cd4c8315d72dbc337fd70452974f0f516008a95 8023977096066d157f254d8bb109fe03e691abcdbe1ab2def7a3fc8b86e1c4fcb06133fff25f3070 ce5f17c48b4b8e65f3ffb8faae6665816ddbdf625610444411050348ce398a1931627cd9bffe2ebf 5df7de3ae765adb24a09cd648e31668f9ead21b0f2df16bb8c87f8e23fcfc9ff1d13125253ef98c1 cd90b2a65882feacbd6ad129ca0d3738c9b386db3aaa876f91949aa78a34df4603e0e7aaf8afba14 618ba1fe7b25b9d8d2662fbfd82be871bb03cb3ea2cd8ff3e6510996af7615d9634d5a22b842e9f2 adff6fb56aca49f13f1ed0d8952c6de4c365f3633112ff4a64b8a97f1ab687ce9877a9e9cb0b824c 4b14271d58bd62b23f367bf8df0f68d32b4d5864754fc47c1afd3cb7c003fea4ac7bce18896d4646 153eaff71cac319674a6b4ff22bb18e4ccf77f5cc9682668c33e00dd1f3581878bca5b5c28e0ae21 b4661acfaed99e541f886e69748927c246cbadff1569ffef62b4d7da17365bda17acd20d0756e9cb 1472b8d889f0ab52aad6f69e262efa7b576a97e5f47ffdf6ff7f7c0fb546e9df9fbfd3fcfbbf80e3 2bf79f3261bfbaa512d028ffe73ffff91fff4b2409fc8158a96c976670da2a9d4ff57b99fd54ec4a b993c815872afde54c7095550f15ed519b63e8a1f63ac1dbbabe27778df6d3cb7ec684340af22629 d2f5663104072db5534800489c77409ab76090b2051b7c46b566db786d5d08999a08b47fcb7b78b1 b7844eadfaec7522852f23e36ef3883cb0b1d235700d4311f7f545b3ae76ec719fc1af891ed66c22 01963203ab3f45de7cff8b1ce7038f3e4e71fc51a5f1fb4e508666bd93103dec7022cea35fb13086 fbe38edc33c7c795f12565a6ab501d1900a93c692e27cabe349e76d176697a819cc3cc5067bfe517 f301b988e74f7dfdf72caed091999498eba2aabf2a8bf5d8c7585e7614ae53da9db98b571df0b621 07c2e8e5bc7f8de54662fa7e8712978c6a32bc7079f932289d15bbc10cd5511cd85a69abe7da723d ffbda0fa224509034c2a8a710c4fa9a969cec3ea2b1c6c3d257c6afb59b870a8e389752b2f407457 08a779ac92bb3ef81aaffca37c3a071a2c56c37e051b84cf524d88fcf7258ea7b5ed33a9f7d758b2 d576dadf699652f57549bb06d44bef1dda5ab93bffb9a6e8db6853bd379ccdda88575bbe041f7710 b838eff2b693efd5f48fd4a1d7b67eb8bae2203332fe78c4a4e6e8784dd928d735e979ea2edb8dd3 71befd1b0561faf3a65caaf14cbcf8f0d5b8f6763977ddf330709b5637f6ed61f8cb3f29333b16c0 16098a68dfe83c301fe11fdbd55e7f8efa13ecb927f0e96b1032e75794b3c9bb71adc2bf88ac9706 17fd8fe57cce79195e5af04fb24753e3544169e15459d3e5729538e483ea7158416ad3e6be59bb62 db7a7d11bfaaf5a7b2fc342405479ad5e94c6c5a662d6eb59bcf772baeb529a0cfda07e0d01e4ec0 190cbfc007ddb5dab287f5a106a1fd26f1a060de3ec2d8eeb98633afbae9fc0540d6f932a3256216 48d085cf93a4bba1ce27743a5983e86b77fb8bb00d73c73a5b02c17663c5ead374ab3928972acb81 0f91028e676f0abf153774a811e497e834aabff928e2e00c9e23ded8b4c6001ccdc75be4b225596d 04528da231a556ab7632611a467b5a3f0da2e9ead919cd1626579eb7947b3adf364494e6eae33d03 cc663cb36f2dd185885335165ec36ff6b4997c39bd0decfef51c7bab267feff738c17e74197104ab a2f85dd67d29d9bdeef262040d1410bed14ae6cc545515c1b5865dfa4dedf917db7ab8ee940c1afd 5826083407e6a984ec2ca30bd76ddcc669fb832a5b27a9dcff751e5c34b8c06b63b5b6779a059e6f 7a141410df861f96b2bf404dd99489b8ea7b1b4321d68ecf43514ecc6b7a5e12f2a79796cab0992e 55eabe62dbf670ddf6b7cafa48ad858dda3f73db5ef5bed8deebad1f8bded9ef1ebb1f354561ff45 57ca21d6db4e4677d7db63ab2e7f8e0788e9e70a35534fa8914c4ee743757d568f83e605f1c7b34b 5e695a57858216379467b8db6dc0d3772b9f12c510a841c54bcc4a0f9fecfdb4f473f487d9cffb6b 8abd24464dde655d367f41592efd05aa5f8ac5335eeede934739f5e8bc827783a272180d9bd509b2 06abe71bfbadd147fc5e2bdefd4b5de81159fd5bad6d1a5ac52e9a8d3e0136dd10f8a9cf3f5d5bf2 5b6915f800c3d66c0e1cc5630ecedfb3f15fc2ace66dc53ab35023bb357e11b987314835e0fd21c7 3b33112e779e132743d4e02f1881e128ec263d214109ebbe472f27e2da13d96b1d6b50f16faf252c 3212af4ff44a95fe0d5194818237bf38a8671abec68fc87036aede86df508c098fef9a233cecaaa3 db1614c67a171749e4a86be4715f8d28a9bebe4cda86034ef6b8cd4cb9b312ce5a91f9996d92ddcf e23f5f3cc12ddd94e33ebde98c370c0bc3d4a2d5fe5e175b3017588e3c3639e054de717b6baaf2a2 9a9302bcd7fef4c79c0344ed3d294b18df7948c59478c8aea87f15b2d202d5f26e47aae939b2340e 5efede1b1d0a6fa87ef253ce308e716a0eb143d5fcac3eb49508a39dbd40bcaed3069b9e73aa1975 d76c35156f588877efeb1fc6fe72062601dbe41b613b49d8f0845eb691e17c9bf1b0dca2e3cfb81d 27b1dcfacde02c69ab84a42d5027d23dff9cada4fd845f77b1cc585fcd5eb0b14bf1664b18d875fb 454fd55d7c16ba7bda83e8436b590d0e19007d33f528d2c73e9706c7077f8f736f3fde9e2870793a 57a6d5eb39859bbf398f0ba39e922b2890ccf57c5acc6f6e9b23ef5394ec15ad7abd51e4e0e3f330 0dda7d0e59c87d16f7aff2524a76f95d21bec15f44963ea5bee4754a2e9de9e5eaad1696a549f12e 3fc21957a1477f5f3bb5b35fc1bb3a666ebdeaf6063c6a7d5b4c6a4bf923d63b5fb45f0ff476afd1 5627c3869bfd3d3d8063cca633440ead1609b45a76dce78116ae9c00a7f61881405d3a802e8dcfda e0a3f369fbfb5100c11b790a458ffc37950fa3d4b880d3c763db199cb77167778b3c841cec7de4b4 fc445d5a9eefba8ff9e98e8ab60ef4caf711d53375dcc34062f4c4a28941f531f7b9ebef7b063198 5646a74101e3022ec933685887a3ebd087d01f48133dee6913fbd7511bcd6f5f73f4e9ccc2b1b97e 1dc94eb06d90dbcb8ea6e65ae3407d9d703c7101f536c51e8e393d376ea399a433c01cd4f1ca7cb3 c1cb3423bb2da6662238933c5ff262aa5fb66c592cff4a5d6ce4103237a96ddf7ce9284a7c94910d 615a1e256245936971594b1ad23c7aede4fa62a4caebc19250d80505a880f97ca987daeeac49ebcd 4147f4c34ebf18df8361eec9cb5fbceebfe6fbbae858d1b9ff73dad8343e593bad4f0439c70aeabb bad3ea7b43077ff8e5f67eed6f5a8e1fc843c30907cd471295b8f0116dd9e328d63be2312139495c 02fc9758de4ba75eba1ca3a3955ac9a535451cf71bb83be86f3e39fceb07b7cdfe906b17f4ebb5bd f48c6e8749373865dd4d7e3ed6f4cefd7807a6977c0375d393cd6ac259be7d918b34a0b4abd0a1f7 3736ec3cef741ad58a19a9e20fb287ed9f68f9963fdf3aa3bdcc8f81be6bafcdcfadfe7acd7ec4f4 1e47526b572b6f278049491542bafe7dacba66ca8e3a523d2a3409bb30c51c18d29234d86e5792dd 106044a7678494bdbf4b5188bcd7e90a4d69d5de8b6d51db187a48c2e854d99637bf865362ba6d6e 84ab68ec95ce78ba91ea315a1b3a0ebb96577b7a2d1d85d6f6c95d874f0be04c4af8d8ebcefa6216 803146baa088e2cfb2a1078d8376213cefef18e5e76c11cb47d549cc4c82f23a8084688effecb022 73f70459adbd73b1bb499d9dd7a908c97d64044031ab5bb3067c14eb2f1f135ad372d320362629da 51ad3ea42e64aae1f27222698747f268e62f2a826b72280c5fa5de6ab99936bd75fa3e0a0ebef901 811e598fc85ef3b58a08d40e9de9fbce368cfbf57e1473f1dc40d6537dadde93a3cbbf0064719c02 319694196526a0588af803270d83eaa9d611022da9f1ea4c1939d3e3ad24125ac31c76a6d1d6b077 e0f9dfc252d57d1715a1646fb18d6af397650b4ccf8fa5b6841ffdaa3020e48bde04783070001910 b227de98468cf4716a55e22efaa365801cf05daad72bfd321f42aa98d96505dba1fd21c6df67cfba e7b3c0af70b7740ebc2f304f73c2c3adcd29c08b3723b6bcf3725847aca165b7676f0905e0db7d54 71ae67aaeefb7c786b54d2d1fd3cda096c7328d48206ad054a875d62ede75c08b3b731e5bd4ae065 c62c175589f9b14e249f1e1bfa25a31cb5da19d2073eecf645f9264e146ee66cddd7bcf1152d19ae cadf7abfc14994e888d9e965cacb5b21e3cd5a5a9570776cc8a7d757bed2b749a8524b2451e0595d 5b369b32aa48efd72f04e455b7dc51a3e1742d3f5f3b569692f961c2293d5db6d1b5220bbb26d4b1 3694202b1792971f76d3dab7ba7a6e10fbb826cb7db8e5ecffb0573b73d6451eacb901eb33abad9a 3ebf828c5c586db06f82bf45bfca723928c9d2ddda5c1aafdac9792f3e3b99b28c6f9c9701ceb2d8 dd49ae2ef35c593fe733c3e1325c7a16624c3d8aa7aaed39682a37b15da393ca8dbdf2adc198dcb7 326d5b8c877b1fba969bd22d1cfdc8ad9df5ba276760822b69edcf6b6cd98a36a67f73a7d27e7fa2 07cea32ae9e517b9963124bc9dab430c4bac129349393576ffdedaa51a2417ef2039f72aaf34ebfe c81dd0ca5bb22ea44ab9cb8b629e05f007d252465f5b9d2c333dad5bf49bd279463cb78015c7cb4f 536c4a5a2a38367ce4add0e94cef12874cfa0be716622efbaeea92b6d48c81522c0db30e83d6df5d 6bc06928e3cabef56bbd65703d22a2ead82fd2d2e583e84813271fcac100fd86abc1ed2dcd56c989 6287afdcf1bbd3aee4efe875e78ac367bd159475292e3de08dddec3cf6fe62db94a84fb967adfeb8 7bbabe735b099f7c3b0b8a6d76839b8c0d25fa53fd3d9b01bb7db956dcb670a9b8018da3eb4bb058 b90e209d39dde010614b80d842bb3b6d02ed2d697f0bc6e264ec72aa38ba5aa43440afe276706d2a 4723cc60e95e5424879841ba64adf9d5feb07215cc147e95686d6e4e15533e33861c1b8ca5fe5ccb 4cffded8cb735aed2827f8dce90f3b4743c60d4557e47a6166dde7dc3486a36fa10923d40ac041fb a6ed8eee553522622a16e376a4a61ab6522e8ffb6bfc99a43fbc51b2eb63a3d0645f8313cfed294d f54e69ee2912d2a07616ad750b55d450d33e86dacc0063472396f2d0fea4b603af767adb2a93cadc 7ccdfaad6d1f53f5f6d15420d7200fe82ba8f8d2ddb8a8a7f7edd7a5cb07ddafe8b8ddbca7566553 11bb1e8a5bd010ad295cbde78d3df339d50fd48d53aae757089ff4f6559d6caf57addd3ebc96fb3a 0784bd171aaa98bbece907d176dc5b15b9295a21d46968dd6f5abb58f9f91f94da6f9796d2f91dea d6f973936f87abb5e7f4019f5ade3353e56bb8fdcbf002124d2e5f57599e0fb1b0aa3caa9e70cb70 a5fdf94263b6313e59ba2b5bf2e17069c12f24ff6854e973572fc3667b89c5a5dfe2c5950e779bca 9bd41d7dd212ae51f6cd3465b4311af34b4d11bc6e6b3c9573fc3cc45e05259a430694e4f5ac7eda 5d54c63d089ad15449b252f61add30589da7dc41d146a5b3b0104642249e36aefcd1d95f71688c7b 74c9b902d58b1cc95ad829af94855e9ef33bbbfc619c585cee6d61c9ed03931c2aba06578ba5700b 1abe7e2c0efc2cffb45f7f2147aabf5d2cafbd2d080cc4673a1ca8bad3c1376292eb4ab3abfea8ba 19aa3dd16124642c0b11be30e0015ce7db5dec2b43979aa69d29a234aa61e04bae38f94b5dc95b1a 8ac266e54fe8ce140b72ce83482cdf5bda1ec7504355bba64a3ee643354a004d4721579b0d68e3d7 3545390d4a57ad5a3a4a3d4f9a6c14d83fcdd4d69120d63234bb5b4fb984192fbea1dbbcc48e8c87 4726bac646338eedb10d7de2b2476d12af2522854555bd7b585b25e99507dd384951543de74cb125 ffda3284eeb5a239cf7b17d0dfdf37ac985634b2d41484b5c43c57a7fbe0681bd3e385505d7876ec 197fafa1d65dd95d65c7f4a9d5be636ec2482a87863295596bbb011e1e9d9e201daad673b6402f81 2d4a8f5fea54dfc2e643c8b36fdbb0bfe6412d1d291eae9930af9a5c4c98edc6e71856c5fe377a95 50540f627ea134deb5bd8fed31596310e13d95a1ebdb0efcd1df78b9f3634fabc51fbd98ee34c578 b636ab3e29ff1c6a6b783ba919f8a457b3a8a3abc79e1b8ada8d3a4d597b40d2fea41e3dd40df01c 12d31379b01e092e2887f3e00143df74f317b78ba3f1a1712f08964ab6f6c712a68bef012e6fde10 1c7bce0bd63001f811a829c1b555cf88cb8ada59d6d19ed46bf7ccfe938915762c4c924b677510c4 c357715ea710349c6adb16ccf8825b191e9e17a32d34136e8b3b665c64251fb60fe15de45262a803 edc7aa1dcc269834b78f3ff7a01bdd04cf9b7f07b93c4506159badf461096ac13be9c5e95d536677 07aa28138574eced417d099d5cb493f0a17443d4b1d69aa0e7f8dab8e7daed7d251c482647fa155d 256a5c0e4e167813ef4c72a6beff22ed7a3a1a64900ff023f8182a6827b434cf26aeedd74894e5b4 fd5d39ef07db77b5f5b864b68b7a621d3f7e5d64f1dd4e97d70a63bc3ecd3b25e33aa17552b2aff7 fb898d42dfe15cb9b74fae1a49d6cfcd110f491d714987bdd9b69e8b3ac23716d685061cd36f0c5e 0cdaf97ac6d69be0fab9fe72f1e0c8385ad6dec15abf49dc20a4711d28c71a423a42175cba25a466 7be7de18b3e89375101e7ab3ef947cec270a0d9d7b57a95e95cbccc9b21f6be7b23b46612f1c69c1 0c08d5b98766d1aea833b1b0e1711b9fef586d9def267e5e567873fcdeeee86bfd7b72246775d30d 080070356f5a2638cfd7eabbdaad426484feb3f887e54ed5c1f884733cfc724dfa7797b33aa59725 b87aa71920d74032c849fca6ca48ffe920d706a17919f044e1c59f1ca9907ea47615c78ae6c670b2 41dfdfa60d887b521318134e16aa5798d0f5f26f5561b041607f8faffee87d2295f145d9acd825fe 01ab59390ea14524dd34ccacf22131c772ab0b0137616132b227d45e1977cd26a1601793a67d3efc 8d827dbfe262cddf574d66ad1e91f5acdb123744f7e74dd1db75bce12f1e65455efc895d9fde35bb 0af9dd36a47310759cad0f23b36f5c95a570f23e59d4afdefa4ef791145ee09afeca50ba5d2957ce d223e0f24001de0fd3e93e1a6ac60985bbfc26256eba637f1528e592093b1bc7f1ef183bf73da53f 042fc6551026c856d65579555d0c349ba83dbc7c014626f7520d6fdfe615391e2c027d35cf64073d 3e9b33a35c1e6a34f1fdc3f000eaf58fbc13aaa8dea274498c7e82bd4de6adb9f2873492ff803a9c 591f0b43bbb770e6ee80f956d86b792a99ed18c7ac5d63e68ebefb5b45276a9e6d70b5bf71ce45a1 af8a35eda5a163b2ec49ad4fe29343e6ea4dfef2844ca087972386b5df69ec7d8e8e67c3522d341f 9b9d68d2b1a7f677c84ad4c55a32f81bd2a4d9d636cf995a624e155f27ca7d6373b76bc1e4738f9c 1ba92bac53622c172402d1e2acf370741a5b9255d167670358201872ab8b3f2fa47665a95c7d92c7 a3077fbf7cb2d9de69af65ac6db94a14d3e070ac966cbdd8e0b386d976ddd6ad7d323b873f585faa afc09c244a53bb9d5b423ba2bd3f025aab347ce6d3a40c2592a9e495db17673d9efd0a5d2cb3f3c7 81bf6adeade1b10e8e52a0bd7348c532f427d7ea22d7757fa117d78fadeef8a46357ce7022f0bc30 8f0e887d17d251de11580ff8f8ca5d1526e4e151086bf95075e64675867599f82a8ebbd4af3a688e bedb3208008a2d2123324880a1b652b7ccac2cdd919e172c1fb325331d3333c99099c8758d438da8 bef1aaa4b02e6219c468d9e1db3d4af29fc38fae0ec6a9e554a38956bf9e84a862842d8162df3fcb a5f2da90472f773e6fea634a5765285a7bfb007975ac098aaebcc9dcc85877890ea8c0a55ca6e78e 1fb3c1bba78e0ab030caf4ab1c80df5d837e7c1e25cd7a528a5be30865785af380ca47026995d3e9 f55f53a3f22c52e8e4eee865139a58941acf6c6f5cf442f915d0fccbcb3553aa7cda1ef568b3d466 e0bdf5d0d1587b742d8e5813931beab752d91b04bf7b807b1bb695d9488ba2d77bc0abb5412a3bb7 d5bfd7d35fee8a2a9da25cd35ab0959be3d5d4d770d7ff7b8c402d9f998ef233048bf85faa22d189 160169cd8261fe139ca09a12b6edd997df53a4e30667f0e67e9e214c05f27e6dad9eaba3f5848f06 0602b39f49513f0d1fc33f1a30bf83df4a79ab36f9ea328a340754eea3ad148ebb53c567855e46db 6b11737728ab38936a110ed3a03cb3a67f32d19ca8e749677b1123ad783a5f8d07bb8a79ff36f825 4bbe4fc1fb98fc721a1f1cc234ec395bc83dcca236a593f1c71d76e77b6bab4f1dac9eff768c313f 2f7daf629b3680ef71f58f49b3ab53fdec89a71d91093cf627e34ff2a13e9be4342168373a0baaa3 777d50ac2aacf031a6bf551e4e5f6e783062d6b76240b66243ac6c2f9afc9cd564689286cbd998ed b375be2e4bfa1f90840619f6c74a7bf890689749ddcc6f7dd0c5b3294bbcb23c59259db4812501ba 5202dd67ab724f5c8b49b9f19b235091a0cac5cd1595cd806b535226154df6e748bb3638d4ae5339 6b6e75db43a33f6d7ccd36328364b87e7c540fda4c4d2183ce5d36591e3bafc54639acb5fc010141 11c8cb31d51dab6a98ed966ecf737ed541541c17a212eccf8cc98d35036cba72535e8b2d36a58f24 21aa349999e7fd4d8a74b2da9c3e633736caf71be265350b1cec572ea23157606b375ff70836cfa5 b772315f757dceb83d6d4419993b156b3feb58d25f36b445045d2c2befbc2f81203dd0f118bee786 c71b1b677d9486a879d2304d5919bc594199173829fa75459f34da695966cea200675baff97d8551 67a544d3c2803ff6b0f9703cbe34e2077f49ecc73a0de705fc49c7b07486d33ed2d7ca3330d51bfb e552c320b61bee97f551fc9856ad45c84c1c6fdc16d8009e3cc8314985980d76ae9c230e1a033498 6653fd8d32a8b12d1d7450edc5b4da7cbf9bcb6cbdf9a54e91814424f477ef65f8d4c0707a4fccc8 2d6e36efa179271ee456ab6a91dd196fa9a30f0bef81ea5bcb265d5ebbdeb0aa44d41f33e11c14af f5c3ee2ee74eb8b404ab95f809df9cd8c317912b4206d77f1bbbf98f72fe46b64cbb2c8e995e688f 57c5a875ab904ba9349167db443d4db846957a4b075449d37e6d08508318fd480b72cb84d933e862 eb5bcc4b73f27a7709a2568690e3672c49444b37e26ce84922a2fcd0530dc2d85ad74b7d636e67b5 40d1811a1587b4a60ceffd465b2e6c72e163351a422ed59d220bcf1b6779f80403e6ecdfbb0292fc 7af3a03a1a57158db5ded76d71a9e09b35852c3b9a5ad87c270447fcefd96059a5142905fa851c2f 5e3c2101656af2f76fdc0d60649a92c10d5ad63c04a1d5a246e473a78fffe9eddd358dcb65b436fc f8fd8db6bbd982e760bed0adc7eb834a0ae4d86ad6ce1b20bdc2ba522abf7fd3ab1b6694851cd027 27f6a85ffbc3a23539a7882fce998d6df71d987cbcc63ea55e4faf1f2a6da74daa3b2899a555b5b3 7146fa52c40ac98228c1b3ba6c33fd362f95f9728cd9763a924a91196eb60428d5fe6d7f829674d5 438665addb6f0d7c6d9b565666dad38ec06b518f152fd1879bd6817a733072db79c9b9fca7eba104 a598fb6162fb53b611f4975ad06f4dab82b179db7dfb28eb3e7422fa07adbc9fbe74fef0f8a1a724 7bdf22aa768746bab98a7fac1269bebcc97b654624fbe088de7ce0d9d570eeb8cf35f6ec92ed3ff8 3f9ab66fca677e06b662a4a3566ed043584d0227fb63db5ba1339d1c85a7bd87fdf279990b8617ff b4a738185f5efce88097c4f2b7c68a893af8e2f641a344af3bb425830916b75c017bf2a79d1a0234 babed36e7eafc822e98f046d986c343b6b3d64e8937162951ec0d3e63541a457af0c895b47c83ad7 3dff6f81dc53e35a3c155f4707f2a3273a2e3feffcc1e982eec16cdfb4fa2e7f08d3723ee7d856a8 abcd13aa8a75d42370fc55d694eea2384b48e30ff3e9c1b86117fe90e60149f826afe7d034f7cfbc c74747e2f48f43e77157ffa4af5ca0b1b638b1c22fa511a70f2852fbc9022911cbb2a2cf4987137a 34ba6f3190e9cd462588afed6dd3c99d356707fc24e0d7127a61f7a7f1de849d712ab867ec863716 e5b9e695cfbfb60ce2ad939e2f687790c4bbedfbc949ed2b177f0db71e000fdfe1a17b1b56de4cbf ebecec5ec87fb03934c1fe52a629816b59ac45ee1c69dcebba7a9111917db15777876491b8644ab5 11e70c377507c8d6bfeedd21ec182b7eaef7fef8da0c60dd86feb485890397078febe4636c972f5c 4c4ed5ea79c27cda3b6549901c10ee5bf19878cdd2d103c9b9e3fd7857f8f7621732239ee2573ec4 50ef449a3bb39d2e091775f7536b487bf8b4b5cb3946d9385b4db7a65fefedb145f8e6265eddb7ed b5b24979770bf39d6775b3f824ac18849bee4be88a6b7790f9ba63c917e92c5a32be3f554496e385 61fe9485fe588e986fc1f245aff69b9396113dc4e57d0b12fe785def2ad7b6d69cea6a4f52ac23a0 f427c6ab209c85f045fcb27022ddcea3d7a6837167b9416b88143fe6b8a5fb5749dad89da9e4d585 705119ee552968108e94ce3f487f5f59fe1a1948f1cfd0827cfa9d636889a45edf745269801fc410 6f3f87ea753dae495d68d493b2c24695ef4b41a5d1da4c49776bd494fe81cf259f5707f07778f065 a7ac6be2094bd3d5b5f6399b394c99e241aefedb5f411b5b63a3a13a93bf60787598d9349c6bc6fd 5848089d3ffa566b07aacdc6fb241d77f36f26dd49cf3367fe42948a5b1a60ab67dd7e02c04cd4b0 63496ad28dc4b45f764bdc0d9436890f86bc3e43a95f5d40c2cff9b403d68a92f27167b9d8fb0ef4 f4c9c36a60381f5b1c6c52dfd8b703c79d17cb89c8d6ce267d0bc7756b86b72e62c687b7beb05407 3a8e257dc9e2e6c4c199766fcba261b7c56af7b5f4b591fc5bb0105e020e162158c4456bae055ef3 8fa389bca2aec9ca71e65903f6f4952a73f1d541c7c9556bb4dd9270099c564ad56875d5d8a153e1 d1aeb50cfc84df2391eabf44989b44b4d56f65ee3dbd92a2835e7e96913e9d3b27b3517273696112 dcfe3d18aff61f430984bdfcb8fb75077ead3417aeff1dadce88a8f57790deb6eb89446fd11de7b2 c63889ba05c5ebfac97506b715a2e59be2a6e9e4d34ece8ba9cb93abe257b653a3fba4a68fac5e85 cf3e5c5379bb5c324f77dc4710b2d19f487a0e386cccbaac38fa54e03fa6fbd676e5353290fe94f1 5a956063edbad79121cdc521a91c5e9e2334b8564f22daf8571927ee6394aaffee469aaaf5b952e2 fb3ef4f6244f8a01e6aa8e758288b3a7d35353f0c5287ebfbbd0e6101828c78b0b2b70f5709d570c 20573acf56201f1fbb2106987f32d43b2265f9607792cdfd71a3cdcc307cc578feeb45ee3817b465 80336dad94575ec85370c3d2e2bb7d91e3be531d5161855369aa10657d992d61d8afed1580f2cf8a 8634e828af3f060eb8cf2ff287cd41d5bacb8c79fb0b18d9f4757ea6476f5e3f57ade1bf8d447750 8495681c560f947892c5e737db5486b11164f3c4554abb73f427fa2723f730902fb2766921dc5edc 0cec52ec1b322e8f96a3d65c4f0cec59e564cc6d6c60e2a92a2a7ecc65a599372e11b21ffc144134 b3cdbd6c9e355fadf1e4c53b1c255dc6ba5232c3a098b24921feca8da15eedbd3091d0437b3e96f1 e10a5dcf761776653eb89a1ce3c4cea6a16911f7d79f953ca0a6558eed9528bf860e4f72851afd60 8d78421bd04af9de4cae68cf053c47c7a106d2174cbe1c4e6ab86180ca7a28c2823c559e2725a653 2d9eadf4bf2f7fe074fa31ca82378bc486743f5eacde0da84fcd5634626450afe8e934471c6ebf1c fdf0c6d8f32e6d5ed186c6135edad64557bfb354d2d2f8ebaad7d6a8877e185ea84b2e44f9045669 9194da4f2c7a8b0786a918176431f7b7b78724d546b7b96e8eee7db96faeee62f1085c4dbc068fc9 9a72ff2d612ecbe38baa95ea2e1a2eee5f89580b88922da1ef92e6f9b3aa1e66577de5a44fb3516b 5f948059b635e73b9c2e3cb7bb562a5f5756a37adf1c0a94abcae6f0fb47d553730ea127f7210fb6 b5422f1eef9ff0f08ee77d6ab4cb38aced183895c43848b574e67aea3ef7fd09be8b5aaa7e9b13ca 17abf2a8d3a42a0a47c982c2ccaf69623dde55a7ef3a7dbdf23af78c71b208ac1638baab9f57c02f 5a33a86f20d5dbcf2fa0b633bf3bec6f075f6dda78ac1521b51d8841185e118796a44f8b0be47516 a4ecb5b029a90d1512161fb504b3f9ea6ea54e4863413d07f3da6f42b6a1d80fdc43cd85aa69649a f90af0276de2c785fdd9c8ff54ef48d1bce2d9d1336a44fbe315eea81ef72833e9b56f392df5fa52 0e01890e5b4b6c6d1c73f7a87485590dd2862b5e6522a8a2d74fcfaebb9aaf9d7833821cf573d652 d11056b07fa2cf75b539a17fce146ac9743dfb892c86caf4436c50a31dccf5e3375fcb85d748637e 7addafd7f5e0a6b11bb8ab4bb0c326d06cfe50b9b22c30fcf139f2ede37da378c5d9c1df0df96e9d d3aea254c17005b9bdf151abe5dcafffa8a3998c6f9b2b4be52e6afcb6ecd9fb2cd45ac080a76712 679caaab31294102268053b2a9f743aedac54b452636f497a25e0cb71cbcc89b251e1ae597f5d2ee fa9f00de31a2df105f66df8076f3d70bfbf5b21063e036d563900e07de69d3138bbb286998ae8ddb 38822f24b6145d6cbeb65b5b0f942a2b46931f99e1188c78362142f9068363a37519a164d9802259 78dd152d48fd5e175d1c077227ac36d4c9a9fd138541f7030b06f0dcecac2e4f674a8e5e8fdaa69a 540c7d13797301deef54f792e6fadfad8c07acd0c1146fa2edd5d5a3e3b53570b1941392de5a6987 ff5a784f722c1cbd9dcd1a3f2eb86bb2ae18f431f9ad5cd3d5e9bd3e5e37b4a6960e4a27ad438a9f 6e3fafda6add5a3efe70e1d6f5830972f1c6b8563383a93054267741b6376e7235ea9025ccc10a07 98cc68d5d2fee29f199085b0d09b8d2ba0ce9b85f6a74f8b7f5dba927b9db3c8f03e31f35bb5e147 507c37a2601b731a220e1d5f4d1d1dd94e6e636d10bdcd28f41c358f17ddbf07b4fb6a1e687e94e5 cb2b7cb47f0792667573356711b5502a36f90a683915f5fd6bfc4b36b3d331219dac7d18684c037c 0dfa157468b62f2b51857643a49de3f15a65ad3f8d062703ded465c4491c3f360d99811a1c1e6a40 c095d9b75e7283e378721543a753fce5e8a85ef8ddfec8efebbbbcf49b23508c51507258b4b6e7ec 6edaf0a65eb72fde4de5c397a3d5c37ebf1fc694387f3ebc2f59b4b92a6a2676f2535f90902fa23b 72e182e323888bae7d0efd5a0999ea35b3371139ffb073fc1e375bd0868cfe53d2ac32b784cee14c 1cf2d75774fbc8c29808abac73caca6da9b49aad35322836f6b3e43b8a6212ac4b4f077551c02b13 79df9c8fec4e75994ece78d09027a9f097fbacda130ba07a59aedfdda55e51a35f5748309a1043e9 a17d730f03949ec66f85837658645bfb01413953f4e2891a5cca1b3303528930e4f54b5977164b7d c777b1ce03001ff20ddb516a0ee9d29fa2cdd7d62ce31567dfbe7685f749e50c43aefceec6b2a5ef 76623240557ba97bd0d0316a803923feae8a7308d2cc1b7e031fece5a2945b7ed78d6acf97d6d06b ac5326463d9bf3a10be3f45677737df65e26656229c12a0b4677b18eac4f87028154fe12e12f754a ba345065ca806c60e0e97e518c5fce7c301e0ac9ac80dc3207a9169aeffcc9f426f8d630da350c84 001b98356c08ba86941a1a1e54066d00760a25734bb1cb94072b35af716eb03a2dc676e720fe3234 33dbe70397194d49b3213e4082e0e99dd5344c586fa5f30e525f18536d29d16575b825696b6f3eba c905ceda4e4f8a1041a29e7ef0589628abbac9f209d2fa8cdce6116f1aa5edba8fc9e5fedb6c7fa2 5f95436b0e5f58bbc7ad2975d696ed68cfdeee7f08dcfd725bfdaefb9bc6f23e9f54f606df0fd9ab 73395c377868d103fe5b65ba562d39f560e3360e85e736d07572585cf4cd7ba58813928d82652368 70a032227f91d695cfb27b165592a426822b928711680315f8883e03712b8608681b14723b00f97c da9190072d857b8798497bdb2ae4cd789578e7f5509b57172b50c60e1dc769935d01e70336935ef9 133599d3e627a3e078d07a4b593f3d68c995baeb8bee3dd66efee2e6bff50dc5ae7ad0575d4eb183 0b1f59806cd19db6b2e78c992514de107ddc8a9a52be2390be1d4e6a2072cc4499b21ab310f4c099 c40be8d358e68d5fd9ce1bde577f6c88aab475488b56b69e441c3edf894f7593859971499b399c5b d78bc2debe8986b31ea5c33d8274bae7fdc01753c36365ab3937bfd541c549da5a79fc263682febd 025fab046a12faad7b3f0f94563f899e3e03cf7790fdfbb9d2bb7f0e419c11658948aab63bda4ab6 7b556be3d99fae81ace45c05ec8e9522f894cc9a46578d0043f9145bf85ddd7dd4a55e96d5f7b4e9 69efd5370e3ee1fce3776ef75f53237616e77377876e708756efaff119f0436b9d34ba664a95d45e 69c23df5e4e66f746020fd113aea652aa73ae206da1d0d2468ddda8568b9b47353aed598897faac1 8d2ecb95f5c13b223e30bfb4d5761bbf551106c98308fc25067f121e53bbea6e9b05b2a239632e18 6395043d24199dd0c98d7bde7b7910314146e48b21cc1bd464e022e8b6d225ff4480c04035da0cca f70c28dbe854e465abb2149e1f8aef72d51f488b0db8c386c561664c7a3e40880d818e3c95b67a7d 76b53c89d36bd8b6eb0da30bc95ddc130f4101eb96b55564f9563fc98799fe8df14a64d1ce523165 88626ffed1164bc4727133a49c26260e375dfc02ba3b49a4a69400c4d97823db272058e389145ad7 4a12ccb939f741ceb2ba7bef9d1041e3dea4deac1e94431aecdd83a09ffa2c4acc94e66e6a5b8c5d 4ea0947e94657e7b44f4b25957e439f9058c9beafe1c5d91c35129cdd76848278651f0c7d92e0d22 227723f5c9e0999db7d797ae68790f65f5d41a0667cdabc02eee15b27766b0a4194f33eeba3cadac 2efb06827047f5a92fd9e18cc1a7f47589ec29f5c541f153049a84206df361262974578c8db2db6d 5a9a97ccd6f2485ecc5c79ec1c23b4db4c68da9c7dac9717bbde613c5b10012308465806749bafd7 e3ae7907261a7db8d78dd604f4816fda7e2b13783a89d7a1fd9bc5e5ce3757f22ad46b1f4c810a48 bdf4116677f716e57c7bfdb82ff554cde01b07dc0c2ab60d57e56ca16ecb8fb33637d99b8c662323 9c79e73cfcc6e7829ea407d7fd1a7fa2cf62db39e1f0596e657dfda73dedde9a67baf65f02d73d79 44e8d716d807db159756b2fabcb399e01f8d2103adc35d778ffd1265337d3ca895b7fc50db8ec3da f854eecddcabc47f4d1a757692e0b4c76d76275c83366228d4d5e08d8cfe251b11a1a83c4d0d7732 a5d5435b78699f220aee5a1f27a6f6556cdf46a627df9c7667db16f7a282932f0b4ddd7b6b85b704 f1f3c20f6b7205058cc25d7db9e7adc5a46f975ae3b16defa4ed421303249eff9aebf77883362567 fd17b7dfbd3c6b3b6bd2947446c4f503debdf16e6a685ab961769757e91b4de9b139526e5ea31d1e 87cd073ed5dda602c1d2d35d4f9a61e7ad161f596f3f4ea63e9cd700c8e2567255727f40b0b2daca 8ad17bc19f82caf671ac5e90ce78e6e4b6060ca3a32f2e9f7ecf659593f2ae71637ba633eff6f5dc bfcbc5e035d74771e3ccc70795b378549597a33ee84d45a8e219021e8ec3bfd737c7c518ff65016d 179388db958721d2c9a89ada435a3bb311e35f803df0ba9c43c260d54ae035e3c5fad2ee54e852dc f8b2fe980f8481f1fc5ce77e75bf6ef5d6e3b8acdd88d1db7a6d942bd474c7b8f262185c7b2bd2af 40ccafc8ccf090e9aa9dbc5eebe554bb655d9b39ae94a0081e1eae83396ddca3e11fa265bb1021bc 434f13d78b9ab1cbae2ce00ea3bd32dfcde6e945da5b4cb8b3ca5ecbcbaed159138bb1c40e391b39 2e7f5b3a7919fce8f776eea96f4cce3dc05a95f53584a4e16f2b9fe64cdb961ee879a0dc75f6c8bd 5abc9ee5f39823e81217ca2c2a54f015adb8f90ae4855dcd12bcc3674bed0e155b20d7fbb32844b5 4107b4d15fa14b9c7fbb4f8e9bfbda16d94e97e21e2320be2149675b08b0b5b833fb357ea3bd378b 357abb8b57d74b05b7548607a8d9a84acdaf0d89672709f3cd6ab950f7d874c8b506cb22c29a2756 b9e29b1677e6f35fa14bf61d9e527a335ee4d7b7ddf40f8f7ba0ec5b4647b85afb53070fa0540652 c262c1762b5b5f0401338e10d7623f9bb46771d5da5d1f6e760277c3eee2a26e7996fa543b34ff45 66d3fe57ab2f14e7c5fca65745c90ed9235fd267aed91b9d58e800eb21e443a8dd9e076fae815d6f d2a55bb68cb78c293c745b4cc8439b3b6857107a0b12d85b7666f267a9b8c675bf200fe47095656f d65f7f9d2a3b36a5a7e9c8fd9f3fcd593b7b829b2c6d89d1f455c7bcac833a6f4f47b57efe88be7f c1d303c5defb0faff921395b0257095b5855bb1f90cf9b1e0e9a659a754ad9511a365a65d75e1d14 6ea7555352a9ed01f3e265b980f55fbfd4d9e1c48252b3d9b0cb9cdff460d5244426e5fd3abbb899 2fdcf84c3e8da8d40a0cae82e43cd36e9cb6aea116003f6d76a0fe8a1a558dacd2b0c51ad40a0e6b 77bdde0f26536e3158a1157f43c7d355fd7eff79075952cbeee20176a9f03a9ca19c99c56db2bffe 7c1c3e2ded8406a6a81d89e4052d12ecad048ae12a6d64bcc55eb9af2435932232462a3ce1569547 f3d7d2a1438b895ee63db6b690a6a53b80edcafffaf60aa1fead48cf3a9aed292165454c7d2fc449 af8379319c06e2b05f5c4531ff23de85a018e282606271e347e731d5390762524f2409a74a2718ef b82509a6efb460a77223297c6ca3383038138e91ffdb515a1f034d567e65ee516c67777abeb68986 2cb78eb1e805760dd3ebf3be0c68a7bfc30166b1abeaabdc08efafe56fcc69d7f689addedfa15541 7386890035d999562a798558210fcce8765de7cae973ffcdb08b9b4dcd851747772f27c136e7bded e7191ff74ed7ee9b4d5e28eddebe261447d6eca9ceaf897420cfb13f38d24991634490eaa1d80201 4195219e9c04b9a7eab694f2887f0f9e5f5e2da68cb31dbbbf85a52eb81219fe19ae1fbcbefc6c2d 3138477f744e7c8ebce663a11ff73c25b235dc8335f2da53de1b92e7eb54388c1b61b51a2644acf0 f20da9a9271f64bd11b19484ea95086779801856f1d24bc201fbb78a1d238b61a6cfc5452e16688b dae07f6a223d9f962c0fc87f0081a69576b42e2663dede4b5b1eb0a8ad1775c193307c5af66891cc fb16dfd842628f4a87b0e355bb6ac1a63cb759f4d8c8220a63b59d46bf69221eeb357aeac2d15f71 9914203e338b68a643839b078887abe029f536d6ef7754a350874771fba831abad59f55940d64065 f5975ded1ad827b812849f94defeb5e670703be0aeb7062a9f48744ca8e9f4b7f49f7fb43b891c1b a800f517982df6a5bdacf4c4affbc74dad5858093754ceb031a2a0aba32cdc4a42212bee549b8ae7 1d20f6c65d409e556a790f8c376531990398ac0e6eb57459bf7d959ebc8a64e6fdfcd141abed6fa6 b24f1f1632a8250d9667005bee2d2c592e0fe482683057467a4ede9cdc7a983b4878821de9c0efcf 72d531f77e31f7567aab0be752d6904179ae221fb54014478a40de9ebce03aaedc6fc10fa4a5157f 857b653786e5b7dfa3e516431c96d1f679fa9373ea4a3213fa6ee2f02837c7bd5a24cd63bbb5b8b4 a61b5d87075f8929aef470efd6ceea45031a5278e87e20bf2dd14adb0cafd2a4f318fa50c9fc81b4 3b260159eaee794bda62c2cdca97bfbe28e17e3cb117fec4e0264d571a67a688be96f458fd4e2aae 74e8ade0e47644ecf03c275ca9e69dde4632312bde9a1ee7e29367770b7ee235ec40db85527542fe 606de85e71d850c68ba944973c0f5aaf8e88da912f0bf169ce74cf3f59e7b8db9950e2d19f4f252a 1faf7df63c8fc5bc435627e3676d607b445095eab7f7192ddcf143f708702739bb3f824949c0760d b6a3dfd4b7b8b351c020cb40335ebf29525c3a1b6ad1e677804fdcf6bc78026fada180754b56a89d ca1236590da02ccb59f546c777fd16c34fe73ee70a76568d202d57e4aed84edb4d0e1dbb53f53613 4fd4f8d6f8cd11f0e8b5fa565bd931ee6eac17272cbce2a13815f711cdcfc547e8bf9ca916b87d55 4b04f62cb0ab2dacee99f5943ea5002fa4495957eb4c32c071ad6989e8b65453247644b695a77e11 1f8d4aa26949f9d7d1c601a1752e930f6dab2e714214e6b1c549c5256e295ff25c902b7b4c4a4b06 79288b4f8674fdf27527793cd7531a1af10979e8fdd12e796fa586eb31ab9ed687817ab084b37247 98801eb6da82725e8f7e5d219569b926e025b0432975c09dcaafe54d6b6f80f3591e8797951a4c0f 077bec0f4293788885f278d93abf7d74af7ac82ccaca787e3f93b459623568ffa2e5ab5faf775daf 982b215585e464dc808338417e8e2e77246c73b572ee77d5e17648d83532ef2ad32df49c9f3d6c63 9c26d1453e1f81fb20ef171b2d524a4bd9eef78376d16af10a0bd00f15d6b61b6bcf1703ef4d004b 4502999c9f9cfb1fe7efddd1e4029dfcbaa690d0b9ff77a933d791f5140eba6e4b33b429210c658e 8d3601188ddff14459b84a54a19fca6d5356fda25e9295ea845bcf8d3b0c3bbcebabb2d39a5d0669 6f801bd7534ecbd3a520409521acabfd5ae5879e4a21014b8b2fcd92048adf8432a8bb3bbefaa896 03e0f0272197c323397e0ef49bfd2428429e41b551d7ea399eeed1c0471e6e13d5bdbadf78b11b5f 42abfa2a5565f0ed017fb29eed18eac3f267b840ff56e473ba5ebfebed841af6d515e9f29120d7d5 6b3e1e82869a5cc566ff5db25a3a151bd4689f0ae5d5a16b048fd6805da86b5b18ce5fa83ec995f1 c854b4b6b05a5f5e5a9bab361142859e22cdad1b6a936bff6c70ee10ac8572a5a4ad8c13de6324bd afcfa54011977fa797fad325f2ee4bdc37e96853edd4ecf7ac8294e8ec08abc4df07f092af7d49a9 3edb66cb4965dd30c1b7eacbb3b9be57bee622ef7d3865fd007e6a4db35e8ff5a88f2d75a5ba6a62 aa5a7504640cfee9b2c5c0b715675aa31d0b2da6260a2963a339dc64e213b49e7a8268b496ab0a39 9d5c0eb63614e7577553aa6afd3670e795f7bd725132a26eb70171f99351723c20fee851ed76d56b 97edcc6ed73443ef3ecbe505279b8cd954679e563defa55175bf2deb20329daaf55cff22d3e13957 a15c0c157c837e9c1a5229fb255b7eeb1ec6e7a2855418871904a4b6b84e7e3c6d5a7f17133363b2 b13a6ff5c07ef5fd7f38ba0a8455c12078165b1110104444babb41b115bbe3bfffd37700a5f69b9d d904390f534625db9aa52848ceb48b1d32e3a5dfba1c5eee3cfa6b8da02db372cfa9f894a123ba8f ff50f8e8ac94d78c3a75a54d50f0f7ccde2a7706e13fef956b8d23f37ff3c5a0f68932e1494efbb5 4aea49ebc5dba0c78dc728aa2aa0dba51a087b5877e8f800bfdece5750aef127407cb96e752bd943 c2c240bef2c21c74019492a32d82f6e04d210ab45920d1ea51b4456186fc122bcadbbfc4c18681d0 be7a2cafb4b23abf7a67fc50833fcbcb4aef7369c5054bcb6dd04a2c491b914d38068f88a975091a d72e8e2c8444fd4f6010e37ed6a9d1abee4b51a9d9b5894d5fbfa2cd8f9b92ffcb12dae5d66762cc cc791c27f37068ddd3f9edebae2ad370fa99685f83bd34ccce2280fc9be0eafd76eb6b8710ffd583 9dc9c8eb54cc016cf656fd8ae3e4e7b3efd6bb80637e8125220dbd50274e28dbd9aaff7fa9b85299 e2c3a34c8cac573896bd19f44cbbf2796f59327493dd2f9597dac3cd578bbfb46b373a7ed99e2555 9a8c971eb1415881ce4d213254c11dbadcc227016a4f7e205f75c04dd073d37a69df013feecf49db 6552783bcd954bf84d112947ad5d5708c713bea4b222760eb0b5f4f1cf68f3b761a9b1f4441f9e7b 6822b15de569d24ea68b43670435fcb6476c35ebc16f6f11772ffd7da16025c6f6a8190741bafb65 3c04d6773e617b1d84dea12038f23cbb2fbd0dcd7fdc9e31e03b38480c1d45a99eed6da9cb78cb02 398d10a37f08d9f0ba57dbb3e296f41dafec0735b83ed471180985cc53dd7b0f11bb62eb36f0b44a e77f0a4f24677e7b824e633b8d2b568410a067de92c01f2d3b2c15088edf10c0c39a4804e3c978a3 aa722217ea3a08eed065e0d6064db843348fa1ab89d32fb172cb6f7bf3153ef21c092e59f2813bd2 bac56bff67c33eed6652d557fe60c244aa2a97ee50689161829e9bcbb616d43f17ef73202f003c6a d4f4871b3823f271a8eb83731bd33c0a7392c5b63fe6803dfad6ceed511e0d36ed5aaf41fabcae12 51dd3f4efabf4017b43dad5e06320824573bf55d3b36ecad89501097daa3ce43f29c676cdcd4121a 37a4ad3550fed8ad710af54d303905395a5c7ddbf8ecb8a337e0667f4030fcec4c62f0ec666cb393 6bd7b9c8dad62dfebdb4f88ad1217b385fa7d62efd3b878e1d3c890fdc1a5b6c03517d249a7ca04f bd5598afefd33ad71b657d7d459ff47cfca524c5437f4944dfe8b983c60b898487410d68a0fea585 cfd80bea8fe28c9e88cae4676958a78bbb1b249181132ae3d600d6d974311af9da4840b20063e92c 66f3c7811dade19be778532ff8834f21f1f23eb9f37847aa37e37b23186c4c085b3ef4868ea35f3c bbfcf2ddd8662a838461e7bf6a3ba93157c390eacee02faf193f0658aeb83e5c1a7fdd8a1daae863 585eba55249db86a4debb56b07b9633796c12915b886a7b9e7a392b0bb1b1d97e73b86752aac19ce 3780178ce9e98cb80ecdaedf3a688fffa359906505c61bafc071ba70c3e98f3b9cb5ab8c4ea3e98a 50139886ffc4c7da0a924415afe12adc3506759b45420fa5235fbdb5bbe885a4fb9ed22a4a2e5e8e 0e6d643074ec78535b4cdb6d61249bdf93f3b3b43a5d3a8e9eec381d12ac31fb324abc936c86fa0a 6fb62782fa6818bd3020621fcc067741475bf9d4939448d5677ef4d108f0fd1a5fb554e365e10a6a 26df5ea6fb6c07f41bc563a07df6fc365ad4919f5b437aa5a1a14faabba7ef8b1edfdaf31c60383c 04e5efda33936d73ae1b393cdb65e713e40d3b3574647cb914101f75dfc46b60f2951bd36c19ac8d a70e9ef61bdf78e58ee5ce9eefb5eeda4e64f3b3c1cfdf8ccb6d6ccd13676468e5d5c327796dfb71 bfda51610bdd0ef0f005acdf081f1017f35c243bef1a2e7600763b61e69864bb7967107565461d17 2edec55b19dc1895874dc0a939f04e5ec610559ae34041fd6f5dec436a37803efc096a055bc7328c fbd4c5192bd1e59aa30630b5aa8fe4ee73c9c38316e0b98f5d9888972bdeaf64e59ddba2f35728b7 7d14b1ce13c55e9239e469750e038ce1f86ab95f3f3cce8be067d032d529ea619f24b4745eb396c3 b236d77c7afca59773c53de3500e15aeca389ebf82c11c22d5576e8faa8fdcb94e495b671f4d22a1 0ff5d3a8b6c338bedd7c84e1a9b943927a6240e41f30127da703fc984dd81ae31612ee7ab48b62f0 db8317cc0ec81668dd86ac3f77aca6092393f9fa9a4474a79cda6cd3a4bfdcd60acf7083890ce920 e0ed6140f8ada720f8b6e69d20b623928e7ec012672a9d7ab21729bf20a45c4ceafd99c7ae23a65a 2a7c45eb94d9f1267dcd89942a0f54e15cfd4bce6f1284f304a8696ed25682331ed45bf5ed78a46f bb283c5faddc0affb785040d8390e6a4b106a694a8b08a1665be9431a5dbcfa0b1e6d86ee9bd76b0 8c149f3eb557b1be362a5ff2ef05e86620170fcd36dc0cba4ce5c39366bae1dfd9a09f8fcd680ef1 6d62411cce866c1459fc7858117ce681ccc8fde5176790bf76cbef5604b36c8f7e2523b32ddb2a09 a5f72eb67c5c27f3b63f6729e38a7256e541ed5205190218b9d376e6ea4cfa61b62223b059096033 bc6f32f769bc56f24e61a72eea798b49742cb7995e5199382d78ffc96e477d4c9c3eee6f2daaddc3 f97bdc0d9bb70ef09a8c2cbf655f7d3d4ef2d6d59737e647fff0b316b91285267c6c7a7607b88fcd 5c54296ff4ecb8a074bf27dbcbab8c2930beb5efadf9eccb84f74390a9a9776bb767ceee175b7e51 0ef922f5b0b0fbe7d0939640aacc100f0ffef0f99a675ab85c138f8cc0ddec29a9d1ea39dd76fafb 03e454592cf71bcef60f005171614dda8e359ddc8489006f50255ca7a5d7183a2536151443c21fe3 ffab8612431aac30634974ddb47d9c06eb482540e37835ecd976f6767bdc4393eff5bf6a2287ce3c 3f778e30c3ccfd7278be2e830cdf7a0a71bfcc5ebe593fcf232757d0ceb0d7e8baed7635f30e7a34 05fa9cfa0ba6d8d068b854aeb7fd69fe9ed03b999d6b801a51e361d09a9578c52ff0bbc6babc2e01 e4065297836153a7382925d071eba997ebc4dc488117b045ee24a54dbeb25de128e196995a24e8b5 83b15309b97bfebf0d0f5169dd54b04223ebd303bd7767b2fe773043dded9b55d8a334c4c8dcfb55 aedc87d66c36fc701627b52105a8e5b85f3f8f28f363e81d15b17b99c881f0d45cc65b5ed31ecd09 fea856593350991fa619e06e53d9acc25cf1d40f8c4b6ff8f8483969fb85f575df5280ad46e83b0b e29d6e8b6baad21c3569718e2fec7ec106da7bd2a5e17c57b5acc15e1988db0da84f1dfe7908f1f2 d6925bb0cc78f05cfe55a9fa522dfd2842897b099b2b7077a7a7b4a29e9f1f1adf30416abf8bcd4a df4845659d84fd6b7abb9f2ca9d58d9044922b794c690359969ac55407bdbc1cecfb5d4d397e3283 aeaf46fc17ee115c8bb1f8a73de1f55d88ec894202627388d52659cee3a3e384fa93f4db0672e555 5a4952ba84c91704f485a1747543653beaa90e53e2f17173b9f0eabc59d3dd18ecacb600d29cfd91 4c490ca42889e77a30cdeba7fbff44fe2b735b5ab4a68414f67aa0e2b2b3ed60cbaea9b085fe6d34 3e282cf800ed99af626c29c2248ec4496df0ac2e4a61414badf779e49ccf9d736eac684c8e1404e7 1f2b3c491697db44a513778c3b7ffccf04fc591f7be882e13c9797e31c942498dee8abc9068f4b80 e3c9139a18e97fa7fe5eab0cc733e55abb970d7eed7f06f5599c69c8b13f313ebfbd5feeed0fd427 cb07a98d15ba357e6705adc9ced6d39b54fb37fbc1a9276b483bbe3155f7f0bf0e0f656d5c974fde c5409b72a3ebbde3c800dea2626258585f8c9cd9d33c779b8606bfbc24fc4b67811996fe206d2a8f 0815034aa8293ebf9e550891880a9b8fa1c9f4ad5f94c318da70175a6c00c2e4bbd849c53726335a dbafbd5b62e79c7a1170c286ff4e86531f50a816362a3e1794c1ab4da60d5037f1c6a92bd2bf8022 dc508dd5f16f36378608193694f997618bfe3dace4d42f0eed2ba84cabbb26c52bee7b16baa765bf aa4d959a439158f5e10802b6d68fd10b822e350cb0b60f65a5b02c09642728f262b93d59a8608f2b 5bea8b1986901071ea0b520aae7dd6a7de9dff3be995bafa9bfdd01d3894e0643dc031346bfc9e45 fb45347aa9fc5031f08915b0fb4b9056fe3840ed1f0eb15251b66c04acacbed6d76395aa6c40d16f 9f9e1bdd6f91c9d7d34f46f633513c79e75279d6db3958aedf76bfca1465fcb91be683e6e2f432da 4dd4885607ec66d9f622c2f954b5e59606bab837703da1d6f08cde5084a71f60852f86fc9e53006a 07fa974b1598a0fc9157de77e82467170c4faf88b9d7aafb7bd1df65cd1feb0c25b643ea029f0370 350617ce8a39689618d6c3b4b60d0ce908b223ab132682592f90aa52bd5bb0f977fcc3d8527168ab 0c9597cccf842b30e47d51b5cd92985af4f0789eb80986abd3beaa9bfabcfb131ede88f0590db781 862999db9994a187a956ecf7b0a93f4c91dcc1abbeee205dd35c7c8e2eb8f99b908696b28231238f 76028c29de6c473bd4d8688fb981cf1e8af1fe8307c6a535ba33a2dd608c0f5afec5a14da4d1a5b0 5afbf53401a1b734d7eff721978fbd92937ee0a9d13d2d0d771e0d76f6e1200c0da6ba6c8b9f7b2b b281dddfdb48bde0405ae4716459876162c28525807ff1e0623e1331d6b34d4ac5304bfda0d31f3c 6f887ef2efb8ee2c9b857b9565c4006aac337cbfb59a13206dde88fcd9137d3fb62b9ba42cdf148a f6636cf3db34eedfad5c571be0c9ad5c87414895d3441fef4a25d1da6f199f6c2ab6d164999fbf21 29bfceb83d15bc1beb59a142e0c7e8daf0763ad1fef6b773e4d2752745551fd2a95b6fa0979f3919 b9f3e95ccf2efb689873cac03ffa6468e012eba38feff1751be66c6cb60551181dcfef5bee06dbdf de45ed756cd94eb8adf4321699403af9579f8b901c4fe2f28d47f40dc6d6495061683ffa1323c365 f42e847f566da70e60676d24d6b4a8bd00bc899f9d5eda8768815a5e3f50d94496e7ba589e1543c5 8b7fe726ca2e5ed7a87812865ea088f134a2851b2fb4befd92000d97ca75c574ae7029b071a222c9 3db3e93828a5bd05902e938a317f1ded282e6d7a6007a1d4cb9ab9d9c8aedd6defb9a9af077761ed b08ef81bca10a8e9b1a226dda9666f9db7a632adcd51839f9a64d3fd4a4293cfaca55dc43fce7a14 bd2532338a54df3684a3b53b15462aac7bac110cf3c0d6d461cfd237f0d37082bf96f5675a5fc77e 346b86b7257f32ca1a1fd180589754d558148ba5e5f4ce531008270fb3654025bba1e8a66f6d77a2 ed3cb7b03523172f65544ce6d6a976eb5b3cc85706e7722db458249f5bb8153bc8d7d197cd27dbed 598c6e7712bb33fd45d5fdf2a26658d658199a6b757177b718095b20a2b4f98a187c6f2d5ecfcdb7 dc5910b1260e6c7da2f056393fff81c366af6c090ab9b5fa39917967021743fc8d6a56c99e6a0abc 5cd57df19110e6caf47f534606f10c7bbb11e4cfcc55bd492093b50fda3bc2e3cd3f4f1fc58779d7 491b74d5316f762199433f88a3c59488ccf47e6d73936bd60c06eac2373d7fa911e6e1b3728ff878 664e279d35a83dea739b47e7bf4a4873cf9b27cfb0f74e6606d0d08cd0ad2defab93616c3eba1753 e6327a2056b6b54051f4be6990de01998cb5ad4bd11a6aaed7001f73ad1898f85e696fc6d3eedc2c a75c63d406ab179397c52ea7fafaefa5c5fa75a09ac34d0813fc8e5ef80bfbda31ed7c3a063dea7e 736a01ea072d818d1d1f42a6923fa16fdeadab2c2529570d79267f06ee0a0a64aa5ba8b95a8bbcad 33d35f484779552a5aa2465fa69c39ffb3517cec126ae9efb6f0664166ebe41adbab315441dc8970 a7d8b29ae81a69f46067210e2778f3247f74885cefecab6b74da780e9306192f512fbc9e61fb53fc 7146cb1e586e3aadb1e27e72448df662fccbdf38b3abb5ed6f70f5633023fd68dfc1b4d69176ee17 1c57dbbd3d48cf93c0f7b3c26e931fdd1d49a6a605eff6ce8a56c2ca994da211139c9397d54ea6bc fd70a6a7ee5d2b8ee676f7a87f7ffb72daf6a4fcbfa12cbe4a7737914feb2fb7ce5437f655c3d980 d64ee43634e058c79e6b3f3beca31fa07260db9978b6872ca1748c7040584969bab74ebbf8ed3fcd 6a1e96f0fed1b9b1fd99060e9a902f914dcca9fd41bf522b66d0d878ee3a055f363f671fdd13de9f 38d8a8d2b0ce176ddf9e3125cfcabf1fd0adfaad8a7535476134dba04da7e7aa1bb1aa434ee036ab 0bdb12a770ffab7dcb1e5b5f2cbe67f9c677ec80501d18ce7e32ca0aeb83a36f2a809eb17bf7ed58 6c02aa87e5ba88678f46dd9ec26d8ca997352c78e5ebae5d9f087cb7e85143afc358732b295e8ff6 01687f55277ee79cf5d77ead5465cd11d679aced3f8d580ac568f8cbb0c79ff7b663f76ecaad4f30 1735c8745cb3e67e89eed83915bafd4bab6f8966ade9024b602f9e5ff0274216af2fc5eae5b25ca3 3ef7c0ca088df61b6d4fa1aca9ee1d0f8b0da65100aa2e67b5a74b1bc7ff2b6bd95572d035df1322 8c406f86bc306f6ac3cd7ac1a83dabf3e5d34950a7da04f4dbf99fdb5b562e83afce3d456ee65902 dc11b6ba1e2233cea112cd72a6b4f175669fab111c0fd3996c082b4c9fc3efffcd177c7d32a73b18 4eebf732f8959370d4c5fa1faf6a0c984c73a2d1f205cc5993358ea5cfe4abf816a2befd34228b6a 5c3afe68cfaeb8843a64e6f1ef8d7984e6e23daf93dccdf85d5f38f7b90ec27d22fe95c49bc6720f db4b74cf3b6d603d77a90a830565ed7993cba58072984fffea85e4693278627ed756fdafe322b322 c5fa6c8db6723f3f3bf545c703fe1a5fb7faa7da95804ddf135d2e4ab44f3ed773efafdefd958c70 8cf028bb5b9b76dd5410a19e81467747fcc080a37029048b423fb11bbb0a6e73ef90b0177a3d8dec fe22f5758982a5d5d92e0721d7d2bd067f580c564f11f0122f749cdde83ac328693676f2b5f59bde 6dcfeb63a7ddf9fe8d75746f17ff193d731d2bd9599c4b8faa974c8e5bae1d34262101876f97d23d a8c7b6b08db7eaec4f4e3b5b3f600d510287ba7277bb0d9f725bc1327554726b47bfbb6bcc25ff86 ffba2292ce27a3dcc3b2530c22c01d845d7c9f3891afbf30bc01bd3c4c512bb6b59b94dbfdfd31b6 d570b3c92a142d29690fa225f9b142e38388fb4c47365939c8e369980caa3abe72d554791eb8b72f a6f0ef69c00f70e8686e7c3bbbd4f91c1a87be3e559cc96496605a5915f8da35576108e946808f36 fb3bb5f950efef4b129404778ecc6e2ea1dd6b9bc8bdbea45eeb299e8f46737437939c5bd08afab9 04fa00a3fe8f694bfc2b327c0498ad1b9a0107c1e338c7e3a6c2eaa7c5e8ec79db3d0881fed83364 eec03909772d0c399d6fccfbf8c0c6e86a7514ca16069bfece404268c68ffa23d0be994cdd287ca4 4ebc9165c1f9ff9bfdc43be912f3e1061898ab95494f0f78bce95f04a5d118a14eab1e96c36d3aa9 0fe74aab62536ff7e2efa6998a87f5526ca58d8fe4de09ff05d1bb79d9aac38eeb80fdc2305ab457 f23ec97a10393de8171ae20b4a2abb93f001046eeb56ea7be69c76348e2e7b7122cf91fdfcead8d4 6bb0710aba22001eaa0e2cd2648f310a76aef235af6a8166d4b890f2af8b61b09f56bd19f3fef3d9 89cae2c1f342397f95ce6f1a9c6bdd3910d2cee3a7cd21ba636f8fb3aa7eb9bd6bb1caae2b513ba3 3d7efcb098b0cfdd8c001a2fc77daddebcfa8d986c7b64242bc8295e1f9c6bd95c3ad6685702a66e ef4b832644377a2162572eeaa3dfd4fba4d68a8ab091f1bba17c07d4d08cbd9ddf62bd0c375216f2 f6dc867449e59e41d61ced3ae4a7dab3e35ca0944dbd551337cf0a37da84b4c3021b68245de1f495 96e8a3d033d2f8ab778132168164efe73d3b9812306abebf2b3e4149760b916e91ce1cbaec58b879 9ad880a0a312a38890ce7ba233a09af5a93a74ff267149b15ed86c2bdf34aa43b702ecaf1783d504 0374d97169d7ea1b03a5384357ed901f7ee726b30e4b9105c0f0ad9791a419efdfd2aea740f65317 070d2a84eaab76872ad8a9fe3c5f1b9ea27dd2563ac7db467a0294d15001ebc2614fb44c2c894ac9 d4623703aca7b8c6e7c11fa37a237a63eb59f85fe27e4462e6cbea7c010ecc2fe8b5686ce45c643c 52c26787b237334c4cb3d260c8bc80536ad7e0fb57c997d66c4ff9fb0b2cadc7cf02291be31d8d40 6e5689f33df730b819ad57b5cf9bf9705e648184fdfc8db0f04a8eabf7c97752e583e1a0fd42f60e bb3b72a1624b7fd86162b036bb9ae3ded97710d0da54584b777a4f473f6f20c53837770174428ab4 b55a2acc45af0fbda8c5cd22072adf7a62986fdc961cff2ee33ff973a313c0f3923df6a089ab7d2f 077428a66ba9ce83ce00764d0a333b51c326a7d9f1684e350775db5df8480ffd4ac70bffc6f68f67 ddedeb78d3f3c4520266fbcfc1d62fc79783556781223ef49f41c7f3c0009353df41993d1ec4a15e abac22d2789c7b4c0f887df8f52afb79777aea440f67eb9c17ded9edc2370b184ccf96f5ea3b9799 30d12596f9e31c69c0573e7993dd84a4e67d08d9581f5a690c44ff0769aea56a43b9c9abbff05d7f 0c8187706d6916ea6ebfaa5b03c52a9cc74a6b0f0d26d5f5bd425799a6a1ace4ee2c9b9d6d086f8f ba5b75bc5ec931ae40556827299ab6ae3c4bfe9893ece6f3af36d71fcffc5738366d121b950d523c d68267f934daceb1291957db2ded96517ea2ce6701f2988aa96e8af761f0494e709b825dc440f605 e68e0b682f561c90324e7df4387e551f34dd14469991197f509a5eee0dbcbfdee6ffa38364bb1351 0d78063766c0c9d89e51dabba95dbcc5635f1eddc0746dd2bad5f8ef9db4d7d60a3598ec121f7172 5a1d5a56b7d9b9c5e162544641df6c9847a2b60fc830e9b4c3b50a9a49bdb3734115fb15278975f1 79753bcdd16c8c4e67050d9e3cdd01f5bd9c3c31cec4c5da85b3c9393108b3b75786a9f421594e49 1a79129091ad91c220e6c3a9d773bddf1fb223d681bde124ed655c61e1e45cd897dc32fcfe4167dc ac2553944855c32e86a6ef6fe5ddbdbd596488353534d8d968d25404cea76730f3f56c54541a2fba 93b1a4f777810749f0b819b838ed575ca18ba92175decc60d15c37ededbd8f788d22ddb50ea5f4ff 9053377d9672e80e69ecd4eef5c376fb4fcfc0a8ea939347c7f15179b989f671cca344ab5e720903 507daf0d8fda67cc1fff0afa02c7cfcbe8cacec98be037fa7bf9be899bf1f4b4e4a5be7ed92987c2 d8fe1f9ba387b4bccf1b84faa0dfcec0237b23552abc864e413d0b3abc5a631da64e813410b022f7 278eaa3c2bd34cd6d346d79941de485d686fe72b14b1279f6b5ea685b5524dab5d08befb558db9ee f4f73f02a59fce7263d931b98a2155918f545a0ce028a954304378350a99913f377523b173c3e47b a052bcbcf2a0ccf04f63bc6ff39a0fa91e5ce622d478f6144230e798341e30a86a6fa42b23950ed3 b1bdaafedf2269b7d80a2d3bcaa9cad79ed9caf26081517b5b83ee2e563bd42a1f17822ebe217b7e a10fa27faa1027613a925ea17704614fa6dc9344ceb5ad5ab371d06d4ebc965292b13a35f6bfea61 bf64965ae5c4fdc82d0cdebdd4b2c2528fe70af938ea57f156c433574c6c10116add1bce3118765a 4f69fdecd39c0f772bdeb0565e2a8b56a7e8268343d321b53aa7d78b349cdd7b1c9c5d8dcd94bf34 4b4cb064323f81f7c4af31468c3bce9fa22ad923d4e1c946b6e4d6801256cba73759f0b63a25d32e 8c377a677b719df7b949dcfbcb7cfaa68cdd83ba108ccfc5b70641de489109e04af4e558e240db33 c2e54d7114896b785d7b0dfddc9a0760efbe76c4556b869f13604ecaf891978e1f3e4096b54e2ece ab0f11bd33a8fca9d954cab69954c6da9332d526b69db0d18f26aad59479b83feeaf9d74fd286bed 979ba7c70dca8b3d4a3c694948ffc0c6dcecfb1b697f20ce3aac5fe66c08c6434521c65f4270d5ca 5868dc500d3afa89591a6b97e9607b3794f6f8fd51852b56f625639e2bef8958d1ea2df4210fbaa3 aababb3d1cedb26cb3fde1f46b7bdb3ffe9729d4ffcab531d4501623fd2f8f59a5d10fc649b1e33b fa7e52f595174a548ceb4d77f4d7b894aa8761adc53cc1c9cae8ca20abbd1a1715934ee0c808cf85 6c6814914dca9930b199d692964f9583e6f1a5c7efdb581bd6209422f31029185a1b8bba2f00f501 284ff231577cf37625159d639675a8777deccde85908d29c432af1bc2a995e7718f2f2be0621464b 7c926eb57581957b6b1233da73b0b7efab16a50dfaf8afa80fa3b3746f1da92f4fac5a6c98979a37 20429b15503a3d8bdcf5968f2210c45b49feec8cbaf8e7b7422f9815a43a507b2e993f9f84b371e6 aef6e8d74f9030a9eead7bb73516cfef488d3ed6b9979057ef9793968136c7e86616a4e1ae7a0615 6ebffd639abab0f499a883a9973a32c388cfb1eaacd7d39abeba1d76e3fe81e88f837d2e4b248df1 6e8901b43444471fd9d4ae35f1cb2bd4486fcd4de5d17f3548e333fe258b7d22ec97b4f14912217d 3eb1ed5b3345452973f8c85a6c837c1e6c17d29439e07a9d333b597dea984a75f0f2879b8c60237c 430bea48912b18bc288b1e7188343dfc54ae2332b990428fc02fc651a9fc3a8b9dd6031c88977a19 31aefcf62e1cb6452aab867b351bade5aa97dd9e880a2e9e23d3d83c14b08d57877a7370cdf5a55c 42be94b65628687a9a1965a8836a4dadf6a7d6f5c035a8f995a14fc7e6f7e836d4ff3d85cb7c3f43 5bebf153c79f0e68bad190cef0ea1bd13be336a0376329b0c9aec2e952e760e9eacd7e08e0e3fdd2 73faddd35fe729d8230de661b46e75c558244314e473b467cc6255d0a09e5d0b7a03e9b7acd2e23a cb8736eec64375f88761e6f3fafd37c4baed69f1e11ccc9cae28fab39eefd0ca75d6332df0ce99d5 32794ebfc25f7377b12aabf75a93b06424e83b878bf2d4d4f772cb4783b76c1f39dcd7d1aafa9b12 df2b576aaaf50a2ea98185330af47b46c96ac9f25905750ef4f53bdd0b7acdf154dd32e7ab321fb7 482f339e1fcd503c80aeb7b9a35babd5305dce7d14f9dbc617db184ab431a9b89d447ac6bba4bed7 7eb14eb5339e46e68951a7e133bf00ea416d227ce7ac18fe1c021adaac33e38863d86ab93a773be8 277f7c02f3196fd8088a941507e10ceffdd1b0344f6d45edd381ac0c9b6029ba50555dab77e3f660 f37affa2834159198c74004023e49d7760b7d6434a8616af92f85c87c27c3a5a759555251b989670 996686796154ab55df72e7627d8ccb3778aba9e7ee819812b8edebfa08d4c7d32c0157c385653f8e 97af3b50e73f4bfbca747425fc35ceb40d571689dc7f0e3a92e55dda56317057d4e935df29102375 ad6021ff750e2dfdaafe3d76476b94136124295d59965056b5644a3818500f1a2bcce6f5b1b06049 b1cb5518aa83e6ecf73456f3a4b4f158aa7634752f9fac8ef19cb4cd367ed70fabc6d5cc4f5f9f77 c8b74bad38564f666a6a5529aab6721d186886e94bda89e24d1dd083b54398e3ebaed459af8e8c81 8d48daaa813d3d94cb00f87f44386517660d6d185f559343a6ffe89dcc12a1302cde7b7be6b0ab40 26647b7d5c2cfa7393208bba69ff49497b91eba2498562d9d0c4d5d9a9c89bc071fe0e6523e82286 549ebc2a76be380e8cd1a4fa4301aad1749fd675d3289b25d078770a68c65978e58c9a711e44417a 84f6fe259f8c0de8daae6a1bf0ddf27c5c7a1b3d255932879d68baa0c318868b137dbcb7c04d7b6d ab5bb3b1bc9fdab7b9985a1ab5f8cdeed6a7fc3bb1edcfbd1d1e37e7857e4b4e80988c86857fbeb9 5f16df380bfd63da53dd73b17c195145bf74b65da16b5f4ac386490b77226876113ad5fb6e4b8fa7 3542931fd77a5cf714442fa682f8ebd06eff2ccd244a6703d1b6331c1ec4a97b4a4cdbd84eb757b0 a26c757be006b44edda6998d565839f37a8fb3ee2b83b3c87616706ce92ca51f4a51a99f43ef6140 cfce4b83b3e687cee661432e402ab2d936f55facd363e50f233428d575efabb3a79c8bf82da6fd0b e2d63f5b6dc84dc8a38cd411d321afa7a05bf2314a792e6a849d460d0188c0bf9d5eaa8203777812 96a6828d2b7202f40927e767117fee2a0dc575b1dfcc14a7d4ffe4a4280f387534eb0f6ce5d9d8c1 debe5dd64b2dac6c7ff9bde48ee14ea88148ba76e8e322528672f5a0399b73c99e392be78b90d241 fb48aa6d6372af8cada74f4f4ff3b6639d62fc02dc17e7ffad8ba6b061ec17bec48ce4d64bcdba4e bd6cfdc1893c732238e30a157deb753f0464ebf4a81a97460bb7b27809c2b1dc1c9a95726762b9cb 2be45cb762e8d49fcdb5dd83bb03792eb527b67820246b59f57fa96f3a6025d7ca2f47c392d1ca0e 9b8f908d057706656b70dd73ed0e633ccd79ac01d6dfdd781bc0c48ebc01d7ba5b59424eb9b74025 5f4c1b6d2d8253d0de9b8066f661fb48ad164a40703ec316d6f268ffe60b58b01cee1ca1991f42cb 788256d0e548596cd3997f1055d0828fc29ba6a18cf46ae979623e9421898d0fafa1438b8bd87c46 37b9cd6059608d33756ee9dce1a56fbbb773f4f09985d58931989b9e86bfe527411eb466e6b17f50 7a5bee76f3b8f5626c6ea4290ccfb6b8e17c099d68dec4c5d5fe4cac22c32766c5c2d1f029432d96 8bd3bfae6d9ed863896e57be2f768b7f01241f831616a388eebe063a662ec062fdabb9558e5fc541 6fd7af1032d1425d144341d80fde4bff74e4de6c532e0a6900ad59cf21821ba1e13559fe68fb964b 34e429c48d7152bdeea995038dc5a579db4786740e1e859f65fb3f71d03ebee443cafd6aa03c1d17 f65450b95695cb00d9bbd4677141f1b3c26b1d36ba38b8796f026d3c2cf4c9230f7dbb2c4c54b673 eeaaaff686f0e4bb9832d3d123d3c44da1ba345fc004629e4e7a35257c67504039a49dc35f49bc7e 792b4fdb08663b9378ae60c375d389a7cad95658adf3aa614803db65abcb3d451e9b89a19fcab9c3 8fe115da39952963742c06b6c7843ea0aed6ea170a05cc93c59854d68f42b5fe8cb4ecea8dd14fb0 331c462f2d453de38e560f73a29ef713f37dd3de7686df4750c868aef99b3864fd9949df588b9b8a 7bca2e0f375c9505413ecab2731c2ccf4e34eeb4a9d62aff4a4b061fd80b560f504c1ee9d65154fe 0fd56ebcc67d607c12b656e52811eec406178a7551533f3cd00be7507dfe312036ee78b5d6cab56f e2684594f064e838a549c9c6b0b80de5a3fbdbfef2c786357ee9b6a14ca6b5c8e3f5a50b38bb4ce8 12d31fb309b242779dce764df61fe013f5e270e3da827edba288d8ef3851923fada2459f811d682e adb57919b89c564a95c1ae798a957e7676bc39a60d1febc52aac78b3ef4b006d17bfd0a59d677377 d27a9e92dff18466cb17e434e35368e91ba8af3e07b4c6ff958b28465fb7946b310d4634aada25e4 e42145f6a0495baeddfee6bede9f259d69216d94f372ff719327edb686c171abdd34ec19776cab21 5565b890c1d37fb0f91e4a7d42376b33508146e79ebfe2e475779041852abc8f1fafcc1a35902966 b17658e18023cf969e3a9133591d564a61386bb23bf6f2101beaae5e72820a3a2b93b5383c6b2a2a f43d9e51d4ce7482fff737fdde02758aa62fb59600d332a8a62e85f7d5df500c3948d4df49fd1630 9f713e380425de28dda48f37bbc2419798178a411c0fb88b986b107471676a64ba41d93be194a924 9b0896d59d09c1f4a1fc1a63d840aa56adfa2c97fc1673cc7acfd76a6b4e2669ec46f56dd1d9a0fa c734de70eca0769707aa36d031a53abf0aab43171369fb8e39fa49b8f82100a883914f1feca8d067 5e455e96babd90f2ac22327f1d2b4eda1dddc1696b3cb7c08ae0d9e2cb3caba5eb10f3a1db930faa 91df62a5f4b0714f875de2395720eb1d5de0e2f8c396ef36b2feb97346e7237bd0e13fbfa8ef0720 78e264e13b220aacd16228a25be5a73d03969a557c90d10e0367d376bc3461066e1c90f52ee6f8ae 733ebc4f4e7f4c38e0b63f3bd944ef28dbe017a0942d950ca3b7d76cfaebd3e9c8f6bb3d3078ce79 c8eb3977a8b7d88d1adee6b91e3bdb08fb0dcce81c05f3e5649563cdf61faf1ec03df09a55ec97bb 518b563a9cd942792165cadda4e88f877dc9234209c2732ea29faa8a6a447d21df6a84e46f3a59a3 dd1392aa7a4a6b6597bf9b8a04cc560789d7c21f74a620fa37a1ff964157165bdd4fb4df26047ee6 1aaa1272c13b5007970edccfbb75f56f3e51bd9a37feb46a4ef4d07dbb8fa7c119d97106eb4a6a87 de87318761dd3edfde6b6a51feeb85b5dbd1458debecb78f40cbe026e2ad1c6dd376114ad4c70cdd 72d2e3fe261ed1e95a17699d8b1fc805a1779128eb96b12ec29c5e18f8e99e93fa32aeb0bea6444d 5865be0e10354ccf1dc8ecaea58a746aacebdd2f7055d11f1de47ac46a624653d28ef8ee87ed0faa 93b549ff6ded004699000d7e5ba5d06165ec55160bb2bd5e9f0ab3cbec7307a8127fe20420c7f6db 1281b8c508263d5bfc8afbc634113c7475fabd9328b262b8f8694fafb08b3a1c5a9daed528f76a4e 21976aadef29ab9bd38a7b8ed7401fe3901bfc5504b54b35dc20e8f76b2bc4d1e1d3f3cc4f6b3308 4d5a7f80cdf0d3931b4c2a41fb49a99925e7f9c75ec16e4b8c5efaaf64c43fe6dd6e14dbd5033d86 58c3936b0812d88d34c5776a4839cff833f1646b23c379039adb4e6dfb710c1a8a5b67e7f855dea9 00c5d2d42a719de6bd1b78d7061fdaebecd16f1fd1be579883bb4fd7abbfc67f34ee89b0f3387586 6e7f3701c12a356fdabde5b661bb1155e140af31e17708da189fe56cd13f548fce1751f3517a5a70 0816c65426bdb618116593fb13dc118fa7727cc82b5fd0c47d9365b66f6d4f653f0e9dabab536b88 26685b4abb1490c5536c48ac3f84246fccf9320ecefabd33deee4a2a5274f840217a0c60bffabab6 2c0d87ae496f788eaf1f53c5fe8ada91b08237548968316a9bf3ea8936e9d4b145b2fe818dfaae4b 78a8c49729d46cbc08bd8491bca7a6936a73bfec90069083f571c71f4b437870bc6af799334959cd db109789fbd0b106c344f2b678752e1184e9d35529f44d273b022769d63598a384387b64d6fc59da 104764b3664e7a19252c0654f9500d8ca3945f630571fbd8b53f878c627a9b04fe182c20e6c58bc6 edd084dc8d18682d044d39137c7db6237445dc86e8bcfcb636dd084e9cf5d2271e4ba8629117ec47 a0c2711e3a482d6030f31c3a0fef14adba6d209bbb668adf6f0ebf66118e53baa1735bf6aa699298 4faaf6a8e05fe769c6d1311dbdb14778a46ca407ff05e54bb90c597b7b60a9f515e972a5e0d092f6 f1ef32e6e5c221592a879f61efc037dcdb049d272de1f1221eadbce12cae6237241540421adbf5c1 0eefa7ab674e96cf36a9753696ef58bcd3c4218ae377ad9e7f5fdee374f8d6d654f544cd3d6d2341 5154bafe268e6157733d70dec7c1c45f9c7b17c8153b0fdb2df38cdb9c9e8396f7970d2d715e8ac5 377a7233eec202bcafb645b97541788bbbe99a18c0cfa6c204eb3fae7efc5c6453de32ea7698d7b0 97f9d8a8b6e2a4ff651464bea719dab32471c01a22c7fc4dfd0bf8951b890782d21af15b7251abd1 ca7672e8289cc57fbd732580b5c68578693da5738584e05ed7fde1a5ca6b7a6d9276f66d409369da 15c1bcec984250f925bdb49345cfa59dbf3eb37c1af575896407cab298a8d8925841464d9ec27a6b 43cb937e22efcd6bfb59e7b5bfe2ed6d80b567fee65f882448ba72894827a6547c12997881cbfe60 bd3a9b034ac9546f7dfb1934e495d73393fca308762e8bb3a4342162e76ffaa4f9051f4d0cabb43b 394dd72d89f3f9b2c4ec978dd8c68fadb2526a6e732cd9ac1c4b193ab93676b7ad1cee56ade03171 5d0eb3ee1df7582548ff6bc9d1ff9dc56b6229b16e42badbfd9e91acde70d82f9185e0f469c052bf 0ea10c8d9c0a634dbb8ecf8c909c8ca1c90a8d3be96acbedaea7aabeb1d271f0563558ac58af2f0f dab9776ff9a63dd90ee31ee604fdff812eedf3f6351e029a63b6deae8e64cf4b59793628b9a827f0 490667361fef18443cb4d65458e0b5a3546dceefe42a6fb9de41192f94756283d07cd65adbeb0bd6 612afcf011055f9a3d1e95a132d717fc5f6848a72a0a9bf6f53f4d886e2d9aa93ea6fd70914aa88c 57c327268bf0c9abf4e942432a4e313af4c7b72fba801fe5e99ef7ce826a2d05653aa4d47c357c8b 52d20125aefeeb54d02a07b2c9bbb9c297e45f7d9abe990b73501d9eba5a22da8cdc0b57597801db 3b690eae460a69ee97da14fd7ce4eb9b6baa54907f69c4393ba8e8683fd37c9dd1502bba92da58e4 13039f159d2cdfb44d75b45cc45271b41dbbf8637ecc4643c6034c7e55b2ae3056db4f6d79a9d6d4 c150dbf672833ae95aaa87da2b4156e0b8fb7588ac771d7d4d0b2783c972b1349bb19d49298bbfd5 fd85008cd7acb9524ae8f84d1f2b86603ce51aab8e0e979fbe41f9596cfe3ce9589f019295da2f8d 75dc935c1587bd176a1d029cfbde5120480b27d4852a55d4ec7a7b5e55c4dfa862b15a462ca5344f 35fe62f0e0ee23bccdc37087f12f5852fde23cecf830d4fa7d1bd19b342e6a3beaa86e517eaf65c8 793769f5b35f3baa753faaf59a1ba2746dabd8edf7c7d119270993f14c1b47cfa3cb08dc73fab090 53b71956fe94b3f83ee10eefde33c4af3e89a6bc93d6ed1edad9fcaabbddeaa09aa97f6b31011fa4 0ddb65ec11f110a337fd2e274f92696cdf85d38baf2a19b52022a804c6521a10200d44efc8773ac3 d7d7021407ed5dcb98b3f5cc9ade7c346649a35c28e3acb640f86dfef89980b94c8c5e9a6e445074 c6ec8547f6a36ea4bc5b1dd93cef2ec49f33f77d704e25eafa0fa87caf2294eca358bee81dfa8c78 42e323f05cde3de836cbd4e4d785598b3dd2eee9e739430f94f64c94bb13e697bf3134f3fc40f099 3f57c586f1678a592f8a1b4df2252e3d0cd4883fe9686cf90721970f8758db8c771cc7e2d39542d9 355737c252403430a4aa2e5c6b6ba85d0a6fafc7dc4257eff3b3727ab46d579f193f8456f5c18955 337fe2c8d87176d16aed5555139e6b83ba3e295fdb28cf812e1e8b04811dbeaf4f8f8fdfbc748a08 9f55c93154e05e551c72cfe95f38b81b7aa53152877ffc965d76574dc3d8c3238d0f9a3f84c6b7bd d3d318bda1853ea7a0acfd77ae14c61f9587f2f0f2793876a3cbd8fcfdb255d853c049f6c1e0accd c81055b6af8194dd3dd8562ff6426d3c1c4308104c38f350afde0c7a3585c31afcb1bcd944ff75af ca42bd32d0bb117d70e371bdaf48fee5c5f69fe99fa3a8fef87bcec0039efc4d1636cd3abcdeab4d 6310a6aff32ff4b982c49833d0beb6f24a709aa21dd9f1acb554e96edf7e9b2dbacaccea0e297cd9 fd09769793785643179319d2124f133b966713a364cb5ea08c5b6aa20eee3369cc72a4b6f1a7b3a8 27662df95086a7ccb32277fdabdd9caac4b2b7c3cdb0d9719ddafda93d3f3d1eecbd62ce46ac1721 ae6bc88f0edad6d6d3d2b1af6fe49aab29e294c4dfd141ed290aebd77a54e9385ef9977777a53ede 068fd4d139e5dcc3da4edf9ef4974ff3c5811b20a268ae8fdb8d3adf3d02011f0043337f6d10a616 7e1809e0c05f74d09c12877bd7f6f5878246e1d57cf62b25e00c6dca5ab0423a46088f9a5657595c 25f8385e1bcb8bb71622cedfc99299dc8cab5915c837256e959dd2f89838331ec1f76757d3f4d652 314f6d9bf5a412f5bf1d3b9b5f757d47559e6ae94fe9a82cdc6b1b75190886c6ce72b406a7360d9d 9d025d7c158eb4bf8bfd3481e7e8d9eebc7785010380a36deba860f677c54d3fa9cba54e48af19ff 0680b5fe02b6173d5b2f7fc5b064329fb00672b81c8d61635d819f2be66444d2e3640e32f4ec5a91 b1b00a19d7359d33d60aed5db696987fe1fc1a03e3617d879ccd57ebb9d603d857bacddb8831c765 1134ec67036c8b234835cdeedf0f3ad57832d28c7485c46eafd7ba693d20b9f1ba3c5f3a644f9969 8769b543f2d3dcb40722b6d31739f582df55d0b394d76c6fdc3466e09ceaf8e16b2f5b530d3fcba9 7c0259d82f6551ae6102dda5174be747d55d1dd6a77a7909e8ddd2a840ed4734991b60bc7bb73d50 3858b634d795335c18066e4966d4c81b3b35ab86290f61691654752afaf5ce3cc84ef3b5769febe0 a40bc56c0d7f96f8d33ed914638caed0ef328ef4ba8f52f6fc74d49631cf65ff1993d18b6f20eaf6 6c8c69a7d19a056c7dddd2e65a6c6137bc7372a71988e9b784c7da5925dadb387e661de513d4b525 ac047cafd389eccf5fe4b0b763ab2ed63779fa7f2f6ede147af541bf90eb671db19bb7d887165c0d 53e94b37b2de97eecd3a767344ec45e1c4466ee35c6a97170fc90f3a25eb24f8eea037579ff27571 86ac38a14ee8cc05bbaab1daae2dafbea500f6edfe5211fa7003dc2d31a4965afb9d7594e4badb58 bd54735849065595cbd4dcaa4bc59098df1858ebbf94d0aaad8d02daecef6b5d76468e3508a5b5f9 e1bd990ed92663e6c02514554bc775112319d385ff7ef134ea581d5ef451e73236fd2f4546437799 1b2db27f337722240323b8431bf3d16e677c5ed94e95755135af191cff921727e6199089196235cd acdcd00de18dda90c949d3a7497762143a1fe09739b87d9cff198f46b56c7238ba736650ee1a314d 22c29fdd39dbafe3766ca4a3f99a8aa2f2de069f016c1c7841437db45d7c49a0ef98433f7e00476f a49a07dae28d463d62d4f2e279f7f2826e1bd4e15661326cfe8ba7b9fc64651b2a53b91312799c3b f5daf5641c8ac5047a6483a3b50f1e826902f1ddb837ed4e98ec755f7ffd2d9fc2acf3d9f8cf3f2f 3380f09a53427be1786de4376cc0c353f47bfcebce6004fb667991fdcae080cfaad6b5b2fb43fd52 18eba2ac805b3f5a90e14ebf4c738891ddc136b0af07d668a5039de8f7dda587cc33dc08c53a09bd c6c9d57ec0efdcec27cdae5e9955b0ef7b890dafa0e4902fabd258a8a7d26faa95bb2323917c8b8f 8bf87aebb073200a1241d60ca5006dfee3943a8fa2e5bcc9a1661f2a2faf9dd65de9b67941e24482 9aae40a077fa90344fd2dfb4bc737cfc4577cf73e1a8385f676defcbd7133876efbf250edae0b42f 6c3a1a1e348b9880f276549a3a9f05fae1d2dc2694445fae9d9e539b9379b77955ff71745d5baa02 41f05b4c489024205172ce984531e71cffffaef76dcfd9803bd37457f554d784369384bd86db2090 c565e30cfac93b44362701baf5f792fb163660944548c560fbb4e5ccf9b61b7eadfeaf0325bab853 75ab479008554215a99966c56e9cdec9e01ebabdc6569b641ea5773641816913fb8297b73e75fda2 e1e2c92f156c79fff8608db7429c12a7ac77c10aef3166d6c1ec9402045a197dbc3fe8f6bbcae52f 9db47198f8b2a88fc3cd4d683ad3927efa7954f973f910566eb58348b6a94670686fbd20b60e55aa 98dfc380a11a4820da24d1f8199bf8bb0bf00ef8feb36f0746334de071a8040f6f7e946fe6b4f3df 4ef78a0381fb477ed83691cbe15babde0334a73e042958d7e06157c601dc3a8e6035ef4f0260f642 82c575bad387d9954f53df7b0781cc02c2b753cb92ea5afe06f5a9ab50dd821e477eb95bf25f15ea 87d3d072d47f8495041907e56c89dae4b5d5ef62f7911624645d978b4a43c994b57809909333fb7b a5d067a28c578a7f9c5e3802afdd8148db6c16fe49860ad80fa07530a18353b7da6a76547feded55 9067edff97a2869521cf046758bb7ae32c4501c768029115196f44c2e361bbb2403ae565cbe694c5 3d6a33396056bf735b5bf54654a6ddd38bb4d7ee9001a3502d05ca17f02f154c2133bdcbdb782aeb 0236efaf7f22729beb9071e470fc192231d275a9dff567cd86d855191b05cda33aaba425eaa4f155 8ae95873c02ec5c5dcb0c8efd2fdda877a298de205bb43b66d67ebe2bb6d1e767b2267447f94ce29 5fd461f21994dd9fd790fd64178e55a1f97811ea39637433dfb9aebd59342861076cab056bb7f79a ca611edfbb50b2369f9ebf3991c9c5d8dd949d872fffe829adc4c749fee6f67548f3dc925f8f8a8d 2b929feb76e4a58bf4a74c09d7c1c345bec8b1eb1deb081102976dd52061140cf60106c5ef7e804b cdf1ec1ce8f26a145db1fd9691ca2eed3fbf5c125e5f9f13765988777f18d52f210e0b176873d39a be3f0488f86df4136568c1ffef8dea01291323d870cd8d0fad51388f815d043d8c6ab3543c83e02c 1ecd5080269746437f36026c4b6a41712b17fafbf89e24cf43bb136b97e74402ee977ebc64e84fa4 45f48061f8e72372322c0bbd0df36b0d618fce6e1f52f6310f0e939608bddc763d2077f76d9c92f9 4909870f20bd9c3ed3683bb5122eca403a11855a295c2109403e06e747d4979ea3b0e40451837c76 c5b0de5d3582802eeaaac4b5ee4ab5179eff2b88cdf98c3f2160aa1e0ecab55bbddc654acb3e9cbe 5ff677e90d2b5854aa02ba05f84b21de714007f4a4cfca71a6bbacf7fc4a629bb44f671d3f0ef02e 4690ed56ef71b08d9eee6619b983028263baffcd4cf8ed494e68ae7584b7baa038e464c846be6aef 5526e9e97f29b13bed4a3764cf6fcd8d67dee55291b923b94369eb57cf2ebd8a76d24d2a1e1a0835 c1690fb751743c0a5df0016447777b84c4ae8f223fc8219dcf506e4f1b7c2f5ba9e9a815c70eeed0 6aa79bbcb4cc25d4c6f4e2bc4f153c6eadf716021a88e5de5cfe13ee57f396f2911781bb06ae7807 2a83d45fae3136ee991bd553395f7f2893f168af9a473fb7847802bcaee858131ccf78e4ad880c1b 0da835ddafbdeb26bf7714e8654a0bc7bcf81bfa734d0ffdd5a0e57009eb47c0cb4d5019f108e7be 3ef8ed6e7d180da45e0be13b4edb17cfee28d45cfba91c5debffe0ff0deb90992809181f5fc766c8 61109c4cc314a494530804436563c5f000ccd075f87a04a0500dc3d528e8435eeda5fbc580ef6663 e86ff7a6bded296ed6ebcd54d410b3a5a695534457d5563c21cc1fb825ac8d340b69236f44ccb7cd 21d6c24b02d94f6e21786fc5ca82a0272995d58fe92bb91c78275e8ce397a9ea89322dae94847cb9 a8c71adb68d5abe0e87ebdb4435e1c4fc240c75f50cf0ba2a0f518fccada185a1f5ffce2a0e0ca86 fc8c07bd5225a0cb1bf6ad15ebb5d83d5d2d0ef3e03f647f3b926456fec800b45a6baccd97ea5c6c b6a763694dbf3b5a73c3a883fb22c95adf5de9a40f6a62b5a7ef399ce40c79f0df300321ef59c1bc 570d7423eb364eacbd04e2eb4350c09f771736dad1500ced177f41c79459bbeb8bdebd945f695401 bf164674e18ed274dbd81f123ddac1faab24bb92f482f1dd3e73cbb52e10f5ddd3cf6e4a9a928da1 7539549dbedd37b7adef744dd981f717b35fd16e904ef95a71d8b50ca571878c1af6e998b9ad7259 885b55f50a163dc5f5844cabf7efdb47993f90d7c4c5ce39d3cd273c4c332d72e33a22fbd375661c b53d62b7f167e75e897d33fea01d09f6168f9be79fcb445493c1aa34799f52bfa629c39e96a2115b 3e80baf790c16b0754d7c21f6cbb00def57cd1921550da35469d88f3be9f75359ad8ef27f8dd2bbf f31b9faaceb6bdeffcb1f95b16cb0dfa0eb3e9bc4e629766d05a37682e47a374b81cb6f0b239a3fc e3aa558f1deabd8173fd9efa0364ff0a73e939978ad6908e6cb0bae8ee4ac28badd4f049381d5587 592f78fd9a9064bcbd0d82cf9dae2432a45c1b8bceda082485dd476db7994084d099f8c7d628ef25 f1b3c95f255c8db94dcbec8c95bc4913ed031609a7c92595e8ce0afbf67c30e4a3e73246f91b03af ea9f5d60e8abff14d7f68efa18504f595b89b1935e0b85524c298f8bf2e0ee1fc3953f857ea5ada7 ce08dd9605b75e6336a4819bd98110d801cbc6b3b1ce71dec1056eaaac1edcbc8b4a5a5f87e1cf43 8b6a83e42f865e3f49bc313d1485218174535a2f50c3128ffcdcd2b7b14eee1d1c7020ccd69ddbd8 8786fbea9cb6ca0061287639db45c84105ac4b2c679a756373bd065ab1bd2658c318ac2888ad30eb 96b3a69cb64df007e6ff988fd4a87b2531f6dacc36ac66d7ca64e1166fe6d7cd58951dbc737cb9b7 5ebda96dac8d29913563e711312898aaf1ea90131d34bd4e85a21df90d80834e9d7d86086e93ed54 f3bcb03e479c606c767f824be559994f35a58e7f032a12ea7afab28dd694c730ff0495302bbae92c 8af88da7dfc3ec879816269b5e468f477c1f5f1a72a356926c558fc1e87888faea826e6cc4b95948 e1a6ad3b465e7fb96417ef44ffe5a350f36dbf1a9b4bbffbace09db1578d25f14824810434b6e937 dad7e4d7b49ca9ebf0ba4a84959e6ac7d57dd572ae9d5ed439f79e16d4e44b28354db6c159ad8342 40236e4a6cb07a2fa1c44b9b5e7c7f9a5b6b57ae5a1dceb35215b87710513ed7d3e4b57e81066db2 3d3202c361949aa16a4f9ecdbc7791968f91f6310b317d687b7f331ebafdeea27690e57979a8ea70 817716407da3597c8d6931e58a92ac9be0af10980778fb40b9dddd0e376c5e31aeb5eb2339a9b79b 3c929e5bf30a27bc15c830a5be0759d9fae2ee4bb804b168f0b32aebc88b3640f40ccdb436f83af5 90e160d2b357ab44ab0a5fc7a8160ee68b37f5d7e5d073faf134a56ff5f697f88bafc95a7a6a1d58 66cac4ee6b62572af6c9993883bc0181fad4056e09a155ffb050bc3f0d03abdabb97f541af839b63 761d59af22ad99f4a5840bd264d2b0bf8322b0d1fee7e72f400476fbe8524ee4bb5ef553ea92d851 713ed481572fdd55e67149b7e9f217b3a7f7304e55b078abb8bd718d323bc8b0cbe0cffec6ab5740 d5bef55bbd86c0ce386f56e14e4aad531ca3e593c80394497e027fadcc619171be0c46fea1ef61fa 075f00fcb92bc17e6776e959c2b600092b7750dfd9cfeace97a88d3ae47173899d323250cc387abb bef90aa34e47cb357bd549e4413d3d87f933bc19b33590d32f0eff5de2101ce0cbd6d6e16fa36153 dd6350f78b657bf1a8f5a31a3cdfa7abd82b29cf7cbc33449a1b267c516aeb642ddff2c9e1b88cb6 9552dd5cee820fa17bdc296c6352e18ca5d93c0be6c1bcc77fecaaecf51b9b1f5b7b3bb36547a288 b95a34a29e4cb7ea52daa27dd5a09c7c4cafdfc36b8c6e18dc066a59d8884bf35608ee4e1be9fd79 2cc3cec03ff5fbaba1a748f0d0d1efebe2dc05a6c5549b554383e747d03435e79d9f96c374d65e95 6867a54a34ff2e7b8eb3a796e9e5db0cdae66233b2972c9d3a4ad1aa29abb0e13bcaada0da43c863 f572383ab8d8fa19509f6ce99936f389bd3f16bb46c8fce239b8eea2e6733ad9044b73f9bbca451b 1a8f9d755e73571df3ebbe41ebed97bd974f00e79ea4a5f9b296b85bb955125cd4a29b7d5fcc44af cf8ad25fcc1f28130984a729f65a673b35e60f0b8907674be7905abbdcf559bbf13ad7ec6975f483 1cd494097a8ee2fa992b779e27c43d602bf76fdd057d24de667e217a25273be215e3db2b44cdb6ad a5f3bad79e565b45375ccd9abe5cef4cdc9c124955705e81780ffff85daf8a3fbbf13e3117bebb1f fdc64af5ee817b59053631fcf68ce78d6f70f3a43f3cfcf6f1f0d4b272c26528af92173eda191ace f4f95e22e32e55f539010d34087a09dee3f24aa3c6d45be9f3896ea987d7711b9af0d835153959b1 4b17f8f19ba0582933bb3d2c333857f4db01b2330d775030cb187a7b44caa71eab39cfe1d5c2357a 9f80c7ba6cc0b74722419d8c88b67278b32a6d3ba0e4cb350f13617471f8f99e47b6a50519688745 5b856ecb5fbdf15ce430ec404999d11ebb6ba2ba66739a8a68f962acfaaf840d3a61147b4b30b21e d3e508e77623311c6db2b9ab92d65f60165ab72f46c654ddf05dd0dcf1d4b7dba806b4dee3f4a978 f0cfa3745d7dff689439de79478aba7616b19126a053535c12b9d4fe22098f8f15f7553ad7dd10ad d5e4c10cd33d4abc2e947ed0daaadbe3f967515e075a435c070c94d609ef15f03ed6e8fd55baf9a6 f8faeb3e80861178f83d46edce05ddd9eb8fb3b16e754a3afde9f75de1f9e585e3fec31957acb976 1fd1e4d42412b36c6d88f2af718404f0e023e9cee754266c0d0d14a750cf4b933908a603675f46fe ca7bcce2f9d7d1d9deec5f1fbac520e3cf1f30331d77d3dae5189ae32fe73913863ec424f3201849 a0bd2ae782754695b7feb92da60ef73eb7ec1184fc7d3ea64338db16673bf9dd109b95e7f3ebc615 f9edd52af607beee74c5f3068f1f8032e39e61d8174fcdbc99b545ac6e0ee9b2cdfb156f422d74bb 4bc20ef3ee0d8ede5ced0e9de768e462a4d1a1bc57b51979dd5532f3178ffb3b0cb8f86146a5415b 375bca372c599785d5292698501ec5bf61bfc051d68e7d3ef45df2de5077fe37244537c63b0a02f6 76949f8f7cc2900ed7baadeea520ce0737c11cd48f15b9caeccda83ffe8b84d36cd165d6da8e08d3 c9c977acc9b283d1c56c1a745638e3b1c0f0fff5bb4c58e5b3eeb07818e3f6bba7d7e6b528456bd7 c4bcabf327bfacf8689caaf3916d2a73923c1c303bfc9c6cde6dc66686345fc37d90ba65431f4420 625797a3a4737a5556669d69dedaabc7d74acf9df36fd12cc718ba4c7fbf71e2cdf132705065f7c2 e8fbc109d72d5bfa5be455c37dcf9367dbafc769e057c3817a6a655765d2e8ef02b17987b91c6b8e b5eb66f908a8a3201253f2fc35d51bb10ed4f28886363ef0738071d07582fbe87dfe34a79d795d6b 0da3b5cfdd9b99949e78493fbd47a6af584593767ce3684e479f8adf3facde6865aad7ed6d3dd003 bc5b825c0a30af8614fa92c75c0054157fb8df56bd83e74cbfbf9e0dc7c16e64f5b332ecedcad487 c8c478ef54f461e0dbec1fac47dde3c69df04fde45438b35ce1b8eb08302ecba9dd204916a96b473 20a067b8afb1bea49b99c93ac7fd34f706a5fd0745343d747717e33747e0a7b3f5d4b18767d1fda0 af8e331e8ea6ca7471e33cb822292e82930bf63b584dbce659e6dc8d8a5c8978b1c8bde08014de6a e7cd60fd5e96bd375a0eecf1745833641bd804f640ee393ade9e89e3f3f0e7ce13d416b3a65bcac1 070dc6b9efcf5c5c755f8cdb46293cd0fcf84c507e237988f6a3f9cae26a8c35ed0111138a4c77a8 e88f74f28e585e0dd8d9c4ef87faee7a700962ff204201e8055d6171f5141dffef969049d1c47f62 dfdc5adacfb151664520299cf5cece2bf44934f2e339365f5ae1b8ebef89ba7f86b308ea20929b1e a20bcaf1c7537078ede6dea9ee1a76c66ae58e58d53a76f52b6f15306780744a9d7f7b631ff5c061 d3a02ac6f798549d83fafa10ce0ed0236c8a5d3c98e53e7f5c26fc23cfee268dd169efa699313193 b36d538886d85712cac87ba916c7ea3aaa80b3b439b0aea401be28228c84218bdc40e2470affb25c f60c59495d9ae75793529bc3de3d9cdb02d9eecca19b762a04306c079f778bb03e2ba39f53afe0cb ee3c1c9b38a135d83f8de090173ca4b75dd9b96a8c101c5aa581fa2429d2487611158c9cf12f75f2 79b48bcd59e7f00c7415a79a7e2d4e6db8d0aca05dd94c1ba0c3b69c0595e9413429e7264e2fc6d6 c8e5a6fe9b64ebed9a7ad26dc5627afee6fba830b7dbcd70e04fcbf237eb0b8763d57bcd85a0d17f db9caa362aa0d9ac9978adf980f4091ecb54e5a20e5df5ce1e7c563aae78c99ab7dcfec3eaf9cc6c 8f36b5fdc7723ff4daf243f9746dd03349f7226931f6bf231136f23ddaffabed08ec4d1abe2e4d62 e10739fcd88d006f296520b3b262d2978e90e3bddd2cc3a96957f35ba5abe707ba3c815e2da8eb4b a343c72bf5fb6f15f0ad5638457a074f8e67531e0e9641082080ed85e11d6a2ab7211a588bcfd7fb 942abfd65043219a81ffd0da237f284baa212f1d39a93c1686fb289980e45ae93606689cf7d0966e 317de3300d3f76afe98587e619a7b4763f78126ec3c736d101869ebe1c80a74ac93588d750394cef 424a527effbfec5a830bee1e40db78b3a4e71ef2ee614db60bd623e11d4a5eae37fb0d638c26c1c3 9a21bee99c494d8a9b7f34a9bb27525b9ee542b1de0c95680217890afb27bababd4eb56d7615e2d6 5763d1f9fabf38c95484ab1e7123fb8f87cccdaf037f2f4c323608adfd59bd75751bf7f5f8a8f412 56c472498f480b8fee84f420d66b7461fafb6337aa6f981ca98f12d52e8a8008fd9818a8bf74a2a7 42328e552ef9410ec14d42c858af8063943a40463da24cb050e91587bbe9708c1eef306b2f6e6725 e4d7b50b542f6b3b37bebe3ad1bee7eb6d6fc52556603dd711f59209b62eca3b5b44376e185fdf00 b1e22bac23618dcf7fefee627d4784776beec6f47310ec1fde540d6b0bd489329509cf0fb12a20e4 f5eddc24cd0a79790f53874173eeb6b7173bd8cfb825fadcb564f72582e7a05ffdb420f56027deb4 2f37ff5e2bb4d52626d10f0b789b3d380961b1da6c9ddb5fdffb0b53261894c66362f7a935bcfd24 4fff0097d841fc32cefa55fd0e07da80fcaa2dbd350b1e95280f0e1be9c53f66c93888f78772e05e 1a3d6a89115080d4fd4680d7debf228d81e7bff55c9625f6af70082ed4296f4a7ea6907c90f023b7 5d634fe7c89c22adc07ed786ad157b8bc2d1543d0795d8fb12bba4508283b406fcd7794421bd4e03 0f307657ff0bb773f5afaac146629d6ebfa9efc0147c975f1dc538563b5a3b00a261f90f579fc711 b9e961fef60f496010c867612dcf71ff4add3c68bab9d502accf80ddf935a684bd1e14b2a8156087 f1a53a7db3bf6d35b84d98b4039e4c1c8e919f82d828b17b31be8a510306bfadaa35afbe9dc8b94d 07ed554c77951c28cad9f94d79ecb554fa6a7a750ba6566f9492df51161b82bc90e3dbd4ce1b83f2 cab5bcee671fa56327042d00f8cbbfd236ceba16f293c109d3fadad7d7f75b2d15274d883ef19599 d990c02cbec84f15679e1167cdd93e1af5660d17b64f8f81932e9ab770dca1e1b6d1895c33533233 25b658871d09b069b934edc7a777e3d82cbb7ddd76e6c35f5f201a9d9f5063f7b94ace9403f470aa d426e04ea9451ef0b08ae4d92c3a827967970ed4bd76e31b208fe80d795c3903e33b8da68f5981cb d366ee0a3c320f1707a0024fb28eec617ef3114269d46ad352ebd7ee765f548ec577c7b1d8c8588d 3c18e9eda2c365bd6f56a047e1f15d2e09f7e371dc78cd7a2dafc73a70d838cd77100802b95fb5c0 30de0ed189d01ecb4b7f8dd71a71f544aee9c98ae8fb5de57d894ac5f277bc8a1bf52ae95b7f983e a4baad3d7ca43b335fef5ed3603672dd361c8cb0b0774a7631c13d7dd6dae78b1082ee8fa8353f80 e40bb903c15fb1b3c3bf4ddffda19d080a6a4fbb1c6c2044845adb4de0cf3afd5fdb2e668cd813b8 52f289c6750588c6cd834777cbed53783a9097b04b982a6edcd371f0d93eaec1d9ad1fe0e747fe06 cc61f3f747a6822f8036716b03836ea93ff7ca1003283754a1f1f1a59bb89f077e3d577f072bda58 d0d719393d13488beb2e4c39a54ef1bdbc9dd597415e38f5f577dbcf801ec22e3b7b48a59344e8f6 574cf403d03d1da9cae7ec0fafb4d16e981f4c21588d1261ed7b50789d0fec91f7184651defd7507 051a18135a71cda06ef1d9630c1cfde13ea85786b22981540950861ee634e9ac9351f98220fdd22e b363a6ad45075702c01682f75c33d188eebc0b046c3f1a3266e0d72fd9bd206bcd3edb695b193ffb 1dac241f6399a387d7e2691f3fcf3426cbb7357401f8a12b6cbfe5702f1b9680919dd846776aa5d3 90ca210335d4c4deee9f452a9df426c10273c0c9887a1e77627c8b9cbe7cd70d32761cc1ae2e83dd 7c6afc1f28cb0aacc3e135921db05fcc6d9d5754ba2aa150735415a66ebfc8fda4dc884cac521a74 3d60aaa191279c9630926c4d6fe8ca54284fd2918019dda5774ef866868e9e080331b2e35d785a49 92eded77bc4a7015def19e06fc88ee02526f54e0fbdc07a8663d1c9eb90db817c74f9feb22666616 bac10e93e52ce0a00e9ba2f7fbbed99bd74a41856f2d62df824f5859c5507fed7ebf51adfce66076 776dfbbdf9f5f77a86d544b8087065d808d7eab999ceb66e87f9c334564839699010749327a8936f 05f9e77e8bb241bfd1a8b584738097353e54e09707bebda2ea6f9333a664e28b4fe92179932ebbdb 5463bec7b2f5ed2e7f42656582d612032c27aaa808435857e25e61d1c1a646964142b29ac828738e a5ceb39708b7bd424a1f545e6adbc21fd2d5b5c6de6f8a7a6232426d6d1dc4609c4d6850ef3dc25c c6bfa140f599582fa1facb020de73e7938c53d1f4a9522d924e532f107e4c2a22f07e18233bf4b96 370f4dfea25512a3214cc4fcfcf79a3439e362c50c71f8ce2d67ceb2b4835e7abb2e73549fb6050b 0da942f45f5e5878b10396b91fb291176f3152bc855776feb0c34d9b1f46652644aa75d74bc6570b fccc92c6b03e7e78ee958b7967084fa25347de78fbc76623d9fb6ed96cd89eea9dbade5409617025 e07f94dabbba22a0dfc2db905838dfdf988f0f56f2b13d644e8d0e521eb851edbc7f0ad0f461bb83 4e208601007a6d0e73d6f239d2b2e006dd28b540ae01c3f8af7aa03007c80cfa1da7b15c4c41ff7e 950f6c315ff7a3f2e86fcf33723414abe2e3d7e832fc6da31c1b1e8ecabe0b71fcb6dead46d4dec2 74b662c544be253b2110aba4cdff959cac75afb4ba652768727ba2bb732b8a55cef8a2d991f8c043 fe988d3b4fd2125b51ab97d79da99a1d255aad6b3f7e6322f5a6d0384c582aac7455a235a08120b4 7a34dbb315b02f60e14130802777eb348187db3e56b5051f01fd4e729bc3a9761a4423a2d707f09f 1cf96203dedacf287219884e732be9454cc7ceb931f83d46e627dbd8ac26162d372e61a43ee617d8 1aea4b8d36f1f5da98ee466da7eb2007243d188cbd01004325d7db52889b1ea34213f9aadd9b00a6 e7e5f8a3d71edede9846599b7bd3b068c2fde6ccda95573fc28ecf1b95d4362e9395ab20ac9e4256 76d3a3d279abe4fafe6b3f0643c63857a1aa961321d2b6c795bd457396683c7c7845d7f3c5c35e14 3461774e39826c47f0c74de71badcd2957db7fd715db6622f0076e95ce0ca96bbb4fb6b35fcb31ab 3d1d06e0ec505c3aababb733c7dde88a0fcbe9c45d3d5e3767d1e1dbf1eba5421e382e1b6db736c6 accff379f25a26ca298f005d48a7dc483cbd7290f419594da87d3bf8815b6f24fdc5ed59d9a9c867 5ad9fb606f198974a982f9e0e4f808de4cb7d47e0d7150fb2b225c90b04b5b5d50f706074d612968 b43ab671253c06ef353aef9fa4b3eb48a444c48d41364ceae8732f3e27f8f5fff5bbe559122359b6 95b78da52d197db31d35d0615f7b04444ae5e6d90931e9405826f8b51ad0be1807fc3354f9f3a34a 78913ce1b32a86eca46b6bd9550b6b344ea24f5354bee48962b742f3147dd3ddaf1018e6e173c53b b1ffc7e1ddf5c2a9f25d3c4a4f8d718fe8ec75b192dd38f34577b04e7397af6538953c0921303ec5 f7b597e69d8e6b2a0ab9650c7b2fc842d1c7a541d00f2144f7abc00c3feb9d579fac7f9278e97d3c 6596071e7095565a0f25838eb91d3209c3e2118eebad1628398749e58225bbd9c222d93be27538d2 0f9fd719245f0afa63383cda35c5829daad753c19bf3d5a32aeafcf2addff01e69b34bf107079b43 ed36b790d94876d1d07ec26ff4043b39a13e34cace462e962f5efa19fcc8fa89d12025dac603934e e29719d493b4b5a01f9ab5282f367640b8fedf27e90b4e58535df70cfa8b605f48b825ede7bfb2a6 3ee043c7300da76263d9e4a5172365231c68cdb03f6b6e696e4736d8b498e5d12d3527bcc37d5e57 a4790c475eeb1851f281a623877cc865378a04417de5a7867c383e2377c3d8250328d5b996c6f33f 49bc07bf6f9e9595f83116c7939e37e08f2d5729db96ffa93cb280f5e1838295f9813e4f9fd5a032 47204dad9e8f8289c999bf5c7dc7c6d17edd9a7cef9dfa1d6217dac5a8b14434b6dcf49331b56853 2de3a7e5b0bb01388b869a432b13b5c3ca5acda9844797b4748cebdd5ae8889c85c012a6ccf78221 b0e49e8681f259ecdc526339f10dd6aba751d81eb6bff35cd6456e9d25bc7f88d4e079380990d72d 451f9e097e91c653fcb449bc4935cc6768db56c6d600f1934637084e5f46629cead06e0e9aefec51 0111f92167805cb70d2a25179d581bb85487792dfa95582ff88f39bc7c6a58bc583cc2e16cdf77d6 ad76cd1b8be75f408b79ff263ac2fa1268d95b3fc8dc2cfd39dda52e2f51aaa71e9e5ce88ec0e691 a46ac5d7182da75f6fce0f2f307c83657b7bb27b168b97c69664b44acada1d3c6dda5e1a6d327605 6dde3b1a0e6908bf10a0bf9bedceb808b58b6b63430db5449fb0c5af30f34be7c472e78dda51dbfe ad9a19f40677f502d44f06233fcad6b05fbf73fbe97062cebc61e29495c19aaca8f8dbb62b7f8469 3ede4ab08d00a96bcd0be6ff1981a1a9a632daa616e7d44a666d76ee48fdb265d8586332b3bc211c d233e8993b44b42a1c6e35afa2816e4e5dfbc3b39e764867ce21df1c9c7cba3574fb6e5f541cf903 376017948dcfbbfde05c38f9f53a5d1fb4036b3fe7fbc4a39a525e0df213b7523beaf07859de78db da8c52c78132308ada4af355ffcbebc965d493208cb27d96fc6e4de5b3ead3c17159f1c96158b653 0881d0789aec7d2e0f28f75980e55faf937172377c4e460fadfa8473657a6c974303fdbcf5576b50 e31a2ad00bb68cf9b6a0e770459ccf732968759753273b163abcfb2c6efeb15bf214d3eaee0c81ed 12f1164c14ad8bdef6e2dab3f7d1322cff98b43199200e2de4622d9c9698a30d0f430eedfc65f760 028e1277ec23137b9fdab74c85edbf7cc4aa9ca2778f7ef2628983ae54cb31fb080eb5d8fe5b1f73 3a25a83fdac674c3fbe0ea3ae2b85186ef4dfc0739822e6d889e3d60c77a4bcbebedeaac1e78452b 5e0bab8d7d572274067bcf61bb44313336d5a9ec7df50d5fdb37c4e5b76d51647516e058d7b5fa53 7a2df7dfd6db6dc0ebb52cf6c70bb5b70e2eeea08bfe0a416bbf6b4ff57116db1e0fe23d7c3428cd cdaf71fbfa54b73182c65c92397d562a6cd7005cadeb8a239dfeb85f87b33f77411cb430a3684dcb 6e9506075419ac7396dee85f3ca87ed01af608829d76a9f57b8c6f9ef78039af6cbf26efba8475ba bf6fed41a5f7b4f83e52b7975dda6db9efe26d4bc33c77aedde117ef2cbe3da7931e044f296a5de8 2dce6daf96f25fb3e5237df5d4d8080e3fa362cbbf03187f7a413fa1b2b3a947277b1020e3e679ac 555cfd81555c8c6d0e1b19558e3d8ab03fde051ae02682d38197ca8c639257556e63f077e10d6b0f cc722fcf798b9e629437c5b3b6fdfa924bbc333c0cbc33b49eb9ebfd91ff756e09a7607df2b3abe9 b37e9757eda07c0896c6ee6a4a973faa12afa53450636e676d810dd85c34c6b8ff7d0c28a75b7127 8d619faaf973b1baf72255fd0929264a0c7acec44059e3261d129e8d2435fdb5bbcdc9dcb9b54ad3 a01efac5e7ef25e676199e9238f7176947d9b58e08014ba076082a83aeaf1d762eaca2f3e89998dd 4a6e8ccf50c48b79af1133def3afb6906fbd3980de5af8adcf4c87ddf5e046dec3fe1facec6662c5 136ac3b6ceae8ea6b846f776f0346d584c77602ab76a4d305846b8472f72faa3eecb9d5530f4774f 8c41d5a931a1a03498f76210bcc0bba1bd3d0eefbe71835079757934154f1c837ef61dfd2c8db8ca 1c186b5e384bfc7135b993f2753136e61a71f41fa67042cccb1f1263fb5c3d701ddbd3c66586d2b0 23027bcacd3f88e834edea939e3ef1fa1330a7e3e6863225a93ff22b2af7b7fde0a1b09b01fabfa5 5a34f76d889b973157786eeb7f79863ec82038134cb983f5dd6cc236d8d16e065ad62ae5bcda67e9 90f4a7bcb3a7efeed1dbbd3f3ed2ed9bae4b4263d6dfc67153ab2c8baedd168497e345852f0c27a7 5f21b03f17a7eb823bdba0e566e5ece4559175af6f85c2c465e8bbb1a3f8de777a57a1747e1a7af1 a209d8441fdcb48783e2ed9e4a8ce0a079586315310fbcfaa50e3b2fa1b4251912003cc5ad595e7d cafd660a91c5a2e67a9b2d1ff9f2e603aac3bcbff01fa45cb5f1b617088a3b09fdd560fc76c04f27 a0092a57fd4e5f175dfc1dd7310dfc64beb31f4dbc38414468710c9ebe7b1fe656a56d226dcdbaf2 e170a8fd043036bfc99e2c7a079210cc3beb3fd06883241c3e2a4188355277981e69e4005dee41c9 955b7e6d1f0e55191f10f1a857ce2d77220a02f2c8f36895f55b763ad8e174d53b14e12ee374679d 8f234cf12fc22fd24e17daf1582087a0d305b90430631fe32ec6f4c5f9924da5e79da3e2da2dea30 e30dbd57ece8a847460c57f11154e9e8d4f7790a0ffb5d0dd65e83a7c58e2f97306c202fb9e34aba dcc246a7f02b767fc3e5dce0d57aaaeceb04859d574d69f640c1d79d21078634f538a1a5c6de366f 13160881af3304bb97f5dd492b50115c8ce82e6ac864aff540b4112c45b9c778b9c41834073682b0 b824f82484730baacd7eb63981df999ce1896bee1d52b5de41b119d7e47222668658f229ffb4b405 8ef6c9c2f4c5b6eeaf7bfb59f30ff7d9d612442bfea94c8728f247849d16776302e2740ac0774d2d bb67429b7abbd38c161eb2f32bd23630ce53bf0e9daf4cabc55dedd310567c92991cf1897ea79c0d 10fa7e07ee7ce1cb62f371cfd7ee3840c797757bece08973e9e7b827b0e68d7d6e64d2e54f8bb797 0d5e7253ebaf12777699d2de33c17eff0d4aed4a558f4ded95df6ba134440571c9bb6eccad2b390b 4e2898e1d77bdb09e13e7770c8d4458ef04b4772e529c0ae87cf9d68edd7bceac3fbc42c85d4fa4f c46f72e0cb5f25d3a06df9bb30584dcc5f97c36d0fa64776cd0db681bbe875dd9d3e499b6de7b10d 1a28d0f774aeba448552cdf1777939f4e94fc2ffc1ce49e10fa995eda4d5e55388c3dd302a2ff5ba 6be601493f2e233834a174f607a42f043ed9d77f77ac043303ee7b43e285208d0ad6089002dcfab1 a65f45ea1a1dc464dff6d34380775aa57aeb2e97854e3b2998d191e81d2c5d9dd5fee230da9b3932 9e1e15a368107ee49e1b8bfa5d5d2deccd9a0492ab42fc2eafe746153692dffb9b9e00bd48a5f8b5 ed6bc8a1bffacbe8798e216d1037b2ddc38b2868ec42abbca4d8f480ac87fd67b1126bfe2456d57d 751f1b9bb1c81cd948d46b416514f5f408227260021bb798fab9c487a72575475e8dcbd72ee3e838 d42adb03a80d0bcb25edcd261abc4897e37b67dbd0fadcdfefdefd09d56c525373a6c2a3d0535010 6b51a86cb3fbf92eacdd0f210c32e1d2f9007d24b8ef7342581e90ffb7607d50550d937794324344 0cad9bc43fc2fab14a1253b17cb0efd2f211cc83826934f8fddd85069b4130acb76e6011c0354fea 0666703c463007cb5ddc699793bf1fde3d5c0adc9e24e791e28bc0b0c91f61c714e03673fb3b810f c4534b810580c53da3dfac060eb0bc0bddd897dd47c8ff7d97daaf98f0e3491e31988f02b411cd89 616fd7f35cf1ae07e56a53687095a4f0d6a2560af0da1906ef2fc1f1b1f3fc473cfcc99e7b71e573 0cf85d8c4afd0b8b97a952f2f6fc04fce6fe527db6b0766be4fadef036f38fbe98c16ed5477dc700 9ca0710f53c11606dd10dc80b05fc0accd68e33b17140e94fbbd7cd82326a9fa3bbf0964bd52f5a3 e8546be80006f96f5668fbc769d08480cec7f3e7b04ef4d8de0b6586950a2e651e11fc256881202b 53bfa690f5669c1e15ecd08857e85bc7e87337a98df83978fcfbc212108b89d24ffe9b9064ef1768 d91ec0c7b0832dd7734a3fae0065295adbb4cf5f396c7fb0601d5bb95efc5d9411780f59b0b9cb66 e3a8af84b3fafad8783a4109e86513e823303de61ea87bfa09a76a97cd48a8b29ee879f4df94213e ff61e1c6d11e76cdcd684c451d2fcb21a995a14e9d1e04e14c532376e9096f9db689595abecf634a 882baa31b26f9578c15f7f37e41590454e6d3b8a5fad02c16abb93fd82ea6438beaa29d8e4929f2c c19d4dec20894d8dfbdbdfab689ef5ca2cee4ec72392d8ad42eb7b39dd22bf06c068dd86060e63e9 51d81f240ad45f6a3bb717579010995c1becb2ba8b6c6e2a0f63fd304029213a579c6a35fc443e2b fc4ed871643b9e39c73f3c1076f1d70491a7abd85dd25d2e0450aa03fabd3eed8d1f83660cacf12f 1399d5b94b7d47b7285a2f4012350cdd2dee5f291cf5877d942076138f7a805e58065317dab6c725 6f0d55ffbbf6cede833b5b40f5ab979e54360a896d42b5d48aee15e5a71d6edcce01afffd10bef66 56eb21908ffed04ed6bbf8d021a382bcbb6c8239712bf96deee546aa0cb519e72402fe6ace9f2280 5c56c81af1ffd0cb9f353659485e30156dc874e20f3ea37b30524407ba3aa5f21f0727e0c07b2ec7 994ca40aafadd248eeadafcc1fb79d4bd2425a4fd4bcac35e5e4a285cad66fd8467d6b07f461b55d ea8f595ab3ae5cf873ed455ee73d6e5b25cf1725702186f46b34948dfe256f1fcb6e5bbf5f869eba 23cc8f9ac8c589e7a53566d0d36ec7e8994a95a85fb38175b9771307789eb5b434bfdcd4c5525084 e5ee463b40a8c27a888abf456bafafdeac5d744f67934e849d061d629f3671cdb7a192dc36b73452 6a104e8d7505787d660355d8fbaf12b03157e5fb503829efa35e6a6007ebb403c9bf4fb724b82265 53a7b455239d3bd41ff85b80fe3bc32afdae695b50718b376ff3ea44930bc31ef89362578761e056 a91523760e55a95dc7bb2b3722ee8e328c922a0d971f84075d1f8559ca62b7d1be5cfbde3dd2237a 660d71ef3d4a45bf3f18c4dcb4d89f7ee7372c800dfc94a86ea5f308687292daf07d8ff6469a32eb 9ff16bf37bf2a3e91fdd79638011ad5c5c8c06d25f9e5dec8fa92547c827fc5cc2b200d4a38a940e 7a44283b2e240f55e74eddd49d1014e7eacf54dbf0a8c9be61bfce50402d15b2b9ba67a1c7ae279d 64719ef22ca57697eaa61a0471c7d8adc5e3e5d966bfa0708ccc5ac553977b94c58f9ee7862d6973 b3c609550a5f8708ecec3ab78cd606eda37926d85f21c86a9291f2f5e7ae25b148ef9e18cf7af087 6ce6756abeacebd162d16f1a6827ee34c2d9f80f21622551ada8c3a367484350a41af955cbb999fe 176e475ce604a430c2c7f9c1762727526b8f8bae355b163fa88e3da9fc6c8e8076ee2a6d761b81e6 76d006dd69269f56bbdc1c5ff7b0123661f00f8297cbe201b0b7da39edac8d08d3250a42da57d376 eb47dbd8056fa4fddc7c1d759d6cc5cdbb89b8e2e67ad75445fec9ae65832cd794fd809a18703e1d 69d0d3fbb06007789a6f9eda9a64b6a4b1ab0f224e1d79acfec8ee78109c547b6f2c1323177b8b73 68f4b10a6f3e0607584ea9122062d2f36dc3f5ed44db1fb67e7375c3fe9b6adb0fbd6d79afc044d2 a85af66aeb26c10d9ebda1d3dabffb4eb3ba92c5879eb00ae8c03b67379c4d15b99c9e5a53ed0eb8 2e57710d047141ec5c51334fa079d9018ec0cbbfceabb977ce2b779e4293ff86e7c7697deb3dbef9 59daa6ef489898edaf5f1a469e3a81b47eb377df227e23bc18e631e0674811f749dff691a8a5bbdf 939df2bc12daadd546a09a3829679dcb20f810dfa14c8849a9651bad521095b11f93d62db83fc04e 05f407ccd2dbc29eb2ecd1a7a11191b0d71c63bbdcd4d0d562c5c6ecc1cac551cff504b9876291b0 4257caeb08ae9b7e6b7c09dbece86d3a6b0440b66f5c0abc8177a7dfeb04b2219afd31e9743538cf 7854375a72ab73b51305455f6defec262dc67ad6a3db8161f4bf3c3ac40ed9681b06cebc65bb6263 e57db103c72ff8aa648076b3a183c53b9176021a9a661ab4f8638ed794d5f641d880e2ff920d990f 67aefe443be41f654333d8c7a74bdba7b18ada52fbb4351e22b84cd53952075aa7a43dbaa1173535 fca5f1aeca6bc669be43e3af2a23368d3c0374170835eb90c499bbdbc523f7ad5f32b5f96efdfc3a 953ff2996a9c3c62b52f1007dac823355e28d60b635b2d1813f1bb21e9658c66ed6f6ec7fedae107 5eed5e9e4bae838334bd85a8d96b6c0ae31ede188543b9b5f48ec99e8509c14dbb165d92814aabdf c8af1dc2cbad796fad01f430f843436086bd5dfcc4779dcfa032b07994f948abeff5a69a4ae9627f 4b504de9956a08b7f71e825364b595be0cc0122988a587db1150d5a6bf9519fc59ac256fb4ee6e04 08c77f7d01e350d5f71e91c0689bdba491a423ace9e92e765543d6dfd3a7c9e1fcb731f586597fef 40f40c621fbf7c830a6746ef134784c6b760ae3735e1485e60e5fc5d1981c500b2ccc42f9673f2b9 1ba071f22bd2daf9fada93b45d03fc2b5f18d682cd0404998e717fe3f679aebd2ae686823716d11c 1756e2f34ab052e58efc913d72b95786e665430f069e123235b730b291d441aff162144494bf75cc 27beb38f13e0374790b2cd26ca8fddacacc4a5759ed44aa55edb3c217f10241eead149099e9a9634 48b2210d8a705c7adc2cb7b509118a1bed82ee68819b5ba8f3d1f7ee9c13adda0db1d69f3f8668b4 fc93ccbb06639f89c78fe252db3fbef297e534c9e5e5bbd6988721694e6db0ee5713b266d7fa6845 7a2cb389e1dcd5a37c58220d8507cf63f3fc87bb59addc13b43f6454b25352b709d80d5d53fe5c46 6e760d1ed067dafd3af2c2feb554d57b76786adf7b8f56f7972ead2f01a82cec4cdcd687efdbc6ec eec927e50f063773f8da96ed5d2fe93776f6e7f887e185b7c78323c1029e52433f47255e75fed8b6 5c6bd67a262e95713d6c0d07adf732ffb51fac10e8a7e6b54c444415407a0ec0456f67b01cd460b6 a7faee59a080f6837bec35ecc52decba380b542b9e2e056ed073edab6999fad5beed28ce776167bb 7a95ac553d001b572dd0ddbbf87db8f3c6e4f7de982a61e5ae5476eb324582a3b643566eeef0cd50 eab5d2805afd342cbcf2693031f64d49c35f74c87a7ef98a3a95e1f30fb4b794aa0f8da586b8535b 3b755811247f3a564df9407b77fe346055bf97fc7f3db5833fa229a4eae47e7447290bbee166e3f5 40dc9fa4b3e32af626332e739b8eca2b85908a0ba1b581a1a4876eedfb528e8dceb5a52885173ce8 1430884cc3f0eb6b800506591dda233107e1ce5dffd9b40525fcb313e2a60aa87c55b063af3ccadb 17061ff03dcbed47d93d6868d405209bef8f710efbdd2b662e03526f7c62ac1c1436e1bb883f92f5 974b5785ea45d83971b46d4b5c334fa54b4d3fb838d4ffed0d63a8dbbdb211c2be57665634b6c8ee 33fd1b31571f971a1388d82f623b12ed9985ab83b7a2595945ae4c5ddf664bdc9b1bef83adaabc91 9da36c6710798d15587f567b5777d999f7916dbb9e5abba8f6ff6e22fd65a33a61549eca7878ae9a d4b45316a76f2dd7825172b3b2178e32f5a7f6341c2cc21dfc1674b1599b3b58c3eb90f340ba6d43 c98599bae0ed6a68efeab226c77ad8362062aa19c55f8ee30871f1231ea61f592dcb2ea555b28832 cfae4d4b33279c9d70e4b14870e74844a13717aa9166bf65d0026de2a14daac45004def2cc2ec19f 8fd1d79a017d3c2494fd5a18b0b527374f6c51159a2ed42c3aaea85d7f6d3b6857bf5cbcbf24832b ceb33e6dbf27a38eb3b6bb7fa51c726476cd9564971fa50b13429d1e392c3746eef1f3c4ec49982d 1ab5f4e37919400f3c7657aaaa2fc6c5bcc397d595cbfbac0a796339f3aeebd5ef9a1d5d199f343a 5d6e3f7e69f036add2bcca61737e4ef9a84765ce7e8ef4a16f7959f20df53b6c6f0d136cf706b769 308b86bedadb6a4d36406fedc07a171543f06f3dd2ade36480ec16a4dd84a567a3516ffd0e8bfd93 a141ee5b2461b54b2cf0a8b8347379af35bf8286c35954f95b1fad7fef30b4bef68ea122f0a29928 18804d1cd40ec6808338499f856148386f0388bb525e2fe05652842ccb62083eb7decdfa6f08d8d2 9521240b87c3cba7a96389a07593d7f44c1af8cb495b849708429bebeb751d444a07535aef63595a 4027c005d2db9b172795a3020ce09a3be9490f8a9a6cab5ae29363cf675d1075ceecd814733fff2f 86dd221c78c0e1bb230ec7351bc84c447c03544d953ee9ce010e0db4556fe986de6a21b45bd9e40d 8248c685696cd09d27b0a88894328ab3770a93f8b37bf522274a3bd63e8b466a89ed669d5bb467ff 4717ed413fb1839e4f343f739535bfcf09e63c3c5b46835eaf63af9ecdd4ebabf20222486eee4e57 17cb10a642212ae13a31dffa4d33efee6ccc1460ecdb358e246da75c58047c59571d262e762ebf5e fc880782bff3c51f576d947cacf5a064e0c6d2f616d27583b8800ed73ee59e639343c07cf674ba79 1447858b7c4b73fbc4b4be6837584cdd5bd683dd6f125520b31545de169dffd10cfd9188a5f9f1ec 55b1ca2f0b18d6962019af02a55ebb74e32daba68d08881f94bc616bfe7186021120225193fff2e5 03f05685ebb467e9e0eb1f51ada3edcdd783ab377cca9ffda3ed3cdb15559636fc5bcc1131e79c05 491204c941112428e897f7d7bfe09eb3d39999b3d6842f0f4bf45adc5455375d45d338cfebe21952 749dbb0e1488f5d6e975dbdb9500b287c7097b945d6ec92d9dc8e90502cce420122ccea679a49ce8 cbd3f66b1f0d691a73f281f65a9d7a2307db139a5b5abd3e01be868c0aaf120368d3ddb8d5d2b436 06e1547646ecd051aa3b2c3ea78f7ed60de97d562bc573d5db972236199a63b507dfcb1256eb6d3b f0c4a0c721ecd4368772b967e38b1ca70bfb54fa74cf976eddca1ae519096a1369ab37ef1f95915c 0d7108a9f5a78d279f96a600bc812029a30215ae58a2e676418e7b01b85460e062be3c3bac2ed7b8 d335f0e24060c9de2457d1a85db28c9ddb50fab89825f18bb8db645b6cad952fd18bf281c84199de 7a5b1e87397e158d6f30c85d21541ee70fc4e675e06b9b5e6d88774d888b236dde7b3edadb02879a 0df9966c2f9c3b446ced35cb55f85473bce29596b27306ab4771dcf05b1b8ecaab70723bcb0dda49 9e5f5868a6b0a6ad3ed62e804c7a95da6be90d9d69f56a40cf68ad7be15adaeaf743bc9245790b64 261b910ddb501913e779fbeebd76d5e09a5a7af7d5a60b111d77bd3850c17a5cb1670d78d64d6c3a e9b87f438a7045dee78bdb6a7b0dec7264b15b44bc7665574b7b4b088546e37e602cdbdbd48b8a9f c85fbaaa5b6ad9c34163bbe352fbf5a2b7bdd492a6c26f9f5c72b579ac37a9327953d73b58d2c81d 6313c5426ebdeb40b96b3e5c8c7672b9dbb522c75f1833bf6a2ee646639ae52ebbd7a90baccf360a 56f871fbfd3836d02997b7046f658b2c3f3e42036d0a43d1c07bd13fe9b31a3cb2656399981c3a2d 56c4ab7091ddde5632034eaa0f8525a1f05c5c450ebf94cbe478e242c60a9aef4600fd2cd4875c15 e2a16206e5a7efb9ea6da6d1ad0f06c96c112da5d465edba24f571151c2308c28973203f57c3d942 be4e9024be2f14cae357b0b4af73772fe8e57ef7ea00ceb03facbef6a6ba149bf5933d180b4e3bb1 df9766797037ad7ab3c9ae1df702fbf6765428d5d26263d95b5d53fb3a920e720f1a8cfcd210ebf0 625526db930a779f088b7d1b5ea596b79a7a382767d1f5a5078f078d02d01faeb165ad88a230bcae ca05acec876bf96a6ab00da7f9eed6a6e39929b354610541b572d26c789752792ef62d0c9a1c4d0e 84e1b9b444e630070929cb2fad8e0d698de21800b75e75383f6e4e575b5c6c615b4910c276663d31 16b7b501ec861499a9b1346baddaf7ea65670c32b16f8045ae40aed5e4b30341e82e289c9b50713b 59a45370e74c5cbaed7ea6b46a4b6a6d5b39e42b0d19bb80eb56979d6d6fa64183104a5b9bc182e4 77dc2a33281d4a0b64bbd6b24f88b6804a9eee92d31d8e6de2b359eb92516d394ecdda2cb3096773 afac93358469453de418b6b717785305364d6db9b56b4a73779d16b8c233db49ef281e3620fbd9ce 74f375d4dfea9e905f5b202934286e17cf436ed73641ee6682736b1dd76c767bd7b27619ee619538 a849ee9c33f3841ab3652b6f248629a88380faeae4076e4b81dd2e54296d2b9b1a52cbd7567be300 f51409d836669a0feca6a52734b3f8da6ec5b7d3c5bcc615a05db10a46e73588e70bb410643feb71 c3e5117758a55953a1ee7688ee0c1c536c7f0350c6fc3ae1ede60aa59ac4ab701fd5120ba0d86491 c36edbcb0d81eb748d258011a68c4662433d6746a31add2ca2be0aef40c19d5c2697622b9ece8326 d0e051320d603c5737fa086928a9545e402977754d5cadbdacd5862da2cd58e3ce51bd212fbe7aab 9d9ae079baca791364305060e0bcae01f3332d9ff652c73a14a76d68bf9a73ade67e7e0d0bb9b336 8c1f8cd978abde656f36e7760397d68bd922919beff7492203aa999bb7e8cdc2e4be78b7ec72a176 b9afa6c75c12be135eb390ef37061b24ec1ff709bc506ec1d9e6614e82a32d8ce3e35d8d7216e565 ded857e13562c7557520d81cd5a8876cd0f0d27f4d8bcc26dfd9cc9f800b93cf4a365f195abb5db2 3453a1ed5cee35f61ca2af7293fc0e52ae9907a81472f27a90ce15a02b33e1cbedd58edc90f25280 8be9c9b8307cd9f8d6cdf562dfc0b0ed2d5a2b09caac5b22d0daddddddac862fbaea26ffa04ca87b 511ac063662eb66949db42e4452915f5f464b97d360118ce47b9617e9e75ae3bb35bdf6d9f86556a ac5d82df26b387c14e4a965fe069dd8c4b435bb65bc4a0421996ca4d737cdaf5479d19445252b5b0 8b5aeecec98405b8ba3ef3ad2e44fabba21d147645be31adad79adbc1bd9e5c48e9d2036e074a6c7 9dec0e33507924f945631865dc794e1120cd76e3e708f268d3bf40534e12c22e9a4bfc25cb0aefac fe2f11ceb55d2251cc25ffeffffeef1fdb48805cb2777e755287fd7593be35f863b6e2a3666ec3cc 937965d4aa17d3e9fcbc3494ef717db04caf750df00026006b160454213c8a4abd53451ad9302536 27dccd6db15339df7ee40edd6e535f6d7b08d465fb975ae93a2c38cf54fcc84f63ccf74f8bc93381 90b38e38d5e7f8b2112eac52aeb22a5ffcf896e47a8568e8466ad2d22e71df7a50ff382cc0e418ec ed9d4c7287828acd61db8d68e16a85481fd2f6b2498e88ce9262ba458af6c2c038d64fe7270bcf38 9033f2fb099f3326983085ebb2c8d533bef470bdb8bb515a94da57d1010569667273328a52df3e2f 5695cc4528275ae6d3b456561715689b6861e79bf598bf5c806d57bdf5a430f5e5cc037f24554309 06dbe33d32643193380628105bb39d6a3f9149ca351ff193d269cc581399dad13d652b97959e3d6b be1bdb37915f1a7ea9905597cd62ca7f4e8a2767bf2bb160912ad3a3930c74fa75bbd224ac0488c4 cb0c45b61ed63666b8ab2be8856ba65b9cd51a3ef06c6cebb8a2d6c5eb27b40fa98fcb70b5ea97c6 fce2b29b752e7b6b09b2abde2647ee548801c53662a1ae44e478bf4272051a655afbae13994aa89d 56f7702764aa0529b252fe217747a528b74c8563d5922fa80e8cf0f8c2165bc93ae75e8fc425d33d 0066126f7423535d16d72031432d32f0d8c85e7bfdd6c8878e53edec138ed94c145d634e34bd75eb 35f2f375681d9b0fbb0bcd1dfb18e75cf5f14acfed80712ec1db90d161be66cb5f6dc87866e7576c f9ab0d191de66bb6fcd5868c0ef32f5b86d557b713606e520cf0e2067c78bc40f8dc4bf03cb17d02 5d6581ce1c7d33c46ff01c60edc63aa75aed836b5e7bcbc3cd1c4e468f4bea994a1a0a74caeb1ba3 5d55af381f47da7bfb0be56b9cd1617e1af503f2d9b3f941934687f969d4cf9ecd6ff1fe6f08816f 724687f9cddeffecd9fc8c49a3c3fc66efffe36c7e9ff77f6d087c9f333acc6ff6fe27cee6a74d1a 1de6377bff3f67f39bbdffcb42e0039cd1617eb3f73f7636bfc6a4f100eaf77affcd191de6377bff 5784c0474d1a1de6377bff7f9ecd2f6c50d1617eb3f7df9c7108fc5eefff64087ccaa4d1617eb3f7 bf77367fa0e289bc14a142d507b9e308ff54905d6f9ea6007755db8e9dedb8bdbfc162fb6863b99e 62759a6dcb6411d33b4bfdda43d783e54303effa53c1fd4adc0b4841422efc6a096b857137689e9e 62d03466f1d5f3d1ed30b83f76b89b3707a8a25bccaf860ed06aec6e55b14d5907b527991c085ccf 8b45c7d67759c851edb4ee28ed72d5958e8d832708af64f07d890ef3815ffd4b22d4592f68f24929 68ba9bea3dc8449ca9136979d93b9a73f4dbb073837d60691dd80466be928df846d1394f77547d37 c3cfaabdf52f4a1bef5da4e3913d8b293973e10b1472fd69890ef3af3d5f508dbcfc072af310084f dce2a6bb5a4c5237b3556dd8cd6c38319f86bd3b2f277d4adf7982a4da464a55dab78922855d5311 27bb8ecc2b9cac9cca76372eaabeb7bf50c25a7dd37f78dba714b5a54dcd7f2d99388df2e66de4ec e8cfeecbb6a110b8320dbb7b96f5da4a87460ca1d5d016af1cf6842085a47b1227c6881372fd1b77 da52cb13572da5840f48bcd2ed077ffa962fa887bcf207eaa9cee0ae228d95c89a59df3a2866f622 c8eb96519e9f575a2d0031c56b5bf102ee52e82768915fed2921473fc893d1c2290e56cb34db985e 8ebf46a2c3fcf521b09d71ef41b69f72d4ec37f5284621c42dd6ea826d93be65723b3430f4d4a3a1 21e7c15cf1081d95e9e72a4254584258742a385fbcc91867cebb188b257cfc483c4e87b74487f9eb c34f4b60bf229392d3bc1ab5f94d236aee83ad03ac5ff13d0fab839ee44bb6507ce850c835d516e2 4f65a636d94b2321c484c5ae06f3a5bc0673a63886581b7d414777a7c0cc7d09231f95f885881ffc 6980e9cbfea35bc92877e6baa9478e6f2d6fd5b38598e3ca9031ca301fa80e3968c8419b9a48dcb8 0a0b7271bde34bfd737caf9033fde59ab5edc2fae81a97f5b193396e99de6bbefb85129f4dbc0d9a c4ac7f7f3c326ad4898e6b51334a0d236b12d373a1a5321ada9c060a75045bd2293f198999405df2 eb4766c95d5bc89cbde5cab3a397b8cc98fb039d31fdc16a41b3447bf997c46b91ff73cf0fcac39b cc06f7c1e2a9458eaf976e55e1085c667db7adc3b9776948b9676b2f39d1f69b8232b3dba7f323d9 e19031d2639b766170248bce9079748e23a68fac4634abb7c7741200a69f92f86cfef7af1e24301b f8dcf3a93bba7d0eae7da3ec443e5ff75442e808f2507fbec41c07574ef0ae596489f2a678eca241 9919ec4490e6986df57d18a75da3842d50a73266ba45caf3b0f3ab253accfd218d879e387eea36d6 1d4b91cf57aed6285586325b7f0ae2a2cf3d4e7b547f1def1b3a6012c5f39d4e3d902795d51b0952 9914536481bac76fb134b3873226e5894b892efc4be259b75fd9fd29f15ff9eec8d9b2e4f192b935 f69ac502a11c22b7b128491c7b42c0577cbd3932f5f4999eae931ab9ae77d5c32ef53a1366ef6212 b0add8b85d426d1c15b60ede98ad3ccc0986fee7255e03e6bb3ff0c4726174bd3baba90e05ee41a1 1ec653ca00cce084ccfcedf198991ce86c6f4793a5074711353120f1262b1e31af44705867363fa1 8fec24bedea0f4b12da2fd4655464217507fb5dc2e6ebb16676b1bf2d053db539691a693f5e3743d 5f4ac763a5daa194de68788033c2142780fa02ebaad61a65bce30e7981f01ee19439828c873d1449 25daf85e3800c47ed629925f93f876f1b7befb8084d5543b1156f5e536ac3ad75b586b8cbbd1b595 63ee0ca3dd7d4ed7ab9e601ee34cda5d8e1bfb1bd4c0081bc9940e96fdbc1eafae089dae6db52599 d439a55f82ab659d5fc4d13f8f4d23ab69cac1fb944487f9e6771f45fd00675c4cf959d40f70be8b 293f89fa01ceef19eda3a81fe08c33e9dfebfdef9dcd2ff4fe9b33ce087eaff7df9c7108fc5eefff 78087cdea47126fd7bbdff8db3f9d5de7f73c621f07bbdffe68c33e9dfebfd1f0a811f34699c11fc 5eeffff7d9fc16efbf397fbcebfc9449e3e1e0eff5fe6743e0674c1adf90fcbddeffc7d984d5f230 f905f5e539616df8ce5523d4c152f6fdd444043d31adcc5cf9b9dddfce814bd957634d593724cf5e bd962699777bac9a3daf7cb92406ba739e4ca4845e209f49c5aa94e27546dedb9f96ffe25c2171a2 ca31ffb921e9a7b622e0cdcce3c8556a28ec94e1f4c1b61ad2d1722a0bfeeae949c57cc0c2e5c27a 83db39ad9f7c7dc5014f151e9209b975d36e1f9078bec0fffed5175407da85b562d20b6e0da41f39 1ea7fdb1ceb99ed83e16bdac44f5dd551ede3adb729bb881619ab31b4553ba52f57e9c115c389b3c 1b8bd7e9a219b7b4a55695b52313d7da533c524bfbd7c87b00d59aa7225404fa03154f40fdc8f111 6a8a609c8812cebbabdaaaede8da76e500ad396ea36a82bede7786602680bc6c14e497ac41fb86a2 dcba7b5dba9be7b3186a8f5098a8dddb5be222e49f1f7e50fecdd934a0beffaae374644dc47695fd 2af3be1ba54d9a3733d59edaf6bc0a5907ac78309fb3b468a8239cd3e02dc52bcee82e490fb7228b 230ed2844c0ff2f89c57b03f2a7111f2bb3ff882fa22e1a8d9e7fda8d947a8a702c378f334747557 8b6dda01c6ed6a84581c5807bbbdb90ed630795eb6d747bdd21db38adbefc5bd80dc03e99338363d 9197c5b17ada345b37eedc2d38bf4622ce55fa8f1b92ff4125a79b9e3f7648cacb9acbb3a3dfbacf db3ed5285b6e58ec98af647b7ec92e434c3f3f4b948a17cb47b9dfcad1225f5cb0bca209a75359ad cbacd52d582c722c7b7f495cb6fbe79e4f49581dc37f7296ee51b38f385304747057fdae7633cb05 3f2ef3c621701d9472b54b76a04e8c32dd87d45bb0c1e5c78222a5442091424e4e52a7ad38a359cb f04f47626919c776b17eff94c409fbb7befb03b5561090a827ad3da2663fee468e1f63913541e956 155ef6f5b1f29f17e16a548ccdbd3fd6ea02b351ba80be97b872951016bb267a3270067d576ed92c 7e742b0acd502794a7c3b478fe35f217e77b0c1da30e904d3f72fc60ef00a3176bb9d64537c753dd 3b17b6a38a6661b791e2afc0a5f4d4863b31b3c220be543bef38539cc16cc3d021e6be44313a6c4f 687a48aff87f495c1afacaee0f4858dd10d9b056d790a827ed04515bdaf4a26654df46d6b4886bdf 3d7167f935f17538778b9f2a54dbd9d9407a2de0a93803cd35af554a6bee9adaac580c7dad8e87a9 f8ae4942f4d0aca2145f6b509f97e8305fdbfd1fd4331af550cdc0132783b6b3655f53eb8012abcb 6cd9a3f54bcf7055e78c57e4239f1a88622a37e6357e343d55386ec6deaaf5d9f1c02b338666e0c5 1f45c8de9ae2f7c51d95d612fb5f2361153fe602fba5a18f6ea6140774e478b01a195469c795de9e b1b9988cd628ed7d396c8015299d27babcded875b8eb4deeb278afd5ffa330493fd021cdea8dd1bb 80486572f705b91899ebaf4974986f7ef74d09ab9c5288dff48747fd7dde7774dbcf599dd52a1397 79fb9ac5629c4259cd8734312d402882767c6f8d43f372996dc1dd2a1324ac3afdbca24d7af4eab5 28a15dec501938ec910bd51c1eb4243ffe41890ef3af3d61f5e69482e6e946442d3ee9d8f66c635f 324ff1ae83ea74a4dcc11a2ff1eddb4328a586390ebde6d3c75eb59ca45f87539a4e2d27396a766a 1449f99e2e9385aa5d3968aa1197540fe524db20cee4a1f96b246af629e041b6af87c89ab2627280 12df8a882273f852dbb3fc4c4e9c3c4158e3cb1b8719a8cf84e2c2a1d353f246e5ea039f2ca65e8f c3b6673d0f655b4d1070e9902440619fc6adee3a8b23c1baf04d891ffafdee0ffe2501c6bd2afe98 6310bb717dc1c646048eaae3e533f2f0759d0b6b6d4673f84a97992767ab94e4dee483712ac76bf5 11266c69445515cf3896444dbc416e2dcc0527378cb0261ed65ef5eee83d517dfc8cc4b5ce3f3f3c c8940f3adb953f3b2f47e6506b244c4166eb7256d06ae301871fa36ee7542f12d492ed1c0e606149 e037d827316f7464b00eb96551daea71687fd5e09167b12a222c9f93905127a1c4ebc48f3a39fdd7 88ff0c8f65732496129ab53acc959ecc2b627edf8ca7f873b84501cca966b7a9025b1a10d6129960 fe3e354783f56583bcbaf016e1ac158ca48a8dfd5ee001743f9b16f17de61185814c87242cef43ea fb124ff1ffdfbf7a8b8d5e5859b3967852e931b5b9989f7b0c8707338319df5637521f4a0151d7b3 29ac6b1339e4450ecb48ea08d4f6f3442e5ea50b562661175ea5ef7db820b943485fd863689b3727 5059336691d8cb9f96f8e9a8781b82604f0f419fa88755a01c5d08b6cc331aaace26016630ecc3eb c8dea3831c8bfe33dd6e7be9333670d4627276dbeeb99d0d97bb7b1b8441c242d080b6ea09ff74bd ed7ce58a3d02f3da44c138d28c29649bba64b1c6cfc8f739dfc5949f44fd0067dcd9fc2cea0738e3 7adacfa27e40e201d4eff5fe9bf36346fb6993c621f07bbdffb110f835268deb69bfd7fb6fce7708 fc56ef7fa2ddfcb449e362caeff5feff3c9b5fd8a0e210f8bdde7f73be33e9dfeafd9f0c81087568 7c3169bb4e8755424944e934340d9a8788b373e5dcfb63792adefb27229e6de74dd7f6c45157b5c5 6d2b095b1bde2c11cb9a8307abbe0a98ebcd0dc42b3e0d74d38bf2b20b93561e7a7eb836b435dfd1 3e2a7111f22bbbff40ada68e8d2fa8ba930c6e457cf6202b247b67968c7b1f144e05ff793a763da1 d698396ab05bde76ed4764d21b895ad6654c5d9dd58bbdb60e7e3c8032bd877f35bb8557606448d7 51778fa4a65cfb57e5672404bbf3f317ce49f7f8c70dc9572ac05c647a7fc838eb3f5fa4e38fb791 f75369a2e309fbd5dccd0be6e6764e34217bdf97510bf597d4b5dd789d2e014248e7315d8c06279b 9ca395dc9125df7052919bfe52fc4be2d2d03ff77c40fe851ad612d9f4235e8de83ea86f187f4ce0 8e27a6a9823793a88e2b3f57f1a2464e39ca9f6da453d9594d9cd99bfda2839d2727e0a02bec81d6 f4d988572fc59aa6d4fb3753ea56415164d017ff29890ef3af3d5f50cb4a2b421db0515b023391e3 9713ff55408e9e0820376f66a25957e9c34d476f354637f0a82e2cfc5a599b0cbc81ce997a6fa717 d53dac9ad90ca65827391e75ca440d11242a859c05ceb50461bc3b713f232138db5ebe706e675cd4 965a99f7d5f3311bfb9c13a1cecce5d55516dbb45bb8cdabb70b95eeda8d223fbd52cbcaf22cb8fe 525ffbee4add43a58dd23891b074cfd73131f00b47e165b70c7ef6a0783e8b6cb97f495c84fccaee 6fcad750a3008d38539325ed65e9d9c55dd5662f67bbe9956fd579ae6d1d12c6e8fd600cee4d8d02 43ccd42b8bcd9526725d4af4a2be1187e70b2c4c50eac0cf0f17f9b41a6cf953c119b29f9778c2e5 9f1fbea0b62eed2fa8c371f6ce20b371e4f825ed2afb99e1e8da20bc99a946c1c6aa40e34af36aef 3c0f9703ed72ea8d145ce88ca50741cec511f65a0a690bdd9ed4692f5e82f6546450810326ed1307 b41bdccfc8df3989cd29ea9e0699fb60308beb02deec3e8e6b9363cd015a65d7b6e7b994d5c100d0 1c67e88e514a825d1595d203b96be54722478f2742861467272d315c713b24b767f7db06c756cd86 70b41739ee6b121721bff5ddbf240421d4fc1b2ae60e32feabbe997859698047d604c528132d5896 1b669fe6d34b17cff2a9d9d0ae9557bc708ed2568daef872ae7d61a69587274d41271c5401966c8d 73a163934e308c970744c6bda54f3f28f1a4be78fb0575ec742254888f7aa87ada1f9fc6a3c8f15d d401c605cec6ba817e1d2402e73243f89c519e3feb2a3eb0da52e8d16d410284ee4917fc3e071af3 f1d1a98533c6f3d52dfd08eff1588066c63991664a21ff33f277ce13ce3fc84e377e222ee22ce6c1 788ae2ad4a3a87eb63680966aa1258860e4c331a5ad2ea72d0165be22c7be8f0a53cd4e6f659a77f 742b931143e1f7293dccf16bea3472496aecb7156a4ce5a46f4a5c84fcee0fde128224750dab9b57 2fba38c962d4ecc154e478b0eb00a3606eb9ca696b8ea797c3b9b09d5f75507da6d53648d6a5d3e2 d8160aab433cdb8ebb6a42f7e80d9d1ed31ff40674e2124ea9346b6ec89c98a00e6a6fa593b96a5b f919798fa1ff44ad156ee27db06ca5a266546844d6547ae6f3000fcfcb53fba0c339f7aa12109e91 8787424d502ed5d609a41f35b6d56b3519667a6ad323bdd1a5323d7b402ed6c6fcb09e193061a8c5 b8b389b643e9672404859315056836e21ce6e528404b51d7893925eb7024804bb600f4fea8a719a4 ea9c315b661360569c3f4fb513bceb94d8d66556621843afd0e3c1bc46cd805c935c8466e7a035d5 2171ce1e66c4ee68ee89cad0c6bf2ff1d97cf707217856ec2846c1c1c39b78829735f3e1ad2a30a1 99bc16c27842ef202ef31ee57058b989d242cf9ecc959465db633cc51c877e861ecbdbf8c1186a46 544be4726e03078d556bc499c49a44c5daf570a4b89f46422f7e4642d07bbf9d288ad1f1f8ceec6f bca3abf2e54abb65c5d0536c5243478ba9d23b5638296d9e6f7c655e7eb2877e1830433679a74426 1a1c2adbc2eba0f7ccd4a18cf159023a6305029cedcb38725c54b19b3b6d45b28ea78cbcb73f2861 b5f4f2a20045667e6aa3313696ad33e7e578c26916d6c9289495994b53e0fcbe48effcd6992533c3 2b33eaf54d2a276f2e07a3fcba1e80dec52160ec74c7ad331ae08dece48939c75112231afd2ceaef e2e544a250f980c445c86f7d17f550e57bd024a07164cd256a26e9f62c6ae23959b983c992c46fb5 250fa5ca384bf204cf242f079ed42adc8930fde084dbea49c21bde56c50ed1703c365a5b695fd0fb b07a45e944c345c24eda438ed7741049eef9331256b971e83f9fa9b8b3b10e83e140bf44434fb53d b5cf7242d7001e2aa77a2c9520d6b4d852f7e4e61c590a210638de0c2f04d639a314ca64270cda3f 0e4f08eb164fc86897169064252def792954f7d3fefdbc4f3fed3fe4fd6ebfff7cf89484d5279170 b603379e7f5a0154628741112266f2e7759060bb46af1c21620db2cc9fe34823eafa788c756d6b86 0ecec71572cac23b24d568437bc1a8eef7b31d80eeb3991c0ecb72fa002ffb210569b84147621e3f 2a7111f22bbb7d0ea54c433b3147b5dd16ca91bb931bfe8cc9a788327ba1673ee01c2ed375801365 27851e9bf32c320e80f25e1ce66afb2c9f68c1abb4db8d6ba783b8761a0f07ff2a9fee2e1b63b983 cbc666075ea49f92738e9c26154aec0a52265588670df150b33e60bbbab2a3b3a48a1e40c923311f 1e73c8ebe0c8fbd90b3522b6b915614dfc08a71d4438ed57f4df4a89ad05e7535bb49ac94452ca6f eb76beb071b07c79433493c05f122f9cf3cf3d1f90b0521f1161d4998721389f8f43500ca4284b11 736195076761d5d81ca3bf703b40afabd49da2d4b898e2bda04cc54bd5af5d47a6a3a698e706abdb fa52876fa51e48d8865a66ecdda82ed8c0b9ae5a7b70763b87dc5037a65953fea8bc0bc4ffd8f379 d40f70c625d59f45fd00e7fb42f093a81fe0fc6fa37d1ef5039c7108fc5eeffff7d9fc16efbf39e3 10f8bdde7f73be43e0b77affb321f033268d8b29bfd7fbff389bdfe7fd37677c91fe45de1f2c0e5f 3861781a8266568ec6d5563eacbae378f64360f3d03112ee1660152a73bf97c3923f6c2eeaae58d0 da8e6c22e39b867757b7125580ec73ae40d83bacc0da956459b24cb27b31dd53601b53d056f59cbb 16bf26f150fd5bdffd255f5041fdf9076a355d57a271755088fefd6c1134a708fbf032a4fd382070 ea4e4faa803f64e98697b2921d470198b82e70d36eb3f5ad7401f7f62e2c90d635f13a597bb2ac59 b5ecc03d8f5ad5b3a6f22351dd2d3cf607256e3795e58efc824a1191bb816e84ea168a11e578fe38 3ca0e3fdb144ac3bfd82533e2be54adeb484d5dcdcf2d9bee93434b42f2d706183e00bb66aae425c 9b0c7a340f3a2a5f6889b2f57c711d676b8a799339d9f637cc0fca17ceaef5fac239d9e861add82d 3fbcce727167eae33861bff75fd0d57f9ed09437b5c3a29bef2ec09b1141d9f06ddcb71a596b6292 8ff6e2fc4a195b237ec9899121d78cbe507692ba939e92dc840747a983a5c86f4afcf4ea777ff077 54f1380fab5b440f6c3742ed5c67f3fba0b0a4fdf16469faa9f436e12e9b76fe765ef52bf6de94eb d728563b66efb08a5fb4734e89fba1bee41f136db314562ad4c6f7ca75cf31329edaf0e2631550c2 7327123f286105c1a877263dbb27bea012a41e607cab747f30b3b9cf9d66b427dc97a697ed0f9e8e 9e4a146e60630a58f842ab9ab4e5340d692c35f562f1d9562f4b64a020263895712eb591bc4b1617 1f6a8113b8e97b2d725e184cb01f9437eadf394fc23968ee22ce41a13bf553ed31e565f7b3cbfbd9 a85beb71bb748db45de747c52b79e380b3d09957f4756f5a55e12b59571ae96c5bf2d7a7811874d1 b9385811b0c0190ec7cf0613e2241300fa7d89ab83dffaeeefa8176519a31a518cd68bfef335987a 627a46b94a7fa0b905ade5ddc0407959c46e9a33079741c1c8dfee05ad122f32d6d0bbf1e424e99e d31be2119a7785530318f16205d8c4555afa5468e7704ebb27901f94ff7086c97708bc5175cd7890 990895d323d42c3d38449465d1015229cbb6824b70bd77fae9f3bca1a7b4b3cfe7945b472b48f7cb ab2cb2c915284c56e9269fbdabbdd3aa662f397d1b2196a93bce5eaa663c77f0bdfdbc841586a6a3 9e349dfac2e9dc2e515b020b91e3eb635759747147e740fe068ba9f37b89092bf04d76710f8c6265 f354ad1e9691c9019213595b290a53b001f0b9ad5ae3f4e7aecd012d6ccac2948d1e2dd5c08f1626 a11f90b8a4fa95dd7f47f52fabb0fa7a5ca2b604e63d11184cdc55adbe778056362e0438f2f55154 ac4ba67570750019840a31eaa7a4c1769c11664934c7e7eb619cb073bbfe1a60f7e54ef388e59a23 c61dad770c713e608ce31d901f947f722edff33a8b19f33ea897f391e3c14164d0d4fa56257ddcea 0cf5a339ae6c0d63939b3aaa4d64ef726f96788a29ba9ce20be024c341805a606bf8088812c3529d e934937d9a26fb1bba9fdde3349d5d236f894baa7f7ef8a884159967a24ebf980eabe5601ddc1279 336a4be55ce4f872f766269da9ddcc5a3b93db9d88f3729b8e871c3a58aad8cae19eba4b09c40c78 450d12dc65d548b1f5059d65bc4bb3443343af4a719d6b974aa29925955ced0fd468b5c63e2a71a1 eb9f7bfe8edace6e82e6f679f6c74439eb169ea9866d774fdd2bd3d027976c7d851965f8aea97890 71a4a7edf8c282e0ef27e0ac856c63974b32148e65e841582abc2bb786079233f9d6392c4f8ff961 c947d6cd183be20725ac5cd563d4935623ce6d7dfb20d30fe38f5b1185b403342f25eb80a2c04590 d1b6a1030d44439b92aaf48a194f144bfee37436a5078b67b5300aa2d78b4e88bb3429354bf9839a f100427fddda04302dcd7133631c896de640fd257195e39f7bbe2f5f502fbd6cd493ce76776671d3 dc55f5f1b4ed19f98ac272f034f474a2a383b68d2b7eb0d0a4d3fd1a3f502614b37ac0d56f079f09 86e7904e15ca2f72d9e3338792b72e1215a509e235bed6c1f0cc6e8535952a8fd9887dfc94c485ae 3f3f849587c946cd69128d9c5f0cec73da558eacc9d9513b6f9dcf4bdc79e9a04af7d576777a9087 7c5217d4cdcce3b00ce03061afe650e215f2c9553bfb3c9459364980e43e8f23ca349ea88ce17ca7 8eb53aeb214a22ec16ed3612d40f4a0856c253584dadf20176e2206fe69fc5c89ab3385bbb649e27 4187c2754a25a0ce58668b795acc3d71fd54432fd6b1bf91ae94b4132e64715bb00e405577896a12 7ee068779dc01c7494c15a46a78852994e0d0906a31ec2bcb0d9bf249ea2f895dd5f93681495e6c3 6a8b2a3d3a26be71b66b88369f93e3366a3c135575ceb5bccc161e3371d1c7c953cd728423833434 3ab3cec7f9cdc1d0eb12b1af2a2a6e8f281373c1a185b5570df78f9998bd4eee851c914c0619162a c0fea577ea9f97b83a186f4310aa8a6115bf94fdf1a63eb11bb2b232b44b62ab359289b31c0eae25 519298f909e9faf0f19899479d5b75cd90a52bc31035ca67f0e6883a619dee5c42e9555541423ead 236c27612243338c1f25d9f3b5d0df4ff4c46b9f9e24d33f2821789e2b81adf965b760a1cdb3bcde 3734b461a04a8f51e3c344949bd2099959dde3b1d29bd3590adb1c2e530fc69d7419c6484c415166 b6259027daa69091513c22c9699adb4f6997df67faae0c4b375787171bd784f3e5fbed6b124fb8fc d6777f49582d51fa7d90eb25cde46ca469d6aadf51a8ebe1102136cd13022989e3e09a28524aaf59 3dc019b2851340b98f069832465e24155fd690b1325fefc5447bb7cf7672302c5f13085ca8b918a4 6926016dc6060d9553f66977a60ce107e55d7e502fba8d5ef29856d7b4c8dd7dad212ee03314516e f8e3c0d44d6a19d837c25a56ee980f9f121160358da48eb9d27e9e48546165726fc205c9ec40dbbc d483ca9a34da5d36d2740797f9795cfa8ca7c46f2d58da6dd1aa04ffa078b3cd73a2d5d5765fe247 4b53d07285f2a93e2bcf98d77eb2a30ac7fdfb36515de328ac1b0da091f14394f7d9d3d688d88656 c4d6f323ac6a1011e59e71f93419974fd3dbba9dcc6e1c2c59884b9fe54dcb4b826b9f4cd6bf29f1 fad0dffd816aa99a214a4438e72b8ea5732d6698644e551b20f561a68e3b9b56071db8e8603f7b15 67903e57d7bbcb9adeefc0f381d8a2e09ede38e8827d576e1b0b7eed1f3ac29aea74e44846dabafb e81bab80e95c56c741e7ba1abc9af60f4a08b466f1db2242e0c547993f4a17c28addd84483ab400f c13b9c8fd27f7d1cfde5e321483b7208d62eb7c70d1e06f783f1ccfa3473ad7aec846c7b893d3474 4f4f64e94e36d0ce4da748c211298175e6d5876e6f679bf7e424e7ae08e7e3a578fcbcbc513fc0f9 ce087e12f5039c718afbb3a81fe08c93c29f45fd00e7bb98f25bbdffe6fc9ed17e8df7df9cef10f8 addeff5e08fc42efbf39e310f87ddeaf1454e9cdf90e01bbb18eb0524644491462d4d11754295042 b06f45a8d43cbc1fbc648c7a063d76cbbc5147112ab470d3ad0df41f4ef9e464c387615d94966cb6 fbe8e99c00ddf8c6ca7bfb290981f166fa05f5c81743b0bada85a0099ea354458c38778559589dd6 c9b05a696941fde6c4af107cb4e6112a39aae4fc7e8c3a9ae0ede82a8f4cdc343e5b39526ab977e6 14c938b9ea55b3b7247e36ddb42e1ba7c03ceab9dd94fa80bc4baaffd8f305b51ed77cdfa83d7817 19726c865522288755be350bece9807cb883827abfa744db0fd85ee0bd2ad79497d491923b95c64d 47d25a4327e7d757b7555878af99525cd7697b2bcc942baabfb473024ef39adae9d3eaf679253e2f 21b041665f3815a31c05e8097e8f05dad035ac1a6520c0a6dd59d0cc8cc9fb9d7b493e9b834d6f02 253c77f6da3f9d255dcfdf364d07b47716d5b64c179e5c6da6bdbc36f41e6a1ed2f3e3992d57253d c72f8e2a70bb92b24d2db1b7c45314fffcf001f9823a70952fa89402c7b38fadb096e8561eeea335 7d7496dd83ff4cf9a2c763db8b3b7fdce310708aed69605fd64eda428ef3e215efe46a6687933ae7 676e313278a8b430d24a0dd61745ec14519e29199b8f31e9b04eed3f2a71a1ebcf0f7f47b56c20ca fd1c2472bc6c07d80e2c3f3a0838bed3af3ae68f445f70256fa23ba58261dbe6a279b750524c98fe 2e973bbf52a3a2314b9c2bfa42265bda9ad8c41702d590364bb592970919836784d4690488d8ef9c a0cf4b08e0e43cac2c43f50ba71f449ca773bc9e4dd09cd6cbf7c7b235f19fa716e6cd72d6c9d166 13fd5621d4ab65df2aae795fcd82f309df257425c1a5b4cdb25b50a1545051f6f6a525df9297b1dc 9ca776e223e1a0022bc39090accfb77f499ce2fe73cf37e5efa8c1bd12b5a55484ea3c6f8fc3b550 bcf75ff5913f6ed75157561dce2967e7aa8d4ccccb954c3ee38ce092301d57572cf7ae95e566a858 293d25e3eb554122c106283e56bd9ed8e7e773817d10083f3d55377c66525c7f4ae20271bcfd2f54 a08c45319ab93d3a3258f239021c7a825947dcc2da676f90052b166e9a8639c80a1763b1d95adad6 a55dc5d21e77b9a5ce5e629009d3c2d3314ac2386d34df75e8be3be233a7067cca53ee9ad37ae6f2 f312021cb7883afdb4f685b35dc702db48da77a69ecabf979800ca510ce7b390b34d3ab46da15bfe 4ad530e92c185d552fc1d5b35a7d4c6db90d93ae1808a987702ab0095eb8eff227599b82a7426ad7 e334290171a5505db3678c5dfe4be292ea5776ff4bfe860ae61295784e3716608708755028e4bd19 9deab905f6b5be5d040bb71b4398318fdce864141696a05ed17bfcc48aecbef2a61862035b38c98a c7cf8149782a70f934b7b9e401f692abb559708dad8e576f17cb7ef979798fa1ff42ade6c0a8871a 63417397b17d0e7f655da596ea3adba633bb556717d86af3edc325ddbd927a591379e566488ac4a4 ae67812753263f770887d3edd0672fc23971b48e72e15877af8d78faa843f6e70c91ed2f19071c2d 3e2f21a0c9cbe8e2548c06243d008ca7cae28fc3fd71f5c7db6cd65de5b3f13385b78b670ded86a5 2caffd696e6fac1310a65a972d2b938fbd248e73b8ca2f64ebcc1952db62f749d539a2d621640803 c9d2f70c53a57bcbc798ee352a2b9a1a56165f93b8a4faadefde12a10a91498f801182f3762deaa1 10fcd1911ea6371383b4a3b341d5b6bb62ebfa2812a38bb030367aa5324014c26ed0d2e0d5e1856c 73160f074f25e7a4b3fb5bce3c36bbec8dbe1ba3071546d14e0d9d6e99e4db648f4a4c932bea6894 969f9710b869ef10a8288d73747d9ad7a31e8a23ee4cdfbbb84acf4f44d61473d6610517cde41568 1b1b78b1526fcb1622f793898320de93ec49875a226b01a87a6cb512179a194617562e53f348fe55 4e1ca47ba17058d0487c87fdb0583cd6649abe2d3e2f6fd42f9ce2b61ed60ad783cf7157c3d131f9 6e63592830b9e9f8158fd30a65a3a983d868a39004888842e54ef0ebaac77055b4c01fdbb5b94c1f bbb6418df9f595ccd64beea100249f4439f7cae1676dd9c6cfa7e496586bb7e537254ed8bff55d08 3cafabe8fad4bb44d727aa1134271ce609bea044d6c4afd747a27e3ecbda2bd02f55bea946cd6227 0fcc2e212c91f0c05dd71e733c9c5e1ccd8e7b71e241a557aa7a501e6b93d86e4b371c5e2403ccd2 c01cd628432dac91ae4198957aae3f2f6125ef45124ccc3f169ab9b4a2e6c420ae321784c8a003e1 22ec7dde28c362a0a14daca950c7da564ae767145f768b34db223d9a7e650a1c95f151e9b0492634 a2b23c5cb05b7aeaa0eeb313a29dd62c8fd014d742687a1fdf8a40dbfb0ef479092bb567948c6457 d1188f48b5ef833cb47380d292bef6097d6f683e2cc60bca26143fa8c59126f1c019128ac28b64dd 1a4b30c3954c51d9e7eb78d8565101b72040c588826ba214103f1eb5b826f6afcdadb03f019dce3e b537f7fba7e462df97b83af895ddd1282ab30dc12e61476da9def253adc1d2461fcef6bc1c57b79a b506948832999113dbf340586fba3bae396dec1996ea6054eeb1c409935248bce14d58f47e04e2a9 56515c248cb82279dbf3f9fb639f1e3f52b0782f95e15c6dda8373f93bf479092b480d0a414874a2 00cdc6a521b7a09c7a17a1273575c87261b5ddb95fe4a1ae1584355eef72f8909a334f4ddd50cbc6 1322ae441dc13c56c4d1c76c4e21cf06c021a35d4edaf3f45ddba76fb6092fca8603e72ee1135ae7 f379a8c875e26541dfdb4f49589147fbb09a0cbc7b3f448bd6a1b64ee997ecbaab123b8988100fb6 a03e5b390e47a93af3428af1a8932ab0cd29612dc925e62f526b9439133b843b0e11246900c43e23 dd2958eebb2c9cc70d01d2caa60a95e6ea656784aeb7db71e9d707247e21e23ff78460668f86550d 749d6dbd71d5cf8f92155136fbf2d05950c25a7d181c7e8442e65433731162a64ca0852e885155bd 810e3cb4878c83d5743f4fb497b0d201b6700149c4f90db4d16c042a8f4d6277f64d7a075625767b 550d798bacddf3e72504772ae173c3046b94cba78942890fffdd6ec64c5b2861e49e6badea7c4449 9de3c7f86da2ae3d3dac6b6f82083091d8cf5e760e562626001724b50e6df37c7b77d9d0bd1d78d9 0fb7168c4db768f5b0dcd66d7abb219a34ba69793cf996b83af8e7870f48583d9a5ba358603d3904 51485c4088c743c55795f507ce9c49e14588dc26a604ee6c181a3d3653dc5e1ca07169085ea57b1a a42fda1654d61ade0e8eb2c3882dfddad6ad4772e3a08fb84ef9286c5aee1388eb9fb575f7f168ae 8263b2f751895ff8fee707234fe1a24c839021949006ca39e9c03e06db62869e797be070995c9b78 eb52ee21af033ade674fe939b4cdf1eb1d5c3a205b0bda1f22b03513314db9bfd54ebb8ff78398ab 80691aabe3a069be4ba0cbd3a4e62d27e9dae3f3225385d3912f6d563cdb550f109342698e2a30fb 78d449588b8c8d5120f24052b4f5820be222bd038d477ee32002b069395c7d4db599ee2aa0f1c1ea d8c727cb17b7992d4fe3e56239492dd7cb9438db2cc4f90c5acc733364915567f85c59cfc8bf247e cfef3ff77c53c27276580ecb1772170293c32504dc6925acb05538ac84a76824984fa4c2ca136885 150e88c607ad1c1e0297b4100251da1c27853006bc1e68e791bb3bcb5be58ebf9e9d7b6b971cfb7e a6b2f6bb833ee13d9c35e7a672cfe3add0dd5016b80d08b3c5cdf71f95b8401c6f7f06f5039cf1cd e29f45fd00e7bb08f993a81fe0fc8fd17e06f5039c713dedf77aff3f67f39bbdffe68c2fd23fef7d 700e84659f834260cb9a6125b106c38adadb47d9df25ca57fa40e68f3a74af1d566ebd68208bb70f 61250588011c26ae4175947e3e502899bf3bccb37a6fe98faeefb79f139fbc96b65e50e893de6032 3a3933eccadae5559dbcd6e913760ee034fc2f8987ea5fd9fd97fc1d9590ae6165c8d6a2741a4643 709f3543909e6423217a11341a5f987a87076699a7bb6bb0c6bd7dc57cff5e27e3b3f17b6d08f002 096f7bc71a33f606da79eb9e524fca11c2c7c9de58e3e3b52e8987f320b0f7ba381d6d3e2561b9bb adbc33e9940c7f41458d4694a61eb11094ea11ea9dc845a2f402344f6d1f2d1820eedda2c4f9f463 a17a2c51b2bd049d7cba13d6cfb9e9d2abe6ccbb41cfc9aad9f94d190d105befce8e5714b0de75e8 e33c81e9f3ce0852f5285ff89484e5190a8640f9bc0f8193694739aadb0c4158c7c36a7a6985d5b6 5e08acbbd38fdb4dbb3c59debb7602f55e1dece825f5bce466e667d3598c98fb6d9d9da76ca3d82b d9dbc3a4695de4c9d0025fdbf5b57ea70ee7e05224f4f974ba57b71b6dabc014b4fa9ac4b354bff5 dddf5175f71656ec742bcafd7c22a2c423d46d507cb87dbd7ba7fcd5d24f24ecbd9762169423fb9e 70d330f26c9fb375c732f5208cd71db4f76ad6745b1860b6e758fb1c78d3a1f10a0e3b5de4d7a8aa df6e3ba53a5fae25b7f89a7f4a224eb81202addb7ba5be3f50c3623b042faf0875c2d9813d79151e ed9bd8f6d9de64eea57706e4287a93b86d73326783ddba72bd11ba691e6e13ef1c5a6668f01d246d a4975059cf4da096a6dccf73557f7ab0528537711152225c7f21d299d3f453129621027ca3868013 3a518b6f7722c7838728ab3adb4133fdcadffbad63d3e3d9767c7bd5cd5df9cd6d97ae607675c430 57a2e00b97c7b3ad1942637dd6174cded1d6e36ba01af025a398231150602fe8ca766fb0910ea5d4 5aec5da999307c4d26df94b8a4fa95dd5f50c70f34eaa1d26ed49686112ad08a50f587fd38d041d6 67fd63c39593c0d0290df895bddfe7e1ab0b4519e8713f678c9982f19a467b8abaf337f1542b65cf d66f32066543c93de6b252db6dd444ba434c8550a6164242ef4df929901b7d4ac23279ac7ee12c16 dd77d7399f47a89319196013cfbe3f6a7ec64fc254d5cd0bd5fe0d2ac44b21e44b3bb3df482186e8 b984a6ef2a47151c23827c63b3aae44d75537cd48fbef04c630961d4674b42c249f4f8e9a43a3b2d e6ef27bd4ef99c31fc94fc1db551f5a266bfed46cd684306cdf6c3baf7352be9cdaa47d0d940bd9e 8d106afc56922bb9be2ecefcfdb8d38b470c55e1fd8d929d798b93bad3a3200e0a735d48eed3577e aa158393e457b2a77c6f51e7d4501d736b961f71a5d161f07d89275cfe73cf17d46d324aa7876d3f 042934422590c3e3205d4d9f1b5f12ae6c9f80db051cb62d67620ccd01848c8d3cde58aa26df8194 06ce1052af68d1c26b39e178e112beeb6932269d390de23dae7494932c9478006c45588fd80a391d 1cafdd7eff53129605b1f68573358c3845aafb2ea6e802757ff4b5b39ff4b5a75b581f8b37d09a34 aebeb46f9fc562b6a797fcf45cb9654abb28111da362a24210bcb8cd31a7a5cdf35cb93b5259089d da4764370d8ff5c840cc4da90d99dbb1d28fa416f7d0efed47e50b2a91c7a38bd3fc1e827e7435b2 098ebc33b5abe1cda873e06c4b62c1468f93b219d69a5563055cdaefd7ba05c64426f3b7b5c8b205 9897ea43ecb4824d8a2b7b0bf668ad4a32e3b49f17a685e41f34e90c727497b8f568f2f4e8d39e9b fc88c425d53f3f84e5b35eff827adc3ec26aead20fb0ad70f09f9aa6b94a55f36f97f331691d12b9 cc851fddf23a50231a8a336447d2a04cce85297fd99ed45303e10096268ea892a71982f08477e576 a119d4712c3bd4305dc8902f89eb5003f3dca798fbb9f729f9c279aa1061c52222ceb2d37bb8773c 2e0df9e3b1ac383a2638b60d6ebc2b4d5c4243559629d56e0d41999a2efa62aab79cf2f9eb71cd55 e03b74b4bd3dceb45f4f8a0af7dc894a8c21859c50e4ed20daf2eb30c7bbb583c8e15d7292e7fe90 78bdce3f3f7c5fc2b26735bea006c720ea9e52fd47671fa5d2330ae7236b4286d52996b54b46ebdd 7428314e29aed800a4443d1f57d57905eb8eb84b015e1c6fb6bd633a5607a19e138f24790d391e24 71a3102abbbc12450f0f71834a014451847a849a823a1f9538f78cb75f50f5e6fb699e306af6e020 8a5108769761642434a87366922658630303ae7abbb6d2725f2d960569f36c9cb66770c83680c98c e97ad2863a256a7b72b6d0f043dedf304449edc6218057ce830b86643777aca6f8256c6f437dbc92 5b763f252190f49bd175b44f86e0cc8938f56edfe7f0d9d6014a83f8450157fa2442e74279ce6956 33e12954615914052151e24be2b3c1dad75797a1fbed31c567e92599abe47784ce69180e57371486 96c6024a640706e2af903b42158302427af810c54748ff2f894baaffdcf33509815210a1be167408 92af306a4bad6ee4f8ecd4461f97f559c673731db21b9c4accd2777938410041990d6b1c623febc7 ee10ecbddf1bc54d26647ec52f881d52d9e0b5d415415d9624913b89f048cf22cf7bb6a1f9fb0459 2eec8797e4747f8484e1a7e43d806aa5a21ca9b063a21eaafc7c7460a7eee82ba26bb2bed1d02f59 7ba53a873b2f8705e321a5f710c803ab5e9d6d8f6b6dfab403bbe42ab51d1197517a89d791c30ef5 81118e305a9ed973978cb84f559b715d009ead47019c2d6925389bdacfe0295c9b7e4a42600376c2 0ac2b361753c78f94f162ed90d9ace1a1ba85fd7eaae1ae7374a6f29a9d2e48e24f81da497d8c382 ad3009456b90aba7d522aa6ca387b99dfb0ced9de83532a4d7fbfd849b92f05c1cf1909adb9da1e2 9af67646e999db6d457ab8dbf6f8f9bf242e427e65f75f1202dca017561e3617566fcbd02d30c4fd 9c776b17ad6ee45b11e20e93d25247ff7fdade745b552669db3d167b0514fbbe1714454110955640 11e94152cf7fc37ceaabfaaaf6536bcf77bc63ffb998e4722c6f93243322c808a4edf3f9e6832d54 bc64b91a74baf73628d3509426dd93abe94270f860d1e490255e2b526d9cb764f9c81f7730c75eb7 5b20a98435312ca2a164c0c65daeab1bda71a6ff2380aa8b0dffaa325212a2a1463cccc2de9ee81e 93f555d0b0bab7bcf93948db40b8f12709712fb929f9396d0c35cf344514a2fbfab57a10d1716baf b4ea43128bc06cb7f9bc565b3277db12f680a7888373e3361e7c4bab5a6d8eb2f3d87496d5701d80 79ee7f04501b0a1350a75b9c29f26c4e67dae256cdd0cfb7b25a2e3a52edb9d8f227bf983aec67a5 33d64f44867f1dbd4de052318d4587dc2504fba2f82a90959b046fcdcdaaba45cd7993a0ea93dec6 a327930dd35eadd6e169bd5d737df688c757494800ee7f87bf82907fff6f00edba4353e8d1964e97 6bee2d6f30acf46c8d42b159331bd7981017e745106d5973dea08e0c72e2a891ff15f68b2f2927ea baf71d72875e5bf4f9b38398a0503fda78d4ebb3e9f8f7ec3a64adf2baff7e22787c711bf8e8ebf6 b0af949d62e2bc86ff8f605edddd556fee2a3f15c70ad5af2e1e662d8e3f7d1eeee54b3a054eebcf 6b2c7a933b7448568607b147ce486d965fed88924e2402f7fb44dd96dd30ade13911d61612613505 bf8eca5a220c36b0af58b3b059beec62b95b2d2dd9ba5256fdec6a595e94ff47d0ad1ac72ab3fdec 2274fa56ebf2ddad0ee722bb954e883879324dfd1350318581bd32747269f0feb15c951281059868 5a4163d3f1eedd35d79587f8757898605f019b61b3dc7489e594e97ab52c75885551efef97da7a74 5ce2f0945b569e98b020d085fc5f917ccddf352bdf55fe28ec71fa78569a953a6b4e1e83a3876b18 ddb794c3611a2d3912cf65842d09b1b78d77e83d1261d9177e1db81ef6e59f1136cbaa60a52cc4ec aaa831e9fb0896384414178fedacbc206a33788158b3ea9c6c76d139ea759b339b19f766547736fa 1f01405f3c0630457400523afc6c86455e5b0f54a579337100fd1da8c1ad1ba86513cbb52aaf2aa0 da9f271fb55789fb4db10c405a37052019df8a9fbb4c1cef8ab552bcb527f5b705cd7befc69c5e46 ce5bda85ec637ff0f94f96f40abb53ea493bb054c4ecdacb5cfe16ff21f5173a7f7ecdff52ea2f74 a659dfff5ba9bfd0998686feb7527fa1f3c716f8fff5eaffe8fc7f77daefa5d64f0ca8e6591f548d 750bd4da1912d426332df98b7c2762cf08a89ee81ea816b7ab9f21a0f16cbc335839467bbcf9b6d4 5bf43e349fb977e3f14622775eee44edcb7011b2de8408ce51efe0c9b0ba73cbd72961d7221b7bb9 02b1fc33d2a8fa3f4f005c210180af541720038105d5ee2d0035884fcc2bbab507359e4aa4f28985 5d9b18d544af318cebd47cf9a6d8dee1dd7c0f85e8f86d3fa20e31f4c3205a64c2d37e57097b9f53 fab038b874ee53ff43ba1b3f4b5b7bb7ac4db7369af7d66660d697c637739efc16006e321f006b5c 62fa2fd553e24e3b51e2f61bddbfb6f6e214a801ed9e20fec4d63aaabe5df4dc8f7ca9368fbae70f 1972827b0e62e5ae06d7ba65fbdfc9f3ed0b4fafe04f97b99a9f759a7daf104c576ed91eedecdd36 b73383b0be32a6dbf5f4516cfef89e3fc75fe0ff2555ca24579a8a7a002d31344007e96ee921fa7d bb4f038e026adb0ddfb5f22c00f77b5a742ec82c77ac3f5b2f644fd9751e5ef1daf45c0dea7f5d8c 5d569cbb45341da4f998db68d7dcbc3ad3f1da98b2dbf91d13ac91be312fc35f2075d8d32380475c 22d51613df8f7c72a0ea57932bad97fa894a25917a468df8f0413f51d8d7a1f0220d5b813074c7fe 9c5c60deaa8d50ae9e788a2ed434350789b8974d9699d0468f52de3a987a5a9de775d4a3a1d9df39 73632aed9777cc7d4f3573b019aaf6ba33f82dfea133d612775a09cec900ed263a416b00502e647e ccc1d7c87833d3621cc6d77331106bed86bfd8c803afac77168ef1d6b6f64b2459cb19d6c5979f05 7733d474eb79eded03e37bb8e48ce9c36d3c16afcfe88e7d3e73cd5ceec66a33c80d6e4ccfecfee0 27a4fa7f4efe2b008c89df7f48353f89d4d6f803d0fa6c98d6c560626a3f7a449c1c87610662f3fe 7254a8ba8f01d676766c3cb11b1eb9fea9735b020773585c9f0ca9fae11f8a7b571fa59c6addf1ba 1ae98fb557d411b3d3d650b098de3c291e2adc81ebc9b1b76cff1669a7fd48458a66e2fbe5e12ba8 e1c417a04b7294d6c560de7e63740fc122f683fc769bf1a04d50714c076bd84dcb1cbefac3cce239 b17bebc7ed25efef6ba4cfe804a808da0b2eeaaafd8e6d95a2d0f47d0437cf20aab76eef31500623 b22767aaf58e24be4aaddf02c0073d0310d41b816ab596e8a4a8b4d8043704f5ee947977ef9d346c 176634dbf14b6f1cb8d5d02dda34dc475e1c83779eb9c57cf8c0baea5c7f76d0b5f6b2ed834acfa9 d38dad8c45259a2d0d65b0c77df9eaea4579cc545bd28c036df156725ae252d79bff421a1dfcf796 ffc0ff2db5dbe6414d93b20055c4442ab163a2f3a6a30473dc7e7930962c377bf393b3d85db1fc14 818f3e7028dbd59f9f69eae26a8df8b1bc05bd31a144df3c2503d3bfc8195e57a459e0bcc45b3d1f 8b4b87ae89255e4f8cd82cdd1020795dff2d007c79fdd41740faf138994ce702a8d97a36cd391fc5 741ea3c34f0e95fc95a13ddc5d65e9d9c7ba119b9fa698334a320ddfab55b5a535e5efe076929653 65f8f157f244e177d29cdeb2e26a311104fdba7e0890fff3923ade38c110bfed4f1a7c356ea3d7d7 b5f66b24528dcc3f74326b11d462230bea396ff4f6cfd34338e110c1bb6f1f6972b96379a46d9d78 d67fcea3fee76e68c3b2e63436e82dd494ae3252a1919ccb9de7a2ba6ee3c2bd5f38f0cf4ac0f1b5 7956bdee2f35e7da18c9c56b635cae5fdc4c16bd38debbfa1f4843aa7fd3fc837f485de626a02a6e 4580169d6c9a1b3f7af7cacd5d20d78b170fa93d6e4e53a4ccd7486e9846d98923ddca14f22adb69 5695ab8137e5dcfd9586ed44ad349f0a9bed7bc9ef46f7ddf550bdb09763f9245eda82fb3a9ff2fd 6c823b7a6623b596a2fa5b00584d84a53aff7a1a454bc97c9fc981faac388ace02420405a7787277 d643b4bbf996fa54b0bb767f068aaf1df3cfccedcc156159eacd1a520991ba02d1ae8d78f4a2ce2f 5e7e8a9f834f9f3af73b0b81bb90870737aa7fd2676ba78f7ba871c3cf0ee12ec34bf5b7f88754b2 9278fe1e9be85ce6139d4c73147e3a39dcaf4036e5d817f36c0dc89ffc1b039f6d64ddee338e7ada dfbeca44b3cbd26a04a18231d8b6f9fafc3db8788fe3eccc35508cbb2245f224968ae7536ed2d458 257b0a5839ec20a71c32454ed329f6b7487fcddf3403d88a72ff90fae5658092a56ceccc90413879 864b6f63983bdbfb72b429ad57fb4775d51434663c7edd46c76d2c177cb120c21b1fe6f7f4bc79f1 0ff7de4f51863c36e1c69bdcf234ebdb5b76d93259463be56e4cc5ee3b09e232a30d3a55b648f691 dfe21f3a3974066a4dfd273d0e55ead9b7cf859d60816a1397ec899875ee5631437f7f36ba73cc5d d4c1167a2839a517888f0299e32dc580aeed67bb7e3e7b7287cb7cd0017bdbd933461f8beba3d1bb d2c7eae121d2a45f7c2578a405018fd5761139129322fc5b00f80d12a94a670e6a98734ba6a7412e 8a80d2f0318aec392dac3530e780ed3d6a9ebbd1d86798262ddd326a469770bee00ae468fabd76f4 73e97cb1cb352e1b1d5bacba690e9875eccd8eb52d87d30d8c3a50b428f287201f3f0f41412950ed 428c50ceae58fdaf4843aaffde92aca3f97cb2e48f16a076896e3faf8f0c3fed25e29a493f5bc3ce aa6420e87ca87b850275fbd8de595edd1d557849eeebeacf9af119c06c81cb7d3f308b97377506e1 907408d076cb9b5047955b1d4e2245eee3bc964c552af2dc8f464a6e7fc11174ff1ec5c86f912c4e 50ba09015b263369498d1da4fffd2931517072b65faec6c65d7ae6ee54663ed3129b985114b82888 cfd057f9e3a26c5f86b74ec4158af8975d773fe5e36b73a8d1146876a82e1b8f0e8383bdda8f0973 4f8aafef95cc37474f325f8973643eb34b37f89353a383fe16c9a45f4da46e2e1840fb75f5dd8d1f be6b7633f613ab58d2fdf015ca5aa8f6166aa65ae3647cd7bf09f66d90169ab9c4d786cd29e545c4 de93e5e268bd8715ba8358e8e1ad1f3b7b61311d93f2b88391056941ed56ef83b0bd8facd7f6deda 15b6f78cdbdceadbcbff07d290ea3f4f00b2eb16ffda7e8e3ec7b740ae02d51a42a3e5fde05e746d 30dcc03729e7ae647d2d9cc47d2554aed1266f9c67126cb3466e141c9d8cf6a1bba379f11047e5ea 5ee48336a99ceee973cf9d7655573b2863edb7c4a2c013a6d67b24786709121e3608b3bdeffd1600 916725503b64f1b75ff339c7de4da777bb44a64f7155503a5ab73cd7accaebf5662536f01c733dbb 5de13ca7f61a6b7ceee691e68b0e7556b9e8302156b9fd7cd5ae9065248beed664a1b7dd529f29f1 0aca1ba2d16f331bfaba92376dfce5acfd605e5efb4fe927b93c3dfe02a09a652080d6ca8ba04062 bd4795a003754ce1a4528a479ef4846254702ff1e27ad9e3fb73b1a89fd9a705cbc7237748639dd4 45cebd0eb98ae891ea0b8f77f7cfaab035733d88b04b8d3ad15c377a9be3b3375b9f121b76ddbfb2 2c7ecd180a3e3ad53dec8beef3bf40f2353f4750756cc8d36e5c787f3a07f506b4cd5386b1d9493c f42a011f5608e4f2dd8f479cd6d771962cd4f6c78e7960a85190bbec175f4622f13caeef907bdddc a266de25a8c4bcdb30ad20b30ed9a0f4d7be4eab9a464e5b9838fb8cb159a1bd5e29d8964df0527e 8be0f320ed070cd9dc2d7e288284bce5371f364a97ebc824d384b24b8e6673a7c7b80c3354f9d8a2 b97a318da132690c154a63a8ce764bc2379aa050f694a6ef5f13817339dd759a864f1b46a2ae6663 b95b235c2dcb9dcc52db8ccacb8a79682d1ea43cfa411a84fce7c97f855182c883daefd68fd23a97 3f5fe39635382b2df8c0552e3d8925f30df3c8c056405d5bd877af0c411a40dd4389ba1e4a34ed6c eba7be40f3335873dde7048fcfea1cfb0ac774f7e92edd78ba2157cbd28e5e6aebdd7959796ed2c8 e94e9d9bd4e935275b6af05ba4f74d80f8a4a49dd8c3957d3728aec26975c65e88633adc726beada bcd287dc19e7c98a9257b62474bb6fbcc3dc5c874cdb4d8495237cf4297c3171f2cd6339d92bae96 45135a6ab8822c2bc6155d10b59f17d3cccd03d59d934d6638b3196a3a6b460c36f5ced7ed6f212f 4ef3edd55fa99b93ee45fa91a96cb3876960e5496deca23b44bd777f2a5910ea64c3344e181e73fd 0df6e5d17d222b775c2d0bdff352c342615979d8f282a86ab7b9b917f539ea328f997d649eb36648 5a538f23dd6907e0c124e489f76490dd66c6b1bccdff0bc9d7fc47cb7f005442e9052a51fd0ba06d ab0f607e7706c87e1502e49d6d806a6d8d277fb11780a8920190e63902b077af0058ca261fdeb671 00f756f48f8b5b600500bd5f8fd838002fde42a5cffb35ef416ff2bd6e454e733d885acc761a9ccb e5b19f91d5a1bb6a8dfa8e2ea1bd5fe04767fa35ff5ba9bfd0995651fcdf4afd85ce1fa7f07f29f5 173a536fedcf52a17c2f0320763000b073b902e4464789ca5ab22eccd90da8f6f4a4ed6d3c017230 628054dcc458f4cbc987d9361e3f0b433ade5e7e36c3c6b5ca5e7fbfc4b3fdde77f5f85db70a9588 aec08dd027da83b0fbea4d7d7e124cbc7c7c1c39703eecdadbfde33700504eb700549dfc78d29034 1b02a4a4f2a09ad701a86e272d5095740254d94848f4821740e2cc072054018977cca7ffb6cec1f2 7d287f0e91db09b8887e156e517bd57845ede13a0ad9e3a91844a65a0f8625b5eb4f60358d0b242a 776367cd22436b2f90dd176d76dbbf00801023953a4cbad4c0470019b889a6ae93e834e80ea865a2 2da80650d226b77ef2a4c90bf8bc6de65e7937cf42273a0aa7591898c76d781a9cd8b0a70b5270c9 ca8f6078b23cff532f66fdc9ba53f5e6bb7edbbdd9d3a1b396d089b5bf53fde77bac378dcf7c8dfe 07526fed6f9aff21b53bcf0238438d1387ea2326bd99f45c2d237541ad5dda26184bf17e51b7de74 2571a099dba11886c2a219c44b74145c9ccedaff0415ca9fa2485ac9c2cf5e9b9a27b7a6b637370e b1ab161e90b37e280dfb5935fb96d51047cf376eb41ff2a8567f148477ed170050c7b101345be700 dc12a77f3d8d6a2849873632a036767aa0467777b1d55e8a6fda86cca8d795a270e08e72c198ccd5 7c910a079ed253965e71c46c5df5713db9e588939c4de3fa70e0bbe9dbbb7ca9601dcad5daeb8854 dbcf37f54aef9b878cd51afa9d3c56f5cde0f01b00681a390022124b05c61fb364800e95a43727d9 446226d1e910e4db95c77cd4f39c7bf059edd32d8a41368764bcdb58815ced8db7ddca773071905b 1eb7776e86b2ec6c74b19a68747b1dd779cb3c2daa1fe363144ac6b4d6401f8b66b9a56f96c7ba86 c268553d240ef5df215d3dffbde51f52193699baafe12219a3ab1ba8b5d68954a7de8f2d87da4661 b1750e473343f5e5e3c4f656f6f3edc2b565c1a93e4aa8e594f8decb7bae6626d72ae0cfd1ed99ee bc3704f7ce3d6453551e8be5c7ba639b4e46bf9b18ac23f55d433dc845f4d63928881236aff02f00 a00d48748a7c31999e724b509d6d7f0a9ed7703607d0d2b8fff6daf8268ca92a1be4f667c9c314d8 701148f0ec43a59eb5da031e3607ef4fc31023ac9b1863e8e4aeedbed8bd728d0ffa26c85d35b358 d13414df44eac1100b37af66d794d3974a3b4dbe5607903c7ad52bbf40d29b79f72fa9480959812a c3fed4efc8a5b1defebb13e078f0f5f3b45ffcb0bc4b0c60cdb6e747eb270e9d78194fa110e41eb7 5ba7725f671f759dd0a89e46b2b3996aab83cdcd63eaf4adf31a084ab8e24ca57f0f3ff2b5db82a4 ef1982a49cee944565ad97fe2bd240d73f4ffe21f5712b01a4dec440f5714b8b4d18b9d81e50bd68 d01f2c837cd7df79d0667f72ea7456b2ba15f4f114b375f7a132dbcf1d5edb05eda52d1195ae659a 37e6630c14cee4d3da76720cce5b79dcbe7192b8736ed2acbe8c44853e17c4e5e4561634992e09f8 625df80592de843c0079461920cb110eaa9e9ee88c7f1ee4c74d07ef8457bb3ef557f98870abe13a f1e1fdfbd9bc86e6cd28aaafd71d199643ed801219b54d809212b96c4d06cabc2367e8c658ca2f0a 98b8a4ca07b1d45a4b02bed51d01eab773fc239e94f8ea184e0da8f498fb05fe6fa9e43491fa3534 80a220f766d6e36698a1aa636fbd3256ce5ed9ee5eefc2eef8ccd333fefe78d0699a8f664f9e9eda 69a140b90cf9bc3ce11a9034c73f0d71d57b0e8475e532e78daeb0e5abfb27777d3527e695f46ee0 5a173f858b330bf229727fc64f4835392617beee03e8eb5400c2616b506bfa7a1a98cc4651b6550f f28fa8e73ecb7a626ad687b8295450f2a19fa08b4ebe869aca2c8eb612cfe2489e4ee719a9700d4a 62792bd478e3bb68f3b5ea348d755eeb9f0e7ea13b8be3a5153ed5b37f9e4409845c02f907d95f20 d159f5fea1d33c6c406d14253a49281b0d76f9f4f6f457b357dbb136ecc03ad1d9d9b3a068ebfbf6 e2b01add8095db00ef3f6571a50452710901e16ec879fe795840d743bed2b8d04ed83f07f07775ee 2d519a3bf30b893b0b593fc126976097e5ce472c9366afa6c73f23b9f02d1fc0950802487822feaa df81725026e40b7ec5bbe7f5bad344b6edd748baf48c4a895be9fbefeda8f6daaea88c0d241d0292 72a75cb1a2456f7e676199eb8141ca974ec9ac9d7bc1b5cb0d6d6e7612206d779a3c11fe94ddc9ce 294b36b3a789d2c99c92f1f2fd057e0ca81fa94d00816aee9248a5327a4ca1df6f98b9dc0aee73b8 aed81dae5d7b2a23a47e37e1d95c3bce58ea363ca9bc9c8f625dc4c399cd9bbe1e5ea9d1e47be928 61918bdb1c72fa16f1d6299759a643805df42882512fe699c1c68b17a3f2b90c5bd0b35fb670fd7c 7e81e4c20f02008ff330a876d4c416d14afa9bc1957790f7d619c712e07440bf624dce1af8d4abea 140a4dd4fe6ab855b2daf1229557b62ad44a55f34a1768efd225e0988bc1237f127704c42e33f53a 83bda1c1f1d1ec624784209823b2459f096470dcb0cfcff16e587f210da9fef3e4eff00fa9541501 d58db54b66a8aa1e45192af0f42813dac7e73930f3f3b2f7a82eb9b2c61ca281f23dc21b49f5a7ac f03c8af2d59de4d3ac884b0fdbbb1c4f7edea759c064d812de2a32d01c428fd506e8d2f56e6949d9 af254d51fb58a7286c13d1a8a57e695415c02f900cd071f0e3ad69dd2aa80ac12e99a15ac9757c97 5eeeaec4be5ed74b1a70be9f4c9d6e8ff33730e33bb2f2fcac44a2d639f24d8e142e27d6d7b98f38 b74e732708d8f2e2f83d1a7eb748ef57114cd153bf7360077e6aa71dba9fc1611f09e66d1f31d3e0 d0152fdf03f3387d7e8164802e1397a988a3c94c9addc5365a5783fc9d55eda3f1bc3e4bca97be37 0648fa344a7da3a58c321faeea12a4dde7bcd32cee2f21202edc577ddd4e8b686e328face51d77d4 2ea69bb56efe100811b47f737e6b3ffc9466a4d01990e4247cc8e4e4b5f0493ed2b2e427af64fe85 34a4faef2dff02804744f45782546d04efde9daf74f5e09dcb985279dcbe5b30436afd4ca8dfb25e 9491eec7755d3864eed36b6ffadd9e27f08c3d2dbf86c418e59fcce2637de1da94e71fa303771867 f757a25e21c555b5b55336adc9aea8e0dbadb68ca55df19c5cbf22962bec8a0d2fff0b00f8c0bc01 f254ea3f0694da20c231f7d9d91d91eb3cb6f8b0a8f55b63ea966b6aa68c59f9a2b80bf9f635ecb9 b3b3f042b72755e64ecc93cd4bb4bbd9de29d629bc0ec3ed3dd8672b872f59c8e3e55de9b36c6cd7 536e42188a97eeb9258cdbf1461887ac4f6c2ba33c6134a7c55f00c0aa1883eac4abc7d6bbbcf4e0 b9d67c3cc4ada7f59b93a6a234ab9c5c89145734bd52fad220de0352ef72d5ca0b6eb9c2766cb5a8 31c7e6bbca53dcf4a51d4680b6f6b9532f208bd7667657612b95ede6dd6913bbef684aa0a27cd8d8 f39abcb16bb76063c585c2e6702d57fe0369a0eb6f9a01fc7e24dedd27570be3e05b7ddc3955d7ba a7d35329de13ef89389744c1995201cf0e24e832417b3dae140973e675fa6e685f22a90310c0692f 2d75995cc13fb6c06e5d9cdbdb2d347c13afeda240d4ad4d7543372ffdb52f06f89a9d0f4febeefb 65aebbd7d667dda5e6f95f20b9f0c8f77d9c5019a3a47dd37745689d8b2dc8b7b3a989246860bcff 7494ebd9198497ecae079deeb3679bb136a3317d6a69ab83808db7fb451562c8b2e35c7770a0dfb6 b598378866867636c7391baf4fd8318fbf3d15c52f6c76887ddeed749305265cce1c366dd52d6c0a cd825f0054f5d1fb99d3b883e66204abe4cbadb34043b9f27578713797acb15738d57af92784b817 8fde7494e67b523115f60ed3373526f17c69b1234a208da1bed218aac46d98169da6f1d32a1e5fd6 8967feddba9838a30196bbc9e94652abb12a3e6ad3a546a42f6e2500ff77f84995ffb716f32d733b bdee829d32d55f94b01fd3cc65e286750ebb9d08d69cba3c439582071dee64fff065fb60bff84469 0c954d63a8133451d7ee26eacaa3445d7686c76737dd9f868f3e168189d3e701cb29cfd3aaa8abc2 12879fdab26266ed0551af81b94963508213fa0bdcab6563ad08933521bc32b5dd59e8a8e9edc99a 13a27e64a0d988ee5b1471f832d431113816c88a52d5b624f47a6ebcc3d6de74bc75b8ee47c30f7e 1df6f389b04a2911564456cbe2175d6a78dc5e568cb0bf206ade786e1ec2d59c6c1577339b45d25d 43b3e67b244dbdcb42fb056ec3491b139147b03e03a5b362d142fe45c57b38bb5fc46a89ac485673 6b62d690b00977b9611a2ab1ee8787c34fa7018cc566d9fe65a52c3ad2aaa8a1b7250e55ee8bc736 f75c20afd89a930dcf9da36e18cea88e07a61ee764a71de09426219fcc37835cbe3d8e9552efbfe2 27a4fa7f4ea44ae5b9ba706665cec287e5841a39b04a5644c12428a469afb956e8e3a377f8c572a2 535c156f2f7859b91bf50581189db949ea8339eadc26339bd617b36620ada64c9fc5a79df89cee4f 9b70639a1cc7d2fa30becef7ece8abaeb991886df951eeb19687cbea564f401bbf00285f9f2228d3 0d33c1ed0ba0da6002e0c9e6e7fd37b05b4b6c97cf1149feba8f017c4c0c18b8f3e1019c4f3e0fbd 560040e70b04a0d5b303a0516106a0e6700ba0ca9e8d1f222fc6c40c986ff3920fdea857fa465417 2e44cda80787cc1086fd3850d36a70de0cefff06ff55ea2f74fe18b7ff4ba9bf90f87f7e0d286bae 94a8ac3d41997f262ec07c3905f0fa20242adb89edd2906b002907499b97dd0398282452bb430b40 d13ab1186fa9ceb3db05105698c58f6f83880966c2c4d5ee5e789b2f3d7dbcfaae975f7e444d1d10 7acabb10764b1f28e0d62eec8f17a78abb6473855f0094edb79c486c275dea04895725317300ebb2 0490e1eafd134fdbbb2840121d00693628003f1a62a27762257ae96fbc5d1de1b8ea5e3aefd7469d bcc9f0b97ed7b90f1d390df81a51f7a116fab9ad1b76972c08069343cebfcac7b23fde09887bcb34 2bb6d1b8a6db127e8e7f46223548ba541fbe40a5012757d357960006960c90331b03c42a3400a2b6 e70039cce944ec4a8e77d5deeb6d0d7be0bdbf4fd3453a72c25133a297d361d4023b2cf4e9d33e64 61f51c76e5e0167000767c3e0b477e66d1c979336d54716f501f71a0b3095975b35bfc05d24e2bc7 9fa4579db9052afb6109c02d0f4b3a347b03c84b4a6c9742ad9160b54844efe918657ae2fb70869f d171f28ea2b61c1742d60ed1e00d85bde0cc7ee7c110ad103ed03a8ccf8f31d1cfb017f3270e7d91 43b724ca5f4727e2b203ddbcaa550fc795e779becd3ecf5b3af767804aa9a0803258253a6fab3280 f12f9e5c70f806aad5342cbd5d357f36276df9555c1fada9c8bb15ae613851f5643a17fce0425d73 fe8725617fa2e22d4ffa12636f4e909857c85f0f6e623d5cdcd2b7f270a008f1ec5ab909acfdb45b 7c39ca00790e7bfbe263be79677f908654ff79f27700957a22ab52dada00cab3503246d14dd29b43 2d5197ff80aac1b6e27dedbc887ca247869c6371c17577b8f9626b647bf2bb02bc62259f06ba5c0c f151e73e78f51c9876e6f613c96ceddab2c45a2f30bbbd6819b74d56c3c3e770defd1abc3a29dd6f e13da7c3ebc9f717009541ed270859a9d30e80667a3225813e01aaf95522d5687d410dd25beff668 3d09cfe7c22698642f473f8ff72557bbd90fe7b1b9060e42eeb336daee41d661d168be8eadc2d00c b6e5e5f33dfcec8ccfac7032b2c3b6f6d369f7b573571116dccbe425af3ddbfdacda509fe017f891 9ae0ec0288f193f9a8bef82989a2835a66fc891b5b168d7a8f49ba513998e8d6d2bbc58907baeec6 67a72a709a6583bafdf258e76df6c545d6f8362a1523970deb8f05eaf5ef8931b5b897e522a9df17 07497be297a756339e817aa8b9dfdb71247f957e7e14ff0b6936d1bfb7fc0ba0b26cab895436e952 2393cc47cb5d5a6a84d7406d4c7edf6d6b0d079f57b9e3170261e26e5afd8d6d3594a3459303d93c 17e8bb219a6febb16c9fa2e46bee583cceea0fa80be948afd5d4d06675a85a8f19ae36f314733bae 3c4d091e1547e9774740be9c9b1f49d0bff12f002a6432222b4bfee70dcc7006aa03843b91a0fad0 f5d86a919fa8df1c9683dce053f72abbcbc041e7b9b9759c12e4f3d3a34f463e74a5bbdec61e3a71 2dba1a397bc42ad560f3376f4a579570b0ed287d9a9eca31fc22e511d94a53b024c1214c694a6b91 a864b5582c2e98e81700156eae277a6f5e323db592a9d314129ddf971e378c59148e1838ef17790b 71cc2b963ec8b7bcfd63685e9c0c6e141e307d5ff3bbabf6ba843795ce53e68db9777d85e3f21f39 d63e25e92b05a834b582bea840654c5c18ec59d06a8e2e6071d7172ad771c413996af01f48bdb5bf 694ea44e925ee50c3f99a1c64d808432096a15477f770a90174c4de7e34159a1643bd0a8fae28e44 c7507af4fcbe21d59d66edd093da26384989ee902e83dd2b8daacb99f13992f2b54d565c8e3058d0 985d47a8bc2e739e68bc291ef1d6b7abc918ce959c81f05acffb410affcf0015659574a9f24a745e f1d6cfd4995645496393d1e90c1c5f218db75bdd1db296bfcd959e891f8b3ef0516faca3f466ad1e 0fde5179efc7bccc3f5f3769de5a1be2aad0f604dccb02dec87d4b7c75586c5cc963737cb1c3ddf6 429db3e910b8b486733301ef9fbd0febfd02ff901a4689d9a4112d50ed58894e2c4aeb321a2f4fd3 b7ae53b77bd1ebbcbf7c8d55e6968654efd55976a035831176eb33d7832c1cf39c34b759492ccfdb 77012e07165f0dd5e85ad799ec85ce5eab67bfffe89fbbd417e3a2f6f1ca45d3b29160ed73e7dcdc fd3bfc8454ffad0554cced1d40d55204e0986983ea26d8c7fbaf710b1227d9708d6ccdb4dbcd8f63 663bcde8b19ed315bdaeb8bd5b18e7e6ca282276921cf8ac58c9cc05feb97aab570bb9fcec4f7300 e69dfd60fae522ad55e6869959fb04246d79e2fb1dee3429aaf7d3a454764ffca0e6fc02ffd0d985 228014b944e7f19d3ef47abb0c2f07b3dc4473abd2e76ef598d3dd5051cfbbd7b06151f5f9435301 236f2ae73ead8d88c5674a40e6edebd5badfe5cb71b031ce27b8e970e738f73ef17ea678cad6aa4d 768ecda66ca112326c01dafcbccda700b90e3b1fbfed5f20b9f0f4034044eb0d90bed249a6a7ecfe ddc9af787f39c84ab6a31e44535855c4c7a6c5597a432ce76e6f7893d68454320f73229536d5b540 e0f4e16a1fb2dce5189ea4737fddd1b9513f679da6153364177923cfac3e468d2933d0e8b88e98c3 71fd46e4e3bd27db4c79aefe77a4f505fe7902a01c9b4815c78985b77b2452bfe57dd45fd5380f1a 9f388bf57247439529eebe6309530d4cffab08c97c2edd5ecc40304ed192afb373f2c208167b1ed4 37c2e97b2aaaa71c66a60614a3e539ff787789ef11415898de91ce80461bb32d8d964391462bd88b 7e1e4ed62ff00f9d1e0e0022db1d506bc26438e29fe9bbf09cd716214d811b2f1f1badcbe8cd4b5d bf5d9c73242f46df8ab829e35d7eafe9f38b6fb589f32094692eb39e5e59e5fd55189cb83e8ec490 7069b2b3fc50547d5539786bb67d6090129e40e30f4c1e4d03c45493c0ac5f0040086f00b8c97d01 12849dd86a16085fd98dd6767b858f0d4ce2873a55bd912ab7711465965d795285f14a023a1da719 2bd74ee63c3e5f9a8d0d971d5c0eecedd9e798f528948ec9dcab53ce66661dfc412bda479361713f 386d9b24e8831509eadc85048dca63cfeda7d69f91966c4d8f00eadc9e7fe57354dbdff6bb1d1ee6 1e8489035390a1cf9d6c363a6af8996f6e99c19e97b4b86609e64bcb5d9941093dc7893bcce5bad9 258bad8f3b068e9b2c6d11364f1dcb9c7660c3653a04f643ba15907cb02a9059946dece65434dfcd d71cb79b4f73fa4eda4ced5f00409b9709e077940555a1d20a2f76dcb11d13078fcd0b4e07b4d65d 9e6a8a84dee772298059b1fafd1857ffb801678003989b5de81e8befb3f3a3291f3734d56ad254e7 155ef6f1e62a93df25f524731322d815fbdbdc16bbeaf56d65d29a6f2b6d8fdb569091be2d879774 bfc0cff1cf00d0d17f016401e540ad81a09e7616e3c7bdbebb69cca2af2ad23c2c4a6b6931162cec 49f3edc8bd5f86e1e1cdcd9ff90abb16f7e9b2767cb98531dd2e9e5687489aeef7fca87822a54e28 ed968e72dfea6dd3270cf49525aa3454dfbcdadc62f342fa67a23abdeb1bd37c7bbfc08f01d56c39 a09a1de7c2f7257e1b4b2777d15ceb7155c4e82a894f82c80acdd1687e0dbb4fee722d7f9e9c52bf bcd94d0c958e76576fd29de37c7cb8d89fd57eba954972811d4fbb4a75216d37abe983d86d1669a7 6d2c93c96d9a75bbb176dde972ed0aa91fcd0cb5351dcded5f00c016e182aabe00cfa97da5f4fd05 6794efbeca8adb4186e73b0289fcec877ef5d767f12ef3dc92bb9bac019fc0d169d5cb54d4e0d1c3 989bf4f6f9093cdf696f6bbd7d7c558a78e5850b515fa9ea86ba3fadb58f7cc0babbefc078345447 38e7f5289c132519e7b679eb0769a0eb9f277f078048a2fd3a65b9edbd8a65b6b751e9468a8f983d 5e4ff2c03c8b806c73ab5c717b5a2f0f02f30a36e6b18d77ded470ffcaefa5700b93e509fc531c18 dec1a36dedf059100dc6dd6ce82838aed98f75c5dfd340c5872fd8c184c6f08b4d5ca1866599c278 2577f9e34a7a97d45fc02b1d36e9d4f9a8ec75ecf62eed3752e55a23aeecd025b91b6e64d96771dc 670e90b63d1e17159eeeb78a8f8370b3bd7db1c37d761b7d52d8a23b18260e71a3bd613285c19acb bf6778bc0a707ce4650f98d82d702b65584833f257c57befb5d4363458e2880b2d2baf5e7ff158bd f6099cc39f61146eec5c654bc395a46a59fcea970f5862711671e6d0dc17e8d3cbe952a3471dff79 df5ab43cee8bc240d83d968974d430d2182aeb6c3a1efd5ef7a34d06bf0eb112f615160896533af5 d5b2d4ec2cb57567bcac3cfbcb05814e777393da7173b275576736fbb667547f91fb17d290eabfb7 fc0b5aab549ec973cb5df2cdcf74c1e57d717a3c4ef78fc3970eb2a436162a3ba2706f6fc98a3b21 a86a806f3aaeba5bf7c3cb111f81c31913270709cbc9bbd4f75c2d8bd863a9e10b6b5931a6de82a8 8dde73f3d0ffce516f9088e88e91a9775eb6a69d0f319c70536e3519e4e263822ff367c8d2e532e5 ed463f2d3ac749196a746c28affe5ee9a9b71d72dbbd361e897aeb7e5004f828ae16b159a65e5d2d 0bd5e612afe43bcbcaa33e5910d5ea626eee217c8ebaf9cdcc3e96c85933ccd353a60fd849788d2e 9341c64f0c0b39931a50e37131638c443cef8c66502b1e2adb79f117107643647c1e8fa9e171bfdc b7f6d9af8a6e511da7d77d3f7fc1a61f435ae2a5bd3a37773f8ff066540b36a74cafe04cb8e12718 c7c2271e5f67c5cc789c470a2371552c8d66e50a3454363964b8448ae8402373cd015eaff4fa0fba 38ec136d64da334f95658fec37375dfbdaa7fe03c9d7fc5d3328cdbe3428159a3c285abb3b28af93 feab5883398076950b80e453b28ec9611e4064a509a0c174019279940215e72e82ca3dfb0495633b f11b379b7c02f5a7906665fae9814ab7318befd176176fce37eefd740de98d765e7a74e0fc57e802 c30a8fbcf40a3b13d60cfabbc9f3cf00a56df1084ab5be90883dfdd4b92d0b8532a8bcc78954b9c3 0328901d0085b902803824914a6e96895e814ef4ba32a8b80513548454ea7197483cded144efa717 3faab559bcb94d88b759a64f6f747a96a28322e9a157baa5b640d859f35610c2f42b383dd72fff12 37cc3f23919a4bbab4374aa4ceef4f50feb42100f5a85522712a00b8edba00ee344b89d87e0b40ca 6ef9138726d563a23790015486adf821f4e298e82c0a3162eeaa6f73796a270866efdd27bb8eece9 908d9adc5c0c3bfc440db8eceae9c78bb9e55f8b3dcb1f6935cbcb314ff33f904607ffbd0594d84a d2abf385084ad2fb052ac31502205649cc8ff64102f0b19078ad9b5129d1bbe9003877497e836931 b131782b71751d986f33f8846f92ace4def55207896c7e9ada021135398ca36620602183bac720a4 b3bc1f9fb2aa3f8acb4f4fc46bb637fd7e6c5799dd2d475b6dcc3f2351873220cdba04e52c9cb82a fb7df5a7d30c730d605a48a4baedc46bbd6fca0016e47ea2575ec5b5d2897eefdb27317216c74744 c5821fb5702d133292560ebb3dbb1e8471711070143a0f06ad13ed8f6bc78b376b1e65571155c35d cedcd4f7748b91ec3a95cfc2b64c3936ff0c50329a89d42d2682f2649838aa915e0730146f00ec58 0a401af39f483f143f05b5f7b696bb45e4b6a7a95318b517533e64f18e1e4452c709ceb511f00136 2ef8d72f5ef5c70cd5f133ddc7cc13ad22e92a1e7a76b4a8293bd070f4b08963dfb6ab9da6f3b2e3 876576b763e3ef90be9be89f27a0e47712a9ec2691ca6f1323b0eb3700bc46b689c0342cbda713af f51cc0ef83796847477e340dfb852f115c2ae1c9ff74bd9b9f2d2b4f4f3a6a819737f58c7b5bfa90 5be2fdc6cf26f24e69e8e0e1726d130ec958fb2c27bca898d24c7fccbd9e516ff032f872643c66e6 5eff3340393362937e3dc9a00cce413240d3d473bdbffbeb51442d911ae9d1dbbecba5a85b5a3482 78fb1dfa8227629e7c262857dd8c79b74cd67567cd43b66d04d0dbae150b39eb756d2056ddef755e 746d3e33d9e6f96080dbfe644cca67e931cfd1f7475ea1d3b8c0bda4b7ef3ae47ff43f0394a16922 d517145019181180fc6e5a150523d3b0f42d26cf52103126960d401badfa334ceb7ac5d772e63c3a a5f4119efd8c1dceb2a4b36c35d4f5e3d5667acef3dd28c7c607fee68cc9b2583532a0df7bccdb07 fcbe5aa8475dd75c4187a1b7a66db781a1be2cf97e73bc85fa5f912662a6c7bfa496a1fb0d543810 2763749c481d1cf6a05a5ddfde2d61658703d088fd79c12cba7a77893a84501a5ab6765ebd984263 ff8c11f56408fc467ac80b547f14aea8735f1995b77e2fd4d3875e3a3cead634e3cb8d552b6fefd4 0656e46ef4a3a9dcdac4e8aeb011aacb6760dffe0c506e6127501ebb6a722f553f00c6c91e40b8c4 0d7ae55769926cd4cbb59f4116fa045eb9c9659cedbe55b1e8bed436b9776162881c863f96d92275 c7d4c759df308cac3d6fa4a15ac7e4ff6c98b3cfed585a976f6d5def28a75c0993dfe4f2280f9dc4 8afed00f5d9a044cda695256a6d43f2351b9e3927ba9a003a8da4aa45e9944aa29efdfceaa2186c3 6556f7d49e6a394f028b2c77e2e7ccb354ab1a7965dabdeb99304df6d3098c2234f455a5d5261f5d 6ec7e7eba69caefc537efb52208f6af79cf4d1b2a83485e65351de4aa4b8403317b110cf7441bd76 55a192c92b7fc6cfea891f12a94eed0e20629501b07ded816aeebe8f7c3eb804595c9613276f77b7 0f67c47ef5510a1873f752baafe95c53af5acc58a5a2d2eac6f006a9708b3d2b5f5b4341fa6e7bba 941bc1b6b8601ae9e355417d4d2b02b6577bfcddab11fc86d97309fc1b8fcc22f56ae65fb73f0394 a9d319544a430340e23e0b90a2d6ff094c86517061fd82b448f3089c9d88485687d7eecf5cd777ef 77ae93d56bac86aa3453efddba8978192c561b390395292917b967b1e8aab280e7e427ff18a8018f d05ee1ba332b8d2b4a0ad8c51e9699047bf972f8d0b74b5358a71ec1cff1bfe21f5207ab2780bc73 0e207db31fbf90fb2e1cee36b4577ee7189b4aaeb8795d8ce58736e42d3d310ebeaa7b21e05b8ff3 5bf227bb4f77424a120e63e232ba93022eec18fe116ec4aba9f7f56b3d3b722e549fc89c3dea5a3d 33edcee2dc092e877337070b0926372e54daf29f01ca57e1279ba842ee4d007df944eac2ecc575ee b809261ab27391a1b6b7d2ad83897b1f5def4467626854e6f9be9dc47a49fef07c237dc7d5405c11 d6428012c78aaf2ae8e15ae773e70b65c4b733a3862f2ecae4d2e7371cc7cee053fc3627a7389eee 13dc2fa7ab9e5552c87fc68f545051ceafc4edd7f3003979bd77cbe9affcf9d7c41d54c0d7268033 6929b0879e3f733ac9d57495990ba172cde70b720ea65111ab7cba02bc3f4ef8da74845f1c3b9986 7d4963cf5d4b94b8c183334e7c5e094f99615462c52f31606762bc6567129628379e0a2b652cf907 6948f59f27ff0150d6e4446a78b3008c39c9baf48c7b11bbaecc3d6c46cd2d4f06d3671e5b2fefdb f2e4a85120abde0658b240894b3f2b6ae81011364cd0e16b9f9fd7ef5edc6571790e5ae68e3bab04 7d025f4238655e639d9d97718fb96da43c53ba655a09c435535211962959279959152ed29f01caf6 fd0a20e465ff04532e510154b3df6e7896b4a1bbb9d5072fce5eb71e3a519be9fb297c507b99bbac 8c1f1d5b5ad2e2477850b50a8f9e2f8d8b87f487e71365cfb9cb9cdb9c84e69266e56af7c2dc3ed0 eda887907d8407f37427246d387a9dde1e672bda70638636bc8574842b98f867fc436a9a7702db99 bf8a8b064281e838a8cea3cf597487efdbe573a4b51756ba7ade86f39320cfbbe84bc4032ae65f99 42e9da2a1feb676e50e971d7cc63769a1e709c5d10dd035306d1e908078e4cef50f0a2ac7515508d e7b64a3594ea22c195a20ef9a698a0f37f210da9fe7bcb0f4039b6f8bf922410b45a8cc9e7a3ee2f ca00b118e99379e8c2b4a8b9f0a27b7be3c7b5923d2cce52799918f935148fae74f75d384753acca 5d9d62e7349b2b3ffba18bde6c757cac325bda6cbf18ca466fc2c1836ec6a1b305ef7d589c40fb93 06923b8ac7f7fb007cf94367884a7f06a814dd44a757f7927bfe67917eb76202761fb955c6cc1cef 9a6ea15e46edc5b9a632ed351712362b32c2937e6857171f7897defe99e17865019ff27ed862748c 1a1c89a83ea7491d6ca8d6e2461dbae3f375cfb1b73b793d183e396ef5d24d16bbaff11a93639820 c971d6be2688853f0354960531b9edc77e722f8d0be1f9e167cceb74ca3fd6931caf06d22252a604 534d6c797b22225c98bab87cf3c4c997fe3cffe284e3393e15f46689b9f76cf4b8f5983edd58b5a6 87201be1fbc8d6f72430f83399b16eda6e5e71bced6ddb2e6e6ff368b4bd2d88ddf6d6b72fdb1b1a f2ff8134a4faef2da0620e95e45e227c509d1079db62eec243f33946739b3346115ccd92f072b52c 980adee75b6d8ab870d7e59513f7d1e35454e89021b268ee881eec2ae55599cee124b4d274ecfda8 1f2dc869ebb5db2d6a37768b350d85b8b37997d8d4d60502b6a111b1298a5b620d8ac92242e7f83f 23b997681520353188badfa565c87e37b5a1f5fa1d90cac79148a9e2e455de29e29f2b0b81d66558 1c609cb4ad9f4ea5bbaf31557ee5d04ebff6a5bacca3b4070d12dd6790599fcc97eab35d09ae125b a85862882a545536e47c6c6fea0523bfb63532fd351b1414b71b541f9dd6f66474fd3300f479e900 3114db3cc72dfc8e7cf4f5edacafd612a6257644eb52632fc3aee39fb3d8be76528968c6267e6a5a 7af26829c93d9dccf6d6e17d1dc67ba1572c9273f751ddadcc6b67bbd6c909b17d12ebcd5e5bd26b 1762c4757b6bdb7880ce0a78502a0e711f6cd6b8afcb479c9da897bf431a52fde70980d58c6abfea d7f5a3d2a0176a576f2ce582f5c1786a2faccf93e58e3ca9773d6237af798321cbc6e24875333415 ea8e78b82e78633f83b60159b4fad95dc543a12d12555a04fac9fd741a352daed6ccb278c043bdc3 e3dcfa64e203a498c56253ee62f1b98663f17e4b63d7327df933e2c461140c29da4fff0af9496f6b c1db2b2c4dc13a8fedef927d74608da997d42ced238d36dd55f8d501d012b59716679e2c19136d6b 5c1a16f112e137d1c823f94dbb9c83d6eca6d8c2a3101de3c34107c3787e4163d9ec495a4966c15d cdc95d615568c0699ef42affa137abfc5da7ff8c57f7624c7574f1192b93da7d26ecf6fde9f9aa29 63161ecda6b4bfaed9d4b07f2c1cb22db7bd9f3f7b0b7215aff63bd818a6856608ebd29036ae58be af8357c15ef71b8508bf08480efbe61008cb958af5d58246fb4b35e82f96953e462e1ec5a7b0d8e8 357381d042666e26bf7f6ee6f6d3ff8a9fc4ffe478874fcfbe027a97b108df9ae3cba0a88c58e8ee 77a9685b61f672f3e9eceeb76f76fbfc4eeb84dd3d8d88e6cb4d63a84f128fb90b838fc0e58a8993 8b82e5e4cb63b52c9eaca5869f8265c538c63f95616b5c7e6e1e44648e7a5a6b4675c3e9acf96e6e a75e62f94d99917b9f763393f8cfb8713dac2fea1132bc84fdeb80d5cb7e87ea9d0a75122372a90d bd45ef236bc3d43b111e9fa00cf6bdd62bd82c336eac96856e67a96183e112afe0cbc583c0370be4 b54c2b69cfe839eae2dc8cea2cf9a9c72d9569072cef136e42d8e358a6a2f1b828e5475f2daa8dc4 7537dd35349ac1c4eacf9056d562ff7a3c5dfba752d96851ecfc89ecb02152d978bb2a8d5f7b1901 cb098ebac44b92b1409eac3db3a9b53ff5d8f97bda79ffa4604dc2cba138e14627681c8bc7eaf83a bb3446dfdba93d1257427f94bb5fc64365a32e8645535d0ff0fa9dec3f688bed139dacd0333958ef 9183a1f567a4eb4d23b8f6b8dccd6fd12de909ef568e5d5833f6231964bc3c5d56b4d3668e5ad3fd 94e9a2cca40fbedce82bdbfc28a769d2b06870ca00af315aff71a01e7dc4239e3db2bb7c75ede476 ec363f5db7c34cebe9d3a84e37d77db7b965f7db8af55ebe755dcf2bad31bc479be28e6e3767283f f83340b1dc5d83c2ff43d8796eabaa2c81fa594c8039e71c1001457296204604045adfffc2dce7ee 73cf1e6beefbe7eb09aeb12c9b0e554555b5323f80c2cb9100149d9f00294725502c66e6a098a8ba 3f160195b341119d85a038918aa0587a7641315b59012458ee0172154580e88fc4ce3d951f00e1a7 6102319f20aac5db173288aedde63caaf1532cdc83c5e1fd9a2ee87747af51efceb5730c826521ad fdf0d3fe0a00358789a867944a847e4a00ee072e40b0722591b2b10445d3e141d12a26725a449c48 7c4a7e03faed8362a7b506c532462512eb3f81ca4810d8007937ddf84c8ea2186b10f9b8ec2a9588 283ffae183f8ccc3a68b6cdf74a7b60fb801420703a84007fde843fb91f862fce870a57f05804673 2cfd9ac26b97889a8c480073880790f3a8068ae270054a8558002568e080624c27a23ebde437c8c5 21281e6728284e0fc7f81c28725ca92b66747d3e1e118146ef88d8767351bdbc29878725d77dbfac f3fcc70f5d7a6d8341ed41fae22160fc71ebca781f4f673c6577f8ffe0475450f85049e79a2d0dc0 413f0045046f8062b4db80d232f5f42fb1eb8f4b75a08344e877222a688d12ddb0b5896ecbee2122 e29918d529cc089f25fa161e2e6610b6168fefdb83a0e29b96c69d80d336533f2e1c36deb77820bc 19a3305eae7a605ffa7dc3be96ebee5f485daa7f5ffc03005aafb0e4c1334700d7b0c4fc9fee2350 dc6b2d50eaf32828e983448bb92af7e4afe01b5fa07739ba67defda851f39621b579936f4f7db36f a69fd37e3aed517182f7a6eb067c711307039529f8f1fcd9f0be9bdcc4cbcdbeabd7f25e275cabd9 65dccdb9c2b9452cc73d31dd6252b0bf0240fbdd2e19aad24fb56b98552c80c872f290eff7362869 370c94cbc48fb3ff11e344f0891a3e8f84ed07d379335f6a168455120b866bf2e8c71f5ef6275dde f4beb7d3dd5349efede5ec30f33a61edca0b3aed066ed165d24dfab9eb9cf0c78d38d18ffa93e7ee 5469c2dc3c3c791aff0a00096422eafa4403d87f9c4111763f496fe6bb89e51cef52f7b91a13d9d7 3d7c4de83412f2cd3e26f94050db0d5fdec3433fa3e4d7defc0ac8970101ee054f80e65a01ecb828 d1709fce73029e3b740f3f6e17b777a70e97e98d61eee87538d2c88b44b347e7fb5e508ecaf70fff 456ab0ffef9d1f00c8a01251ef6f3679f0854bf2e091b4d448a307cadb3a01caee558d5a75d17973 ecc6f73f4afcf1b3a45b7e9959a1f342eaecdc2d67d6db275e9b528ffb6694262d3d1aeae874a7c2 e9f5e63717fe35ac2c33d721a9d72f998e3e70e67ab03cafa4c7ee0c4f22ca4635f3609716e2deba 140ee4af0050a812c900cdb13fb1e73f4be7bd9f03257fd2fff14c47071de3df7c6670f2bf1be8ee 2d79f5fd2a96f1c2f32a751a8f43fe35b81fcfe2fcc6f218761d755a49b74139ced12639cdc96b95 cbd9e8cfde67f8b0cbd9a8f76c5b9745255539acaa39c0cc7d717c301b4eeb601c2b5ff2df91882a e100c65bfc7f02a5abeb3c284fc801a8c04b22ec5a153a98d4728ab7eaaa96eb1c57a95fe059b7e0 ef9d36e864c927bef5cb2c73ea3b8b07353fafd5c9d6b6c3096597919e60e1d8c4b06a39f261ee87 526cb84abe667472c3e1895d919b531f3eed75614b12faa8b3fb27d280cbffbd03e0fc29c1652926 b69f90d6c5381640996206d1b39c4383d87148eff46218f7d258a90fb79e716eef10f72feae0fc75 a0c50439175bd5965d7ebb43ab66cb0bf390157ece5b7b0d04dae850b272622f9aad4785bba70b56 07d23e25a1ab4d7177a965eb5552d59e30a12ea4fcff0700ae5a04404a7462f8f1465a1641fb09f0 4fcb3587dec25afaea1dddbeb6b91ef96c66007b0b195cbfccf5f3ed6c5d66915d79f979abce0b55 b3b55d778deea039390d8a9fb52ece2342fb081f4e9b7a595dcbee3a7775616c3f8a89bc1acada9b a64340411861279f7b3221637901ff7700b8772301829a2a28be5d0f948b771854a053ffcd6f5613 cf685697cfbb656feeacb92793be0984b34d61e93b69bb2ae67c93e2771fa357ae21a7c1e3d5d0c7 aad2d7943b3d57f5f366ab42b90da5249bb2a820aee2c858f915cae5dba622110d7324d53e6534c1 94101f4a67f727a48eaebf2f003cf7121d437a68a054013e288f7d382624aa13482adc7f6dcbdae8 71bc8c26d7e92dd8380871a0ed5a1f3a99c799f4307a762fd641ed55d05486a8a8a775b7a5427465 a414896ffa7a55c6ac2f295dfd9893ea838a293e9e1b4f6cb5bda2f00ac67d81e69495409fb2440a fcdf91c8f924ff4a9129cde100940f008ea8444f4eeb0b68b94bcbadd5fbed5bb47ff42e05575cda 576a40594dc1538dfe6a7ed1c105049ad6e232eaca181415741bd7e5ddd2e94b374a9d49f58b8a89 2d913a0ab427a94297f93e782e9a67f981f8eef20369996e04496be25c8ceabb7f078077effd5ff9 1c25a69a46f5e6e190e9672a9e31dc569ecd4350b9ce2ab3c6f97c1d4cacc725471881c28ba791dd 38fff8a11fc64b5df7464029d95f48ae7256556aa06447a4d8d558600ec30dff567b077e104e244e 1a31176efccd7dd82fc3b45875919b27d8ec5815277e47eae84a5b0033f1feafe48392d37b83725c 82dfeffb05796da705e8cecd6759c77cc6159b787c87267d77b0539cdbf17af6121aaad93a3e124b bb16c9b5f22d27bad2ae24f878bb21f4c4d2801f3299b4d338e99e21b80c5212d8f96c78664e273e 6456a36a8d59adad29b3da5412337bd3c2526c7f0580b5dc0114c9be094adfc91b54ea8d640c2078 cec535397de371fd806b78c62e3c625145ab67f04d7ea3ab709bd1a09aad29586f74956b761488af 3afd1582cc04e1c3075ce740623272195e9b269b989108713558da86dc138dda93802e6dc30a5dc2 76a3045e1a3594b4dfedbf23d9ed8b89a8a785f59fdcf865a31064b0067836cdf2f3b2d4379e4dd6 50c8ec5577edd3a43b5968a76dfea016454d9509a399065948adf2d5130209fbf062ad54e0e48f5b 61b5b7d6610c7b3f6190ec1ea54bf1fe78c4dbb246dd59e05277ce285177b23da0eefc6943a5eee4 7fc78faa5eaa1f4029b7b74179b37bc77513643d83305e770eeb4a6767f3bd9b9e97cf9e40b3d1d0 e77c63a6a2ac4bca37752d4854b36c0bef81f8e4c5cb20e214319f67975da3c46ce07d8bc6b28bd1 117f36d321403d4afd3dd5c4f7cae168daf743075dc30940ef70b4a97582d3f6df91aca4b3232875 8533289bcc3bf41c2a7c6da9c2e99a6bef28bb5165d27993be8401bad6f1ab1a6c81b18217ec9de4 f6d682d89b06275ea2881b372f540276f9f2334c91148a7465baa81feb834e9f6ab5cbf343b786e0 7b6ed317c9c861afa4b848b6d84873bb64a40f1764c4edd221b0ef070cfa2b0062258a7f89b61d50 2948c19b0f4b8feb07ede16767e4eecc9e523a9d32f6fcad21d0b1a8e0acdc975c6fbb11fbcd1acd 03fda573737f93c60bb0f006f26827f949c79b7b84a8a73ead1d9809d2d9f3bdcf948c0f851df1dd 211ca156f76762f6a865899969b48899559d114abc43ff1da05837b91fc3e3e526a2e2967ff7b9c9 d649b4b7b54591d3f5695a67450de9bb4fa5c61672d2ebb0688b5c75b4e03f5876cf2d2c5d6691fe f24c5f9cdcf3b85f5911d5c9e2b943efd929ed8717a4454e8ccf88983b59f4c7739b6fb238bcde5b 3bbb08839df5359a38dc6ecf70b8846efe1da0c845423240f3ce7b00faf245bbefe7769d4466c650 bbce34641e51f28399fecc9bee28d9c8e34a54e5954f61c22db90067ce3ec7d3c466703ab672dfdb 21904ffe5e1ca01f52698f6042afd5eab8592cf47164d75cefcaebfe11bb229a89e1ce30c670356c 60383b9960384e6d7e907a07ffbef807922db42682f22363dc7b07637ac62a85b1f1be2863cd50c7 63f9a174d72207ae3aff3d3503ee742410d6d2cd3eb34395f5f1f9c08e14b36b298721fc4e0b66ec 2759c7257312191190b6cde3457154d961dea88b11e162b17d8ed8c3b6a584a76d4b95e26d0befd5 b7ad8534d9b6fab7f5bf0394ce53c1cf8dbee92ae018f267607a6d71a86badd948c1c17b28c4e86e c89d9e458e457df1ce541fdf2cddcc0ceac74e0d9b1d426780efe57d852317654f27d675e7826fdb 277f87d7a80f763f08c5adbb3bb6b6edbb3645d966986a9d9b9099689b902dc69b10576b684f2f0e 3661bdbe48b1fc15a0bcdf73b7e1bc3fb06b4fb17f9a6c0603b53c4c161ece5af5b8d526d3a7f7eb 3676ec28711a2f400df234388c477175af388331a9df0728617ec634eee406eaee0a8a36f6cce69e 586b09c5db2e9a8750ee52aa6fe2da70bc198b3cbefe4e72ca5acd88fe5ab976cbeb6f56efad152e 3193ff46ea52fddf3b3f78d6fbece0bc5dd21d63f0e8f6549b7ff7a52eb4ea72a7bddba61beeba73 106fedc35e6dbc2fe4099b7d08eba456885223eee3d5596eb96b2cbf0446b9456ecb043f96341a0e 20071d4a90bb914badcf26830f91f5fc81b6d679d199afd619e8b08235da58c1fae2bb8289737505 0fcbc304cdf1afb8e43b5ccbecf65b1dcda8c4bd1f7fda95e9f1b3306cd3a45babed95a25e27e1e9 5cc22f4bebbebbcbd00773afc312d61990ed2d6ba91354f0d5f5e6b370c8cdd4bc719bece1abad17 7eeebc32fbb9c70a31ebd1122b0e0b8b2b43361644ef99be5b5bd40be3c3fc619e4ff303b58de687 4950991fda9ffaafb0f67eaba5cf675e477e94992effed852dfa06cad5bdac8e11fca2c9e8ae4935 d42dd79fdf50f1c0473f952c54bbb0c955c3ea1a1a81f6ca2ab6c7ab8d87ae974e0fdd2d2b06795c dcd60cbfa8bf546d4e754ff6cce39de78c197ee3e95b69c0d3616ed59ac4276b3e898dd66122ad1f e789b4c9deff8b344af57fefe85ffad652f02dd311442dd362aa3052d903b505edaec42ebbe598f5 6ef35ddbf27a95bb9f5745db7e2c2b573b983f29369ab77c253bf3d82b32eb46d7caf42d06cd293f fe747fca18a89fe1445a20b3f1d744d66375d327c73967ca8c56d5ad3282ee9a33449bbe3f2c7ab5 6c02b991e0d2fd156ac966dae220936932ce0a291f866a31ad9fb6bb4275800ec2f777bdca2acbe5 aec491f3e70163664c6f264e079f963a99e42ac6780ee7ecd1699bbb8ea06bee39446bf16be01cde c1a0e23dc33ed9bb817e3d0c733d6a14c25d4f01952e33afb63b6fe3c70fdde1d7bb553b3e33785b c29e5c82aff42ba48ed568b156a55f3984c57a1e2f45df7013bd26fed27edbb939d56cd4a7fc101e 8cbf9a3f1d9d3697d510ad0ae97e33d8358f789fecaec91e351c515d4feed0dd5eb6c676f825c2b7 632b2bb627c58fd2527781d6cadd03b309bdce7603ed5e6f75877fbcea15f00a6b372597a9357245 b8fa3c158bff40f235ffbd00f9eb7e0af283e31ae4dff01e143e5f1d40e2d9033061960092db8f01 d24b3e40e69496fcf57c00a456f900a4b02a26868cd44e10cf001ca7baee73432550c40451baa6c5 f6b17c8bb79d75145d38ae10559f4a256cf84aeb7d5c9c07816f8a8380dd1cfa298641ef3b1bfc8a 444e7c02f24b61030a95ea1e40ade2094051f4536e0abe0615807485094076c96708c327a2eedecf 44d11e6600b260924fd3237d907a6e9e60bc8b6d6b49c55bf420c4e5927e8a2e97d735c2897210de 9fcb7cd82c619577b261b40256e4067ef8c5d2733c7c419d0d7d81190cfd51affe3b403e38265d4a c889a8d8f000a0c3c004f0a01c02a482d400a2b889104ef201e23927805c201720da3c919395d228 d5f85c737af1d6f466711985b0e85a6a5111ae0df8a8b6c0f4f051109d706f3ebcf7ab54cf0741ad 550efa8771cb1f3183bef7e90d469e1cd647de94fc8ebc6cc3fb23d2d0deb405852c9b88aadb2828 bc283ae9d0c319c0fa340608d6a903e49b5980628b3c26f00c502cb55ef1595b65e372849623a2b5 eb443587998487aab40e9bc68978bfd61efba68b90faee385dfbc779bfc3dc80cbdfb2be883c8b9e 5279d6bdac90ebbf16201ebf0a4767ec9a1d354199ff1da050966789b02e0aa091c30178615e0052 3e26e3f0b56dfef8d336ed15288a2706140f5f23aeb0901b118fca373c3830f2f6f2a5e69b16dac3 2078b5170147cf77c9bcdf1dfde82649be48de4c7f0c7fef9e828dbf2f9d9cc3ae29efea6e31cbf4 9f678e993ccbf13c8db3795ca9c6f841b4e1df010a537b9e3cf8fc1640c657007090bd03447e6441 716d74fee341af7db9e8e6dcb4905aabd7373353c344e3360ac1c0b5d390785f2adb3defbbb4a79e 12df375e6ee2932f3dc8247b35d7d65ed06077753792153d31cd2d3c08d3afde1fdfb07bf3f2f7f1 35f86c07574e990e2e71aedcff153faf22b69744d4733599bafd810c9069e7098a4836078a77bf0b 4a0b098dee638b0edb062507e16c65fb12e8254648b99ff1f250b3f45ab5cb4dd7c2136dbbf8a8cd 9fcea68f3d2b6a9f7adce6b8f4a80bc77415b83f47be77eb9e5e994b7c7c219744c486337bbefa4e 0ed5fbe7e585e8a6e8fd0a5038ba0b0035e789d9af1dd2fa1ddb1728ee3b8911d0877b71f541ae7f e2a1ab732210941eef6740467f19f8e5e6da1b237c5e7a5cee714736e54763b669dfdbc3c1f0c61c 078b2b4fd4b797d86a51974969263bea02b39ddce3159d21ef5bb0d17ea5663962ab67edc695be55 79c5e9b666dee867f7578082ec27a20a4ceaf0334fa0d8907d500cd7102869bd7e7864f2d340f864 504fafdbfb17821fc5e7b5b8b61f7ba1e6defceb39ba0a1332cdbfb9c8b549c9d18a8d86939f57fb e795509d9fe1e67067a3f48ab64b155eb37664ec9a245dcd186e0ead18ed55a212f830d93b31f6aa 7bea6d879d5f010a3648d5410085a7d4e1f74a663779094069cec2d1ddf05beffec91e262a323e73 cf97e9f6592f968ff70ed0e5ebe8d9b51d3def3dcfc64b8dce484ec8db25775db2f0f2a269de97d3 8149c6f8ca70272c69b4592d7dc37ef207f9f3a927ed802e648ca206167e5b930b7e579b3cdcae96 39d89d5f91ac4c992580e731953cf8ac098a6ff006255583c3e3795ff1956dbff52a4eb2fd1f3fb4 10cd6fefe16177991ddeb403e555cd3e6313c7ba0eab9ef9e8643ec6ab702d189df1b97a6219b3a3 877773a20beb17aa01e573d4a6f98eae650c3b5017c54241cd3b8b8662ecf0aeb2ee6dbb0a1c4ed3 ecd59ff64f48764f78958cd162b260c24dfbaf44eed2f702bffbb71aec41fd6bf949e689faed7dea f62ef39ab73ca3f91d6957e89e643e62df34689cbd9fb8f122ddd6f468d7f9ea23a38568f2a75ed7 b2dd7a5f5d90ad8562d86342596f1559b66fc853de92879c5c6ebc6ad2e555e84ab892fd41e75724 73a9ba024866f073e45e71354da37a1b615c9bb3f9605c7f655d67bcccdf3dfb8b5cd48bd4385bde 6e6cdd5e55cca40677c6e85e8e9a1e8f3b8ef6ad665fda6c1800755956738ab994ca0a22496d79eb ab13e95ab3b612fe2ca64340aaa1d859dc5f828fd8e49695046657782df51f747e0580f6c33540da 4b1614f97522a7330ca3a631f9fab9361e3dc959105da5e7f8ebc08776f9c70f2dc07d930adcf529 2ced295d3ad4244d9d4596ba74b99b626589403e3f075fb9526ac312b168d7c587d81b8a4d6fb216 5e8cca089dbb77e2830311f1dc382ef2dc64d9e10799719b1f94967fe1c7a5fa7f2ffe8b64bd9f27 a2a204978c51f6923cf87918763e99f065f591d7dd87678f4b9e7947f6850e11d3952f6da317b373 5dae75086d4ebf3875b55274a5188ccff20e6aa5a92412e1c591f8ac8679b1b5f62b427776eff21c efcff941b3b1e7447aa772e35e3148c0c3ec37ff6db1df42b193a2fd2b92398f26724a1cff9370fe 5792eceafd1e4c6dcf7584f3f536bc78e7b32d4881b527d9bc116485a6fec98c269a762860eaeaad d34a69bb94e5eaa06e4b8de2fb21b6f3d65b609a7286e72bfb2217af8e2d6e2c0b535609ec1d3be3 1be91b763637743c46fff40acc09b19a09bc4e8af6af00701e4d06ea531740a92b3ba0bc418360cc 6def4f5219e8974571a8d938dd7b9a9de3287312a169aa40e9d9456da41aaf00554a003bc8d56b4b 14ddb17712fcaa74e14364f7e287dd39e0268d0ec4aadb4e9dcde9a321b382f65bda9ad83c8d6657 cf042047a339b44ea315b59da2f55fa42ed5ffbd03e01e996820f587084a98e1c4f837d9297387c8 7cb439eae020d58c6c35397033fa41e9a32b68b3ac158c4f5fd9bec5a55cdbce48d18d73bce03b8e c6870275e600364cdfad7199553562f3d36c9e5971d92a6d3d913e5d2a75d6c71d7e648f5508be26 d032c76aa3574bb06ba768fd0a0033ea06149791044a67f7121de699d428f42067c35fe59ebcb2af 0d8d37024973f4cfc30eb5e52e4094f3d9ecc8c490984bcd6b0b1782f193e1853b2573f2796e276b 7c295915a830a0ed85f1a54ba2593ceebc6b87baf7b34baa71e81f0f6eeb9efa3a0f94bffa1cdcf4 2848b7d36ba568fe0a003b0e9a4cfb6c1adee73b21cd0bd6bd37b6178ea1d4a65633e81f4fa2b630 35dddb7a6a712fe6e55b854ed541e9602f2742107d515edca914a7ac49895dbcab27666df9177aab 19af232eb280ba7b1c7270ef74f3d041f4e99edd65f7fb7ef568ecfb854694402beed965a995a2f9 0fa48eaebf2f00528c3150bc2112288fc0d9cf8dbb876b66dd1c9fcbcbf3d0e0c407a1e7d691aaa2 f98e2bdfe8d157a286b39ad80d5a233e3ec41b4ecd4bc9ff444f78a6d8c8eb742573718eb548484f f3a19a1e1e1fe82a01ed39645327a3193d2647a717417cd60b8df88ce130018b24089be448caff0e 8050753c994b7d199405ff74ef7a8ba1035ff5decf5195feacab7fe7cb8d8a763951beb9af9b449d 1020f68ead320feab91e37df590b16faa038537cf7d8e30df565eab930ac83373edef7efdefa4dc6 cd718ef856ba65624662035cb7750c5f624d195fcefc9ffa02cbf916c275356ce23ae3367e05488f f00525ba2ff8ca3ade5e54db6cdbb5c9a0758a91675383923d4abe3706ace813982d081325e065fc 0d738bd6b3fde387be4b53fa82a1d89104304551be2f1e7a0741df0f77f8959caca71ea12ab30c7e 3acdca380488fe0e9d9ed15d29379230870fbd5df18e177645e35eff15a9fb216d93e98425bdfa99 7277da2977ce5b68d6343be1b7a9e778b22edfeb8db6c8e22792ff94723aa717c62e6bb6f82cb30d a92a5d53d0c1b1c94fd687a09327f651cde748d911544233b63691ffac9f3f4519728b7857023318 c3a787f6f6ae3f56dbfd70c46df7a392bbdd37d9dc768ff8b52df9717f0728060ae17f46eae232ef 100dab398febfab7cbd7954bb6911a1e22bb3b9612c1e035bb19ed0406c36e179a30eaf1f1701e16 a92ed9681f06c76a625bd67c94ccb5ee14015565012f22fbd30ec38e378c586d83edc3e60adb56f9 da42691259a0dd86cca15d0475d1cec74fcbb4a11daf5d433b4eb5fa2b40e9f8c0ef34fd69d99709 543b4567a9a65af2b0261dc7970a9fa5cb0883d9dbd1f149b8078ad9b64f079ea35e7b00b9d97da6 0dd25f43e6fb619f80c7fe122ff50c6cb7136c0623154dd93edfb28d7a4076d1def4f2ddf0a76c75 0de0e9642dd9fe712d29ea6d1d7bddef3a76a8d25a9aafcabf225d3a17cb70e09cb841cdecec4f55 ed345cd5e47d3d5fe393e959623049870eefefacb4976bc89a5c14719e58cf5d07b71f8d375ede1c a11deec80decd1bb8cb1a6705f6f3b8a4da05ceece6f22323c6d46affcfdc773db6dc4eb2cbf2dad f455385e2d11eab85a7c07d7d5c2e1c2d582b9e5578b9d57fc15378e57ead6bee75474e5865695dd b55a13a29e5061935d0ba6984d3df7d369b655d85dc9c170576f1668ac856dcd2dfdbe78dbae51cb a2ef3d5e41071fb9b78995cb6c33c9ddd1b54a026a3d6f96a4d5896e982b38b77297d6c9c826eb22 dc58382f66b970bc8dbad8ed5eeec261fb69c1f3c5aebf847ec5b9d4f2ca2771b6aba8a54ebb26b2 aa56614dfd83509ed8ca91f9e306600fb49ddd325b7a83f2fa87df48a06fadb5f2fab9ce7342bc5a 43e7d43bb882dd4275691f9bede576ba9e2ccbd9e37a71b95ef68b5afdcecdf772fe346f6ee7cfd9 ab6c64661d1eae2738ada6c17a234d83d1e7390d8a7df02b0cf645a64320598d26c9c83c84356ed9 2d178fad473747cee5658ced5535d800f6d9de64211a5f9919935f9efb1f7371450bb7f903cfbee6 cda81ccd5ee6323febb688d234f0b8fa9453afbde9601e4d2671adb9998cc5699af23bfe8e59713c 73eef638873782d1a92fc3a3d3b23718ada013365ac133f55768a7c6a42a371e61959f7e46a5e3b3 3fce91aabb49449cef5eebcf3be3ae10cfeaff042a175abbf9e13b6367ddfb589e0e3e4b7332c98d 9db1ba9a3dc6397be68d56e5f17b6811633044ebabecc0a156d0a0e2a3e5fe8d659a7db2ef0c7b4f f1bdec519316d96b6768a9eb69af73d7d3474197592a692ac94ffb272855aef4d7e4a1890b9627bf 601961fbeae4b95606da6db1d3b4d7cca34bcde90094c6e3af0eaf46a764400cd1ea8518ec9a06d5 bfd106dbafbf8d34f2be470d65b9ebc982d6ed6559a3c32f8f765b42b797d6f7b27db4e635dc6f9e 0e47d0843c1d6aa0bd47ad518c907edd1147f3fa6e7cdcfd0a21aa1ecb0c4689e9afd94b2b2cda55 d59dbb9663dc995f3f677312a9fb609c33e4ef10ad70c8a0e21e6abd27b76c759949abdbe117c541 3b36a171eb7b8e67ad79c55b34578dd3b2611da575a3183068bd12a3588d9ca064f5a9a169306cc5 33e75ca5574495f2b0ba324ad27e77297e5f945b54e953f42b406ea3b440f66b0e40ee395a80fcad 7a0405cc3340c1a37d004dba3080a4572f593eb79b1fbf00f059007d7a06805ed423c12d0290574a 3ef056b504523781378d2d36b74e30dec7689fe02247348da8f20dee61231ff86f6afd018177fe66 03868eb341afe264fdf74dfd0ba9a3ebef8bff02e40e661be45a8f21c8438b052854260c28389005 a0961e24522e9144bac200c00d164d00580037274602d54d100300171b506c39f35a8ceec8d4608f 4b55699ce0bd8e9c074444bbc3800d6fdee114367afaeddd1ee95ec02857e087152fe7f33727e70f 4935e70198fc1d20275e3b89b09f11c82ff73fce94024d718994833380c42004507c280278d51f25 ffdcc0002c348404940de0fded15a32f0fc4250686a24bb5518d76f74927aaee77a3f0dee49721f9 72766183fed26fb7373b05fe08bbf9e10c4b4b1a796085c59edc50f3dec4650a5e66bbcabfb4f2f0 77809cf948447d2289a8c165030a1f470490c9dd000cd592fe22ae1500dfb66300bf431c20f9551a e01f6fd7941d5d14e619d5726a1cee8772366c807be9edcaa0f93ece4a837727379c077eb2f507ec 4aa2821e88557f94832edeb4517fbd169d7af4ca07f382bbaec379177e80fcd33eb8ff0f529fcdff de01b947d005f9417f9c0c4f1803d00152005c3bdf01ccadbf00c921f51f4f7f7caee6f1e8da2cf1 e1a3db30deaf46e9faee1cdb41109487dfa04f2c603faaef6bbe60b1e910f03e2579e4c997fbca9b 1279c2cbd6e7e2abd0a66c1759a8cfe7d674e2c7e57bc93f6a73a170bf87dbfcbd39eae47e05c845 5122a7be9d24bd3927005c98e93faf22f4820b90b29205083d6dc667cc9e4635206fde2f5d3e0681 a5c97e7431cefeb8603c3d656c845e56bde65efa3c2abd0a425077cd31d277d7dffecc45d4fdee89 e535e64120a1793f54c0ed463733ef346aa87fbd7c2e625dc9381f6afb7594f6f47724d328db0385 323b01d05e3a00183f9c00d219790091837c7c3e6f9be1139e0ede5daebff0e34907f7d44233f5d9 bcf46f597f41f3ced5dd081dff797e35e367a53b283c08b25bbe3fec6ef37ed86e86b7d7955e5f03 2b59ae23ffa5389f283a3bd9efc73d2f1708b04de39bb137a3ebc7463efac73a2b1cf82f52c3e3ef 8be4c197fba070b4a789e5ffa60192b95a00b18fc14fb9e6a8ce172aef5e36dbf127adc7e87532ce 1b179d2987e76e284a0fb27330ef547d73bb3199ae771d98c9cc12bff9acf39d97d2431c9c99506a 38b9d164745e2ae4e60c756f8c5d5c170d0b3b8f9fe6adb289cd3a39fb1acf46f76b1cdccac7681d 73e04f0079ae93c809c03ce9cd0a0b10f4638362320cd379b343caf93743e9b03f3910d5d7eabeec 3c2fb3e5fcd1902bd88d79dc8e17502505473d0f75279feb5dceab61dbb5ad6323b28bd766ce7288 45c5aa58ebb67943e59959bff88441d587eac9a3a8eba9eb5be9cb629def9f3efa20e23f5a2c92e0 5724337edaffcb8b06fbb3ffe4f01779f71d3e03e3e383c520f35a5773f0b39675aaf74e5bed5f80 d79b398b0648d315ceebece568a3b12659bb0b7e32ef85edc56c8cd72f8352b7b1d1eed1d089d973 35fdfd32fb3acf20a81687b8a04d46b6ad7ebfa5409dcf1b1f359f87819aaf563fcae99e07ff40ea 524d5b5098ee0700baa31b80b40921e9d0fe1994f299f7bb27757c2fff32df4f5cde805bd0ae162e d3b35b3d9bb0dcb7cbcbf5d2aab53bb8d9ac9559a3fd099413db7d987a485e6ffad03efb1a987a69 9a8f36d1bf255505505b4db4b3856264754a599d3aa66cc3f84b466dfb2b978ec60f3ed2a523803f 2119a06cb2fd54f96d624e8b62d29bf825bac3b2eb032748d37cdceda97bbf774ceb75c99ee7f179 335c23d695ebb4cd43bb30353ac6193df51585d247e486d7647ba96bd9eccc5117f5ee5331a841a4 ac4b2824db8bc4782c99a7b174418bb88497145dbce39fa7b8aff7d34e13f783f5576cc4a34f0af0 278082ab25a2ee2cecafece8e26def84ee747af516fba5f578a0d2e9a20879e7bce13381752b2379 b3b579d54eefbc39d4451a5bfe14075e7571559f1619b580c792b236df497f7d9f37b9dc7b0612be 7733e2fd1c96c4fdaed717dc3b850ac74345113a2dfacefb5e10f34106f9f2acfaf9a4007fc28fca 01db89a8ce1b0745e891e69c0bce9b5d7cac17727ccbb7e091171c682a18d66d22b866eb217f4e7c 9528e9e3f3b8a3cdf8fa4c854a4f54d92c9c838cb5284ebaee484daa193b47dc7f50577875d1343d 4ee8907b98675da3cdf7e9d28a8bba279113c2fa951b0987901b19e76f8acfaf00d0f09ec8f9fd26 720e8234e15cb003c154d4e795dbd1976f174f338b6d6739d54c6a31ba9ddede22d227cd16a4e56e b9860a49cfa1ecf4a595742bae70a93e9f30e261df96051acb1b7c70cadcf83ec8bf39b183e4b851 b06eb289d63b67a760c8b159d971d8acd27d33fa6c999a518c8e61e0570088f407c95c2a12a0487c a4f8d23d9afe546d718fc3465b3a4b801016896625a3378aceba74cffada9ccee555b8feacca8ec6 f7a59bb84bab8c88cf5d7b2b78ebfc41e81e7d9e1f10b6c68996e1b0df8ce1b1b3fe35c3e852b6c6 2c27eb296d6a97036d72a8456f969e4f6f56f54f8a98de905df02700c8c80cffaa32d2227fbc68d1 9deca8dee2c663b70119f5cf455f47cdf65ce17430534c6dee9aae0a73e657beb4f9b274af915d91 521b138121c335cf6f6d828b398166bf142eb3b3cbd6624e85ed9381366c7a4219bd51f5d2d1c97f 8647ccc4896365923f1d2ba8e41d311bfaa400bf22d9971a638048031294723529749f2fee4e77a3 ce65765d36ad5bb45c9ec2f5ecf8531ffa449cd475877e28a5988ea53b8b22e2b1d56e0a6c3e33e4 79e0ae92412aec58d5da1e935b0b8981a28e41a3adcefde8ec46f1b1626c108a44f41e55779a58b2 cdb9fae189cf9f54fdf2fca448e74ddac67f02807bc30940e2cd1e94ba35c1ff200e7a1565ac7eb6 a85ccda4bed054973168af1a765d51ca83c155ae4d9661a2164d208165f2355e20c30137cdb2691e 013b8fd72863b4a769207d53385e8a59fd589d67af142920efc3f3d5820eed2ed1d97bef0fba67f6 acb2670ea5fbde4ba6f2de8b4e3f88ff040033e81414ebc4fe27ae1343984793bed62fb965b16257 7b52e9149ea481b620ac9db2bd78a25c93a0b3f8c2ebbec015f3595ecc812a37958d2ebb20f65366 8d4e37f47651238ff8e8c352fbee433db8e4f57c68db2f7fcf56a054eb24c3d5a0490e6d65490ecf 5b991c2ae18d1caaf388e4bf649c22fa1392e5693749e6128187c7526b7beb8359f98c16ada2497d 7a903edd365aca79b2dac8b5484aa354457a67990227c42e3feede3edc6c782fb185ebbec920d662 4497b5e6ea5813b338d5a41fc74387d4a53d6beb1629e8e293f840d72f319de66a44367b98e3daa9 231099f7fd4a64c25e986012a54853b07eda7f00202d29e9d52b87bbdb45a779c9d53cc4ba853bf8 24107e5e35fb4659aed73273917686473e1ead34ee8bf1372e47aa116375588876eadbfaf1561a0d a88399fd396fedf589b6fbe09d3cf2c81505e2733d9c881944dff1647202bca0c5e5dd069a4d761b b8c6edd6407376eb4f29d899cd5ef82b0022de66a0d451b0fbf14dff9408df7c25d8f0b15241d333 c7ac7c759a7991fe08235e5ab90437efc212bb5a4d1c063dee02ba5a1f658e8d76bf4cb56be5cea1 8744e3fd30775d93632012c43c4bb3f869705077561b737645567c6358f4fe396cab422d47db5b07 a1b1cad6b0b10a56f5b1caac1da678ff09c918cdcdfd897eef5e14364692c73487f40f74cf2b67b1 9f11195888f9c90a69b34676bd614a9290a62ed2d58b6b1c1b71fe411d278dcfa15f28c07b619c6d 90f22c1810daf03cc78d0eb7c5614e3cee4ad441c276be78de920fd143ddd6bb8052fea68f5241ff 8852d78b8952a7c60ba5f85ef05fa42ed5ffbd038a7a77f16876e9f2b9c8ec0a463f972f68858b90 979ef342869f2c3b2163e39b125d6b3da6d4ab56db1f82f156d9473bf9b21fdd2f3e397d5db384de 7d947173786eefce533dad2fb02b73e20ac36385dcee7d9a455f7de58476c497bbe132f5dc3a5ad0 ed75b45cef377d32a3af2378f8588760eefd8ae0fda9f62f0aeec1a68bb40bfad4baa7ce14a57258 e785107e6458d8fb84d4ab4b44870152e990df1cb12173c3074b2ccf3503b7baeb075ee48ff10e03 620123b37475fb5c72dd6dcb12a7285352d0cd1b37a9cde0f152d652bb705d7d939d74a5f65fe921 75ab592ce32bb531d7570a906eabd991777fc5638f7165db99a38593c081825aecb305d10b934d7a 515f7f8efb0f1d90dfd2ed895bd4b0b8732ece18bbc5c5b4d00cd6489eea96f2756bdbd6b32ecad6 9b31da7b2ee18dd0ded4927972e8ada7056eb6ce98e7dd6a510cd8a581978de5ba2fbc17b6542827 08d1857d2485256cbfed853dfdde1776e7fdff2075dbfd7de1e8a254f86b062ddb9744e170a6057e 5a36b3341ebea2bd68e75e78f13bbf6fdbaf86bf89726e7d3312568bb5d2bc51ebac5a5456fa0ebb ac963dd35f41f9cf77693ebac87273d8a49db62c8e95d10253cde5a282170ef35b6da1cceba2719f 3d279ddcecb9cd0f678740a266cf4a459b1d4ee3ebafb01acd465e9f3e7d48be892c2c0cc127cf6c fd66aad91c06abae8fa3add51df5b786b3ce52ec63051d8dc6b278cacf16bb214ace6fb62acc1b9d e76946716f67d65ed55f093030f51b2768ca80b836ed2995ee24c4b0f944e85ec9c9302c4b636008 f7b17ce8a4232d69c3e158a62c7a3cf1636d3c098ae69fa07fea7e4171163622f6ee1d8845a84de6 f0ce616fdc3a900fd49fcae795e623e662c726e37a5f37bab3e336b39cb25c350d229f44d0841d7f da73793c6557a77176c15f467ae1f41c150eb7f7d09c241a01a295e0c1199fd707585f1d0ecad177 d3bf5a1ba64f5081d5278efba85f9f152a09dc75bfbe2be1ff40f2353fadbad16c58f4e70b982b2c 951c45d35c48ac0ad273db3e6ae7d562fed0e7f5ca469b72c5283719d7a6edf16c739c8e968c8c0e 3786460eb097460f2a8a2bf6c99cabf69e6468f65a6ee874995926b56f3aef53c6eb0c61386e4bdb 66bef5bd4e2b2d95107aaddcd35db672ee846a9e8eaada5c752bcf04e3e04f90dce211e1b39da070 3cac1f3171eaab89885bc15e2d7ae734887c765855e4b174cd6923e8bcfd0e1c922ef7c90ed6e851 03acd76526cb51875f0ca6edd81c2cdb936277d352772dac79da4378137ae5f60db49ba3eb0e0f09 b59bf4556a8d6cd1aab6a1cabdc2a0e3b0d22bf1e91028bf2f5ebdcc13f56182f1f44fe065f953a0 6be107906ae9fdda3e819d74645557678721248dd4629b1994bdb7d9a3fad0a5dbfd7cdd767cfac6 2d75eb669a27c24e558e8645c970a3e80ba57a25da966be4785dab52f34ebdc2ac9166f97d2e744a f135ea17bfcfc7a8386f070b64d57fa1303a7e91d06e1eb085067295f2141699796a5738ff09a902 95adbc8b209bcf5541d63974408eea2c41be0e689027a913c81b900bf277260b0a85621d146af418 1406592cc1940685a5a68002993f2718ba09a828819d8dcd2128c6eb4fb51923ca240d868dce336a 11954fb77d48c05fe17d287fb52011d40e3af1f7ec0792f71f7013e78f00d9eeb704b2f37a0d64bf 741be4e2ed1ae4f9069bd83e96090ad5c14ff9f642cfc983023e6b81027b9d8182dec1131c5950b8 863a28dc1a97d824276ebc1952718c002d179de57b31c2a6a01e5572d55e783dcde7611d56f7ef56 d9e2836edd527c4e7adbfe60e2395e9c71d200184fd454c71b2f98f39f9048099593de9cd5416e63 76407ea96d4121870aa0b0cb255dc51001285c201814fc431b40e5c222c19a88cdc0e0e3e2e0a245 e718a45f1355a69947782bb7df21719d7cc33a8143ef675dae26faf3bdf36e1df393c0eb90a4ff1e 50ac276576f2eb8b4bf64b4d23bb660feaf2ca1dd08b7b6a4d9c3f016477c5f46d14c815b144d4b8 3048a4cc13a0a05832287cc75700b5ae118026e32280d07337b6a6cd65e42cfb7854796ed9906cef 95f79365cd37d5376eef56e47a4122050898492d1ff432d3b2ffd6f62d7f5890535dc08b2d187fa9 6c89714f62557257e3b9fdb4d4e1e589ce1b978773024e8acb9f00b24ca50c7207b101f2dc7a020a f4308de9ce690012c50780ac4262143e0fa51fe77de9dc0d6fd175fa768bd1f6ddc60bc780799445 3f6c35759ff7a68e077a8ba72785fbb73711e4ef4bfd98f06bae44f5577ede1cb8465ed83ed1b340 3d766f43badf8777ebe67684f3d51f8cec2b1337d34ae4d79e84587f02c87add44d4e8da04858ebd f829e200a0c7cc0070ede50278b9cc4497d2180e1be6ac11f8cc72e087c661e581334d78d38ac8bc 3452975f79d74ccff44a74c1d7d58583c07dda3c123f4ba09e7b5c8ee3f2a31a60edfb9d7badaefe e54c5cc2c7957580f7d09c69ef6a9d358133cf8b11619ef39fa5f127246393a882fc22fa89872e9c 33abc4acf658002f380bc0d79a1797e2fee7eddee35cd09f79254f769fad979efd4edcb5f5de3ccf a52ff9b8d662f651dbc4f27def7c8ddbab96bb5c03e4fdbcf6b1f87d11ee70f632dacfab8efc62d2 55e0ace7bcedb9b02ed2368215156b7bab985699cc99e6b5fe364dfc7933fe049033b944d4a099cc 96d1100570bfc203d8bfdaf139dbf7c2a6fa7afb1146c45ed646d315dadd0c56d567a584f6ee0f73 38bdd1bb067a1dc080b88ccb3eed284b5770b296af9ff56dde3917f4bc6b9bcb766c274616649d51 ad6595aff0d2acc5c383719890f2e9a531d6895eeccc53b73037f5c0eca5cfe6a7fd0740bea05741 618bf7007467d200e4b10090496447b5c7e3160c9ab3c74bb7deafa7537882fbb36743573eb72e3b df6da7ede488cee4bcb49a6b7ba321b8e51490d42f6061624d34afdfe6c9acf79a17e3112e3ca335 da7f4fafcfa372eace9b639d33f6b81623b6a08db1d054bf95bba12a37e3f42b401e7f2472027b98 98fd16fe57922c26fcb87bdfb4bab1bcd9f5693f77858973f38cd2eb32195c22279fb9166ccbd46b 96a38a3df3261ca666fd456c0caa4b90278fc49853d7de48fa7b4b9bfae0aa3c3409d2436d6ce760 f5bbdda4f1d0eaecfad82aa761935396606fcad674f11fa0d9bef12780fc25aa0268e88f00acbe89 1f1f6a74f5cfba1f93b1e2a293b6746fcf0ced92198cce67981da51b817519f73f2619561083fa14 9b2726f80ef4b01e2ff421fac634497beed56f74e354558c3535e77d1d65d52f7a0af498e764eb70 6ac845bfbe92760b9a96aa8540176f6664fe05f210183f480df6bf2f7e000ae54222aa519a00d8fb 123f9188e1215a8a5e2e181e1fa4b1df5f267ec49d612f30ac1a72bd19eeedfa3eb1472baf0b8454 d1e4c9aead655874acce1ff3a562b456f84f04f16e7294516322caa5116a4a3b857a48d5d9f32b92 46a92236d6ea5ca09cdc4168ef964a02d1e2fd1e67fe0a509896ab004e346f80b42b6462a85e859f 7a369ff8e8a2a2865e43eebc3eaf37d8c1aa8d96b271bc6dcea7be4c78da6730fe68d9760f510bb5 525d596fe0be6cab99995c7a0354c21b3e29deb73e2736ae4017dc55e92ab4ed61c8fb5badc4f76e 709a4ac285244f7242e32b73c2686973c3cfc0fc15a0207712517bf34454aa9b88babc727ea2a5ec 9e5506195db299eed4ba56733b83aee4783d9221435396505a8c5ed5d162a4988b3c249f477e55ba 56e496545b0923712f1f17821bec77426770a479963aca7cbfa49d3901777d163c6b102b53428f9d 76da389b092489cde6203b85c5687a6cfc17a94bf5ef0b5070a7350033d40220d680882bd681f2d4 a73bb9f972bd7c5e1bb781796858e869503fd19aa2399abadc388eb2d9dd02195b1b1989980925f1 30dc3684d776da173a7a7fc673503fcdf4e2a24967cf8dd88ec0ca8fbec1665bb8cb68de23c714fa cd366d44778c36e2b990c0b1e8b56599298c3f01400dbc0e60874fe48cc778d4c814b7777758477e f2084ca66097cf64c7e876d0a5f66da3077569ee2465e3f0b65cd953ae54c75020b6565348e84e6b 159ee39036173d0b63f6e3bc96ec2cffc0d9ac10b1cce25bd069b357bbd3889058bce7d1ab71dc7e 8fe9af39629992702c6ba8712c5f283385f1270068b54fbaf4cbcf92313ad8fa9381d1bb86da29ef 14e6d58cf9b8351a7a3ca8ceb41cdb2614b43814e4ca7d6248f5d7fcf15351f9d60b85ae5dcbf103 3d5be6c6e2a3c92ade79c0e6aafa9c59ae158c3615e548177367f5880d5f57eaaa94638a987155aa 9eefaf13dc59aa5e181a09e6660ae31ff871a9262d8078a1fe1f2fda72b07990a762fe32ddcc3336 065bb1d145b592a68eef23c5dabb5bb99a0b19a9d12c6982f7862ffcdbcdf85c7c71bfecd7346176 96181fcc32243ab4d5dc4ce8228dfdd56937ec40dd6042a2ea5bc5391cf4c7fbd05a8dcb7b0ffe2c f61ec2327baf98d713548d14a73f01406fbb019083330ba93c3abab72d2f77362b716cd5cb64ea1d d4637c01a9abf9be27ef14612d35c8cb51f03b77990f07fe990313ffc565fa3660f32db1c0c055ac 42a3eb65fbb89b7547d48daf2fa93ae89207aa3310f61eb1b4f6dda7e8937cfb8b907ce73c2307ef 51baad917cd7d549be9731529cfe040077c31640ccdbd45327e5ca053c661f1b6b589141c7ed4053 f7e14729ad9b2da9719a2e04ffb8dff3c252103919d54c56c3b59f63dd8c0d1bd1f67c933d5e4683 d2b14ab79a5483ccf50f940b2df67e39b723c342cc9043b472224066e31293855320321036263230 b227262b4d2526c3cb2985fe27fcbcc867e0362816ded3c7ed06e59c05934b3ab443857aacfa9e0a cf8e6f69df79d404dfce4ef8d1b2b6e3a6e88865172cae336b11bfd15b66e11f71619ca1f674053e b837a8b6f72dafbbef97de33725470d22140c8e31b4564d4af86cfa3fa1dcf8b52b2f44dbabd041f 726774d6520241c7f347e3f42724fb52a31d112adabff9935326314b9dc860a0e15b9befe5978cc3 48da69420061457ea4e903566f5d378cb9c81ee9f3a1a91caf4cd3a61e42cda59aaf727ce85cbff9 7dffe99549b1f46c139f853322b21b6b8d2f546bbf33f36f79878c8b576cab621facbc80da58b97f de615b6e91865a615be2a8a7d0fe04808c27edd73214e1cb74dc02567dca473ac894de8a8de12ff1 a8e957eec3bcb3ec723c6833c52ab1385e3f22493d2b57816a919171e81e839ff3d6b8207a9362ec 65886ffb8610b99a5ac79707b5bf33d31caef35dc5b10a62f15b02fbdadb7a158bd1c7036aa00ff3 8ca20fa9c325d86be883daaa7f02401e6aaadc3efe0f5df7b9acaad2350af85a08e69c130650949c 0541401423a008e8fd1f98fbfdf6ae9a35cf9f8765ad594557937a8c6e06b40241a76518a68f8321 1eede1ededb5ab63635f5127abb3941faf42a16ab935eec17fc7ac88557126f43622339aea068da0 fa89329c834fc1e5434c16720a4ce02badba715dadbba9c11ab26e16b5f54fe6766609abc08aad95 b8dc84ab7e09aaae7adfcb72d57b768514de58f5cea4fe17616fba6d5cc639f4f34f6f6acfda5b5f eb76a8f62a455f42ed92cbd3a89555ea63c55733c78c1ca24b234f6b412d72214da677bc2d590466 1671dcb357a292135e1b72257fd737572be29e4dd7f16e511caca48d812edfd707bb1cb74a0696f8 828fedc44619db11118aed164b1edb0d76d93b1ed8ae2e697ff120666df89fdeec3788d8b00f40a4 b118132a93bbef0b041f9f996d2d31a9bd4b44e4d2be558975501e6d2818596dea374b58b3e360b7 6e7d4b59b4860b44ef8ef76ae87b253338b88c7ca2bc9cf4b836a62b0682cd4631899a7a5b45f390 7041f335b480e673ad399abb4b349a3bdcb68bc3cafe939fde3c3cba5afc3385b7efc4bb8be0c55b a5bb788bce7bf3609f1a77a272b3ed7ed3a82c8f78d0f800786fb76cae94ee6db68cd303bd440a7d 11fb7ab48e19bce160f36978c760a8f44631ac09a285265e5de0bcd85f94bb8f6c867d4e586d615e c37467762b8e3fb3ebb73e9cd7daf66676759fc2ec6a78e25f1c4f4a9858230d4ff4d3044e7e2620 647812bdf856b574a7a7f7e67143f10363191b42767962f038baa35840c18be3dc6f2fcaf7fa747e 56c9cd9c5c1d85d92d78ef668c54b067ad05729db548eb3df5eb6f68cac7fddab4fb30c6c88bcf13 883c550d64589d8693c82eb691c17b872203fb49210315c81e043fdb5fd8222526c621d7f9681cf4 fe2a003a8d857a8b0ae88f449c89ea88b756caeab2430b425e9eb5f0ab86bc2e5480c856a9880c69 b6375193d37c3239e588f177d3cd7633367aa4369e43f6610c59cfebc8629ae1086b2dc11136f6ea a3426934193a814b0d7169b21fe266251a96d1a8372c3717cb61b9b0a5ff62bf3f63df1de5560025 e94a3f33ec95da2564d48e712536967df8af95531fc0f909927b9863e3a12763780d9547d86bd81d aef393f4c9e450e880eca8c4a00edcf93eb3f0957eab51dcf7bcc7e0d4eb22cb47f7553e7eba0319 cc66a33ad170d1eba8f927de197b84da19fbd547fba307e5f6c7588dfe42af4e06e0f6dd7c81e2f1 0c46acdcf83ec8d2ec7d5c858797fe4f13f93dc28d67b31b333c015096811aa4817cd2bfb35aa1e7 1b56bdd7ab3d7a5d791b4f3ad1279e772604b46aeb37886843e32ad742f5aad474aa43bd595236d9 c7d255af7e5dbfbe75ba37abd59bd01da93dec0155e396fb5d8d5bd52e3f64f505fefdf1c3cf3489 646b8384eb5edb3e79080a27bc9f070db4b0fea6a3f2418d1999d696ead3c3dca627168d63770405 cf0e52083fed79e509b7b0c6add874b85bb5e14afb7aa31aedb22c479d9eec7a356ece0caa81cd23 d57e51422ba32a83979126cf94e65d492e5af2d92ae613e096427c0aceee582a387ab3f717f29e62 be7c4bdf3c29941d9df1c0ec666301143b95e47f5a992f2c891e5792b1ce789f63da503a926a3aec 62d770c5b553a7c703b7f6d007b76a60d5bd4a780483727cbe3f4bdfbb1996e66d3d2e627df653c0 c73898bb5a3518e6d640111229387b950454b8421fd0a4da0c40063891e2880032cc1b7f117fb930 4a8042ef9b008a924bc0feb09d806eb448a032cf2550173052b04b8afb4ea05e339f4033e5273504 6dbe93041267ab14994e20e32527d0bea227d079e9c416c95f63acbe0fe2fcdd4b2287cbe5221c59 36dfc4809f84f444c25edc5c229ec16dcf3dc5ed8e7bf697dbdf644b7bfffd117fb56f9c00340126 20742da4ad147a09840cd2fd6b3731818e232bc5b827d0299fa410a504f2836e027d87b314611d5b ee898d31e225c78541518f9ca87b8870759a959b8aca2bde7f9f4b76fc26dc28f7ae91ad7ad864d7 93574724b1677f4b12c1b6b8e583912ff17ea2f3bcbf9badb9bf88bf49354980d0801370f32d26d0 ea354aa0f8a726640237cb4a028fb843022fa120050753dc6a02b3ed418c059359749c6ff0a86cb3 f4fbcc98e29b6cddb577cd8fccf026164f21331f3fc2164cbe5ffc52065fdda25f7d4a446b148c5b f585bfd317595ec007ab8ce0ed4352f016ca82f772a301ffb0933af78bb4377120017b0e9440a559 da88fc609ac0691c9ec0575c4be0f0714a72f0e49562c171b1e856a34a2fe8be49e59b656ec37bb1 8a85ad4d9b78f9d519f7e2435679be46829e06cbe6e139d8dd2f41b4293e03b53afefa1f9a2dfb53 ef3ef072f265f658358bc4fdc4b7847b15ccf1b7cb3ee26f8ddc81fb45f6e27fb64d3b544c9b7a8a b355c86633814d6991e47a7d2ec96d1c23c919bd6b8a1aa5fba3c177fd4a94c25622b65ebd8a367c ca94350ba287bb0a264840f95f10127cddac6efd193adc7be66395cd787828273fbc7ce71e3f5632 94bf6fb461e7469d57c8253d2aab73389f31e7e1adcdb83153a2dd8917507f918033074ca076094e 6034d74d72d3f72ac9f9a298e44bd04f44906f6f6eef6bb1ea876dea1b3dc34e02055a1ea8f846a7 dcf121a939f2b01cb27838ab39fe28b9187577099abf576fa27abb3296796b7c82f3959b43cf8bb8 2c7dcfca06abbb9ac20d4fc6d1c04e70c5fdf9ba024629d4b1d0a029c77960a4837323e217696f06 69530f935c92031783b495353cc92f5d29c91f86d6fbaac4c757601a97203ee39e3f9f60b157a8ad 738ff298afdd6b33267b7bf5d6a4f9e1b5c333b34b5f5e6267255e136e8290acbb03b68a0b2cecfd 699f7b9f4f305e7b1dedceaee41c17effee18cc38b43ed5c236ca60e9076f3f6242d8f39137ff173 b36943e98532908b496eb71fff5306b990cb2bd1b954325ecfa96806c8b9607bd8e77db99fb15b70 f5e45d7c199405c84dee9ba23b9d08cdd35e647b27f82d4c8ecbb18c1e0b9f03e1ac6727cea94051 96e83a90cb9c75a817170f9bd96c3f96f7aeb4acee723d33259ddfec23684deeb7d694d88fb1ee9f 2490da48dbf98caa49bef94692fc5b49db4936e4d0534439d8b15eb696c32b16d7db1bb3ab9be7a8 7d39ba53fbf4382d7c3f3a2ec30076d6c9b574b84c1fcd43dd0cfaf67d1823764b8b31cb9f9648ab 6bb645f385ae0c73e0c8e77d8447d17e7c41ab863e3c4d0c50fbac75739acb4e011d05224acfedef e45f24503404d3d3b35e4ff2dbda2c29cc1d223a6f1fdc332a2794b7bcb5885bcbd119f7e3f6a413 3a1e9ac75275723e5cbc9e67df5fbdd86e0f912cab6e091a52315fcf51cb1c0e90e13e8af0d97ed2 c656c627101963d6b7b606f88e1d1d9db4033df7754bbbd5a234d895729b95b661655aabb629eaff 4b7ad451f0672ac25bb7d20eeda74d7503e2ddda609b0054a78b7bfd8522eeb7e86247a7e0b04e55 7654fb815f0f9658772ea6d2b69efb5874bffbc9ed98378cd6b966401bbbad5b556face7b1db62e7 14126257da14b232069a0b8df6dac6d2eeeab508802ae56bfdeda317625bf6dda0b7ed6d9edab6ed ea9f24b0b401d2032fb5d3dec4e749b114ae5f72e336f34a90dcbd0c45b17dca5bf3e94ff90fb9bf b13be591688611a2ef931271da03f0fc61c04524d2b1d918dc39d6b8b8c3478ba6e6f2e39e567d4d 11f59a86296ae3c1b3e981bae9dbf60b3e2b814c7f147154eec8e10e5fc8e1316465657da5b3ec60 baa5e451c5227f919ea3e427c9d376e767d95cbc761dec3929b7daf74647045de804549d5ae739b4 3babe7d21ca567eb1e39255b03a6dfa68ef9f07587bf92a7766ebcb3b5835a0df761953ebfaa6a73 11b5b79cfa1a2ac1f7832ae2be45cb6134506445dd9ea4f8fb7e4bda5e6a4a085598494003a75268 3a83fa8b24d7d1d32ebd1e7f861c85001fbfefcbf2f04e719bf8323c39af234ef1799b8bc88eb9ed f3f33d709248dd5676d2eeb8dbebdad9dc9fb45abcf7d4e693796df981006c3b77b9a84825b62ebf 09a1278f6c612a25cbd346424e2fe927735bab1fc4b9cd3c4598edd605bbbd4704bbd32405ecd9a4 8525d0ff9324c75dc0a400ddb2b7b88951a0cfd5c255787f5fee7c84784e8deb82d6a0d46deea7e3 6ef6f4d4edeb00dfad77534e23cda5aa32cef2b0f581c54579e5c68132987463792bb6612979b72b d28e197624c09f21e2a23a5b0a36cdf2c2b2e95bfc5168f9fcba77aff0ebfe1ae1cbeff32625a1f9 4a0ece4e819fed2f929cf38292c22c69c7c72f36b85f9cd9eb8c5489c7f108bcaf36df3fc7fb4ff5 553572d368b85b3f3e98563773b4da722af2b67baeec15b95e7765b5043ea4cf2cca5e5790a68aff 15f749581473dda021d82134148acd3ccaaff91ecb9d5f1b9d23e5d783ab8fd8227bfbe44629fc3a e54867507f91e40bc52c8506b743bfbbc8e6a46f6d120a5cb8d9b939646e7fb60663f265984db5b4 3ba16a57ab3fcdb9dacef9e4b6577a09cab016e9f2a4e51f243dfbb2ba89e9be98dbc991b002b51c 7f428d1abf8e9d1e77411e73ae2e8524cbc48d2cc4655b9a7c65fc69036604f0326404688ca7f054 06f917497e544f9bea165ac1f43d012e435f497bb30b5c6dcf9d1ef79f59f1aeafba939c46e1b396 da1efc7c6f4d094bf25a8e6b2a2b7dc707559a09a625a2b4e00a074ff68552858df80dc541dcc5a1 2b5c0320da2cbb1011c63f581ba6fb0d14fab59fb8b4bc08007a98677bf4b0905bd232b6a633a81f b294eabf3f7e48f2cab49014bee5e6a312a3af3300110f871c64df9c7bb78e86891edcdd462e812a fb6c379470351ccb5a6d8849c678454b902e2862de62f7c22aa14e3c01a277ee3a9c843fe96eaef7 65d9e7a8c804f5518be9b1f3312dbb044ec53543a4d47be9484d58fd437e3bb36e8a8f51137e4c67 507f91e443b218af8d72ebf628f75e27bbd0cf92f787d6443a9be3e4e3e82b7c75501f133ddef6de 4145d636715f9a8ff30b119b5509c1b94e04def5263a5fab0c1daed9685c590eaf3e99e05cfd30fd 5c21472b93429d8a8d5a7a386064491a63822721ddcd92f784359fbd096b516da7982809c9452a83 cc207e91147adbd24bbadf7397701b04c775b77cb75ec4fabc872a7747bb0e20632b12e84bd64e5c 4182474a5b2cacafd39f8aca5480f335edcb720c98dfb21e5a4d6f3c93f8c4f48daf4f2bef30a292 71025388f0ae92c61be891f0b889129880301b27d4f61b3c7d0a6c70156a6e70964d6307f6456610 1be701923f6429d57f7f2405e156f58a17f57d06aa9c77b8e7bc9ba96ec7677d15cb07b5537476b2 96047709566aa0b0ee8eea3c8972638ec1158ce5258b629ed155620693874e6f8d20bd48df8f3b35 4da392acbec0a2677d095b39168942e27536ebee7bba3e2b35724d8e447d5dfb5403fc26b855fc26 a2d375cdbd6c52ee44c6e6173f27e89d7236ef53e1d87fd8dd607bdd4342258bd6b4eb737550b690 b8136daa6f0b958e1171ccdd2bb10250ee31727d34a7d536b2a63e06cd51fadcd028d0b66c122d5a 17e230337ca26859c9a6b293e03505ee1bf81dbd8df0d6115eaffc35a1ae0430ceceb455d7b2d2b8 f03d1ba71cf0157f3bacffe2251d4ef065d4ba060e69161ea6fa666e3a4e4567b573ef1da4c5b4a9 f297f66dc70a9382c70cfb0b889e2cb70d6a269cb3d71548334c3032bfac51c4ead411889240189b 4d481fd7d7267e5d3778e2951e4f1e5c05c35d65d5d3cefda5fc7ca3cba13c17b178189e3135b10a 58dc9a0db1b8a86358f43157bfc8d276d9d62bbecce8648f30cf1656de630f7d9677edc6dd2fca36 a83ac289aa65dff36b09d497264fa4b53e4784a3144b1bf738ee6e6a5d66baa6e51dbe6ea24f16f7 0a0505efaccbc6ead945dc555f9e67554696ca631e2d479c94c7b4e7b18d01836486ceb51183c278 e8a0f0d48150b837e9a270555fa03064627f710da476e890979a674ededb875eb65a77b52b1dae92 697db38280dc0301746ade38704479a16ed737bcfec0bd74fcb57a1ef4ea6ad08e06cb08e82e96e3 c574837dea128fedeef6169bb24f130391da055dec3b4f34b758038be541aa2c8aabf768be7e22c4 bc2205c6bc225fb2b4ddbc82cd9af3cac844e695e669f1172e6c898115963c2f8dbe484f63bcbaa7 4c56f24d202a86c328f67e47ac01995fbd20975c8efd898dcd2035404dfa9b46889f6196b3591c4e f26851aaeaf3f9297c6de69b6d919b57975375762d32d68cf28f975943045ed3c77c919b72b0dd46 8215bc40c4b621236287f511116c9411e1250f11e1624eff22bb75dee54eb0376ec760475acb602b 5f8a81e86c7b57f6892007d256ba2aeeb77b1c36ebe2c47cf32cd0337a601ca68f473af8ec20388c 887bb18af4c9a03f7937f2d389124ff0c948a39871821f94f1aefcca165c8e9157f732064ce93dda a351211dc1928311fc2953435bbf3a435b34e0117cae3647b0848efec28cdde4a9af85dd4b15d298 4886be079ff350cba5d26872bf667372364ec3be05979913a5648df485c572b4e7ebc428d7bd99c3 a555bb0f8b8af8199cb06769b06935db83ca071ff72fba82f6a94d40f51bfdaad47bc01bb3c7da87 6bafcdd6936e30d16b5db1dc9d75c50aa0747baf389bf7ecf62ca2d2ed29d77e4a30fc85b11cdf42 8d557691fc8d732ffeb629dde82993d89bfa335297133ae6e62e572390bebe5c8e728039eba70f41 b4c775d67aaf537e5ebba25ccb76d309e32ed0511ca6dc197176ab9da4a3ef366202b3368036f1d6 be41f32d58b3764d7b5d3a379795cdbb59787d2b8da3254f1b6b6620362aadf7b551691f8b29cfea 2f768deaf9e7bbb8f16b1109e7e5ca63c6d2fcb4b9f0f5dd527d5785b9ab75c8497aafc386f6599f f558a28174b6b0bc6e7f720ba33d65b7e7d6c2d8fbcd346c8f9ac5ed0b6cac3f51a17e21c06abdfe aeb46becb83ea8faa769f6dda86aafc76c2ab2b217ca7121d997551f7f94277a0297be73a25b326a df556916a523c85974307eb1557829118ff3c3931d70c685d88cd6fb653c9f4873d7195193f766b1 1c16076c764277fd6b3c6e7d657cd03c0814d1707bd1b67e5d16edda237d52d43a50d7abf60bc5a0 a2ac8befb296863ca5effd0b96a020cc17b17e5c29e0e3b099bb5a4e0fe6d6160289d4630df59b6d 090c3df9002a7ce4ffa454052a432efe42b291dc9beb52df3bb9ea1ded95fc659545398fd193b7b0 5e0d4e396adaed61c1b80539cd5e9d1cc0831a37cd6faafd9c855746258d2c23354328cd5b62f69a 4f11ebe1db023e1a69f91a08efe0c7e1aca7a3ddbd092a0c73003461ed02486fe703481ff8a62c2b 29b701800cfacb148dfbbfbc6ed5fc45fc59d4aef1b74c67b3b8f117d1fcf89b8cc004c0c266026c 37d3943b9100ea4049d91d12807ddf13801bc509a0edf229cf5a02ec9abd043812488a81c57bfe41 c48b6eccc6703a8c8d6ca5a7a7f7fd9513150b66b69bf77a03012189e54b2f665d6a3e79b9317876 d1e12478357a93408adb7f127fcec82dfee29a177f9d6b90008a51484078d84dc0fe054dc0418b49 91b404ac3f8f3f0b951ba32045f9a6bc4bf13ea935e3c56ed48f0e20328996171a8d8a94b27e9f1a 16fd5ec7a1f4aeecf2bbf032ed1d420aa4eeaf3b7af93e7d3c2c0572fdd1f0232d3ff455bc81f893 4e15f1be40291baaff6c7f117f3bf43dfec641da4a2c8d84c179a99a80863a4cc0676595c2f0294f 23015fa34b025e9430461b36101db85b312a0149e3bd4181eebbda688ec3cb63f4f3bd358ac356af 0740532f76aff0aff6c2559f412eb69e82d3be0661994dfcb86e143dfd6b353d8808470f6b004d1f 793b40ee0ef340ee78cb99fc22ed48c74b9b987f2620c47e7e3a2d401b0984834802690c91f29413 6837b2636b2adea3558c3fdf04407c423a47e65ed9d7d85f5cfbd87e06c66df014e7c93408ed040d 9455611d8cda23d68f0342f1913e6778dff7f5ec196a23bba73db0f9287fc7b774f3469c94e1950e b9c9852f8c4697ceb1393c3ff9da6f1260043d13e0da480fe929cc8a639c3a09dc582e1218f5e804 c6fa6a5c5823f69b7834cf619384b333edd5610bef67dfa883c116ee16fd8459347cc45f74bc7d6f 39f4e62133f56065b77c2c0b47f25138be85fbba0a6bb733dd716e64530c2e3cb505cf03645f71b7 e767f7b48b6e83d3f4701e9cc0d5be7f344b52b644f167fb1f096055d356ce664102b19342dac04a 3f812f7b2cc94135362e36fbdbf0163df557b7bfb783adce9ffd29467ade82e7a387fdd2a1fb29af 17ef15dcacddc8f3a17dbdd70f59fdb48b3f394c2e5de3b438cbf07d7d1ea4a1bdabb6c7bbd3c790 dcd3ac0e2747743228392b62d13a9c6e8bde61b39df60fd571b7675f3e95df24607598b6f3a38709 3c302b490e5c8ffea9da9bace215d464c2bb74e49f83774bf067e0477d1cb89775df0cfcf3ad51ba 7b1781515f6759571337860cc89dd06ee9a47b6eed34db7dbb470b824647d46a2f8e799a229d95a7 8a4e89ff5887aa35cd165cda74d3cc595cf7d2b03a10383045f4d233fb79f34f125058a5bd79287e 12f805d693dcce9e24f962077f53ce79f31c8ab9b567bea8cd7df39cf1d7b6fe95cf43efaa672b88 bfe6d539cd1bdffb11eb8481e39850ece068133ab84eb3702038ac6e5f9f78d7a62565623d226b69 710ec49acf726f678ae7c37dafc479c0489049cdd8016cdf001693de0ffa3ed7eefe90e56cfefd91 7628eba71d3a0392dcb4d548f285db382ef1c9e2d58b8a53cf5ab7c7b7abe1ccce8a85ac4ec6ab49 1f31a7233bf813d91f880839dab71571b79b2efdb4f81a1d5b9ddb0e36a5d62e5b6d67f63f5e73bf 9dbd07fb315c9e19898d91c6b4a82806e042277dd1673f3b7b7badec0a5fa8af1df5b0f70febd9b5 fb8b041acb5e029f55f87f09d42d307e37f3cb6c654a005427ad7b6d82d4cea3cbb97382cff6d459 4fecb57d4b8315cbfb1cb7e60b3aeecd01fd3cedb7defbbe1f0bf0cbd885858f31555a697778f5b2 bee0a76d3dd765c6bba579c47745ac206aa702616b95759c959850c9705e56eb8ad1dfde47da60cb 245cff2f12c8566f49ae738092fc48ca72bdf0f025067ee371ece0d0f9bd5b82a75c675a3f5cda83 a1e52f5ba82957ebc45e1d0db2cf211a9fdd54356615d2d24d8a39eb39877cec0eb814ee8a6715d0 4ee4a1a055ee97ba7ae1c0814a7506d8f62e91ec961d3c4dc5d7b09722e0cfa2d2abf4bbf2eb321a c832d5ecff224ba966db04aedd1f494e7bc1495ee16b7179b46d0488e77f6e4dbf7f39ed232f3a5c 984bd11230b3bd8f5ee6c4f8e67d5c379f3ea3e70791b45b69a0aeb940eea05517c5ab7acdb502b5 31a944dbfb7798d54fdbb6e7b38a12c06c47e92df5a91c96404a96dde54e8a6b4f5f52efcbbc3451 6f1df13b790ffebf243009fa497aade7927cb8ab3ddff3d6e7c62c11f73ce678fba7668ab80eecf6 6c03efe3295a37668fd550cf4babc5ae7460098d28c99c7a25f4addab899e6964bef374ab0d16f8a 58bd8472485f3eb2d2fc16a578fd6d485ab53716bff406178da6af88b30f7913212397cd1108d67c dd1230783bf897fe2fd273b41efc64d1625c214b8fb2abb8e768b8b74e68ae621c1aa3cfd5549ab9 8f316f4065bd00347a9a7b684cd5abdb5e6d1fef6196ebdc76c6334911755497c37062c98ab2b948 71b2f125a4bb8ac4ef4b84c5f9d0a889501c0e056c5a5d0905909578c7025d1ec774902f178b4dbe dc22479ceb2f0619fd5ffcdc3acbd35792a75b70c81dd6c98dd9ce4feeacbcdc3b27ffa259c1fae0 1adfa71aea85b695d708db69aab41f8eb75cfafc57c424dec8ef19c4c9232baf48bb42692f0153c8 11e726741561b4f11230ad995519110ac8bccce37bbacb9d73d682239c2acfd570d5616f95c197bd 75fd064b87f561c620a3ff8b2487936192b70659f9a6c9e32283efc3f110d5d3deb4d8ad396af4cc 9f3c746ef5d4ce7d1c5299a258df76426da0f41363268f416325ed50879200c715c545f9ba136cf2 6a0b8583e3f2c755e0f36537fe70641d2e70b543a7cd32257cc636dd2bc378e4cc66f806f8613a31 9b95d34db7d1306390d1ff457a2df1615280c8c263dd762feec7d95a8e3bc91956709c287ba01d68 bbe3aaeda9377b046cbb3da422bf13a4237d406c22810d0213171c97cd140af699e684629d56f835 cb19dc39e08e1c492877f6763322b6d53ae6182f009a4cb7df41e8e75ba16949ad5af4607289e901 8256532e8394fb9f24b9f898855191eb55e07bbd2f9e5da823d9077a76d74de5d5500c5834148ddc 04d72d7f8c63391ac1454907e1a668366ae9038c1b2d84a2d4c7f9753462b8cb0491b8bad8d7d87b 7f7e605b5beccaf01f22a45f3d329bf4a2a5d0ae51d1e83da2b61f84a0c6fac3203f7321223f8b6a 25451ea4a83ff47f91e487c9f7f52c75a3ab5044dca3335858768736f53d12c5f2eeb89d0b6a8bfe f9749832b8682f49476c5844ebe79a9076748f3fa5a7255fcdd5561cb56a52ecdded886c1baea98c b06c5af4ebd4b9d043a8e9539135ff5293025321f5b53d2067d5da8630694e234c66f622cc655022 ccd52c1b40a5db553fa3f78bf45aaa809e5dff5ecf2af5741c625237adbe27ebc662152a5a7d0330 ca8bef58d227c67c11254940284da40a5f9def3a1c651fc72cebdf5026e87959ad214678033c9d06 365b7ac8277b4a0d8113f91dc1e929c9171212ea8e8b042a33ddcd21be2d37ab1da26c567a35d8ac 04abb059898d4ecab0f71749fe3acd3dd6ab9ff7a4dd9976740e4d0234f71f70a3eb45d155b6beec 31f26471db89e819bef1eea41c7374ad5b60b9d6b8c1043a3364fa9034a3156cb7a2e2e39ea6b4f5 4d26bf979b4ece1b9e4358dcf34ee4cfeff7066ffcdc3a37a5c7a2bd263a7b745d7d3525fc2a0777 fc4ad3b975f5966fafab76b597d1fd455c2ed4e1dbbd6f5c8f0ec91caca7155986d998e81ab5e464 65980eca45ab3d157877bbcb4e01aed93bbf9820b983f4bb0a56e9115def525a329e50c074819273 73b9216194e709cc91b69b63796b6df0f3f1b23ed7bd705d633f104e9f2bf595575f4f57dcfdcdad b887735971c73998f268aeb85d9c95d3fdd9fe22d012c1bf0c7b4dd7a9ada607739bf8b6bec27b86 da3e715b699e9b52bc7bf529960b8b7ba6ef2d1e54629209053cf422b9e85c9a84bdff0c89623e37 db1c9dd6cf17ca2ae501bd3e9f17d2ba5e5fea38c39247bcd5defb2b5e3c7f96cf08ac2c0793f108 7b7f6f341601a283bd5fc32ff6be5835ec6d3eda7fe115f3d5870b69dec9e6896eb67c740fbe4c5b bb92d5bda258f3ad509e4504cb77d1153d460485dc47872371287d9e9bd3a00d6d2a8755754d95d8 0e7e27e411cef6ef28de7a7f362b5fad70abeeb7b55dbe8ca9b51cc2eb2ba62ea5373629fa856ce1 984eb47ae8acb65fa3e01d3150d0c9bf5150e34a296e0b0539abf98b1bdb77efceb9be3d996a0b74 fe97da6721534e83f82d77bb8134f599c41871601ee4a65a6b18e96ed68df1e686b7376ab412c220 bf0cc7507da91487fde5f0b44230ad4a61d8243449f4abf8026a8c733b74f6199c16d6860c16f9ab 09cd1da6d89ae32d099d97828e3a2f9da1d7bcb427f2f39264d5e72552cbcae9fe6cffc3354685b3 cd1b8bd31e9a7e4edaad2c1c95ad105a62918255e6b5f319e270b330fc811566ab7e851697233470 30842ff9a8f11a7e5018a58b0b3b6f3416d8231c2c0a7c29ab3232c75fd5e5bc6ca2f4ec8c0ad28c cc9f8c59cdab5ca7377e1d4f99eebd8c78d614413cbb2022de367820de0a01116fa254ffc2392f8e e77f264acac3d759ed0ed86c25a40c5ac181bb7fd31bd7973ab09b5ad1c1b15d8e9f2c6c82c4e6c7 3bbc9d9dbfa3e3ac4e88c1f45ebb7ea7ad6d5240fc49b381f05fa487745d713a7911d67222f7bfcc 6410b59571a452f6585df9dee8736e670f82912e9d3a235de6c9d1ac393a8e742c8c47fa78544c41 4abfb0bb2e7f31b059ebaab53af7ab3229a1676173212c66d45a6f097c8f322b6984ad1676b044a6 ad97dc9fc89a9fbd4730565f5d793c31b78791d1f01ea399578886d674000f31705d19e62fbbf6c0 a16ea3013e2a63835282507df7686cfbc4063af66b3d2aec5ddf50a547db8745afb9daee7bcd4ef3 d9a3f12dd4a311bbf04356b2f5df1f7b6349de7724d67a6c87f5d35d74c690cb8a7b604f62f14bc2 bbbd2b899af537366b147393b156eef506f84669f76ea320ddc3a9c7763d41de75bbd0e5d879dae0 a3233183a83318b3d9abf2edf7e7506d6f4f9f6e7b4c74a7ad4f5f225a7a0ed8b64016739b260e7d 9b68c7e935d1aeca35d1eafada5c84c7a8b9b846e02ff47238f5fe590d6fcd57378e73b003b5a82e b20a97ebd66ac861a0d9c4678d716b3ad6107430c075a3dde5a7857aeb33d9d79bf99a376c383982 6cacd203d2286925abeee2e8b54e948567bdfabc7c6b57192ad66874d6ae351b3a524dc30ba2da99 4e95cab3e265935e153164c04adfee0ccaef65c896dfabd3a5ac04d1eb175abbb07cc9c6eae2f3b7 4a70a6a7ade37e7daf0be24f567f46ddd68bb1c6b1a34159b03a9de70ba8b74083acd437bb4f167b d66e2b00a935833959e5e7b45079de34ad32183b5679abbba7d2a71a3e4abb77232aee3f35a098db cccb8565a8f5f2272c5ce42e429bcdd543610fdfedfa1366994b056eb7a708dc9e3ce9ff4877f3b3 5534b4f212ceade19d4e22d8de90cfbb827d128b9ed1b0848d354d1ef7cf33a3d31990ef7a134d9b 54a35e9742456a44d572141ec7a56ff38995667a992ea216c015561e20e70920d1728dfbcdf8d94d 703e407df87c06472dcf0790de3b0190feb4f47f99d3f93a25540164b8b9a444e9ff8e66a55f88c7 9aecb3830e7122d64977b754ad2f37afba1e3ed61e872c3bd8277b56b71d2d924663b51d57aa5c43 cd97343b068aa6746f14f061ae9b2726f76eae09ab7d2838516350a17b1340e3ab3300e9f696291b 32c514523ebbb49dd353ca3e48db598753b6d90298b4a9e5798ac8a47cd3bf1a0e9d5fc449ababc5 c976b98b137bbd8b3f64f31a7f9ed74ffc054795146b107f42084d5931711a536de3cf0db4e3cf7b 794fd9fd949efcbcbd6ffc892a857836299663f0db6a44a6d8ed44e87c398af230337b1fec3dfe5e 2d9fecbb54aaa961b5869f9ff713e2057e88c7410fdbe6fc70b4cbff8352d8fe26db4db25debf167 7e36e30f7133e36f47f1e32fdf84e2efd16ac6df5369127fd38140fc150e7cfc152b46cada4db1fd 78f63ebe236b7cfe46e8e7957b3bb357e98dc360fd5db28bedd05d768721519c66730461d5dfae5f 57e1c6bd1a61a23eb9d6e8ec874acdf3474de2eb69bb5dd143d6dba20774a5e2630ff2bf495bf74c 3be809dbf11799bb0950a8c6098019a504604add142acbdc26c0f24226c0aaa5c4c677614636bc74 a3c2927cbcf1131f86e72a9f84c455875eb7a65e78d1de291dcdf17eebe9cd728367c72a22c1131b 2c03a9c030fe7bbddffaa32bec3ef653c07fc097fef7be44f9ac8cc1edf858966e156450bc5e807a e117f117a839f197439c04685cef09102a6002f6abf5049c0ba394689580830117d9411a401ddfad 6c8d307878ddc2caf5e9172afe933f0eb29c4df04a43cc60400d613f6aa0257ffba0eade0791bb9e 0e1cc6de74ff421f663e473e72f840be1749fe78bdacf6fe85eddbc959605e0557d6f59c3b9c09f0 29aeb2bf499b383ffe4c45c4f8216d653b48403dce2a4e08ed046a7fa609541eaca3521ba4438abe 08af7645df06af87bcf7a3a77cf42703fde6e987d3d3834aa7e88112a7effd7073e1fb8a09cbb7d3 e7dbb855e7f9de95b2abd952ab0b7b6a60e7a0b761dd10bb98a758006fc7ef75101fe7a34dceb18e b39c83ad7bb053a854a15fa44d24d2dea434276d621a7442ab5929810e97acd4c878fe76ed17f6e2 302a7b7a060ada207da45c153ce89e57ef8e10ef6fae123ad7eb31bc5c1e97c7e31cc4d7e7b98f5c 23f70dbc017744c18553d268d64e8886748f7b80408ef3bdbb71ec4655391c11cab5cfd229b4eb79 08b66e87387bac59ad92035aadf615fa4502a0e2210167a54b022d3f9f04cea99504a6eafda86cf8 a357c76a0cfd5dce983cec1abeb897758cb8786b803e4b834474b738a09d76126c9d8008ce8ecd71 31295c8fb9523f709644ffed14fb2478586f99c2a132ded76df2f41cd875a28359cc9be34c7f9577 cc6eb07aeee5be061a912d4286ba244063529c03bf48808b6127e06775ff59129f1b0209bcf7abb1 6db6ba2fefd36bfabb3d5ebf1fafcfd6c5cfaf7bae9a1b4f4fd360bc3aa2f0927156c58578d8f496 9a7d51967b9b1af147eb7edc5e2d766dfb56bbfb8e4d41fe80666f542fede5e3225ba8bc1faef753 23eed56863a228a66e14e2870e09c3ef0e0d3bd03fe49532f88b04ec1a46026df547024b1498e4ba b9ea9bfc0a9560fcc0e0fba9877fcfafc32b7fd2ed4b3691ef1c42bd7fd81ccd999d3ea9d6161b9f 6833c05f92998657ea5e19a6d1e230ae1c8d58eb5e0d0418f8faf74cc6ba419a397d3ef8d47796d6 1aed30445a6bcea5bcd3ca0dfcaaba71f0516bc813fe214b74dd8033f48b04dcb97a02d77c2fc995 a56f92db81859780ddbf8fc376feb8f416ebc709625ac9c1551a45bb5969b4cc206e8ff66fa0b730 92fa706d20ec8c31803625e97343c846363a3cd7cd1d669bc75d81f16fdab19d0b353ce881eab98f 9655123ef4d5daa1b2dc322546563c09be28fc008b944eb485e5a7ca40b2345982bf48a056c94a7b 33e72739cece0a69be2f876ee499eee8707de4ebeac9d8dc4f8733787a593c6041fb376b558d5ddf e8e8fba53bd1e1d303dd2d7b5f423b6e21565b8f1bb27ad61b9a4a6e66b65aaf52e72d43b3deb6d5 3c248ae7c505a5db6b64a780fc82d4852cd945418a8ad249da06f05b1a8bd39cf8e977e1bf480ffc e490e4806e90e41ec7c487dafdfd956b5464575bce38073f4e4c8b477af7fd561d26065843b20194 9e6b614ded046103ad821153953c72cbedbdab915be6e5f28a3f7465858f035de9ee204796c1fc55 1e98c350521b042c8d1f6e53fcf055449cbe584604adee5130b15d28a0854fee1f72473f4b10ff6c ff238161f698e4906910de4fe6fd5e3d4aaa9bf455fe885d42ca661681b68f6ac1d9003761b85b26 6f48ab5c81aa5af7e0ce96ed96478a6ff6174a2f3fc2651947287958c6b3acba149d454d9ad44553 fca411a5386b5f031102ca80802e90aa90cf1923fee034287e855f6cbed49dbeb8d34b87520e396e 23abd02f12786eba3fd93e7fc6cecdcb33ff5322fcb428a2dce1ec1884f96ad392312b8ace6e3512 7df572113fdb7b7c2c2901706eca61eed89387ec1391d4e08d4a13a9b011f5a8c08833b5230be6b7 6fa4a12976e41d98b8f32bdb4a38b7f82c729b609e7d9282ab8ad70d7b1d50164b4591cf36b40998 b28233a05f24b0f1baa6077e1f3d701830cea320929d635865ed96846ff6eabcc6e8f96adfd4aa6b e4be656324527ae76596b39187f77555d2da7c4734facc509cbd7733c15ada4b217f3a92bcb3f178 be747daa9ccb146c8e68f56fec55c02296eeed0bcc4349ba0c37c6574cfb5b32e8c0d53d3a3857c1 94612e03fe21cb0efefb233d472b8ff43202fd7bade9192ef03e48072aff65cdd761b43166d88dd0 dc11a0a78f71e0acf4cbe5508aef4540fc06f592389f4f5b02b61cf478e7341ef3f866bde0dc2bb9 e6085aca6aa9b2576f2bb3347f3498c7cb73194e293ce92019c3b4b8dbb5e8fea6b3a4ded540a794 1b7a4ff1bf9472cfe732e05f24b9cec07f8685f6f9ea7577faf1504caf47968bb3d77cf66a50dbe8 f935b3526964bf5544f5789492dad9178d8ff7116ce895170a4bb0c697d79f36475cc1017b6be610 96f6ea18e3755b04c3834b91e998924e4bf9dd91ee3bcf80da568a2035ba902d32a1ded94bb2e4ae 296e49c4afde52d44fca03ce807e91e4f0e5d3875698757e6b5dd5396d28c9ea490e6b18f372da9b f200dd76160b511e196b4b0218e92ed8b35dc4af0b6a76797267fe52616ff2a9c936e34b8ff1b468 c27481cf827e9ac50d3dc85738eaed8c556a5c5ed86472de3ec869fdfc218147ab4eecb9dd8c5874 fb62caf74a2c7a5492929ed229d07f6429d56c9be49ceddbcb0773dbdd9d8bea81526692398c3456 c7b0e746a535702ebf573956026e2d4358ceba67bed24c2fd47a77f16519992d32bc46d5e85759ee d0d2f930a2a2fa69466defaf35f969bfe89f4e7b1615121cd44d621f2d6f446e22c51bfbfba96c96 fb35b229a2309f72bd6c8ad82cd914471c9401fe225e75c8f07e863bf669f18e549b4d7ad21e5929 dc0ef73ce267fec6f5e7d24e303782edf92a5fa13e47f63e2dfa8c8fd613fa8537607a701e562875 80b6a871c40e487d2220e4f46b2e09737ea00814f6a5cd6119e99b55a9755e9f8845b8ded44ea575 e5de1de117f6969dd038d5a18ff8659d7fa7605006f88bb0dd136eb7a69758c752f1a25afd4a4932 6091e0b43a6813f276abcec41c3644f9ca45905816512da6573fdce8613bbddb4ee66196b32175fb 5b2467ab468330dd769740c5d56473786fd0cd6a2b139bd2f820ac37fa65875fa1e8885376f7b97a 14d9fc8a3d7dfaab362191ab368939abf6e2f15ab5d10eb86a0f6bc02fb297fd8cbae164e78f71b8 f674759fa090ac176bb4b0e5639e90c00d3ae5379d0061db489ea1c3c144a7b4d2ca258d1a1b10d6 584e88bc6ee53638e45636a5ebb7bd7699ea705df51b53fc2a4c5638dda3d82c73fb500465c58d2e 8765b0fb784b71864258681dda98b21ce398b2aa999832d17c4c4160000bb9c26fbc425b3c9e4cb2 a5db9d26a51a7b3556b4cb12179561794908a5c2cf3a1b96bd13037af856d6a421b832816181b5c1 85fc6ded86ed705dc3d203441f8922de5c2bf595d7bdf656dc2b9eac3a4a095d8a498358f68f1b19 db56843d36badc6fe8ae59f9a088a73651406861284000595e0005e6f40305fad724e5f1f9c5ed66 23c763892d1ae6b68b6afa3208b62aabcf25c9c87509aeb1bf4ee851bfd627e169826cf0a4cfac6b 2f62873763f1b8e271d75b3ecf61364db494e4660e7bc79d32b6d5e64d6c8c8b03f4537666e8ee1c e2e894aa728bfda3a72e169aeace0f801fce97265a9917d1683a2f8e8edb79b135bacc8b45254cb1 925f9cc7f1ceca1659a4f186b10787c3ddaeda0f3445c16a92e0b820c13cebc18cb0a776774de283 f68abff3cba55c7a8a983a28ecd18fda3fa3b312f1444157fb2cd0fe39bfc8bddfd5f9412db4e7ab 4967382f9568747672ad6c11f96c43c4d2f412b5cd29a5cadeb481e473c803d087086b922272bfc2 47e46e6101725788f72f7ea69fccb767eeff49ed73cb872e034822f1b5eb8ba492af33df54a87d36 bdba94979706aa5f4f83057aaa6ce64e8f54e625cbb66744c1bb4eaf02f09cd2bde667da08a7e9fe 2da986740adbf624389e4713512860937e6fce8cc35053c78a5d3a8f474b211925a5466ba4b91139 d2243e9bf71cc517e03e8acddaf31787a651b7f7607ad56bd7fdd052de7a608ac5fa4d665e539d24 73376581b38e3d42e790da9c11645841bcce10993c172c31e9df2ff2f8ad55acf118e86669bb5172 2683d18edc2523e06e42c3bd1a55860ba4d21ee680e978609ff7f8604926fca0d8989bfdb5e6fafd ca142bf52e5069deaff4c15dbf525b5ffa157817fcc2540bd1412f3dd46c2a42ed4413470669c7e2 ebdfbd427d8732bda9faca72395af0e339ee51ad8944d0a5a10901a5c161448c07c5a383f737dd2f dfaf42c35def62514eaf5110aeddbb77797559fef3edb667bd62278036cd8e70bd6491743b6c8178 5bfe90727ba8bf4fad98d8022dad8e0c5a5abe27b5d4d8745aaa077abf30f2f2e5a8359faaab8c83 de497041da62869fb14c94eb5572d53fd6d0c5b25f1c22fca39415071e81a74ea947eda7603b1422 a815bf979d16b2dc4d5b40f1b96e1a41936fce0d62d784e7daa181d9d6a55160beaffa71824075fc 2b56eb65f73baa91f5215eab4577b57a73c8479559772bd556b5495499999b5519a932fdaa5b656a f9d37fec6a8e7fd90e22f52a3a85e0c88a84a793055ce7574f85c67f52e6884fadbaa3d992abf5ae c82edf0efd0fd084f9455c2357afa8c2bfaf40f9a54e9a6579a56685cdcac376b42a45cf3a5352f7 eb6d6942da76f13b84ef453d46e2e2cc39968b10df1916aca94314b02abacf3b7414e6f1f1a99d73 0d92ca11f051cbb9736ff71f6af7193d24ab36bc701cd7cfee0214ac54149cdf3c487471382ea66d 501f8c665ba3d1bb6a5ea93d123e60635919c7d57be91296872df4598468282858e1a354282c4b9d 3ceef7c639226ccfe11b866ee0a6c772103f3d68e0d3f40fe0a0d9ca16c302db1d0b02e34dae9db2 5b0263025181f1fceb01e3855104c6a880a6c8d87fc8465b7ff0b4ac1d694067766b469d70182897 5753967f8d47b377d0eed1dcb3dc4ac4225c3fbe16d912c58a6fdca3d244183ef30e6e3de0dbfef1 825baf3904f5f258091c7688263019605d60b2b09094ef0a98a0432645d78009963b01135a7a0313 a6524ed98f81093ba25302139870d41398f0a5ac60c6cff63f04f753bdd2c9f6b6df907b53c2f429 b699359683d9685e87babd260f545bd37e3157af981ba01cd62e7111b2aa4ff87a1f5d41a9975c00 d5104300e9c0404a014ce9177e32b71da296e2f601a40b4d5326788aca01480fd453d697ffcb4d93 e9df0de0e9ffd2bcff2ce8656f29c2fd3fe278cad271bca6c8f45fe8268e777eb6243e8efd851dc7 1fcf8fe3ef0088639729c7b171ebc6f1be394ba1d33fddbb7c1c9b792d8e2f333b461ab353f47566 97c8c0578f685e665f11745693b7453ad03b7fb00a215ebad65f2e11f69ed71594e53a9f0da347f9 c1b221f8bd0fb5f742df3c7ae1e774f294d321c5bfb8ff11c7479389634fa7d27f09449c308b5dfc c9156e29441c27773b1f276ad288136e348a135cc4d27f7db321478cbcf272b41f967611ac95ccb7 8db48fef2530b986c7c5f011e2d13008cb2a1abdce130e7cd50cb1f0bccdddc6b369878380fb9650 bf6fb7196fe4a3ea63b7318f0fb0f7baddcd6a74bf2fc2d7fd9e532ed944fecff6873869fa6c9cd0 11137f6a4b39fe9c6ba7f80b28cff80b0250fc7921b5146b187fae101683ce978872e72ff72ede63 39dc74203dac3cabf68b1a544fcff4a6737db293b6f76c7d7f5e8f0b84f924097a300ef9af2d57f2 071fade9a9b3ebc89bc00dfc6e6edac2ed7065cdeb29d12f17eac5dece0f14bd9eb9c6ec7a6e3f7a 97ff485bb790e20fb116e36f07dec7df87f3f8293bf193524de862fcf5aeedd8d8409328ef5c17a1 bb30f0175d12a9275713852060946dd0f335c3577a9aed0f43c7f5b49175f526c9d57f18d36bf898 5761e06ed1e5c23def75ea37bc8b0daeae6c2ccf8fb391456bae780f8dd31ba9bac744aade8ebb01 743d4ee1ef6fe28fbb53e2cf97e7ff97eb55aacf0408352825aec6b01777dee587367835efc82478 12cd85bf6d95d7deae57ce86ea8ffd08161ef0aeb0bd2fc1827e3ba205eb56760ac72bd9a95d2fcc 007e9c3db5f03a774b9daf2b11d3c2e9fd965a691c7b438e53a2c3380b9bde1d0ee3c7f9503c0577 7b5335ae76e52a5eec4aa85d7fc80650d936fe4ece72da9780920097879b80b34d98804200c7289c af86541eab3ebb07a0e98fad7df7615698d1bdc84d67d7cba0bbbcb4dcdce6dcbd7e68571ee78453 a4e79493bae91ac7cfb56b1f7565998d058e60b27e38e6711f39f9ca053aac945ced501a2d477675 7d26ad86026f4dae303fee039eb8edc5297edbf741e4fa8bf87bf0c4ffc7d6996e290a2cedfa5a10 e779645641e651401010109cc559d4fb3f58dd7bd7fe7a9d3f4f5675f55a84499a19f16664e40b98 11f3ccbac1e10596d7cf1788b70af77d6df20d0a2fb3839d3b81ed72feb07eeeaabbaec234371688 f7d74e9bc2578b2143c5213562e3494b57a258d3f5a80accede5c68f16cb46215a86fbe37317b6bb ef5390cdeaefa047f225ffdabc0d7c48c398855ddd7e351bcf6dbe228f48a1a307388d83ebb3d583 4b5681fd2f5e40f3a4be727dc77b811de9f802a35cfaacdce7e9e5be912f4948d9c95ec6cbd78db5 3a7e568b342cc5936dd88cd8e71e5a8a95e41b118407e14285ed47ca0553ec2307bd4f71ea9b42dd f2e106b2583c0e4cbcc030fde0bddeebbb3714c0bc976b506dd77f9c33e7a082e94ed9f097f335f4 3aceb9c7e930afd9abfd3ff8d9bf111fca2b774a8317b89ced5ff9c6f372d3b6f1ee349e2ae1eed4 91fcb53bec6e639a6ddd96bb422f172a17a81a4c1f50db9f955168f1e4c7c402db8f49ef6d19bc37 7c19b2977317ba1b70c9dca56a9fafd7e9447271ed30e8e8ec544a2130e7b9b43eafd729cc96acb7 6625e5a16fa96bff607579fb30bbd495fd3f78e56aa8fe02e956f8ca43ebfdfd40dda3d3f854d6f6 8771915fbb71fc3d4a1297e771141eed45120c0661ba484bab82f7a9ad6ade6876edba41fa42dce2 10183a4c00504e456af3f34d0b55e6758d30ec3da1b9b60c2c96766bfb395a5aabf5b4baa955995d 5c109e41b5b96cdef7856fdab589a2e4ce485fe4e10f1c17deffe295738dd90b7c30d173454beb64 a9dda4ddb99ed21bfb468c630626b4503d400bdfaa435befe342573794d08f136b58d1a90262732e 90ca376bc8de4753c4961977682597156d69c649b07ad04b9d5d0b6d6b0631d0c27c54e48d695d92 9bf182ab05c32d587d83880782eef73c571fdf721b3d3f2b1ffe7f7881a5c4f9d98a80e6eb2b5c28 d907a9e0491b7c6850ab62e5842f5bde49f42d6e6b798bf810b925f87074aacceb691f1ab79cdd9e 172a96f6a9b566d771b33f33f3306a3e94e1c8b43b2263a2675334dc913f3586e0f5fb180338b457 baaf08673d7f5ee6a6cb45af3ba5c77b565b45035be3d46093e19668b5eeeef00f5ea052f77f84c9 5301c5a6db7bbec5af26c53115d50f3616406d985c0094f2ad64e196fc693017bbd39dddc99b57ab 5fb6df33930f0aa66d45351343cf1dc375cf0363387c63babf2f933a29f739bdd02695e9f265dbd3 f270196a6b103868dc9efda8bbf6aaa98a6782549bdec7528e63eabb4d94b5de5151f3fae11ffcbc f887582f2e8f7cbf36dde0af0f1f6d813615f6741a5bb8d527e1d2bd9a3217d5a6639df3cd78669e fb8969dfe187f146478091e3a8921ed47ed247f582a476a674a243d3b21311da9a384f343e9713d4 9dd4d455a9452fd466e2ef147508a44a37c7d5e573f01acafac432645de9aee4418739ca83f2e8f0 0fb26fbabeba3c9ed5efc1ff7d52386beb7c2de1974a334ff98f10c63c120e50a7363e8b76c7bb59 b359fb169a78eebd3746a5dc55a76ab9d734928ae0b49cb62a1a4f749b5a1dc07baa448e10b5d5d4 c7caf118b08aaa9e35f90294bf8187acfbc3b53c20fdbb746ff56bd22c59111232650d31ed274b31 1d748f5f1cfec12b2facb7e7dcc89fed0697b91aaf90900fa77360b2704f35cc2d33ccc03e2a736e 76db7bdf2160e2c3c037c056b4d18bbdf5695a81d70fad8e471f555a6c0a6a6b7ca92aeaf2de52ba 6a6520675e382eeb1f8991ee635d96acfcce9690637325a6aa7213f1cbb1247c4c12153eb3c65458 205698e1f375a07eda7ff0778cee5d6be3d8b81c6d1d8b0f66e517ed05c3323edf09f5aea5ebf8c4 9c9f46aa015294338d7b7cacf14f6aafee19fda62482fa52ba0703948d8ef1f5a1e5c1795197eefa be2b59631095d07c97121d65248a446736133ee7f352c842e69390cfcf0b7c1835e10c2f8d9fb06c c04f88789f61f32fd2d272b639ae4fdc6ced9791afcbb154b602efe3f5d5c465c4276eabf2bd3343 66e9d018e72ad234d6ea962690dd506d57ea1ba5d7a827b2a174b317f9f585d1019a175fa050159d d06a8b84bc8745a0fd190963bdc809f91165f04bd0fb2e04fc64993b71abf228c7b1ebdb8063374b 8563b7a89f213c70acbfd8ff837ba79584073987cd5674bd28873acc0a8b91b5a41dfe78c4ad81bb e9189fd882a7f131e6b4c678f3ddbf514ee58d2b5f07f795f440ef0709753f57d105c15404e40628 f86db4228c4f6c4bc8bb0ec42f871b822f0517965b4d2a1ac7b5f905573ded0eac3868016ce31ef4 d8c64391d8c631ef65107619b4efeaf9d3fee282cdabcece509f46cc59a012cce684e04d4a3e336f 7d02dc4c774e479f98504f132eec4439299a2a9b03cd96ecc96229ba5cb81381dde62c90c8e5c92f 9d07c0d3c3d27708f0e55cafc17112d1e36a2d0165b7c962c28a4eaab0cde1c0659440da321deaf9 a6cf25a94d9fcb43813ebd4f2e7dfaa09b0c933d7dba10bb5f9c7397bcbb798d033d6a26a9ea3b15 f42b74b9ecd465ad6b43c78db133696b5bfad0504ecbfc5086470d417cb72153ccf5898550a0d835 4fc7f2915b578d1bc75d371f76076ff3acf87c54d966dcc8e61c964018b52a8fe9b3e888b40e7f6c ba9f8ebe39b79319113c27b3e1a83941c0323b81efee7c021f805586c6f61f24ccfee4ae494cd5c3 0bb05717e3714f721a81c2ce6c8cc1a72bb1d1524e77ba223d87262c0ed3252d14b607f5a7d352c0 e178a2ba64775b74cfb69ae30ba3aa9327d3ed4e73f4f9e297697d716dd103b2044d6651839820aa c053693736a9f9b512911f98b992def35325bd744791de899e91de6a1b65786d7ff03dc3fedf5f0e f2d475e3f58831020b8e356ff280147baac18c99abb7305559af1ad2735a2b8bb934d7e4197f38e2 eaaa28b2ad856d32dda5bfa08df22aa60717e038b1a0d26d823c7a6f2ab587df4d2fcaa1cd3af959 bb5d72c11f50720c3568127cb0ea38b4378bf104ef26e3e267531cc53e4f8ce2596d3a8a25c51fc5 94bffa075b0cc859516b829b3e318f75b78641daec2996999fcb1d8b1f4cbeeddda6103c77256e33 964a4cb28e60fa727fd39347b9a952af016c5384450614804e3764e6b79fc83c77788c97b537309e ec6ba5d10a859b23f62542a3aaeb8f86dbe14b188af5893d6cec936fa71107850508a55b1c104a65 27130ad0f708f9cac5ff605db08f7608f55b336fb9708cb93a6a4ccd1176a435c90f51c969bb6d4e 184b45a6a783e0047d81750ad82118994f7f3253c6341b68a3d5f5e68c6af03b1c6ed3e676283ad8 893856e907a16c6739a2234665fcdc7cb6f1690a63d88de0686c066ca718bc2322f429250f74ded6 daa8fd4678d43e0716faec36b33f54cacb1f7c65bb6ffb67176258042db7f65858d62c3d697a3c8a 18e5125998981fda5db67dd02b941b2f7263fa0801c39d20b789e3724310ddf287c375a3a561b707 6263b3a51a6208ed6dd0b47c3ca1387ffe46d2c807aae710ef31aa22237bd683c3f27108539bbe04 1705d785e2663b8198e3a9023171388118b76b40cc545a64d0835f84d0949a7b115174e65ad9999b 0bdfd5b4434965a49721e07fca509b7d7a569ed5c7916fe509751fa718729be6904f72ea20e36115 87c300a2e189a429509c383388d5361e541d82abc136d73a0c8480baf70f2d23d797937daddf9e56 06bd698e9ff47ac159ef5e27dc7716e89a9d66ae0bad5f44d7f8586ad7b8dc9daeb1f92c7ee1e7a8 91e7ecc9e2c27a8c169e513adaba72cbf19c58b08511dbadd2036a01aacde17eae15d1574d48a1d5 d8bff40f9ef9ea9deecfef7588bd3edd81bab73237ee9a679fef3c072bb533cf0376075bb683f69b 1e6fda5ec7b9b473fafed30ac6bd7a8bca1b4833a2ef5c93e9aa6eb37ced5c1beb59aed32cef0e7c b31cf666cdb24538bff80685158d09ed2b530c6739d4f2b5a3c01992178f046e3f1a9393f44922e3 2a87b770e80115619a6da7bdf3083bb7df5b7dd72ae2d2b959113660837f7cea8d7a840cea7b461a d6e56ec4d65b979b52d3c8f237f6acf50ac3b07a39ba872ad4bda6950730ac562c7f8b965f2d422e 3bafe2aa4cb8974299d0a351d95974b27f5b90fa2fe627928a2d6c5189f5f56eec2b561936856aa1 28d08fde75345e4da2ef794fdc5c3835386a99b9be4a6f9fed4fef98342b97c1aea6b9565c7e698d a84436f3dbe2320b598be5ca142c70c6b256a841af7e7e5740477931d2f87c53bd98a00a749760c7 9f9d7367a95ecce94884e40625faeb720033ee1503337e0500487e3cc86011bfb0e148da1ad1298e 54035a3a62a9a7aa8cd92668b29c2f20f80d4f5b4861772df6bbf4f5d5feb8b56b6373191daa576e b12e0fb369bf50e3e1efbb01bbc5f71cb8d70a2180cc8647000996298084852280ecb93680dcaf04 80e6c7028042f73980c2cc1640a9dc1b40a5a003a0f2900650bbea66884f003a1f173338cd5f98c1 a3fe4d89d734a2ea4b79ea6db2d3d146f82399f7936e1729d6ab957ef7d8c9b5c75ce7de682c98a4 0af7e36d89bc36e27c6bc0f9c09db839000adcb2c7f40b0b001d627136b92eb3077a1d0040d7d32a 807ef2df6d2200abc81300238a3a80695e04602e7c07b0d5ad06606773086097b606609f7d04e0c0 e89661f6f9c5741f58b10cf0e49c53989642058513459c031d42a835dbe85da23ed8ce73d8f3678f 4090ce156cb7d917d6cffc2a7799603e8056df1e80729d00405fd31580a1ab2d80c9d7fd5fd8ed2b 802d6d10c09ed51680372d1cc0a9ba00e0b3c801f0bb980044be9c0788c60a020878c2ff49edbdd8 0081e0ab0cd2fe177ff5dd21c2fc07ba92e1364fd3d1709d61794dd371339f61daca7045d3946c53 1904f1f96126ea73b499188fb031f956b87c1495b17b67ce847fdb0cd0e5ad6ea1ebeb1e9d1caed2 4bbe5c92a1915eb4dca178be50afe6d9284d88d39d73f813b2db4c9354fa38099e5cdc1f1c3fdac6 f9c57facab933f3a74d66690a40cbb5966653fca609f32030b4006ae9e613548f1e589782eca8fc9 23ac9fb847513e4977e674d26efc6da95ff7883fbbb61cdfb96880bbb874fd5574360adbed7910bd be5b1127ab5a4a9354848b097eb4fbc745f7343e8226c01fa8675bdfc7785edf57bc83b6db8c96ea 2ffe5a477c88349d76c9f4553a6be9ab03bb19dc4d9abe1f59975ee05c4ae4b1ef1ec173f4865b8f e2be3fb8b36d0cbf6da643f256bf0d99ab8c0c854be260f2a507c0dad92061f3748fe079922eca6e e2085874fc1cc8dd71dc112f87500f807d6c3d9b3b61f886b77248b21bed35d536bd95f29554d7d7 a9a8adcd3ea9fe224d5fcc24c36098bec2a794be05df4ddf66699dbec5f1397d938bcfd3bf4c8b8f d2bb59be89c8ad7e558867ef32256fc8d9648ec4e9b1399227ac716492d7e1f9756e9361e7a61c7d fda61f4928671f9676cedbafdcf772b70d9e9b5db3ddb96e3b7d1cc8d6d3a0bd864fb5f10aef8b4a ec05d99407b694591424b419510ef93f485f96476726cec8f4fd717fb6223e1ab44c3f8e73cc7eca c2eb2223bf6efb7219b89cd3207f4648bd7ac219bd9d800da67f2c7628f4501920c49eb7706ad7e2 eaf456ddd5f9cda55591367a8ae8eb3b81db6b64cbf8ab549c6f56f8f3f873a1d382280231b8e55b 51a9701e2fab444709852d370f0ecdbe15c8cf8e15b4e7e5d92fd2f7e04ca7ef4b934d3f4969fe02 10237e01d43d799644eb726b4fae97b3e560d76461bf9f87388cbf4916bbfd4d2b6e7becb8ba81cc 497b6d176468f562646c455c8c71ec43da241e3f1c3e5ad2a112d1e5bdb15ce97767c941f528dc2e 47c7b059d6d340d1d39a7fce4bc3c50ddb490b78539b7b4fa130f7e650ee2b3f78d8fd66ff227daf 6fe3f41399e25fb9377759a785e26677ebd457abd32bba45c7d21a89b7a7ed7eb77e56e2ebca7dc4 ef38286f0b513438d4a2b2756a2ff9d26310eeb8071a4a83ea77ff26385aa549a04e20c13faf50d5 d73976b6b8dd2c7f319b255b2f2dd5efde7c6594ddcfa08ebba02c65d16c7b67394577e7cce36130 ff07e9a7f31efe1550fb86f3ca4d2fdf0dc97b7379f54e6f72a61fb8553cddcce09ebd1a8ddb5114 bddb8725bfefdd43e985018156c38afe451ad6fc4142b61777670a2d50608a79e9d61f7b8eb865dc cff120ba0b27afbb79a0e138e15658391371f31d02f3386d17e6556201dbc2adc55b0784312da538 73ad762c39ff20fd3c39fa95abf5b4576e15ce1ff5cacb38e3557c72584fdad80631b66c1cf25b63 2970ae179c8afeda37aecb6f61b3855dd83ebd1773ce79c4e656727d38d770f3f35cc759e235c8a1 2b303e5f09f078ce412237af3e5cd516b1e5cc6e564a4b4b31c689d581b6b9d9b930e9cffacc9e31 cd6bcb306173e41a4f78e0fce0ab0efef79717609ec4cc4ac77a5610d44cc2fc9dd825daa9b7b1ba fd763cd1fbe3f0c0f565ff1af7ed0546c24b2fd7c5f66e01c22e0ebde45ef3755902e7355e2ddbbb fabc6137ada06b1dd1dd3713d2525e376276e67393995e6f0ab3fe9d33cc196a7a265206f6c6733d 7919f8e0d4d43f7983d6477432d5c14ac59d06eb8ff30ffef426d8bebb673c9965a1321f0ed7aff9 f4abdcc64b74d7080f7180fae673c7791eb4d3ddc27ae739e5fb6d35af977389ddaabd6f962ae75e b3f3a908ce74775031efb941c39c05a3ae89483a6acc137d68e05ac4e89f614ed547bbfe5c072573 3d0dd1fcb7d0cc947a4d6b5accb7498dad1baa56d95f5c75236f9c7f908d4cd97c328160251393a2 b7f010c2570581ee2fc59d54f36fe2a0eb790b7ee2446f5e99d765deb65bee6c39bb50d2cf61bf41 323f99562f7818a9197d0cfc19e7f54f74afea0b166ce9e0b6d89f86e6009b4e6086d2e268266a2c 739e69d51e1ca99bab7b511b54b3ac1c8a5b42915790acb439639ec172e4534d9effe02b747ddb57 ee94f9d18333261d44bc45af47c53c1ed53b9d7ea03ba3baf7e9240d27c6c0e1bc7eaf0b96c6d58d 1984b43c1325bab14190ad9dbe38e2173ddfc51fd3f02200d3122414b455dea969557af14db250b7 e53da40a97e2486d1808a7287969aab40fb7403ea958224ff17341ee03262e5d0340944c6a6c6760 9d2cca22e6bf7881e8c73b13d453d84def97c9aad8bde261a277a0456ab6be81871b8e8cca5ce062 d4d29284351f64a2196e656feb3e740fa74becb29e96bcf4a871607a51b793cf53153bed9c72d0db 4545191075a5036a3df91486983c953fb4743db714c9f474578285c2417c36f4efa697681f6154c4 d4a520625adb12debdf6fc1fbc40bab838e59db3b879ae1c2ada08072230c202e42d8e95865345d1 a275422603f3e1cb9431ecca929e7f08c6b45c363cad5637a26fa789b2b5550e2727513a7de72a9f 6e712af7a91328dd4a4045324fbdb60467ef5f7c0e17a438cfa59288ed47d9f3ade33e5bf6fb1f61 f82920426e31e5f9607c9df101999ff301f4b27ff1f5d33694601f57db485c7b37995aee1fdba16f b773b03b79a70dfb30cce767265bed186ed420f4c218e2b575b9a7a9bb0131578e2811281d77b296 fb01ba936e147b96e098bd8b4f567b8bf3deaa2862d7c7575517de417b207892301246ad58e00317 b4786ac8aff962eefde6a27d34e0a225c66558991c431fe65fd8bfb85e96ae76d8c181b40a296112 9e307fb8709d37e270fcbd694dc59fe30a2666ac6a3a79bea2da7af2a055a9ff92954e7a35647d08 b8d28c7c85e23306d6225e05f6822702276104f79e42164d817c10d9559e524f5dbed8abe25c0cb0 2cc7f81b93ab48c88addb4d629bb69ebdfc986e5cf3596e5d78a9e6139ff071754a7d5dd793312e2 3861e9e0ba76471ee9afe179530e1b3358f541236773a569f96e0e5469ea93f2393579e95ef5a762 daf46d115757dff2868277d9c602086db77c5800ce7c9101ee5c5c697c38e64297b9cac26db39bcc 0f6785668f66eb474367f6ea276264427f30f2106f33addb99665af7e19469ed15fb1f9c8720f975 07b7885de123e1da9bf8cf813d76998207db9dd7a269bc175370caf065509556fd8e7c41605cb2aa 434674604e111638670ae04271f949de5c72b1e26e38b6b34dd88d7eb8b1c21878b38d7ca7c8ec97 e4770830b2e2c34cbbf3a2e8e433d6686db109e81e895c27d7c2bd3eb9163d6a727995bf37d192f6 3f3815da7d6dfd59a5fc5221dbf4e21d9aa453efaaf0ec9e131a7ab01ce6b4bab47cfde4aa9bc7ba 8456ce90f079bc49215f290b7ca9519b72ac0dcfd9cd1bf359c1e356cc0114f78cbcf7af4c5bdea6 f4a9fdced3dabb5fa77b9e38985cf90339311b0d79021d8c05f598b72e948d6f6a94ddfb398547d9 d59b42d9b5be4dd9b9e6ec17c735d79caec2db9e0ffbf32ab300db536ade3218d8c4935e7d5ad1cb 80a211dc534219aa20f8f9598f9f9c3d82e3c01dcb6e274f956d9ccb16a30c9adff451fa6475227a 8a0e7793eb8a3e4d4c6efa98c0b528473d6eef2a655bfd1e85a12241be4b579174d784430ef9fd91 cc35d822996b2223325708241278be67ff609f349fd398cbbe8c5f3f0da99458371ed29465d02dc8 c803a5bada6e6d3f5216453c0412bcbdb995d76ab2dbfd08610e2f75429f6b3391ce3c777d6226eb f90476de2135cfe5d61416d40ee49bc26ea4d7523e644e9b15c741eff83db434a6721d6c5c081476 14518fd9882909db51795dca65b863a3f2821246d914686408cc5f6cd106af2c8f3d475c7cc600e7 345a4d7286ea2034650f879a0c6d952c5ad1cf778e73f9db77a41da9b84cebbd4b6f32a3c06cc548 062c85f7460ae9f9d91c0016746f1c44de724c31bbed28da1c931163969fa3ccb1ca0fd74fb53ee4 a3333cacb39d09216d9529d192f27186f88d272501c28faffb57b9255a1895fdadc318bf58e7bdae 120e604df2c22520d8dae736367c6a33500f6bb726ba47fac3712ffdc6748be06932dbd6df141eb1 0d7274b1e031f5588c47319df0a3cafaae0e3746fdab400d050859107b1b8f081993f644eb3dbfe0 c9faf6c17b8d6219bb1cf00e6628de1083f0a682de3f6e805a3efa405104e8a296644d508bba2ba8 3529eabf886b7251f51d9c95ddeae925fd5c8afa88c6d33520f7957e28d604aacbe4183518dd2807 5a2424f8d62e23d65a65e6ac1e2de23068a344db42497c5a1204eccac92a66d6161606dd2e3e6a23 f718455f9503f272911be20eb51c02ec36df451af6e53a02936d958596d3a70d2d75ff04d190d880 e8667e04d185b1002d614bfb4578a95eb49f94f87947f24533a7f323ad55980ca4f98aacf31583c9 d3508b799279da3913076dfb95bbf13e7cba6230db29a298396c215e618a2039c61ec141f7c4c1e4 35af42115c3221fad973a17224c48335b3380e6a9bfda3bf13ab85be046bddde719e8c7b2a319af6 bac07bd33d0751a17b9649b4d709b7df21d0eb2c4bca2f7ce7b8d1dd9a32d12da4278a7a051d8c14 a382424238eb37d8b6322a522ecbbf47b59171c56ee2720f07d62d862a83f275c08714d8dfb767f5 7ecb5df77bc9f04df434b036f92ab7171916bb469b9e760727dbe9dca7db65070581433b0d89b44d 94cc72eb732ec1ad856ef02df0515934437bf76881c7a8df02e31ad9021d52f885b7bc6c667375cd ceccd113163599eb7ec514c97934606eab355b34cc23e571444e00a28bca7764b4e1f68346ce8bbb d9ac1ab4dd4a2b691317e9d5f221a7d822f3f75673497f064dbad2229aa50bc33456c64c6e541f81 59df46b9a0deacf4f7b5c3c5fdced0b50ef4ae554f4f85a84ee39e51b9559343e5968bebd5fe00c1 aafdba4effc2d9e713e74fd1935517531413295202ede551e66c36bbd4c81ad58772779c479d007d 40556578ec9a04f54d546ef96b6751dff5ae56f53c3f05d541b57daccc44f45e4160132c3f9fab6a 791ee77ba54fb58195bc2b4b974666a816c3c2c72dc4d5d6a65031f5679e7f7eeaf9ba331b837236 bb833298bf80d2ddad80fb46f55b6be8a7fd817deddc7dd3775147530e35551a55418693f6d5e1c4 513bd04808c03a869e4e39881b6f6fdd5bebb86f05d827aa4bd39e57997dc459095c1db57ca387a8 b953817272dad18b733df5fc3dbd0a5c89e613807c0404a06dd402a05d0d03a09bcd03d0bd640130 b8d802701ef964487b005cf078002ed20100b74bb70c7ef51716668091be0ed085327b770ca172b8 73b4b55d7e73a0c65597ef608f035a82d6d5f6ab7bb36aa716a50e36f5963b092a6877e91427eedb 00dbdbba04c09d307b0c036a00ac322e00cf8f1b004eb01b8014231040dacd26804c7c1c4094b600 20cb8d032047eabb919fb5690e40d2791f405e189fe1e101c85b4e80ec13e47e6144abc55235f2a6 2d96004a668c579b1aafac3384c3825583d62c0d74ef207d6b15e7ccbea64db5e85b80cacb6f67bb 6f5a42ae2f0e54002920530021251340ccb90520db5d66c7ad160168cdbd00e800f800282fd600d4 7bc1007ad15800abd62d0023d65b001b0edf00c6e73a00a6fb348019d00cc016fb3580f983db0f7e 04e2acd5b45e7e21e55b479d9dd61d8e5c6e86387e63c1365c292e0bddfbc87e36b3402da95e87d6 aae42fae0178446e9939306300881fd8ff2b46ff08beee7009a0273106b092bbf9790c56ba0298e2 16012caa66163d9d118023b806e0562e02f0bdff00f007d9c8f0180244415301a2052e3250db5ffc caa7198aa30cbcf8ab9dfe48aa23f7fc7fe4d32df45c945dec11d6edf1a328bbcc9d39d9fcbd325d 2ab74d7fabdff8dbc9baee91edfc2aa557ffda725ed125195677976ed0389d2fd4383d0f62e5aba2 2e5b492a95bed51212a735e68f8b9ea01f4757c33e82a6601d42783cfb456656659ca6448affd550 87232e43a06726d6820cd36386c72b337158798ed6b546665dae7bdbf4ced0ad6e9ebf29f157e979 1e5d5bf3377d4988027fe9fa39e97c21cbdad92834cdd39da9d927ab02fb492a6071821fe8c371d1 99de8fa0bece1f8a76aeb3db8ccaf8ae1ef6e9ad5cc2c46d6bd511360957e67ff1d3697facdc407f 7f1a0ee80cb3af04fc76332bc94d86edfd0972d3f79d49466066e0a07c49f04afd7c1983edf36009 f6bfdbeac8095917f124e5ebe304df5726c78fdce28ea353ebbb481fc27e777a28ce06d66e43d4bd ed9eecaeb6ad883a6dba9b30bf361a696b75572bf00ab90cc6716ad447b10381c35ffcc744ab93a6 da074bd393c4a6e92acaeccc56e3d4016bdfedd507b55daf6f9beefc786dd9fcf53c08eb8f13b2aa 7c127c97cf1f17ad7cf9086af9faa1685c9b7be671eaecf8f7b29739873eb46d853eb6d1da5b727d 991ed8f5e0f65257165ab5e3d41dc731514b9ed12284be72f7f7d0ec6039f9de4b55e21644b8ea3b ff83ccc0b897a649dc4e5fe19c4adf10364ddf0dd14e8937ecde573dcfbbca9b967f36564994bc5a e1ee988727c7fdaa425c77c2a3f7dccaefc6f7d4f7e634aa819bdebe535e9bed666df5703bad953d c4faf16b47a2b12b29e318485d2e22d9d3342a0c517f49ef9c63c8bd4af940eab35dffb8db8ffd2e 92d08b731a4f16bae350bf48d374d1f82966023248faded4d8f453718d0c57f7ce23c7d945efd667 c99bd3ac43d4eb79bbe6fa106ef4ec63af91fe3259cd83c33df692c33b067b4730a2fc73292a92ef fa924972ed6545ab0d4201e86061dd1f7e17e9406e8a7cd03a668e9386bf168b8b0fed17507395f3 50b6dd759dab3c72093fe61cbfa031ff207d51712d7d9f8f44fa51692efd444bf5c1966bea65b0bc 7009d807985d4b81bfb7fcaeef21acaf8821ec4661d25c2e57407f176e9bfd53d8980f1f81fc1966 11fd462df8d38654f67b0fafb930b1796701bdd7b0f758bf471ed62830eeeb3154dd2136751c7f7d dd66aec7f835a797d78ebdee42dfe25936779179bbb610786b371e73bf48df3c3f483ff33ef9029a 36f72c097df6daebeed163c88add6d0f0d062b1789a888263d29dc7e8259a01c82853f7daf578b1b 7f3e2ce0fdede2cddbb7af6ce7beddf7c7f54695829bdbd72b4e200f9a0ed51ef5e6912b617366e8 51f67a77146d5e6e5ad6eea5ac2c699d7fce9281db9e75add7d0bcd063d134ca90600ed60dfe17e9 a7f3245e00ec7e3bedb11ad7262760f56aeeb77eaeb8b95a75609543baed25df6ce1816a0cb8c5bd dcd5bcf9bd6bbb8b1212b8597cbd71a81b7f9cc7087f99b3c5796a6f5807b0856a58b0ebd74bcd92 e17bdb6a17ebf02c89f16fa7cd7a3d95372fd7a369425473693c5afecd40a7704377732eae1321c0 eb00f512a67ef1c4ffe28f220dec00f9f46e1883432d5db6d62998c5368119bc96f57158f32ffa06 f6d2d3e67b80d91d4b7bc999387773cefaa9670bc774691d7ae0d66a9be0717622bbb7592feaa6e6 954101d3ecca65e361c875c3869cae81e5efb8eeaa75461f76196dea2fd6fe9424fba769a1f9ae69 b443635a99587d3b4d5d035b51e57c5ff8c50b2097fc7d7b28d349b12cc39b7b8435e3704515978d 0a9ffa97731bf4dcd2b8eb2ca93131af012c67379b9a6675ba9a35eb8f8d85692e9dd878969dad61 afe36fed6efd3dd8df74f77e7fe9433b0f4e8372a33225d7684b8b0602acd1f790d4cacba2acf21d c6556be7db51d92de665a555c8c1f2312269596544e11fbc805b49393fb516b1df5bd057ee5ef950 a1b914a96ec9bfdaddd473a9f8e52c6fa596bdeb95d02c982f4d66fd4b5b329e765537f032e4e81e 8f067a6e4faca6549bd84d0b2ff9ac3143f9ae5572d65b5def0e45b5de3ad594ddabd1572477f25d 3de524fbcec8eabd3e97bbd66c275d68242f0dd6d140bc0bb549065214ad062c8856bec3ffe095cb 35d4d3c82c0eb726f08062da2eb483335b292f30aff2720bacfcb077aa5b9d9d0f6edf9c79abef48 337079cbe9a369160453c1c1d4e2e2d5d52aec3550f9ed63a5d667d58322a5d5b3d2727a0f3961b9 9cdcdd2a65e962aeb28f8800a89805828c6869b129a2fdc15a78e5ce1fc109d5be404c6e9440d09d 9f4b833eef1cff8b474da0f9a4f82c8ed7afe70a5aeea6e7b63ff3c18a1b049ff79c27db17ab2b0c 8b26b2263bfac7e2b069c889138d6dd182bad1444dd9fb8aa5b40b8a2b2791bb947b959fd293d265 b33a481074b888f7cc6b10ada85f1051866b08afee0a16083f3f1100929df27ef313f3e3447df2f9 69b7932122b965ff2970cbda99ffc5b57ff3a8c3b67c1faf82ae0f8749edf62dffb1c00f978ac324 c9db4a3a8fb369a1a7b7feb9971bd3095786b42a521ba90da2c92a6db1afc83db5a94bc6a569890f a8e389d6035b0a2f0c5b0b6e59490462edde0540bfe5787fdca9f1649eebf3f9c3e1bb10704bb5a7 7234be59726560f4605701d866398a1f65d8092c8744fc2f2e8814d0bb53e88ce268e122c16576ee 79e03baecc1b40f89e19a3edd92086ca7d3a89a38aba85a3aea254d65f074a9e36769464662bbcf8 f06eaaf00a8f86e04e9e7301583d17bccf97639e1c60073e7fe7aedc32f43f5cb97d2eb3ab73adc3 729e32646ba387ccec1a82cf8847f09a2169314d0d19324d56e732ccbe0ed44ffb83339e7accd63c c9a3883f2888ff281efb6e69e454ac53d3789be8c43ce97909be68d52d955714466ac97d4885c5c7 9b1f0a6f90a785e14415797fa5aa7ca1a69b1c2de973ae8c44df6458765d7c6e586e553bb1b52999 32bbfeacc048b9679b69ee87047d949722ad76da2edd79efcf74e7b3684ece268867a0d8c959a4b8 5f9cc6ec9cdd603774b89431115dbcd8a83fdf4ed86f699699d91dbf0c60ca9d340e3f1f14659efb 48b746b12adaef4a57f0ea5d8c0fda5d922f4c118ea36fb8ccae9191ced662c66676acee33526fbd 669ad7db913e06cd07ad521448775b5166cea9804e7457f976da6430021cea0e1a0975cf4f6ad46c 9960d46cde62320c386aa6b6d81f24255362d760b1310c358f443d5f547bf66145944ddb805ed3e5 ac9fa8cdf16c2bf7bde029ced14d5118d592165f482fdf398d6388cf88e5c994667649516424ad3b a58fd7ee8c56cd914777493d9e5c0ac17ea21f9fb7c940ebe6a83ba1d6290b38c214b2c3583295b6 36e9b4c707123f572b24beb6900c1f9ac41720fb836f5ee7b73d6c8f0cb72a66ff3730eb43cca555 bc6b4d77d592315c565f5a8d791f65bd33da88d9c49708608dcd71512ad7d84dd5e9337b38c499d6 7c43d1ea27e1271721552746b3684e06c7ae4b59ddc177b398423efc964c17f333e908b7374934bb 95f1e7391b8c17f3f7643c2644739ccfe5b7191ee5317867e10cfbc918dc5fd85fecf406c8449b6d 7eecdb16863bdca5db9dc1cf57492f9c5e5f954351a1e820a660652d80ec65c7b1c5ca8bad5f7b65 a65d1877e85e97452686218fa847de6129945e48e4abbcd749677db749a09e2ec68b47773d1edb54 32ced35e3a5a96d3d268b241baa3921191c315dcffa65d0fd9e7763dac3a6661589d5607c32ac78e 87d5a146ff62335fc5f452c9bfc8c5f0d427ec6398768cf76755d4f860994ab333bf174067bfe2aa 052166e4c5e246f7942d3881364083420fc077c78374b1364e021e448dfdd1841fe7f7b23a5acace 6c44b737dea874ba47436e54dc0f6b207e27b6a10112a29cb68866871ae287f351c115838bf00edc 01f04e73d5c53bc5ea300341e3ed149afce0eb75823b8709aeef2de9157365c2d24ffb8e1e36eca2 da4acc54f8ec913d5785b598692faae1047a035bf20d8e5f64aead94c6a4bb6c8fa2dc011e95a9eb 70c89df2ccb0366d4b84786b6b44734658f89132beb300ae16e30d76662f674cafd5df58ffa6d5d0 dbec8ea033941150f89df391a7b77c224f836c214f618523cff183fa455c7564d69f8fb613877fde 08330d8cf6b45ae68ab2b1a5537e726a7d550ea6fd2162ea59da06644e9a2e47117bb80cd7413947 ece2669568f5865d5c356904ef92f618d32397c106cc5642679b938e2222304752188d90f9533ec0 1f7cf18017005084475ba10f85cd8486a884f96ee14154369d41947bae41940ec3194cf217cb8e38 e616a0bc626c4dd031234fa02d5501e1a2f88afa2957ebb792c92cdf5b91b99b1b0e793ff32013bc b0c20c289bdaee4b1d44d1b2f72d088838fab10b7fee67185ed8b9213ca661160ad798044d78d180 4a75d71db0f2361e54b1eaa9bf798b40bf315a357a877c8fe8c9cbfdb4d766a46daf4de0a55ebb17 f47aedea13efb56bb9e10f028441859f947877c9cdecce089b7270b5255f5fe5a2406e6a2fe6386b 9dc94fa7bd19551c2ec47b9db58b382fc481c737750bd145ef3a5869176050bd7ecafd6d3068f79b 4564d03bc43cd153d8d9a47bba4662776adebedf9b6e1f697a1dd319ae3b30e1dfdaf6ae506c63d2 146abddb55a1e59ef6cb96bb5ee45aeea2d26ab9068564e0b11f2cf2f9aa38ef1469d6f0f7455455 ded7968403ef12c7cbb7173d88ebd771e841dfe30a44db2423e4b59f7950194e663d352fd85dbd13 ae3a37ef7eea208dfaab3d577af936de19575befb7d56d8d460ba40582c9b8192c8b7cb3d841f446 74d6178dca60b7ab6ff2f0ab5ea7bd466d5fe98f6bd2e6f11d02b51619de6ad28ca8d424c9e96508 073f703905fd936a3e5d3b774c198cae1d813a1ecb4cb24a018af08bf761e6cf1f31784eaea18863 16bde327b6db4eedaa358b5b6edaa816967e5d604e5f05aadee855cf35d9eca5b5364c82d553c1ad 577bccb25bb9561e5805321b74f9010b5a199d875ec9ad560e25626be58a3ed21a14968427162637 322e2cebd57771ecf9f50cabe60fe6aa8e1a46f03e323ff77b6e2f84f8792dbb9c7039d626b69480 e3b20aa6b8ce35ce083886b77d19a1c2f6abccdacd92e64f6bedf35928db2ece16fdb4ab14a22a35 2f944529ccaf9bde365f9b1f2fe08e28bcc1a6df2de58ea4f42d729aeb441e069cb5320ff4afb405 f46f596cd4bfb9396000b2f0f7be2425c36d0df49fdc33c3f4fd83591a3f74bd3a38f30a348f8742 39b71c307d3b6a9014b12f12c9e8f3418162e93668e8b95df6988e4d16c366b95db5ab671cd64a6f 97e7f3eb8d45e6ba7a69040cc23697c13080c1febe000687ee1a183c66d9387ea4000015a83a0035 cf2800b5861c0021571b80506107405835976133002046fdfad000c436fd0cab4b8657fa0393ace5 2dadd3de89524e0849aed5daa213fcfdec0df7cfb882ba35e33d1031fad4b1b7a355b31c0a8baaae 2a6669e47a32b817421a303c8804201da500c863999fc7ac372a005dba2e007df41500576e2700ee 0d3f00cc1eeb003cc510005e5d79003ecb0e007fea4700a9ec0b00d2531100e9372500196e7d0019 a10980f0c2f5073a5f78cd1564785685ca32617f2e77f45d7cb4ed232d0ceb940a835d7e7befcc89 60df58ef9db03a8856f362e89ea7b953639f75cb59660018db0800acdc25003ed65500698e740021 3d0740c477042081780690db1300d03ad700d0d1ebebdc02a8a10900baed7a19d60980def82280de cb0880812b09c01a849721de65b8243f50f5876289c52b2e31c6a94a8e57f26e803d1b7215aa6ddb efce3c7e9f1a02785955acf9d52bb0b79dfea34337c1cc2cfb23ff15a3ffe8d0ebf95fcd7758f3ff 8fe6eb660f2c97ee0086b965009b4310803d2f2c8023ba0de0327c0070eb9907707b3a00f0798303 f06861fd9498882b7186d6fa077ff5dd3fc2e48f2689b31922e35ff9d4c9f7cbcf45196e3dc27abf ff28ca307a674ed8e89e05c8931b7f23b95b7d468bd73d22aa3f926a3a35ae2dc7b62f1a60ba97ae bf5c9e8dc26a73ba3387e4846c5ecfffa4caba83e3a2fba08ea059950e214c9887e2bc61fcc99765 de8fe90f32ebee58867890e182662676277f527bbff2e928e765a0b7291edab7475813dff7b82515 6e9b1e5fbdd54db57d959e6aff92e02a72e92e24fc7c19ebe3f360a94e4e775a674f56d99392949f ab09be9f1bc745db9b1fc169141ca8dbf9bb53b867d2e765570feaf9ad5c64da9b849d639bee7647 af8da6f71783a33ef9c17fac141a7f85d41f81fa8f827efecaa7cde8511483f58dbfe8c7abf498dd 2f5d8fffba836703d4f2a7fbc4a89c9095564f9c9ad63e7e24b9771c25ece010f626c8a1688ef13d f32446bbfaa24a6ee57c9bdb68e586bcbef0e86c3dd80bc1ca6ac7c738d51bb918bfb3cd68811a50 347a596804baf2f7a6a59ff607998172330351c9b0e8fd9f2110e8999575f3dab20ceb6ce4d8c5c9 2a0aab04df8ef7c7d191381fa84befb6671ed5e7aeee5e5f5b3977063609b50337dd78fdada8bc36 aa51653dd81e9b2bab79ecc6a97a8663fcf21e450bb8cc46e01c9b2ea9cf7411c664fe1256222c17 0815ae1ad437d396bf17b8e62f32c3a05206b49ca6acd54e532d45ff93ddfd049984b94af7127342 229a3d2e1a987228ea3563577776d626213d777d6166c17ab099c52baba16fe25451b3e1759692e8 a3ebd76874d79fcb1055dfcb49292e8631b7a98595efbe848056707f9f2d18df828032b7b3162790 3e2c7af2eaed99e55acd83d6cda6fbd06bff83ccca3c90a6ceb894bea84f377dc914f15cdcc0e175 ef6ea1245582eea178c2e16dbb7b27d6c632a157a8be1763627fffce02918f81f3e59207fd65690f 46e1ca6a6dc35aa97b0cb62be41288533ef58f350bf495dbbeba3823496bd15f81b0779b52130fbe cd55773e2904cefb64df1db0f62ecf290bebcf8b683f5b7c5eb5de0fbe92eab7cdbab10da6afd9a5 91be79094abdba8ade44a3da3aa18b7971b7ed19f9f5f52234e3b71de2113508e965a5bd91837d2e d48396b49ffb6a92f88b0b718f1703ffb2f1ee5231f1ac56fdeaa65afd7b5cc1750822e77c7ca2e8 2c44a3e980c7c5601e3a20392f55478acd8ac1c2ae22e4cd6ab0416926df72bd597b5680cc1395fb 1fa42f695f4fdf5e163a0521059d915af5732c1c0a979d98fcf8d0abe7a7f289a877a31e0a9336e4 9f72edd1e2da4239ef418c55f7258e4d97384e1cc7c785c0197f8cd57c2958bb39dd084ff3d26173 b3392c79d9d50f90b7b61ba86e351be3de4cb1ade1acc3809239dd50df73d266df2c5c0c38e24bfa 93d9f5f479f78dead8e58c4cdfc61afe41fad65528fde0b9e11939d9a543e6fc7d3646229de2cfdb d82ce397f508146455fe6a8f1df7dd5d616e6e7c9a3805e521cecbde6bfa7d373cf89ad9b565d5b3 a44e23b49a67683d5307bdddac0b4ecee639941fe6a06d03c6edb42d1bc808e8e82988e23a2e5bfc f48355e6d391a71fb4b0512f68d463d6d38af61a5363cc437ff19312df791237b91977937c605637 b701f68983267e59d626c4da3f174b27efe9937937f71936e6d143846d7e2d8e2c692f32b304b565 f3329c4e4da3b6b48dbbb4f40cab7558eaa973dde80ef13a4c3f01f0cd879e2ea4de6b3a6ed1052d 74cc8636611f881aef7a8ccacec299b22d75b74a36ad80f26130e8c8f25dc2fea02de3c80f7ef4fd b3dd1877f6cd2d5c5d79af6e6e59372a575fbfb5b7de7ce3ff5cc21de2c0c7ded8b9aa25f385eeac 87963113e29a94814a354e77d2bef22d66af4f7d60684ff3e4d0d3964d25d24aaab25157b89fa8ec e7f450b6622ea788cd7e553eccc5beac308f8974da4253696a26dffa02521fd100f19aa62dd17407 a8080f5bff83476ccdf1c47ff7ba9bd9fb5689e25b3def5f4bc9d52398c7d6996070640ba1729f9d b8a068665fb8a6feda06900e1c03624ae2475aa3c7ebaf66a3ae963b45e5e89ba16ccfefb9221a65 5f3e3cca91ac2ce19ddca1b98b343d9b2fa9af1f4be26ddce98af0411909cf795e13e6b811093830 78f36f3f6cf21ef941f85121457f71d58ec9372de15801abbd3500eeaacbc631cd2fb05a7c7396c2 6197b97a8570361d964fc6b38800fad0c7ab5332a2ba5ad9a050957bf063452af38c7ce4794156eb 334d3acbba21e9d9c229dedc7520ce86b7b588d46b5f555d98cbfd8780a36a9eff946e6d7ec43771 1e1c64336af0e8051cb5fcbcb822c3d6b922be81d9e8b3417f7181bb09bc974b403f5e4eb21578ba d9e4dda068dce635ccdcce4ee93a309e64f41d02faf0f34cb548498b2a3ffa341549290e64550731 e97caf90d200ad30e26cd59584b4d6cd7a6987cf047c2679bc97aa4b7e14aff73cc83eaf5cd84772 5c31d01b6c4cdd50966d73225b39bd16cc465f7cb78918016a54331850861063ea0f0bfdc119db5c d0ad61badd889b7a751f7e387987e9b137eba82b3b73d60b427dd41d455a74d0af6a9d777272822e aad2651876c43bb5804424d90f05fc1a923f72b79970fce899483c384fa7dca40a5a5c71dbf5d9d8 64d62c4b0667b65a78be994d32aa3182b346e843aec6d172e03ab4bcd7ee747bf2aad06d7a32989c ca023a49de13e4072730bfc736e8d1fc2e044b29376f78ef370bce797f729fe921b93370520fa7c5 712550eb5e379135147949508d288a686bdc10881ed1e517be80f2f9823ce2268c3c61e38dccb1ac 60c96c15da9b8c60270ed3602a117de852dfcc145abecc9f74db07cb9393c80f26d3e68d99f41271 4e5d5de84a5da7db32650eaa3dcaac43c82f92a2b2c1d7c087eb85aa346b7ae08800ed56d4be99c8 a9bf9b869581af0aedd0913529f986511244a477e1d50272bc0f00156e59005a5c89c9f759765342 992dd418310dbb31a10f182ad04a599ed2edb5634f4efa259c6493d47ed2cff30fea7ad8172953ed f628188f68f209c01669fbaf136907f6f7e03f8921d72e89a1d9d2f55f1c369ef3277b3fd053bde5 c4936aceea554a37fd73cb6d3516aaf84abb2fcf2548fb7f6c9d57bbaadad2ad7f0be680a2e49c25 0a181004c59cc5acffffa073ad67eefd9d7df372a10efa40ecbdaaf5564567668c9bfd932e7cc297 a63496a58ed65e82aa89cddaca3ed9134a57be7f7d36f269f992644f2f6b328a518e74b97586d220 892712617fb6e2a38d5dc5d1db2f8874f4690b6fc39184a8591c08dc617910b8855814729d595bc8 b117e22fb6274aa01795bb884d7c56ff1a2e83f512c80d71fd75ed15c4f7d66950696c0d4af8c818 8b85482f7c905d67b9cfa2fdcd5bc92987bc5251dab607c9de69844957744449c3dc5e9488e9bd23 06a59725d2a7565f787bc25888b87825f0b9dbb73c4ec8edd81c3fb5e3262fd130cf17dfab1e378f 950d371f54f2dcdc34a10c4be22f3644bd46cd7615148b59116b8ef6c82bdf7fd9cb8bbb54571bdb ab87914923b5912e7697a38eb63b7df76f54ab52bb28ed5de125f728b4240d3914141f5b0e1603cb 2485f7b1cb09513851049edd9b421e7879bc64d502be04b1336efe8c8f9c1a7ede5c55eb80ecba9e 32acbed31d16ec56962ca86c01165489ef73a3589031b10c5dfc87d55b2ad0bf4283a8a0e61bbe47 2c73bdc9787471cc7eb031ef4f79ac4f0b27bfa3835c4f392cbd99dc1b8ef6521691dec5e072c809 1f295713f8451112f22e81f3d285fe567df3a58126710b62a073d5f9c461d7eac9678d5a79ca8297 ce9ed90d9207033d810a7d5cb824ed6a552bc37646bbbcf7a65dec0dd22e4e63b4db20fe6029a977 66dadfd58970d94b1b43421d7d9b037bca5449edb322ad0dee828c3bab6830548ee5664fba49b940 0c59622dc4a692f249d07df3a5cfb0c8758c39c8d51a679835466f926d30759ed97d08956909b449 9f1a5e9f76f79b98bab4ef2baaff61bf1e280a8f2745f22e365072544c34925af031498deb0f92f2 7ad50c6798a4b433f2c37c2d64eb32ddaa5341530ec19fc8e06c4334b5863d6cada9dd7ba81c297a 20f9e2ca1581eab0c7cbb355f293bbcfd71dbb119a7766df8501a6db962af42972201acdf750ea92 4c696a201f44f27e7a68e4a85777481a130362ec67ab2b275f4e44ae42e7f0c96ad9c6c56ce6c48b 8d5b80178bf32b5e784225bc7054bfb74076ecc13fcc5add19174d6b0fcaefa172bd57aad43e5d67 53498d8f5d5a77c0f33d9271ae3d1426d59ecb69edbcc56433cc903e97a50575459d23454ca70ff2 21a579923e7eaa4484962082bb42dfaa6f2237e4195c2ae9125e5c442636ef9cfb58659b8bd0d550 d9a2f5e7e4816cc7cd3a6271330e81f2421f69dea123d2dcc779a439bbd591e6bcdafa614af0033e d4d51e31088eb59a6be4ee1feb36bb7ee3343d597c36ca09bb4fc4b154f1f94a43f798f6746d5164 cded12d13c1e1379f8b6c2a549f5842d8af00353e7420e5dc34605d5d3104276788c22d6fdc82050 909761b70a5b30bcb6fc76bf319db5b143e53b0bb47c582fb4c84b0e879e83b1053d87fd0df4b4ab ef1699d8d516e9fb8d1fe2c81f4923b740e2bdc4d857bbc7e7f263b2d13cd56a9b742b0ffc7b2214 dd62c036df4c8f22ddd0c6a5f7a5831af2e6db221cb17b85103ee5f1058cd89d43fbd2ea5fdbb817 be5b7774536c51d33b08bde41a028dcb140d015a476e4eea49b759187ec286fc22d78df278fe0497 3c0381b5e4ac80b5d926016b0e75013b874d01eccc2fdf2dbcdf71bcf03c65487129ea6edaa3aa8d 5f061fa3989f5fd5ee61bd13dfcbcbfc2799d3d80d1b1082687751e3b6d7db03a92241e3f86d3427 0fc26f161971d2987dbc55a3624c8ee0aab1bf81da21f7adf7ac6fe14ab96e7ec456ed2074c95a7b 3691aa67b5e054d1548e2b437c71a8100fb6587e041fbacc0041af1ca0e6befcd0eeaff283afe47f 189dd89cd82f1c43d46963dd9a19157b39ad7e1adee4e13af92ed24269ba5f32eee8119380571a62 f5aaecb6afa8a743b9782b81ba0eb135d8fdc8556c0a752b7e891c56c8851e974364b82c3397d5a1 1413876b291f7c80626953ad1616a68414aa8780cf1bf0c3ceed06f4b74d5bce26f627e0187265c0 e5001670d1f51038f6f86586e1e18721dd8d04af3ee8a0f65036417d9eaa7915de3a0f31de276776 df99ad299a3f4cb12ce31ac1bd6ecb83f8856282463b52aafdf0f6753f94e24f0dcf9b758fc8b59e 0301f0d88906a0c0c901d05c2bc860cf01b49e1e0114c45e19e21a8036402243d001d0667b9461b3 0350c82c0028855300da4abb00da766619b6bb1fbeee87525e157e7dc2cd98539a9ddd5d2d49afd0 7af35a145fe92131d91125eabc40e0341db700b036008d47d3aa0e64552d89aacde69bde1803d027 8164f0b2b8bd0ce8197017c05a7e9821fd1a2e018c268f00a64cdf00e6c06086800430b799bd355c 66ef1a0b07008b80ec7d514201d85aed66b8c600b669af7ef0d67e4fb1fdb54ce94b49692bd78a55 138b9b7e9e4929ff41e63f936f5929dab282650b888c103407835e9548e666b192f6851cccedb3bf 3b9d65b89e1800cfdd5900077115c0b1d80370fe1302b8c12f00bc3f3f01f8a4fe06f04b0402441d a5332c4d80c084082084fcaf6b2f612caa0061f2d96be6c306084bcb5eb366cb1f9cd3b8a79993bb c1750ea1874b313884b8dd21ae5080443dd02e5fd9b7b8ee2d019b9bbd5f09fc73b7a0a76be59f31 f10d01c02341fee9d0ef7ee71f03f21fe1f7d3058880f101e2b05d0024449f0152dae60032112180 bcdd3980c24317a044720e50e6e30150c3a009503e2a0054b2f7006a864f32b8eb1fecc736f81a2e 8d6a375114dfec52fca60e37a9b1b6c9a1878a99b6b807bc0421b436ae30fb7a2fbf5bdfb4dfd8fe 199872cc2ecb0376fff1f7fe04dfcb21fcff4dbee39f1f9a3a231f806e6f1a00adca2c402f4a5d80 7eae660053d6ee00d3ae370086de0800a30a6e864594e1b3f8e15ff9f4e7eb3c923ff93443e0feab 9d6adb0ca7af66f3f8c8d7fc835fe56a7769976bdd16ad2276ab7a45eaaa5f8bdc151cd6a58bf52c 2a1728acebe9916d5a293ca93be7546cf7cfd8bc3d3add546e7222d7daea183606a7c3a7bb791ff8 b452df27b846ed16f4e8bb47b05df393eed6c88fba5b30e9da7f910d3041ff1526ffcaa7bfe60de7 d1e323bd678fbc56d9df4b56fd7aabbacdcf151c80c5cbf72177a9f32935cea9506a9ffb05f09be2 feeb96cdd3c7a75ee28ecc2e2f1e62e893c5b15eded84bd792b3539ff9c1d6004ad1c62e82cbf551 95d2556a24a515b6ffc0cb9b43b24b1fe6c59f5ff62fb2d175c09f30f9c7da1bc2ff614066a68e77 5b34abc3ab9e96e28b750757291ce5f7672cc95f4ee4e2f93886b5e7e710374ff943ded997f652ba a9eed4fba8b6d5df26b8d9f14a7303250abc76cb0abe4a35ed1b0eaeb06d575a92c7aebe0891597f fe19dc2773fe29a7b3849de46732f06a240ba98cfe41a7f8427ec8068694fec54f96fedd02907453 f7827881fc42165300f7ee899c5fbefd3a0f1f631fed93f624d9557d65bed971e86a7d94eadb35bc a81c56fd5af1bcbc19efcb92dc3fee8ba7937b2f98f327378fb14f699e1fe5eb33e95d6d270b1ea3 926a224953a31c39939d9e9b4c6cd03cc5477bf55dd662b7f5aec6f0e9fa1ff8473e656bf97f65e9 107ef0f30e9ec261a575226767fcc0ef2276bb266161034df2ea2a556ee6d20717ddc5d35e67d7c4 8bfd397f0dc35942fadff2b899f48c67c9820d97490748b6d3b5181fa646717d99ecd4dd7302ad2f f9d86d16ea51ea2068d4877d697cebdfdc31f95017e133989f43e6737866787c824f7c78fff0ef38 ff19ec9f5b40a85df553bb78649687f74ebdc885b59b3f9597376dd65830470f9ee7071a312b050a 9ba81f4d9aae05ad330567d95c67b74d67029dbd7eecf283518ce4c75194ee36b3086fedd7e3dbbb 701a537ced2b3f84e18e2b8440cb6b06f11b6083826e7447f23d3ff7975dfde26bf43ae7d7de31f0 17ff2ad259f0fa3ccaa52bb8181c8f4f199b6fd7b8bc5cf54b9bcd2234f7d759921e7249ad7bf86e e44f1bab373c69dd73648c568a7c3488da6a44f4347b1ce44d6f4c27de308c5a8330e44e9369981b 9f5681943bed82e2ae761da92de4e3afc65ad5d7b53d36dcee1af210a2fcc1c059c1bb7e8a0ddffd 7e3efd06507d2c59977b37392efdf067945f03f2f9d2240e079172d69b76a13c5ae143ae3f9f8874 92689ff165e228d35c8cf1dbfaf8d9dac2e1873b9021bf7df0414215d5a0342e1aa34ebdd21d55b7 ed6f8d87bfb688910f3eb978b80bb5f9d0ee8cb683e366721e20c4e9d5bf94da953ede51e09e7fd9 883d6a2af4bd31f4d97980c7bcdc0997d45c31e757ddc2d4aefcf01be2d5e43de0c8aba3c7063e92 df586091e593f1ac6a56fa532bce4de23e4d1cc76142bf4261ac9683b2a53647b5d0c27c13e831c3 bd1988c3d6215007a7606b0d90cfcae95fd68f619f685c83defd509cf668a6bef45e1ffae0718679 f772f8f6fb4027570c8a2d675ed57847b9005e7725869bae5eaabeec6d87ad64906bb655a3aa3f3c 5fbb3c7426352ebfdd57d0c71298dd7689c9eea713e4fc1846fecbf1c389b5dd0415e47df7b7a57c 7ed8bde4bfb7c0a057acb6fb571522fa444a72bd002725ef3de3352f5254cbcbad54d79df67a43b7 781b8e9d79b29d3bd5f279db5d9d9bd72ec84b397b979f37ed2c5464ac233d712c38c697666aec9e 66bfd9a898d8a155ff96ca63f35aed87eb566d548f82c595560fe8fa98e960b29fa08f71328ea8b9 1f1625b037d26bfa627898f7ce835e1cbdfb643729f79868ddf4f86481b849eb48b925efc839eaf5 2839d5e947eb1aa5cf7791ee82cbaa67efbe9d7ba12b135bc7696f6521a5e9c94c4fb9b789737cdd b8d70fb4e1ef384b7f51afb91ebeedbbce46fb920ef0f77a16699d6a3fa4290ed4f6f55ab3bce4d9 f36bea9cecfdf86328c9affd477d381a81cccd1dc2dd67dcf77170d763dbe4dd2be4d9bc5b6ec935 a7860a50d718caa8bd2f1994ddea189c75aaf5650b31fb9a7921165db3ff38f68dbbfa0c8d510d9a ebaf01bfd7c7e2f4a973a5fcf777a34d5c15d7442e6768c5dc78da9925c8b5a3c88362a7529ed5ff e24cc92f7073ee1e4af38579f8c4b7ab7c08e52e3d1b35202b18228db5d7a7f8beefc5afc5d295af b7d4a92d9eef6ee35af86ee1d9ad2750b73cb6d0322f93326612c532658c9c066fd03022ebaf5430 744ee8387aaee0fbda647f9c6a4526bfe9cc3e4c369cf5a2a4ae081055b547d451eb0b2e52b69dd7 45d96a5a51b1725efd87af3af83dfe2cf1ab271694660d66f88ed82372086a2b743ef46a4cd0a756 b6eb4d28bce72c8742d2355d7b6fb745f766a1aafb3189fea86c8cee63507fd3e396ce45134ccfe9 7b5a13777b5e2bdaaf6fc8d199d32dbb5389909ebad2ed48d5b1f54a050be045b11439af40ed1496 0fa9aac8cea412cab0383bcb30d9cc4be7a758ff8bc33c7781969f64589e9e0cfd3d16d9d6d1dfdf 2af3014140df8619bd5cbee3b915e4deed36dd42649dc5daca1cc28d931108f8538f1434a7e7ce62 559330b1d199fb0adca9bc145c5d8dbb8c0ad607a2b2ddce34c51abe5c0592f1503e968db9ec9c8e 6729e51b80d4cff7bfb29d84ed2149bc7557be7873bc93e8c38f9ce8d7dab50c68fd87bd4eee5b8b e4e554268311f30e2bc2eb30ec951ef3de5bcd07eebc8cb85dab3832ad733c199a647b39d33ff9cd 4ecfb72e57ad849ebe7bd21d35c915d575b9585341add854ac6d115620ab49c8ce93e064786166e1 52676c4b7de4ee4b780e49c4dbd43d88bef5fc88544b6e0acfd75310c2a83710588138082c3ef908 2c91abf29ffbbbf6c3371c3cd606d07ca959d578f486df417d763a0c869dc3dc9bf6d2c0d1d1b26b c3b0699a649776f538afc49a74d2569d4ece3ba946a97b53766e0428d0655a925d625a93d260da94 faea1593f05a965adf2ec4372310fd81668a94381b082fa830155817d90a001b3ef949ae5ce7856d ccf1c24eeaf30525b7cbd07d73c97b5aceb0aafeb0c11e5c73b6b9eab588cf7d5e23a8bd3ff683cd 7ceeaa7e3cea42c4e9eb7e30fd026ae89fa8686b72ab19766ab9f65cd91ddb3ba5c58a571911e9a7 d49ff339f15e95caa29ff61ac20b1fc242789f93023b7b0802e0b4757e022b1e2f7c66115f98006b 6e6676ee598a79a971e5e7af2724bb1c975c76e91db6ac86112f56039d5286a0fac3ea0513507290 d4fa5892d2d730c546871e7f70668e510e03abf7f16c83ade4744db6224ddd00ab81623fae53d9fd 3cd6d2a0993b8977a7f20d3944ea0303c258c04a0230c36a59dcaeb779b13d24f8c279ce710a7f56 b94aa1d265977b2560b5ee6cc9d699c695d97c0665c6dcb428a669aebb4c53eaad9926957bd0fb17 56a0f727a6fcc37222d4bf37f4b4e792f5b0da899f839bdf3d783263cdbbad7a6764520dc5d692eb 2a1b25cb2b4aebc0d8d265ed84e2fd1e2d84d73bda099cbebdf093ddf9c517a94f819bbdab55aec2 834d7605f218abed2c86d952c1f71660ccd7c1629aaba64f1f74734e3be0fe4cb7ef5c913a8f4e38 d5630c93ea21d882422fd31b856e9e390add82e51f7e0511137250aa05e0a6ffec87637aefd60472 665d486aa44f2aa8dda91d87df8a15a5ddaccad260f5d1457a86f942ae874d7829af6f38b5ac1ed9 95d7bbb33a9a00cc76ba2b3396740319a85c826947ab12348cf20275bef6740a93b67df256ae4cc8 e1d23c90a4f7ce114fac8b10c1bdf63d0d11ec6f332298ab970ceb1c110469e98779fd5d84a358c9 227837601f3d61dbdc77f75d70668e3a8d5167393c9b4a77a36a12b1da494224bb125fcac50e5725 d390053b9f39636dab3bfa48c25f7590769ed28b4a3b7681ead7fc2a7933c326e9377718491e3e2c 11b2904ab080eae09fcd36c205a8b8c1f347f389255eae89c9e858c664cc9d6072237fc2e402f3c2 a4bb5df82171730f642cf7e26fb1df105fb71e5ef999fb36edfdcc8c9cf01a29073632a5a10a7604 def6254eddd63866d7a975e8e3a433a0d2831b533893acb2607f7d205e8df44a8487fc9b609d5611 8f3f541d17e24e1b9b155c1293678befde1a56764a16da49491fadf58305b2218a57a4115855a411 a23cbce78000dea3fd3dd2d09e0fa4c1b7f23f4c71e9810566992df7c7fefdee98ebd5ce7c588b44 9b77f723b937185a221397346ea175beb5514c0bdeb0143ef278e2d55b5a0457007c5c6c972778e1 8caf3085170e5825efde504deebfd17a6b53424c6f5f479a580181f7379c81db94acb4cfe5b5d746 f5d2a47505ad636bb0cf175b4437a2a0072c7efd02d0a35658b708d6b8b60824fefc1073b539e67b d342c99ba3e39b7d9e0d7706dfe9271d7db50c249f983a7c923c75b661b1323510963c3ed9e548ac d2790b687d4ad88805993e7c70bd188681c977ebbb7d9eecf7ed9e045c5bd745e1d51aba74097a5c 44100a0603b4f97e2cb96614968d66be26fb8de9e6b86e9420f205ce8f371854b59e0ece233101e7 bdd9a1514c1bcf1fc215cc208340dd975c1032bfed742d325277d93f6bcf94e36e1c8a6c30f5b8da e966d228db5288a8e2f1e82a3c53edb40a632d7286f150d8764c8879affacd983f844da1909b3592 6e7dd390dbf4195cf4c407d8c17af9fa3a59817543297efb75d6be0f74abb540af5b3dde0b932a42 1be74afaa9d52afdfe42a8f4cd5e5849a7b975062efd61e4f81fb827cdb44217be103723f6b15d67 53b417327ef2c6fc6c1df5192797da2443d43a989a88df0e30b0db0c28284c9e68a36c75e0ba5933 98dadef4955a9748bbd55370eb573db63aae5c36d8bc3230d55df97e702ee591b3064a6f205f2f71 2286158b555e2c2846e4142acdf62caf39937bbe5ea3a17cbddefafaa1f31a178ff2cb085cfcf0b3 9abb0dac5df8b566d1953273544ea9b014b9a912716061ea53c3e7cec14be7bb86b4b8b6d8a25287 6928dc1aab753705a8fce8996051caeec9822a1154a15a32bf37745eef787a1eaccddcdc76730f72 10544800c76d6d0118316e00cc01950c360ec07cbe9321080058a08f008cbfb31788090bc0a4e001 3014473ff4e40b02753daa523439157d75cc2bf41521a5c79e5af3ea4299d2e96a1810c224f190bd bf375aa363516e286f98a939ae8b979fed7dabd001ce15e0b8562b0082477086070d20042a038838 b0339c860022b5a60062f9fb0c9f2780d8c637e4c88e370240ba960e204e3104906073061057c9fe 9057633384d9c77bd8f407d79c975bd6432b967515afbc95deb19a0a49afb5fd63dee6287d8c6ee1 7ebf4dd627566351d82a3597ca7f75e87238a78842cde3a02c9b9cd600149ed70194cdc100da1199 0c1315403da09b81f201741ccf0074553a00e8d97967f880009a5a348001152bc3640260392a0530 b050cf107300d6c09c9f40dc98c63f74cf9f1a62f2d34aad63d3d59c3406a03bb7d9b1272a80e915 b6ba4a93f6ad2b0f9b72a7dbad9df8a053e688059f37c723144017951680956804c0480d0330a98b 03d870c900980ffc24552ce93800b65b8c00bc00cd01bcd13b02b85805007c1041003e6c72003e5d 3a009e083300df971e199226803f2431c3d905f02737f9e1f7d04abd736c3695410f2b0b6a91fc30 98865fbecb9a8c2b7b047199597366d7fcdae953b4cbb96149c91d96170ac0ac010160db0d0de020 c0fda3f9fe0cc8df6e0f3fa72ffe5e580001834380508339408495d33f1d27641106c85959f9b598 d8c743802aaa3b802ae5730005cd3180a2442dc33e7b81c6e73f9822dda03a4e8d46a4e8a637385b b54be4a4d4ba239eb4da4045cc8e6a3d98f64a92056b397855e3fe195836a69f0efdbfbcc73fcdd7 ec0dff3476f823fcd6dbd909b9590a50115d00a8f71d0168c657003ae44700bdcced01fa342d00f4 5bc401a60ae800031bc30cc7d9cfda9b1dff954f5de81f1fea7fc8a78ff0df2603cbeb232e4fdef7 a43e2dde4bd6aa7eabba53e8aa5fa6c8654744f8c57acce9f4c864f39ef3998be754982ae77e61ae fd9154a7e6c9af6cdde3533f0c8fccee111fe2766575c8f788f3bee4ebc0ae3adeb6b7e0f4cd6e76 32adfffa21fc9fa608ff2a915f4be77f6ba7616e3af8f5b95d44d1afc9c04d3d6c8f573dddde2ed6 7df54e9d77923ff7f37ee97493b35b835cf61bc7b06e434766db470e31646387bc6b907be9d26176 0b42e6b7fa0797373b81cdceafb0ee2ad5e56085ede2af5f60e9b7c1d7e2d9ebd41621b6c617cced c6ce3ffe8ef98b7f95d39f26f9d37c8f6436caaa7497d66bedba6ec7cec5ba857eeabce2c9199bfa cbe3b3636c0f1f533e1ef20ef50da0f6a57eebb65d53f7fb66c72d1f1b683a7bafdd5204acd24e50 58f56bd3caf26646f5257998b61621bcc2e79ffe999be7478fce4cfaa0fd642178b3a453acdda76b 552c4d8dea08ca306efdfcb23ffc1121ffca92bf4b1b23b705f8422ed6754c9e53d613fe75c912f6 bed42b7a5b309cf6d747d1f157a9aa8f9737838997e49e4c16611b5b2c9833b99ec718b69be7fdec ee4a28f2fbf39c95c6d83de9e4c8cf742d09a529b8301a13bbf66d0265a6520c1f21274added32c2 aec5747c1b12efb14f4a85bff83ff2e9e32302a5f448acf2277226550e79bbf4ed96b005031b5ac3 331c5d66bf3a6a11b60afc3c465ef22c212edaac149ccc44fdecbb4935bef4a646e1349c82b37b38 b12b977802ad5eb3d805efcb28b55fbba8df6e5cc6b71efe1e9337ad1286d4180e3e11f00d078398 ef0f827c92ce4789029f467285b9fdc57f0db5005c41b77b3c866569bbabf617c7552ad7ce8ba779 79ccf9740ecc4aa3a89c54a3a8310593009ed8e520bb26daefc19b31bc0df8a80f0da5083b06eaf8 e64e8d317989ecf03958f542e6b1f2834f708e02fef3c8462256f6a3d29cb8fb0b3528fbd5f5031d 1a4d531f8287cb68b0735bf381cd76f68316c0ee06ad9abcff8deedf5b201ba5b63f3ee552bc05fd 91b7bc758ac379dc5e45b3921f2da646aeb79f400beb12a5a6f41cdf824e7e4c55dd6a18f6fd6608 14622488670b2210ba293b4ace47218b6e6fcaa82c544dbf33ab7ed71bbfd665fca1f1b6a6833d3f 5c0fbae0fbd63f8df8721f794fb1de65add93dc204a65e50140e1e7db02feedb91d31ffe0c710d8d b30b2943d38ded4fbcc5f3a32bb3445b88d375a76f4c5ae2f15bb4140daa9fc5982e960e21a732cf 405cdbc5917277ebfe8a76215faf24d870abc7e4d0c276dce0e0efa481233fb5fe79f9b4fabd1ed8 eb5d6f70d01b26facc7bb6a2bdc778e787fbe1f99acbefb6b493b4b9ef7fe3945697797761e0e76e 0797efddea9dbafdf064e2ddfbdc3f34f7bbfa145e2da2e1c84fc08a6e4eda475f8c2e8f8634a63f e13098ee8ff391bafa1c7dfd5e7d0ced4a233770b156a59ffa2db08f97f16f3551efb614b235a1ce 52def36af01e4b1ab2079442c38d170bc72d20a9efc8d3d2d4294bccb6db39f66ef6867d566cb36e 91d6de7a5b56971acfccd30a389b9ede78fcc5b551daa4c75c2a7e7d362b5fd8ad92f5450be27e22 75c72c612b61bef712478b2ee5f90dcc980c4e54b8ed5fb4c5a537229397f70a37852ccebed4dc89 7969ba85c3a9edccc202e1540080ee2e372db15b6fb6547bf3906dbbc974be5e75eb504d22abddbf ad4cef01a6263a0f4bc610296106710974fd3115139d298d8f195e4fedbd383d7e4851affcda19ed 4a964644ee6ae2394438e638d8092a5543f3cdd68c1fb6ae17bb3f18177e7a1a3d6c2d3c6e411d5c f122df1de5c1bebb2b552b76eb6bad6a5b0dad6137ef23c472e8016eb5df13c6ece94bd1c4c08b66 0c4705c7201536d0839531d3995e7ad6e2029ed7f8fd1ad1f20edfe9246cfd57f55d9a28870ccb67 86cd4b5d10a3c729d8d681f5009a5e926d8b5a4581501f072a5472fd6696bd0fd0ad27f41eaea67a 7c73d4774bd5cdd4a9e2974d17545e671bea030fcbb9170133a52b25138b8adf14d7f0c17ad32077 28a23f6d8ed49997c2699fb1a7684216396af9eb6bd891a9c6b4532ebb0775710600b5c61b6d6553 c82b8a319b474a436dee33741ff2be6a3e7f38e69f95fcf2cd46b7295caf7d97b5719e7a8cfd1dfc f4065854d37bc1b32b7979a2223a6a4a385d70af84d631d2e666baebee4cbc35480d8a1b3df5701a 003a2bcd4b5abc98d7b402b26a7692cb15edc8d30aad2e4b7541ed1c1543ad8593efc68a6202a758 696cf19dbc1f266fb9f5aa35a5d37221499eae879267e4f612da20ee125aa09f3fec3b9f7761319d 448ff82615d6c1c23b4743d7487bbde7ad60b8c9b7cf7cf53d656cdb3e1816a2bdbfb18071a772b1 fed22a4b9dbd12474d78b62f9d198b3eb3688fcfa94b532dabb5835a57368ed3524c668a2b8dcf99 93bb664e95db04e74ae7e2249250f5b216af88f01087c0bd21125357141e3216084179fab58f0a41 a5781346effaf3876d7734cdcfab8e7a8f42e7b21e19d37934c0478b9e37295d0ca773684ab67d73 18f3a2e9b23192a69ece21f35013c5edaca3a8abb5ba4caf27b58e5faf8a397abce4fdfbf975dbc9 ddb85496db06d690bc3b8f48e8c8a1c5abb292c5215ce90a8f3e31120261bc149866e3cebf0fe33a 1f8514cff35c7ec4f3a8b5e673d7d525c3e3c9e7b6e7c7fada73f2333065ee63215eaefdd6c2f996 2bf45e2bbfe72ab589de6d9c1fa28566938a414b20afe710c6eacc8bfc405dc17aac6c7175ae3467 bd9dec547a27e9ac0f6f12ba1bbec42115e745e2b5ab0a8f55be25043d8c10184c17f84f7e65f2d1 befe2d28e3f36d71ce4ddffb0b27c55c852b9900cb2e9ac9805d94b0558661cace9ff3c70fbf5632 d35301ba874a61bc1ea28815f572a0dd73ea84a75be7fc5634465790d6734eca74d4c25353eb9fd2 57e550a0423590e13638957a3d78250e7d70273c5ee0496038f0ca7feafc878fae6e89e7874983cf 4b37944b5a30c749275de34ae36d9f5df0cd240bdfc6295bdd37cbcc7ab4a1198371fb8c8194968c 5193bece1406dc58f71f96718b2f4c06e5e72dd0146f3d200652e4ce29a1d76dcebb86392c0f059d 2f7ee88e2ad8b80ab643493e82bbaed43b2d06a29f5b8f85a7749c09cc225df331f239f2f9c1e7c2 2512fc7d9e342797d802573af64176e12e10b606a434b39e820a63588ec734a0434cef5edc91b6c7 8702dde235823a155a6e86f59c3a3eaac70cfcfd875fcb9b78a41d6f2373a5adfbf4838c9cd518ff cdd0e735ad1b4c5de2b5127ca455b02be0b20b52ac84bd0c43786e0d8fff1c8d112fb0fe944b26e1 922b17e31dbb381c53b606a70f660394738c31c1ab4cc3b4dbb47de813742b4825eac4c2d95a520b be8e2e0ad9e676e4c5ca929a01d5c648fc74b2c92ce749487cb5db93f8ba7afb61aee59872c40d93 9bdfdea1ab5eee531a77ad69c13387f7a6ae4d9f20a71a059f925dbe8689b7e79d1458f325f34258 b37f176dd31ab04b828dd85a20cc18b36a6c98c6da3fd1dd4678a35bf7dd873a8d5e250aadd61ae4 65cd62e4c0e8f324813f2d6214c0238256a335feaeb5dff878b36ae3e3edc0c0c70932c1c7bebbcd b0fb06b7d9f1789d991659191743fb3ac4a8f6da2b19f9c87621a0f793cc3bd575cacac7b3408a77 f28e0980a9c1dc4c1df3ac262c7566333bb9f4fe7c1fd16da130a1bc597941a10ab223afedce8524 7ac6f39be23eb0519e08f2a73afe965f301eb52916e7cebe8e4db1e300930acc022bce0e0f74aeaa 4d74de69abe89c3d8dd1398a6f32d867745eb7d35f779e60b511aefd27fb5e396b7b37b67078e3e9 05f4fc2d25515a8acf8877bb4c08c0658c7215b8d962cc7d05a7bb6f4ea67aa06292577fd027c9f2 6c4c04da32c1dfdbcd0a8f7ce088f354e3864dc7ad3726695a192bd5c710aa5a1b02ad522d0559bd 5c0f01b9e377f58477793e85ade45983add95c84ad401bc196b359c0965b3ec0db2395c68ffba234 8282daa52734e7cb6e8beb87c64b187b9da534d7a54be4b2d9a407905cc511912c605c35290c1db6 8827b465f10f7bfd6ee1e1fcb6d2c512a8995da0a718a10b5699a11dc0db206b737c428ce6fe8180 c75c1eb6917a0d6e011ada76c531df464a80d54a179d710bafedf6d06ddb2942b71dcc40b7e4d187 6ea36caec0eeb35d86fdb76225caf78ce2b0dfbb5fdc65b5b7b42e437bac8bb4e9a9661819629077 795e02ee14b35dd328857911847f42b08ec94415463b8ec42346ac7790861074617bbf1ab64feded b8ed7e72f3361237b7ad41f6e57ccdb0f84c7942f7eeb40451e733d41ce345b2c9de15b531a19783 865865d78d427cfc8033d1c5c019c9741b052b4bdd0a767dd51012621bac4fe57c7fdc1aa74eb320 2d4dba8387596ecd79f2d954b3799a32be2224bb2eee691adeb731221cf65a58d9f8807017336aad cbc9455b04b7e0a047eea642b45db29be353b5d7e43c326ce6386bd610a7bd4da328cdcfa0727cbc c18a0757eada55856b5b32e26ae6abdead1eb8eeb71379d5c9d7aed576b287aaed51a857bb076894 c19a550fc560f9b3c47b8a459c6c34aa2ef4a9580ed56d42f4a4e1413579e9d5977f7a394945451c 9b75d436bc7f2fc016b1ac951b536752015561f98da1c16ab340d775a726d64186d56b562c3b3548 708755679f4caa70375d557a69fe54c1faccbbec17fc5ae9a9a4788959c34a91df5bfd42d27eaf0b d27a982b944c912e24b942b6265dd961318fbae1808eb56f1ae55848f16406c3db42530360acb865 62207c52c662377557a588c298c345eb4a205611835b43db069b79f15ea91b0d3a5fe95bd35cd95f 3c1aa5573d8794d861932e0a615d2816585a2bc813b15b288b83efea99efcc67d3dc067eed728d41 eb0eec8941056805170c6885b606b4bcd2d7ebb34d815696cc002d0315330cac0c13c79baf80b7dd bb1f4ebfd62c1df003c492bf2d0e78a54076e973c9d008ee3df8de0228886cb399392e214d01959b 75231e572a173d972f4e06cd476e9b9fbe80ee322e026ded08026dbd8a00ed9ece6698ab40bb5f74 32286186fd1a684fd87b864d05684f5922c3cd00da49ef6bb502daa3f625c335fb1b81c703edb0d0 05da6ecd736c72f9ce7e3bc1495b02bb7536179e26c264fef6d9e60a72c96042e9583531a5763f18 d2cd49eb86d537610baa0c565ca5285e2c20b75d5d9f002c07dfa9333ba60500ee3250861e0ec0a3 330fc0415303e0b9d703e0c33d06e023bbcdb0ce3ef462eb198e3400bf751b80d3dc14803fcb3b80 005a13802fafecb357dbc9100fec416df43126faf69b46759ac47127d1a7d38c5b5d8a63eae67f13 39b861c21eca2950aea1b3f5ad14e39561770f1516b5431938f6ab0080e0d51c8018580140fa5f59 3a9e4200b2a9d3194409402e13134073850180d63bdfa91340d1f400a0bcf4c99036015450390035 8b6e86c902402df609a07eb99d2194007454723260036b74d916b2fcf7f05030e67814e4dc7dc520 ca3526c4da678874f7e52e14b9cd0e08eae4b74d5bc51765a2d019d34de0d7601ead463500751ecd 3f9a2f7a6c666778a23080954c12c0a08d086014620398dcf701cc7ece006ca69f00ec91cf6770da 005eaa4b000e2d077f9b4dfcb44e65f7ed37c1ab00ee6c7b19de2333df3c943bd0f2f09680c62ee5 1acfd78e7c1bb939ba3d7cc25688be3c70fd281a55fc044985e5ee4500689883fe19ce4f87fe198f 7f826fee571b05e05899ff47f8d51111c8d62b13c0d7791fc03fd61c20d8dc1920c2a808902d9300 48ba6600a4b28e01d2b12e0019c275805c1c58805c8a36409ef66186d744afc1879a42e43e855f27 91efbee72d573ee38b6e7d030f0028692cdd4dbf4ad843bda03728f63fc6f6c77bfc1bc94f82fe6b 3cfe09bef352f7bf5a0c472380bc36170085eeef3f6b6f4fab02545aa3009ab97501ba339c01b447 dc017afc6e02743410007add7033d8e30ce1f45ff9f46791fc366afd53b9ff7b38dbc17f3e7962f1 64122d0b1097e6fd2e6dadef9c76538f6af1aa5ff8ea6547f08d0b1430add4f990480ac73c71ee17 48ea8ccd18f6e45748e144ae78e51866e9c2e163f3f6813fa9fdbd74b5a29dfaec2db706304f3776 09acac8f1d0b5fbbb583bc8637e76fa9fc2a3557ea1f7c3b23fc2b9f967319e47a861df16b30fcf8 4892734f6ad2e8b668eac9750d5b9b8b75978e291cc1e99f86b27e197c1f9f5a237f64b6b5d23f5d 659dd777ff662fa5f7c64ebd2f9b5bfd3d6a6dec7c17591fe52eb18697036e856d47eab77ade593c bd72b460aec6611e93f7c29c7f51ad5ff78359c2c9c45ffc2fedf44f7f0183fa6394dd61b89e3a2f c43d63d3867f7c760a51b61a5f927da9379a6fd794b2dcd8b9e67f5b6573a73f5659bf99bb2f9edd f7ebbfacb2257026bda1ef699205cfb3493519ea53a37cf5273b5ddb4f6c70ff9e40fb662d3e76eb f5bff8afa19673f7a4322e5c418fab9f5336d73a3eb394eee792dda9575ed8d8404959a5caee2bde 2f6ffac25a3cedd05930a7b03f8f51df9fe7876e30939e76342b85ee34e900f63ca94efaeba95174 77939dda3f4fa0b57f8fddc62c17a5dd4b3deac33831bef55d654c3ef2a3f019589b3064b6b79f4b f62ba9fe6fa36c01b8ec90eef9e4e71fb77d02a9cf0d1485cf55bfec15164f4bacce3fae08cd1202 459205d326a76ba1c24cc1594598d895a23c8156954eec8245238677956f4610a576d38bb0537d30 be79683826afe8247c0e9555c83cbbc72066c78f203fb9964772a98df88bce40f63bf586ef57b7c6 7cb8b6924386f5696840f1f1ef106f556b793a910934d9aef142f8b35d2fc478fe718af39974bb6e 93eaf89c4ec1e9f9398116693e8637bb72841d16f5b10f27cd3199ee9030c41758c8dc7754f019a5 5cc0bf8f59d418ddd45122e4cd51699677fc4ea53af4ab2b66323440fddb3969b0b3a7d70174fa14 fb2e26a17df8f6d67be948f47b7d7a30ed61efd1fc87df28ef25bd7c39dd7829d9aeb189b324d78c 3c4bd09b3e5db3893381e6be1fc3eb783abe75fd55f8ecf987e0e3f7bff94dc0bffa8f51c2f53fa3 d27450f017d2b8e2571783da70dd193787466d060f76e60c1fd8cd3dd33f3a27b1ef22856c248386 dbc31e6ce8f9aabff2a85afe9e854b76dd054aa0e8c4cfc07584f01c3ac2b2f25dd69c42e73df923 9fa6f474b153cf176f11821f2e511f77646217ba4894ea263bf65ba61a3257b31be48f9e3f2ad766 895f2396eba1313f1d07fbeae93268f5efcffe89c8017d645e2cf62e6abdda1bc0bf424cefde2760 6f2468a4fbda0f0477dc5d6a2ef07e798e682091533476c7aef24072f68a8911bb7e914c6b3b3984 96255516160455963ffcd1cbd916385dde87ed6ea269372946f11331a6f3b9ef6942d68da0a0201e 187fe9368da139518603a7384efa3d78b2ee5de3fda947362e372fe85e5eeefbfdc86529c2a3e4e6 1b95ba33dd3721a744a36877fe16e96e5537455bdf799a0dfaa79e65571a13f3d833be351ea68bdd 01239d7560a3efc0a67efb0463dd8fafab0c8ff50f29bc7facb75b32bf98976acd5edc7ba69d30ae dcd9d1a28c62be2e3a8da1c5ac987e5ab86a3dbf5ae97921de1ebbb142ccddfc59f91a951d9937cf dd4562debab5b2f9b2d7a7206f37d0a06ced732bd06a49ab967982de84897839d618e4daaa816fbb ae3e2237914ebd1a5b6dacf5df1a87217067e2eff48ea8f061a758099619e69b1fbeeddb5fab7796 fddcddd9d456cebd7178d9e9a3ce6ecf0fed6b9beca71fbdd5bbed19d40b5743d92dc881ed94c75b bf5bdb9e27b649e556d67e5cdc5bed5cf16c7a56fd6e5c8e95973170e1bcfe00b0b23e9af0dfc785 e8b4d943b4f1634a6b5c70953bd35addee14074aa0cec5fd5aad2c5a0f65e5ee214547dd8e02e6ab 81bc4da44586de56defad6e650bc17cf0b41a766f1a5771804b3796c0cf7af8dd827d407e98d67fc b765ab3bd19e2da75206397b9b2375eb00499e7966add04437c1cc200ebd951e30c15ea73ff3548b 84e94de31be74f67da4d0b1da9fda9aaf3b80aa9aac113ca6aaff38a3e5a19f2ae5a1cca56da5dca 50fff58d0524b7d86d48f00251c4b4930cc55403e719f8ed0f5b1b5d9d675a2b4bd6df543018d5d7 7d7380e52692171907ca99eb5966a73dfba0dddc2484799edf1463d8bf597a30fbf4b50f0c8e353e 26bf05659da440ae3b2585dcabea8a3cabd59efe50f49bfe5140bf5f9477f21a94a1d30d918e11c6 49485eea88e96ed517fb23742edc2be3abe0afd1ba40197749a0ccfe907f35d319ff82c0ed0feb0b 34fca6b84996cfcec2a9660d86877bc7fce9d06e4989a82e283d61eb08180d137358587f1a7d418b e9bed649f4d8ed94760b5fed50c758598fd3b9d2c8a56bd9b69e07b9d52a5d24d72b3d2484c373e2 a56e7f630111b7fa2de14e1e2961548655fee5591e3f46cf539ecb2b290f24ef2a37e9ce044e84d9 012756e324c373fbc36ae40e2f93f3e0340fe6577d38e85728d3136aa2d45df71cca3a862962620f eabb21a98706d0d404ba4577ca18a5a8358a329586c67ab27d3502e944da130909edb93800ecb588 6fe2a3306ac617817aa46ffe15168afcb8c33679e062e3dc64729438d1225dae0845135619574e6c 858bbed544cc2a4f33ccaa0078198c29b3bcc7eb0cc966c94df44b3c0cc6b3915162867dbf875bae 3ce0647b7753b36f6112c37ab82e80598e39abab4bf4842b66e9c2cb5d14d0248f78d9e265511b08 f70df0dd5b1368a236e15f0f64c1730cb2e173d5ce999bacfb0f4eec6ff25c512c83ac72a030b6e2 f4856c4679da8c5e53237a6baef6b405f105da6a5528ba799a3974f3dc8ae9e64a5e6630d68be281 b944ccbafbb525f876071ef6c25bc3726a415db2bc324919d4c18535e17601550d552a4ab3a4b5a4 333062c46b712c0b0f7862f06f7cecf0dc6832e0a69565c815f5e5849d63af15abdccb7bb632a3ae ccaaeb018cde5e7e3756e86df48269cb60391a6a84d9091ed5807282e18e8259284fa6c00a27d3dc c021d37c2126cf377c99815dcf5669351d0b0d65367437d5a1279601abdbac566473e061b40ea46c bb53292f41a529115fe7bdd403eaa0481c0842a03fa2c0f3054ee5a4b668b1f39ee2b1959be633ba dc8d18b03c9ad1dbd37e4b5bdeeb4c433cf2a28e60b74cc1f61822531aa0c9de3b5bb6b0d5ce276e 06b9267cfc0610e4c1fa557d9347da26c8c524ccf09a13e4b2bc4aecfc33fdb9f70703a2e4bb4ae7 62d96dfd2d1bf4304f7566f75a4b31afddbad4437265e1b15f55f828ba22dc74f7a6d9f9bb2831eb 7c4e63b2b9c1a1ad53f3bb7f434363714cb93925a1e0ed604da6d6ec48f6c9d783b88d2b79c2d794 0641d52704febcd5543cf4ed3e0e949f4b1ca8046f2c5e9308164f72660633c0e2c124c9b05f4e91 f2e21cd4a2e6ac4fd3c76f4199a36d369679b557929e9ba794aabd2e900c97c4baf06c1cca3c1f49 79aee45b20b3d68638bd1b46d977f89c2b94db395b64bab97a244ee646c4ed598f097fc12df1574d dce1e16574c5d9c101c001a9f9ad23c08405876285ce44446768c345e56b3843cb3e7c4796d41142 9674a4214bb83244cbaa36cd3058c4fed53ef9fbe923e971d162d86d1e06a6f138459256aaae29d9 198e9a2239a9d478fe3d2cb10b1ffe567a310db45aa14e2518262f084991f844158951b3a7e32f67 d8c5c7f07c8003f136c426c223c1c4667b8bce1ce28c2ab0f6449671544234e1dd46ea4d85873787 8d0d3791569ce17869efaf36d8deefc8af39293baefaed7d1672c3cd023d8f38a8731e7ac76de215 77fed03a7f34538f738ea4aef190126faed6e4e3d1b5c6767ca648ef915b8e420e618e18190988bf ee370ccf553e2c26f6ab323abb4306aa8cc8efd637b2aa4843445b391152d7a70bd8bc1df6707354 bcb50f8a5468b757bd46ebac5fa956afc1e9d075b40ca021231c2122bd9721e2b216a0619d77a161 6e184057ee3c0d9549f9d87f6c82c4d1c7c2c0f41bf4b7f5a4264392a4b4da0625841b11e2e4f3a1 ce344e5889bc3c921cfec6e117269e1b0554053b2d44a70c1cde8e5d16b6b4b5da3e6c7766dbb19e 5eebfc2a8d5abd313c69619cba8686dbe11922adc3bb199c7e85314d66ac638d38b7921b7c420c1a 7979bf01934ab7004e3f14d3c8c3a9ddc823b0dfe03b6a34da8e2ec71e1f4853fb386c0d0c966e9a 9d1a8b49d28567295e2cd22db60eae40ea5c87ca049dba39acf8fabc7e7277a9ff6cbbc0acdcea37 f710740b5e38e4b30daef99cb4e46668ca66e373e8b88dd8f1470d81392660127fb6a02c62d7fae2 e016ea35780dd5d61798ab35889e5ddd3d9bb3aa1dee5f55db1be3d5dd0aff666bd9b1efd5c0f76e f473efbb8b3e3cb570f5ded766e8db54ba444912473e4e73ca5c41e8833c6d902454aa60d37b278f 80dbe3bbd51f20b766f829bc1ac2be5e066734d104cb1187d697bc49d76bbbd1d79f56dbd84bbdd6 38ed9dea3e2a8faaed3c92544e89b1aba0e5f85ebe9c6b953281197869e41f3bc571048f8a1c7f3e 1581bb532b724d942bbe2633a344e70adecf12dfb54fbbd8180bcb7ea70e6d4d19ed7ebede41216a 512cb38d51e48f64ce7f6e5564bda20a6d244c3ecdb15abe83959676ae1e48f45e3977a44205dde8 b5f2b0696597ff31274a01b3628beff8201773b3835198aa957ea1987293bc8a7bdb7ce57ef91630 e77406ad02db78400050b1902dd88f7802349fea1d689ec056068b06acd656f294e27b97c5257eac 1795f1403974e6964456f32a37bf9659da6dd038c14a4e0bedecd7b5b627d78acd77d3f8806afe77 9aaa6335cfa527dedbe717e4f3905b6bd547cea853796067b115c0866c08808e319ee12300d08931 324c870074aecf01e83db902d007ad64d890400be85800742d2519560f00ba592d00da9d7f4217b4 af2bdd43a3b73122568e3b3a6f0ee46c31eb0a3178ebb08d528127471594c42a00df86dbbd188498 2c3904d53e01548fdce8517aaecfe7fce296dffc3fc2de744b5565dddabd96104551ea4a0a014110 14c10ac50ac50aeb5aefffa063cc3dd7dafb6bedfc79325b4b3379138220a2d3a30720a7c136c5f9 02c8190300b9f59114ebeff6bb80bca1620a534fb17201792f0d00951dac00952bde538c8a808219 29c5a105a8bcb700e49b7aa5d81280fcd4aa29a64eb33f9637b63e3566a60bf506ba882f7cc55ac6 75f1b2be7fad567c35ca89f47ec131c4f0e56068ec6d0a883f7a007854a4ee5009920f80b2ce09a0 4670fadd524d0fbd9c640175fca0803ad134a0de1d19d085ab096884f5004d8f4680668a5b4057c2 17a01512fb09c44aac005ad5db80d672ab142b00e86a954e71ac025a175b7f73c27167615cc4c148 cb9e871d99ea478df27870a9310973954991473874493038d2aed50bb0128e3399ad3dbb019ad42f bfc304e307a0a3c513d0c9363dfa1dc90126ab960083ad78c094730a60aab53a601a9b0e60facc14 30b3680f98283dfb4c925080d9e93a601e8520c53c01ccb39605cc2b9bfeeecbada5587f550e271b d847b3255bcbea9b6d4f7efe6de1694d5a5c115d5a14bfdfab58ad7d2b23a7469684a17cae902126 b90f60a0c7e7af0ebdb6f380f9f491bfaaafbc45016b3e08c0fa2c0fd87050fde9d0f1dd01ecb1d2 4be756c91c70927a025ced96039ce771800bf17a8acd08704b37fdc18a4452ac25c0ad391b70e77e a75e3f362f46df71b76a4cf4e6d2f9340ef95a2e68d3ede3b281ebfce2d7d978cf4482279d1d9d71 f70be46f59e609fb2341734495fdababfe4cbeff23fcfe899d80800a78ceb0016fdf02c04fcc18f0 e7c705942b330494eb8a04ca1dd0fa454cace205289f9b2f2014390a08cc5e0782a2f8290643f3c0 f61fd5c9a07bf8858908a3db6ac6124814109ffed22b25ef85951f552325b3cfd4e8ff2cac3f12fe cd99f8e9d0ff2fcdf717ecf04df2fd93ee501d0e8130a3d640cc1c5f40b47b3810d79a0ec40bd407 125824402a357340e2f03290d4791d480ed94b618d7fdae91f53df4f3ed58aff2cdeffedcff637a6 f7f131a8cd3d2a958eb79828ddaf6b26f7be24fc07ba10834ffeecbd9fc5d359bd61272e3a93c79b 71638ec2f2c01dc252221ce4edb9b2570f6bedb77518b3ae25713976b66b39f637897608d707135e afe9b5f7fe67ebb07ff210fe1bff25f4fdd25abf3e6485d01f6a0cd977b801b5af6beaf99d785c12 ee333b7baff3f2d4cd1c92a3104f0e87b038b9ec27b87fdb45b4f1d8c15dedbd5d4bf9f7161d67c0 c6cdbcb36b1fdec3abb3b5fd9f54d913133fbdb714cb172a9d5c971b9d0534dccde706d01e3f49b5 3a2ffeaf288408591ce9ff259f3ee5a988de6127cb5cd1f64e3cd3614f3ddeaa6debf0347badfd04 6b7493982bf5374965d65f1faafde1ea6c36c7cb9ba34e7f0e624c9fc7cf96fcd72dfbe9c8c942bd 097fdcb27378243c222ba365666bdd2accd038a4a76ef1ac4c0e4dba39f189d3627c6eb3977197ed 6753ccf263eeda877ff8b7ce6f13f84a693733c15e67ef31830ecf1a07ef22e256daa2430b5bfb59 9e5e750bbcb014124c597cfcbbb6807a3b63ae3f76f52896378d0899ccddd95adbb467e87cde9d26 b5cd604aac96a3c9c1decd2674725cfe24d516d88db953fe320ab832089f410b0de5d74e1c4e54be 31d4a0643a84e6f47e10d5ccfb1ffcb74bf6666e8fa753e73d5eee223c59ae0f6a76bd0c8ad3dde2 e3b52ff3886fbc7ed7466a66676bd546a689a1635362a95313bfa473e3735315c6dc419046375f55 4701a3ebe1b3a79a6158369de167a8bbc389ec7587d0a4331c44da6a3e80e7d724b090d2bdbf76ec 421fdd6df95ee2d5bfabbe7b2e7d1cf5884b71d93df4b0cd0fbf2a1feadc7da455f617494c83cef2 668e9d857a92fdc87c21c134a9dec69383f9598ecfce6737bab59ee7513a717c86219f05c3cf209b 1baaef7412048db3df89c7c080003e80a32c15c446910b90655ee8afeba4d247b7a4de4b9a9ad323 0e4ebbeb3341d839f79265877b2297f62dece6db620695fde7cc73fc50dff6fcd0284cfc0afc1eff ea3c1fca99f54edf335f1bdc5258d35a140b457e9a683b697cb647fa2820478d3064a79de1449887 83a8329d0de0e9721558b9cdaebf3697c73eba5e5e7b89b379f488ddf2dd3db4f650d7a7aef9ceb9 732a76badc9b68df8237d30e24fc6b54f69fe3b2ee57a05ac39b18a3c0839697452bb28d5b0b4e2e a81bb72cc5b5d27e2ec5cd7391d3ad7db50fedfd418e0fa36580c8d59fc23bf1f3efe228203c64f8 e9d9d420924d2188b566b5bfae79f64feeb63daf4724fd7ed72787a3ceb93d9c75b8eb306e07e9c9 6b0bcff1ce7f86b3a35f292eaede67b37f795af90679d013465a8645936e7cd105d7ea8d8de626f7 f09a98af4c1b3b66776a90b346c93906c5effa1ba72d5a8ec3e6c3768a49e74c0fb6cb4df3556ecc 0dc597264cab4a8695d0cc0fa2b48506b16d81be9337901e99f1992e23c44a87f7cf565b9cde5c7f 24a07d1f8468e8558bf4cccb5ed465ab5696bf5a67ab90b30f6eddb42f6e89ee3f9b9b4f926de2dd 4fa9e1e508c63979aaec74e459ddbe6e323d5b28d7e2fae3717fd4656b405b1f9631adf1b5db4911 0f2cb51f063ff3f6525cec9de99e08b470acd6bf793683c2658cf7f6e35bbe7b12e54f97b9824cfb feac90fe98092bdeac9158adc5f0dc6a21c8a7e7da3d64d4dc3e905993308965e38070db06edd07b e7cceb57871be84ffb56eb42b6708a8af5905b93f54a16f936817470a19a16f41e77cc684dc5661e ebdd6af143226ad6f05eab152dad9322088d4dd11deef2bd643a5f38fbd6b87f71f5416cd485de61 d8213b8191206df9ca43beeaed80a74f8b780b19f24233e9567e9b099359d7693094ef3bdd4eb7ef f06a3cb683289ad55fe47a590f8f876d3d7dfa1eac2904ddacac8b7ecc3929c2667ee4e0b5657d2d d48a37c8301aa2e019787e1be9bb53eda653dc15ad9eb223a37aca15bfdec16a7b51097f589f1c67 3a4ba8612b9c2c34a3bf6325b173774cca9f2a71c9cbfbf9acbbc9baaf66f2eee51bfe6dc338bdfc 45b607eccba8bfa262a3ae904cdb9ab6f9f4f6ca8823733e3322b3006bdfcd846bcb839fd44a8c7f 34b6207e1878e302e97ba258d2a9b0c2564f5654adb257a8a55d679d99d6370a17f5b1ec17d5c7a9 aeabc3cece57875d2c54874e315c0e4fd864caa403af617e421a3d9628896da5257f7568afa087a8 8b6bf75ce378d2de4ecfc533f660d920eaa3eb40b066f0543317ccdc320bbd65b366e78e9d5a69f1 1a184de43ed2f73d10e95eb9b8aa9e16e4aeda312b17ed7a76de5a7fb24134a15164d4e1bef2bd6f 54791837954991182bea667654a0be8e542231a7a668b7521c079588bc8431b47d4dc60352f582cd bd58eb3cac9ce4e7561ce33619a3d4e848cb9c53bef19ffadbbfbcad999e2b9a8bb0ccd656d3f2b7 a519dbbd543508b956d7bd49ddad9eb156a7cab55a8176a306634d184fe7ea53dd6dd410cb9e94cf 807c2a13a99653a0d588a844ddac5c3134c791e37932942dcfdccb450682a5f5655191d657b699e2 e7b391d6dbc170be842fd39fd5bcbfafe5cdf6b87b955aab7412daa4ab7cc911ec41ae3e5e82b705 97db771331173923e1f7847ea85df9ead9be56aadc3d63688194b3d5e738e7aa15b4d4513e093b50 3492fd3a2115e8558f2bd172baafe4d9dd4d8e6fb98c6c45354cda90534172de585dc2c6dd40dc39 f44e6c61494e24f79e2c92874c432463a997a23e8c1ac5e924ac6e09bfd7490bf1a3ec49765df8c9 34b83df37de351ff048d9ca55f8eefdadaacdd0de2cebe75a6d02c5579ae436ba2d117d44abda328 936d4757b2c2d8ae18e1d8ade4ad7d575e16dfa15cec172269a34b1ba901b7ce1276d87cc496922b 8a54a9ce0bc7edeeab4009ed40eb0bac78de942f6f1f2af7269c9862e5947bbd4c2705359851e5f6 e46789eff6af3bd38b6b2bb9e95b07c67e9e41c98a46e5ac59cccddf06a9b1f7ea85ca3db50144e4 d5ca91c015ed8a7e5f7d57e6ba2055f2b1acc9c50d6d480d9c4ecfd2436f89bb45bf2fb6bcfd54a4 e8f7523881f251686f7aeff2155fe4cbbd27ce96cb615be71ff56c871f9682152f25f4877bbbcf32 f76e35ebbc34dfb7796991fb8e3a2757b33619d821d4ee84c2dc6cad5763b9d1ddc48c0dd4176a2e 3378d670cf8d57f5d22adc352933bea8e01083ca7c9a14e5e5fe40ca25fac64bb87693c5d6fca689 94095b42fb8c360576a276cb57adf97d595cee638b45b9bc7becf8a18cdf7919f160eebdde53dcb8 27699c5a5efa1c947618ec2cfebc595d9970acaed2263bebf4fd14db744ab99626013149fcf6241c 98ee8e1b569c413962adb9b12cd51ab9c75784d4d9acf2d224657f53a64ffb5ca96dcca75c1afa79 095f773191daf9b4d0a1bae5f2753c51ca7d35a9f18fe4ecf0c316e4f3b2c40db8f14a9f71aadddf 701077bdb2b33b0db170ad893371e12433e6b9f60bd244baaf885e978307bd161c965e133783410c b995a2d31f8deff8a4cf6423df37360db3d9164dd956649749471d73543f9e67196d702a3c9519d1 bf560a1bfa2435a5e745a4f96246e06a2452eeaff8efba35fec9193c2f0796cc4df26e955397619d 8dd8b8c5ead7738f852374c2c486ba629063e744afdbfb0feda81c4a255147a4dc5ac6a1c8427b4c 1ed6e4853c6cb2244582b09ae2d3a4c80cd74dc75499af3fad7ba7babe57c7ab66a33792e57a7562 30b566a68756fbd8105233dbeb535e6de44bfa9cbb1d85cecddf97856370e72b994d8e9b34ef2807 bdb2346b2845818967c50a63352b357a7dd01cda09830e8d55e2ef8a15cadddce614d92476e49168 3e49265c17888b4270442f33b6083ea286f8ddd81df141a187e2779951087e387108de3bf8c3227b 9a7446aae5b77099351d694bc85661c7337ff472596a4215335efccc496e8ebb0a3c141ef870c427 9cd6e7cf6cdeaa7c18ebd2c8d39bea00a7b1c588a15af452a4c8f34523dbfcdd24d91ce6129785d6 27cab43bc1efe7788d4bfcf382bd73460e1b2dd6349629a25574baf92dc444ab4d2541ab2e85a0d3 e828a3d3016662e0ee7b410b1e8edb3992f49a1df45db3334f48ae6ddf45a67aab48a812713224a3 97fe536464e2c6bfc4ee919debd984de4c465baa35985fc853e19d215927f7556e89de1dc7f18744 31f8b05093b0b75dd7b0313aa863caede8a333f13540ab6f665e5ad8eea15448e27771d5c2d0a29d 3e5c916d176e214d7e1623cdb29741b6cd328f6cf55eb5589abd1abdde541d797609fca25904715f b362e42c1b7ee9c5686103472b96cb6525b2317c97ef5de4ce5559e3c894e463429e6aca0a7f2478 828d47ec0d834a3a40675b2f8fc2c2102b99e1982e2195bd58b4370fad8836711b691ecafe571d24 c2dea8e083cdaa40cfb06bbe4bb4f379ee087838601d1b1612789a938fde1516e61d0aeedfdf15f8 5656ccced84447aeb798b7ec093bae99583692755ed831aa364ca7098dbc000b3dcffbf08a7bbbff c47b413c519eb3dbe34f8e5aa3516516171dd9dc20aed3b92024bf7a17fcc1215760a44f29df5d23 147cc788321cec742d179e742b07b88e9fd5a2ed089a17726b286f57ef9925ba2f668aadb2043685 dd6f71b9b376966003c44ca65859e3196b440bbe19c5c3c66dd26ed5f344b3661ca9aeacbdfc25ab d4e81c2e510a9dff2922aca9ac1fd4a1889d09e1dddb63d97c6e5374f6e6227f090eb31c786af36c d50abe01b4d07c135ea0021ebd32cbfd319b29d1f722d85c301ae0655e4c119a00d7914e8afe1ce0 46e19c629c07784d4a7f6a020fe0f26409f04a3a37c399030e709628ff52d71d50535a66a3287f1f 6b7acf762b5aa6b8e1e52df720fe48e6fadc806897089fc43d97bf62daad752c36aaab4dfa3dbac8 ce04719441b39709200af7252010720f08dabda7586600c1648b292a748a4905102c54ff29b78ad7 4ff18e01a1b66e80d00ae9a7b479fa81aad1068400af53cc3e8010d5f437c9bed4b887e4a05e8072 6e8d6ac8d6cf67ae98e5a5209ef35b92d7904f91c154264bf217ff8d55ede33730a3b865d963fe3a 696eb27ad39f65d0697e0c88477696825f0132179e539c5f8024c85c0a0b05a4b0e60029222a208d 4e33c56300c85a2dfd70edfe00a4d94a3fe2335a8a730f90f54ef213886d2e9b62cb0252c3b55f34 8bb9754bbec1e5545b8348b72a13eb96500eb7339a2deece2879cd17616c26573ec5ed7174cf07c8 e99885afb7f430557206c8a9b100e4c55f020ac409a04ad04f52a54a1a00141b1600a53c4940d982 90a2af03aa9b69016ad218a578a59f9f3a6f40cdf2e947669101a8c41c006a573a006abe8001b528 732936e6df9c7092ed5643596f56d657e3fb2a42b80f1a32570bc71cd5e126180e958ff962d22a80 7c104b7768a9ca7b4031f10650fe7307a8237104745e3eff845f408bde1dd0c61a003a9d66017a60 d3809e6f6540efd346483fda3e6088e2cfdacb10dd0360c8220418313de78c64da80314b93149b0b 60ac7a113095bb081845b32c472e4e7f39e1aa71a75da91d8a263fb3eb2aeded4d1e9fb45da28475 a7487ed03f4150fdb4ba01eac59dfef8a1bddb1f099ac12000188d80fe0abf131dfeabfe9e8725c0 7cae34608b8a02d8cad2066c9be90176192f007b92af290e05c07e3c097045aa95e2b8001cdb7d02 8e23f15ff6831a2b80d3888649aec97955d1a961a549a91d41ee551c16cb5775e2d535a5d2e669d0 f9c7b85182d6391b02f4edf4fe5bd3b79c3f01183fe3f1cfe4fb13577f89134495fd1de6173af153 7ffbbbf480096f031e3d0f01afe92bc04f734fc05f163828676a5550468b7d50e6920494353f0bca 0d9a4f31ae817233e71be51db954cd50994a3caa077c7e6879546f5bfdba5431533255844bcf78d6 aae3e89fb3f4a7b66f597f2afacabdff9177fc6fcec42fc4f797ea5008ebff19edf0680e81a097f7 40d8bea19fb597dbf0404ce72840ec7133204edf77206ec718106f920a2468e302096386ffa549fe fcb2bff00642ffb3c5993c5b0d1f6abc9fdff5cd697b43bcd5f18a7667d70b31f859e2cff4d8fb9c ba900b1d6f86031f85a551383ceb0e7a90b706be9f101ab5877c91d9c13d924f90615edca293acba 21e659734daf306fd545adf1f2e69e8fe938db2afc1b86f02fbe0997dfafff552a96fd854d3c3ed5 48baebeb6ef586b4bace15ed04ed0b1184c33f5b8709b138df7f1ac5e51ef2c06607778ec9762d0e 769b44a9ef37c4ac7a5cfbb072599dadf2ed67b8dcb0cf658073207e7a5c7ad4b35c5a4c789d5a40 838e3487c70b2bb2b2a52042e6bbe3cc41d8ecbf2908ffb56bd87f5a4f1f506d9ebdaec946e9ec3d 71f2976451cdf00779b597f790bbd213f3dab136c414a9ffc994bdd9496b2924eb761c92716ff169 47837f8265bbe3b9fe0ca328ae04f11fabec2f55165d4497a98b24efc9a1010a137acf7fe7377f33 657b68300acab364243c4beff019129930acc0e0877fabbc4705f176695eaa87a3106d2f3bfd20dc 37c43878acbaf93a580adb6a3a513a54b105d463a9393cc4b9c8fce0df0d9d666bad54f9bf6ed98c fd73cb8ebb64ceff63950dd8d2307cf6f169180ad5f5f013b64e430dacc020d20bf8c0807b6a10d7 c97e60958265806ccfa71f7e56d97ff2057ef2e985e89536fb49311cae0f4aad1b3f9dc770017566 d3c87c797f43065cb873991cace16b7c6e7433636e1fe44701dd2d8e8473888721df2543f91eb2c3 89d4e587ea3b9406911a287f24d5893e80a3c40eacfca1d55fd76fbd3e9ac0935ee29a498f38ce9e 5d9f834b5dfab6d23b5da9d44ed19efe2f97ec25619464af6efcc1aa9b1bd616503ba8cc1cc01b93 8359747ed706cbb747c2f1158421979f0e3f01b61caa2f66378814e6348067cc2db060f2192031f3 e93b450aeaa31b16ee250da9d823f665ac7bf014aa4b9fab7ce7dc6d553adc7d506b07d2bcd9163e f9a11f6a95afaaee7de6d1dd9b983cea41ebb5d68a1a483385de4b6107b7b8f43aff04bf5f9573b8 8f719343ad501add9a2b3c942f8bf2507dacd481fed95a8195ddba7da7b0edf6127b3fec11c969da 3db88fafa9af4b1f1feb4e973d6d3bdcf57168dffaef735b783e6efe739479f9950c94f13e33a2e0 69308d79506c722da3e855dcb8b1b25d8b28f59b6bbfb16ca297cba391f4db64c3152e95862bf2f5 06f1121b67ef7efd2e2edf1023dc99eb579e9fd0cb1312860c94361ec97b0716e4e4fae8d2c17ac4 d665bb3e11563a5d26d0dbb7dec46e0b8fa8e58772d4f13e9328f0265a1c7ad03c9ab4a2da7ade4a 3bac951bdbc9d6b5d0dbb7a535d7eef3d6448f9f7723e930f9463af5249d4310c90efdfad8765755 07360f9d92fa6dde81eb41ebcdd445999553d4f59f18fdb31b4f89398586f2e70382d846afbdc4b2 76dd43433b75bab4f67d15d10ef85ace0fa53aea4dd43aed41514b6819f99eeac6ed51cdb5945da3 b9de1ebda6d3bc771bc9f33568b8a3ecd83996f291e36fd3e7c645a81cec1edc7dd4efd614aa8b97 3b66bd6615d15288c836a723e2ab0b985565713073a857aa2db219b1569bb37a8aaab54583861bc5 4d921f0d325774907f40b91e25d2af4e19b74efe6b7cdcfa15b797785a7d7a6fe59ba79c5b0c1f68 b3c1525c633793e40645d474a71d36bea34e87b506ae7d65365dbb3cdd0dea43fc38ae4bcfecc21a 57f21b4b2d8a2773d6b39e269c1bc1b5453a5eaa210c5f31ec69d030d0263dd5136275d189918deb c42aa7560fb664a7a8a6fde162684e5b4cfebb87e4706694e8defe0c23ed27cb677c480e1e2dd3bd 1fdd55bb9d34b742fddcd8fb0bc8396daea87d7dbc195bb04a527d7861abd6a7cc9ad624576b5a90 594be71174bb6fc2ddd6b0166bd3590d99cf9786431dbf330203fd14efbaeb28d9ea61d7c2aafee0 2e6917a4e268ddf379acf1bde659bdeb02aea6776a451de40bb62a2e0b8dc554b3ad71bfc128c19a 79335d3e0b957c4d67b2ad22e63c9b6efd786ed04f2971baa0f43d8c1de0b54fbd8234d31eb33b26 cd683ee3cd7c6121d7e2d355af15b9ab6538d1bb6960694bd1dd53a9af931d76546d43cabcca24ed add62347678d7fbd41da26cba8f2baf5046534cfdb4aa6e00c2bd333fa6dd0956af750aa547ba69c 6265a57834e78579a7117eccacd227cb27b63dcadfd0563a2ccc36c913ff747ac2fc628b1b7c57af 0cb65b4b1bbfefa67127b2b57a812b190d341d90ee0299d75b86a7548f4beffb86bdca963c4bbb5c 67ae5616c6befa804f0355b2ae13e5cde22b4599294725d31cbd2bd5e7159117f5322f9ba5a92917 6efc405a0587bd644b41512a7d202185594b316ccc9a63bd318cc62fb57b3117df719a9fcbc47f74 e8f4714fbeea6fa67db1a6fbdcdeaca9cd55adeef68f46a3bd78ebad590257db8703aa5d2b1f462b 4f3f657548c015553a10ba3266084bc94cb9666556d53b95dcbe399417c3c55c36adccd7122fad2e e587644fc70531c95d19b179a81922e1dffb829f7113c1872a05818ed665819e1386e0ddedc6e4ec a2cecf67deb99f679c57684658e3d4dae4eca1987d59b36aed6216c6db9db115e595ee95886ff643 b523e877ad6fb9903a6c3805e5fde8e28a2a074c65b69e0815189b5464b335a9ca8874a84b6b04f6 24d441fb62c2eb53d1cd8e37c2a175bd0abe2ce7cae749872c771b856a99c77b1dfef6e4377c3002 df17f9e9d72ecf07edf4791174507b7c7b218da0216eb4f638dbe1dcad9f967e3d4d7375d5babc4c d3a4cec63619ef749a4557da2d775ea94f063a29931cfcaa44483e5b819d22229b778690d612c348 e84afcde9ea28b8a1591b8b935e110840d81c9c7edf2f9048dcaddb1bc2cf3f6f8cc07c919f0e240 25b8576daf7223a4dae62aebd78a038d29c481a6c872400fb514076b342a3e1b7d4ae9a9feac6673 3fdff6f7be79aa23f88f0e6d9ff18bee1ded44bb553f2b553efb73059afbdb0a7c8a6e72118a3f12 969fa785d59745e170dd11e5f3734e97bbf15d2cf3f5accadf59b1ce0f32aec78bdbf5907bb9d082 abbcb8efedc981e5f8c34e6d0865ab5ca7c2cc8387cbd4a4f69ca9c9da87a955b24c0a4b65e67e64 85d3c6a1f18b66f1162b956b9cab1a56ff446dd8b4cacbb7416633972a77e5b66a858e979568a7cd 656bcc7f1f6b12e6564e22d916ef423b53cf947b7015e6ef56b5c80f981ec5bda6239e1b5537154e 21408d034fde65a7a1d36773c5c98c995fa11d53ebab4f7a054745babe4405bad45e34a92df7dddf ef0e9ed416fb2d60a6b63856a14b55ab369c3f92665720ea5a6bcdc9bc735bf098152d955c0d93db afea25bf3dabe1e2b3a9ccb1ee522e8e8b91b86b1e23a15d396ecabd26b8f08330fbe2dea000714a 8342d8e99ec5d9eab0f6bd3dd99cdc9698da7a54650add8b43afcac50e6de7ac09b5358335d5a43f 370abf1879723fbd95494f376d92cee7a6c46939bd91d4bb41a4384a24f5c18cc1f77d4347f615cd 754584b35f088699cb12fb0da337c875ebad0dd8f159c94e928d5c7c5762b1d5bf47026bf526fce0 d188b93132387099eef2cee6e6cb0fb3a85d61a670cea0b4cde7281acd96056a3bafa91441b52d72 7f5a7aa437c90e8973968f89ce2efce6a7119c97cde237b9cbe201402c5c98ce46d85357cedf185a 0c17dc9680f79363352030a7d186a694d6f4c30f579fa277b4b6e95370f5b2935eea6836bcc8f541 b8115b372c2ef799d18c9773d2989d4d7e2140cc622725f4eaad9c69d4ae3fa966d2cc5044304348 efb5c0497a7462896ee92d115c82ebf82da83771e1d5eb61cfe53dc22a28b9473fb7114027832289 6a52a8a3d0870b4ad1e47a40a1cb0c41a16de1fb1617d54a75a577ae194d2fdee7b406bf3970d63c b860466bf8ce690fa9fc56aa87e645c24fddadc091f9252f575b33565f17c6f4da588d28975dcc49 5f3fed88f331732578a5f4c6831991c5455d40b017e17d5b1a56f13b3c069885826ad3ab856675dc 2f1987daa894f7979ba275cd3f8ac559af846c0c4c411af9b88360637583381f0e4636f48249f196 ba2287355ad878abdaaf47c499f5e512d3d9f53ea78eedd237cca48200f522d2aa9ff0cfca7dc941 c37ac414b39909792c3587443a8d1c63af9ebec2945c738f4ecdce15cd9ec7efd2bc9be44af9fba1 585c2e60aa58420801d9acf52a82f3fd46a13558f50a94492df22764f09586f2acf386e12bde2ac3 653fdbcc0d2ec11c2ed7bb00ee8d210cbebc4dbead1d8e4eb31d0cd57a75edb035fc3ac0b4c77d96 53f4fefb2d25f837b9357076dca4775c31f1a7bea0dc573a392ec3f210cb60c9d7ab5e2a74a271b1 14254ba4993fef0afbfaed5cf038f4953f052494ef885a11be8e2c12164aa198938949353bf60f8d 2c942106d06cd659416991f7cc6269a3195379ab19041af432a6e61f338b5e3693a252fc59e27b23 c779b46dd542b206a777e22aaa7e6a63b862c18f8fe89be8950f8fc69e351ec70db59bb30ba2b79d 4d31e54284c5d5d8ef15bc65b10b07b9fb30279fef5176c2bf56596898dd43914c5e2178c27faf4d 26d6ea700699b731e014e63c40d7a00ad0b3ef03f402cd52f4cf00bd72488a6b05a049af03d09db8 01e8fcf001e8022bb89e5c776c8820945af3cd7055114898128dfb791963b61f819fc0df093b97bd 8a07ba31196d4876558ab171d8999590da2d2c1c5ca39f7b3e461e94af516de0dc0a038005ec3445 6395627e48f1ba036c2065530cb014af32c016562dc5b903b0589fa7787c57e4036cd929026c5556 01b6067d804dc20460d374a2874d03b421cc4bb66531b0aa9f6f594eabf414a2b2443b05b15d8ec0 4f32676c983f919d412fc13efa6359b2ca7a543842f12837c22f5d287f2fffbc83b89af153940700 b7478b14c72dc0bbd83985f14a3185013ecd93292c29c5a10ef099dc4fb15d023caa3e00becbe129 9606c01746fa5762e400f0304a7f6d8450b656bffea259dcdd5aa90e8817af56f12a25e3a2572c0f e91062cdfae149d1d3fc19af70b55dc9da2c5685631789724a931966567caf0bf077b90d08aad107 84b49ca478c580302b074078c13dc50e0022247f39b744ec3329ce0a20968293623100c48a5da7d8 bd01716e5180b890668adb1810dbce1910099a4f31e6ad3a31b30d965a6bda9b24a5ca8a2ed3c2f5 aa96387de764699288dff8a870fcde9ea5e5b27c28b43fcd752eb3f5a719543b0e01315f0e01997d 8d01592e4e0159e5e7209d556e524c0e80ec434f40ceb41c20b7130c9037880714645601859e5a80 c294718ae31e50781d021491fda98314113b8012bd0850147e4fb128018ac6cbb53dd377f43ef5a9 a9ba825524ef8eb3fc24e430061b5b7942aaba9fd2ca98de0a9de1f5908dd8db12b80d2cada9c147 7f75e89fe05b49b6bfc3fc84df11941e7dc95d00756cbe00f53a17005da04940539e00e8dabb06e8 61ab93e215013a74cf801ee5f3292211d063d303f41a5fa6d8bc00bda9e22976728dae9c3ceda33e addf36a28f972288ab52992de10841965f3c829a9374d6709eb51e59632ceffed696969556923623 5ab4af8076fbf7bfc6e39fb8fa73fafea4df3fd9137a0130dd1909980df53391b3606403962503c0 2a9325606df999e28603d6e95401db2bf7013b033bc06ea759c0260c932250f5dbe6d151f3c6d311 2f17a0f3b9a020519d354463519f4411ff8d67b3f3ddfbfe13c87f3af4afb66f597f8af9a9cf67af f09fb6e3ff167eff443cfcacb5fa52015c203601972421e0313b017c39fd6ff8ea8a057cb35e077c 40fe5246f8e87605fc2e2801fe414829bcb4cdec1f7d79af7c5ae5f13e6b31ae525009d082f8e21e e4095825337970e04eafff28ecdf008cffc83bfe2758f8a743ff84df7f0386ffe44cfc8ff04b254d 501e7a132008b50b1022aa0884db4b056276d20562b9990051a7b3406c9e7820063533c5d2ff7fee 1bf5cd6f50202585dd787c74affb80accee816e3cdf9d53eebeb4bf3aeeecfde9b3b9dba107a3dde 0ce4711496f0fb1096b299fda7f1caeed5fd05de45f41149627e5edadaef16b671a11ab93e183fe3 d86f0bb1e5add93697c261d789e56b71bef8f42f60a1be2ad4ff6f1ec20f46e9a12e34f61663bc7c 453bf9efd6619873a647987f4c1f44bd3f5b874d07bb886a8d12f3264d3649e531591faadbd99a5e 2ce6ab2e325b2e6fce70bd0cb0d92e7eb68687583e0d2f8b09377c2ca020cacce1d1241f59993335 5b1b6865862e9bcda98be2cb29914c9f139fca147f5b87a55fffefae61ff585063147b5c9a9747f6 78d39af9fdc7e6d09d7e84a9edba7ce6d707cd1756dc4a949742c268b17c2c1a0b359d6dcda3f2dd 9eeb8f57338ae5fbf7bd67844c8eedd95abb7f83652fc369527bfd132c8b6537e373ab781c7327f1 350ab856217c06473e0c25a731fc4ce0f970a27997a1961d3effc5ff0a193875dec3af716c0f350e f1faa0bcd273634db7b1bc6f9f16eab9fd98ebf74e2632df5e7e86466a71eae6557c72a8cbd484de 0aec7f596565e5ff5a65cdd650fdd8dd41a479e14f52cdaee2203633a70059cb99be83ad983eba6f 357b890f263d97d1927ff1af97f71840c2ec2752c54f4736e7faf5d99c39206a4f894538f8850c8c bb58bc1add5ac977b6163e3bf139946ff17d3811e3f7101acd33834859a6c7cfec9020d697a5c082 377880c497b4cb2e1ef93eba79c93d9700d51e7120edaecf54da9d73cf1f77b847b26ddf420db445 7066fde7ac69fba17efa3abafe35cadef51579fd4521ac7df0a82fd463579ca19336393edb143b12 8ea54a285f2973a8a603fa8101a84e1057d941802cca93be83488b5ee294573d1753921eb1ab1dbb 3ea59cbbf4a9f67dc3de39771aef0e773341fb1678705b787945ff391a937e25b32a7b13fda47a5a 9e6ab4a27a63d082b7c78d1bbbc6c7b5a83ce72227b7d65c77e2568ac4ffe9e59b44ac361750db11 fe28bcc281837ea10cf7040a62352ef5d7c69aee25f544ec1e9a6bb54b1fce56a7cba477eead77f3 db8100f5fd677a71fdb002a7ff3f40226f5285634fcba11b0f5ad0fb968110273776e8bb6ba5dd56 73ddb2f24d879a7cdf783492ce596a1077a2ee1c06c381e3cb6957719e2c7276b78a33295a4a8aa9 f153f563393957fec8a73f85171e13d73e1a07c7eea1e1de3a5dba9569077cabe03f0723d2fb8c07 9ca741a36fd7e941d1a6da32f21bd38deb4bdbb54a3bd745b69776d32176dd267ab80c1a89ff1a37 88cb2b720efde2daa19fd8deee56f89bcda7dd763da84ed1ba08e7452bb42cdbaa149f23f3b3ed9f cd890bc3a646eadff94dfab55f4eef9b9c1d21c31dfd3b97e939842f6981c54d873bf516fe335062 ef33aaed3d6856bfb5e0b89e5e9b62273d2378b3d4483c2f1d8ed063de397487a2e3f333253d6f2b ddee4a0bd3e6dedbef3b82fa6d7cf5ea2274ed5aa1711d58954261664e6c6a6d6a5cfb5a8ba27db6 66d428d2884f03cdb0ba6457df64934477f61ad09dc71ad59d27c4a4370fa2fe4cd183a8e53d7ba4 159edae26ab7f1b465e9bb7558cbe88d47ae45cba3a683a9712369d93be7d0695e1d068d80dd0d92 7cfd9ebf6175b17e26ad1707f3562542240b18846a4e49d534b36dd9aecd95a657cb6f8781b1ec9f 6646299fdbe88db674d57165f155a0aaada4c46a2763606a1d4a1c69ece97355af131f51fbce8151 fb0d58fc49fb2351ef17fa9b8ef769a773f8bba763d9a35bba4aebc6dedbce9de31d1e38edfc6d64 f75078551f18dcc91ab5858795c958d04feeee074533d73c50b5daf3c6d70a4b5836ec12ac19a52b 6dea4d5d68e8046cb6ab9eef0db43318cfb4cee69da83781bdab42d88495b0b86714f95a332b9ffe 6b58515fdd53452b88488a88ab40ab83fcdba7903eb2c369382c754f5190f5d561f474edd7e5d4a0 637c6307d870511fbe8989f5bec6434b259e4b5357e043cd6ca27763f51433066a1905bd7971b0ea a1ec50553f1796b5b319ca5a975e57d55b37f98ed3d440fbb8ca738ff6940acd8d2b9f8fb7aa68ce f622470f04928d459f96964cce90accb20908a7df1206ef47b5edc181a932294276d7ec9fdccdb1d 61e9e65af12478358e99cdd90e2ea5a40e28effb2030a3d973528b7d77504336fed440f793ad4e7c e273d577ce2fedfc00598d9741410dd6794c7961794a09ef7c5901122f57a6054baf643bbe2dcfb9 c897f3d17928d529642115dfcd93d8b0d740c479e1db0b08adafb39632a55ef984dc93f2a93885cb a752914e6148a3476b23f49bfb26de06190b769dbbfffee9d07505cfedcc1aaac6b5627c98eabbb2 38a81ee9fcb0ca742b4b8d9f1b7b553c595765a4fabf905310f5b2952a3942e4797b84c93565c748 cbd9ae2cd55da0889b57b12636461557d897dcbed0ba2651f96420fb7227ed4cf8ebe88cf17dd554 7901cb75b9c76eb4e11efb7a2ec591e21e715e08c7e3d157ebecf9a647fad9a6916f5205e36d4b56 ef62e5a4ebae5612e9586ff58249b5ed3143adccedfaaa241e278a626737956a903fcab511b84aab 52f12d959a2548dc126c41c44309133ca0d002b5698be5536ff9f5aa97b9dcc5e6af07a6cbf7437f ca3d41bce5865be2c97e846e891dbf60995597339f856c65c5424e096221ad81a758f2c3081d885d de9189d6d227e1066b8befba72493b8f953a4bf47d1d4e1f480be73bb8551fea6ba08cc941af52bd 3686b2098f62c966a689d82c0f8ec27e38ba0934327f973bce022a73fc15e1fb83477a614d9ce39e 744de1d2b666b21fe7e8b313bc18b2d0d058319199dc18a3c81718f872f8f6d074dc375bb425e6e2 147b405b9284d316d5e20671d0933ae25d20dd2dc0f24e3921df56eea15c7e3a74b5d3bbc5ea63cd 8f15959c07153853ed4aab03de139b536a22780b2a2e9f6965c7dff8f2d799c20b83f28d1bbecb2f 4e5e3573ec84eba79d0fb4a55968979398a8c5ea8c21b55d3a5e2d03da4a87117491f72fd42697cd 539869f1e4ae08354877338ed27aea2f728766507207b14ce02ceb525b9108f297445ecde66d99cc bf4d44662ffa81ececb4fe238995f1bd38aec04a2f90ec27d5150ececa2f9f5bab804ffbf4887b26 8f35fb399e76acc65ecf6c162a3c19c340009327e5021dbf3a246d8d663cb5299dbe117a9473a31c 0a8bda7d72671ce7247922d3894f679425da1ac3116c3671f0cbc29ce297987d12cc735124985786 eeb74a8ae457c730d938cdeff9baeabfde357452ba54bbac9aa8a11cc51523ff1a4b0ea17c1528e1 b07e74cabcd46c712f54ef7040f4c66cb6de5930c6b6bfa197c2ec4817c3c58dda58d70fd528c130 b9eb1328d9d24d96a4e0618568fb7b8b6015aa835f33ad29dedbbe0ed883940036386e284cea18df 2c55f4cd43a314fb1b2635d43c36d8aca81e2b509257d8de48e756d8e52d03dc3f3fc95c0bfa5ca2 684e37968bec6e2cf89b5c8fbfd7c66d6ec4c92db69a473c267f85fb740926a654c31796e4ee227f 8d63646bea5d8853aef324dafb18c2af7482e07d005378792a88d810b7754c7a6c5be83bc447a85a 6c6dd1cce6fe2acd9a0da2a49379a3943b46c352eee45d4a3a02e54a33d3267eab0cdced2122ec90 9b7e33537e2ef3b4315d2f6af844934abea52dc49d3e1df3f7f9a9c7299ce933f323e4d2f54ddca0 f061d426a9f5614474f8e71cbf0e3e6b5c4090f42cadb91b26f3ea077ddf5d18556b218642850357 d2bbf0b70994605e758ae67c1c141133b744d648e386d89b4c09411b03b590107caf9090f001418d 2083d87308fdad3268faa04bd427412f5fc3ccc15bbbd76767a58a7c929f5e2eb4fdf6987b53fb3e 5b4dec6f70336d179f2dd2331d07bf65f926367c8b5df4a31ae9094afc050ab9e1a6a4bfe2633156 d6b7a295790164ed160b884356c84232f2c582ab24b5fc2141db79dff56679867c9fe06ec784619e cf88b960d0fe0a5db960686ce07440f686bbba91f7e1d6426af06597b4f27505d68fddc65b7d5dc7 974a015b6fc53d4ac6e5f2b436e632e36dc02079bd4b11d5b347706ba7897e9647ab64348fada215 be026493016304d3b3df0498827bc036856f7c75debf68f73c331d40702f372fe606c892cabed2a7 44b6f2e0ebd054f67b90f6f92ca1ecb4f9c8cc7584c8d4e09991315ef551662ee5f750b6e53c5b8e dd91ec11c213b5cdac9caff6e6caef793383bd8b5c1ac489700ab025f77e281366715b0ca844917b 44d75cfb5838179b2563bbac23d845b1f22c5d6ac0bd89d2cb49356a981dadd86956b1ab4b687a6b eda19cd8bb66e6af2dc814940702ea51e9bbfe06948cba024a2dd04ad19d8192475c521c4aa06479 1a28d5d93e2829c7049454f2f64bbba9a7bd1961507e06d6a413f5ae988c7e918842b42bf79760c9 4674794a3be22224199dee632f67f25db4549a7730176984031bbed6de463673f4cc8ca9eb0d605b b207d0a2dd4fb198a678af005a2a1f53f45e291e0580a20a03506e5705285ff1539ce7002d376f00 15082cc5d50028d9feae8a0028c51f52042f67b085ff44b354afeb475e85d0d247b6a7ea55a4c7f1 81af94c09ab1bae5883c2ac3112e317080e624bf8334e18b9be7f0aa9d858cbe0ed63a9dfef18360 a7e87700fa3c85bfc3bc4af3f479524f52ccaf002b64408a6a29c5920718c2d4524c3a0063e4658a eb1360e98905185a3601868131c0b2e11960b93c54d7e95830bcd994d0e4f92a9fde3eef8f94f49a df25bfe5200a8facb1dfaca956ad34c7074f7b8ce676e701d20cf80e7cab0e5c287257267066b20e b0be6d016cb668012cca04004ba429c06ebd558acd1ee059ec91a29e4db1c1019e234580e3939fb5 17278820c562037052f9009cca3029960ec0116b06f0227c4d11144cfbee0bd55bd822544dda2295 228942c245161edcb4ed9ee8c67abe2584cc6381ea53758ab8683080ef996c17327685c6ef309961 1de0cabe01f0f6db05f898eaa4a80e00be9acf017e065b807fc4332090e00d08fa55004445a353c4 0a2014ba9962360284caee52ec2140682d1e1055de0544f9190342e8ffe2740991c08cc3db12b557 65402b73ea814a2d05c9fd5259189ba89c891bd54ad0c8df2e911d82cee081a20da102df68a7e5e4 5a8028281d40d4da7d40b406012006d3012076f70920f6c505201ed6eee720c6933b20050c02a4ee 9400d93c70800cd2eb40060b0f9003769a627302e4b05e0064589453a4337a72e4ad0199763a80ec f431bda75ab2a6304bae5252afb8507edc60d61c7ddee4794a7edf11605a54dd23bb4567054bcb78 96b1b565f0b7acdd2efceb3dee55e680dc38f13f06e460f58ffafb3902aacedd01d50e73805ac104 a04ea6f0377182aef752dce39fd6c9b87740b369dba4d9a40a68ce0b00cd97bfbbdd9dd29f9625aa fa50434d5954e7e53ffe6d687d46a8a370cfe0e003ee457c8b1fe1e14edd64d00d36f95bd8b7a63f 958ca0fd4f87fed77bfc13577f4edf7fd5df64ff064c96ceffddfcae515240dabb36007325c31451 02989b08000b7d58c066c736601eca14304f70052c3c2a02369fe77fd12c4964c82e3210cbe175c5 308d5a8212d2e5982ba1c6e5f58b10c938cf64fd6f6d7f6bfa57ebfdf97bbb72eeff1d36e13df03f 1113dfa8e1a7c4028e98ab80730d1f70c96706b87bff0cb8075d487192010f0fda8027b535e0a53c 007c6d4201de4315c56a07e66fbf4aae76d896c91b9a90683c3c22057ebb055049f5cebf6bf3abed 1bcef16f06c67fd88eff751cffaf9c89ff25f8fe9cbe7fb4dea70fcade2c0602317803c1a831bfdd e35ac53a10e6b72910f6e31b109e3a0644f85101225973feaf7cfa0b6f388bcf3043d41e135868dd a3a2dcbfc5b83ab9a25d243a1fc4e7f27456cedbe34d5fef0f4f2bfcaaea0779135cf6eadebaee22 5abb2731cf3cb76b29fbda24cae5b32166d7ec9a8eb7f955b778c2fe8622f8b9cae2d363ed85fa18 85730390e7393cdd95fe8d40f817ffb56fd41f840083effaea825dd7e492b910fd81f8db3aecc44d a7f621441aee2ea20aaded5a98791b62aafb6b7ac175565d84ee2f6f0e3a580a3b6414871434597c dacfd942bdde16f348b8ad7f5b8785b75d6481e779b6d6e1d734b130784a6c0c6ae2e3ebeaf8ec0b bd71972d9cc6dcb5931bddfacdffc07f787927d9e0795d13f3d3e92c53d743983fbd76b06f814d22 67b3e961d63ef42aaeb8e591889fcd29bd98302d6eae3fa47214cb65294226ac3273b2b8364d6a25 63ea16706b72b00bf6c44771f74faa6c972c07a35b5b4a2799d75a1c86427b3ffc84cbe770a294b1 21349bfe71a9e6ade1005ede9201bca2af416c23971ffec325dbbcb0db9f1d719388a3de32281686 8b09b59dccf5db228eccd76a3773328bf3d485678f895f1c7ec65ddc874637cf8547c2d9417e926a d745c390f789e167e0d2c389e47343f51d888348ed5606061419416cc476802c13afbfb691b08f26 42dc73c9e1b57be84085aecf9d8c2e7daff53be7c16ef9075d691e3f3e5af4fa850cfce23ff04a7a 054d539feb574d9ca1d3bc36f10b776bcc254777241c8fdd50beee86c34f00a22114de97837482b0 0de2eafd10a44debdc5f9bf75bdf415ecf5ee25cde3d177b433d629fcb777d3a53ecd2e79fedbad3 e529aec3ddab957620b54cff399e7a7ea815a7de67ee1cbc8989219e861c340fdac8fecf20fbc3cf cbbb83bd62f7274cceeccf801c774be37c1832323284861c15c41a510e90f92ff0bcef1474ab97d8 66b3472476bbeb93cda0736edb6187bb9a9376daa0e6ed40f096fe33b4d77e58f1767e05f44fde67 3abe795a6ef6f6a0c521d732905bc98d1b30e35ab8aa34d7deb0d144cfefb091f47adfdbb3e10a0a da209e1bb941bcc83f46d93f5108dfb48669a2d5f09f3039d0dfc2adbfd60f971eb1993cbbf47e03 77b8f30e6b07fc85f19f8397e8cb1fa07a9f49b1e669d9a2dd8a6af966cb2814bfaf225af08aecba b1cd042e92906173edf2d3a6436a7123696b9b86cbfa27e7d00f1e0efd9c43f67904303b1de88bf5 db2c76ea629e1ea778dface7aa998ecfed3d6f854e41f9e9e5b3b53ca7fed1a17ff2695a20b3fd47 3eadadbccfa87df6a059ebd132e021e4c6d60c7191cd026fae1b27b689eef7e5864befd3ffff7c52 1d9fdfeb0e7dbf5af679f06ed8e913c1b3b90fdeab071a3eac8b59e9b7d95658abadcdcfaa7d3227 f6ee6342bb42a916b5da52edffe3eb3dbb5565d66deddf52a2e48c248922288a624e98b398e3ff7f 91399ffdecb3d639ef87798dd99a3ab847815455a7572f874782127c19ce9c6547793a9edc4312c4 f4e21b6cb4f1a5bc46d3864c15dea67297a7c9907b30a8c30bad1f601b25aaf9542d690eaef2dbc7 c38f5b8dbbcfdfbbdf4a5bed652be27b86957bc68c2aab990de70d8bcbbca7c31bd9fd2e63cd8dbc 97ed42db975b9ad5e06a098ec9a6b30ccda1835d6b8ba22f8f8f45f295fbdafb618db6597033ad78 5afdcdd6acd0a17609da4f4b4056502a460fb5cbeddb2597e1b1a992a5797d361924276dfd6d5703 d50bfcb8a9372a97aed3af88af605a5681bff286767870bf8bf6c5b5b0f6c38536d1b7e45023a804 1fe6bf29aeb3ac6f09c7e3ae6c71337ee48bbe9555edfd81b2eca0ef54acf813849650598eccab44 6e4d695e7b1a8f06441aeaa5a2ebef4eaea98fecd5563718ea91a00ca50f20d2d66c5d69e1d0b011 e337b2a9551bddc86ff65ebdf263a736caeaf351f5869b7ed5fd3e462d378bce8725a7759a3bab2c d839781d3915fd33722b5263e963d7c90264b3fb126a9d7a4dcaca237ddebcae568a29e74fa6f184 a88a5108f4dffc461fb3b3899e1943076d5aa97db5dcfdce14dc52a558c0b05c4f5d6fe6b1babeca 9904756c74af55922ff49e7c841f4d3d0715a3baf35bd1785e1eaca19107c15ec7cd5dde8d52e9d1 f49dd5385f7508a8fc13538a345b8fecfa6abeb6cec635b6f2bbf7cdbcf5a88fa920f9acf15c1550 43cbdb943eeeda9c6e163b8a368d27a606eba7726149b0ad02d6f522d587977b955c916f25c8b729 85b993961c3ad38e1c96aabf715af2f303e4d02de043dd53fef8b71bd348bd5663a5b02bbffab5b9 07edeee39287eb5d879c1fc222d3b2ab76a387fad6f9946f9bf74c61642ab5f2c218bc9a3bfdab77 4ebab91dddb5193d7a6bc5e7f1d7ad1596ee0d2b78788656371d22af52395953f6c77a4961b5795d 0eb760280b1d6f2bb59fb387242d2542ece3335d54775a5354efdc21c1e42baa8f0c36c84e78b8d5 2d33df3ad6c7af7e3bccefbde8ecfed61194bce5725c3cd042cf0ec7a3d09254ba6af6bc9d670ced 75a07fe34c5f872e9f48736c7459581eb97d01e7b993ea47f25d4daec78f121c2a59f9c4d53039fc 4c59e95a39ca5247226cf13137aa62bf3effcd3df3ef6f76951f45de2d9fa1ae983039fa9a608770 28e484f556b09bf63bc114e9ad65134f374f0e2814b9557e2bfddcb9595c3ad4b537b69b1ad2331f bd6a688cf09baf4f720d579b636ea5b0a26abf2bad80f7c743955a8fa74a9d1c2fe55330ddc9c27b 7d92aea3f35d9289e7477cdc19582c380a99ff20b57cde688ef57cc6f894057b2fb7855c6f32e75d 14b9f0e8ba09736b1f57b80ab5ac7315bab5e12a4cee37f74c7e1673dd9acbe3a10503503de51fb7 b291fdec4b6587591439bb1d5972e5d133462b23d46d79ed6bc8d774d5edeceb2887335c954f77b2 2de71d7128c9656622f677cc22ff51c46d7ef42a9ef2c6b2952ef99d0a11108ae08e09b91dc3f12e a3a93cfa1a94b8f52ad3e4c8bc316177b755ccd666468e8991adc434d64e95696cc4151363c70793 4cf7b39dc685211a4ee39df1afddc3cdb34be7bd533d91ffa3431b9f60ded5ed1dd1d050ad5551ab 07aca470a3a52ddd6a91273ec365432c5c379dbc99db0c85a9bb9908b9cd66c12fdadf3defdafc95 5b1fb5375719b4618ed49614bbc76199653a5a9189ed698309e1dc2f3083be84e503dd169e195aca 7444ea3e637deadebbcfa97bbf72a593f302b5db124cd4bdcb295379bce637b7b45eef8b8d3cb4b4 14cc8a8ccca9dfd5d0e7a7a1d6a462453eafee25a9db6ddba2a6ebc5bc59b57e562ba1d870423ee9 437adc26eb8db8caa13767f75c6fcdd63efb986536f093897d0962f88783d397f92c4fb71bb045dd 2f4a95ea45d301a556b91d393c763ea41ed21c0984ad974c6b7a5352aff01772b81efec6d02dd549 060b556995297ff4fead5421c77bbb558d17c697a2c63a9caf77d5dd775d972f285996946ee4e423 5c3085990c0cde43df25ce979080ddf7f116cba2c280092bd28411446f45b77bfe9e969ce867b5a2 1eecfd4ba9ad2c42be4d8b2347e44c2741702f139382d925accf6e8dcf7df98597a8238323c76a09 4796c2184ffad5133e77cc6f535fed891a57ea67ca9949fde6d482d6deea1e970b03aa67c7858d52 e8a43ab4bf6848bdfcb79c8fc2c011905cd6e436e789c61ea091c584a74985be1adb062dedae5daa cf3c46947acacec891416d4823a39f886484f2246c659ec5e7c3138997ca828cad76be8395bb9726 46a866aa0e6edfc71b5a5dd74894b6721672ccaefa286dfb07b4da899f61f61611fead52021e9c2b dd528bb939e4870b1d3e3dc6ea7e2977e576bd5117f5625c16105b2a72fee764b2ecbda327838ebc 46f5bbf26f5504995cbb553223549b84dda9f7889c3d9fe0a5c57c81a3ee758f95afd92b464c0c80 d672550ca58f873cd210b226c25dbd006ed971048bb079ca29f82c977dee4c35fb9c651a3905daae 72b7b67ead97e3e017055679deed4c69c3cb37bb6db807c3b2824561739b8ffe48e6bd4fad91cf0e 37151e3f0a0e5baf1c2cfa869475f253cba844113b1b38fa38bb58a5f4a9612446b4d09a4ff791e3 439e208d7e790d9f3ff55f7f038b95c923a7048b4c7670ba11596d5c90a108ea9420730eda9919ea 2c33c5f5fd9981fd369b296e8b5ea638cd8c32b34c751330c022cae68a004e6011c940bfc8ef7567 eefcd1cbe50ef5ec880669a6e1c028bef439da955c263f5bd994969175a2e81e55ac7268a8485c29 1af0a557f3723d43f3b3af9d1b6675a6d285bef120822c61bacccc3abb43067e3eef60b954b200c7 6b74828f0e70a21e009cfcb34e1a273777805341f21acd3b00cfdd8620b95facaae200c13d2493cb 1485e073334108ef35fc2c2d94d3b23396fafd5b376ff7b490dbd951953947ac47bd909543cc60da c036fea280842d5eca8ec04cfee50b641b6d3d8344ed225859b30ac0e7cb3ac017502f81364dd0dd 2678de00be349352976b16e027c34a700a017ef61700bf604f806f36c90bdb9a0bf09d1c2518ed92 49c5152ff9c7ddef2e60f58dc5559f47f77d813685a57c139a913846cf3d013d894db631e8d56865 8e944968d774705c202ce454e86ad9f7321633a5862701c2525540f84327c1a50a882ad64c600e00 d1992e00d1cda4d210d1359e091608207ab490605404449f6d25d8ac003170df801832c90bc39b0f 88667302881672f2e6ea042f36db5d601addf14d2b3fe283caedd995f49cd427f962e53ce06a37b2 45df37ed7aea556f3e2a389e0cd891d3fc6c65472748c92cec28a9f2ebc980c4220390025406a4fe 93a5f5461790de2e0264195d03b2593e27d87e01d9e28804fde4136dc44dd04bded7e1b7092ee9be 5164ad210232280409de734096825b6997a9e2d6b3ee02231777ef855a697f545a3d69239a8bd234 95cc99f6e2dba640df6be0e5daa98a9ce5bc9b1d07153383c97b1590835982e34507e48b74520731 5cf401c52cda80523e4340a9dc0c504e7d07a8e07403543d0f25e850806a640b09820aa042689060 700054534b7e412ba326588580f2bd35a0aaf0a7785ecab8395e9b196d032a3f3fb41a7397933434 b27f25f326d11d51867fe9e215480891f37554cb9ab3a903365dd2045491b70135754b80da255715 751b54009d39d6014da21d408ba508d095d51ad06de404e8c879017ab345528178cbf2097a26a077 58f2fedd6002e87dfe92e08c01fad03100bd5093dfb1f8ee013d0c81f5aae670c3811448f385e95b 91ccf8929fcbef1dc715e805f51956227c7d8e7a485b7837b3590af5d346fbd5f6478c2edf6a805e 130d409fb9f0aff0cb95da8029f40780299da68069083b9034f01530c707002c6c1120e96125c0b2 c9579b65a72dc072f212b0e8f60158be4e0316a78aa9408cc77dc012ad18b02499b5400e250dc4ec a2eac91e7f25f08eae7c0d7b1ce9c13eb322b0233f41da27a70fcddbd5f06f61694da90efdaff198 cefe23ae56a5597a983fea6fb002ec7579005c06ba012e1f640157bed3801bbb05c0ad6f15c06d4a c94873f3de016e127c013765857f76bbeb4e003797938fcd8f9839b14b9c560d5ab83cc0da1961bd 1bfec6694caf308989157edc20d7fb770695bea75ed24ad5cedfdad2b27e19181c1e6dfefa7b53cf f1cfe4fb0b76f82fd5b7377d01fe80c140c80e0420d8741a402b7426f5bfd9c2ddcd19083d0f0542 1fd312ec9a401854d720b9e23e09fa74bab5a62ae62ba408f7bd2c975c9c6f723eeb5cd0163fd865 e1627bf6b7d17e4df4a7b0a4a654524d932f52e1398d3a4e6dc7a9d9f8ff26fca65a6b9ae9bb9445 907f1d4a4074c22e10fbe43ac1e20dc4419105e23057fa9b293c0a52e5564c0635bf481a5163a2b2 2c03d865f8c338c4d287203853ee3cb2b331bffb2b94a749d16953fd0afba3f5fe6bed4d8fff1f11 134a6a86fd5f9aef9f60877433b93f726fbb0c649eef037989c6408ed730904fc9fc5c3eb3215078 780b1437868012d6f240197e8aff2df3fe0d6f48235b272feb692ebee547710b8777f708f7ae7b31 1e5df8513b3add8bce2c1ee2ece218d1d9d561c65d37fba538dfee366a73b7dd1ba5c3262ee68f1b 7ec99dd76d9cbead7a34fa5abe1a0c94dcf4186c613eb49f0d6e5efc348cd9d2bcd567d8bcb399fa 1807fd49410828834af1dfbb8665c1f36b6dbef72559cd5df7f90279e187347f16279c1abf5cd44c 13117695175fdcc4d6e577eb5cb7d1c85df5c8b0bc7c054175a99dbc6011e5adc602eaeaad79f125 75664b5de8cfb089309efa396136d9bbea66c26cac380aa9e039bed4b7e858bcb0f2a82745d591f2 6cfd760d7bbed35dc3fed93a2cfdf9df7908f9dd4999d5b687620cc5dbdac73baddb0895b408417d 964386ce2da0f61999c3fd1d31c3c6537a4ace5aec24488670515cae8811bf2bcae3366316c6625c fc29b7a39e60da23e5eafe8f5bb6160e226d3a1840d16ed67772b9636fe9baa0876dce7c77535d35 ba3ea32fba64dc3efe8b7fb7e2fa374b762d2ec6e58579e67ff96933f7756f4ec9c9be376116f124 0ab1fb6adca62efb518fdb9f86afd6fe36d4ee9be73f56d909e8cf8c79b6ef6456486f599ce3a955 b6bbf1565c6a95edecabd79f55162fb5e3500adafcd5eba592aabc59b7c497f26af6f457bea966c6 b5a60a15a6e16bc64d52fcd930ece7e54d1d93ffb864ebccb84d7cd89172ca1506dfced31a4083a7 d7774026e8c313aed3c316c4a0eb63449446b6fae8ac1350f4aa9d5c50db76c8d187367fe64eadb6 485f5be29d7b347b2af7692a6f2d1b0e0d136b7ca72ed3888a63a5012d8fc5ba43d0611dde8de6c1 32505f81c75172809d233f8d914d91e67298c5591a859026c9a6aadfe0dbde7dfaf0587b75378e05 77024225db71cd11da7cecabadb610982df13a749b3d79e887afc1b01e6adf5eb3115961bb6165db bd06341f8eeab35234a9c3ebe16fe2112c2bcb4de091f1b1b6092ed71a797abdff8fa4818de1f31f 32a85cc68b79a56d0db2152947e413748a0946a5d4cb3b4b86b5f9549f4aa5b43ff902fce1ba6ef6 c4c93e352aabdb7be33bba818695f920f59993a3eaf02ac705cb3297fcf53bba50dbd4f2668d8cb9 62751fe6dd6a2014fc2a7335023f948da6cf3f4bddca65501f55c46f675eee59a36d59cd5eafded0 4581a763e5df5a5cf7bbb9eaae45eb75178aa175823d549a35192641455e446c54f8573e4d15dea4 407bf6473e75729561e061b579cda71abbeabe3eb824c3e9f1d38f5bfb8c1f8a77a472e9ddd3f98d f8bed0e5fbe8c5977b262479afe94bf1860ea47b3a82d9ee77c57bae45f05517da155b2587a90c4a 703c5d389e703e3ad82dff29fa4a8b2c926fc2b4f7a379dbde8f83831d98978f1d58029aaafae906 82c9e9ee04b8be0f5f3d7a9214a8756b9bca26ac06ac55f3c3bcdeacb4156758be0ffd59727b0e37 ded06e1f3d3d175dddef62f3722d6cf975a1cd215b72a82d52820f27c259d61f8ce3f140286edae0 372328fa12aedbfb3eebd841c1aa5af138ec58a1b99f9a97397c30dba5dad794308833ee9b6ec9e8 550b6343a5277b4365c03d6dcd9e97795d9af756755e9fd96eafba0ffce47ae91cbcf23d6955ef15 159cf4dccce58a6ba1a5666956a9f64bf0be1e39cb60b474b0d3685bdc34e7c722799b5fec7d77fb b003e5f0b6e2e1316385fa0d362f931b6eb68b39d6b82f09c9e87986a9bfb6d5b23eac6edbda37ce 4eb528f47f8f2234e87acd15665d5f2e382a16141c67312d20c8739bfab7d3d66c7ce34f54f33758 b7d221d4baf76eac92c20e74b1e48ca67a09bed574078b8b76d1171abebdef74439b790eba56a80d 8616ff5dfc666b66db9a2e4c893a6e8d5efd7834d4c2e7aabf31faadeb3e93497ad322a659f316a7 65ebc742c1f9a2aebaf2eda65a960e33653b60cf0a8d4d10f9b09554b95e7d35e57ab7314f703cf4 6785e2a3d52e1d7ecbb1ebc8e438497dc6dea7ebb74b0b7f1b3815b4502ed2c5ae69d73d46b5d9e8 5bb0c2235434af3c56311e16d930d403dfd1df7573a4eb9fe2549b542a2bcd261b3b2d7b9f9e0b25 67fd28a0ec37a396c7e4efd99ab223354ea9910b5d3ef63e6599470b1da9d51a2d24d162ef6277de c745c5158cfc0bbb77f32fa1b14970bef6b0697c0edf95f9a6b6933f8b8a5260c66e7168751cb23e a9db8d3d5ab65acba9637693c9f44f4f7bf663d52854da057db45f96f48cf30a34fb43760a0b931c 14d03d3f512baab254897771a7d4ca95937cbc35ee72a39b0c7d2ff01e91c4f0cb887743534565d7 2ae587cab999d711632644e5c959b0c4c22f45919ff5b605de29042d7e366056097e71e3afcda531 b96ff7d546e3b6f4be15685cc2b642c78ec7cdba2521998a31b4dda21e712f5d9b4181acc1ac2a17 dca667a99bccd057f670bbae30e1b02d37aed12fe152e627f1546ae7f6492bc5d05eece9d839ffda 8aaffcb0ea6585efb3470ad1e22e095981b1796752abf348f51971decb8e397cf884d9add154d96a 32194ab05fb05b4b3bb79bc2ec5a77d5edd117a3f51f3f74f7322eee9f48c76aef4b7563785d9775 6b603a1a426ef402f6d66595bc3e0485793e5439d47147ba6c055fbc1fa9baa86a423bfffaea83bc ee3b13217a042bc1eaf7f642b614df78e7fcfdf248a4fe2e01ae4c8502873f2e265b75f91a73d834 864cbd933b309cdc85e8d38b93e8e6f014d0cd5130a34fef6fdcea82cb33a88c26c78a4a2d56a5e5 6b33b64f6eb66bf655bba14fec45a5b0f2b1a2eabf434d0976a9c9420e9f87bc24457d49ec675766 7e84ce4b79d0dafa82753f34f8b99af41bc8ea3ae0562d7cc6954d7dcb6e6756cc5683e18b39bc0f 39a6be6659866b851add82e20a9d9f2b7daacb4db7947c5100f96c9f7f2b24c9815cf7c96737e9dd 9fb5debea9d1cb672d20467159af0f56ce568fc6d675fde81aa391dcd04a6ea3ac6ef7dfa2c2118e 265d070751ecfb553e3fea1778613296657e3e2b983cca965c8ec8172a6cb55bf88d6c58ba58ef30 47663e66b8e161419f0db0a75b847aa7f3b70e44758b078a52584125074ddf2535e3de21c6336d45 98c1ed4d405c8bc7a71bd42520ec3a264ccfd9856663f8ac86b566ec59e7c6aa7898b4c7a91f1a5a 75747b9aab17ca1bb7acd44f1b5bba29922616da23319f2915f864b271a6b9b57216d89d7e2bb0f4 2e6333dcf1e9d2e72151a3454084d46d6b244dd5e9ccc8576ebe25078beb99f80af8878840f5b7a0 8c30b727099f3178112fbe7a2d6ca9bfe69897693c306c46d2e8c6593b5832b81e62ee13ac1bc56f efe5b7e372ec964477659f73fed8f838a38e567a9e03b5466965b9958f6c71a0670bf9ccbc2ef2ae 984bb31f48784ab38cd065e94bae21d2a23bd0a9ee65e1902f695126b5f9b14e7c1bb92e615ed011 0145c6129ff9fd18870fab27b61c1008867d0d0edd6c56264ad14880ec9fc329120cf91bc2ea7b12 8e6fe6efd137c28a5c0f095a93651a78532980425c2af7b4957573adb19159353b858ab60e149ec3 3c49517c2b3fde028d775b729eadc56b96094183a6c5234f52ca97e7489dd254c22a6826017d2a2e ee541a3f7f1ab6dcb742ccebed06e8e67d9ea2fe9ade2107d1b822c17d90814fce93869b68a190eb 889d4af6d17b0fb2fd4270c8aa5f3c07bddb4339ab5e2bf56c1f7f8e6b074e7a960d9b8b1d7a84fd b198eb3051fee9d06a2d1a07b2d8fc7aa236b42ca178d8163872cf894ca82f385a922b0c395c7f09 22eb6c281c19c502864f2f05741b6045943a611e5237f800e132c50edc2c5646b94ed858641fd7c1 3e5bb0cf0fe803f3bf853190e1d57828233ced8cdd359a999c725a82f9db7f83f919613339ee55c9 e478b3536d9af8d3cb3572719127e095313e6447850d936f2b61b61548caede2e5ad6454c17b8595 c6b22a2dd1eded9427dfaef68b3a266ce84e62e572882347d8a1e116f0a55c37e714b24fd7b3b285 8def42632aa842c6216a65a6857894c98dbf4bb0b0c418a087c607a047884ad0d2011a13f504ab39 404ffe0ba0679603e83afead5801e846ebf8722ffb2aadc7b7d8bad3f1529f15c058dd0fb98e2c71 6e20eacac6139caa6073fe78a431c28a90a9bebdc813934864b135b2a79063cdc1738af4c021b332 a232c5de3c9f81d55301b8a36b2a3f6066d64ba03712f4fa095e0b80f9c553823d0058556312ec0c 80d54a21c082ec32c1f203b07a9007984b56128c076553583d1d26181f5337bce6954f63258c3e6d 496d69bf9b4dde5a2ecbbcf7648ba9b04f3d93b34366dc8188a3739243196246e7ba570483cccf1c 079e5a2601f6ee0b0047ae0580a3543181e327885a095e238063da3ac1f8027081c9261870a90e9d a7ed04ab36c04567037009cf009cda4800a76b3580c399c85dd29da7dd2ed78e46560a9705aa3bff 2b99ff8cf0c2dc5b56389ac71dfaa6764cd2cc3d55dcf51b12ca34bf7caea75bbfa13a64950e18c0 837282645e08f03990017e50cd04810bf0c7be0ef027dc4d604e124c77808089478216020824934f d02c0102a57a09367b807ffd1c2080a801fc7a6c00fc56fff59ece7e537898efa276d4562567f947 327f1c9e5d310315439ea80e7da6258112f925ca16bee46f1a1a148a72ae17cf78283b4028404023 1c10e2890684852747f5751510edc8014474f701b1e59ba90e7d6d0c0109ce4b4066b81810b7fa3b c133f9e4bd2403320b2a80788443403c99e4d5e7050544dc350071323a8058bd7769e0ba61ebe4b1 501deb2b251f8e26526190e90b2ea2fcd1cb7f0fbd5ef56c994060bb880687859e2bc02f29333fea 5c520cc202e2ab0a80e43d11905a4f05a4bb370119c22e2087c53a2097e32e20e3670428c4de2698 5c0185a250822a9de0ad030af36ba9d689c313408171f2864c91047f329fefdb3e200f7e6c7ef2e7 878e76e0586d90ea5aeeeaf3597ebafe0cb920c3b7e95e3baca7ee72b45ea59d5ca1a76919971e4a 494d4182c5a600a8dcd7480fc3c026a014d20254517100d5ed560135dd8580dad3434043f505a0c9 c301d096fa4a3042006d2302a0e5c006b40235130c968056f3cf041f0ed0dcc80334a68f13dc4f46 51593fd34d510fdf531acd227eedfe9cf79ddd8891865087cc7ef4067ad4fb7ef60b4d8b6023dfb5 b43640d59422a06eade4b711d3ca5ff7712aaefe9cbe7fa5dfe4cc31e8680c180ddfa60e62d7bb00 66700280897532c15201cc8af1124c7b8059177680199f9357a38e0898895a054ce735034cb775d3 dd78f74ea3cce55e101c8515565fb279621a913f5719c6a85433fb8d884a1a439dbab6d3dad2b27e c5fc519f53cd37b51dffc455e6711dfd1fb113c20cb09df60eb03be801b84cf26de1f2f75fba70cd 049c79ae01ce2a4780b3b3699c2e278f50c029969ee01b028e1daf01c7e55f3a8ec0195568684fd1 26f498e3faf69a32bdc6140bc46890036e3f04747eedff3545a785fd6afa53c4ff788e7f76e3f430 a99736d530ff43f84db55667f0027c1f4600ff98fe36932bb840b00f1d2028fa2ac1fd0504366480 c0892520a0e70110b0660c041c850a87110fa7e6eddf8c60b7942ef4502cecd2d46db860f82350e3 0acd3fa6e8ff947bffb5f6fed19c7f1bdca5c1c2a9b8fa275de27f8285d3688764f8955a7bffc8bd b6024496aa0131d84740ac271d81587ac12019e8a840f48c0610f5f7128846ef05449322956e1143 d38fb292cdde49e45d8891aea4ad33952bfecfe94b3dda493bfd6bed4d6dc76931a9f0fc6fc4c4ff 4df34de384ff4df2fd57ebfd24f759b9b1580179597e0379457009b6ee9fdde382119037fc05c8d1 0605f244fbbfe421e05082807e7eed487dccb0a17d5f5251e58ed5f7cd6beddeee9c2f86de3f294b 30387efdede830e39ae37f62116ebfc1ed26b6d7930dbf98ced66d6cb058f5a8da6af9aad7764bed 5c8b17e6bd729b17dfee7bb63486e87453dc0b5372954ca702e23c8ee2a0f9f83707617c69eed091 f252a0ffa833394cea43bd6301f2737a1e73a7bbd5c2e2215a638e11d990f6cb3ca36c622b90d7e2 9afded1f86684b2d7e180bf3bab5e63379e5cce1c1c49db9df7e79bab19afe949cd7834980d69b51 5ca977237edf19a759aaec6831ba37b787917247dec3a15aa506df31b0ffd935ccbdfdaf5dc37e7e d9ff1dd940bc2fe74b61ba3c46f826da32a36db41697cdc572481b9b45c41bf17c2619f75452fdd0 afa90f616012c0df6c14e2313cbe54f7d8583c2e89d1bdb16746ca65c90d8752240cb5c7521e4485 5961008d6766df81366e6fe9c4b51eb6fa76ba3e29cebae47e7ae9041c8376980beab6e3f6eaf7d7 b44339bbf917afc1f7fefdb3cfd5107e8dff240dfc44b5e4bcb3fa84598052c46fb2b5b178c8b646 f7ba301c29677536f8f6f0e5001a814ddf01ef7d6f695f8fff5865afb7d4a58a3d9e5d720bbeff87 5b3647b42e6d946db5a582d2bcf7cb7653f9f4abe1d07c0c423deb1f1a918ba30d0bfb961a163ee8 37a06deb0f6e64583e1ca1aad74b179cffa3a7fd31ca6a572c37800624dd872346ec6173b2d0f551 deee92eb62a513906ebd1db25ab375692a9d563b6f0e9af7ae366ef61473da545ec54538d4b555a8 83e2ae11d9c5b861e56ab7facc6dbcebf0a6974d25553fa602ec0849359f374b35f2b2eb54f79dfa b11a2825bacabc413135c8a648b7354b75e874597cea98fce33fdd7bfeb11dd2d6b5255eac6fb327 5591f0d56f52e1b03011d238ddf15c6958d05cafcf9cf92f6960550a96e57939f08855b5b6a9cdeb 359f59356b647ce856f7e1755865ae8fa91f7791f53f4903eaa322814eaedcb30f6c598575db1b7a fb8e372cafaf9e4ebabf6d763c9dca1829fe2cdefff9250750bff64c45de3f06d9a19a8b1ad0641a d59ddc6d1b78d8ed54dbf8cf7bcda74950dd37c85c35e045bcca5c4cc60f258df7f94751aab40b9a 52113fa6f6275fc0b5ca3db356f25eb35ac51b3addbafb5d0dda6e54de8d5c68f75c966641fe5482 4fd5afb36c3e19c74beeac0ef66007c5cd007a2468e309623e3dddff6aa7a941361526d338dd9f7c ba0f82ba1f37fd76e5d20dc795b6325b96efc3e5aedcd32fe7b20abe0f6f687f3f9e0e23901b7959 d8b5700473a12d45951c1a614af091129c6543521cec22e9c54ddb2b15c94750b5f7fde56fb66633 9ffbc48a23e56009d9eec7bc2c18ce6cbb6bd76c7b93b92911d435413d3316772d3855d1521d3a55 78ff977cdad34a456f68c925f73b37aaae8556db2eb4ee0f4b0e39fa89902578bf5a39cbe0b073b0 d3212e6e9ad76b91bc1deef6be7b7ddb81f2c9d8cc3b075ba191c32d21433366bb98174d092ee946 cf6bba868a9f9afab0868c759d090edaf79485b4a83994344b34eb1a743f4e5349f5913f0c2269fa 4eae47b049d5f23f558acf7ec9d3a18fe142ab965a82778aec788c96b448e8262d72ad55ed406efc 954fe3c16264f1dfe9c4bc44eb852965176be33edfed8d5ee99c26c3bed6c7ab3eac3c5fda77ffcd 68519dc134e82c328559aba614e0fbd45197fd5ca8621f7ba26cc6a77332672fa30a95038504cba6 42c164947e79d2d60cb0f5a55969574f650f50b45d42f28ddfaaefe2e68549f67e33e46ce6e1b056 58f044f3320e755382da45a3e7f4cafa6bd5abe9c372146adf5dd4d1a2da72a059cc3eb9d1859b59 c1112eeb027cfd1e544f812eca6688bd145fd773f27e122463d36afc4b2297e2175f96c261b72b5e 09742bb677fdaf283904977f202b2fff40a956fa0d6a44fa62580d7ac3d07b0f0e7ea954209c22cd 54f4a48f8925b353f279a34f3c4843ad6d297d18f619edbb4da659163df9fd3585596362161cede4 aacbe8e4ab3875ad2b9b67b6adf8036a201f306122071767259dacf02809f3de4dbcd61f90287d59 2adff77d255f90ee6561b4d07a42065b6cf9c9b598e1edea59e0734ab392208d344a1fe6d411ba36 a85c4761d3b537db5a71d7a23dab15b66ce3797815f471c517b529f360b41c5ec60a735bc60b4883 a355ef6b4b4a355b48ced2a16acb75aeea4aa771dd979a9549285ef7938ed8e99d46bf29ee1379cf f3fdb374103eadf02198e421c74ffb08c3174b81ce2dd61b9fc32466c0ae1fd3034b6a0cc4ecbe6b 89d9837a90e09adcabc7cd456debbac3f280e8754a18b5fda3439bcac4b57573bd491b6d89b07975 7d9d332a99b309a5c6e750f9683e09990b9e9cd4420945bcb5f2a6a84085527ee0142a798dad3684 f1a8d911a04c67c84f77db390f2b8f1db744841b8735cb19d6cf4c4896dce32a13a8fe6f85241d8f f63d3a34cd1d75d95f3254bbe4e6a9b64b57a9cb62320887fbd2a25a8fcc9137eeb6bba9646e89af 6759ff1e155b83efc382eae7b37985393619998f505c6aed17b07867c768fe45b4c8bcd65dff9eb0 0be3d75a1520ef68f2c5edd1e196f2d3e7dc57aec16e3cbacbfa821d31fb4973cd04d5c3998e5fd9 0f1d2e4bc985828f444aea02877c14c30ea92e5f6be25d6e7d891149f2096e3e31cacadd86f5967f 7e68bf6555266e71e9758bcc67d43055fd58d66617d852376f4f5558fb9a972edf52d2a5f66e78fe b56ac1425423330214f3280f5f4586f36425cf6e16a6ca5298693041db7518d61a54e95376dda485 faeda7aa53578d9e53d2c63e908ff6e44916b21f84781f4b79c2e02736918c765bb86d0f97780ee6 5fd87cb967b1f9a8e1e1d94fae532f350bab4a776e4e528bb92d88d5baa14f16e5c24a7c59cac1d0 55e9eaa6bda7d8a76426afafd6b89055f82c8fb8af0c87fbfb2c4bd59728139c63866e427b81ba3a b1427538c8a4a48fe891fd8a199005b2d121def7dd84309ccf86c870c60db7c7832c9ef3731c563a 180686d6b73fe3185afeaa3394882e0f64277b14b253f8a4ffec2e5a41255fda9687a83871fcaad0 b5a49e5dd7eddca0ac6e7bb1259fa6822a3e361d216f70399a9f771b185716a06c1ad4c77068f143 e7492b4b757ae1af9326fb239f253e442812a35da41146f7e2e053f8ebe3f6496c610bc31d616e66 b044d7b5f309ada80a4076a30689d42a190d3eeeed2adc08ae51ee7c762e39514c0648a2343473e7 ad58af310ab7f1a20af9dbe53775c39bea5bae6b25ade2298753644a9d2550f21faa2a08b9fc91e2 562f176369eb92a59b67f74b7521e64d0e582a438c0d0ac1a7458ac273b124608bd052300cf826ba 9ef45c94a49701b27b41bf4e1a6174660ac778e300f39ddd2b270e5e48b6876bf9acb25d97a021c3 75212d5eed32dfa69181b435a52468bb69da8d3bbb6353bbc983ae611874bd508e4c4f3ee7dba638 c0ae4a3e53b7051eed2e7e0a145b033c4a9febcb2cf9dc8b5fe2f3fabef1e976f6c216cf7906ddf0 c97d9b6c3f58a4f678e411664169708809c55c5b922ad97b5f6f6455341c40aff57e09e9127cca24 c30690b10a4b3a93c51413cc36ebdf2e5800c9d1f304a71740e00e071c95b22bf76b669b8c742e13 abd3ba7653695fad65454fba757d333fb6e632bf68d13c47bc0614d3a83218d55d473952ebe9197c 86c76fcccbb59fc87ee37fe1b05ffa6510e724b380677bfb120bbdd9521e1a9e3a1a04f27d3b634d b77e26eb8016704e520490667b079016949433ee910089183dc12e0448a7b4044817ff24d8f20009 74a73cca9eb70e8d2f933e66b6fa2d25d1d0c53150781472c5e7cb3285e9bc2ff31889f26c6d38a0 e90bcde0e4906ce70868c566b0e5e4f245f65fe799eba0e0098d6a876f66121ee14cf67a2640493e 33007965c4040503a0f0a80c5004b452e516b12280b2f101a09cf64910d300e55d13a042ae9560b5 052851ca009444c404b38a67738bad7d967b9354da57779769208be5bb9bffea9229c0a5bec255ee 743e55f5a99e06a54f3ca2590fc11143c8a2d4620be07c567d411fe2facae4bcce1ba09d71524927 29159dd27c82929a6064277857013a533b00ddcf66003d903f59ba07007a24d904d3e42db191bc7a caa6137674354fb0f6928f6fb07a694db6b656d7f527dae256ea28b1d40dc4576d969cb50965f21e da57d940cde5e976a7c5927a04917809e96168b598dcf49a9b4906fab4b24989aff90b60f8e80b30 e1993a2131436600566e4a09563ac05ab897c06924980d00d68697096a9704af2cc026ad3cc0a684 0bb0eea40fb09e1303ac8e63096213606ead5d3cae948df1b5d449c187e58edc2e557e3af45fc9dc a6acd408cf08e14724df830a87cf67770aad268351b8c5bf61e8f33140062e5edf005b9e922a4f68 52e2db20008e8c0480b35715e005d6067829f0015e8fdb0077b171825aaaa7e1eef90ef0d04501ee 9d258097ab158057f0e40d95fd15e0564801dc561c80cb9fa12d4eb18dee70d9486505a223f5ce6e 3d6f0d238f5fd1429165f7758d7a5c3f32616f5c01dd353734dc5aea3fcd068ab84e0e2c7b7406e0 2a0d01bc67c3009f75922af75b16e00f200222ab1b80a07a2e20ac571d1055ad9f60304ff03902a2 68bc13ec0840384621c1b50688523805842b3e00a1bdd8542016a66540e44b91a9bb974d614b9c22 f95acd75ff64c76497b19f0c8b2887eebcab069939de548c2c8979b8ad442c14cd1ff8dfc2ee1d0c 10e6890244e541ff557d37521e10175f4d1dc4d8c606a4405601e9d5da800c7763408eb84d82e635 c1270bc896cd263899806cfb4d4056a16582c50790c54a1e904ebe0648f534d34bd9de5aad17b713 e9f1ccf4c4d1b8db4a83637ebece9685b8e4f856b2305f9a16e0f61e12213b27318010b7e4ff16a3 53cdb73154fe7121c71a205f900528464b46e54a2b0054edd307d4cc9eff499ca01ed42b41074b05 e2c35bfc2706f9db05d472b003d4cacc026a9c51126c4340b5ad456187061ba5f93acca4a75d1809 8ba0d2661b8b28a074e45dc6714f2ec29d4a45cb2ce830ffb730b72da7e5a4874925e854f34d0dc8 a9b89a3a7d532133157e5b740bd0ebea10d09fc71a30bc75014ce30a01c693e804330d3006534db0 18034634ce8091723860a8890118da69a702310c6dd5e338dc29220296a2d990232ec89b5dea2d85 0dbc5cdcfa69ca4966b17d15fe0ae56961a9f1f87f24d5fff41cffabfa1e0e69cac81fe197c6a780 b5fd2d60a7d0137099360a3811130147d68a80a3322dc0659b4bc0e5d817609f0716b0b15f02ec89 190276353dfc316fbf46d456c0257a9aa6831368cb6a22a25dafa6a1dacdca2f3dc474fe6ae4ffca bdff91779c660bffbbd1ddffd3e9ab8e3f80ef6b34e01f670d0874357d1421e41e23c0bffc23e04f 480ef0ab9902f871b10ef84d6e9ea07355dae3d735dd55936d9ed10559b4c911cabfa50ee43ce8da 5f953c4d8afe57ebfdff8b98f87f6abeffcaaca9b5374479908c738b7f5dbd25780dc4c2e00d445e 6481889d4bc99da33e00f91bf2cb14eeff0cfe69fe364f5fae47ea1b64d65870c1a6d91cbfedfc3d a169cbfddaeb4f1ac7af81fe789fff751aff4711ff6abdff8408ff6beddd6d0b7f931cfe28bdbd32 901bb508c82fe306e4e51103f2aa690079ccb680dcdaac81ec173f40b662eabf65debfe10dcf2897 cc5d8b1be7f7bdb96375d5be55ce9e7f8955a17e16278f301e62a3d6d13c943bfba58876b67b7dd0 dec476a5b3beb8ca2f1901ebad94033c5c6aa7fb7811e5efb3f94cd92fe7f030decdb0497c9e928b d76712e0021e8574f0cbb61b5f42a63916afcff84f0a424f29665383ec009a379eff51e51fabec3e a9e6dcfacaaf78887cc1d1dcdd91031ceec95de5d966d66de44a2f5fb53ebb8884123f9fc9723e0d 651870d20c8b72cad4873e85c9deb9e99300f9d8515c7e96227e772b8fdbcc3318ddc34f6bf8ea7c 0643ed45cc07915e3a0da0c91aea3bb0adfcb36bd825fe77c3b07fb364533f74b4ffa41aea499931 93dd469c34567777575f6a87456f01b5c6d16ca936e6336c34d84dc9692b8e62af78195faaca7d2c 1ec5d748b9609fe1abcd4243ed81e70651218b0ccc0f4ef4676696eac3b3ecef296ecf43b2627753 460a5d72c7163b01a357db71d8eeb5f9eb65d5ba741bdf96f836b564c24c7413d4e7cd9e599aa52e d9345822cd43f89334f0d3fd5213ea849997f8b1b8cfab23e594ff3dfa1e0ef34679f0ed96ea8348 e974fb33bd3eec2ded52d4ddb8f2b44b6eb8796a95ed3007ee7f5b652f2df9d612eff967b3a7e6bf 4de5ed22e1d0a8518def7420362cf868d56765ba5e87779d692aa906d627f038550b3c1e6afd31ca fefe77e1fbbbd59fadb8d20dc3d2adb87e1a6abff8d11edd8d437e3bfbb28475989dceb4c3e466dd ba84bef6cf7e5c3dafa93cdb7e38d4c220955423bff1af5bb63e2b0583ba83b6a3605969cf038f8c 92d319cc8e35f2b4bf57f7ad6ca6cadc25d20fd550a95c46f7df7e5c835945ca76731529676be5fb a2e9a4d8f093a3f78f0e9d3a11ff3b64c05e35bea372dcb032cd477d561c80ba03af913abcbc5181 87dfb83f49033efd54aafbc64dab06fcd3f4e3f6d9f643e9e9fafc33e357da854f5049e632bffee6 9fb00163e60d4bd59dfb5d8f6e6ee4e760173a94f2a55923eb971cfeb22e39c2062e39f9a25c826f 8a949ee9b4c4ae9f7c2bd3ea5291aa0e2ff84e6d53d937abfb60dfaf32a7efcf41ec8722bcf5f93b 7faa5c7adabdd256dd4ff93ef2a072cf0810ef35f5306f580c284f8743d6fd2e7b79d7c2dbb20b6d 875ac9a123bb94cc40cb8ec75fea0e7625fb455fd6e6f67ed08aed4087a064acd2fda91c563c35ea 56e8088704a7ac1596702215f4ffc8a7ffe33ead91fb76dde7af8f72ea3e2d2bdfaae70dad4eddfd ce275d372a1dc6ae85be172eb441b6a5994f1d4bf0013f3bcb3afb7bb0e26067ea55dcb4d86fd117 e5acbdefe5113b500dd28a47066b854655322fd38e61b68b87b2715fbe43a357b626fa6b171df561 2064759dbda8bace9dfbbaced70fdaf772fd0ccc47f7954a680d68d2e8a78119ac55fb6ff95453fe 914f2b6ed117ba357bdf1934ed405ef46ce6b91b59f1e03cb542edb9342fd16d63b6ade7de947299 93d173333743c5d097feda8890ae53cacf09a945f522a359dc4029cc5abb62c191e886baec5723d5 d3c059c5be7d4cc58163299b29334cd0dd7437c5e52915f46b3e1935d2125d0b19179c658d148b3e 3fe3ec4072682b2c48ac79194bf9d40c6bfaba719f057fe4534345a29afe5a6d9aba4e2c3bda77b7 e96b512d1e6b50bc9f1666e1655980af97adbaec4227157b610f653354330a053c5cde4f96a2cce6 beb6142f8b0d29f4ce334920aabfd544e2654fb3091e65f17270baa9189d9e733fcc672bee7776d6 93e683f27660f98c79593d70e33e6dc2860adb397de8d998f6ddda9466d1d5bc061d3b6ac1e13b46 01be8c8aeab23df754ec31f9adf45236fd555d213ff3a6bc1f1f7a72603ec6523c7bcfa5b044edc4 cb5aba88ed4af0cddff7132cdfab2392f03a5b8e306cc51d4197dc2dff7d4299046b998f86b49b8a d1e9838754d02f39cdc1aff7b483ef45343ba8c4e8efda1cd726783e57983d56df8253687ed465ab 9651b17b0b557cb5452ae47bccc981314eda26b350a5b038d525015edb62db5bbba2841ffdfc7dfb 09f32a93e90aaf988b045d2ffdf26cf868dbbff056370371b35781e19ce5ca6057045567bd5d3463 f140bcb3b873a198cd472c84daeb394ebed852d50356c1296ebb0dddbc3e66a23ebae3ac96d33b84 ba3a6672ca763d00f221401f72f0faa4a173ac7e4e86b1d518122fc7181125ed4ee67bd185c9ab55 282f0c8f9022e803c610805b74796b935c49d9f6a0c595b2bb11871c89355bd6ec0bb3dd8c330c2d 27ffea8bb24973ee23a49a5b6741e5abafdf61c81b1cd0e48df96ae989af5c3378d5cd1ee492dd80 bb9631286d14ade8b0825aa9362985c172a874eed532e2ad89be45d96e5cf28f45f19aef6bee5378 b7034830b26d949f944282cf9e7b2c371f6f7e2e550e85360576b5ff1659a28055981de7b6187a3d 8de886f9d952e743fe4689854696bc7d6e1ca9f8864d0c0ef326a135d4153ebe2c5ff8f81a0a09be 76b06c309df2a386d49c35277b9628542c7d7c5efe666b05cc7bf30ad33048a975df21a2d2d43279 ad7a7b0be341eb2e6436fc992f66993bb7e0e017878e852c5b21759cd9f51c86a9a9419e3eaeda2a dd686e6cea92b95728318043f2ae9a435259b596c4307ffc793908edce67f0c8693138b43aebd8ac 556e60f0fd3a479775ef817a3ccea2cbc6ccaaf911d4f1468347adb81be7cba6d2312dad780f1595 dc9e78e91271a4a8bcbbb0f09d32197ea68d5fdc92e2af1ce6a45b55b295d1f2c4d4a6d18d8ee1cd 97e6c3538e6a5d4f18254e1196ecc1489e5462552386bae7103a3e0bf0a873e9e256919f61b3b876 c4107df74157199e44f1da4043fc53a68650cdce143e907872e2f7db5f7e1a7ca01ca35a27bf5d77 eae1a11d4344d9d09ea255f0544f51587ac58b8f2c4ae675a306a72609ceb32a4fd64fee394c908f 623a54ac3dd5d6a598bc4fd41bf13a5a5f42e7ed2cfefd7608dcf247bf4e1acf4a57059be7281b43 5ca18caef8b085e2d1728c6cabe40ea19e95275cd73658ae5985a5ecf558f1b29d3033ccca78fd08 3d76682e2b6726621a21535a6c41685d9c4c59b734d252fd91aec84263c4a7f11f490582352dc2e9 4c88f5e7e69361cbf72b95141b930f81dd136fe9bd2740e97dc6b3edec0b731e398021731945cb68 8142f1733d8f54cd9186d0e4c385eb7daa916b8ed95ef646fa8bacdc5bfece0df42c5019a8300ed8 ccc77c599971b6d1ce644a9f7526a3753e196358e02af2fb357036d43534efcb5b59735a2f53096e 942c499b2a9707a31dc19760054ee74e0cbb261fd4b536bb907ddd8a0970b8eed246fb8c36184a8e 8e28a14e6e4875b4fac047e29885ebbb0f9e6bbe3e4cf666e4c4acbccb1bd040297950e1350a3363 ef31ce9884ba03d3eef0058a2a4280dcbbad81dc470c41ee7c5e81dca5fe06b92df13b3765cd8807 c57d7d1b1a6f7c572e78f7d8949b4a56160b94cd09d9db00e7ca3a96630ef3d697ce93d0837cc4bd 0b615485189b7f563b742d76b6480db177309f29c4d9ae47dca0e7967e43838e99cd7c9ec59ffc90 192f065c06c2a63228b66f368065ba0a607b340070115927e83d011c485482d80470a9d202b04b6f 13ec0080f592e041b5d9c08ecf93d000d56959adaa4b53ea0c1e72de28712c8f2a8d9f08c956a36f 8e3ea3952fd5d9434fb2308c6eb87dc4630c5d1f0ec86ed6dce52e197a0b0db1e33e1351c70b9871 8b3b80c7c72f8023184e60d200de2475c2db4c52e7b55a4f701a02f8a66f0192b97f521d1a0a1880 64b1e40d8f6907c0cff201c0712e9b60a59496c3f6c0ea40cd509b0f5a65a53e1a9a62ffb292047b 8db25c39aee0cc51bae7521d827c06d08b30f1e11d735dea8ceca1d511e647f6ef12c82acfcf3663 598b3d40b86e9ce0f80088c640098a58823107102f53486094124c1a0069b23f597a7800488bfc26 98b100699b0e403ab90140aaf313406ae5e4b30e6e38fed6fb3d26320b6c29fca3eee7a79e911f4b 238977890fcbeec20ac1708f3b4cddc641e68faa0fcbc11d2523e40287ea24ceaa457a9789bec7a4 6dfa83a4cef9f9029023f701c8b3060334b7a6000a67d299340a17748032933240d96c2b813f05a8 fa3d01b4106601aa617980f2630fa0823d062891bd2638d0008502c73e2da4813ecd24432efac07b e27329e8c2b4df94b8f5eec3b18c5021e956ff8ea4cb15ee1e84cff4ef17dd0c82277cca22b7acba 2bc7998997db03945aef006ae7ce00adca0f8076ba1040b74f02a0378e0358a6ae26d8160106e1b5 047e2fc16509b0ac794d10c300cb55e4542086d12a405ff3e46f38794f809e051ea0eb976fc91132 d0d0e3ad2e9f3fb097ff0aacc12fd1bacc91b50fcf34ae158a52ce314a58aa97c370fb0afe3ff6be ac4151640df6b7a4209ba088fbbe2bee2b822808b209a228eacbfdf517a89aee993e33e7744f77df a7db0f51ad5245f0650a99411089386cc387cb59f91a6bf2e5a01c7bcc063828470f30e3c4f00ef0 ea0900bcf70af63e2f2701ae0a79803bf71ac05fa57e009b39c02fae0070b7a203fc6d3c007e2d12 019ccb00bf4da60037a9e01824fb0d70990f7e77db9dd6ea259d2b8db4e3245a4634e1f5b2d00aad 31f86952a4cd31964de693ad2409723681f71325044d336a0c2e9f503f16b7e7378067fa01cc8460 5fb6f302b8ff0280a0b308204a1d021063390d080e2d03c26ab7400236c7910efde142ee4a011867 40f8857700460a10cf66988b715f00e2c2eb80d01a3020f6640510abd3acd2bd4d3645e6be9e7cd5 cb1902933ff572a152a7098cd64834ad10611788d472a0a49ce707b73f89d1e604fb742193720224 ea4f1a24583a0712e2b80a12aed7072451990332b31300998d9d02080e934c9ce38084ca9900ac36 20e17114759c7890679038dba10179d70009b5b62e997e87cbdfd3d369768fd8834c7bdf68d393e0 c44e959be73cd185730c3a5d2f29e8cda928e88dd39fc43e3845627424f8ba416f22f146f4bdf970 214722eb542a0352815a80f4eb61ec84b106d4b82c036ab33f036a023f03982400d5799600d51d07 9b54713180d31550d901092832dd0ee0ba2d6682915bee795accb3fb3612aa8369fd5ced260bcea4 9e4018a384da0b3c0b83024dfd5928d7a8f4a70e1d49d00faffa67e3f137aa6f2465462bdf2d9a5b 90bcdcb5485265582f0027f81f914f8314901a2079cbcd41d238ab20690e9f2029271990e4ac3e48 4ed95d61a5378528cd3c8309f834b55866fa247418b6307ab2adc0e0bac97c14eda34e01b1af7ee8 48eefd6a3b8e428f23c7f1c76a77ffa1f9fea1f58e436b2ff306e9e088411a4a9500ed6d0780b692 c126b27c06f4b68944d90fdda0d2746f3306742d27470b80660fbbdb96b69bef79f0abd4104f8272 07ae3d99ca671b4645fbaaf57ef51c7fcd14fe8be01bede61bcd3732f9468bc93d8ba1cc6a4380b1 d659908927db8079700bc0f81915308af4008cd048016607051f2cb61bc034127abea2eb0a43d127 21597a7b2b22b181a3f8f6cae2d1fdac5c181c12a9f57f5253bf260a7f4be28bc9f89b75e4be9159 2b06f991dadb29815c293f01b9b67d00b974e5027298176ce08eca20ab0715c9ae041964d7995b16 9dee4fa95becb84f743b2687787b6b06f4fea1ffd19a1f0118914a1f798e23313ca013597b23325f 79443ee33053e24f39c25fe5de28ba3774f57e2438448bb34559bd5366058ac5e0bc5c448c3828a8 851c28085716140651b01928e4de1750a0c6f17f507a33c803eaa2494fa140f63670eee5ebe8766f ba53ffde7736f1157b7eb385a1d5721e2363e04f477a6a9f1b9ddcce7bac7a0373a2f8633e4c4e52 2ace6671acdfd895dcf2ab1ba907f2c2416b96c25084aa2eda6cdbddb99339d8e5c2450337799ee5 fdedd4e0f92a02b662e3f88c6264d76c4ab4ff6375b3c4d375a7f75b68227736d0ceb2a454ea6268 79d7d3cdb2fbd6ecfa155137890e727ccf1298dcba1bb8d47ded1207169a527bb33d48ed536a9711 67897ab88858254c466855046f56af0bc56bbdcd571e95c156ac74675b485c474b8721f251ddf470 cc5b6bec915e93676eba32676d23d27797ccfd7e8a84d4afe1bc91867aaeeb332ed2ff8eeff13b4c 95f58612cedb612282cdedc7712f4c447095dd92d2b42894212d593c9f5bdadbf766e66eeb4ff6b6 8584f1836bc77a41455a1db0e921c3f85aeb0df135a90fa9d5383961963366525cb84ba1b9c8ddaf e3f9a692dfcd8b6ff73ae39b52665645b6f3e95bb908d306b1db462b2d25ebeb8f55c3a23c849060 b4e651b4d895e08d4608cf67bae4b6fee86639a932ad70adf7a1cd21e265b8c16563ba264f87c52a 652e56cb193de53e526597d951982a3b90e69be2287cfee66bb0ecf4bd5fb8d3467cfd98485d099a 209a9d18f792486ea44d5bed11cb3c36437325b8c37151c80dc7a5f62802d6166836b21b473af47e 0c9729de9f91b13f96e29a9d23a3723263077fbef75ce4dc797c5ebc4f886849ae195fd6f3d3f74e 2f4f1b905afb3babec1837ace9884de98b11693b9be138e308acbdba4aecac48180397cf5f077930 8c45926a534df67c85aaf5f89ec7f5aa89dbb3fb360ff9006ed508bec968fd081908d5c9e99b3f0b 13a939e0c64a77b01fe3da561fb1d4c11991d6f93e22cf786c386670248a6c5d2238cbdc2172e072 383dc8bd20a6efed905c7f53274b3d5fc2ab3dbe9d6af6aa68a5df15fbf5711732bacb8e34da891d c43e6bed5e36796fe3c13ca8a56dca8d165ba6762df24d8000783a827001c1502d8fcab87027ba1a 89bc9169f2235f20d45007b987cef63795fba21f5c9c859ebf2fc93dbed9d67ad5f8f8dc7d1f37d7 3f825a37cf8ec4ee631dc4e2e0b632d963ed5e5a4e7c48aa16dd62734eae696eaee5e6b88cb61af6 2e336c64a0f6aaee4adab19ec7c86bcd3b2989da865d746b9be14aad6d463510c030fe219f7eb19e 460aef605990bb3dbe81d523753234c34afd51a8a12e1aed5eb8602b6e1b9336eec4d72d6d9e145a 6c367b689aebf4b1392e16b486cda78dc6ac5ab01b1950bbd697cdf2bd9e8f375f35ef388dd74af8 82a8f2ec96ae56a95bb9224eb14ea5c1f4a3204d69a94be576a17a2b233e4a979127390be0a29591 57d98b24f348458b584612f4b7f269ea5ea21a36d74837985723b49e2e6bf5655dead4f39036a879 921b5ed66a9bb6bfa8faaab7aef27d7f5bad929058799bb85c69d0885a819c94596e67534e19b9d5 1fa55eb1172b6afc9228b2553757300f64b3306e2d66051ac58e795b959f79fbc415f233b6b708c0 3f460d1e252a1f995564368e58b6b4a9c904dc68b2beaca9586dd3eac7abbe528e57ab4401ab88c3 f29fe553692ad4ca6d466e951157ef957a79952de1777d5c64cbeaac48be9c75c1dc3d85481d849e 87bc2d535a3e83e59d9c7b1a3f7379728f663d0bce643713b6952da5dfab8cefae8d0cbf2ac633fc 3adb0c405f44627464378e9a3b5278238a11bbca5bbbbe2b90b50a177128b7d3cd7ba9974b3f4ab8 577817d9521d2992cf41a2600ad354615cdb660b74ec50cccf5adb723e831cea395739b57379e2d4 cf7afa659c2d256f8b8c6f637c30554bcbccfbdab79846417830901fc3d212df096f45a4dbd56b2f 8dc6589e5624d2a515194907b06e7e6892513543413f12ca6bde6d1f54ee8a4165b43c7f16352f7e 2b98cada2ed0a062e667cd8a93cfc45bb7dcb23b78e5f2f80cca7ada269c116437ac98c8f8169fcc f0138961de1729cf880badcc34725e332d6dfc5eba5d2627b4b2cbade95e7d744869d2c14cb11dec 99a2b05122391e5cca499aea8c9374925228fbac3c29db6ed3d197bc5f7c97c335bddabd562adfc8 4caaa96ad55fe165d4ba4145ca2bf879873fdf72ab64f19cbd973d3d5b1af0a78c6f8eb54c95664d e6edf057a691dddc19e826bdd3d25a87d388afa2b4c25b099a00562aa5ed9fb914db2242b93b69da a54e72cc2f279443985b6a76634ee4b538ba912be48d25eefd7a3ec1252e8344496a0ac4b37db589 6779827c347c958ad75bd40ccad7b801f5e12f2e0e4f47287fa183815361b5bb655e381e5e083231 48d29926dd50d2728d38a6dbfba792468b40a77b3360a734f7e9a6d83df1485a71e29d1c9f0b71ca a9d4086a8ecfd354c6d54be4aaf16c9185647592e0b6cb2df1c2dd13511ba66ef8feb144f178c50f 0562ec084603ac7388ef30ac383ca3ea3306b3f69c62bb307dae35e9f7bb50dd4174ba741af61385 e9e08ce4d6f3f43bb39b8e6e4c2b8f38e92e2d9af420965153fa43dea7865dee90a2dcb99c9ce405 8372382e3c1a2a8bedafe46ab07f9085dc259678c0189e284fd229e2551e1689dacbeae0872531c5 11b8c163ddcee684e10c724707cb368698b05d4452ddca30ce34d602ecc6b1337c791cc2652ef55e 07158c7afd46d9a12e50398c29a6683c3a643e37dec6b395d5ebc51cde032f8d976c9b26b3ac913c fbb09a641855a6168dfe9ebc71e50359b8648f896df854dd4bea5c88dd78f52062ce16e007e18ce1 48e295c28e5eb680e1ed7ed8d3d081230c51720773c898ac2a71c6693bf0b2a6439027516968d316 db50f145ae62fe6ea7c57cb10efa9be3bed74ec40ef57a117e97cb4707660a36934de48aea00ced4 1f9757ba7b4f5d695250eca45dcc87c6316af1b614727def1f127e392b12efec4d20eaebcb1e6ff1 c61153c0c5c0ba06e6a25a31eda32cd28551d2de91c8b87acec667cd731976ed7807ce57cb5368a3 af84d833e9e831de6f3c63a02a93601f2b85f902a0b1d6e6002e551400d337bf273042bf953c18ad 5aa5e3964b5ae6cee4dd652291ad26ea30d3660ecf0f099a8ef136b5acc21ab969c84aa2f2caee89 46ec26e06d64cd61bdcc8847d9424b44cce33a1c0e2234b33bc7338ddd155eca071fce4f3c18dabc 71122a69ad5c4ca0a6b518e04e03b0efa63700d686011d1d78017009001bf95a00fe12c0d2f40460 3907bafbe5bcdf649043ab0a0dc452d11caaa15f20e7990f22d368a5a0746fb97ea64ce77d4d668a ec99bc077f2ac173f323d1a827f6787b74d9614475ba45ac648e8b67766f1ece5b880c71695c8b3d 77a973ac0631d71830bb2fd02c2d11003fb5248863b9683de9783a9805c599e41ac42bf209c4ab25 3f806bb0416dd604f11cb30171d230419c1ac19d2ecc0deb1e316a561073592ad867219d7dd26782 692b0844b356d74f3a59f74ae5f9c639f1cc3b5a0294e7e1a81397a1c401ebd9a71d4ae9233e9e79 bd37d0dd73f998e0987bb0bf690a8817351d0483133780fa13c4c7120ae21c9e0e60500171f9c182 f8b1c381f8f9196c67cf5e20eed0c1a7aad206f1d328b25ac5c58403e24b036f0f3a9361cd5f0d5a 65026d17f357669ece82fe0e4fab953794b23a75ff532dcf94ce89eaddd189c665a8e0a89996d161 f528c6e7dbdc162e2ca16d6c479b0268254411c46f5ef4906cdc23cf0081ba5e00220008051101d4 3300c99fea0029a4470069ca02405a1933003dd8a4dd0b3e2d23fd008c3d409861f0bb443a1505ae 57c56dae59b4b2f9628ecbd7e80c6c2c42b99bd645174a3aa79c4f1510de25cb44f543d8c73bf5be 8aeacf78709d5bedf670c14df1b1ddc6dd02242b067badb9124016c560d7bba90b9093f1048886c4 03a85300718f05805cb156244b8747835c9f224063331b2041570e40ca03e4de1a02e48cc90039ea c1af2b5caeb11ca78795b6863723693f2b70599ac152033c65dda558325b4ff9e403516e0941af3a 44bcec9858df1b6ac834f33a463a7465b08fbdf3a91d683d6d1e2017e80090574e05687a6a01b47c f6003a6562010cf0008e6980f25839807e3700670150a12a0560bb00ddf550808a4809a0cbc304a0 c34ef05746592852d5dba05c7b561f6c64dccf17bad742a6b98ba5d289591d4fa53909a26ef5e0ac c6f99a471c0aa50b765ae816727e3675b8783c1e636f7120817687dc03b4930b76c80d823f2eef4c 80de6f1780250bcf48872e2c02183e92001b65f301cc1b015c87001b973601282ac026390f601d8d 0058b75b0558150b8dd2ba01b0ac80018cec35a2c0f5e244951ad917bbcf33ddd82d458f32752272 c187a7ce75aef24ed433d21def6e4b573425a936bc1925f498e81e025a5ceb18d05a9f406497c61a 171360ab44c052ef3c00765dbd019e881100af351980f7f765800f4007e0f5d224004900c1a4c688 04e2c6ee09f0662e19c0ad05f0e27c03703a6f031c8313007bb9dd72df1d0df2b7c3ac918ddd8542 7a30bad0a9b35e24928b898d4446785c698a3e3a66a91bccc58676ac893f8c4f62ec23caebc4f4a4 0322b91aaf4e3c80f7567780f317007027810002ed9080283905404c328d0066a10bd95d01625a94 02383a80e8315000470610b55a1710757407889c760304b5a40111af848ff914cf566590abcc4af5 3f49e63631fa90cc09492dc118b595def05d756e9f6a7944ecaf623481c6c0a7ea3b2ec70121f551 4078420224c8e7c7927b5120b268f640e29a9b83843e1502b8692071287b20b13e6120b129174062 1c748b4f0372c507892a920189cc7994f706503fdb00f94664318fd263a2e09844c3d488c85d1e7e 6f1e9dd22b8620fbfb2737fffe06443b0b7f30fa4cc3084dbe5f0dc87f08bf80ac2c8b809cdaf58f c5efa8d47903a852e11880e0002a1d0701cc5391d689c3c156ee720ec86bee0448dd8f0172bdce03 72d39ce4f8833fc8487ab5c5f426d75a6af64c16c81ad34ae389b948c68b0d0b892152e28f7a45c4 fed3781ce9d091f738f4d2fe49f88da2864335f353f3bd4c4072d0e441d285cd007a8f00ae3848ea d53c48aeb46093517d1bc0eb0c92a52d0a92e56615241930cf810d1eead0597826f5696be734a8f2 215ec24f56251b059d00959cfc51b425487d5629d4a13f9844ea73245b7e5de5ee6bbaf057c177c2 6d221dfa23359796013d146d40df7af1005c06d052ae0ee8cd6906e849ed08e8b47507343efc8815 06a9abb2c9d5eaa339d3df8b6c723db6da8452795591a587e763180e273f55f2a05851db4419187f b51dff6979bb3f09cf5f6285a360876fb4dec8525b5a3c0063e5089029c46b2013ef0c01f3b8ef22 ad5361830de600014c2de8370c498e00031df6917f3be532b3110927d45e14b91d3b0e95c267e542 ddfea35e11b188d3d765eda238e1c83e5b35a393cd87cff8effcbd91cc1a45f74681b991a1564967 40ae4d0c406ea3f020d7614c902b6b3190c32b1990752f3d90d5873cc81e80951ed7ba1baa7a9f85 8ff9e0c999c0c25069dafc241809e591573bb21d87a5fa365de24b92f0c7ea71df68bd1f591218fd 99e0305a45b7f03ef21b22a5f7b5ec81c2169f81e2ba6c8162cb8881225d66404195dba0b008ae0b 85ba698002557cfd493e0d9fe18f4ca8d1ffeeadd30af6f0b190bc91f37dceb58bab709c162d75e6 148fe5f6b96ede3a66f736eae86300b53546d63aaa379875157fdce81ddf73ba7f0ce61143b9e583 b1d47d3bd3030bebcbfd1853b7e28c540f3b777c37046f41dc797f3321f9ca8bee6cc5061eca0fdb 06bcf523a3ecd25e517ab41ad757f93452276f5a4ab02fb9dd483ebf7bbe640ceef9a366d7515d63 a48c7b5aa2255fe193f7972ce5272f09dfa5df0752c2a1fd1845107146b868e4201edac42e77d6c8 3f92113486e7f35a7efbe6b430144169706dc8ee6f940e345f6b03465c93e6da5ea52e24be9ce531 36127717b917779cf16d4c8c34d4c869fc955db01b63e0ad26914a29236b3a7f6001a8ecc771aaf7 118ae0b299e52e6795c25084cc9eaf7884bcadfb4f8593aa778d43f68eb1e9c5adf3063f3a97358b 5bb795c9ea8f55ca525fcb59da8017eee21c8ed31639ef41cf8baf5871c6d70b9de95be29653b153 32a790462727ed24349a20e7dc6aa42dccd9d0dc8cc7517470a4432b3cf9ae7dacc715a6b57e04ca b65ea3f7a6076df0b5d696e8358bba614f5b9327a4be1a937867c9d86e6fb1ccd8ecdc5b99e379f1 a14c67fed65cce2a6f653d7d8bda76da8095dd44ea28d2a48db9da581978e7316e41de884dd3b1a1 b9e851c394a794d9599909b3645fc6c0dd27c38947301b55bb1f46d952306e30c8cd70fc7771b291 6235dfe43af28c2f75f4e95b585da78d98f09c4287737c0a49183969a3183d56faf1cc183762f911 9b7c9546a41dab46864be6511fa6aeb1366bafb03ecb3ce25fd6e5ca6efa9b466ddff3e5a1d6ab06 33eeaec8c6e2dd0635cf77a46966da69678ad70e724b6522836c5b59a772d1826191d3386217850c 840ee250a01cf7b0f722d2ff8629a73763edb9c8b3b3ac7b64190f0a0e7c93ba0c96a5e2bdef09b9 67bf14cb809e7f28c57bc1851aedbe953cd1157bd564b741b4998e346ce43aede4b8d20e6ade0c66 2bfce023b2559bb7c8072e36c79596d9b0c5d3bb316b4e0b8d0cd25c3632e8f2d9c860c178c555af f8d7e0d348988cc49fc831f911cf1a69a86fe956eb36d057a70b9db2938e34a8850ee24e9b1c8661 037be92369a047cb7a1b77f4738bcdaa971679d36f4d73edf8cd946fbd1b367f831b19f0c0ebee01 4bd5f348321705b5d64a04c756f9e19dabbced92561167f758a591552a9546ee11de29ac34f2f233 82bf759f86bece0e62e4f2913ad922dd0ddd1ce7674c3375178b0d9b739b8d59f9dd6bcc2accb8c1 bc2b8bfab2515cd7f370655bdb748abb5a09ab4b1ff9022da55a255b7ae56d0ece95063dba562067 fb2cb7b3125c52d62e55ea9598625113069d225bbbad8a1434310aa65cc60ae6b1c7164ca5a804b0 382f96e9bd3256baf832d2a1bf6aa79151f95bf9b400ffc97d1a3dbc2f0ed57200f746a59182bb65 69faec97db1978545256605aea15d065097f505c91ad50bb22f9cecb8571a3aa1768787cc9cf3afc 3332c39e1c2cb764f3b95c9e123b59ef9ce3b29be9cbcd9632069d2d65f54976b3187fdc76882c9d 51213f145ee6f9246a25a4065744567f95a5c9f0515216c95ba9974bde8bda26f98ab4d37070cb96 a68922f9dcd20118b9c2b8762a16e89851c9cf5aa77a3e839cdb3957b9f57379e236ca7a467c912d a512db8c6f97a54c35d33798f755bd338d621c65207f984b07b3807e00da21ddae2f5ee976635d8d 24d5667afa51d0b0cda3e60e5a154d04e5cb421f04f1dbc32d92fef68b728aebf94c3c75ce2dbbe4 2580ee3397c7c7b1aca72d91ec86e5896c899242ed544c67aae9288c9e1117529169e4cc1a03797e 27dd2e43c37430295cd2bd7a6b4713d0414f6932f648b19d2991a2f05839696ac22c391e56b5e478 d44824c763a81e7db523df7624e84726e348822e6a879b1b653fe84533b7ec686a763360e58c6fe6 0f197e9c9198b7432b8c38cfeb4c23db7602587a69693df3d3ed220768855fc174af2a6034016432 c536653a45c5af85a4a9a0f5e4b857ea53b6de9b51b3a1120ea0a84c8a34c9e56cf024f3193f49e6 b3db56c2f3cadb8477a76e015c93d14d9c8ed41f14a36e59ad66b058a9b728dc0bf46c77c9e58fb8 9979d6372a233e89435ad2053e688921472bdbda96ee559ae16e68fcdd97529ab8d2526c43b092a6 cc39c971677fa3ec93f0a0668313a032e405215d2b4e91f9742293f02eb54a62b3e07a84ef597382 e73207a25ae61d5c140184371acb1cde681687381c7fc8388cc8af280126381b45ea7e7d39bf1215 b8f18815292971cfad9a5d27f33cb9c687ae8beace9e268e433e4589d83a39368d65922eed9694ed 6cd75426b1e649d7d8edc9e5e8784c78f6e194d8cc0c2b51ca446d43f8b7d883e00f459800482b81 8bfe298fc3d547039313b53186aef702aa9608132504f685e8c1a81a61bd591ba126d01aa13a3bb3 e78be9468b7c8be95a69bdc74bea24b8d638f6eb9ee58e1587d97b8a1139886b192535dc726272b2 cc6ea9f9f8bc2257fdc18c2cc45af3c47d949b274a0abe208249314754f7a91dbe8f9307bc712eaa 383ced5a985cd9deb00eee020cbda608b4dfec6611fd7c682049e631822f712cb4f3c0d9e950836e ccdd870aab1a197b149c5a2ce8bef318a778c7aed83e55829a2dd3d51a221045fd7288e56efd989f d975bbd748824e196b4a4da6dfdc81cadeb342e2313fae8957919912b5e97b82ef352df440e1cd3c 3fc6646e33c3d017bf46d59d22a0fd8127a3090ad19121c75ce2934beb055f16120ee720370dad39 a60a15cbd3416c7bba6c6215b67c023b4bb883fa344d069d605f0310519d0308786ad441c3536746 66d395666f4e14ce83239c7de8379f398839971ecc9766244153b9c6e49078bc623cf1ba6dd7f881 67e678dcf5a7186689137480b1e3e0dc410fe313ff39862f427e032ffa3d11ce6579155a1fb45055 878a63e81ef34b55385651671478cfdd22a87b640740d27a092039a104203f00746c2601c4831680 669b3580e645b3addcbaa57a016ba4cb5db022f317730b67de49c34f2b30e9a64646dba05cc60945 48b2c8d5f6c43b7bde125071bac68e576886e179778692e9e50449e54a2378a15c59c8cbdc275071 0d5631bf450bb18a9d97c09b1f1ba041f02e8074fb05a05b8900704cc903982c760338ad004cd522 7510a6ee4f0027b93480917c1740be2700e8b9755b7ab152aa951755a634680ec99c474fe10cd4dd 3e68ade45f9276ab6a901ebb3f26f874614fd48dd3164766f51586b3fe0c1d58e60c195d6613d86d 13e1f39ed02663b131be298f80b8b5822abdf50d8009740fe04c4f077055bb02b8460000f7672480 17502980511fc0629c0b803300bccf069faeac0c80d74b16c0e3dc01c0edc7a369f14ca95a2723e3 5871746c9259bf5f8718449b3e52e65c732857c9186409e68e8438c0445c6a1db698d26bad51ad09 2d103318a4c519af34823c071ec678dd1981c6961b01786a2d00cc5342000d19c08a6802d881bca8 68cf1efc87325dac047064419c29f301381688137d08c4135401c463e618c0de5401f0bd021a0bc0 542a084d650a8c964c64de460e8adce5293aab5da87c9532be4ae5e1f706f1b75bac97a9af510a7e 2fe2b3c56c02dd696a181388fb1034e36ac0f2e14c413c45ad41bc383e80784bd340bc0d39203eae fb20be51bec8d2d31a881fe3e300e63b1057a86013e56309a4b8d02b83f8829c077033409c3d62f5 6281ac96b460ca9dcf53f144064e9150e42e4f66461b872c2d9f7a02a4874702ce250f389a3a0928 bbaf6c107ae22fe1bcc94e63421f1d8166530f0ad302d36837ebfc0ac48d310f9098ab00844c5901 746f01480020792438a5e6bb5980346e2d80346bd3009c43709a65af0029c178004a0d20e9d11a20 78d60e26fe18551531b7521c37752692f6c353a7ca78b1d404cb3da8556a73493c87c020f6d844c1 65322d61047fdc2193427d0b1772ca32f6a2d939681ae8242046cf02628d25082eff5b80f4ed3d40 76a913402e6b3700c307c89b8c073088d66147f14b09a044a11b80bc046822730cc0bc0314ea5100 b9234d805846f0878e5c7084ca2a1b65f244c6fdccbe7620687d69c692f33a7227efcaec92109ab0 49ec3bb286a99ba48c58d75d68b582576f721b7b6d943568d57a2137760590b9c80144358342bea0 3d40cb7515a093ad0550f97103e8a30505b0250278643ec231507fcf02f4496c02e035805e52cf00 ec3440b55114d688eeb32240d7b00fd0d1b358621db69ce31a9d34d3698d3e2473ea06210f92e397 6ea256402dbcb35869e8f09954e075b72bc6de142e7c12735c01a01824fea1487faabe91a44a1926 c09a5870595a357d805d2f18c06369fac3858cddf436c03c6a12c06607b03b790698c5c70076aee4 00767cb0009b7112c0fa4300b07ab55c708c52391b4b273f2de6a15e1e8e6c561eee7f48e6cd07ed 60242498b097343ed5f23f7193070ac0e09df687181d0b7678cd5efe507d570f80cf2e31803b0502 10a89c03441aad07d08bd6f422b060c08dbf493980ad0b08c0a000bf1a25801f3a13806fa813c0c7 7718e01dad967bf4408939d663f4a764ce999f7a79ecbaf1b081165c9397b5a60901c6387d12a38e 519245a847ff598cfed0a1c3f48e6f84df4d3f98b499721224a8e0dc9360951e4834de5390687677 2051300c9028969f20913a91208136eb2081a12b40bcb448ee26b471f0ebfb7a2b5b6f58a5af2ef3 149344b00fc9bc97e47d6466c137082849ebb3685fc5e890d3079d48f0fd9a38f1613c76a848870e 85cc4fcdb71d9a7c83930ca914594021972d209f692500e10ac80b1607e46e9e05e492e801929578 4036bb2e20533809c860e6904126fb5294ca1205ae53c5978747692ce17d4f471cbfa0586b75fd2c 5ac48dc6627fef3dfe9a7c1cf955bf71fa7e9559138fd0dabb3a80a446d8d1ea71e2f80d92ec9502 c966bb1a4050ee646a71044934fb0494734e03ea34ef330ae754d3da62584c2ef7db740296cc043a f129148248f4f5d990a16effadd6fb29a97e90891cc791f0fc55f38d14d62f26df3fc9ac91a459a8 aa803e203ea08d250168092a027a3ce8039abe6d41ea353c83941b943998b544bb49adaae36801d0 28f284ac6d77396cd4375390d891d1cfca45427954af501bff93e7f86bc444243c472ed5c8dffb97 4087afabc745726f645e0d1f5dfaf0d24a3e04328d4b016492c1899579ec578051321a607ae21330 890c0dd286d40269b3be4e0fb7a52eb5ad356bb8965f176101b0f407bb2f26f2302ee4c3761c71fb bab25d649e8dccc65fe5de28cee1abd6fbd7b5da3edcb491cc1b796823fbece119da12409eb43990 0be6bd20c71d7c90ab3049907d1f1a20ab671720dbe48ea91c9b1f44b9db518e081830d5cc674247 c4eeabe7382216718ae8fc519848eefeaa39ff5d92c3f491fca41565f57e91790b6969008ac9fa02 946285132891c1d7b3e84dd2209831b641b13c5e83c2c53d81c2b2183e24fb7f827fa0cab3c9d8fd 346dc3f3eb6a8b8ceaca05c387660297369746a27abcafc9fb053a27170047e91499aca4b5666991 698feb7a0e6cbb50fe81ae8ac505c34fcba9da41ad68acfaaeb5d74eae01646fd8e4ec5728a8b473 38e177ec2c1d9c4d1bb9dea06d74c421b8b3de688bcc92935c7add9edad53d3f6707ca6589ae4c62 b5371f8d4dd9076bcec3108bb7673974c7eecad53daa371687bdd7d5e54a7c0a1f3d7a555467153e bc61ac51f393aaaba2f5369bc6357bae1490a1edb513f26536a5fd2b25e4d33755abf7efcd5b477c bc61f6f62cf214f821f8ff85fbff85fb7f5eb8e0324ba1b192f12e40abc9790087770590a437b3d0 c1b61dc3945a8e2160186b87e55b915c5f3b51376aeba7d2f688a2478b5a2dad15e86906794252b6 b1bb5c7342f3887d1432abf5d8d274540ccd9765339d385771f7050505b532f57d59ec345e60ba6e 15a4a6d65e7433cf8e9d40933dd2f4eafddef4341bc8594e0eeb7a1b95f92a3e5ed753a5891b8f0d 6729c5d905c595ec859a5cc22bd8e966d7b565211c816eb64562c3dd9ebece33a2f112c6ad5d6aa7 63934658e9f9a139668ed28e897bf2e37a2394dc462d07e5de8c4e566c20ea845c768c4e2f19370f 24c8592fcbeed9c5d9817396b9b971b11fed7758f5d0b47ceb37f0a6778c3f168f98aa2b7e85e5ef dde4deedfd1ff06c9f826165307e093be49f7f7e29741e2a60db0e3cd787ebf8795cd510e3e47b61 3f25f036bd2b13a23b19268414151ae4c9bbf33693d9a50d52d3e2214d1baf79f33b0afed76a33f5 c6856d34845565de7c9492c776f60dbcce74ef105db32d8577dafa04be180ddab789f44305ff53b5 6704140c8f8d4b4624267267dfc92cd761bfd6a4d7b6f03c166b8970b6a3b82cdd3ed12968a5b1ce e5a4ab4bd937e3c51565d55ebdfad927464b2767d6d4b0873f5c2b0b9337c273ab5ee7789e7f2d74 5048220e787f467d5433ff9a36a0abf5608392f6c33b06f1347f1591a4d90b4beb5dc38e0cb0aeee 257044ed6609c87b35fe2832b121b99a78a40a65c6f92c736a9d0cfb75353db09e43469999bb2c9c dbd9b9ea638184652e05270831bcd95a1ea90fb3daeb9513f57dc71cb60ae6c4eea6845e6980ae87 ea689b92f2537b769597e8de4bae773837dbe6264537e89387b4d8bb3f8787388dcb4177c41e5181 9425f4acabf6d19c69546db10fbb63383834d0f7037c5429b6c814836e6976ce3e68cdecb57f1382 be39d12e19ece9ba74610282a201e2aab797d95b3ff7ae791833ea87fd747e3f6487c2a38e5ed5e0 54d176fcad6bfa5f0b193e67fc37b5fcd5858c4c0aff59cb5f5dc8b0a7fd4d2d7f7521c33cff2fb5 bc2a05e571a1d7f8c8c922c5b37df57b7d7bd5abe176a1ba1f9e1f355ab1041edccdc6bde91b68f6 fcd6719f853596c8a01ab942899309dfa9d368a2d3a714a666d5692eb8148410ece6eb8b9f867fe4 19ece6a7a97e07fcc0d1fc4c49c3ebe7cf52fd81a3f97dadff6bbbc07fe719ece637b7fe0f1ccd4f 97345c28f9f7b6fe1f47f39b5bff977581efe019ece637b7fef71dcdaf2969d8057e6feb473c43ab d2ef6dfd5fd105beb7a4c16e7e73ebffcfa3f9855fa8b00bfcded68f78860f00ffded6ffc92ef043 258d469dbfb5f5ffdbd1fcead3e9af1d40fd23cf7000f57b5bffdf77811f2f69b09bdfdcfaff7034 57a569fa5fa85af68aaf76edd5ac1950edc9c3f336411cad5ac5b9993057f675e5767c6b1adf8034 52279093997fe2a7916c91a7547a9f54a783495a59b4cb6117887efe42b8f6e25250d20339b2af4a ca0cd8e53b1f63e8327a7e6404f65c36c0d1026ff566b45fd85defadb72f8dade6632773183ebb73 b750d5c656b83add4d48e5e2d592c775ef91fa0e0876f3bd9b461051759c1634b4af3663d88559b5 737e1059f4bc052bd67a0dafb2b91785ab810aafbb4e14a74f8d029138741a535758b5cb5cd04a97 09aab850013bdefc07263fae47e2d740987afbe5c5153f8b9f5457335c0f0a99699fb96b123d97f5 096bed92ba6c361bf3abaed62e9ea6f95dff64eea1f72965a9409dbe8631c5cd15a0a3977a40b24f ec20e91d1f201184eb957d79f1d310f0e4ef8ed37cb341074de94121a9f6b9bcc7c2bc4bebb5ea0c acd894930d59ae5df4deed70d558bef238d1b0e6abb3f4eca5b883d2ebe8b59eaf6391575f726535 7c4b10938a7d2f8447f39d9bbada767377e6499f0dfa26a40685443bd68b7da1563d9eee9a076622 199d4ef6a213ebf5f56401dc536d59bc2beeae118e6c8ede06dc8f455b7fc8157d7897eac7f4e3d0 dabd9ebf1082dd443f5db6c2de02aadee0fcc07d3560796d59b5fb316ec5cee9be195fd624037dc5 6d8df55b9713dd70af6a263dbe2a7988b81e8bbeeaca956bff22d51f69f7d0badcdc7d7078b7af10 ece69b77fe25b814debb399987dd3f73ae7b0a28ca2d2b261f50f330c5c2a7718c8e851df47eed6c 9dac6aca566796e428f97ac63e9652d659ae12434b6ac493d6a18dddac7d0fda5aa2569a5e7e08c2 ec84ffbdd5c5741b57fbba557ae7b2689e0296c111b4d26bdc382e8cbe7ea21f92a61fb6e689d6de a6724db3e67163c2a65cadce75a9514887f7db0fedf4e5b4efe5b627511b8ff49dd9cf19bf1ac231 f4582cb976019fb58382ced480e5bc6f74eb75c2c0bcdd404ff40ffb93a554343563f1ea91835045 aef28222c1857450faf445daf7063b5964db2d79670acc51b00f90fa0d84d1267ff3f60f8133bbc3 f69913db4df3d0696bc671d719ea27b398d4134e6b703aa72aa1d0a5ce595b3ade77f9835cb5859d 040fb3bbbd8a19bca843235ea44a457e47d7104198addc1def3eccc38f4318e3fc5f37b057b4ac5b 3b98aa9af157d5d24f5e61aa194f247d3aafa1817ac940bc5228b3822c8c9cad04ebb5cdbe8f192b 71088d56bbf133b5149ccc3b5c7a4bc8acec35bf3c8b9bed9dd96c7f3558af44337c5e3228285c33 7092b8e8e4381b3e49f44ea9d9f7b2abac6169230bc67d7190f5f2744fbc0ea39db5a98c04474046 42e6ea4ef855521c6fefd3e5745b72d939f764db8bbf833044e79f3efb0e30e37c5c36ba35aba90f 4cc2d7526a4250f329083d6eefed8afc5ae46752bc17ad4eb04f948dfe6eb2297685f9ecdde5572b adb7bd9f97bd6d9961079cc0e6fb5c8dca0f37fb0935fa9710def3f8a7cff4d33e69ea83b5dcd5c6 c80b5697abc7ecf8acbceef21e7633075ce1b2e268722bedd266abcadfaa7e63fb486eeadc0b2fd5 38412b35b9184535377b196d87bb89a3f7f65a56efbd5f0d9a19afdd34b3a8872ecf5326e3bc95d2 f1d296f7ec4d3d24eaadfb6ede2cf9db6729fde01a85f57bd37a97e075d702e84a432d7c35105472 4566f7c9a56170f47234e232cb1432cffe2384593dff75837f8447e1206a378944aa5782368c2b51 866ad7dea4e8bb2c898e1d7b76ddd9ee50389e6fef4428a69c0bdee96471cf816ebe12b9b3592b42 1763dfd55ca3e18a77036e2c9fba74e760bddd118813a908ef1f87b0687ff3f60f51fd0e9ee18ce0 67a97e07cf504ffb59aadfc1f31f8af64354bf8367381cfcbdadff0f47f3ab5b3fe2197681dfdbfa 11cfb00bfcded6ff575de05f9634bcacfdded6ffcfa3f92dad1ff1fc57df9b1f2f69b09bdfdcfa3f da057ea6a46117f8bdadff97a3f97dadffef4f9d3f5ed25052fdbdadff0347f367aaa58b79253a78 fd0bd58963f34ff15b9e5b746a98af6cc53663713f8c3f37f653fb66c0227fd7656af6d4dbdc04d6 d1b440aa666ffefe3510de93fea09a236baef63c19ae8efa01e974ea11508583d6e7ef414957b272 f6f0b47a2ee65ea65541b7b6b9cb76afc601416e469cb11fba2c4a2fbd430990a69627d8899512b8 62ef6baf08423dedcb8b7f09f7c788d76e2d0faa063c8ffa27cf3213f0cc11938867a8a77d50650a 9ae5932fcb7ca347d78cddba77a385a47cfdc83c81de112d58532d01d588d79c52e9d9113d2eb771 ff7b215439feeb067fa5aa85542b21d57b40356c7de5199474afab67afdb312cbf9170cc77e9e219 87dbf46920a936a477ab54a8406927f68a69094ea64ec393905532d7352a9716a7c7af8180e7261c 727c506585edc96555bbec6a2fdc73c9db73fc495577b4b3b79d5b16dfc8baa6587af946ebc6c774 253546350dd0098d2cc0a9d3a86365224d56e6b64544aa7adbfb570815a8bfbef34370df1697a71b 7278073cd583e6b28e5771758cf45c0a02014f0716239ee1187a339d9f2d7ed9f4cc46197b1bedac 8d685ab5849d4cea4e9c5265995219b2993cded4584a7a752bd8612f1d9e3f04a1caf14f9f7d4b95 9a9b65973d41b73fa8829d7d054ffdbc39ab178b7757be0997192892cf4f66e31e57edf40a519941 078bf434e485cadbfd1695ea9541e2101f1fdfbf06bef20caf9e547fad86544b9f5483effe852ed1 7bfb9aa5cc3317bf5eadaabb7f870afa2b927b55bbc301c5ad976347aff802b29fd9016977ae8043 2b98faecbb2533f60d447ada7fbefd1d70af2063f5aab4dcca274fc12d0764fd6bc87314f0a444fb dacf043c4774d8d30cb99c7e7cd550157751f58fdee8f990fd9e7097c478d63bb476176fdf958c97 a8a15efcc721d2d3fef3ed4faaa3772da4aa5cac9a5288a85eccdb7378a1db744055ad1ad1fd13bd 1f3bdd4e63f17d0b85c9dbd193802bfbfcde95c442d63e4895d8252a9ab775c48133b8eeccecd4ff 35e0bd6c56bdf6526ef56265a7a1987299c4f87cc0d7bb5e2c080454c7b4707e64e9931987fb6ea8 a13aaa032b1fc2a4ecdbb22589c3827190a63163af0c0ebaa8b507a13a5739ef5246c5fd3b08732d fee9b37f046f37ab1eafbda25f0ba9ca974942ca0764cdcb656cdc074ec67eaeadda593d7e08a8b4 e89b91a3ebd2308ea5d15d9744bda91ea4635cd92bbba3226a9bee3114fc945dea869c04e6e1e9ff 1242a1ebafef78f56a5ebee2be5d0d088e64c759cf328e33e2ed0bbdb67ae72dd84f0d6c123b6849 a4ab7f0893a5c35d964175221da4477026ea2d76a2a879d3fdce7418714767f143a4a725cd03bf9c 2af2af010f2268c9657b52e5421feaa2336f8dc35c0b675e5c5af6959fd4cdf8e535d486c648fc10 26b93ab59701cfed0eed07c6ef7b972327b26677b31b3ba98de0106f4ec80c6d8e5f1ec5edf68e6f 76ff086182ca7fdde01bb8b5f6e8c1a53262c599a752bc7db5ab94bde2db9a79e86c33da28cb84c2 24221cefaf062f035b581f3a492c94edf6c469371729b33bdfd12a3e13e6843fe7af4d65c6e78f9b f9f65e992db725b1b7fa19088fe6cb8bab92bfee2e637d53b1af0ab2393f321461bdd8a7a28d5432 a32e1871a014b8e55aae8d9cc5a1334c4ff609ec34de59527d283802c40a59e08df855411cf2f987 38d9720c3be19ecb46f8202327e41b8b5f032e9b1eae9d79bc573e3f707369d5ee02ae0fa4729807 a32e0beff4715bcef4e49af19c1c8e447ff4214c5a5eabf7214c5eaf768f2f24f9de969b8efbdb32 deee73025b6223759203323dfeef101ecdffde2a02c789bdc67601a78b568c2bcd0c3c5748aaae11 978fbceb25e406c6560e47856bec13f2abbd3be7a66d61ae424d7ea56be163325bee386e6ecb5db6 c50962bec5d51a997628507636cd0edadbc0cf67ffa721d43ac39fe7328154ac9aa4668ce38ee24e b37e2c7d7c766f82bc6fbcde07c2cba03bfb1147786f53c4b67ec625b9777c99e262ef6a7a7360e9 cc266ec1b9f5b17dcfaf3b4fa7b8c604abb43a65d5caaa7f93439523faf9d3607431b36e1ce75c4e 4b29ea4e299d0d4ade2fcf934362500c9ffedc2daa8ac99717aa15cc8d4ee6bae732e715bb71dda5 e928f7251d9bf90b7bcdbe16b3e2105a3076139ebbb326325f12652c800ef91d10ece63bb6d28c3b 32d6c9d6bca4dc9bc24ede8bf3f7217122d3bb8522b5b6ef2935d974d5f272957cb7568b056a6ee6 abdb72377b206d6956aee6434975fabcd2da541812c6b41687ad2938c2ce645f7eba937d05f57e0d 04bb79d27d74e967176ec2dbbdce5b0fd2eab427560ec1a9362fe65c12b92e9d7333bb74d29dc7c6 9e2f0efcf9bae9eececb67ee60dd812f5b9ba1ad5ac587a29b7e97b7cced9bbf981576e3196f587c eb2d120e47365a17d95f7f06be83677821f859aadfc1e47f1ecdaf296928a9fe2cd5efe019ae92fb 7b5bfffb8af66b4a1a7681dfdbfafff3687ee1172ad4d37e6feb473cc3d5a57f6febff6417f8a192 8662caef6dfdff7634bffa741a7681dfdbfa11cfb00bfcead68f1bf7bb8629f3cb57088bf6d777be 039ef49c583e6e398df0ea27e3b3a4f5f73ae0d908e6a864eab976ceabe6c6491f135b7b7eb98563 81f3d55b1fcef967e568713148359f495337f9ad7236dedacc31decb80a548f16ffdf0ea43a793c2 79aab12f383f04e1a8f3afef7c52addb8980ea99bf496c2ee535f0ddf126c1c11cd5b09d80aa3ed9 3ae9476167674fb7fd79e5ed64ebfe6ca9563986eba690742d1354a48b218acb9bd1c86f4211d280 6421aee12deba5a60a898b32e51ee79f810f9e6b48213c6861043c9795d44717984a01d54d2ea06a 0554af2bde610afdbd9dc331f9bc260df58b2c1db03cbb8eb1af68b75091f675c99ac7f476679f50 cdeae4a930939b73cc0dd6e637104a437ff3f63fc29316e8d51f54cf1f543f795262e1329ac96bc7 26e49dc30ce7073b57c92ae7227909f534abfc5c58e62e51bf187b1f0a58be355fefc0b3b78e4ee6 e849f39298322ba41fb237d8da72516ae93f0ea1a9efcb8b802a15549534131f54db7926f967aabc 1a50cde8a2ed3657929ddb76b47391c52cab523a5dcc9851bf194d9ff4f523fe8ce91825235a3fbd 22d5b1720a1ff339e6d3b7bbc4978aa6547148ed67e08367c194889b5437fee0e9aee54852b56641 ebfbd6cab1abcede7657fbe3d99bf675cb67d38ef96edffe907b912aa463da336079d7c99381c999 e375d88d4b4f727b3b8842ca3c3408fff477108a90fff4d937f0a4d5ccea0faa6721a21a74819114 f2cc5f4cc40978f6aea1866aa967efb8322d5f2cbba6d88945eeee764c82f4aeddc3b4c19ea24e86 7c65be28bd71098c47b7bd74f58d7d7b686bff12c2097bf8f33fa8368aa980eae0706bdd37858b99 baae3eeef35cc1533b876aa9e5bf279e29ca3960a03113d1584063a754674fa84cae431d6f0e9692 7638831ce0e7201c43efd1956f896a5cd17e063e797a5ae2d6260de186c229ea2bcf504f338bf7d5 25f5844527d3a7f4f3bdf9bc58022e3c0ce985830f6b6f4a96219559744299f711972bcd2922c5de 757cdf5147bed82f786791384ada3f422842fed70d2278d24e7e757f6c4422a07afe83aa187ca110 8ecd053c9df985a6899d931132014f237333f78db9af13ad857f1a37dc50ee56994b2fb2a03ee5ca beff90a04ae2b93f7a3e249ee68bd7ce88bf9ddd70aa993f03d134ea0b55c43cf21f54db8dee21a4 9a7749135e5ce8362538996755b35e087133d053ec76b208f28b860adfe42a98b812348d5ff60a79 70458ddede45b2afc476491b09871cc299d1ce3f031f3cb76d3ee0e99d7757c522c980676b7f43e4 71c0d38bc2a72ef486e642bbfcd18c5343471b225d5b7528f35343e5679c2941674cff229f9a3bb3 5f77047b54bf0b5385f10406f79cff0e9108f9df36f01d815e4654afcae6f44915a5eae25599b433 912c6d5fedfbcc3c5426a29e489c8cd0dc6928cb575b97797baf4a8d61219c491f50eca2889a343a 7efa25576f9577b79ec92f7bc53b9fd3a1ebcf803fef13a16c77afd01be2da6b07547be3377e55ee c59dab5d68d2715a6acbda0dfb03bdff34849365d755e5ca2d8ff273e41ca486dedc1fd0ce431475 68badb99cfdc4eb0affea75932cf48f276b3348d6da9f1be44b25df0f35f829fcdc273eff55c1257 823e0857a2e463d71e97122ee39b05cee57d92d14fd23a4c18389d8b454ecde6b5fdb14ca282d424 fadb03ca053d5ccf4d57bbb143ae845930c8e2af4965c3e7a73cb7dd6833817b52b303c75b1be53b 20d4d3fee9b3c70d3c67614402ee6ace7aebb2aa89b83a066dedc28c35f593d9a24ed3a6df55b3a2 bd3972a7d246daf3abe55ee51e3371989b4e77139a990873c20b432642d3e424944f675b0e1f870e c7de8aabca558e039d2aff33f028acac8907d55822f27208d3cdc5caf23167de5af68ce3893b9e66 313ca9ac1bbdee718bf596d2dedf4ff7ea233e12878bed7097a699819025dc3ebf6a4a83edfdb864 b765bcc77242233fe46a707ebcd977e8d9a689d18b0842d9eecb8b1f827bf9c10d6fc83a8f5dac44 3d381f19d3977998ec735a4acdaa8a674394fc2e576bd2a1e584895dfb5321d917938b6d57b8a064 8bbf01b3c9179a626bcb6dd916f77c574375b2d4e1c09de86cf6dc1761f205b3df0b9108f99f6f7b b177b673c56d1809be3cf8e45c16ed9366b60b45e51e7f1d8f80f53009cde519719cc4523b66b060 f8db93cc6e1fba96e35ec7719eabbdab052eb6cc1437873c5d8e8a66a1954d7c02d7d6f2d3a9ae3b 82d3586335abf53370ed4ad3fc652cca90bdea2aa1a4aa0f245855f3c35be1587d3ff607f532bdee 2e047fe7b93a77e3f6c0b86da4e3e0b141dcdc7bdd83e1d84a2b39f06ae0e8e88a9cabf8d24c08c4 72745891cb546b9e5c9cfd616a31e5fbe9af10e692fff59def00b391c83fedfc8b7a84aedebaea5e 55fd58c38f9943ff5219ec2eb6bde12bd3cc816ba1b5c3ba7f9c86f76f9696e9494bfaa99d16f3c4 d2985f5b6d6bbef41bce3c1f0cbf66f74cfe36dbe894372b0d297ffa8c13ef00d2d00f41a475fee5 1d5df1816fc6f9b97b9a55e7eda330e54e87c425f6dc2dec4d92af9cd1d24629355b2bb379192c5c 2a359cafe7ca74b66db18be98baf6ea6b52bcd4f6343623739c4e1c3a4c93dc3c1ed245e7eaa63f9 72d5c69d816306703fff0c6843e6f952978360a6032e87dcde10066160f82ea790c2f63d591a9bae 7ff556a9632536f7c60a3eabdc7af414e23385491b64ea63a54975c6f8111d8c06affb68442e9c49 00cfc5d024afabe1480a26d8a9b623b0e7a7befb06c2852bffe6ed7f84672a7f293fe99a6bfbce7b d17cdc56c98b9f03dbf6e35638d88f02b364aef27ad5bc50f7dbc4b1cac7b943d707a19e66db5271 6d33ca7b7376332e7f5eec35f19c4b4af239fb9054eb369574ebd6356deb5679df8d57ba7dd5e329 d9fc2108bf375f5efc4baadfc1339c14fe2cd5efe0199a937e96ea77f0fc4bd1fe25d5efe0197681 dfdbfa7f399adfd7fa11cfa80bfcd6d68f7846feb4dfdafa3fd0057ebaa4a19ef67b5bff8fa3f95f 542b7ce63fa97617f6ac4d2e43aa5cc853f81b9ec6579ea12e1052d5649b33fe253c538d47e583e7 5c9b361feb66d27ddcf475fbb18eef9cfb03df840f5f5c3b6bb9774916b09933299f02aabbf5ca9e 4995a0e155687b5e12eeee9c5b9992e5c525c55a4f45c35a7735c72ab89a676e29e5a9c75bba73ea eb65fd1fe1ffd2f6defdea22d9def66b31270473ce62400c2019c4004acea5efff01fb37d3a7e774 cfb37be63eff7c91727fb617abaaa0d6aa55453254ffb77f10a37ab149d791159d9cd89014d67243 cfbda061d710ec80f9885db7b87b6c6c18ab9fac3d139e4d4b1068b329cf59c32d3404a3538e92b1 80ee63da5defb19caa4525dad04247f63476ab7d9ef34cca5336a999fe8019eff11f0a40b6efe1d7 932632b1a3421ee661afdbf2424a1262d4f0e6060347eeb9c54bb0b3e1e7f26cedc30a6d5a6f8333 5b194230ce85de450f2a999bde8755458bd8bba1b11fd17d7d3a22784d96e7b4a21cf7f603d6df89 d16ef1c8e1f61fca17f517a748cd833084ddb037a3d020bcdd3cff2dca7db7e817be9e34923ad1d6 016e31a63d4c0b86976125a35b985c75a6525234009b2f6dd4e0cc97382583d744e7332abab3c063 87d5ad9b2d508f2bb5685ffebd2441c8bffa2e460d075fd4d09b1ee641948b4ddac38455101aafc0 17c8fbc02d411dc2469a126b1db0356f12c3a2647893d7550f0be85d1f549097c6c3fe778f2ed1e1 dc5726c0df4f797f2d3fb4f232b837068671a5e8e15d668f1ff13f14809cd3b1498592f37d7a6ea9 45c0de2a6e10c1d42a600f17df9f6cb5915b6a774e36b2d279ebf0644493600757839ad8773dc485 5f59c8a9f0e3beb2241d2489c71945a5d3b9fb3157f1af7efba0c9ec237fbd08d2f7950edfe3df97 df517f711a2d3f88bac23a60d957e8673a894927fda38d1cad644b40eb98ba4a2611627783128b4f 9dc5154313c61be735bb679204e4675a7d2c99f2033f0ad95bfbfd7265aed67e5ec49473952e21c9 ff40126fed4f8aff072a55ddcd7f4365436a1d0c879720468d1b6a69d2dcdb0867b1d631beb79a64 97570dba34d6f521edd95aa672085eb9a89f7e16493789432b4ffb5cbbb9dd615a0619cabd88ebfc 5392bbe245ca1f70fe3f148048b9985341dc90ea108b6f30e500fb01971336fea72404c9fc49d72d 6d3a7b1bcf8318759dbd9be4597f19fd0666e9a36a2b78c96b33fd5bac37c9eafd2dcc3b28e5d317 a930b1a505a629e29d2565b1a4f6b9af2441c87f9efc54fe804a2d8321db0a82e18ddaf8c2e71626 91fe8e8bbe5731673fcb5847be90381ee6395dd08dfeede9e9a92d917aaee6644ac1b37af69b2d7b 0ddd4b494e6f335969396e3be22a549f82fa59c9c276dae47f2a89e3f1c712803c2ba3df50bbc16e e17f86708c6a085b5f2c2981370fc8a65b5246988d2f529465f5fb57f37c9a9afab8a107cf47cb08 157ceb87df24d46bb492a224769acc4649b7002a886bafe70bcf9ba609557175e3f56c4df80fe517 a7dd886fa25962e97fd056f290f63f076aeb4f9c8b97c426eb8e2a7456362e9749ab55688afa7b5e 78bd0aefa2a5e84dc87a340747e7d6cf3f2d7912568cdf524fa1d32e1076d43ce28da169f2fbedf6 c1d7f4a6f4bb2441c83f96fc7b89518bc32f6a105e37f3d89070e07f5801f3332265bb77665575aa ed42ec1f23016e0c5c977e2df55612e852b1de4e7b9cb2aa7ea3b5d45316a5b122dd89cb4384ac9e 22202fc4e08fc83ce4ec4f6473441657b9d6a026ff2d49025dff3c01880bc7567df7bc20d463ab4e 1c38f4c521b1f56492d0dd72bd93b76b97a0976c8eb178ddc80aa1622f4a7e58a27fbf0544e7764d adcef2a530b045718b4f44a156c95cf866ce49ae86eb94cc17eb3f119fa5aeb4c652f5d1fd3f1450 4bd547a05e9cc47726e6b08c0d9970a26cb2b2d82b54d187ad4f32c0089bfbf2eb26bc26eaae851e 95fa0a91eeddfa4abc8ef30ffeb208738cf86c13b4803f209a6fce5486eba0a2c0d2222d3320ab6a 0cb7d74c86b557cabf481284fc93e23f13502bb74691a5b4bd60b83ca09ebcad033fe3ecd6ceb358 e2cd33777c6ab9b79153777c6ff2201a1a7e3f0732771dcff524e1f2723db708a96cdf4ec2be801c f8a6a425299d1cc1d21ffcccf0d905cba4065391968af28b9e4e8eff812481aee4086acdee283a09 3ddfffd463d445a712ba77bd31b12c4ac4b4cbde1555adb6c93f88d01fddbbfa16bf0a99ece97275 c7b854a9bf76c27ed1def2a751b4e53a21b96599218e337ca7b367524cedf8fd99778da2b3c254a4 ae134efd0f05d446dd61e84125d79f08e8d22b648b4192ce5fd3c781958ca19f10bebe3c9c4ba370 8b4879701588ce56ce1dcf9854d97b2b612f4f967c6be42fb86e978bed77d82e199eeca04c2aa8ae 69695fded259bbb0a796ed024115fc0ef76792bc80efafbefb5d408d1b0fc2ae9c713db93d47dd15 1eba667784b84f35b3ee29cd4aeb7a038d4fee3a5d72dd0b6af2dd5fabce7d74c8b71ef9241392eb 9ee5313b38ac9285e79dc96f0bcf67fbd4ecb755e7cbb58552c54a809dd73b77ff1f4af2f464061b 2460e71fd7bde36d34eee773e9856e469a725cc3c37b9f7dcb57a9e3a72e65ad56148eb7748ea317 8d2ccb95cd2233615725fae274203a5f2dc314ba7011aa08f4fa59e1f5d6b9e2312df245f1c94c21 b9439801099bd2f43f141b5bcaa13f59c996a35a8db5761994978a790cac3b930b87d759c748c669 d2ab3d7cf11eb635595e5eebf415bbfc23b953762c52037797c44f7248982f3c248ef315209af975 eae470e3f4891c8d73a7b63b281efdf30cfa4b897fe6dfff81f69e2d9f2e1a369fe67958eda8bbe8 ba8929aff6753665db12ec67963ce5924736430434a57869ea0c3bc333612f1cfae4b568eed4ddad 92ab3932f7fee5381835af07e056ef07fe5c560f6338fb3aa4ae40df4b68cade4fdfc0fb0fe5352f 1595ef14de8491d49da1e4ef018cedaeb94eeb211ade2de238385b64f2f400a6204969910db53738 d1356772e45aeaeaf09993d861729ce2fbcbb379dccf675512bf31e08ca37597c68bb6cbe1452510 779bcc33713c7615d952fe437996a6b9a78ae0e2e2c6d7d3402ee29b9ee0f44a5bf6b39f9ee97bef 299ff15c2b890b1024a47831600f1c265121b79f7f0208bf4fdd065ebaea9dddb6f818ec20e531c1 70489a638826a15b139736db634dda6d1bd6e3b471d60ffadf4bf27eccbffa4ec16bc6f39a4b9597 12528a9e3c5dcabfd989752ed2252ed720cd2531896d98d91cc42e47ecf3e288dfa9cbf22d662aeb 18f202cef688b8c1c6395a9fef6b389b7676d376bde2da3f7b9535ddf5e0752f0c1bab884b7757dc a838fa0f055481560470b83f7d3de9cc280b6ae4701f0b1b82da349a835aae748b8e682de5478f3d ecca68d8734acc616169503c92ded1a5830547d1c93456e6d9349a0e6bee9fa660eecfcec5dc639f 8759cf675e662d6ad87a372b274ee153dab98fe70c79deff96fc8efa03ce2498f2dfa2fe8033b99a ff16f5079c8947f0dfa2fe80f3db04fe4f6bffcbf91746fb7f58fb5fce647af5ffb6f6ffa209fcbf aefd2fe7b709fc3fae7db8601601923b13096706d4c4f9e1db04c44b94a02e40addb7e44cd6133e3 730fa6ee2e86e1c056f7046a6993216eed64e86021f90f611a479f360f29ed8b2a9af5997333ebbd ccd3ac238865b44a1fe7c56f6a2ff57a3cdfd465134b5cdceff1a7f23f51a145f68b0a6aeaeb0d6a e7121acbe619356f78d11f3dae2db7304a8feded9e4d268b2d8d98ef2d84868ea6117d48f3b0f219 b3ee9a8261cfd48b61c38e629c6ebe6e9c38c4d70603dc7e662349512ab9e2e5a7924439fe7902e0 9a13a3c23c096aed6d6c4d7517b703c7fe809a84ac41cd15f4a895932bfeb810f4dc02d79adbd5fd 6567e1041a73fa3dc23c80ccd96c8c7cceb093b03401ab37a3c5aa4910d2681d3e8e1656effe73b6 b334658dd56f0fb86e4877ddbe897f4b00dc8f4a00e95ecfbf381d2236eee793fe3681676f1b59cf 8b119e592e465d5687ce831ba37655b7f7166e5347d3d47aa479ac41ccaf48ff59938d764e53750f 550dfdfc89c257ba5db0142535531ff0e075b9d9990df7bb7c43aa7f28f94b8951fde22fd4c9311f 530a67502fe733a09e4277d149ba9961f72057fd31d31f3b8fe8b4b161387db0f675e53b216941e8 d96c164accaf083aa6dd92e0f953ef7e54f7f5de1f4375793feb0f4c7fdfeff50d215dbd5a83bd9e ad12f3534942aac911c08b4f6cd5d9938a51c9c22fd426948d85c0bf931201db4850037ce628bd07 fe2b8cbef7cea6b5a118c32d20dc3782aed34990bf87de4d8d25d5e899bbb8ee37837806bfee7581 bd5ebd459b97c35144cb6ccaa4fe96fc4fce0d5704f5d28d8a11911ca88fd8c4230829f86605c3d2 1df6275571e928b1a360c3cf1969ed8de23fb2bbf1b9f82b829ed35e1a57ba7baf4fc78d3b833b30 ef87baa25c3d7a7891c3e387bb083049c44293bfcb37a4fa87923f9318352afe0155a163ca7621b2 9bc23eece514cb1744b1ea5df484532b9f6ca47ca6ad4377c09aa777f8cdee3ed30759675a90a271 8669bec4ad0854f4023edfece6aba74d6e32bb8a848b80c98c7441174422a7bf2549bf81f799d8aa 9845831a219740bd6130bfa112e5cb3e60d98be94fc83bec5d02719ec47c93d8f493b50ed29e3789 43eb6a74b5cb5d1fac27ba96ba45fe530ef8ec032f63e0f7607462b4c98ce3a40bbba5a49cd33989 37b27afc5bf2279cb149579352d472053c181e14eb9bdd3de52ade5ce7a66e89181dec5d1424315f 4b36c9dcfe61f4c88aa6bd2dc17de5d0cffb9b7bfc5bcef110345e970c7697a53cbae244546c9ec5 52357b4ce4f02ff20da9feefe2df05c0f827aefdb3fb0fd461c87e51430aa376be58ba189edce14a 5efe4a8fdc92b2c06d8d2ed396096a379374c1cbe8a3e7648ee09b37fb44a363e56110525f1ef942 980479d57f067959611364cf02b4b00e7f5f00cc648adf09c92faaa0967fa11e3795b037bbec7db1 71d3bd45f6987357836dd7d96efa6b5bcf74ced6a9d6918cb0d5d7b4d996f7d4eaaaedddc976334c 3619484b4b8b0062d9505fc2565dc842b55a4b22b7bcc6a4281e2fe8c7bf2f00a63f05804880f9c5 b94a73a0ce1da0800d05dccf8884e2e5033cd9e7d651dfd3868dfbe589d52aa476c6b052655e6876 7655e1fce67927dfab97fc71972fa9441f0c416bc9e924726af0f5ece8c659cb82c0598580e68eeb ebe92f258972fcd57700be66fe81aa9895b8c7a7f9b82fa195d89a14ee2d3abbbb5baef7027b9f29 974d370a3afaa4cbcf5fa56af1a0183d54b8bbba285f47a694446ea57bfcdc13f071e9ca39ecc7e0 da5bcde25ad795c2baefb2c4ba279765dd8a44fc7d01b0fcc903e49565bf4f4fc581a29313f2a127 c5a81971b37557cbb660ebed4cb2c7b4f9d1d3e11a7aaa99f75439cc11ecd1221df60afc147359e6 8a9458c58714df9c695c128ebc32201bd94c34205f4c48546526ac04491360028a2163e1cf7f4b00 ace70b5f5450b3a36ad4123d2e8872f3b2b7a80e97ce56299dad16a2ca31e234b90bc4941ea41cd3 e8f84e4de9ed6d20f44939575e1ec5a7201c786bf5d97f63920ce84c692635c06fb468df0d5a84fa 77fa7d0122fd46783a11ea2f25890efe49318083528cea95b9f8399a81436febf1fe079d97636b96 86563c005de913b87f8c119f96726c6da03b3dfc8cae6fbbb39267a6b493d68cb711f6786fcdb992 baf926c37e16189362aa077afaee30d475b2bd510b197f5272bf2cd369f3c1fe7d0148168a1ff5e9 6adc8d4a25e4fbf49cbe454fdef6f2b65eb923c6e0396abc4a9327a198a7b573f7a346f5fab9d203 79be7367d22657fcb5a4bbb58a7e254b2631497a56036bea4aa4f654c12f53e707bd90cf2b6b74a7 f28f6cd2a0a93c07c4bf2500a942f100aad51722eb83d48350f74477b50803d335e7e10bdd303515 b1d6e74767def56e23299378d2f262d26949d0b9d5140e04d5e13c3ed36359981e30bcd319d2929e 1ad3b91a5850c5ca133d3f5eeeee5ca9a548f269f545f2b91eddc8e7b873fdf7924407ff58123516 7e3cd25f2fc4e8a442755f78db49daf1eeaecdebe453c50be3564c59a16f233574e565912b8a7af3 5ce4c9cd21c7f5ac5289f9743988c99446309d0bb275aa683d9bc9e2f24d856f93aff9a947eef2fc 84843969491c524f9ca85faa6c2c35ee6f891ff5a000d4785c0aa9cca7eeaefa5412bc370624b753 b55ad97890975ce7c637425a5e2e054334faa590efd6038f1d3d722e2d1b1b8fba8b48745671e773 861e8f0c89a7993c61f6b022d138cea093531e2327e2b96e1ffddcb27fa46efc32162949bbfe1e7f 2a0efa4e3d410d8872c02e9cba75caa9e8734359db87e306ce8d1fbe3af2928987c687de47e0a2dc e2ce5cfab3a46ea84748c8673813c964cd92ff91dcd99b1aa74e3c2248f23a9394ce727800b9c2fb c0dde0ec6134ac97f62259476269d57e2049c2e53f4f745fbfc4f7cfca4af4f2542dff425bddaad2 4cf187989277e5e23e1e95367ada901b1a3594c9d3a71d059ddf47b2d141f627efa19e4e5d8f3c1f 07e6943d08e51a7f184b55692f85a96f5ee76c1fdcf7b9a2abe25725d0f1e504387821c846bb071d 847f4b34301004ff7d03f40b2db5770f47552a31e220d9fbe1a2f4c39b40e60f3e2bd6f9148d825c f18cdc5088686b7efdc8b58ead83d85b0c0e196e3adb2f527d14bf77cb6b1c350ad84ead83fd6ea3 b8c71d34b1484ccbb80cb6a393e44e4b977f976f5ee71f4afe525ed9d85caf7ce80677fa6212d75c dafe48af53a3c7d32575c3c82df74041528b219c0d2b1da313f2384c424ddbcf3f62e278e0abec31 daa94b2cbd8394651ec3a1410943b431bc35f1717ddbb0baed8d73eaf636646b30dab4bdd97ced53 d836963df653495eb9971c55a5d0e66f7d0eb9492fb4ba17c8ac6f70434d06f4bd57cc91e6b2573e f93b27f686cfdb710c585dedd4858e63dafa7ac69097246c8f087fdd38c7b3ba69bb7b6ded9ff7d6 ba17aedd55c462c94aaf1537c43eabe1878807af225b46c5a9d240a759a1feb7e4463947467c411d 9efd60973e8d06ece68cc8e889202bc3c4f138728d48ddcb03d2c14b72ff83e1957471db308cdaa6 1d7b7f6bba739bac22e682ae866f0143c509b547a719965cca0b825e2e0a14bfcc3f2869715f0bf7 c5aaa2e88bd2cbf3e6eacef81f12ffccbf94fc9900c82b0150dd205d000faf128023ab0c90c2740f e0076903f85d68c5929cdac77bb4f7a75ee055d2199fb9bdaa1e4f52ed647a3575dd8ddce97b3e77 ae507be52ce4f6ce59d073d229d450d6be5b846cafd637cdd4ccb6a2bbd9fbedc5f965f135a253c2 4f251940fdb7a83fe04c663cfe5bd41f707e9dc2ff12f5079c49aad57f8bfa03ce24aafe7f5bfb5f cec468ffb7b5ffe54c4243ff1d6a3e0255a2dd0330fabc00241f5400525bc5ae20101d800c3a9d58 8edfb80052bf3fa346060f83ae97ce7b6ff48678e90ed173674b74e25c891eea14b406663ffac8d1 5e590dda2eaf637fa13cde3cacea1cd58d26e93f5f60d3bfa9d7e689ff334942aa7ff51da8a6a1b8 f685611fc0074b8e29531040faf83116c70708b3ea01e4aa51f1a78f113a10f7f1432f57f404f4d5 702f1d6ae8dc8ab1358bed763286b6d78bd2de2e5b79d27aae21dec2d28d9bb5f57686712ae78d17 20fa8f6796932eca3a077389b03f1550ad20bf717efb0d1b5c63c0320c9005798ad9f22140026e08 6ad90f039010b143676365fca8552f7b13d468b9397d33748a426f69af3508b39ed1e76461ab0f63 c1e9926c555f6dd5acdd1ba6c60c8eafe72caf27bbc1290f6c203cb0ba4d3faaf6ebc7f20b5599c4 26bda76e31620d01082e9e01a2372250eb6863509b357850ebce933b744892a9bccf8d67b09741ed 8e73d30f135b11862bbb22437b0bb332670b6e469ca95f325753a78686ee928ef994e6ee5d2d3c67 528c6830f79abf21ff5292dde0fea418545bed37a8dae8e0172adea90184be53a0564593087a660a 6ad25c883f117e483ab5a2cff1a79a97b9a77b0eaa13537b43f4d6d6ae9f395830177d13fc8df247 34f7bb4c321531745ffca6f95297cdfdf5f164defc7d3fa1e85babd038fd2d89391be017a759b87f fb0d3d6d0044d668509bd269507b2233504f5197c8926e51e81ef08acf179e4d57362b7d6755bdcc 6d88186ead1d8d9c4ce318d1e6de8d2e66bd97d7f46e47b5d52bc73c1ecf5b5abaef372c7b6bd5da c950fdda6d46473970cdc34f0554c7bdd8a4efdd30be3d551fbf3835870135f29e01f532ba8cecd4 e512b566b73089a127afdcf3f97db6e35e7bb39153ee280b4b23269885e0d0c93cc4a312c3c6fc9b 71324bce537a015da9c095ebbdde3e7057174067394c99c7cb3b47e3ff5e9251e71f4bfe17aabc6a 02c4ffc4eebfea27e15e6a15b5cec225a47297200851b9eabf3bab81974b5fa7ceba5c5959dafbb0 b3107f489a8dd58737dab9ef1a76dd5b353e8a921a2bf7bd227c43e68cccb82679e10d1cbfa41ba3 eddf12505d0fdf002e12a3f84eda54626be2dfa767ad0af1719d97f231e5661976434a0a58540882 614986fcb7280ebd995d5d3a6b6eb9b1abbab137adccfc6c38a7d245a76e710f9a67b2defd5054ee 57aa0071f2e0a95197f16d7b902ea5112ee5b6d5e49ef63dfe54fe04b51937d0a108eacd5e31a460 74110c8784e40b2ae5fb990e077997801b7c774b586e5147a1dc9d0d9feb07f3b46169bd8f69d273 6e05cfc7eb39b6932c6cf9f296eecc6fa1e59c183b4468368b8945da5dff409290ea3f4f40f53489 51eb548c8af4d5b8e2e936a86d5129b2cb935210ded0a52f3884e4c955c2f3f20c5e74ef03bce3a8 93fecc46b8c7da6c1f8a3b4d18e70f6ad9624e77533584ab4fdccc6f1cfa7c17a57c634a89e8b470 101419e0c2a6af6f858a755dfd2df957ce5a46ec809a485cbedb4d49c362304477cb9812e5bdfc75 63ba25bb9f4a62a8b08dd3d5be794e3d677aaa6da0ea461dadefad83b795f9768690aea948114bce 4810b6d9c25980f0e0c8bfc063c7ef4efc86dfad99d5579290ea3f4ffebd802a3f8f5107c21820bd e9f317aa73bbc615dfc8fb1371328f1127b45b7ab755a73a690496b528e4cdb3d549c20fba307b0c 9f9574637c772c7d2e0bd70d2a2ae5d54e801bab0b5fbb5645ce042ec599fce3c81d2b0cce1dbcfd 863bbcf66bee7059af7e2049483539c6a813f00fd4c50bd4a07b17d43ec635088d5ec1933b93a9bb 1ab44fb69efe48d6a9f7d10db6f909f50c1c549e9b6ba5f32086fef03acecf46d26a82cc62efbbb6 61a97d9663bb4221a91bf63cb61896f4f8134b527b9c25e7d88e75e6cbf5df12507dcc63cee56d02 90f95603b5b6d98ffb52fae67f4a48eefb26d93d3270aae3cf26a6344963983225ed123ace7313cc 0a4ac37b756e5cf63ebca0cde548a845d198ed93059416952a474fe4ea8d7ecf1f2c3340ce24c320 a72343dfc73bc637c79b5866dbaf2421d57f9efca5c4a89308c0fbd71420bba3fe458d4e6eeee667 b650d62d17f34dcb9acb4363e0724b2ddf58314f8829584a7c31851b28f7dadf785abbdf179181dd e3ba4f75c0a4b3e505557859c7f363717c52f3bb2bd0698ea7e9f10a23687e34c669301a603468b6 b63f95ef18da4263ab32468c4ad106a84da2614855bd4bdc3c731f5bafa845236ce2552d5faa8d9f 101d7031e5d9bef78fc3e235bb37dba2761e207c7bd1ae274ba4db742ebead9dd73b69453ead324b 3e5bd5c48d3aa3ee95a572eee94c5d52eb139d7a76f1bf25a00a3631e7d59b01e4229a71b74746c1 7060888efa506cd3e5bedbe96af3612af38470691253aef998b2ee5ca7815390b6915be6c90d9167 b99456a62f620da68a8f478b7cf5ce23c2684a5b62ffaaf26495ca8aa48a3dd8338ac5b0396c76fc 1749025d7f52fcbb00b8b08b51f5f71c20afbb15f72574e2672617d6c65f9bab9ebeaa97e74663f2 ca71bc5cdc69b32e5d6759c3919efe282d340d38c5728f74e2df30991b9e495ed5533c432d062291 3cd6241a296c742266d7eda99d6ff2278b051c81a3679284d0d981848683fd4f05c0b5c3d78d4272 c505a8a51d276a6de723b70476a411382afe54fce92da6ec9663ca1c9abc5248ba5426135d6849a4 cb020db3e985b131a97227ef911a2f02c2dc619993331a944eedf3a076a48c59ef1035cec9cb1d0f 51b3461fcf2aa08f6e75b9ff5b02e0050500d2ad2fe3be94f102b6ff69db35cc59bdd04976a598a7 c2f31e20e964001553bed6970a31a40462a55ed9b760dde95b2abc9fb169fe46d61e9c4234a99571 22ef7df7484bd5e8108595cc61d4802bfb8f3a68eec5ed79b29fe6b2c7fd5b07a73f93243af857df 01981662546c80821ad108bcf9655ad12efd35a4c64f4aec1ec0ba714ba90a7ca9088d91d05e895b 566c94491ae5bbd47937dc51bf76ee7ce1fc37af936b5e9294cec7610c17b464ab4e7b9f258210bf 768a197ca1b72abb477dd9dead14b0ddadd414f65309bae1dd04c8f3fb2008bd4a649a1db0965584 0da14767c61c6343ae75e9b58e52bcbf3a22ecc47e74e8120f8fcfc88d9d136d1d591fa3968c1d3e f9ede190766bc47e0e1768fcb6043c5e24f4cb4e6907eaae825b3af62a80c4bfc130a192c2e0c9ba 1a0b06ff546ce5b5bc805a93593bdb8232538ec50675038379f19acbd82b69d762059e2e233a23b7 3bc95d80daa6784036d47cf6d4b3c8e2e1438de064817c7b9f970a037c954b4d76ea12a03b4809b6 189ed1f75bb37f25b607ebc16c1b274bda102febb1692f0a4e2c65f7cf2449b8fc9362dd5fa2b43e 980d9487552f9ad75959a62f6b53f8888daed3e1b8ea76c9e4a9ebee8c67df04d1d650ee38742379 2f0f6905bf4fb7460c36f262b03ec010ad924caf6e8fb55c6edbb02a950dd9cac19bb65769ade95e b1b7ee45f5c92ae2c6ebd528b53ec6829d7e2a5a2a03518f066c72972bc325d34482934d9db868b3 7db199d328a4d4a191231b0a523972cd2d1203663b7849964698b6c61618f2c276db8639386cc866 975afbe73ab7ee851531f651e0db6af8292ae847ac68a838852d3473adf84b79d94bae66995726c5 bf250adc254979baecd0fc5915003377267d0a12261bb2f16813a79e1109870cb3bac6742915439e 67e39b737b58066b9f1ca6d7bdd8095871831e827e84760b9d6690de525e9446cb45019a2deeeb3c ba5855206c517a358ef32d829ce790d91366da71a7ccf026fad712ffccbf94c89f9a7be42da97ba0 511f8a08b2b4f81cc49697c5efa35775b7cdd95d0c518df9c6d95fb075cfdf10e8876f3368e682c8 cb451e5117f715622e4a4fc89b6fe17c34d30ed9d4f7671a85fcd424b3e5e9b15380a78da0da9c90 836a6fd27ef767637ab2d8c6b2c27e2aa0c276745031ac1480e8c60c543bcbaf275d9dbe8ba03a9b 2d4155726fa07a69a540955ab741b5b75f0128e2190099c10b407c398c102a9d0dcd7c0e0a1ba374 33b0dde22020307816b47303cca786cbb3178a4bce4d5b1e6da3f31d69bedc7cf2f4d4edf716fba9 fc19ea0f38bf01e2ff12f507747fb89aff10f5079cc9e28bff16f5079cdf26f0dfa1ceff07273b1d b0ff8373474687ff6db4ff3fd45b3f460d833480e4de3ca6c41ea08ae54aa0fa64d600ae549558b0 1c80d3523f267eec4075aef3a05a2b247781081f674178ec8685c0416d38201cbb1db4b7efb1efe7 72a8df1bd68f1e4b765977b2ccf136badbd1a696ca93ba533f1e5ec3fb73fb0349a2eac93136e8d4 00503e950190365980eaf6a480eab952017059c700bc1e3d017c920af1a7601c438343cc1b88a08a d7ecd04a05efb0097ba5801c1a75df278dbedfbb2a532f1ad81b8f75e0249ee67eaadd8b7d4fb3bc a9352146779634f91a9a4f5c95bb83cd4f25b6e622e644b231a78fc6754d326a5ce1752806cbeebf 21d547ecb2c0965502b052980098689200866ad7484f504fa9772a200da3ecfb9f57cbef576f432f 62948537b40ddcfd4c739c231742d982b8366f3474997e71737052656cb6534ab6bdfe4a1252fde7 c95fcaff40ad667628a88ad21354dd311c23768e002924039841b50a90fa680e607b7a8e61178f68 8fd79cb0954e279b00059ddb0bf2fba4d0f2b82b3bf646f5cbdafd08af933b0972b25dba1c2ec6b1 a3f12f6e0748553ecff64ab918ee1e55885dff54be379bdcd60450af9cfd85aa3e5e006e9e6a0006 0b02208b8f0310665103c8fe88c6d03403e037f78cf6fec20fbc743aeb07c9cc04d8b21d6fc4b033 57140e1b37e30b94b328780f131f41891ba5f5277d4e959f18a994ebe1feae6728ec66157aab9f0a 80603ce69c57634e88887d7f47d700bc92ea00e9731440f4de77fb7624a01bb1681b805c4d0e204b 534b662682c01b17b33e7333108fdf0a3d57aaefe66eb6bdc19dc582e22cf57250f4ce92be3cb323 9e51ca68fa74d7210abf1d417b7b3da7a2d5eff24d86fd43c9bfc8ff446db3b1fbff71e36679775b 00a14d1ad4aa872401dd6f83daac842559e897c864f346e8b4c528a0d6958c3f30ec24cfc64b5793 4994e266e92cb4f9d1be03f66a58e592f29cde1fbc52668be7fb3e431c6ecd07845d3baebf91fb21 bdfca900a84b5adf26b06bc6f7cfc96513f7f89419577ca1131b34c3c694428c2a411d507ba2fbe8 941b5ec20ee1e841bfb60dfce12b97f126c30be4ce74b4eb2c37c9cc44013959f1fde5f6e2a19aac 946f45fa5ef30fc9366d579787f6722069db0bb81d57977163b1fca9c4a8c7d8a45427993a53b671 032d5800a935bbb135db7cec537befe834433a8947e086433cec361aa20f64f5e549ad91e72ed0eb db2931eda2f57a6975f390da8e74d729ac35665066d5c2732ddeeb19e27c3daf337b790073db0b7f d8ae2ea9cf149566d3eafc5f2409a9fe49f1570034a3adff85ba98f6626ba202a8a79054e8e51aed 201cb631ff3d2df19e1483ba45a46adb58e7145ab54d2667b49fcd640bbd97400f6aea2d77e92995 2ab5b9ef3702f38dd15ff8db119324b5b39266db1a2a5e35301797b83bfda9fce2bc8cf2dfa72761 c4b7fa51d58edb28de07359210a35637fd0e421469f96fb5b7f5d301c2b9684b7ed8bb66c130ed25 e6e9814981a7ac7553cac69817ee87fe1db97a737a2c87cd88bca4878b9d949b96d7e2520e96dfc8 edc29a0b4ae13913d68febf4a702208cfb0df517e7aa19df94346e0062d348e139cc8118b1d4f02e 3ab4fac6d38a10e340947ab31a52f365f4362de3792bca96527df2c1cdd16e1f39ea954b9749476f 4b39a7b6138b72b0153685e74aa8f0fc9c7f8dcfb35898298f79d8f8cf2409a9fe49f12fd4d73446 15bc1d808f7d1720fe7d086a1f5b0ac2433af433d57ccd45dff985b36d4507cb3439d608b2e0f25a 94773765b7e8bc6eeea762c97ca5fddde776d1c1d282daaec3fccec2163c3266369cf15a2f39e3b2 9ec782c5422d279cb19b8d7f2a003a73e62f4ee58d7f9f9edcd40535c8194527d210fdb71dfa5e7e 9f879dedd81fd8b5f97d61ba9c7cd453367656b76c5ebc9329f721bfe5972e2d51d511aa27107147 ee56623bbdf4986d47e30ddbbeb7962c6176e72c716f7d5fe5624783296b9bddc94fe517aab72a7c 51016ce25edc9732a3d0abbe783fd3f65cb76445251bbfdc6be6f928266b71f549aebc7cae41faf0 38b565e6cab127f9925f1f5461d7214dae35bf7acc009ee7e894caf6e8d1b3bea5410a5e31fd5471 ced0e5e28cf147f094f1cbb9f15fca37a4fa87120049a209aa693c1e393bb93d40f2b41ff725641c 84fb1bebe569dbb2354f00e679b54deb69a3597d2afe75a11ccec6e1c6168ed4e53aed0a621569df 38b2387a31c3d137bb9bcaa9509a5496f3f6b988a637d42c975bd1a96e7a498f3ae18ce6a6e1948e b6e1f8a702a0a79c706e63937e2a0780f4eec177364a1d8ee38a67cf8eca5f9ed6293f7fe913b867 be4aeda0a222fc697ef70f3679cdb448467c2d5b3cdf8a7b11c3679a2ab5989d6c725b853f04a2a0 4d0221eb385921ecd5b9f03616e76bfd35a3a67b63925c8db857463f955fa8956311c0a57a3c76da 994164a9e8d8bbf89b635ce773417f4f7d2646540d7507a4ca839ccf929676e3d4377db953775e68 085d9e1dceda32755b4c55126379eb74dacf3ec72ec4368f9d607f3c35aee90d01fbc69278652e53 720d51a37361c30effbd2401e27f9e00c88dc775d5161d8f3f1bdd13405e9f303ceb50d75d15da5b f33cd4b0173a599031e5ca7e90bb2e7ce39c3076e9df3b4ac4dd3ec3f51739965e0c21e90c5947f5 e4ac52d6b177f592454bfbcfb6dede4f6ea9f381192c77c73608d1a30d6eb393d16747048653c39f 0aa8964d0b54d77219c0e83259c8d500c100688d24fb013f93232df7b086eace2ad00fe7550b9297 c737e4eb92588946de3c72c1fb4a3059f7469dabbec99c5c6a7a3df69f59ed901ac4ed3c5bcd95f1 c5121be372d13dec7965bd39f489caf2e0af5f9323b1a69299c2eff10702aa4d2746e5b5d819b99d 295013869137a79a45fd9de966552d2f4d1f8e640931a2febe6675a221c15434e57b702799f764e6 b5017e7eb1c489685977f2c84423fe309ea5aefb593dd9b432e346bbc7a00eedca0433d9adda1686 cfdbcc6a9f928bd3fda8771bfe4092ba498ea0bafad8a06af91040ba0f2af4d291651db587a2bec2 d327a63cad624a4696afcc3892e0970ff1bdf3a2c7c8edebf4accd8c054156dbeb635411778749b3 79dacf7359165fdaaebc5bcbcf64270bec09408861c4a0bcd5b5c708abd2c16a57c2a5f9eed69f4d 7e2ab135f33680eb2518d4522ee1e53bd8517d0599dda3539d248ec78d2f5596f272f1e64583124c be2775d2ccdc5f16634a168a2933497227d9394ca2c5649f3f5757786999ecd739d1496c873ff9ad f1b01edb4626e56c6c7a90d910b5476b7352b2f32dce3fc65f49a283ff3cf94b01b18314a3eedb48 10e84647dd92fbd8bd4754f29a7d99a9cbfa311f8a0da477e2b8aa293379baf03ce3b98141b4b58b 77f850a530a91b79e866f1fbf459d9a9cb6b13d3364c7f6be2a7e9b6619dd71bb2259dd63ea50b6b ba967dae7bd6c85f45e34765c5cad9fe4f25f1d6ceb795eadc8ba9a7f25a23c2157013f5b236705a 34a46128b459b9c17ef69d318d46a715692e5ff889ae35c8c324a49998ae23c6743535a6cb1a5b73 17781be7a8bf376dd7c8ade9ae515945ac974cafae869ff710156715149de666473473932434739f fa3f156b57bad0ea3ac511d7883cb2e2abbd90f8dee31a7fac651f4c9eca26e16e0a122f59c2d940 490c95acefe76fd0da6d0bfb3e86bc06b1e5cc7a6cb926bc8fd9d264cc96e656c3f7fb828a93f703 cdc86f7db928a6bd655e69a4162b68589aabf8a11d4bb8f85dbe799d7f28f91779caa3157eeb65b3 47f1893027f683352a94da8f7a67448636045981c8637488a443862115bc2457750c794af6866c8c fd35dd69a756dc004af6e8423f4209423397a8b65ce49dd6e2be327b8bd2531bcfb7b0b69843868f cdf046e23fb825767aecb6efd34648383f959beb3f92d090b4429638cb2837ecac8deee1a9a7973f 7bb9f72ae025e955c734f4d5df36746abaf60974b51a8212864e539fd3729173e8c5aa74e7e7ea56 9667dafe789b2136ae4e4de2684c1b3eee4cc8fe3698b4c131d92d614c8f4fb9713fcd5746dcfcda 187eeefaf0a772c923f50dd7510eab732514e7c7a1959377ea7472db9008775b0dc38e8266c48ef5 0d775f47d1a2a4cce26e0b61d00caf2deb53f3b4ee4d1bde7a3c217bcbf9d8e7c6e8b89f1a6c47dc ac850f3fb7fa69382dc1d440dec2fc20afc372ffbe1f3cfb257b66f75482f9fc8bc43ff367c55f01 e5c94c8cc5324079bbcf824a599c82ca181541e5dd4e0168f08a9fb7c3e20940a5ee03541c0c808a a8d440054bc77fd76ded41258709a01c3e925b27289ba90094efddf813c734620946a07c6aae43bd 1b6c82a6ba47bd41b1b870244a98dacbd5646c553a9fe10fe477ceefcffc97a83fe04ce202ff2d2a 2893e7dcffe25c1a31270a13006a8c9339823fa2ce12d403a8c0fbf87f84d73fa0dea5e6bfa0fa2e 892526cdfde22c96dba33f634ad6b0ff29ea662dc5e2c6a8229f0795913607159ebac494bb2c806e b9d82d30963480d8d32be63d7f40e563d441e5595980ca794682caec2cc7c496092af9c2fb5b37d1 b814cbb307ca56611161951a1e9c8612e6317b78e54817766e1747c399895dd591512756831f0828 1358cc49001394d56be16bb495132380d70d40d74b3cbe8607f358180e54737aec19deec187f1836 01546ea1a0e26214a848d23db670e082cabc950115640dc7e28e236c04d6e17e060e7eb733c6dcf1 da58dbcbfb3cf1a4cd57279aebf664347c85c767ef0702cac2e9125b336b81b2f32c82ca315a02a8 ff7e806ad62b832ab64741f56c89a0ba2b78a08a54be517588cbb50034eaae62de3d13f33e5450b9 c4155fa1068598f7d48830ee3a09753788adc93e4eeebb9ec3ed1bb2db98af69b4d46d62357b0d53 e7de336d743b7f26c92abc3f96fc4255cb36287ff41895cfa00062ea4f509d152ba0fa9456004e57 afa0faea04a04acde23264dc03d07db901d02ab635d4b4e266912ec7cdf7b5ac4458b44e325342a3 292dc33a168f7ac2cef8e4ccbc0b6e555c68a3dbc26af11a36f9913a1fe6ba0aba313a3f10505618 39b666d50695925b02954769f5355ab0d462ca190ce026d802783cbac54282189a8141f5c2c7231b e48c01c87c08003a020340ad7a2adacdbb50049beb7e587f329ba0c54a943b066fc2da34919d6edb f8faf9499d276abe914ec6d00fc567bb77ad80b67f20a06cf331eaa7e9804a7c5b891b687b03aad5 bd0ee0f2b90ee0536d07e007adc462a5e2d3a001e05690b851913ebfe2516dc4f0a129495ad8b8f1 6140a842316877ecb64fa145d463a763da467bbb937160e4ddf3d3e417caed1d8e1f1b8d1dde9153 bb7b6b489ff65f4a32eafce7c96fa895523fee0fa36c054085e11654b75713c0ab678c05b6b1c35a 739e00a997330029765a51fd15ccc2d608c3824e58a77dda29debd28c8b9def09dcfba62268fb813 7c92f89e4ebe429c4c64ea1f9f9f118f2aa8ed4defda6237ba1d2bf9de95ec9a1dd967b9d60f0494 dfd76b6ccd51ccb92a43715fdaec40550decdfa622522d80f4c5d86165100d20d76d2e74dae54640 ddaf139f9d8db7de987811ae840b17e75ad918f6dd1c02bb143f2c2d889d764d84bcef5e1c1260ca aaf85edcf1da7c7275247f20fb37ae7b899c45923e7a19759a8d1f08a8141fff403dd6aa00c2091c c0a982039042b30310da39815a76a347cdfa2a13d078aae67da4fdc0bdbcdfc9cdc641fb8783bd7e 219c059734c5b0062b5b77fc415aeb5760f8298eba53e57ea8acefb5c2647625cfce48ee0ff1de65 b4ed7424512f34a5290eeaff5e92015472fc853a5c78a0c2756000c99743dc8d6a6e6ccd698c1a94 4e919525b420988c52fee80e60570ed75da79c31a6563ca4db998d864ae95d212bbe52eee0a15e1b 695b59d14efab1dde4ebdf24f23eba9403f83ebe70ce742049d97257ca2e82b6b8783c1a6261cdd7 7e20a052d76fa082ae62cefb080190ff3ac60d74e40104df76bec194ce840cdd3df1f281d6035e2e e5969df267dcb4f6356568b8f9d24a1b2a29fc99732ba452d96cf8bbfe40d4db09fe78d78e5428ca 74c84d2ea96d73286569d01317e0d9111e63a629acd3a78650be60c99ac2eff1df0ba80cac7fa09a 8b1aa842de09c05c7c8a68623789f59241606f144f0288ef285dab60c3e2ba66b6d6545be33b78d2 6fd47b3cd67b6845627fb33987ba76d9d5e5c2338597246d9a1f29abb93db1603dfac2ba756a0b65 0f6bf14f6ad9e0b1deb8ce57a32ef203f93e3dbfa807dcff85da7e130036491fd4325a2f3a7508c2 7fb7e737f7b6461cfb65b869d3c92ecbfa308f20cf62c5ea3ef4ce7b762317a5adcc1aa3c325ade9 8cb87c613761cd313e8f5dea4913e0e1f9accbe951abcded39b8194bbdc1d59b392496e24f045496 56dc4a59327e1046ab98735b2401921703506bfbddd0d577c9de0f7e9aee898ee241ba65a21f60d0 f574fa95efd045058b4ecd9bab10231934ef4b49de0498581acf487eb7ca495c3d2c5a2cd1102a6c cb79b7d9961a36d996e835d81669d7d8d6d64612a97e2509a9fef3e4cfe41fa8471f4079bc0eaa62 fd0c10448e4dbacd7783b03edf79f9429bb5b55d433549f16e69a28739eab638cadd6d775cbb0ee4 ef8a954bd6584cc5f242daf048943eb06456e0e8a84d3ce9e13e9fa5874bb645b37bb64187d74b9d e9064a8de9ea3798e95e2fd51f08a8ecddebb709dce8004008d9886fa6ad1875ae0549d264d77f13 edb55b3a35ce9619419231c88eaecf7529d41ea6f7f9dc7ab7341437c97c5b5cabd5115faf2c976c 47bc6034a74c282a03daf773dea3c07901a189274d65369b3a25d8442cc206a6decaae4a0fde87ca 0f04541810b752430c01d4e39aa0fa195300a19c206a653e4d3fede7e7b6267d92a8bad9f98887d7 6d1c3f5af54078dc3be63d94a713a12095ce5a9d3734d06729a435a3c7fbddfa7ca521822c3f3f17 a27a9ff844956f34c972a1513be7e549ed3cd726f0f9e24f202a9399547e97efd5fca1e477895183 b8f60d2e36e95c6e017888d1712fcf8461b72fc22eca1dbbd6d1384eb5dcc39dab3b8ee71fede2ed 791d9f71577a2c7619a1363c42ac1f1727db1888a9f4e87c53762be275ca1c4ff59b291c5bcf9e7b 6c5d7aed533d8dd489ea1a4108f5d180c955a10d91f75aa3fc03896b3d159b3494c3ef008ad2da00 3e9ce8b83b95025fa8340bb6764e55f5f4a25b7842bd07fa703046bc7119c3bcdc6eb82bc20f22cd 9d49bc946c1cd2a00ab3dc80d03474716aa6a1fd816e7cf83dd745dd3d876cda876e8424b1ce63ab 97878fd62e039d70e95326aa957ce907125b331b0fee612d02d5f4a703e050a2a326a55aeeed760c 8c80d7ad27048765a5f1649219f63b7d602ed7e932e74adb8616f18447a4984f562c512862d54998 857ba7d6829d1d06bdde6e9f7287029e352e2e9ebd5fdab878243afbe1aa5ddf87ab4cf5e09a99ca b169f8c57f91e46afea41840b9ea1d405de7d7e24864f638fbef54e16a1df12ef784aaafdbc3d92a 708cb8dc5da5ecf87a59a76fb66075ee110b904b9a2e14c502a9f1d9da896c2cbbdf64d87d65be9f bcbc3d9e3fe52ebb128505985ae8b677c575aeb7bba6a8263e4da175fc936a54e32144a6fc0389ad 598b39b14fb29467f8dbd333a51f1de5f15a3d95b45e7e906d21f6a88a54ed3af5dfd8657db265c1 723e1a3798d43c7a396b0152975799d379a6970ffc74d9d86795fa68f758143718568199adbe638c 6dcd9ac1db5a8b1960506b9c5c0db6c694faaed0dd54778b5cbbf20301d0acff00d0a5f401f07ed2 f08170683f4bcedeb9fb97ceec2a5550535ef67b75e9153fd2056204255b81b16f657aa76fc7ab79 86df6fef448d1bd141409fb9fdbcca233b855876b15d7f3cdf36d2b3c3a68d48d735756a8275d7b3 3a9be6ebd1dd22deb9b9dded5af5ed33542a7f264948f58f250092560aa8b646292fd3d95aaa5218 8a77f2453de565b78a5d2aab862d1a9806f3de479db1627175a2d1c8e24913ad5c4f74edf03c88bd bcb5cf8b6ab2309ec925cbe12bdf14c553abb5f6a9f16cc58d9607f423e937549c9fd3e80494dbab 816935d7e7bc548bdd9673f50702aa295205707f173e17dc9db8b78734f3dd1f7a2fca6223e31e78 bf7e3739ae5a2d3272eb1977b5d16c421e8bcafad4b310fc300945125f653bdc0e52b2f2d6dc59cf 0dd994ec35dde5c16af8a10ae8347b692c17456fb6ccab337271c7b27a2c4c12ee5e2ed265049dc4 0ef90f045457f7e74b30b7bbfb492d1f2eb2903af34e1f7fb022026f99b97b92e8127bb5ce7876ff 21daaf4fe138748fc92e23fbf9a790c450992486ba4c62a883f5da3f17f7ab887993e847f0f8a5bc 50ae8bfbfa65c477e2f03d87ac3634d34ec268a611337286b72d658677fafe5fca37e1f21f27466b 2d628f1af1d8c899d619e72dac823373dbac9cf134d9261b77677df2b10f791c3aa694ace15777ea a26a60c853f736cee10cd63eb9cbc574f34a927f8ac474992476ea27638145e919cd92c8e9668638 fe71da0833c2841c0ef5b12fb29fb12f4dda637a7659fc401448392f65a132580bb5ea6b4d5f0aed 15d9b82d9207c1e173d23ffb3ca79477eacc68611a1a8eb7c7aab15afb0485afb8fefc848ae31eb3 94e788b8ccdfa1fba2a47ed4f9b61a1933bcae39d3635b0e260efd488f7dfe5a1cf7d34f6434ca5b fda1b8ce2e07f26e94ac901c2c10f9fe03b90e5af052d03ac2924971e6946864b3c3bddc65254c5b 361e9bb6797fae86216ea219110f97f9eb29b32829fbe21cd2b0eab76e6a58736a9eb024868a7d63 a8b3b1cfcdd05124b5d6a351aebe1b8a287c1c6654981ae4f522df5fd5e15b4f255a5a0ff2974117 ef13d97f2ff1cf7c8fa222b2738633d531b197af1dfc8a30a3edb10cd6ab6130de2ef372ee38d330 819c361c941ed303841b8dd21f7198b9ab97c1a2ac3dfa774c7bf557b063f6d483e9f42047f3bb1a e9802e1258496a6fc764825ca7f10e4a6d472cd4da9d6ca1d5a297e541ab5f1c2d9adc6681fd4040 d19cef41f1aef0a078aa3c63b97e4069e90e4079f6e1be613befe283b29faf82b2369981324d91a0 dc7f3d40b9580c40c99e974149b8f44069f35e81d2747c06a5ce5301a56ed50525789906a59c04c5 9fac1a28cd53cd70f7616abeb387218f5edb25e713a149028c335df5f2ff5efe0dea2ecfff39e7c2 5612cef0cb995c4d4910baa044a6ff813ab5d404d5fb850a3faa096afd5f51fb15a5e488dcf0279c c9cf14a3cd21e6350450e4eb2f507cd8f13f376af1fdf9d51641a56587a03219c6ce769b58c6d037 1a94e5f71394f7dd3728d737b1bddeca00949cca1694c425138bfb02a57327be106c1f5fc54caf7d af6697ed44d0cded068d60dcf0e85308bb298480ec85542a598facf3ff31f69eddca2a4bdbf66f31 92cc39671490a08820495130002242ebff7f9bb9f658fbd96bac79ddef970371cc313d69aabbab8a ee02f933009667a0cef8a54189ed1b40bd2803f0d2780225322620d4d2f72722b86c9af0e4ba82a2 5f3220b2c41de0f7690aeadd9400de7b0c005eab3200fbd20a147d7700e6b400c401da861834627b 9aedbd5d4d6cbfc20fd108521db9e2cf9c72d1757a26ee16d575e240fd1c7f05c0ca1b1eea8da054 af7703188a42db14a5392004157aae9f511a14d0531b7efad080d0720740503d171045065ed2fb54 05f83e1aff1834748e00de1754283ab8011c9fc00b49e9f0c6bfb2dde876f2fae1f638ec0603e3d2 f08ff551cdc38fcff2a3120e887b1da4d03f03601df9a728035649eb0043960f80554b7980073609 08ef01ddacf90e7ad8eca707e7b42a9b24fb0d40d8730f105b350f88d6a30188645f15fe180a0097 f706c0b9f703e0e4321b53fabe12dda273ef5d1f2f93e47d0086af8e7f24172dd761edeadd33ebc5 6baf4d23ce416ee7fe0c68e4aa00b02e6a40b134d4b99f43e77a98a140a107fb57e1fe4040216a0c 7e2282a4de7041164f49b23f00c4f38202424fc3e6a61bf0ba4af36d4c2f04332e3ef54774a7c0f7 ed2daae53018373aaf3d8a0e9e5997ebbaced16dde05a95e753ea5256ee79a8ffc652ec9d91f244b 7bff3ef907a0a59f36005b574da85770019e3d140071a8d1a0709cdc40b18d62a0480b6350841e36 2896bc0b283cde517cc37c2c2e171fad88eb3d6651557d25fb08dec22d52df8d79e91a06fe247ebd 6f72210067a3f59c747643d77987dddbab82d49da1dc285ce637033d138751ceba0e3bff0700b6bb 409d52fb2792c6667b0fe0e37b091060ba06c5ac7007457d02bd98c09982e20b9140d1a85c230f73 de513dbbcbbf9b97453ddcdd46a357e4f599573f3d92830fc39c83a1b20b9e7a1063fe5cc9373d62 9219dc5e5d2471d56da38696ce97d906b798060c1eab919b3bfab295f93300663cb600338727d8ae 07a8534f5540a1bbe140717c7c80524a2880d2303b8b1f4437710723410697773b2b046157ada55f 8359a61c8c5376ef69e6cca57f7a4a1b1f7d4bbab742cdbb7b1d23990787d62bb7d7bad8b5a758ae 7a26c0a2707a5c31fcb8e14cc4dc69eb9cd919cfb2ff45b27bf57fbff901c0ae2f3856df2650aae9 3c0191aad440413a09a068032f494b270bbb07f368d3c1c530da1e8f2f159db8c164fa024f44e771 1fefb79a5e318b4cdcf2e1ce24f5d36a535dbabd82bd7595393d7046ad08b573df74ddba7a8bcaf1 997b16cc9dbdc60cf9ddcaebdf1e92d55500327f06d4e96fffabf367e81c0ca0db1abeb649ba174a b54029ae355ad377874ff12ff5adeb41bed0b8f92b5c0ebdd2a2957f78d2b9726feddaddeba0654c 9d8cba622e16d2942ed890b1cf85cee36b9597b5cab1c96945231e0fb02473fb657154cf084e569b bef6298843facf80835d049b345c5a50af97a4a5c90628163e50e7a0e58332522c454da3327c7db5 3b1de47d3a31019fca7f4eae5b5ab9f797697daf87878fd847b659b99057b77b2ee2fcece4ca33ee 54b379edd83c3e03b3f31c60fa778fe07ae6eca1da8c38e4548ba1332a164f52ff8624a5fabfdf00 3cfd11019e61cf506f00a55a7c13da282a82123ff663ee8c17c2d84d755fd996b7f29dc264e35584 b47e0fedbc73cdb44160a3f74efa4c1b1a7ef2f44afdb8fd38c9d06976d787a53118ea3b5d6380ad 67c021ab21e93daa920c9d3fd86e2f7ba0f846e650ac9752ca2d40be7f06d4194197a32040d7a898 0d01112b3f9174715bddc11b4ffad1164e8761aae6359fe7f961e2ddcdc9fa214e36e255036bd3c6 6ce1712e0f1ff16953afe7cda8aa978c61a5d1d2c72f74aa218b0fab92fad03c3839f03914c9525e b9e3b9acb2b63fc9fa34a542bd52b257bc7f20bcff0350655afc8fd47131c9f6596dd897da5067bc f6dfa17ecfbdb25bbde23bbed87705509adf3eb55a9252b5cf0cae9ed7fb8673da96b9d0948f4eca 489b6d5c9beb979a4a604cff507c332ba5e21b8a2c14872f5968f979b97e3f65202ee97dc01aa97d 303f7c12807f43b270ecef138013d028f1e61e3a725be8ff14a66ee73f09bff2d60f416e9f0ad0ac 58f0b8c2bcf1e8d4af7de734d3569752dfd99f82e5cb3241aae61b990d05b4051f21872bb72829ec bbd0fec9dcae32f37d7bd6dc49fbf3cb93f617262bedd5555ada6fe65f69bf9a00693f9ac4ff3f00 25e660938e741be0f63806855dd801a5da3cd97f13d7c8eefda57d2b6fff8a56b3ee462288dbe8d4 acdaabe56c66f963617bdcef4e47637c7fde35725d7f1f6eb491556a5499d8ef04ab2ef5f3ca7837 4ab98268745637d1e816d2bbf48cf8ee8652f5b33bdcdac913f6ddc16cc4bb83548dfe8cff485d1d a1d42f0540e1fa4d8a4cd1bba8e964ede0380a7c8f23f4f73d56c48f43366f85b3dbc147a74eaf97 988099e6194d4751cb518b672450ea8bf9772fcd9ec8eecbac2be2546ff5b60b996236760a3b6f2e 819bda2e06d157cc9d731f71e2e5803839e762510768f42b92759dc9113628fa1fa9445ffa806226 0fa5caf4368cef07f3495426974758269cab79bf3e2e374021560b73bae67785d2fac919292a9359 5b8aa0dfddfd9e82ada31352464406dde2c67e14939738086baabee2dda3a9f3aebcfb08b7f6f5bb 59898fcfe6a23fe22dea3cdedb45ca0fff0c28b1b403f8c67100213b29501c95a1ce48d8bcb44f3b a9daeb71f8697fd3ecb260d399e6f95cbf16d3c74356681ab3e963a1154e98a83c918191d40bbf49 69160d45144e111b9aaee0bca7be9b5cb0c7175ceba3695cebb9fb70cfaffce5f9be1c0b65598d84 6b6424e907e1fa50c33f03aa84e3117e8101c5cff6b8a2da6c471b3b473dcf3b8ebac77b6d683bc3 f1f22cf0b671ea71b7d8cc7e7a65cd218de981f7bffc4f9ddb464b9546df832d2e2eb9e78651361f de3f1511ae9301b5f547cd4ed7a38c24af87ef4dbcfe64b514fba6d40fb7d5e498af7de590af3d77 ff077e5c75b50d23231fdc60b7c733a084761aa1321bf5bdea735c76480bf99e85933739f5736bdd 34b637a01361a374f0697924efcd1b2d4dcab824926ff6b859dfd20f7eab736f367e56b26b8d2d56 1973334bb692d0d6dade33f9e32362f25537b34eb3a7effab09763b67bdf84acf4e65e7f069438d9 030243eeb02fb5a0ce7da7ec5524e4e9602df476aefba5c41738aa7b716ee6c7634bb77bf7b4eace d89ad2395d46fb4c2f4f8b1777b6db3cf68ec9bfd6bd1bfb995c5febc9769d66d02a55a689a635a1 e8fd48a19c533ba2a86f3f4763976f8a3e5dae1f66e2ab11a37fe4a4d17e8ebf0210280d2338aef5 00c52599761fdf8f6e9fcf8583f5646ae6313dd8b80666d28c76538bb6ea658e39e54d4c6a7bed79 1dedc84d61b571839f62f442ab96d5b84145b4d726d30b18122fa7e9c2a35ca6d6356eb27245545e f1392f5cd5f812b662352e4d15b7ed2f6553e998b29bfefbcf00444ffc493f247ba4c2f777707566 832d6ff90b4a34b53baf6a371ec355fedae60f62195c951ed1c8ec27fd576547a0a3dec673ed39bf e78a34970aefbbf57c281a0c915fdea8bb338956de63852e83a7da5eb6db44e20e9230c0ba93fbfa 3cb36cd597f9e52620b24bbfc8c6ab6a711efd1980d89f5550c4cddb55e55bab73c5078ca9028fd7 e8b2b757deecb72a7ff05cb2054b4ed307479a7f0f1ff1caae88cd661a3778995e0fd8e9383b5f63 88c7d1d7abbea7389e35571b5d7c2c3b0b01907dca20166a541a2e32fde36e6ee8036f6ed04e7671 a0294076e9d69b94e862f883e46afe3ef90700f10eb44783a09697c2e7489a1f81a6348a6c3072d4 4705e95451eabbcb85e3c45b3177daf2682910a4b29de2b4f60c63f35ab6c250c8b14d97af421249 53357799e450db4981b1c67ea18d89e3dc9c37dc99b55a656664e1d39adaac2e4ca95acb86d0c2d9 49e6fd794e26dd3fe3a590fec6c6503a590073dc2fab73cdc65ea4fcde30e46e5557a26d4d412a42 4893533ee6349ed35a9aca4e3ffc85b167f524871a2439d4e377196e65848ca1212cbeeaaab1c898 d3fe3c7f1ecd66d875ba9e165c469994839f628d63be53488d6bb1df1b3d0f4b0ee2ae8eb6c3e3e1 cf70f4fb72786a919fb1bea85d664a6bd49c8ae7881a0a522108b8cc6eff5d93299560a8dc237107 e935168da9dadd5fad9afe952763499616df03a72dc669ce9acff28beb8cc4fbde942ad5c2c98d6b a5c6eeb68e8e9efb567518aae3c1b09bd1988132cfeb83016abcfb5fbb5fee6bd4aaf55fc09ff9c7 373fb06a963f30727d79ac6c9bf9d10e39d5bbfc6ec9b6d68b6f1f1aeba5ec2c43de75c97e64868b d1f79e99cfb2776c46a2f7f2d45e79cd69e116f427eb4a3019bb42902cb51ad75e1e3d6ac6677e28 0dcebb8132d10ffdefe97cea8f31efde9b15df51d7e2aa68177bb2ad0ed50254876a0fc43fc3d017 cce0505b7e07bb999c4dae8617af6885c15b7e63d57c2415ad7a737e66cdaad2e446fbca98af5d8c 61b8e74e83585dd9fdaf39bdf7c7c8e2d9cf9cb9a83723984fd762a8741773c95cc7e679bc5308f8 527bddded65baeacf45ab5af95ac876e6ec72edd088ff1b6d1c53ac77a6c4f9d3f432d7d327d69dc 2eb684463a5d62d04f0625a3d9a83ecf1be5e6645d84f6b06d52c38132ac4cfb99d33b593edab5a8 f3aa435505babd6ec2e6e7bb43a1f93cf4c4466810bb46379fdbd707982fd734ea7aa865ee17bd9a f7d563856caa97b22da9b772019c9ea5f5f012155de39929f2d330ff0fc09ff9c73700196fe70029 ba1cc8435f1ce46be51340caa518a01bb10b306c2c00acffbc006cd0ff02acae5400867fa7007d75 04801a9c01d09def01745d4b416c8a009dfdf58274743e5901b477d842bc3580d6eaceff8bc5f816 311960bfab403fbf764ce3f867008492a0ce56c883fc035340bedffc293181ece65f80fae73ec038 0986ae167685e061d0adb80d802d0b0b8035a630fc4eab2728faf304e8ad9f05a8b92b01144e6c00 dd2f1988b304502e6542618347a2ce85a7a4175da962b282f89dbc29ebf57acdeda0bf88ac3f0320 5b6d01db358652a302942a4fcf004dab50533f350258e4ee015e1e3d009ed772008bbf2d809d9b3f 4b143181930036bcc098b68986002b2cf200cb185580bed343088103e8fd7e00e8a37201e8691d40 40a777858d83f7e39ef5c3e6507c04b24b5c9f99b17af9372459f5bf4f00a299ff918ae46a07a897 83ff77192100932b138077320ac019ce07f8f48600bc5be8001c995200f364196007df01d806460e d86885425c6183b78809c08a02ff7335c5b70eb05ceb065df6ed3b26beb3382a178b61b8f1ee41a0 e4dbde33b3bdde3d121d397f06406c9b0488981700526a1e003239411bf24bd88f3b9823e700373a 1ac05f17e87585180e70b3df03f87ac3240f255480a3b93bc0fcde0760971d0130f5d501d8b63a07 182508b15d7c9b716181434f4fabbcdfdc1101e1b3f28e03a53c7c3d339a9ff8691e595db88ff5d4 75fe0c803c6f50aa8e41a993910a10e7037fba372f005cb45680686c4c40ac72d03f582d4a802870 c324d3cf25997ee3e76ad8aa07f0de2a0d45abc5d856fd6e4cb572f3b8f01e0ad1baba3fbef982fe 089febfbfb150a8f54101be2d737b7afc8b52fa3e0eec6f6e3dae552975f916c604e8e00f93e9750 607903107a01fbe8b0ed01ecae9701918a29401cae16203e930f207cb902888b3f05c4c8e7015124 8e31dd2bf9d17d554e45ebb053787b9551ebcd9f37b3777d78e1c2ad944b8a00bd24a1740b945d23 7c6ad634ed9b4e3eed16479de816d4a5a733b8dc6f97637772f933008a85506750833ac555f25082 7e02bcf5ae016259667edc41347d0685b9f84d32e80d50e813f3b89cf1f9a87a3ee96fe1757a8441 ac82b05db7b0d77e62d55fdd281c05877a9e0e067a5f7e8e4fbbab8f50bbc023ef76fa716f0e52d7 7d6910da93ccc1bd205e2ff1052c478fec3f038e6ef112a0a92694ea6ca0f5578e30b4d28b0d4078 0b0e14f6430714deaf4c7c3b951a11dff427efe681832d38ed2baf7ebd99bc6f2d50db8530482f8a b9e7645bacf8c763b1eb239f2ee99d3792e8d2dddbf9514dbbfebd1ebb1f079c5ae032efd281e55c d3ee8995d5db51d046973fe367841e64e0088d773610860130e90d8d32d56d824265c3439574b2b0 9bc844fc37570bdf5d63f8528c11158cf18cf4ccb3e6c95f6c14df5b75c9afcb1043fcf128b7eb77 1fef8def8df6727ddd6b626202ce70be77ecc9ea189ecfde3c3a3d728fe7517832aeb9cb36ef868c 84f69f0150320f750e1622fce41f01f64662400cc81628ac551e14c77ad26891b707dff07d3b155e c3c6aaf33c9299b94fde4dc12b1606dac325dfd73b8ca35ed7e85d4b39eaf28bdb46276eda39a130 bba00c74ce08de385b25f1f13c554f8fd8dc15fb4fa30762575797b7bb9ebe4ac9cffc1c7f054079 2c910ae71bd423ced0465bb0f328db3628dcae02286a8efd162fe7e8a5223bf4792ad76adeb5680d dd6aa145df45fc285d07a357d268b6b964ae17abdc09cf9700495bd753aa787a6848f7547d0f56c7 c6c7d2ccddb8e019325b8bf451c37e6a86a1785a3e4fded50559b7ff8c1f9d3f2640aee09c8954a1 d4edec0b88d8ecfcac438dbc236b85bddbd20ff2e97eca2fd4be05b78652ad5b441e678eaec279e2 3ceccb674a7e9eac4a76eb9e6a97717c6c4a6dc4ec2cdb354336d8a9ae5a204910eb1962ec68d39d 12aa0b65fc3cd878dd3dac1cfc7e28d029e7cf00a852804dca3350e76c6203fc29a641a16c439da3 8ff07ea16de39506d5a47bfa7639f7765dea96bb7776a0ec4cfbe8e0b2f4162bebfe788a27a148ea e6bb016c238ecf81fe75d4b49e91e5a296df5efaeae2d116548cbb9f0f54aa1228b749ca57d6d9e0 a1948ff7bbecce8fce0f9268edef937f00a056194a55363b8099c21546fe0728957942a91a2a8432 111d9e56ea6179ebfec57db47aa3f89a7196d8c52e5c3ad6c3c8cf4f0d8de7cdbd9a4a425c6398df 5b7a96eeb9aaf54c4707db8c9143e1536b29ebe68e92dd63fb28f3f3a32fd7d183bb7f5ec407c4fe bedf8e96f69f0150b74941b1bbddcf241d1e6e80e89f33a060c49db8125e98973add89be3de134b7 4e4daddbe0eafa362913b9f3babb699c9a9c3d3165b6ce18694697b4b9583554fcf3700e45530e94 8a2065e53a63d6f6db737e26bd8953f2a200e9dd5cfad2bb307725e936bc4b92d9bb420c9d3f034a 2d2f7fa402bc70be03420eb2a0f0fab6a346975b0653b2c67aeb455ebcefbbb7249fe6ccebe3eba5 a8faa953902e97cd58db0e8dcc30586a8bc164a312c78faa3c0af2451684bebf6fe70b5fa9976a15 7787b634da0de2a9b2830ea1bb1b78b93b047adb0dcee8753738e4ecff2249a9feef373f00685c83 525d55fa6b2f17f100595024becd306a60932746598b4730e2a9eb84a90b36d1c6cf96773b80e3be f42d1ada7ddcd52ce6b8500b748953b88d2affe4a141ff244137f9be3b7cdd58d4ef01be3ddac5de 764ec5bbed9c56afdba37ebd89d9c0bf8a59e7618b9354f47f0060f926d4199fa013aaa5dc9fd933 9f855257f9c6eb235a5def761c0f6e20767af6a5765f9f39d4314f22a0dfe6b06f63faec5369ab94 25ce14f7945ecbcd1bb793e4575edf8dfaa62de6dedbd7e6b262731bc2f45ac275ae6d84eb629134 da86e036d7cd39529dcdf9215fb668eef47fe02fa958fe02a53e71efa7764fcc12cf4a305dd42b6e fd70283aa752b57e29bde74bab5960134b333fd7926fe4b61aa23aaf77ed504d2d47724bbdac24e5 dc117699c7edb0b5c6d05359f9034f28af7b69beb6146adcb330e3f8dab876e6396aea08a527e308 f497b3852bc69dff819f94eaff7c03b072074a9d460a206af5a42a4a2bfb0ee219eadb23217d3bc8 bdfb85697284d55c46d3a382b465632a3f6eda8a25d307aeec946431aa76a583b99befb2af32bb25 bf6632746e8a2c798483027ee7b687ca879576cb0adbed7619b65bf91ed990ed5fb9cd7d6473fe7b 74e1b9d6d4fa3300d619ad00762ba980e0c63e34d071b2da2eecd9e6fb96ca8c547be94d4d6b73ce e48efd78d5374cf12a68f676631ddc553992c3814e485f8c68ed721e37d9aef2182d3c5a9ac80b17 d260df0077d6871d12ad53315d64268de292d1975f739d5af593117aada81d87ed7c3a1736ec754e 7f06c0981505f0e6440785dcf6098a877e7c9377166fe3a2245afec994cdef69141b30286c6a4e6d 4a1dbc5e27993d95969479ee07b498dfcd5ae9ea962298bee00ef224dfdc5b022bdfc6f27ab46c5d 98e974f0a6170391a02ec680a4d11ba6d3a8b4b8d147b36333e35bc15aa73279f3df905ccddf2700 db71f45fdbe30aa2f478341afccab6da386d794f9c33bfbdd446276f9aabb2a36ef1d0e8da53396a e6f7fb74ddbeede60bf2bba5bb514110ca8b26f7a6b1097b405d669dedae936570f4394d9d283bde 06ab7b2ac057eb9db55cdd4c465badbba71b55a8ac2ed4a5db3fd12892d7ff0c805d650610bda31e 8637fae018636776ae8c7e2a91ff64fd74cc853f2ed0475d8e943bbaffa6a5a904e74b69874d6ef6 96d1f85880c328c645f1a5ce8edaabe13af76c52f4aa971129a69939aeb86ef3b5f40f6661d90817 cba5df22d465a357729602f24abccea57be0cc15739eea7f06c0892b0b0ad9b7766fc5a9e9f95668 8f8f3d029de90bee431eeaec83d9abb67793a69153de91f5f15cb49da7bcadf46dfb270f3d9f059c 7223326c16bf1418eb21b7696acb4ca94a6fcaaeeaa3d561b9358d3bf9460b59b2bbba0ec96e51d8 2fe2a3775928d5fd856c7fda47526ca8caaf4852aac911e07e49f0ec1935b351b53a38be717fa49f faa7e9a1f6194df729af408af6c5796dd76fbab2f138642e04f64ce4f7fbd9894b172aee1aad3bd0 db8ee41c75ffeeca2be1ce7597a2cfccc87dc825cfd61607a06a8bf42ae7cf8d8251984f8eec6a9e 4381369fd4b2fa7cecdff5b9b65a2a7f0620ee07e99a3ab67ba72779e91b93c56e74e0fcf2689f1a 28c9729e2d83911321f0c488ef99e9128c4c9723d610eeebf5627a579842fd78a2b8137b5f6dece5 7bb97b333952fe2c0a0badb06a2d32ebcd649e3f18ec0c337c15b6f3f0392df8b9d2e4363aae26b7 62459e30d23b7139a6cbf0bef9331ed0abed9f4bb1d13153dcaaff934fdb0fea54775bca1c9bdc87 6654368f7d5eccea51cbd36b6cd7a45cea3a5e3dd9c76a196eaec9a30832964ef2e27b30ccc5386d 38f3595ef66724be89a636ad6427374e2b8dddaddd1dd7228c1a6dfb9c366aa5daf130d4a3fa509a cee7c3d060c93f23599cb4dc192d5391fb1df5dacbf4f64a7ddcde16a56595fd70971243653307aa 768d9da5547f3e175fd98a17a32f40e6b35caa34b316486386d9c5fe942a56a6931b5b5f4dca7e9d 1bf3cdd66eb4ed560ec3f0d04a125d83d8e83e06833cffed8ff173b19fb976273d937177bd598573 7b79bf80fe19c7cea9dcd496ebb82377f8716b4b49c3320bbe0b8c62764a6ed979b24944b0187d68 714622edc3b470ad1ec72e8fdaa36d2bba0fa59eef0f62358a0683cc37d5ff1eb17c7f8c6278cf5c 61a55efe56a877c94aaddbb185cea453782de8f6ba234b2d577959ad7aba1d369f868135b733f6af 942a3cfe0a1d319f2df9e58e9b22160e4b2cd06a798a298f538bc3844467643ed39c94efb7e1a819 4af381329cadfae37c6bddcb3bb8d0c51e29b153f0dff2cf9adb66a0b65cc9339acfc3f1d8080dd1 6a74f3a253873df851d368f659cb3cc4b83aab19d98ab57d142a58946e96a97e635c2e7c75fecf50 7c866afebc115365cb9cf4ade528dacac7f36fb31e406307e830dcd1b9fe57cfe57af98b5fe85015 b5d272778b7a733ba8351bd204ebd495f9b75ffb5ee2512d73b3c7d5bc274d2ad88b9a95a90e4996 d683d6b2c84f8a49258bc27681704408a73f3cbe230af6f5b306366e166dd49406cf3f03e480d303 39e13d03b94c9601d9b02b829c96b1409efb0080ccd27580dcce7f0585d9810a61b90089f00c401e 641520276b081025cf402cf610270b2042da83e0b2107e19209b6a074298036475a4ff5f6c1c265e 660ef4fb2e5457ef6a3a97ecbff939fe1b401ef1fa207700506a016140ae49ee40ee5b3e837cdc4c 4195ed16409b8001e898d2219e3e40dbad3c404bdb3a4073cf09406268d948ccfd3c2944dceb0520 5e2980d8c1533f5d83180e0072392c21dec20fe265e1c8c678a8b351114cd6a1f7429957105d56bf 02eabcc326bd6493bdb85065710d729bad04f28b8503902993852ad92e40b74516a0b67902a89e09 002ace5080d226bc86c97706d04e5f8050e185945f30dca9b4df004576f06a50788d283a1f01e473 66e2b30a125f202632e74d44aff74254aab042e8c351fa253eaeeb60ffdd30bf02e42b016c521f99 83dca80275fa1ad4f950ef00b91af0b7d4631f60182900acf6fcc9aa6378f30db0d4160768f084d7 702d930035600c8eee4e0640d7e93bc404409c3080ceab9df8822e67313112d7116deae29bf558f1 5d3dd062e8377ae2ab7d7a084fb535657d635e58ff2009d8ff3ef92f40befb86523fc505c8adeaf0 741e2b0099441e4047691c0acc8e01269b22c0140cc6320af301187f2b016c981f00ac31a20146c8 7b80a55f678086a59f5ddf68c8a5e2cbfa548809efd389984269f6e62a6d36dc347abbd7cb68ef5f bb69691f44b22f3d47c5c5d63b1d62debd5c4df657803c9b1d803c5a813a2fe39f3ab7f9a8a303e4 d58037d9ef1501761acc005efac2e0a5b3ba033caf7e93647f15605e7f02b003c7c6abd7468e6e65 d98a18ebe246e565faf376d34dfccd195c33dc28a7f96ba7c39b2b9ffcddf35b7b2515fc9f234a56 fc29db975d3bedeeee6ebdcbdf366f6ffd2b407e8f0f41bed72541beca7300a13747802ee91060ec be02f0f67691a4cf0f00bf1a2efcf44d165cc694fbad4677021b47950ec6bc79bebc0f9f7ac10c9b 51f9f60ae545f4926a0af2ead86e35e83fcad367665b66fc5976b1f3acde54f3b00da63e98782b5f c3bdb7b1bfb71e6b67dc0ffd5f2489aefffd06e48f4528753e86a3c0dee4019a7ac0db08ce31c0de f71a8c2edc152086b29e64d09fd123ffccbc05512b85e262db7bbdb7dbe5ab7bdc6e03e523e941aa a938cff1da4c2a273db3c83bebcf4648c9b34c6ae4524b9e7aac1949bcf324afdeb674fde00c9ab4 7831cf69fe8cb7378c45c923ea5780fcbd0275b2333882649fb0072f7ff202b041b35f80d3480310 8d0c0588f3558fb8e9e511be7406bce45a0b0bd46ca5f5d47d64e61f8d37eb236e207b4bfc6db94e fbedba453e0b1e6ba78adff907dbbbbe5bf4c2513e34676b21ad5c4c504a82c233e95cc5d3bd3fe2 8f4226648ef5dbf37780fcbb3582ad4942a934962426db57d89a8d14c08356134aec30919b960f61 3860ece0bb28bf9e46f84e4cc05f2ccf358f709703b7a4f7568fea02dddc82f071b8ee97b2e51ca6 3bd74929a7af3d0ee2e2659e5f4cce4b7cb3b61cfa229fee1cb33f0aadfcd66c23266fec852dab83 d798fe0792ece0df27b02dbb50ea7d4b0124184900ebd2b0df8c97999f746f7c3d37a977d36d8ac1 37c2cce76cf47c78f641f9ba65ab4ddc37f76beb2a4b9d91334ac52b7bf2b86d2f734dd3cee7b7e5 fc24ef7deb65950a702a63e7b3ee51d898941984f1ce6c77ddbdd15b4aa23e64babc3651b26b2dd7 7f33bf0220c5f118b6ab440174b0910166eab0df6842e66769afc2b7a29ae8ce5f8ae672cf996f2b 1e8df0e7071fe3cf9b749473ceb8fc2d5dacd2ba73be7c0733eb7a44d6707c7c4b479f09a097a2bc 5d537ce15f23ea0835fd3308e6fad0686df5f4742069391e495eb9a7ce9f1e7fb88c8fec6195dd33 bf0220add918b6ab46035439c12e9eb97b00ffea5940c452330c61201c8c8fd4ca73ccb1f0f0d375 ed0adeeccd3e761ec96abb0b3e2131eb66e3cd135f398f8ecddd7c6976c8066fc862f5a0ab5cd5d6 d3f6e4ad4db32aae9e168db18ada07286ceb4907e27dd82ad7bd20286574ce2658ff1b92803d3902 644c42a9450db6aa7b85529b810788be938dcb837115b626def1cf9bd4c4adf2deea26d7f63b27b7 42cf1702e75e165bc373c7a07ba89892d1eb1903ec35d547efcb5a9b5e64493d8572f2d6c5c3c533 fc4381f8e694ebcd1828e52ab1961f9fa52473ea702bd7460d61ffcc10ec7e63a6995ff11f9dd40d 4e35e58f06f06dd6ff494c46351d29fcbcadd4d4ab1ebd133af75d7b3876a6bd347b59950dc362a3 9a777c659cb4d923864523c5452dcd7cefc6aa755950077b5dde28b7655997dd4e015ea0b24eed9f 8d427bdf34547adf3409490a67e9ad24e57fb62b244736c1fa574095709646b427943a43f5bf6af7 10712a1b86c723f244bc21f1f0c974f93a5a9d3a36363c51e7f280514f4df47b377b1e95acb9d5f5 47846af3d6a6a6e29766ff5064be0ba532b8b172bd64cafb1666db9234c9c4bbf8a4d4770ad959ee 14de97e0a7eb7637c00d7e3748c96c02e657fcf8695b1e4a7dc650aad930a08d56fc9f52ed2f38b8 a63d2778a46fd17b94b5adcaa8715e97178bd3368749a61c6a1723fdaa84dafc7bcaa9c47c5456ee d7b82d0b036db2174b709097be3328e74d269e8da8f9db9738a68a45315b7c2ec46c7bb34bb015b3 258617b3b9c55a1c4fb8dff1974e14cf409de1d084063a78c6b7722a15e80408ddeaa99ab81c8e99 36e24b21882b56bd5e981ebbd5e7c6d0babca559f954a0168a5c5a796cca457993775bfb364d8d76 e05b59ee52f7ec46cc5e10638b98756f438203ba213ffc64b3c43ae2e6dc1f6d37e75a87df228b7e e2726c917e93f915f0c66fa154b2b60678610ea5cae433e26bd2fb89c4cbdb7db7e91f6ccc1dbd2c 5ee689e3bbe50ecdd4c8e4f5d97260a854cd7115b7374c5610cb9b5b0697a280afedd445bd2f4ec2 f77c3b3f3bdc6669dc358156c083bfbf26599e952a43fe1e7e3742a9886d047a5ce00587c2d68233 ced0bf0220b634f9f1d3ac110bf09590544620bd77ab85dfef6d66cb3a668ae4cfebedf3717c5315 c41ca2eb8e7e5caf19d599228743853adce4eda61a49b165e7775a695616a71ad2d96215672a5c0d 81e61f8ab0e77cf1e74921d788ab69f6c5833ed7f02c8edb20a1c0f99527cb57bb2ec3b3eb27fd2b a0816ad3bfb6c7e197c31114daeb87bbf634da315a1e79a1aa017d12d15992e8328795735a3f86c5 8606fb3a7960e5f75e0e8eec65df0d09e8d6a594b488d4eac4c6368386b06eec877ccd5daed8901e 8a6ce7249ed77d2d00ebbeee75d7fddd9a59cb5793673b84b9665f82cdb0afa599bc2be2e7f86f00 c8d7845219430044f56884125295af074a1c5d56abe1c4aa77db3373d871747d7e8561fd95d4cb07 ae1d4ce5d756dfec7b97a1b9d3e3c815d1d2cf3bbd36f41847848ae754b880117bec3e359caf0772 9b67c6db89419b6fef4d9b0fa7459b1645339958e318ada3ad996f674badfb537ef92b005ab3a03b fcbafe2c1921f68e7687f77960cf55b4f7b334cd3cb44f231d15ab3bb58c9d7de5996b60f2ee2bf4 f6fd3dcdec8cb0a18a18fe7036cc8209797f8f64b85dc32bb23d4b6cadd393f9843e7e7b0c75c9b2 1a45f47ede1b4511b5a0411138bfa28894c652e7bac4d033925fd126bf267f054017b70934d0b4f0 8a9c11e5e8d574fbaf2415c8313d1d6dc1e191db8c96b254251277703f28c869693c47eb3ba4595d 6c293e123795d2e9c86fc7a307d7f960f15aeb3808330dc40abd7808036af53cd02be6ee1c966e6d 102c5dbcdb583e0277b9e46877bd7cd016bda24569b972c4f5e207c9c6ffbf4ffe0b8046f1181068 95bf3f17cbce85e8ec9a4729d769e9c731d639700dabb357067c7f77cc4f55f13c4c875b67c71536 77d71f0a8df669cdbd9f1b853db0a3cb7a522824afa7621017a428877688d59dbbb7971ee5cdc980 2bee49b166fba488db75524cf717648be9d064ab53a0c84d745b907e64ce7f05c0aea359a056cda9 931daa490598d3866e368cf136d3524b4badb58febada6687d4067c31e794e683c3b377e2766102e cead5aecb726936be3bdd9320b7565d22b73f1a0cacb4ebcaa315d74d9e407757217f0d345dc7e8a 8bbeb27accbf8d7e52d268d11fbe26f3cfeb43cdd5e9899a8378b498033e3ff915003f47ccbda995 9ad62355ae1a5ff559579db3d6943bf5625db4d47d551086ed3a2787a4c88e512c79b6b6365525b7 468a993a83f7ca23bab86ad22baf52d8adea87fe69d9323a0fb26b75c0e280cfd0454ad4daf37114 2f674792d3677362919ecd6e686736937a9319d2adcea624ff9a4d2dfe3afd2f9294eaff7ee32d03 b17521e34ec58c51b7aa9140aacb2fbe58fbc9540942ea5560d3db4d8921af75992ebaca9d629778 6a2518b3e2f25556dacbb6729b923dfd069bb5144b8bb482255b17e7393d7f9d2d4acdf7f4c291c8 e4aa298d0933292cc78fc7c91873bc9a1d738d7677ccbe88cedfe8fe0a475b729553e31e56f49964 d61521ae27156076262a96055e37b0f564f6cc5277b9345a35ecb742468fd675a16a83d7dc788bd9 797eee14670bffdd98615a7d38a5b25d727263492e29d4ba1ff3cdad317a4ae66dd404613cec6670 6c109bca60a0cc3b890f3d1860d557ff6b9f08f8e953e97fb56be95758ecf65b363237a57aa876ea 552983d24581a737c87a8268a91557cf00b2bf4fcfe6e668b29b61e79e3eb931ed642218bb7cfb31 7a8aedd73094abf1b0f3ed6406b1b6c006832c53ee6b73aed11fa346bf67aeaeb35efe16ad93fa02 72c7deb0d70ed54aa73a85f7a1d5bec924dfbe29051b8209fe8124d7f9f789a1c1485c2dbe065549 6585e2a61cc8c87a7c5e815595525ef34c27f0a614766b8e5df6391d35439b1ac40793ef6bd3add8 33495ee95a34a577b10775ea149ee34b7bdd1adf5a7caf951874f3a9765fcd5666001a5d6498ad2b 4bba50fb5ef5566d5c06b35ac6a3c45ac6cfdb5573237fabe6b653fb15aa339c55f772eb58dadc06 06b2d6000396de7b96385033c33adcc72ced85c34edccef4b54909efe56db4d6a12a7eab5d7e9d7a ad5aac8e9aadd466da90261bb2ae2c16ab9a4675e8aac996d98ab5c9f0152ccc88e542fc554a9554 6c14f969de2e6cc9824f487433f16cf0f8b1c671857bb67185af4d7e852c7e83f276655d107630db c086cc8ebd99c1b3d751edc61dfba3e33cec6257df6f9783febdb9ed97837a7ccc809ab6fc29ca50 359973b662f107a46c8b2c56bac913a2e86ad542e1794c1789f0ec97f1f87aa9615f576da2a6c874 104b5e0cf2b6b69865dd4b6199ad179a5ca6558116260923eddf007fe6e708b2935705641e510b64 6ad10064acf902643b4000d99b7902b9d62204b9a05f04f9fa6708f24b9187003ac8afc60f08eb0b f2144e40904d080bc6b9749e8658ec20fcf34fa26bd77b42c829906722f8a76bb4fc83781147c518 531e3f2845f6e0f43b40968ea0ce38dd0699417a08b279810459b32e825c33fe599f96d3e408aa64 e03f3f3426207f3e6e40deaf1c41feb2f6208234c8db2d7821f6b603f19c81bc536620b83dc85fd3 0ec4e40571ca80fc8d28c7d6ba5a8f31f75389a8c1a39c20293a171502b5f25eb757e5f77ac255fe 0d20bb4b57a1c01c94aa9446203bd59750e57a0f7276f30af2b508369a75a9010459ce01d24b4910 b33340aa6600901a92fd896f6a6405c2ea01a49e2721a602c449054883b842883104f408c8355a8d a843aef9bea71ef5f75a3fd5de95895c7f57da9b7a585ff46bbf22f999ac918352cb4407366d1b4a bd7a2b90bbdc5490ef73f0fe5ab53440bab92640ccf31220f7960ca1380039c611404e031420b254 87084700519ad08b543622c4cb04c8a1fd88976921e93791d339e1effbe0520d3dfada0eeb91da7c 051cd37a8958bf99a01574c3b8f104925bff3780ecab54830dd9e8822cc34e406e844323eb6306c8 47fe1320d3cd4fc10c249c7400da4ad1001dafd524d9ff0068bbf685d816015abab5e3b39b9fc54b bec9c4043e1523c7918d88a6fddbfbee563fa1178cb057102daac1fbbbea04bd71b3fdfc640bdde7 418a3bcf61d74d36fb79c7ebacf92b402e5d8552993694fa3a40a91f8a832a172780ecaa2140890f 01d08dde07e873c002f47b33011a14fc78d5eea6a32b372e44259b4cb62bbc1f943279578f1a15fa f3e736dc60981636ece9f5b5cbaa2090511b790e6f76d9377a999e9f13ec9e776a687d6f7e5df7dd a55be9dcb9a2defc152047b4eb7f99c0a40772cb00f687fd6d0b10f16203b4c1c600bdf44a00eb23 63807170ae5d3d6f4654dedb8f37e73f3fe1e6e661af10096aafce2a3508e24c910cfab316fffc00 52798e865beb9949c72f3f0fed37a9cb717975cb6ea13ceb3d18b23ebc7354d0bff9c77df7da6996 5af688dc557f05c80d270d689bcbe4a1447606901226c1864c272551ce1f80f5d832c01e83c4b389 6e4f9e7ff37bea10b646a4f3ea76c9773068ccf3cf717958f64d72d2f1f3fa7cea596d81f1305997 5cbb161cdd8258f4ee6e7f92bd6db393e2b5a3b65b4edf247ab6967f75ed0c6974ce56a9d3b0569f f0a7fa6872fc37c0869c3760d38a03909f771700d1a70a400fed644d773a05b0fbb112dd6b957118 ec2bf4ab97c9ee82d43965f9c789e77be7daf9eb3a4503778bbd1fe7f65169ee06f77a855bdcb6cb 1d7f95a63bc551c8d3c5d6981cb8987c133b5be2a871c6a251cfa2fa85ae55f8663a47d7941ae656 68557e05c8094ba873a9ffd4e5c8bfb72b80d665156015d6852a1be9d8c9de2bef46c6e9059fc679 fe9c583aef5d4a82e6d2dae47af7fc747813a776c601224938296e50b7c7f678703966c9d505e9ad 376772a31996fdce3f4fb7cf383181a397e16bc73a744ecded79d6315bcb6edbe8eeb20d7d7072aa bf02e40e2cb456cf1b02646c2789c9870e3049f700de9a67a2ca87265ebdc3b0f1ccb9bde495145e 2147900f8eca49b74e9a339d6180ddeddcfc165dd0a69e3f13085bb2e821db3edd197a7caab887f5 51c01dc50ca629db6c5942dae8065e553f74b23d7d10bdbb9ade3fb6b4ac2d36d57971524b50fd49 a9c2e33f007267014e831500a56af11a6048ce0478c6f363673c4f85e22d977b1ab45ff40a63a371 7f8e76a36bff5a63eca976932f18ca5facabd47a9e1e4b3475f4dbaf64bb82f9aa5d6b667b7ce91b fbfd63a903ffb3d587adf649d3d963ac65bfd5923a1fb37d15cdcebb87f37dd23910d55253a185a8 fa2b606bee5b20cf224380e265eeaf2a2325287504fc8865f138f828fb8f77f976b28f9a5f285c07 836fc7ce7f95c599aa8eb756d92c9e8e4fdc79983b998d0c99ea67f5cfb15fd2d39b725b9bdc9a33 f5848c38151d6eb4c3d24443e58a6c92a2730a7d497795d226d7931f8da82db3af4753aeee4eb5bd df91abff06900387d65fcf7950b2cfc31bdf307fea9fbf1bc2f1f99cd2a5c02ddf4eefab6291597b 76a2927b73a6ee83c9c9cb64f963abae6b462c0f6dfd4b955e7aa61ba7b43c61e32a96d7ea87555d 1b2a4c49a3e4c7e2b997397cf1d8fbf417d96f3abbcebe11cbdd7d336f74a497c235a5dd6051fb2f 9294eaff7e03f2c8b10dfb5265006d7421fc95f0c3bfb8fb8a76c39b8fb1aa736f0d0b77fbd8fa7e cfceb6583a796a6a786cb947c638e426b23ea6d29636eb3e7c9524e4cf61159349317a8571ba1599 3bd57b7bff3b20f7cd16bf9376ac6b4b9d9a94dbc5a95a6b27ebfdee4e76a69de45373d79fd4ea09 6aff86bf7422ad016cd278b30178934ea2b5d879a66e812afb47773dc23447afc18106ff6cdf56b5 7bc68fa271ec1a9f14bbd227182a6a88743d1e1c827b28b7b81fc9ae8367f7cffdbb28852ba72575 cc60b6933f88207e47ebb3383a16d310b7a4dab5387a947b093a6266fe6d26a8ff0ad897ce50ea76 0de7a0b20ca5ae58337a348fe7e7549beeef41be45dbc83ba559ac59f48fafe1326ff65c36993d75 23d7986b68d3170ece79a72b6cb9e9c8422af3da6feff794245d747ca748425dd404612466ae676e 3b431073632d4db0b1566475636ddc6e82363cb51bdbbc7aabfd03494af5ef1390efde3b00b1b750 2a73d902fc221aef4db771b83f17fbe135356f0fcff441dc9f1a96f730fbf95cda48078d9ab6a8a5 272a615e79857d2d54b931c89cf7ede2cddf01750d762969929880985d362a5b64dae86f48654e0b 7670548562671609c56eb10cc17704ca342124a529d8a654fb15203ff3a093846950a771854dfaa1 151f05f524a57a1d54a2b68dbc1edd933f2d6d4c59641d2353733e9a954b97d5c2e831541ede8e91 9bf9da5e8ab6cfd3ee33d75c510fc7efedf15cca6e96934f5170b84f87bfd3b9055f79d00a27d48b 2127e0f702e72d678903c57902d5e62b07b2c1af3daaf62b60941040a97d1855e0e907fff6f317fe dea4720d7b6670f57369306a1c3bb5cbdac8f4d193b678cfdf2a55bd1614772d27a5f5e56d7aba94 e2da77bb538f9a2e4e9b6367bba8d45f1b420fbe42497ee07c757b6d720dd69fb1a25dddada3a2e7 ada3dc1e67dbd37a3b418b15994a9d6ba40735aefe6a55ff81e46a92230c19401ba0a7780af0e18b f32e75bded68c355f94cd7b1f24fcec2181d3ba446160ced70bba47dc5cbac10797b5eb5a4382ecc 76da36e4c5192a1db698d43d0b371cb8bc9bf300b7d1d4c404d8dd5eaeade58d3166d4f58b6746d5 fd9519614b845149bccda8836c83f9f0487dbd7791caaf80dd3edb847da93083069a66ef9bd5ab7a b15cac78f2b36e52e1d2f866a8a24636acd1617deeec95ba61dcf7ef433e2d01038e1fe36f381491 bdc56c9c02b913d6e78ac9d7dbde956bd5cee13abe6f32ccf7c295988cb9ebd3f9b3bfa6ac117ba1 acd63c890b53cd04c928404fb7a0c6a49fa0f22be0145a6c006cd7495e3e023a8e46f48bd69dba15 4c794415f4fca1523854f86c5d6ee74c5efae0cd8b946eefa25d6ee112db4bf1d6fdc9435fa48550 ed0e782e78210776cfddadf5b0b8f798092a7ce87986c3a925a2765674daa75645533cada8f73eb7 2a72a9c6aa387fd756141b5429ec782ffd8a9f471158ab0e70825b3caa9757e5422e42e2285e485c 37408538dc2207dfefd91eb6335af8525c54256dbba2f2fee6faa67202a792557e238d875c6758a7 d69f382532c6f6acd127f6e050979518aeae6b3571a0960fe1da5856c3ea92dc541c93f4e74e9af4 6bf51a29d8d90a29a8a0b2acf841f15700f4a1755edd4bbdeba8af326155ee126ea8529750a98987 cbe298c67ede53b8bbe636b774bb27f0796fc36f87e30b175eed08c604298c4da5b3cd75369f9a30 0806d6947d3c4babdbe5785cbaa6ee92cff3f143b60ae9f2425a2fa78b6eb5ae2da4452bb5e83cf9 f23c1e774b097e52aa9d165e58748a59e2df0007a7e6e4c15eaea5b36d9b9829675a9846a600aed4 6d0193d2ab1cb261f46986ebb630944dcda3c57afc150fccbc05ee0c9a23bff492d393c7ab14bd3d b657ecfe385b7a1f87255f19475e44c8fdb2e86d90707e0827d83cddbd0f67066aa933dd5e84335d 0b89d9a4a8e1b34956226659768cff8a40b93a89413b99768c1d83cb00d5a707043f54b8232e1d7c 02d95c9d519aeb6654c020461f3a21e74c7bc52daffcf2999958cbe6fa1890618fc892124a9717f1 6adf5bf4c3f362ae753d7e9eb920da6c5668dda69624255514a764af589fe268b89ed8dbcb63624f 3ad9c92a2a2393829c472705f68bfd8abbdf548bd6fabe468d5105c7d4623b20f6bd791bddae064c 86939a06a0cfa53079e2b114ee7c487673f160a1281c3fd702df9867a7c46d86f0ed707a1eecb2d3 25fe2a4df157b5337176f3d9a494dfb36396bf2be36aa3721d09c63e356aacbbade1ab5adb0d836f fa390cb41248f0198acd4f9217f839fe1b2e2466a266cf2ca2aaad03426e17979888ba72960b5727 405b91fd5c36aaf8633e2e5060361fdba329d1cfb393d2b6af8cb92c678d36d5dd7df8522f49258b 6127f3f80ea23b860ee4fdb43ae8cfd55e5fbd5c67fdd1b62af4ccac78ec4d1fc5772f2f7bd5ae45 869baed5d85cbba797f1ec9e8c8df72b8ee2a68b6ad610499c5b39d0445c3c4d2f39bea95ea0ca87 e993c1c1bbcea6f8d09e94bc333a7a16c683a184f0ab8132b4b67d6d6a293d93348dae456be72ef6 306f1daa66f9eddbd67effd4686df13d80369f1a9ed4661d24d35a439a2bab86b4e8a88d2ef60a1a 5dfc5a6d74099da9c7b7bafe2b7ecc52f11667623779bdf2fc137d408bcc593e293674fbffa3eb3e d71454b606405f0bd19cb362ce224acea082014449a5f73fd07b9f73667afafbf3a2fd74b7cba228 aa1645313db53fa7e1abd7d5080282d3a5c006c8a956e99d37e5767753cb121db2854ddb87ee67d9 62087fdb0c349f6ac4a71b5dffda3a571f974e726d56d38dea99b6ec8acd3b5ea5f8c1bee52ad22a 945c43ef970ef3ce3e213c951ab90796f04e4f6b3fdb5fc8a4f328f043259b3952c2e7b39b75d20b 4605c19acecd933e6c1dbe721f7502bdbba9286edb6517af1633587f9b3d748cd5bf56355b47eef9 620d7f42956a2e19cb251f53294666a35cf90aed5203ddf78aedec9c28f48a93699ea80e57b97173 426567ddad98c943fb13be993c5e7809af2118b9141a0972f32f7e0e1e7ab76d4124a2b9691e7ad5 902ff6742e281a11e4ef62f76c8474bbbe97cc660fe9e9757579916ab8b3552a36931c6a64bf6397 0ea3daa3c8cc30b7c02fbd679e288b5e6e5c5fbfb278d8f233b94f2ec4aab81121afebe10307ce11 4b3e0692986d0122da6613223aa549c2814cf8ca10d14594bf00f00bc100dc460a000af10a8037eb 16806d6f0410423800e458d50122ab2e40bc1e02d0ac5bfb490d0d1663802e3c12a087be0850493b 03f492f100fa5c7e12cc2c403ff97ac2864808b600fd763980e5f413c020d80558beecc5d992e344 97c7e95f567bf1ff459ab3f9fffe04c0df1c0ee071ae98c45baf26518a69ceb73603c8f9c90034b3 3901b41abf003a6770809e5a6d803afa0ca071f900309c550056015780f5c641820c036c0e157f12 c473a29590747cb145710f308a96123ed7f8d468be23ab03bfa282707b86b7befe83176e3fdcff0d 4072c524ce4d318933e9c503a47e4b3bb70089d93540670d01a0fcd902e86310020c0df2009bd03d 80edb015c0b82d0330fd6d00cc683c0076e7e384771262d8ae2430dd84600eb0684ac7a7d3498bb3 5fff1615d1e815566a4eda42078e777a07142dfa41bd45fd83ef058b3f01c8a09b4da26c9592a0e9 245431ea0274f7d8020c39c8001b579220362200d875500638e411006fae7ee6d9e0c3a708f051c3 4a38bc00be0e21806f1af904b2119f4bc6205e44c832ce89033a2aaa941e564cf116d42de5e533ad 53f40e782a7cf3d5d10fd1bbfb6cfd269d03f5df3700590c9250ed6e126a32e405e87432045832e4 05981527e5f55dbb006f2030c00f7a3240b8742600078f23c03f552d3e2bcb5b9c470eefc8be3fe0 68b387f2e1ddcbd44292eefd3cdfb3da3acc0337b0f741a35fd7fdf6a87b4b7a9a83d78b388ee217 d46c7dbcf1368e3db4ecfe009e86738eff22896e944df6f9ae02502c4a537e9729c03e12fb5368d4 e802f093f306783cc5fecda093f222cecbd76354ba856ab8afa276f03c565fc1b139fefa6f6e95f1 d9ae5cf5dbf1b3fb1686d0f4dd877be44b9eaa9a37c9abf673de71dfee8572bf6efe25a57d01e7ca 1cbe4ee991f4781efb1278ec6b68fc170039edf2c93e179350b94212aa955f025c880590298b36c8 ec9a11c8e8562e5ef50aada8dcec8c03af32277dbfb14def8af03bac707e0b91e9be3e836bf41a32 0ee269a15ff02662b9e56183f9e8395785ad6b6190ec6c5f79ebb10fabef7b93463fd7a82bc4d7be 348fed4fa319daf2bb10da43160e7e914eed4db70071a87c52a0b76a52a0c316c0e7cb0dc874ba32 c85ca33bc816f6205ee9b76c48e59dbaef9fbcc15b64a2f56bd4f7196f3a8ab4e7c9c4aecfec27fb 76d7a3f2c7b9f1f58c530683ea831aeed285669281b5b8bcf992cf2755dd37ec510373ad69a7f0b9 9cf64e7cc97a6274b6682a3caf5b8be094fcf9fb2f001271857f2e496117ad07f050dd2521ee1490 5d35d3362db6563208eb8d05ea77a951f1a59616ed64f8ba9eb936b9d83bf7d5547c1cf0ba7967a0 e2fdd61582d775a0bc3eb61284197b2454eb96ee11430b6ff1bb4b6ef6d5ce6b6be09c76f72930dd 4c0598f5659c1e37c62bef86067d3d077ad0e1fdbf0068592f02d4cf5601de0304c86c620a648993 06b260ee449569dff7bb8f08bc90c0c59ff9e6bde290ee6b70671c66710525e267bd4ed52a899661 a0a695b4b88fcbb29909cff62e8b9c8b6eb37422f3c38ee96ed9b9597733a2c110e45de73b7ca4c5 22093469b1011a911bc7ead76e87aaba29f97ff173bee99c8a001b369350cdea0864a1fa1164cf9e 16db99f93d687d6f8f175a1c3cdd6bb3951cf3443b77051e68d893ea6568656ec2e6923fef997349 9baba7aad8b7ccc3abef19ef521f18edf934abf3ca2ecdd9e8bd9133d1927332ad4159cd52c71b34 548c07fa556602f45132bd772c9fc1359497b216fc054027b712c0e46132462dcd2649690e8e3f2b 754795f7e7fc26f6ad74fae8337fb6ad47b3ce78d7a132fb5aa7eaaa7429ec3add9393cfce4d0f83 0fc6db73443dbceba6decfe80f4d1eeaa1067167541dbb6e45319b394299dd76a47ca9c227392f6f 03e93ad421690bb31fa9a46fe3b4d7f9d8ad22715f19067f91d451270915ac9250197ef66f6292c4 948039518a670e0dd1a9754af275f87cd856d67d04e7db2d9b3bd5735ed3f0cdd3448f683a3d1168 1f72cd6bf0b0a5a913ae7555e687e24bbe5cab905cc0da05e92a1eba52e9ebaec5fd646d8835ece3 0bcf7317168e87e65768360a2025e2df6f38fc0b805ea3d24fd3d93b26a1da6a126a4ca539d49ae2 f76c9c71af2e4adefac1e4682da613f3bcd3b69ef91a88a8c185744d1fd053421bcd5a6b157b0707 65517ec9f23a675fa45b5b75a4f2de88c5bd75cd085e094d6fc1129a7362c1b3d64be13b9bb5c745 ed004ef0bf9c10de404ac4f5053df80b80c17009e0bc504986fdf60ce4badc21babb3af3d266c4c2 a96dca7d7b4a38e95cf5f3bd01abe68b2a3b460f6d7eb4af552faac60cef29e7c66b2edb98b1976e 5f46109dc7d810ebd9f15da047e390e77a43948b8efb1ad7f71f13f693f44259a586bbecf0c3c309 dc971d65950f0b5b745a055858d9857f01b0663609f56e564176e14e418ee3c8e0ddfeae1ecf3b5d be0de06ee5629777cb533de404a3d73ad93a54fd44aa710b72ca7269b7e4cd449d48649d4cab8078 407bac407f4a1acf0590cdc53878b15f2882d851375b6674693e64a643886170d878d027a30dd1a7 fbe89bf24989939f75c3bf00d8b1560299e2fbe79a74567c4d40eec66d9fcb08aedc4418c9d9d385 9d3f575ec8d8083e5d468766d445359bfa5b59c6062e6fde6a43aa2e6643b131ee6d84762d43f3bd 42a87083d83cb3a3b7e0324659fbd0e7dc294fe7b6583a8c3ada184b1dd767e29af0fd1ed7cfe20f 20253eae2f78f817497bdf49421d813ac846df4978d818a3c77156cfd8fa82c12eeb470f33199eef ebc42d489b4ed55cd50d65d55d3ce5ed9247a51ab6a90a6fa6dae3c339b4e0c0f049715045165834 bf37990c36bfd3b9ef243a6eda54f670df5b9d43e5d5dc522e8b5a09063854dec137e5e75574a85c ddf4ecf9b3fd4572d837f320a3e61b491dcd4d9296b05cbef5b72a62e54a55e8e4ca00d6c1aaddd6 5097df2aabed4b91cbcbca43aa6d075fc1cf558bbc60421d4e969f5356dbc9691560cce59ea39751 4d3d5eebb5ebe1512afbd4335b41a9c67ad7dc3386bfda33e62369854f873841fda4c4fb376647fb 5764047f911cf3fd1cc87cdb4d90dbe5d2e7143a7b649014e860099f7720f81a219e9c4726e56b55 5969eda5e40c0e82e831f14de0fadf8817c7688e1b3eaf0d767293874c76395fd1854eed702cd731 e9502b4667aa99f19efb0e1c40a4306bd5764056d2bbf07640618d1d507b4102f9217baf439c1292 428e09fe02e0e4309b1c4bcb56f0da57db77f610633fa569be6eea4727deed58b97cfb59c9d93f27 62ab544d278e09dd397fe107f4dde790aa89b27841a8d0d679de3bdeb4ceece088853d45b73c6ecf 552d83140bfa7da7e0a7780713dfe276a2ef26db89b1d2b61321f34e20e2ad162e131ee36007a333 ff87f4dbfcf7cd0f000f56999f84df339fdd65aef062069d2bf2fd63f4cee4479def412439a4f915 82e2bccf0f2e973d3746aa3a3bab2e1d66d924bff4a6b22a1c2bc9b9f6503f6547544b89d2fb3dc9 b8a71f77df2627ef90e46b6cf186186c16db576e6db953626d3d27cadab2206f6d298d6893fd1009 5e3bf9956ccfff8be458d2f220d78989c773964b4a13cda59d5b93e1a48f36993480bc2b6aa1c0dd 0a6f4e5d3a0d3653e9ac983c2108f4e606d947322e05872354c32926ca56f63ddae990047f9bedc6 4765b79ded686eb35c52e67ab3e59eabbb13e3ab6a7eda5b550b93f4dea8551541dd55256887abca bdeaffc32e28bdfe22a9a3f9ea7be0764a57d9d924a579797d7599587e146b8002917e6e430e7929 4ffa5a47b2c77d9b1b1e8e14964e54a6dedf954e75361767dfdf5f0139a46fb9dd84b71adbf9511c 6c561cb75c5f5deeb07a5c6965f97494fbb259ff420b96dd35172c77e416ecac745fb0bdb1bf60ab bdf782cd345ebf4853aaff7d9334a287b65b1a0df09f0235c20af3514f58e323b9c803f012d709e9 6b7fe4527ea618ecc566d02495d96849c2c01176d375f9b23ddd87de36d7a39301ee902aac773ad7 5a51d22ebdc2be7c41dc66116022b7e82e9cf35cbcd6a2d9b77caacdbe95eb71f6014b7ba6ac96ee 4c198e139a3d7736f4abce5f04b46c56af9f32039f0ecdf34787a1e557bebd32691510f8230bd88c eaf887e63478eca6839295de9296d9141db2bf2607c176e55ebbe2eac02ad6f28d79de923917a145 d81814173d76da9a4b113599a95f7a3743c737796a9687cf6986f272d3cc11da4d67b29aaecb31cd 14ceb7c959676e936577f6c3fd2fdc8db2c85cac07fe357ab10c2996d8814466f6f872a8548e8eb5 f5c02347e5d9757d9f99e745d83ddee7401ee6e7c3e6331d78cc6164bc9b4d4849986173fc323dd5 16afe9fca37fa7591b294c2c66d49914516e3ebe99113daed42797910b65e111a5e76723e7e68a23 aa8758230a7f59a3da41baa458bf483ee6677b1d9e48d86474e3ab9e262b583ab4b3300f71dbcfd1 c9526ff2f3561f9b227f392f7837ab4fe773c518df5edbcf7877ad3546ee44988eeace7b3f7c49bd a43d250e97613b7f7f12815ffc103c374b1fb343f4a6e706d123bbd301e89de8819c9d5c07c4ab99 1b10d7f16e406c5ffa00c20c2de5e795f1173f4790a64347582673439407ab1344df5027dc0f1a76 3a8cfa37ca33ac4d335e571c7ae74024c2fe02217a9e571fc8e3f2b0afedfbab3efad91e7aa67591 7ad9d6dbecae7870ed16e63dbfb3f5f670a7ac3c4b6d67dceeb7a9d29d6cd7a2dda95dc729bce53d 7f1eeed8f2d690d0f29a4faee515aeec5fe8c0d56065edec3141c84408b3d2b0781fb9dfe7661d3c 2f8bdec25727ab16ce0f9b6f86ed6b9dafde3b9115a46bc9a352e7e6d39d9f876de1e2a85d3f58cb 16fd76c8268f397423a63e5263f0c68cba3aa9250d87b3f26bb3ba8254cfcca75a5d7656e36a2ece f3155b7abd2ab61cb72a9b91b84b38517f917639721d161399b086b1f343ff4371b3a2b7b15bc0fa 27cabc1f7304d719d33d9cd4f69d4afb7e6abdc4faab19a833b8119bcb5cfd6b8d4b75e44ed46bf8 b3d1aae6fc7cb7b2e996866592c84c4b87099a165af1752aa5eb0b54f97cfc20ccdcd73b3c732a03 a1b9719befe490a845e690386b670d49f8fc857450273887e1abefa1858cdec9a19db1ff17e22012 4b69d3d9cd1d27dbe67b106d1a040e943a7295e5ea9962cd8acdc8f732d9dfba2557d9fb4566d609 0afcaa1ce7a51dfcc9a9548c6467ed339659f68d3c46ce6625b4511837e1c01d1350fc663690c47e 4588e8ae6f694a95e8e1500255fe0b1ef275f858f3b860872f88db228831ed9f28073988ea14396c d5e0cdc13419f728c9be69e954c9952b9b2233b9af0abd9cb4cd1325355dbb3b37ae315c160f081e b7659447dd13cb21cc6622c0bd5a2109a685a909432b417b41441b4b626aef9320da6000119de526 c1e593a02bc65f0088eca62382f8bb0ce2f87b297e0194596200aa1a450009c9c10a3da22d809cb9 98f0b000e4b6fd04190570a6540570f5d007702f580078dea500bc1745000b910960b17907f04908 133e1880cfdb9fb983b0fb1e01f849ec62fdc87031e68f9968316dd0ff90ab54ff6f00c40f92100f 719c44378693c8a54c125dae06e0b63501f0a4b907f0d6547e3e66574d3e7dc7440026a12c800db2 09e0fb6b041074b44d101980942205209dbe95c03e0132011f804c873980ecde5d8090ed593c7d92 54b4f00f7c6877c75cb8c1092e2cba3d2eb81faa69daee67fb170032a620fe82ca2779c5274585bc 720066a83640a0f202207939f9fc66c94c60928f6e411040469b1240d6afe4a399ee0c20f68902c8 15167e12c4ef890950c87c00148692117a61822498a5784642bde80c886578859787e0816f84a07a 9889fe93188bfe116a0b7ee3daf841fc0b00dd172029c825f453682d174fa2cce501b284d389dd6c 5254202f261c925202b10fd06c5237d05a5007e8603404e8e2b406c910854dd8aaf13c2f5ce2ccfb e946170e0da3d5748846059c2d85d7f3b3133c6ea5743d1bffe936a837abb58577674bc82f21aaca afbe9891bd4f1f489efc79fe09808235480a524500acc449992ce80a402bf309480e8a0340ef8c96 10df011aaec14f6a287ce501565e74e2b9a84c232be76ca3020b33e16dd251c2f269780a1c8a7d04 54fde2077508837d7a522bf82d6cdf7e7773d785f7099f94375ae4a5a76ea3da132f3e54f7743755 77410a8a9babfeac06f7b3fd05808bf407c06b1b0648bd8203b4fc6902d4bf2e00365ab200a30313 60eaf4997087e26c8417a362aede0a2bedd128a8cfd72b9ff6c483df523ce9cd2391f193ee7ed4ee 2fa9d679bd06def6e3a9a353ce1ba358f339cb4ee66e2e5851cea627480f72b8d5ee876959b9310b 47ba06b6285ef90d25fc45b2c3c52f803f5f0420a7d1cf7c68d49f7401a694d600cf9e79800fba17 802ff45754146810b86b29e33333bbf6ee35efbd976420334f7de6761edaac304f135ea8cfcc94ba b8175c77ddfcd2899ccd0dc19dd26e544b7a03e2f41a52d4cfa20c60437136b45fc8d6d8ab4b164a e7a48bd9fc8a9799ff12fe2229cd73728c2c8a49c59f28b964f8af0d00de5f6eff59ac3b83717678 0f078eff2e16fd774f2ea517243dad5b2d3ccdc3ace55eb4c3d8b99e0eebc7c326a947adc4f0b7b7 b7546f9dd6c1ba0abcf4b401b87fece108cd5b9ab1e95fcce77973bef82e772e745fea692ba98ae9 401bc9dcb33dc1ac75aafc0fe9c0e3bf6f7e00b0f54a76bcd3450056c896015e8346000f0d126426 5d29dae6baa6cf0eedcb6bd8383c9e738a0c9d5b89c51e54562bdfb8eaaa7d159bab91ad74e74b1b 96b69435858e3f8b329c3a47fd9215e5ebd91abcfd73412b63a71db66d98ce015a191e5b658d66d4 d3746e9051b44879ca299226b634e12f92ba89c000ed2c907f1293b8d9f8794e616613ece30b3b12 83e64c155fc3734371ad39383fa8d9d3bdc6b81cd9cafb845b5351a95c16aad23ddbe8697a2ef6f5 cd69a758b4e9049664d67bded9a015f869b4462348e70ca5a2c5d9f24c1bacf7e9105755eea6ae22 d5bdaae8cfb9a24c8f7d49c19b35f12f0042159350351a01785568fc9343cdbc91a4e86af7c3eba3 a3946b99abc3fd556d4af697fa9cad59399b5e5ebde4be152839687379d3b5724db381e40883e925 553a908b94def58bbc2675eb86fa3d74efaadae062156183a2328d1763f94c384739a79775c99e62 9ab4c122452a3afe0ff2ff48275cfef74db2e35b30c0a0fd2719f99b8d7fefe6268abba085102b0f af0fa78fbaaecc6d959a1e2e4b61ad9d4b6bee6e3e7b4a64b0308feb02ce543440305d8d6099a9aa 3a878d8ae60e695f40996d78453e9baa2d2fe7df50b2ed5e562aa1465f24a9162556ebba26b82fdd 100e0ca7098d31ada6c87f0150ac0b016c79829261ffa39e0c54f951bcfa06690bfd1e2c858173ef 2dea57621c0c2ecbfb6b7bdab770c96c66ea961efaddb7066202d1a077bba4a24ebea9cc73f9917c 19679672de2c1da5cda724898f56f52c56f9d94b383c055468d0a536cf0406c9b7cd9f5514b9703e 3252744ec8b6d414f92f92237e9a847af61190698575901d4844e05d40c75ddf71ecdec666b095a1 89e669bfda2c0c1f3738bdafdd4d6d788a9f694d9bd8dfaf3237df3979a55ceb499b781948e5aa39 176b0561271c771acfbfcfa6c177a087c3857a13e6fab8dc60c165bc65e5d54b618745c46421ffab a76829ca2fd2946aba05283bff023c83a1c98ecf34409692facfc5eaf3bd05273cb4d5c008cfdb29 a81a7e051debfdf7f8a825c3584d3931fc5db60429926e348b4be53b5b156b975557a0e1f984f7bb 930d17353a6915e0fadbbecacacef6cec2390d30dab55661b0f273cd601552a2cdd836538c142d45 f90b80be5700e0b35a72b8bfaa8d802ecc8b8f03d40dae5091498f1b2b7319bf4cafc11674717025 545d0877cae9d593e5b539b3a59db50c44ca98a082072665def79b6d2e72b12137c8632b56996047 1616b30a3379d5aef43c1c867456f0ca47ab7e5c1cd750434a60d22b1ee9d63816b48d9a22ff05c0 f2db18e0c61007d95ca3ee611efcb9f173f765cd40cf3d97cbcf87d189cab88694565d6561991b79 1d428244661b17b17e2fa5df4660323588e7e16c9e13a3a8c12a2fabc722a5d38c99ce4f24bd589f 84a36538e763e183a51325d7f943056dce28c77cf094736a9b293ae5388496a2fcc5cf7c01e21883 0cb2cd2475946838779c8eae508bf7ced764886a1ebfec4dfb7c6e9072ae159a72f13e5f88ee4566 c4460b3285760f75f95e03008e287b59769c3b5599194a77e8739f9a1cedd662732c92ebb4d779d8 3d4593a26e278faa93adec9e7efa933dedc9dc9e7ee5cd143d45ddbfea8d3f01d8818d7eb26861ed b82a3cead756f0539aa7bdf378e8d163982e63a04eeff348de0cbd9ae882f254606e9303cfc7acc2 81e5e3c141934bc8a203056332c91899ced7a6ade3663d1a1dc8597d491d56f5e33e19d16bfb56bc 73489e0830b2072b49b79fefd36437781b29692b906ed514f92f00e66a49a8cabbe44db3d8e71a7f ecd7a590ce0ff255f6ae8dd2d560ec83e78a4f7c5110dad7db90efc505921be687223be9ec2d665e 5fa5892efac250f0f1ba9fe60f8f75ab4e3d67b9fefe4dc4b37d9bfdeec99e84cb3be9ddbaeda08e 04255dd96977ab3ecbc704d348d17650f5a1eea0cc45fe0b8057de9f9fb3e7375f75d79c1edbe3fb c13beda3b7a38b8fd55d39ef1c4baa4ed6361faa5f9c93c54597d5ecd38a31c30f4b5bb9b2411736 1de758de37e3436d9dc3a8e6342eed5929ee90027b1fed64f1bed96acc3d5de1728b46f86533af4f e34de69d6b6c323e4c6d32b7a396a26ee605390166a5bf00385f43a22d2927477c63115d2e97b167 f82fd3d110ab73974b4bc512da9f6fdaebe4e0dafac39c5a719db6e683e9f146d3d4b18238f2a15e 8d6c8a5e45af3db70b20525cdab99d3291aa3bf8640cb6982a2d3659d3a0d7abcfcb58ddc65d7fb5 43bfd5d536f2b6abadbb525394d50ddfa667cf742bfd45d2de0b392f1d985d25effe3e39d3cc531f 3c8e8eb234b277f1b8a56d0eeef11a6d71c1fbb89337c50375bb0f281ae436fb6035e5f65d9f3348 a9aba7a9a15d727a0f76082ba15b5ce44b9b1c43b6d6ebc761bcda793cb9745baeb2ac0bade7e2d5 8f8b8b57dd5b2d5ef9adb278417369e1bd87628af017206381b25b9c2af1cfc3847be4cb0872cfa7 6a782b577a9c3f0f5eac8f6c7a3dad2a87ba963feff985069392566a9050f930dd8d4597da9af964 d49fd9d6cf9ba5d375d7d7fa285e979825beaaee89f2f2e06dfa8b774b5acdc39e9d568139ff19dc 67601464e63de6b398f7267b61164743fe5f9c0ef717c1d32f646e215609cfe55bfba513a58ba714 90b927f8ace3b07a58b40feea1a491c4ba93766eb7e60cbc37177a9e5b5fefd7eebadccf2f568fcf f2b8da5b6765e9252dc7f218e6bc85dfaf7c161da5979d8bc8b2315332e46806af6c6aaa1707e7e9 e48140d3c9091e4d27a2cc4eb1dc9c99a2dfe13fa429d5ffbef91fc98933fa58f319f3368265e6ad ceb3da5baa3d3b2fee3b101d7a33bc5b7b3e7ca89b15716397dee0a42d5bd0112cba65af388fe261 7f9ef46396b36f233cce54b89db6d0b391beb9cc90ddd99b4e5d174ccec37c7eb28467edb13d3596 e34d75208f8b1e168e8b76b6372e320239ba97f6d4e88eaea8d14ee8ecffe2de9e74a313b56bbf35 0d7da71756e41d220442572fbc99e5bc7ea7d87cefb435ef1379e9516b6636928dfde44cd6cf937c c68ac7b6d72d8e4b23ae3b7a14a3f9880c9bfb5155d80ac3e7fc620e8ff58c336cbcc631f156af79 82ddf6fa44a7f34ad3768330566e83f079aa0c426b381b84f47c3308d7c3cd4040caebbfb85819c6 d7e337ea2b2beb1a8aade324600d5bf70ec78a6aef26b4acada8ce899f7d7bc161bc5956d3d11ad1 fe5c76fd4fa967f415f4e5f76191c8f461e7d5eae987cab8372576db1e9eb7d8eec9cfeadd85be7a 7473bbe7a763573bc58edd03e3ce267b163bc5d70bee1459b1df59dfb06567ad25d5c5f2e17f48d7 17f8ef9b1fcc96db0efeb9e85427b59853dd6c70acfaf19d84fba1b972345f9c7d67dfe34f94c957 5f2d7ad80a9e773685a7d2be1b63b75ded5da0d6339b29b41aeb46a3f96e91e9ad8b4d16b1978d70 16500da1d6921abd8f68d7e57514d487ed5dbe0e63e5511daee4e55ad207c76b9a921bd7b4e1f7ff 469b9433915c19851f5ebacd63baf419a5bd4e52c11a9775a5909567df3d4e8f4b8dd276d047faf3 6e963d4d5af54d77d4ec2c05b921f8e8a3ae541a611d96e6486d9a670bd5d356ae55b3d1b55b592f c27179b709972527aa1e4bf5c5462dd2affba3d8d266e96a70856057ec15f81ecc15f87ef553e87e c276a14b87fdbf906f36f4117859fe30f95079effbdad25eef8e1d75f6158acce801e5b77d70efcc 3b85d36ed4087ac1a00e79ddf4ba670d173b4235f762ad4a31649d72e5737c971ac8362ab633f36f a1574886cd44a59bcda98769256bb0d356e62cae87f866bc5b61d52cc7a0870d7c411b25e18336ca ab36f2721b07843956bcff917cccffde88edeaf5cb665e9f90ea48e77411f2ad3e1f842d76f4c857 77fd61af306bef068b513d5ea9bdcad906cdf2ee995b951a50bc2a04277b9b8fedf4baf8c316b206 cd489933bf51f1cdb09d3e0209754fa181309bcb19e6f7973b44b4640f22da5938e15881884e6694 a01e21a23bb824f83144f44ecdbfe0d07333a6fc6eec6cacbb66cea5f1821f3d1aed74d1b93e9cc9 ce5ace6b34aa7d75b25bde985abde08f84569e2841c31cf2647b9933576ee19b81d3445d936a21cc 7ada837bd5621f229ac830a13e4b586f13743ae123ff27d77bf193609bf84f1eba7d6925f1f61609 67e63f41ffff883f5a35e93f95a104b6fd48e839098720fef8a56c02df8a3f01324d5853f1e77e93 e3cfa36c25ecd30e54fc099fdff88b357309fb5afc2d3f7af1b79299c4dfee729ba0d2f17786ab09 eb5b4204e2ef7c948bd5acd28c8ca2d80bcfd5413b3ccfd6ddf04c52bd70595d74c3656fd7fb1fe9 84cb741b7fdebd5b125be31e7fa1a5137ff38767fc25e24ffc959952c237f96865b14c7098f8cbd4 f404f6911046f157eb63096cf27bd7b01d7ffdf628815c0208be530042723f2955a8b83b25382e80 da6b34466b5429cab4b85698ef718360f3a41292ae7850bad0ffe03f8ed46f92e8c64988f94d12a2 74f612ec1780aa9be4dfeff19f7437b45f8f12bc1d80a8be08a0857c01d0124d7e6b39fe02e870c9 0348ce370074d91009c61c401e422674d9585328359e8c2c3bc64ad97734df2fb0704573d5a0dca3 3b7e6da58cdf5e534c73366f1ae6c6efe68d1ebf7c8efa93f8dbd8240524a9c917ae7cdf00225b11 808bdf0280375c2fe1b300f0764e037872d1003c2ddd130ec9af4cdf3880c95e35ad0213b0ecc4d8 451a458ba6bd8cb27e4086965164c3f56ca486c5aa6807b7a7f70a2aad16f6f6c0a0f2e2f441c78b bbe2cc93706ef6fc1ed6d3a74a2ca6cf717efa27ff5c8a609c24cac003109f7b0178748601fcddd5 00b22c8c13f6bb84b70090157101c850f2e2a97eff440b37cc841b182a87c55baf1390ddd9d07793 3eaf7f58aa3bbf917799f7cbcf29ef76b79eaed4f70a71da7bf59601ea4115a4f244897acfcdb0eb 85b3c206f3c795aacc1e252f9edc1fb43bbeef5be7d12fe26fec26211ac524c4d6e0f54fae1771fc 16402bdb1940a7e067c531b44728f14c26adf05ad93c03f2bd8cfc237644df6fea527ab7bfefe64b 1883be079cccc41bd64b2b0fface0e4f4d65842756b24dd77472ae9bad4fbf8fad7928ddbc11dfb9 fae4796247f2616c0f60229d006329d33c618d70e84f003443fe8d12f6381fa0985800a83eec010c 7e2e0156ecd1d1e5d99682478c1a7e730bdd5e82f77e794a29f37deafd5af6895993b40ab8d9fbb0 e558fc91708a196dfed8ad2edb3bb5bb244303e9295d033873b97677b3b73d901e390bd986cdcb34 6e4cce8b6f383ee7d4c7f8b44185e1a968ee89bf00d0bdf8b3a232ac2c3c809c3000d0eba70cb0a1 300098925f45165f3d04754ca35fd1632278a3d1c4744f60fe70d6b755f0a8486be8c69407f8951f 8c8a766cef1a3651a27bd637b4a6d6b8ef6d2f869d652fb3cd241ddf9ccf0fd93de70735fc54da6c 9b66752f4c8c83b79deaef5667ac334179a4b7796cf81749697693106b9cf76f7e2a9d848a812701 f03eb10a0e90b07a49252c5d42ef89eb3ae594205dba316afa4cf1da343913af69df4279f17b9901 2373beacdcca397f7bb54e57121f9e4acfe2c27cc8eba3598345c5384efd9bd1ccec10bdd3bcd5b4 7e0f1eab20bd336148385315864ee9bdb88aa689634563e9e12ffe2d4d817b00f42d22007bae6a00 df4383f0ce69c357fc7106eef9561cdc9fed6061035eda5b93e659ba642beef95c2062e754de21a1 b977f36997c3f01ad5bcd1641b35ddefcc7a7a47dc4cb568a0eeb4beeaf2ea67d2b2d4e1498c15bd 51af2a184b8fe579442fe4acb89e49d660349556dfd62465fc0b007f764f80a2a7c74f326569c100 9f1d2b000f333dff9dc71a4fb38a94ef4f86a8dac319d4bfcc6fa5f5e9b66cb3263599e9064d9157 dd7ff16fbddb56bf9a289819f5f3b956d4d1e8d156e1201c29d33eba56b00fc1ca8bd14f064acea1 882fade77c59bcbdf091586957968213e20b81e2e359ca5448da85c92f00d2a75d806e2217606e94 aeff2394a26b86adbce45a0e714a0f37b415789fb964df427a73b9e9bc8499413f1e07bdabbc254d 2ae52faadaaf3e55446903651a3451f9dceb14e5c573d890ece67c2015b9fd42bc77afb45839005d 709bcb9750e7a102cf6448826f5dee6b2e58194b8e2ff0f394f4b107e976fa8b64c7d3f764c7973d 80f70a28c814b5e28b782afe633f27ecabe05eaccb62f0814cb75ea8e9c1be36d4a4f56aa7aabcc0 2bc6c534944cf3edc8f9ca3590360727bd529844efe744721bd504f7147585461e9bf1afebf0c0b7 b1adc2f18b87c3f5f2c73c2beda03e4b74376be61b8f97298b9439a34aede92f00122a7780119337 c0f966baa4517038ad80534697d655ac98a6a56f87c669171e233db8eb458d58f97d159d95d74ae6 3864e4bcbed5a42d7ebc8a8fa5f0169e932d108e9898e1df03b1c2b719aecdf1a13e6141cddab204 038b2cd4dea6358d198b18cea003b9479b506943cfaef955ca3265fe17002d9d9250a56300f0608e 3fcd2efeb8f923f46ce9da503b5b1f4f365e56edad11f228ab985d3aad02f2aa719b4bdbc987121f 6e53169eefe6456876fa4f9ea53a1117da2d94eb97ba45569e779b2c648d86cc04596f6873a6f074 c682afc7cb5a848fabd2b07bcc3f9eeb84f70fcbc3757f9da7a455e067fb3f003ab66e008b2f71bc 2487b873b58d9bfd7135f392a3b2aae9f2b4a8f7b89ba3a2b75c12e272d090ca137a22d6288b1468 0816790e834f5c34801f5c5fc502560e11f86706711fcf335833a8d1f35ddc3f5e9eb9e5b150e832 87eb4dbf1ccad5da877a3ca336f5f0d815b5a7ff6191324f99fd02a08c730378177c83c662183ff6 0f324d425a0674344f7725528df66a20a81a31b5e58b63435299cdd5046fd11df23eb3dbf05df4c4 7283ec496395f5edc6c286f562a699db975ec02a7eb4a646e55890ceddc3f67d9f514e1739523569 7ada7b4494e66cf6475f69ee9b467f99b05aa5fcbc9aff0540edcf03e06401f6d05dee71e34625eb b2c48686d9207545035c4550e69baf2e95d5452c78825de2390649671073a2d95ab2dfdaeec8222d 4a61a63c67d1a727fda4734d2e3e162b1bf4b05b2e8b94733db4a93ac64ff6ad9cb1277d0dd7c82e 46fbbbe83cadefa20b364f68ae5216293f6f673fa489aeffbe0158bef64c1aa721e65ec4f8617f2b efcbb954af197a28d38a3a5904c9a0b5cf4b82f728fa7c77bdcc7103de68b3a3e777c64c831c499f 675d81ce5da6a7e3061e3d0e24def5299768a7ad0055671bb97d4b6cd449eed51beee236b9db0d84 bbb2fdd647afadf2ae5413bcd956f1b3ab94c556b9c1b394e92f92c67efe8e97bd1aea6cedfbc39a 41f2d97c8eb1f424adc9b785a264555710ebc506cb77e5b3cbaa3d14658cd5b84e9f397e44e77177 73dc1c71f64072884ab92e62518d06eeed992d02487e0fb05d6c43951d512af5b7e36c75b931d6a4 b0999572ee067fc4e91df91bfcc48d139ce56686458bcd34f26629d35ffc1c4b7e7b030777babfb9 5df2e5fdd9e8546fba3a89bb8ab4dbf33ccfc5cf033baeb46d2693973ec72b16968ea5432fad0287 aab65950cf8941510dc795f66dfb6c9242c9beefc0de0d7784f580b7e3647cbf3133cfd626b3cecf d7cbfb925b5d33ee6db5b18cfc6aa34e872bfb6b2d56f64b9fa54c7f91a654d3edcfe1e4a1b7c1d3 fe768175dae78667edb33a194aae5157c5bac78bacbaddefe9a5aa69c7d2a9ea534f759fddbf1f97 e6be3340c764f8ed6dc8fe66c3ece4c741ddc135dede4ed6cc7333270fe971b3be3c85ccba50d06a ab72e53a5cee8f256a596b9eadc5f3ce64164fb3d55fd65065beac86dcf45f1ed4f817ff54d0c212 71ad0c74b10c1f2d9f55fd763a496427a7fdac447e5f8af4f255230fb52cc6ecd9fbf14e8a5918de 29ad4e79071bc7de769a31675bcc83c8cda295e737d9a0adafd73de2b6bac9337fe9e87378598bb7 c5054d68bdb9afa3db3937958c79377340e69d4f2bbd1173def1a8e9bc63ad27f38e321dfd85fffa 96c37babcf3ccea5d3d1d20759eca2e45cf12c36c49cc16aab9270d8f31f92142ba3f50e81227373 729bef4d6ea967d6eb6b94deaeb0ba776bc48ac4b7cb55e5ac51cb43e3212ceb3e7a5a30ddfc631e 488d68de7df7f19934119ad3ef299c4fd525234fc785dd673a0a8a8de9e8be1e4d47c674381df103 e22fd2d90f18bc7ed908c8dccc2633b755a305db12990c3a7841f99ab4d585c4bd7f89a8cd197517 abfb3a382edd077d5dbc3e5ebc686feab979d8a69bf39e690d6760fe59cee47ae730235e5b710631 da693a419fee4fe6f654fa4ee6b979697cd99883f1aab364c679b7fb1aaf3295ca387f1cf5c7f955 376158effdc5c3b922de79dbb9deb46fad7995afb877157c6c67b3d3ead93c50f645fc49de77756a 75f78ce5bc777d4f66507c10a793e5f73e31fd1198ccf5737e6c55e2c678f5ac13e382bc5b8e6e43 9d1aed0a1f7154be374f438717bc21d5fb208467cd1b04bd413744ab14d804dd79e508bad24d4fd2 44f39c6b114d0949a0e2e62fecf1acfdf8b9f2a4668ae5bb5423fc07f7798dae74915b1a64ec0df9 4d7e3bda2f3a99f16232b757c3a1a394a7c40bb98a446b5fbd0e823e13a699dbaef5c0faf12653ee 4bed71a74fa0eab4f7355fdb9e4ab5f8deb84e993de4fb79758d2b9dedce7635a23be31b7277366f c35d4370eb5d636fd7bac6dc48e84bd55f9c1cec99f66cb4716de5c8e56ad915ba1dc7659699afbd 0fd1abb2b9103abd7807c6668a51e664481dfd7e9f80baedcef98b0fdad73677686f314c6f979c95 d37a8897b8b55f6633ad5a7e586d3edf72bf79d4a165b3b99ba5c9fb86df754f0d2e330d1a9d4fbd 568fecc2be1eb18d77a3c33ef28dcef65d6ab05a98c03e8bbfd0a592ecfe5c80105bcdf28b9d95a6 0feadd689b5b932c0bcbe63a26a7530ca449c8216561449fc88f3aed6bf1506bbc7db4521bd541ab 8610f6ae6a14ea5275ea737615e79c57e53cab409565952a5672c06997ed643f94376be1582eb5f3 66e9819ea312e930cd12195b42a9baba41a56a2b9b2eca50229306ed2fd48cc5be44ef39f1b9714e 7f1e9e5dc9da4d467b65e90d06c7e9b49b5f0ea94f6dd453b56ea7bd45987af2d5c372753ade16cb 9bc7b35d2e39ccb6b41ffc5cc22b268db2566c762a76813d8dbc7c54a740becfa8784e415f95ac5e cff5b3932fb7cd62d7b29a59742e7e26870b75dc7a1a2c6e7949677e5d0c30ecd663a05f488e2184 3c51c9a697228e8f117227e1ec555fd54e3b66aadfc69be1a1369cf4c6eca0dbdaf766b57aff7429 5572fd5abe78e8ea9902b7be76f3629899e5bef33195358a0d26739e11326eafda3a4676ab17b491 2f3d9076b9eac3bd7a07fa2709392ffc67ce294f414477644144af854344bf39838841ef9c60bc7e 21f4e0e13fb715901f2c7f5a395b8b9f21cdc36e78986f673d743bea359f60564f76b3999ea4cb45 259bcf07ef019a83cbeb5c16f73b6ddc169d3a5645e926f2b2d63d9827bb04443473e384d12a814b c269fae954d9cef93fb3646b69c4d7f6bf1364bb453dc1099388c5dacfb7e963c35fb019c67aef23 6869ad6fc7b23c53c09d1aba0f79d143b5dda0790464b33a658472d1e56ed99c14e5107c551dc4e8 21f3ca22eda4f240b1db4620a281423ffc7c4ca39d49d89412ecf67f72beddc57f82fe49f7265593 6885e0df59bdff2cde60d049bcad5302edfc228ee5888f630f13e2f883a597f0929ff85c1caf3e52 1c2bc75b1cab501cc7cc241bc71bb311c793cc288eb7e436e1cec6f12ea7c631b7b69357a29be085 714ce6923f2147c95f906235c1ebc4f1be394d10f6714ce5d3c581e3814ddc2264dff6429ccebf83 33dd0882b3d20b83b3358e82b3b78c7f11c7162cc4a0df1463306f26af6a053e7975346320e65f09 4724068b47290644b51b83c6216d0592ed8b8ac1b02b2570e718ac7c2706eb7290b08012946c825f 89c1a6dc4e588d12ae9b582ea34ca4d57b66380b8947b0fccc027f5b1c037fbb6621bfbcd610bf4c 5fd0b7b3bea60b66fc6cff47125b538a3f25598d3fe38b96bc1294f8a3114efc79bbdff8e3f78af1 e72675e20f174ce30f3fd827e8c95f08f039fe981337418e127c24fe9ccaf978942ba7153a86ed7e 33d29923114ddbe63cc242b00f178b861858eae1eaefe6ccfb5d6ff1df177dbf665e2de391f502f2 9af5f8de2561f6c8fd22068f7d12980d8cf80ba109c4d8fe49a99ee330fe824d26c1adc75faf3e8c bf16bd89bf8ac7c65fb563c4ea787b8b9187fc8e66352d8e70cf43c365cbcb8679345b096cb3d90a 4ab509e1dfbde3dc27e9ebfefd4451f1dd98930f2fe99fa5f7143ee58c863d21f95974b5d5a3e8a2 be56704c5d2e38f3e931ff8bf8a30233fe2edba7f82bb50d00755d074081840018c5cb000a479d04 6b0e20a7708827b9a618651eb5744410e6bd9a1d6c4713cfdf93f3c8af5569f87d3c72997773784b ce21bad37875c8ecc0139eb599d797f7fba7029be2139e95eecedcaf868f75659bb939d761e1ea45 d9dcb5358fb276907532ff239da5fadf374931b6cff1d72592282b331dc0add11bc0368203f84bd5 13fc2180dfcd7594f5a27db0ebdd58bf0ea7eb59b8d6f9d56d780f6f30f1df4fc5c53ecfd1a18eb9 063428b8f86494a6549d7399eb3a394a1f3fd6df687b27b12c77abbf26961d3c58df92141db1a096 5fb88c793d77363374f63cbbac33e7cc6a84ff2209717906d09434000cbb17007f4e1140a869ee67 12f9e9d6888d437914163d71e2d7c162f5eae1f5fdf3fb6ef1ae71eaeacef935b21f763870ee8fec c8bf1d822eb8be6733f4dabe2c73b6d064aa16d0cf5d4b9e7ea716f41c50974953d1ce73b2f03aad e4459ab331b7b775de70ba8364dc1ad7b3464dca65746ff0c57f9114247902902f25a5e9756f4974 d90f403b7a01a0bb422b2c09bbd6bb7d0e3a1e44f1849b31c9c5a38c2cf7b766b39dce81b2a30aa1 5a1f627cb68657f271d1cb87d7051355703e2dded8396b83c2c962dbcd5311db0ccddd425d9b9546 4534ea13d1d15b540c69dcbb9857e3769053c5f09e5507829151be7d1eff1f694a35dd0288e34d00 0f7909207bea0ed0ed0202e8db2f4485ba587ab3ba5d784e1650feb1bd98f52bd7d8f6ad8f735c5c 745ba6cea7f0229cecec553b15e9d0324914750c775f0d8c438df81af50f9bde1ba53323ad9274b6 a0bec6ef1b0bad57935855525b0f4535771f05adb92579464b0539d33ae4a5f37d9cfd8b649f1bc9 8ebf9c2580966e0ec0a02d4806d69fac4f67ecf47cf3d4dbe7e8ee1c406c0f36a7ec65ea39cd934d fa23f36e1536861b3469fd9d3e39bc7d5c995a38946f5a4fd79e2ad8854025dc0faa42c75a4999c0 f3b682eae64c36672546ce5cd4abb46a153fe29527d3364d2c01a2243ce44651d837d17c4aee1700 5e9d5580cc731a40b574d59f251b87e54fe47b1f47b21f37fd68da8357ed793e5b5dc47c541755a3 71dbf5f5762cceb57efe945e5e5565d6e75418c7546572a8590ad618baf29c9d0472b643c3d2253a e5a5c2c06b8837a83616cb5bf6203895aa25d40eea873ffa68896f726885f33b618963a34791eb88 66fe173f97222e770520e1e204b046c78b0a11e6b90b5216ee54f461ec78bfa0cf67c0d966d5d563 9d65e382d63f14db2a3ca98f9549b4dbcaa70147cb59fb2449ebd2ed241522f821ee06982f96bfb5 af40b55b6915106ae1aeced37d7dc8f9ca67c771e3bdce464e3566454a2b319f975c4929330a732c 31ca665bf80580c13dd9f163dd06d861eb7b5f8c341e157fc8dbc47eb4bf9808923ed0c9dc4f8ba6 de6147beaa0cf88c322d9debf262fe1d4a6b26b714ef6897122bf3112fb835ce14ea74b24fe93bf7 e45be429e6b8a78b735d3a5765c5a03b60be3d6ecb289f82c6202333a40db453a0a7e63c6dd3687c de2fa7948ee76cadf00b80f4d2a9b2f6e71167affdb7bb0842e5cabf1ce6322fe2e4694b902bdd1f dc5555a190a782b70954b2b39b8a781f197dc125ddb9507fc6e9cde53c33cab17c1b69a91c3feb5e d8f832705869b58b98af4e22cc18974ab4b1bc77e959a1b53e9e77a2745c56e7c131f78cf2079b2e d70e9b16564929a7947e911cee45ede75244bef6f469b27b7ddc9f17c5861e0a73bae12fd2f03ac4 4a1b949682828fd4bb949e5ec52a522d0a8deaacc333323fe1c282bde17a3bf7c84a6e20b1502336 19d5cf3d18b45bf4e9d9a10a1dcfef5e5ad38ef90ed93ed8d16b79281103917ac0d89b2275314725 c39a5a4a35a5b277e353f1170031d3870411c397870c08fdc6e27df1325716b44911f7bddead9756 8a0e7e2e484a9bdecc16ab4d23e6df1590e7c261a9c9825d6fc812f166c94292443113c810687372 36e8ccc9be1e97dff3f3985783cf618b6572d463506d50557533dd1fd117bb6f9c9427f95e763309 7a2d25ad02e9b642b279aef40b80a2bbf34ff2fc89db3dc3066f4c3817929d64b4b6ea5e558b6025 2f0bda4eac0e519367f1b1cf090d1967e56d58673409ed3318549dd1e67599b60274b64c72c71575 48ba4b367939944b0787daeff968ffb4346cdf2c3e6b24bb2f8dc94e4d6576e16bedee04369749a0 eb3b61bbaba494534abff83996226b5b4b673f387658322c2d74f853a5d762b4583cec9559efb112 ef717fc9bfb7578deb37f21e0b637d98c1eafb123d572f9da3550293638142d787dba0c71cca4c5b a2f661efb4f7fac97e692ab38064832db28b7a72bac2e5ae2f87c3ed67b4396e8766f1be1d0a2eb6 1d8aa35a4a35a5b21d52dde22f7e8ea57f2a68f5f2d6ff1fbace734d51606bdbc782396709820282 e41c445010c59c413dff0f9d99dd7bf7f57e7f6eec9eb99a555854d57aea61b1a24b0bcbbf30657d 0e9445795661e68cad5632a43940a98f98a22f72abbdb6ece45eea1a872a6a752fa539d72018ca07 774fcb9dec4994b4f1c394faa5e24234d94a24c4bbd645408b10c0bb0c53e333db60c091adbac4e5 4f9788cbaf9d2c978f3a6d2eefd59a1f7c25d5f458ff85efbd74f6f2d6720bc1aa17d6fa9ab5b08c 4477031097a7fb85cd9a8fc712d717f58cae95cec45aad65e70fa5d9ba1564d96db7a54b8d40a4be c813e21d09f8af8378b6d285e96d35e3dff066c98f9cd381f333c0932bc0d5124b3b14c8a4f72cc7 b00b2560587f0230ac95b43e6830ac78a97d50fd85ef207a1aa34a180d37ed8f4b35388a829daef8 2fc68c9110c5ee290463643476a4ae6782a0088b972f7766d059d2abd3ac68c1eb9a9038b79ee030 ed21ffde4214ef098cc4678fbcc98dbbea820d0d7bc396e3e0c670685c98ec8cc6c7323211a1194d 1f1dd0a58fb3ce8b3eaa51933e327efd83daa4b9dafd464203c3eb6103e2e16ab9cccc7c7d454fff 48fb8d755735a77c9bd1cac532261fa13b298100fde90222026db70236c9c7bc771d15f91c2434b9 20bf86b9227dc6d955b7c4b215a3a1321b683863ea532a9c886ff54c9ff0254077edb841e96f02a7 c079714a818bec83024da74e818255a34052a97cf19154fff3c3175778f25e6f6fd56b10d6d63777 fedca2ce6c8d87967dc9d635234b6718f9c847a86879e381005047850bf04ec015f7da9965064780 d978d90ac3736877b2df93c844b2a71fb97bd21e1c045a756f3addc3ab73ca087a9b715c621ee301 bb2c93af3a849243e9a493433a3a93436c5c2687ddffa082957ee19ccbefa268d4e51741d75a7bde a289badfbaea41383513e7a46a93d1819140d9c3f8dcea0ab2d5568e621aaa359db4179788d65acd 1bdd3b7159ca1c7a350ace1cbbe37857c3c65311a5c6684b164977b6b0c8d1305e120bbf71220a45 368b2f57af4fad219c66e7224ee3ce1ea721248f970edd225e5ad60b78695accffc281c795288ce4 f6623160a6f3afb46f5fcefeccc0e72b4d918a534698e9fa90692844974e67bafe3829079fd9937c 43b919395ae21b322bfb77827c3fb244615eafe2213fe9e1938685e2e5c38e1a454e591a7118391d ee368b68280aa578d86a4eebd8f1494d3045c356d8d1cb01980202394ca93db298923d65bef848aa fff96183c2a73050eb19df5b98a6ef887cdfb7ec5134d7185ad524c824198e6e8f47f4a52241843f a61b78a52ac3a3fa38230cc513e10c5bb35584a935e08a7577dd377ab5a4326a20c147b341a11230 183cd62035b0354b190cfa7717793e467b64481db348a66c0e904c25f090614f7c20c3aaf3468619 fd856037f137be7b3a8bc1e9b69c458e19dad7251c1a8b96f3f10e2a9d1ca10bd96e8765c41a468c 9de5101e6d7a7073f078e02564e87a3092e19a2c4c3c0413ce0787005ad2a53d4477a8182a5da639 90c5b735b096cff7fbdb0345f485e95eee373170de3b562eb79e7cf33eb367af232eb49e6c1bc7de 61ff487a87e014f70ef6e6f10bfeadde5b79694ab59e9ee66664bebcce5aab91c85c74fae9d8c449 779642e6ab21ceb4163df456ded561b206147bc746fb931174fb17a6d9b96f00bc834088d04e62dd 6a3bcb4bd0c626e55d1be8d28fd61c880a2d7c5168b572823c6a0648466e8e9fc6b2595c5399c64a 17c7cde2dc5c35c7dbfcad399ebfaecdb171bbfce0534bf5739c8fd68d8db3570e5b2b9eca5bbdcc 784be9be9f39dc644e4ad463d61a1325b03dc07a61ad0d936cb7d26fce995c3b5ddabc9ac54523ae 49fae1553d4364b3aae5d768b597b4d9ca2d54d3458572fde8021573861f2bf0d07f97935abd5e9e debd61190d60b5f496f3698e87959a695a8b58252f9b3d96defddab98c6afdcb2fcc36e3d2debecd 6f4783dc3737f2e59a9bf34567abd386a87cca1810256e8861fdc2a8071758acf69907f26dcc0fde 0d06ce3faa5a4ebe96deb5f2b994cb7ab9e2d82e770aab128ae6377d9acad7fda994938a5b3b7b4a 13f96c2f4b5d32d7ddad90316c09ce40745706e2f2fb5b5fc0de669bc040ec99c000092260503456 bf303dab8db339ba6807e554104321e7b7ed496f741108ba3c23b03ea94150c84e1add6b9e29b488 a2fdaef36126ae98b6762ffacdcbc7a89cdb1d905daebd6dbeb2fd0256ccc05dae05a01007022871 240054e80b004a6a16808af9550ae301a036544ff148ff75ea4f01d499a6bf9b2d1000d502074075 eef20b9fa53a1ac717759fd94522b036dcc9b9d357c8fce141617d738140a1abb7ba46c32e3783d3 2253eb5432cf32baa6eff968ae1c32d7da660d4cebdd13809593f73f0db55b482136521c6100eb35 c9afa4da13847f722f11fd534e8dd65f83ec9feab65fa577d105b08121fd82b62975f6e273fff499 033435c8c5026131bd1a6350f88aba5d283ffb4c6bcdd2ea90aff6f5d7abe85bcd5bae45777700f2 54d3afc57aeeff27ca5f68e5ff09bedfa0bffe5e574de36c07ff5cbd1f211d2a8ebead8122238d97 4c1b8264debf9024c3859642165308420a4d4ab1359364d45ba6985f9204af6653a8cd144f344908 6c92c25152dca7494236fdaf404cd29b14d3538ac32349c60520055a4a9ca2d08cdf8cd68f47dbe5 e8411ecfdca3a0f6ac3b7de397f7b2191c6e113c3fded8c438fc7f91c664cadfd30c692ec5884941 b229967a1a65c34f611cd30081b44938534bb187d200bbe3144ada38629736892c7829e03085bc4f b1bc264ee1fc8cdf934b261e6d92d2c36f663fabce4741e94077fa4a9337365ea8d71d7a9e5d9bf3 4c783912f9e80f945cbcfebf90248c9c5ec895c9a710d23899517abd4eaff4fc672abda4eb208dd3 abbcbfade188721a6ad0fbaba013e3b44984a72609ff7612f4985dc46fa5b98ebd2ebc8fb37ce7f4 f01ba3c7a330153377fa6d956f11e8b5aebb41845c8e78993b5fa88e7eeebfcce0743f2f5627445b 7caaf31c9f2377fd17ec32fa41923cd752f2143f35f6c52d973cf70d3579e1a7207911e0317941d6 2b79e54fa5e4e900ade4a96083645651a8780e59429c9b7ada832aef9cfb8adb7d52dc3b534f56b7 cd34bbbbf168f372dd7b9df82ae1e3ece51488954bafe474ce067b1f9d06ed34e11cbae6fc40666f fb3d8d6c0fbbf56979d8d586e67ebbcb08bbadb020b73f489e41350d4ce7f5af899ceb89c9bb0b78 c9dbb1f6c95bb93c9237d3cd27efa1de4a468205c5858a32baaf0f1c7ddbbe75e97acc29fab54359 ce453bfbfef9def75767f37edc9f9271723d39a5f2f3f8665bb9e36887d50e3ea27f76710f85d971 bce35a057d73bc7516917e24f76bbb021fd6c8a6b95fcda0ecee0fb0c77dfb83e4e51966f27a4b4a f24667ea5f8952cc7e7cc81c907e0a2bf178ab34efdc06fc54f0bf9ecac5c145df03c4197101f684 f9807c9cb7ead62118d6dd4371d10af64ca11bed360a76dcee0df0ba392d91e7a6d7918a91a1cf5b eb389f1bae5e1d4e5965b8a31fe697b5e3b2744e8e013bda1d825ad6ffece2fa5bdfdafd42f2eeac cce42dabc213808bc63393b947cfcc88bc7ecb4ec425552cdca4e851ba40ac5a3b0dd561e7b02c40 69be7a8588ad5cced01b0daf8a912935f475fc829c8fd56aea128bd5bbc6af571ffd679545368f90 5cbdb2cbb00f3596654b4202ae54947c71242e168a181de79767789e8343e5e83d329383672db0fd 2f24efe95e4fdeb1cda5214ebecefbccbab379664bee2d5eeee4e4d6e958f7333acac587e5c605b6 c71c5c8cee2cdc5a3be311bcf2549608fdadc68645d89597136761049bca761670b7ccd2df93d5ad 2f1586d7c54991de0bb5bbadccaf8bc6a772d2dc2067ac67cf5a9e3bdb8a0737d3142fb3f9717c9e 110a729ae5bbade32f3c81fc5e7e02fc260d7515cd9e59898ed2b5c7f2942e04bcdde9adaae13ec2 e3680322e6790d90d66701155215bfbaace677bda0d1b9a0be342f508b73a3c92f7a53589ddf50da 9e43913cf7627d1d7953303eb9af6535715d9a2db999cbba375b1810ed2c972bc72977bbfb296b20 d7690d6a5dec6d5c3cdb427a037cd4c1cff1074f80b84acf4cb5ab3cb354c17be672dafa2e3afafc fccc32c63ef26039428e476795cfbfb7cbaad14cd2d4142c2efa75bc35376d05f6a66b0bf750edc8 baeefd22b9a3a0fcd9939ef9ed8e3b23cfa3e5ace09a7b67925dde9df2be9c9f726db23dadbd6ea4 2de2bc6d1d83ebceea762a77f3b27e5c4d9ddd9f4db0169c7ee109cc1f427a351deb99dd8f82bb18 729f9e762ca62bfeedc9f789c826eb78b86c8c8da05957560bed1cdebc44dc67ddf7ec5973b3f77a 7f3646baa813aef0b133512d61bac9f8da945b1ca7d3ba58f0ed7dabbfb15b2a79b54ec3306b7517 efa6791d0b9fa7224cf05cb50dbbaf6e0de47e8df51932bfe9d849bafc5f48bb675dfe2b4ce28be8 44b4aad2f68c17e8f56b010e57796f3e089aec439edfddcac2c310e4e8e67adce7915f67d5b4cb4e c5d9b4a77ce589d87bb342a46b3990b14ee158b6d489625abd6e38370d63b73621b870316ca7ff36 0613afa1cfb6d9a13e1496a6363fb5375abe273dd4e046dd3fb8a994d0bb7cf15107fff3c33333c4 b5afe0776f39e2ecc05cd35b7dba0aa930ec02c360d71e800bb028711e40ae66330a786c9d4a528f a78d2e55b4db84d4b4d4c30c346f9d00338df79336a1794530d27ffe8cd0c6e080cf74b7c385faf0 e21db505787c6a641ea9a94bda4155ba32d295357f8814b6394c94eab1f790b793fced83eb2f3c33 aa683c73756d751ef6256d27cb5371e50bdf5ddc60f7d80c17977b09f4b06d959a5118634db7f920 b2a5cbe966698d6ace34d15ecd8823aa67a0757ea0bf1e735277ed3dab674b4f555b4415472bd45b 81badc8ff76ab9adc70aabedcb4a0d446059b0771fb95b6ea2f24a3a029987247baf2fee5207dc5e 7fe1997946d33f5f7c5e64f40dbceff3e124e4685f6d0723efe99cc1593075b1e9166ae876fb422c cddbde381bf16bf5590b1828fb2aebeea3d1d6b3d400d6c83531d20a9a3151e9bb2da9656b6329ec f3b4506a6e712b8b59f02e1dc75251528a4f48bcb01c2feaf54e28f6f7cb7b8a432cdc29f7f61f5c bff8a8839fe3b78f3e36fdac7f58cf06fafafd2af3cbfa15a317103bc3ddf9750b39557d04db9dd2 5a32cd203337a6afe141f76a52a2f9885bd00aab43439df45e7db57c6f604ae41394522f52822c32 cca7c484dcaace3c4911569178395e2fa2ae42e9a96f7657b0ac2e2b20836cc8272fedce3b9afcf8 e0fbe9c63b2c7bfdc1336b77165783684df7c2b6a1af826ac2fbe77c8bf65e96f4b93d6774de85ec 4350e899f7becc1be869e9e8d9ed6ba3159eedbb3a61e8ac12dda635a54eaebbf2be100fe4969a27 a453afcd495db3a78857987244d05157820dcc4ffc938833fcac4075786072a5b979e69bb073f32c 7ae3709f7c70b8f5f964a1d75ff8eeee9c5fed87bdd5d1ad1a32f08e5fdce4fac4cb1679c2a9a126 6c69a75ddbc08acd899ecb08961a26cb95b2a9c417790f97dedf8b16c225e9a4686da97b3541f16a ac87a241bc18e111662461502959fc73335cf2c33cb5e7e6f4ecc5e53699061bf0338aa59adc9ca5 5ab52b4b15e1982dae9a77b6382b5f7fe1fb34d1a78f2e461b3b7ad63565c98791308fdf156616f6 8684add44790912cf4969eeb5e4875927474a5be217db9159b0749a50f8978bd160a2204d56a429c 277b82bdd450fed571c7fcecb215f80c78fe8c021c31cdccd9e052dab094ce24cceab1ad31cc9421 982a06ba4cb57b394f36b7ec63b2d924b70faebff08df3485bc1743da70435e800bee879608e7538 b24d58e0a0037d9df70ad6542bcbf486dd473b495297654f342ee8568841e1260c023fc3bbed4b85 1f5eb22d3ea377208ecca1432e1f080c4b9734852dadd70ec3d6e2f5642bb5ef93c6592bd307bd3d a26528e7d072c3fbcc9eb49cbfdc3fb8d152bcbb7e70f9c1bd95f8f3fd4e55a7ab52bfaf2cee0d5d 74fd00e0a62d254f184fad06ab4c23d79025f7d1156fd49815a67dcbe25ffe3de433e7f2892347e8 f3eb20ce8a7996169d1a5b6eedbaccfaf41e30acdba726bb2c2a4e045fb6e86371b1a43b4c7ca6b4 1d5aa0fa623c18dfdb1b6b7c2f73fbf1ededdfc6b78b73fde032be45faf90797691ef988905b50c9 9bcbadd5d7e60e2d4ab3c9f3c15bd7e845ea0479816469bdaa8bd009aaf0ee6283738b675765c36a dabfca82b361d8e472636aab3a3011ab6069d2dc8e1bf4d174405a817743eae2dc27e9f7ddf98c02 e3fb96f4c64873ba2393532d4b62bd699fc46aaa4662b9ee8678db93cb5f48f8e917ced90e388f32 b9ad19746635ddc32351711ac387680eec25a1ae311f12e122f171dbf159e8566227401b626ab1c4 4c44f961d1cabc12d05d627ca0f483fea0407995193f3a8ff21899175be49300217296e770123b44 0231ef2416815fd1151e408b181f2768132fce7a225e541f215e6491cf03657871d43de1c57efdf8 0b872800e6abd2da3417d6226bb8a5baa8d91abc1275b2cfe2f2312f42bc57856a0c870b055a59c3 650adcadb1b13d6808e473cd4fc9613f0889b9b5ff2cd50982aa3c887c6990c183b35ac1296dd5c1 4bfde760c4042d6a54a53879b82d1dbd217f21cfd801bc973129ded298144a1e26398f032629a73d 264d36bb5fd83c8ec42c388ee98fa3cb9bcb17d311069c614eafbca046c88890fa6f0ce2c6e6b049 77cffd0239732c80c81397165e72607cc4564c71b8350ef650804bc1b05920f6d82134ee58a7bb04 d0f3352ea39ad968a37d924707f7c2e623740dcc55cb44929eb546a677104050bb3040502ab691e9 54dd225399df20539a8a7e61b5b463676157d1a95b8aeed33f3ef36c4c884a6bd82705402a420c8f 559b63fb5efbf86cf0528c015867ec3d0777b6de1ac077718824fe8547b052c180dfe7d11cf6342d 4a5b71b8c0b95cfe05f9015c848a6db90d86e703064ebc810056f095dbdf34b85b9f3b229d3e3783 b53ea79ed77d8ed97ede2adf8f6687553f52d7e10f82e36e31fbaafb8e984b5c0bf13947ab443d49 7c1cde2457620184ee3de216b13815cad8912b020846b6eee0e4113cfa7ca75debed3d1eea49f866 dc6b372a9f2547f7148fedae3ab5fc6e0fdd6d3bc6267beb403c966dc750d86adb496dd81e840ba5 f552a9a8e5f6a152cbad8ff9d62ccefaad57a31cb45ef9cfa7ffc157da9f455d74615fe9c247b331 e6a7892b9f9197c42ff40b353936af28f95edfbaa33a74ac0c90d50600197c73ebf6accca1356a08 fb66a1bc031acb6ba7dea0e71adc28135bb21e35ab629d3d1256bde6cc83da8ec91d6bcd2df2aa1e cdcd474ca92a631caf764b59ab7239c7f78ace9786d5ce0db0ab4ab5eb55954cddfd05372c68c1f4 4490a1f9e20aa1566bd65dd1a9c62a5b7b79f4d83950c888cff49b035b6a15c075a5f5fcead0d9c1 a59585e45d6d2786610524bb7e09e09eab22919f3c8ab9c3a650089462ab50fa783119de9ae4ab8d bb96db246490e3c3f335d760e872f6d0af8eb2720e9865dbcb5c262b25e8247b686e8d5c035e7fa6 b5eff107ce1e5b44562cb31b9d9ef94b295dabd95c05ea0814b2d98ff06a71d91fd80fae02ae153e d3b9d7f87bd32716c79a68dda3b2dd04834210186e562e9c2de0b6d9ba004cf63ea300000bf3342d b7aa6500f66d1080f7150a4072be01200d749d227e0108bce802c8d810018452760022871d0051d6 2a80308df92fd8377ebd37c8a01cc997e8e9f1c589ab7ea6b58fcb1c67ac173c88f5a801b28a91eb 58fd59d2080feea9aab6926d691622616ecb126ea63f394f0178b39e0248e5e502c87010a67f7c73 051077900390f5aa05206f64080c3a1709184c341f18a8ed8f831818cce23a3058af28607071e6c0 e0aabd01346362009a559d14c1e207e62812f6caf18807424ecc9b5f8b39be0607d820bebcdb209b 9c4a5f41bf51992e2f156376df15c9eed7a298957744da5ce0364b63aba69f66c3e0afa4da9a47c0 60bcdf0203bb7b0306fb7d0940f3781f40a13b05a0e6d402d0dd700760b96a1ec0ead7c13f83acb0 fb6741fe7a7821ea8fb517a2be50775b6f25021ae34c4eefb2442cd139819ae1a80f7248b1da9e65 0f409d97cff7f2942d9ff2eca1b8ce80c5c1024054c80790131f0283a2bd4ec3a91c01141c5f0194 53ee00aa5b1f833f807a5e0ca097dd13c0806ee99fdcfbf5239f6d00ebf3696c602397e204ff7d1b db9f92bceb5b8af8f9837fbaee5731fdd14e17da5784fc2ff9340fa4e0eb294ef03ff95414524446 92905937457f993885d1264143f618bf27da2df62a8b573cda1cf37156c8d61e7eb3df7b14147a78 0fbb37f94edf7a9fadef5b0453abaf967bdd61c4ee2a02d0f60f9af3c6e607ff145e984ed149cf3f ec535ff5370d31939e7fc4ef523cd226e1c3dfdae9b7351ff9145dfa56ec95fd7440e4fcc583dcfb e1a320edb6f7b07d3fdde94be1712feb60e6168162f556b3bdde557867c7d7a6472a1725e77ae70b b50ccf7a69ba3cdd59daff858f0dee734cc3422729ca788aec30458df827a3dfa66994c3758ad52d 8db29d06861bad148f411a5d8e8c4711c03cfc7a4ebc87ad87722fab4ff3c6de1ed35bcdcacdafc2 b3f8a92f706dcebabbcb7138be5e3a0bf175d60b41f9749f3c7b27783ba68f4ed3520e6f259c1e46 57dd3e640dcedafb106efec2dfc0b0fb20c50efa1bac2af349c21ccc3f5da01da4b04f7fba80931b 5663af44741ee40e82eef4a987a5b13588ab90e4a9cb112b71e70bfe12cf7abea4a597b3649dac32 343f26dc707544f7fcf1e0b567c921abbd2afb820dc1db6834f86cac6c448411a3e36a20448a0a09 51b7d7e0a3eeb027fc20499e24993c8b701ae0290b274fffcc25af1c6126cfbd314f9e541025cff6 f312e78af6e31e2eb4cc8d9b08a56b0b621b972e2d7c24d5b31e6983d3a3ae8d4e88eda657e9e572 8779d6940eb9b1a9edc7698eb85bf50ec1964bd69bcd9ec9dfa3930017d686b24656d3ea50085d41 32c24c42e8cb8503694b72d2d396854a55fdc1d7446eb954f214fb70f2ea2f46c9bb6c6bc91bcb79 c9bb8b45c908428e8fd26e73be1e48e27ebef1d5d769da29e48fa35ca17620dbb5ee9e1ed5e05dad 90a0db66372136f2223b892ecd9a18f595a6b6b600f863b258253c1bac1c687d08df4ee619e215ba b62ce60fa3a082f564bfce0fed8518b7ad456bd9b0e6273a6fced5526cfc20798d623a850c27ef4e 9b4adef1557b02057c9178a112ddb951f0d923b8e870667d421feefe50f04797ed6e514b365dac92 5b3f1ac5ca1a5982edd5ac33865780c70d43a2ae53cb40d2d92535889460b5bedb41152c2cfccd63 b4f31bd4345e48eb7765aee5b49167b66fb21b7b5f038c8bd6f3ceecb58ba733573cd8bf90bcd637 3c79bdb6832790379827b068e94f60cbcdee1b69ee9c1f84661cfc67646ca5cac48da0ce385acd1e e23524cefa7b49d78252c022abc657ee5e9d7a7eb3f71c2c8ed93ab1e88c5b93f9a53596e6fd9962 7af7a1ef79f036d9b88908df5df4e997671e8b0e1ddf5ac84eb1549a4f27ccd99d56aaeb991d6d5d c78e16e6f407c9bb74c3be96f803317e02b7a2f4cc0cc7faa3caa9e2c57048ea40b5eac34d4f0f98 95ab6dcce5f2795d06351f382e8eb362bce856a1dc1c848755cf0a99b6872833d89d01c1d0c5e627 ca0584a230c38f756d9673265fcb085571d64e314d89a70c3c2e4e2bcf2c6a37aaac6449c2d6334f c9726eaa8ae69abd2e3bfb85e42d37f0bf8a3433d16f07a9cd1cf3af09b86b56d4667473e346e8d7 f21f3125e01848592847c29b835763eb0df2c1cd1da2676036dfe4cbb33cd4683a413ce83b259a42 a7eb8e414eabfa8cb5b7f849b1857c716a1d646469c91de76c9ee7b99ca9a549ba713bf64503ee4e 3d7d6a681f3145472176aebd0eb0fb0b7faee647918ee934b853962f8fb68af2ecaf9da8540f7d95 2c0575c545e7d7c359f00646c9992d3a8d95b3e488b3537a18af294b2d0bf6f67cfebcd3cb6ef66f 1deb982bc196bc1fe0e6a53da44ded654866dfdd588655077c03dee347dd19ac323afa46ba9a87df 792dbb4467eab88c2ed442d499ab05afe629219e757ff0041ac6e42b4c7eee1b585850fb4de98eaf 9f87793f2c15b7f5a09e64f373e3d0015d17179859a194f699287b0eecdda07ab48e6cfb6175ee64 d6d4c742d9048b6ecbb0d465df407a67547f66ab94eef810af032d46d73e65233f16c5dc08d8a941 76f2528be36b4b59b53d56a9e86547ded43bbe5c17df73b93ebe7bd2beb8737ff0048c13ffcc9c7c fbe42926bdb99ec6d82aefa960d0cc2c9bf39b7e2cb8aeb66b3b61bbf359d94c6bbb916a1d57dadc bc66b63be3d13c5f0d64967feb4f062de9d87654d70153eb697862225a2edc126a3079b16af1d650 959549cd944a6247f2665549e446d5684afb1dc648ed5634154fabbb2faacc77a7505487a127f632 8efb83ef17ff6015403d160d61120d6ec870c9060cb4d00bd396fb1e38052744f08abd4ba7134b39 d56413dc8e5c034984b53eac98276d6eac132d9fbfe7d4202c7ccdb0a56e33bd5457145498b93652 aac47222f387b324379486234957622db58dcd43d40a83bad85b15278299e12c015a84c1073e1fc3 baf70bdfdbe8da5ff7f83d2fa39f52c7abc5b1320c9a2f029e2398d69e8dfb5c61caa9f5a2a51674 d4786c8e823e0b0b536d7eed87ea323f3aa825597e286b34cc28556f5792b77cb629370e859e7498 c2a824a3ec583c7b9a206af8c1166e87ea27f7144c59bdf3f1e555e5a7c68ae25118b7b9d7d10a38 5711179c3b99ccb95105f37ef0dd35393fb30d61abd5b3741862c5d1e25aebc1ee1c22db4e451c17 ad137fca198f6b17d28753eed31a2daffb865a0a2e0b85ed94b7f2d683af72b34ebf245912f35267 e0d7c4f3ebd811b5a88008f77a0d17ccfd88e3938166f0d3d76dc9bdf1de95f372eb3297a51982f5 01c04cc12d59d240161f7cba004bf25def075ff1fc0c8855318a9f11b5e40c009f3f7c68300ba670 dbdeafb09209a12aa00f837d47cb47ddb1b20e3855debe7c4f3ad68e91d411939378197412b1ef82 19e1ce729f27560478a734f9c40a41de41ee431e7dd518ce1bc12a97dd990bd6973267b670668b4c a8f7706652dfe8cca4812d99f2b2be60cad3e2fc03ef0709c938f63158e5a5b55775a840ca3d3f65 73bc57ab873a4c546b5beaad55d25f1c0568f9b3da5058ff81cb4dad224a9d1d3e1575c40a85fbcc df0b48f572e7935df6cd6348bdc0bd9f488df3564a8fc3191f65fded8e4a3b415961c213ea321375 f9a93e3ad964fbf90967c5d8a43eb0b549bd5309e8ddfa31ff0bf7e4fee0def44eea776367e59b16 edf7da7bc2cbccdad8b4717fb74db8902b69e4a0fe56d85da3241d557520eac286fdee113c2b26ff ac820b1e13c61b1e809d0b873b7ec2e5268f1c1b548b15b66876dbcc0a1ec3cc24b1c8c9060b8509 5f6dcee8bd20ec68a995cd5027750a5127965129357bf229e5be99ffc5eefb828deff18b2b383094 dde1b094c2c951992c207743ce96f3176a5d8ac7b6eebe4f2525aa5e5fd2d1596745906cf4f927c6 503cc0cf140e8faf33369854562c55410eccca98dc1906d2df4c255e7da4a1094f9fea9346a5d8a7 f7577c44b72191a54ef1d1a67a58733dbe018bd7d858d0fd318434a431d49a2f52040bf2b1523df2 31e3673f383f3549dd58f591b06c5438668ef6f794c31e0ee8b76c4ecf6f6be37a50925b85e0253c a041c263ef658bcbc599114bd143816174c962aaf8c69ff0c1753369c8f50b2d5d5a31ddd6892c75 c6ed2aa5e5369df18dba2263b303d263e8a2ebe414bc04e420663fef5e255c2cdb22dcee4d20dc2a ef11c305f1854b0ccdc1ec0727c2e7f5b577ae09fe39cd8cbcacb8a2ed63e4a20646ea6d6553524b c28318263c707e3dd8d2a05662aa9a064f1abb03fd95bb0755953abb9043f56bcc727cdbd9bbb129 6eae637800bcc8a95bca93e8685027de350324dcdd8920b2eda2802f5eba8717f0ec7914e6c2da68 194f99d1326c38231a2bcd3e483f7533d32f3eef8dfa1c0fac3a3456c57b565cc034c2cdc2d38ab1 f4878c6a01366ecbed2c51e2e6512666d62df3461f16c293d29c4b736cdefa433221658e44435723 de4aec12a35b3e24b2667b87fb2473c3c705fbb3b18217c2436934e9655aa3f26d301846e68c19d6 8b390bdbadac1d26b258116be56a24d67cac2dacb93f385833584fb1a6bdb07fb005cb6b75d9082f 92f78e10c1a96f0dc6789e47a85a85ba1ffba8683cdb2596ae679e74076e5ec884f3d3c554da1d70 dfe3ba78b11e8e4613fb9d8ecd34ac0fb9889c0deb9cb6c4f6e0768f898fd70d3da1a537aa96e90a da8d16bdc195cf1303306614e4e100216263c1c72f8020376b8820bbbe8620166a2188d8377f614d b459d5efcf778a4be530d9ee4d06133d2f1606b28c563afc3b532a333529f31e2395ec151fbbf861 585fe54e583ba3165155387d4ce483ebb3321c18339c41e2aaa220f6369c2203f31dc02fa4b98587 33f80267180b8016d57b0d2276300c2e119701a9576f0696dcc7adbf06af507fddd045b004ed0cb0 d408f55f484ff36717029b869ab31de0dad76aaeb24a828ae67ddfe60acb7399ee0e2f003107dff7 61a3553920310aade04cd03b42f9979b039751be0596eb6db8bf7ef0649fb5e77cbf463d8d34a52c babd26877c66cfee11b4ce5df9f1003a17b455ebe8651febf437b0d2be9ba55ddb6ae59b6df3b99a b4efedbadabe978a4afbf602e41ff860b56cb894179ad38e3636751fcdb3f2719ece29de6ade99ec f1c567c9311e34c3cc685d3a3e06b7f9e90091d66bd56b1e3bf30ed8a8456d6b6926ad67392ab49c 6bb9d5c28c11d20288e9b839cf1f84267e6c588da03b4c13282038368a8b2a505f09b376bd72aa8c 6b9bd9ddabf1a3cbc70f5ddb703151af1843b15ee121a13ed910e20fbc7998b51d61bf732c640c5a 5a85cbf0a2ddf1862c434a5dca78c9557c692859ac233a318c9767c7de3eb35eb791fdce6b50e3ba 5d3d34369f6cad72c68adb8a56993c2abdcd3c57be9940a36cc22054860bd6b814a7b74209ed359d e2eb666e8baedf048a2371db2b2cce8c5028e0e34361b1f1c162e6d69f1447557852741992f9c12c 029a9fa7beed6bfb3e37e633c491cfc583c0130f1d9f74ba3c488eee5a73d8ea7005042091b85f3f f70e6d7b0e878d923576ab9dfccc2ca1d64bc945931a9f392e6b6646918520d3edac0fc015282580 3ea74b80beb97d2cf100284024009a7b0d00adc90a0083da2bc5bd0f80cb73fabb532501c0750e06 4017237f61aac49dc07c8ddecb6f056e717adbca6ced6c51638cee41c33d94d4be8ae3678fa03190 2eedc1355d99af778e5fd1e2cdb488b7335a765fa9f180d1b62600548a040042473300a2d71100a5 9d0d801c370f40a77607808b1b1280d33b1680e9c21a80e51d904ec12608c0334601607772fa0ac4 9ede07e00d9a4ecf1bd7fc8115a3e85aa7552790ee126371957c811b3f891936aadfe016323b170b fdc6fe14b7de95f5a1ce652fabf23d01bcc2f85c3533a779530420e92e00701e56bfa7210403800d d706e06d350490ccfc0620b552164046760b40f4d608402e17151854cc1530e80d5ec0a09fe90283 d19e07065cb80606bc570406c2740c0cc4aaf60383b4b36bf9a2ef3f3d8d2f7665997aa81572b439 2dfac84b1a57fa4da8fe6e79ddf85c13c5e3baf454728bdc661b9b80d9e82b7f03fb2f31fa3f82ef 1f5d35d8028341eb010cec5315181c26c857526d967900150e7300f58d1b80ee890680c6750ac0f2 890ba4eb813b80b58406908e84c31f2847a7bb1072e4cba0afbecee0eb5c633040eb7ea3df34886c 0b1f566fb55650de95329562f0d1a10f7e7b06c0f385fd3736a6bd009015b5fc6f31fa63f2fd23fc d6b10b80e25c1a8e17bd000c50daff6a4f4c957faede53fcdf16642871be3af4579efed616fe877f f2e957346d92ff239fe6bdff914f894a8a553f49880afecf7afa75f55eecc4c9bfbd040d0b9fd6c4 ef49338abd0a7c88471bf21a6779f9f9f01b7efe411e92c63fabac40dd2268abddd8a4eddf6a0ebf ff2f99f7a3ebfe9773f6837f0aef473bc5ded81f6b6f61f43ff22991feafd1fe9146d92ba4b0db7f 0cc85ff9d42b57d838cb55c4875fef6a0f728fd98f823476ef615b0cee654d896e51df39dcd8fbee 7eabd9b9cc5578753f9bc597e368d2bf74fce3e4ac97cadab9bf26bd935543172778d7991f13b1ec fdc2bf28ff239f6209fa4f41ff314acfce7fb45334803ea779f8b57cf351101be01ff934eaf5881b 7b1b4eae3b78c85f8584512e478c312e69c2eb5c3af3d9e2ace783f5e94eef8f27ab524c8e4ea357 3cbce51974185daee33d1917e51dfd7aa85b2e6b4bdb9a2f7d1e5ddcecc694f0837f5186e9b78a29 cd1417e45f17982a3f5d20f68afced1e367bcf1b7b6de6ae425c2b5fe477b971bee0b5eeb91fd4a0 d39dea632778ddffe437c78445e923ba43b983d71ac987ac4a9b7bf2267b3b3ad156dbdadc3c6d9a cb4b36ea6c9aed75ff000f5677992557f0a58f87895e1bfd810366875fa48145608a492d055ef946 fc5f5d80e5531cacd82b9cfd7b59a6c3cb7150d89cfb8bcbf1048797fb11dd1c5f07af71cdedfdce beb42fe8fbdaae6c07cd6dcdb53b1b316b83d1713c43a3ce6a4eacf5ea865ddd85a7b6824fdd4fee 19a237fdbcf4e0472ef055a41314ab1ce287b709e24f4c0cf62b24f25f4812c74f3bdea4954b92a3 554b9e63144d9ee262923c5bdbb4f7294df1567315e97c61bafa11bdacacbd4fcd3fa7d94d46c17a b33ba9bbe894312e515798c56b3d5967560f6c5d58219b43357c9a99768825252804c221be24ba12 1b04f38d1194e06ee833c7e36bd1b821ad79a7608d3ccd7148af8f29847b0726b86bcebf557bbfc7 2f3eafb8a9a72300574c5e35b89bbcdcc32879796deeb1ec4db9ab34e0c627646310fb006c4f36fb c54d8a7aabf66c152fa0307c25c43ecc56a8eb9234a46710e6cd6c5096dd921fa187afa45a8be2de 626734d0453386c67385668479b7b39f7aba5159bb8fbc9ecc9e58ab3303789170f0589f38b92947 4f0314a7a6e33738fe41f224c36a1a62bf93bc4b673479d3a551ec67d79fd65c655d848e2e36ec6c 8516db5d9b8940841e210bcb31bdb4830a770efcba7ddb2ca44ef5323f79bd64decbc119cf904645 0f1ac875372e2d7bee805d23b317984997d8414f986528d57688f3753d5d82c8278d9a968267c7ae 5e30d212708eb39a399c350f01c89832559ffce04f941f45faab4edeeb993278f4cc417e5fde53cf 8d7c341f2be7903497c543164b33ae263b3f0f60ddbb69c2a70bb8b1afad5d14599f66eeeafc9865 abe797b3b8970b0ee9c3b5695844db53fa24c3d3f26c4bda5ce6c6d9b5ddc0b2c496119a4737f730 bbb5a06de8529dd4efafb1a823fd0af78596dc01f68b8f66f339262f4d0693f7b4473c56d91b7e02 ceb9e6f6903be7d7f671fb580187ce3560f65c65d1a939b0679e76f4ec6d26ea2cbb2a38ce788886 d3702b1ca615d8bcd95cb878d9f5c92e6789d77bc56a99d9cf93c5e6898421b37ba447c655715903 0262437f2c864b7dd0dcdcb5994a35b54ce641aac4b825a9b92720288173e5150adb703ff8abef43 33e6fcb884e09e85f7adb593350b699e384dbee2fd7d779a6bb747ce7554b037cbc11c395d619e64 6ff89d6ded97efc06a776b5b539df7cf668f2063c33888190392ada21ea3bb863e8892bef6e26b98 368c47132d339daa2a59c9f86a7e235d15ba59fd08c4f2da991372f5fa90a4adb11725010a044968 ccf81f7caf663cbe40d429b3f7e00d98a79ae17825947cf122bfe6e0503fb998a2bc1caaf168db9b 6b7d6449078c37b5b2621a37ddff74013d0ea2b53e95de47edf52edf35d7ebbfb56c1dcbab8b3d57 5549dbef2acb5784287454a6e4a88eca32fb58cfa51dda3849c2fb50118f0435123bcb95245c505b 14b4b72408da85e17ff0ad129f97e8ab6a8eb003fd72066bec02b582cd8dadcc8d807cbb4358bc38 14d1badbfcdbae9be7e0841aa69763f4e9bea56a6f8a73b5ac262ed39cd1dba9053fba28a1f87e2a e5732e2b472e5891b9d1f8f34ca1b4f3454812c51d291e4f6f414cbbc14cb8660f7b410f8402ff28 5731de8a6c89477049e29e394ae41cbbc7ffe0bb15718192d168d71a63c82a0f429d747c1d555d97 6dbe1d3a5d4c7ebdeaedd3d9d402a86cc0ae0a696f7f4f6bd9d353568bd9a2ad4c44d097a3271ec9 dc4c3c4afbaa7597c46df8965ae6b3282acf5c5decae3050b832ca48006f3b9e7f589d298f3ccd2d f779992f37cc9e3f85343920a044763e184a2c7e6e08bff015cf4fef2e8c6fa04c0e5eaea576777e efc2b519e95780690dea5ecd4b4ddda7035f9cd73c7fd857c733895026dbb92073c9c690f693e727 bf915ab7ca4a3c99e05eec15f0ab700dc5a76028419e7f5c3755de5ee4bafc8044306e168a0c3754 128b9ddf46114b9809c0044f176128adca33a55a49fc62b25adcf81fc4c537f3b15a1d7dbe43ac5f d013098475b5e73944ade6948f4fc0523ab9ab811489bde66d17698aafc42d79233430691f52ac78 baaa9ad883668e70cb5f9702a4005b3eeed6cffc600ec6dc8ba0329c9b37cadcf0b0ffa81ceca2f3 445812c027cc927074862ed45693f5c47d4dd82a0b4daa8b98a3b7702cfe017f5cf33fb849ab2d79 982c6be46a41ef115fe997fa2e4eddabf61ed80326c83e6f3ad0fb1a60d4623888659e77eb529b3e 2362cf28d182991fc87cfa5b9b1f5cd439f7f2566b6e94dbecb9cc01b8b10bb9f566499429314b4f 6a3034be8226eb436e3c61e5893aa975ee4b5a30d8846ec230481d8eebcfd049c993ad48c9d852a0 3ac094ffc1551b1ea87dfd5d1887cbcb66b0d0fd3c381b9f2f35ebc46f32fad3dadf542affdac9bc 7ebb8867142d0b377d0af2717020b9d739e6b911de30b86c9df2be72b7c42cd942dbd832cbd7e6c2 9447a76412d5abf909bb1fd4e85d9be9d1c26b4fd04daf2b534ace5a509d65ff31be4ccafdb10e98 ccb86fb8e2b8cf9bc2b84fc8fc0f2eb0b9a6b73dd1ffacd3965c3d40e7762b033995bb57358de92c a3cd57f39b52456f7bf14cc97bc1e4b2791ea5e00e3772b511ebd7d62c5b106f2a43bff253a6ecf6 fc09571ba619f58e3fd23bd1bed3cde71aa08eb357995246bd4ff5d1f165270dc7ba7815c6607bec 919676bf9208187588e480d244b294452299b202e17460ee07670c3eb3d194518840b4e79837c4ce 907d282a556310491995ced877299dc077023cea7cec3c9c5734dfac3f4c1a4c283691493443c713 8e994af46ebb30e8167c70a963f25a52caacbaa5ba0c711deb5be63906cd65917cc0af1689ccda03 e2c9cc386256abcf0840f4cef8bc24b670ef5da3705c873fcfb0e338d7e1f15c2ecbfee018606b6e 9d2571c2ef9eeda19be79790a5f5c6553df32032f2f648dc04734d6eb951725f33e1a2709d448952 a6f795b047b78cf788520b1d767c9da0cad8e86a1fad937c18e69cb4a1e58a1ce45f47e219361ec4 b03bca12c0d5ace173e306e1b9a44d8d8270638f4a55ec305cf9f5fa706545c49069bdf92153bc73 c3c9f3c8fe60bfcd6b5c1886f8781ed7d4cf02ca594f4dc88449f05327a7979194cae0ce79e3f286 9918ce8adea3fa8e52c167666cc860837cbc15841ce01e49ccf6479e18ca598dc8a07d07273cd4c7 f3b8b0192debcbcba8241d9ec375a75c1c326fe1a30e625b7c4b60427ea0a1077a1fa1722528a3d2 0d1fa2d2d6e75069e1b0a864eacc0f36b7479909f67c7decb9a24a4c9b3a0de91e56ac2af56c3e23 3cf2d53b33d94411ddba80cb3184ec3f9bc5c46b84dc888c651770b2786d8f965a1d1d95eee078b8 b626fcb056d4356cbb0a1d4c602e3ed6ec3777a86cc157b483e899c1a59834067db63f40eeb5a580 c0d22080934e3b07c7ef1302c7578481e34df7b3e905c7f33afd83b5a7de59bf37add02ef9b4294b 97fa905604af554949ce191e7bc5f749a3116dc7909d5be20b819f8fca5d7e8ded1a9b186b3ad512 aa00500bed6c8cc1406fcc88413fbe7e5ee280dc9da28a2040dd8693cd24801ddedb43efc3ee0e79 0e588470cce981fe224b81637231038b4529060be7611f2cacf7345870571458d0e6e31f8495a3c3 cea7973aeb700b8a3266d5e4f3e8a2c2b783aaf0b82e32ec327b78d0edc3e5408c986b3864af0d6f a0b3828d20e3ed06c6b4cc1d0646641ec2776603ca895b101c3f5f23b0e83626fdd56828f5999a6d f736e2caeff1eddcbe573f8f9f5d69b4faa4b8dd760e443b2ab5533bbdf2f4d0e901d356a7bbad12 1d35d3203acaad84ffc03f1b0aeffafb226fab9336a505c71094ce7dbbc60f63333bd9aea68f71ba 6e3e8c26cd798876a99b0763cffee761bf7eb52ceadd432e5a76a543f3dc39a3f4bba3be9d52fb86 87cdb6d9c8c26de8d0c15bf154e65b53746d345f5e3c6fba3c71688e1a5ba0b1507aed06893d9846 2113858dfc2e2a37f2018d358887306c107bfa233f7c8f5ffcb1c457db2dd91ceccf63b57a752011 46a43a4b476c8ed261f241e4dae211932af61a7e6597f3feba71b13a1a55519aee91171b05de76ea 210c44f5b2533fd7a2c9e455e32ab3cf6e54adb6b9b6aa3bb385549b093eae1cc350a9284cd92b5f 6ef2b1ac5b857ce951749192b556ad92e579ef325851c03208e87059ef5aff0597c2156ddae9b734 9d749794ace42d84c7abc4275b9bb428384fceba6832dcdcc7672496a908a487ecbc730175abe909 1b25cde7f25cd92876c8e230ff660b7365641508cc0df214b1d8e64bf9f8965b1dd15caeda15ead9 cdf58064f905c4640e05dbca48abfaa7be40a6ad9ef2807a5730e00c6e834c9bdfe632d2026a65f6 b7c17fe15bd2dc4c4e80ae46e9782922de64c0568e506b1cd79a45bc74e8bfd13e035ea1710eda76 1507f65b008ba59d03223e4f7d97a1b1cc14884344640eb31302f4e11309f4114406fa637f06f4a5 5a9442b8027d3bc901fd29dd06facb1701f4434905faa7ee3ac5e50df4cfe977d27faf2d0004ae2f a07f533b29f6832f3e7adae768ebebd035885ec5964fa705c7e7290ca3cf9b418ff05b700d53b856 16cea1d76bf7242ca356a614ce6b0d636e9462ff20e6d6178f042e2c8001600d1802e0a84b02206b 8900a8171c00f484f557878ece1700bc8eb20054b9b501a8c71300342a6a00c4461100e94a06800c 1802a0056400d00ebc02d0bedb05a020a27f60befaf74063e1ca4c9c6e97125b5b2b63f2fdcef787 ad69f8d98a808926feec9eb3a5437331782fab87cd635a4283bc9aabbd13260dab4b00e0594cff5a db6701883af200b42c48009c176c00865e2b0016f82b002f5e3900bea7970a01410a40b8bb0520bc b6031013fd14390510bf340090ddd30090fdf10c20b15f0790449ba430992f749ad17de94e112657 7ebcd9f11332d0e1de6c36e1f9769ee99e59e2d224e57e54ed6ceb5e11a71a46e68427dcdfc0ecb7 f8d5a1215a0760636afdd57c7b79efafaefa47f30df6c0a034048001f5680283a53e02d002aa0128 928f00747cc802a864c3002a8f150075fadb14f7f4773317fec12729bc702b872f5658897a303962 b4594d7bc8f0dc2af53aed55dc2463795fb9dc30bf307eb52de0962f8800dc18697f02fb6d3cfeea d01f17fd40bceefe6abd39f0fcd5a1bf72ef1cc8a75d25fab87a69ee5fad5efb0460e0b0fcef1572 3b13c0607af3af644301f8e29fa8f795cbbed6d3af09f5ab9dc6ce573bfde3ebfccaa75031c5acf3 af7e0348fd73f5867a822e433b7ed39fd763942f7e3c8adeeb38cb97f70fbfd1bf3ec803f9bc871d 317fa72f51f35e366ae88d8d35f1569bde9ceb0e1b6cbe2ed5ffd5777fd743f846f95522fffff2e9 4f9d09a991a081dd8fbdd21c8db36c403cfcdae993df3cc8ddf3af5b963ee7cc7b59ab38b7a8df5d dc6a5637bc0a4f64776dceb8eb45c9cc80f3853c55cf7a718c9dee8ccf9fe0eddbfe23e93acdb77b 782b91f3075ed79b7ef1d54ebf170df37b29c4468ae3ffe3eb3db71665da6eed6dc11c5094244972 120444410431206631ebfe2fb5effbede77dbeb5be1f7dd0a3bb477b5e58409db3664d887fcf74e8 7caaac4c3f70f6f7b7e8dfee7935cedf4a465cbd56ed18ba68e7057ade7656e4d9b89fd8ec40ef85 cc7a5f9453c65d7a27b7f0b28e5739373c1269f1b781396834e7fb779fdaeed953ffb613ae71692b bf2a9d8d9eef886bb34cf5d2835ad152bbf65253787d52fee25f8557ac7f4056fea9f83f86c0657c 13d6c6b7f1b8562d637501ddc1f10cf9a37b06872e70c26666f1e895eddae1a1dacd03bdb1e17d04 99d83e6febc44e38ebcc56be73dc068c10696d16c85e6a5798c12ad3ec68856d0fbb843821b96580 99cdc5dbdb7edd0f0bf6e9918bfcc424e6b3aed8f98b7f45de46e103bcfcaf2cfd55a9bb847c13d2 837e863cc438655d6478bc0ae4e70baa21cb3dbbaf6d76a5c16dbf49c9e4b4de7627df7d04e94170 ee29bc74de2bb766e5936bcf2a275e730c2e1f560c2fe96c452c22fcc2cf6774ae372f45943f533e ed619c7e9e0831984af5a9d9e836a6d00e05ff0b9f9a5adfaffe17aafd3996731f30d5dfa9bd47c5 80ca0e848a1f1ed285dcb3dbbdb0952f23756d02b491c20bd8596129e425c4ae3a59066d70b6780f aacb057badaee733b2ba9f0b4ff83c5b76b1c74cc989f93815ccef1d3a0697013c3591071b1d62b6 1f21d07d19ba49ef3ef173fbda84dcecebc1d358d4820911ff077e55fe8383587a3c1ee667082c83 e6256d75cbfbb77a00b6f2d92ca77601f8ced3926b584097815763169cd392e7e28634670921bab3 dac19dc43d2698c5cdda369deeceefc3b43dfa5c7a2742cc45e8d2ac8423246a85442e4f4fc606ab 05afc3621cb05dfe341646fbb2bf2cb751bfea14bf1d81972277d8d3cefbf65ffc53e557917ec68f e64d2a168a27f78eeef7ef045c6c74dc5eafdcad785976ebfde25c7aec90199803bad3fda3ac4c61 9bb1236cca8cc26bcf8e42e21e7c2df193409e6d260c9c9d260050ba7f3a951a10149a5c752c8eed b69f5453da57d6a0e6ad3b83b1d708eac751eb1c56860e91c3ddf3e282bb437a81baf87b8cfcc5e7 f431c5c7f35a6ddd16c8ec3b813a8e87cfd776f5dc1f530cb767c938778c16a57a238bb74ab73845 3a4e3bbcc1537af2d48ff2045800fd80871ba3f13cc2c371a5c1ccfde4666f7c753e3b799bcaecea f54e3760b4e7ca959155205bc353dffede6c8683f655732f11edbb4461b31b8c2da3e284d1bee3b0 dc9e74f2850561c7fe00ff8b7fc4f370d4395dc96979a7442b60750b06d9628e71cb597aee4fa6c7 ea6417def6b9dca4ebb6bf3f4d50f425725c39f9920fe6a78667983b77b47fde8291b5aacd861986 a4c3c195d90fb1997d75bd56f07689e3ae3c0858001a3079927422d3539cfcf131b267e1686b2de7 60c9aab5c79d7eba1a537d5db3becbabfd062813fd469eedfcf0786134725d9db7edc3149dd7d30b 2b00cbe25db8c47ba7b58a2e74370c299f4a0301f39f7e5afedcc7cd7c111fd964871f66c77e6f88 772703d7fb34078327b49e0e82c7f3ebe518006a79e74cebadcce12ecacb9e13fda22d95d64d2b51 9e1dab7621a5fe7ab41cf69b65626df6d547c9386e05dc704c8b31d0324bf5ce0946f6ce1388f8e1 f13a2dc82ccb94d66e054d1ac95b14be31d4b3ad0b5f238f28af27ecbc1e0525f535f7c167f736b2 e34165382c5d30973cdfbb8389d15607c0a36b39bcd2f3ecf9ba1fdad270bab02b9dddc65217e7a3 5557caf77eef8ce6fbcda15a37ade21433dbcbc27757843140f441ef32bc26bd11312be8f70984e9 e3aed0d5a90b4c6b2f234ffec56fc1e4e47326b2ce8aedfab28c62c5e9997c5f27d3ea6d3356c262 ec6d1fa368846c8a67d7b789e2a0dbd3becaadc32f42ca5ed077d9aeac2ba6a5359b6e7f33ee047d 435667e63eedad4ccb9d1e4c18df5d8d817f7e1b988cd67aa354427a84bbe3f5f1bd656b6f3a5868 11a0e4b43c7f45d5f851655461fca655c1fa3d3d7fc71f6ef293ee1c737d1d4bc96a059caf475839 a2bad92da834765b6f17df6723b4264f5cca8a0f83dc309fb317631ab256a946f437b701df87a458 33add3de36e1a8e81b6ea11219d8aef3bd0bf4ae7d69db235ece597f84f397ce80f78af6dea1b0c6 b57b5d2dff062c55e4f4b95a2ee280a2d011ac54dfefae9cba5b5a4ef525f517177394c7f78b9588 af00bdd29cf50f5025cc8db6df65227f2325bb91f338cddd71119b38b14ea776958a5f163878d7fb d01cc24cf8c5b286cb6a4aefba1d983dcf5b0df5c77313e8c12a37d719adb9d6a22b75d238aff750 67cf71511593574b4940945694edf4ab40c96b0a8965fd9d7fcb0dd785a55de3d095cc79444b2dc9 a5fee2ec40256207ce107c399fe7a01803f29560b18cee9e35f077eefd1ccf07b977696257fbf945 7f3b30eea66dce2a863bc9beb25dcfdbbc29fd4942a2ce24744f0754cdd1a66838d60aa378aace85 cb4a95cac5bd9238e84d5151a320af476143ee89154ada25aa2ef53520128fdbd9437424b225222f 8ff941380f14ea87afd0f53d661dbb446d10e6862d56ad572b7a34b2ca38adb9f71136ebed065173 bcb0e56b36e99bdde8f30814c173efd6e50afa73e4417ab77420b5a99317b4c2a5aaa992005b6aa5 c48e94e43088943a325afc4eda79b1917b71ee22ed4b1020f5975a5d3c21ab8ee85c5055b810d340 183ecd9bd019d420fe0eea0cefcf48ea1f7808f9c38906f274ea77126c6ea8977618b5a3b26f7694 fb906ca95fcfad23d2c6c26a9497131319e9939ecf1c8e7a9728031aafe075757e3631b5c2475d45 dd6f65a56ebd4db977ce0fe4e6141e4b7b5e98495673b4164f567c1207f0fd255ca6705518f1038c bf2f2e123f567a5f95837b6d1a19f78a6f4d2e241986eb1e1b14d74dcae45f1cd929c8ae268b393e b32e2778320b838a672fc4870b74a9bd5d73e5a5e9e4dc4f894ac3d7734d7da32eaad15d5971cf8a 52df23df21201b30434afbc814248b0b34096e1c6c71d0cf462206e743e1f2261281e0843dff68c4 0f7ebc7b97b9378cc15c98c50297ef34876c7c3f1f58c19e806ca95ea6d9520ea0ba8be18ef88bdf fb08fc079bf0ef492776853d122833a53cc2d3d6dd991bcd7d7f372796bddb8109f41cf7f455b95a 982b6041bcc850db2f4856786c8a59a3808b58bfd115ae14270b4428f6f88716b87c00a601f7360f df5d785c4455371cfb622fec8c75f3ac08be5bdd659f65bb4afb326052d7d9327a93aa327af14831 da7d4731a03521fe62d7080fdc6275eb75a6dee3848d1b47b2ec3e6f85bbad06b9bde99cc0efcd46 cff50a63b5f29a7ab2e1f503099edcf622b66d3c058f54abfc23f1db3c83ae49ee7d79f21c47020a 5728237d76968823b65cd7c3eef2b25c7595d1fbc4a44fe8c5e88907d2bbfa83a25ba6d6a78eede6 5717a0ecd5ab4cd96eaf43d97a8fa09006dfa1900285ffb01e264a776e523d3202ce31e69d728df2 202e9fef56333ded7bf7cd63a9562b892f439e3412f15ccee53f0d5ec203c8e4cc71d37b9e9d434d f0ebb92ddb1da49b307dba5b9b3a3cb3e6e71ad36bde6c7a67177cba8f2031759c2a1bcae19737f2 bc2c94c9a162e2c46dfdd008df88e70455b6f204f9aa6304796a76087255c1ff62c512adeef7e989 843d7a222d3474d4b93fcb4e3537bf9b8379bcd7d965ba944d7032166eb9ca9007e4d061a5c278dc 55e9dc86e9e9ed2bbdbbc979ba3f5f34a853658350ceea459117bcc293c35b47233bbe6a13fe6bf8 55d5096a755e76c246edd8e9ee7a001e53e716cebf4d095bf060882d88ca1b5b94c66d6cfe5860d8 fc10a27fb154901a37f5b76a776c7010e286e9bc64190fe76e10aff1412b039f2ff0dc72bedd1acf dc2fc36eb2636dba7fc9fae4c5a94624911336c4d8185f3bafc736d70927f95a87edc26d3cdeb004 2e18168797a058c5e4e0e260d56e73826a31bf42416377438c63bb8a40ce9a81adab3284ad9df6dd f20b5be37c03b62c08862db9dafe8b796b77e37f51329e5b7e2083d2ce2f9bd999bfebd1553b28fa dd4e845b320c59298a87b485e52d623c25355c480103530adc044dfb418a82a7f42b3f20db28ff44 cc4f79f061273660bbef6130d24e99b63bb8ca6d9c239cd6ad308a5aa474da424146bd21c6bd2010 d0999acde8e0ed9bef275769be8f2ef40f12b3f943dc39dda449f5d2fdce6c86411cc1766320950d bfc33fb5f2933d4a4ec4247c6ea4468cd11f8c4862995ab8081734c49c5042eb26bf941605482368 c20f63a8db3c6e9a53eb993579067f378b9f895f43e28750a352dc116072687d372d817544b1ebeb f3765a6f0ac4a9b62f65d5da6746c6574fe07051ebbbfd5cadafafebb55db4fbc04d6a3f44f9d959 f59dea8b1fcc6d1be93b1e55e9e5e8ce4b69c6e05118fb78ca56156a4ab9ec2f30a353e8d816b233 13ade58f2f7c635181a87af3903235b85eed550706e65631c8882a97c72aad8c82d7a9fcc8d59fe5 f1c6a89669638594c2679d2fce72dd7eb124cca705f9485e0ad541a1954fc1cc2cc8d3ca2f4b7571 0e0bc5522dfe0f4c6ac7a7f1cb3db137bc889a9d2755d3aa8dfa5bc28252c68958794ddbfb4a4c00 09eca3bd88b15bf791aa354b8558a81da003557e8c6b6841517938672ebbdf861d38d67a1260afe7 7d00e9dd3c00319805808cd213807848ee830802101f6601647e1a00c8c2490164d92d0048427200 b2923fff382a3e3e608b00f2e95d7e18c360cd71056d2458d65eeffca25946f5ba7c481e003f2d1c cecc6e79db122ff63ec7eaf7c7b83d3a959d6669ddd66b872b2b94274d97ceaf277d1840e5530340 6d14ff602202e8a4d0ff80f301344997007a824f1f0439007d43dfc9ede718730056e35d00ab17d6 1fac0b0006465d00c3e310c09afe0bc0a07ae3030afe61f458de064eba8d1593d449425d33b986e8 c7ef22ab7d9a698ae08f07bc9aec1318e70e61533cacbf5be56b8eb6d1cbb9ce81cf379375e7f31f f1288019c3cfef461302c0e2270f605ba50f60d7ed18c00bf012c0f13803701eca03f870057fee4f bcf8c17304e0db680be037b30ce0779a05f04727043a65fc9b06f739de60a05369103f0cf97ac1b3 8ec958eb15299c96077c1de685a45afb450374cafae4040f1961d9acb46a7e0d4deb5649204a720e 56cf5f69fb480338dbe0007c400a00be5224007fb9caef632acb1ed0a1a111d0b1e225d059b63380 804e258030fb1840ec700d206ed710208be30c2021190448b22301a4d4987ef07e02a49ca200a980 ec0f030d2a04e6380ecdbf62f477a5f008bb95cedc6edce1e1325c372b333daa5edebc5bac7e5a2a 00c7caec3fb57dcafae33deedce17ffdbd5f0f2d71de07ff9a7cd139403ac90920577c11a0aa000a 50f2ecf7d3504b2b04a8239a01d42b070274fb2c01341c4c009ad12e00ad82f50f34f4070bf3a57f c5e8669feaee8c6af3f779f0c5ab1f9b556cbba81259df2b3418a8f78f4a2e1d7abf93f6abad3718 fd23f7fe74e8bf710e7f7326e685fd4fee0568dcbe03f4908100fa92e70106dd8cfe0d6f404b1fdc be6113810d3004b1fc050bffacbd6435f7c3bff2e9cfd7f99523ff5b3ea5920fe6e7c7836b16fec8 a7ff64dffe5cbd9cfa088a66fffe9675e71e55edd19d4dbde0178bf073a936d2e54dd865df6484c7 e92a67f0eb5a75f5ca45bbcd900b382694b3f11e0dcf50f45afc9174e1f93ef9bfe15f85f7a79d7a ed7f930630e98faff39b32fcdf31bd7cedfe96f8d69d5da9f86d06f6e85bc9ec7157f9d893ae55c7 d12e29ea9b177034b4cf5b623a3a1b8ff5243b30c779064f9f9b5326209713b654f347afb66eff5c aa062e1ee8836dff5175ff2714e1bf92113eb545c8bff2e94f93fc2b9ffe82905f93fb5bac247736 a14eb79281deaf551bcb5db433fbdd537836ee44233bd0443b83230c3db9058c3861739a397a1582 3b122b413e04a0a0ef3fad80b5678f96b7132e83e9567e84eb0d385d5cd6d0b25a4ded9ad65965bd 85b4729ba1b4c2f603f18f69f68b9f6cf739fe5be54f3efde1274b7f064390c3949b905e8d6bb5ff 185d40b7146707f2bd3a65ddf7fee895b2f3e1a15c1ffb776ffbdee7ad797e2764d3d256be39958d f652eb6b334f357e92aa48b55338e13b2bb7ae7693abe1c8cb873db196f4f9162e22823b2cf2c1ba 301773e5d66c2994d199527c22b3eae208ffc5bfd5fd8f30f9473b5d625779fbc47e6b04a3097572 81a17824164363ffd6fb839d70eafa1bedf10ad6d0743f4de1c562bec2d27192788df1262176f161 19b4c36cf11e84b7057b0d5ff319392bcc4b93b436537257384ebf1bb5c18455a7667df16da3a283 89ee22f8387c8799b3a9842e1a9743ec322afdc5ffaaf2875afe1e159ee50c1e8ff3878724d476b3 56095a6f691b4ee1b94824578d6197410b911611daf8ba8616f951ab3f171eadc16cc9b4bc5975da 08e294c7e3185ce0cba959c5d75328158f91ddd06f61d6f7f321765a83130f2f13c1c357d4807ee7 a7e377a41dc65c61fc18e7e7d6dd9f49f2ed2ffee8d0ffabd4f1fbfabe80b673dcbf55f833967c73 bf72cbb5f332689e9f0b36bb94e6257f0fceaae11e8ec1d9e6335d2bef99e8a0eef908deece530f3 724688972bcee47aecf813cfe1bf6b04c133d74f02c6987c6eb3c4f331e626edb23faf3ba85f369e b2a79667d3d13ac4b25113945fc3dd55780dfb1ef1fc8bbf257e4ee4a7911abc468b3dbbb6fdb5f1 94acc4abbebe29238ba8bd9fce96e2228d1b9f4b2a3a9260213c3b0438b96d6474428d16dd20842e 72d07d568c71ac369db150ef78e3e2458d7c5970967e154a769e661f2fa36dae921b199b3e343c40 3b66084f18cb75c1fc77356ae091f2cb79246ec90954bde83075aef017ff946ab7f299bdee1df7fc f6b44fd11d1e2e73f4ab3f17572525ee95a16134c88dd3c9fd70ba056f142a8d6710d61c979646c7 579051f727774f63c96b3457fa687bbfdba39652f48647048d3e772b7ee99ef9ded61d3693ebe066 ddf3030a409bce573c70ba50c3b279e633ed9d6f88b75581f46a3f397095beaab44a7ff17828b5ef 4eafcb3a8fdc0fefe2eeb9464af27e319b01d3d8a4364e94116f351c6d4ac320b285642c0ec38b5f 1b6705af712e3546ed268c0d9d31fb99b75735d11d66637d70e716f6605cd88f9c57ff327142babe 707251fbbb906fc77aef62177763c092a90b6855ab12ddd7f463dfdceccd950931a5b7614d99ba01 f348ad77ba172a7ff15b85c806642fbfabe1c357d2ed408778774ee2f0765d0c27c0fa6104056bf9 5dc8f7ebc5d27cb487e56c3868b8803b92d2fa601c5e11e7dd28510eeb43bc939704cd1656826997 0603d752f2616055679b595f6fddd77df0d4cc4c93fd7478ad7c5c37ec7e85ec6527c7e8e11c9ee8 de7cf94b5124e906a83dde85ba160c8ed5bfb8739fb3787c4c86e535ca765e8bcae97d8c2ef76431 c93d17bebfb20e7dcf2828c6f0b44aa7ee68573d0ce813fd7238d0aeda22e9b5ac659212560dcdbe 56abfe3a5f92fb0d1332cc5d8b70ccd644f08d63d79a1ac8265cf5cec6e9d8c39f95a77e9b68359d aaef096d627674addbbacdd4e9c07daa85eb0d54e6e6bafe0752d9affe70ad8f5bd503f7f942bfa1 da74abfe9e41b3c369f28e9cc5b8ba198d47073db5878317a20f1e9e39713835dddae2acf4b06a93 cf08eaa10a64ee621737dba5cf85edd833c14099abde1b4eaffd5ec70047fa1d22429d0ae4a5f662 c26fc2a5d65d9f1e5ace802b2aff18e1caa29b531539379dca2b917bc8ea730dca75670a4a1b7458 9736f57eed87b39521e04e0da45a9277c0dcd49d44a7409cab4bcf3c18c110ef47ce20d8967b4e41 ad7d0333acc4b0d37ecf4eaf667f7acb1bcea306f62e0a8ef43a6791d2ef439bd7e9a2a768afc5d2 d442eb31d472d93b50e329b6504b4576ab2cf6f15da9c2d792ac0d314406f9bd22194b2994a06eed 265ab1f35df412add1b021c29054ff8bcc75196863f1edda6279010b1179f1b271edc5273f1d7af0 5c7903a770781a96da3b3ae61e692d8c53cfcc7a97c51cd0efd9aeaad33cd0d6c27dada3b1e3dfe6 0b357e73922aa4764f59365c5b91774b5fa9faef99acbdab6b194cb5ab6436a28278e8bfdba24d8f a4cf53b819086ee77c15b0035fe3afb6d4e0bd1a5ae73da059fbe144eef0567aa573b5790f2a7d4d 1661aea4677e53ebaedc3bcf86cef4aa0f2c753aef997bd9310dac718e7b84dcdaeb74a43c3576e7 94d419356faaa5d51153963a402bd55b4d90531f51655d522c69bb32479239584ec5c3f5b0126d1f cc7eca6d45cf0beeead0e26f0d5ae0bddddde79ed561c605eb669563f826c8319d4afd2f8edd3e87 ac82c1bd3e6bd3d7e2a4682be7d1c96baf06a1058776e5480efacdfbc430300ed5f48734fd6efcd7 22f6bc51677ee3a62c579dbc52c3d4ba9c5e7d586e880929edca47566a394f593c62354374f2dc50 388bca44189683257f73b223ef6338c03d3db7c931cf37cb01e178c84eb95ec672b757852d58b99f d05590eef5eeecb5aafe702886089270db1318a3d1b6385e0d916c787956564efc86a2feb68eb8c6 20957a7a4015358dc3d0a15a26fd955253d799ac5f8b80b413d1aad43af22dd141254c44733ef33b 6971220a9dd25ae36f47c0e67d071973af9c38e72671b4e772a5eb93e555bcce16eb29d3954c76d0 adb4db472659c52526094f20a362718d51c171f5879d36aba14b89dc37a2fb2e2afadb62eb9bdaeb 06b5fbca56f28fd0b489b2ab3fe6684fe3d48ba624d0c5967b757426f585fe413c1ee60f1165ee25 6118e740fe0e3511de3f50244fd92ac78580a370dde9d4e472fc79c4f287f2942ddadca62b9d7fa1 73cc4a00aa8c56b2297aa3566ddac85df674d3930b74d31cd7a97d59ab52bba750f96163333b6451 7756cd303ce905cfae3cb34134bca456a3b50d0d5cbeb91ae7bc74452d8d5569b7367aa2b3d87cf7 7b0a17a4bee6ef7ce7cc537b03e0427a5ce1baef0dc4e5d6399c159a459a2deedb4257a6bb7ab7f2 ee3bcc2add850cd80456b461e1671a82a725ca1ad670f2748f0c72100c36e4400573249623aa3f7c 0d97e805aefc90fa451f99ed2fabd644cc3385118eed32475cac52f3d88d42fd191d874a626c7469 0f68aa882e40991f73c2880bfd70c1e52b97032bb8f547777183f35d79aed599b462b4186d157e55 0e7a8badbab471bdcb34e4d74dcaae743d32d3fa33d2058f07e2da6f0384d74e90cec365b44ed081 571d7abf7a77e8f1bbd2a1ad6bf92f56b96a1f8d3363038fd367a3e03ec56966d7b2f1d7ce630c47 5ea44d77f3a1d43f1b9a30a24b32f7ce2f455698cc7a5d250f858cdea257f4d6d18eb4d95d3ca943 bc2d50b699af93d9b1da265d07eb9078d7e0082f1ea90429ec9d4e70ac841d00edaef1e8b2fe3e3d f1028936b1d9f32c7d5af3d91c2b37d807562e4465ac74f34a7fb1acdc786c4ada31e2b7d6e7fc60 3af1cffdcfac78d5a3967aa47e2eaca188758c1ef7661f4a57a9333cbd5d163ec3702d7e771693e7 4e1491f8e2ba26fc5afed4790ea14767d2e10a1d602c56f1a91cb471beba25b0b97ee530a983f5d0 64ac8d5095de2c90f5b49e21cd66588577cb4e17eeabad10ee77af57b80fe245b89f6f157ef84aaa dfe3dc6c4b44c8376578345cccf34e65a49e8d4b08afb4784a46f236df1bf10f4b3658914c157a57 6af0c4cdefd19d4ffb2ae0bc9938d8fc05845885ad2dd115281ed0baa95f910d39cf21bde7f93bd2 e07df709c156bed5699f4c45680f5acb7eebe2bc83d68835b6d07df6c84174c5c29bafa83b80a833 7382a8759287a8e9340751431ff8211e825d2aa8bbd5b61beafdbc0521cdac37be82a92adf7f5b17 25f8498db82893fb4c2fe72ae4f033d1ece4d80e8dd6d716065b56c8b707d1c96c637cdd6b8df654 dc22ac5e0a3de82483e868f768bef962b1c92e9a60336ff53b0d219b0b8dd2b066834ad19e81d565 e9ebb3a9ebb5a8590737965e07bdcf1c4ddb155ef5b4507ed55737e0f94314b6ba5deff44c5b3f9f b9e9768a673dffccaf95068dcc043fd7f6d8f28db628e7a0a89dee6bf87900d817aa3d7843683372 ecaf6cd728431e052af656aeaf7379abaec7b0576f18c6ac661e46eb5acbde6455fb7c7f559198ae 55cec218abe0cbbb58f651de2d93975b5a9a904ebe386511a6c4645a5c7a32cd4b99b4d1eb5ffc52 e22f28370c041db2b6d74abef7a4ee1735e9e6b7125a00661c6fdd3d7aaf952c82e65b2a56f524be 9d2d06240490190ad6968d66156d4b95d28bec40c5384711c5a2d1e30bd2c1d70b157b37c8afd0f2 f7b196af8f9024b711bc73aeb9dce580bd8ab780f6c66381f6161b00ed5d6efbc1a506b417cf1ed0 5e4633a01dbc777fe11fab2776209e11a88f3c5e057d1ade6ff2beb8ddf2afc96ede05f1fd77c583 f4a087832d574f0d46e20a0f013b8c04eb828b562efd79a32830ab62ce48a13c00fb601580e763fc 833b07c08b8e0ec0fbc910800f85d907ca1e801f4f00809f3a04c0af12ff41fcf9dbb7b4fd2a5008 40d50024872b00928797007c0d2f1fec9f3f8c2869c4dabd7da36ddc9d7a455d79a787384a677b76 29cc97941b8e037ccecf06489bdcf5a06eb61740102cd1956b07418ba5a459cfb568a3f4fb984828 01483a0601e45cec7cc0f3000a240680e6ca23e073a1cd01147d1c019435f300cae5e10f462280f6 281f400de0f0c1ae0ea0e65201d0fe6a05a016fffc6051f9c12de5e56fb69d05cb24d6cb1bc59a6c 77572f9ef3a313d30626299113c753641fd943e84d3806a8b743a972aba54c2111a708e0587b1040 831c04fc5e77873e5018c04a120e6050c20118d932004c743d00f34bcb9f83786e9c00ec5e2d0278 31c600bcd451001c7a4e00bc1565004ed94d00a7650dc0153e0170f533fe7075d2fac1496763d924 39a4a3aee13b2852c436cfd6c3f4468e17931df6b918e7ad67db1cffe23f7a9255f1279854a89b4f e253118500d8f7bd9cd8664d0178eec6fca3abfe157e435707f0acea039dea6009749862067496ab 3240b41c122024b20f10fdf70220c6ab17402cc7f8cf739ba816409c980d406485c207b3ce0f7fc4 68dee718f9346cc27c49795769d73cbc7165a71eda7e0f9c831b201956a8e740cf1b3edefda7b09f 29fa673cfe69bded99f693547f9aef1eb4fef1d27ec5d53fe109b9f61220bbc71b4086fd1a40be11 1aa094bc0d50f6e79ba626831c40ad7802a0b2d6e7cfdeef3540030100d039a8f58379bfc4a6a6bd 5cf1db11307005fb234697b3dbb57d47ac14dc38b5a0fc6e6dfab9cfd52cfca7299a4af57f6afa95 f33767e2afe7f8bf8285e7b55f2e075dd89500bae711007d914d80c13acb0f9e2f80c1e728c07414 1d6088eaf483f0f49f32ef7fc8a73f75f21ff9f4df579cfd0d6805febc3a8c93eaffc4df7e5dbd41 b126dcdf724db947d556efcea6b8759b3570f7df640425ba2edbf6e22a9fa2cdb53ac8ce17ed5606 ce5b8a01cf501851d981c3fa193c1ffd47f6ed2f4bf573fc8f20dc6f50c2bf05fe7c9dff5824ff95 4fff2ba6f7787fd0f35ce1ceae72b5db0cacb4aecb560ebd569d1c7149d10a730147bfe8c9f39668 ca67e381ead981699bd9e7db1c9c32beeb9fdca2333b5ee5f9f6f0d0d79703bdaf97f6113c21f7f9 21a0ed843b3edc95c60df7978cb0ad863b7b93b273eb87ffa59dd6f27ff434bdf1af822eeb0f3a0e 477776b98a6fc27abfbecafbd3e902baabdb19f217efcc7aa59f9f3f1f558e5731028f5e79d93a3c d41972a03711be8fa088dae7ed88dd09e748dccaf740db8091f77d3bf61a9a47e3145e1d9295db10 5ec9b59f361322abd2ff9182405fafcc5ffc3fe4d314bfe765af7b9577a67c0107b6951d48d33bb9 39e52bdb1d8925b3d8bf7bad749fb76abbed123b1f36da737a5a9b39e79c1e84fe2d8597fdd7caad 19b9e4da334a09b1376acb001eb4166f372616eced24cc6774d39a2d39299a55e7b7739cca6a2506 53079a6e75f5eb1dfc1dfff866bff8ffcaa78dc2ada4f50be72dda6d9c5c2007ff71c9e6cd98dbca 174b5e43d3aabacaa487915cb5abb37c98dbe1923e6ec78b084dc345fef3d8f8a68fce8843322f05 dbf54c01b6fb38e50fe7582fbe9fd3add2284da135034576d3a5c2ccbaea217636e289d7d91f27c4 0306828029fd03fa7d7ffff07fd34e7f3af4b07b3c3cc4cd7557b2e9677a60a3777255d5e2328024 701121123c17ee546726bf513a4eb90e1f83f38e3c352ba8368556dd7e64839413c15bc90bdd9634 09b1637f36f1d0611a3c46d363403f8edf39f438ead6abe37cdcebf833b166faa5c49e7a4a3d3b78 d54d761aa5467afc8bff2af502daeaee402f0f93f5964cfce4aa74a78b082e7ece8e77ff9c98eee5 3a354b9bef4a6164d7b6c530eb65f510db5f5b130fbea2c1c3cd3a4180bf9980be578571f4e912c6 ec1b36fc198739be5890c7de52b2665e75156f473a78bf0fb77dba3634db77d63db8eac0451ae7c5 20bb17d6037701fc24559cdaad7ef85be5e744d6d7bf2ad343b7a82d1f3a2ccc85cb418b7520b2a7 d06216846ec39b4fbc76b009022ccac691993ec7f9e7b9e0cf56e59a5fc61a9097e419ccab99e2d7 38365a932e3fea9597ea70e764d6b08f953cf73823662edab27683f32b0f0c3a9ad176c60554b743 2a99dab90c3f59b1db3a59025e3e5ac5ebf3f0c3afc46bd5488e4712d416ebd6ec385c4c71e19b6d 17af4b083135f109167e7ab4ee84147766000c1a137fee312b6fd59a5dbc7a72034606dba80ef75b 1c1ac22483ba59d9a45c4cfd34f05734920744bcec390fe3ed3af4b114d8ef8990d85cfd333c67c6 e37bb3b14a4f19e92bddbb6aa6dbc9cc6c50b5b3614e9effa0d53d663ffc4acd0e6b7ebf93cbf16e 195e76416cac3423bcd66c614257e644d0f524ca5f38c7be078ab5700425cac6cd62f3a70be04402 0cbce5b1ec3c9167c361e232e200064edadc8361ed42a0489654b37b56f9bc1cf4d5ce21e8d74b50 62f66c2d3376e765de680b30d27396b1a25f5075a67746c7b37627ce37cd3fcc2f3f7c359beff173 9d97cfc771d1bfa6d992decd6bca340c6fa26005dc42977dc59b329e8ea2e4a805f9863b048ec180 2a95574e9721337bbad65f76919f942c699f80fd15bc6ef7d5f7b3636e7a05c6349aadef43dad88f 71cdb064d3e99d526fdc1bb8d7a57eb983479d90cd9c364eb3b646f70c590d1f9d999a67a6676506 ac1f8a30f46f7f71d167b5db7e064f9fc98407f7d353b11f4d725ddaf1eb1cf95d2c1eb5bb43d63d 1f72c4c01fa2aad35d0c3dbb98ee96d667e676e88370ed6e1a51276f1c1a7cd5b06e0ed4cba409d6 732b09d5c34e4f41f7b8b2a69305c4d282bee86b4c3b98ab51783da88502fc5644c9fb0666286598 9665c57d4da574cf9f24dd721e922ef7ee52a3cadf7ec84e240f6cf5b9fa5c2cdaef434454a5e958 36087764f98cee12449f775ee99db263ff255a55aee3f641df9999d029d9190878fef6373dd72f02 faadd2aee8e4a0d3d09e98846893fc90d400d167d5696ba7a805e76c2a73b6e529e51913cb8939db cbb5e3fb25f530b129eebc8c17fbd422128e51fb243878f7f107c8ae79fbe13b17181dfbc5358ab5 9fb3cd76b69f440574ea6d95aaeb5ec3cf008a1849b04bfe86eea75ac89a66bfe21888d6897af8ac b7d6c9e330d33eb3a6a7066c4e4575ea5540b5f0acb4947942751449951839b9f4bf43405647d39e b4291d5da99734a6e21e55b6627b34fb54f4ac81023af9746ca3bc36e13bf3e289bb53e893f34fc5 fb5f1c19b45f5e8dc9ca2bb6cbc12110a9663cb2a7f9e120b8360c5b1c76bf1fd3d7c398318e2d9a ea0dd185a9fb6671a24d92ce4a9d5ef8a35a149c87221dc2bc52094eb5cf8cfd02c9f54d05930ca8 4e49cd438717f7415f1761c0778441ef180a18d45af3235bbf710fe056e7c6b1fd1588d9d7131eb3 e164736059acf864d9faeb3341da9cee3f1c0a865a49c223084c71d43c8c6b5a2e1ee2bb6ce8f065 c0b06a8bb668b6aee36eaf532f11da0bd2742da72c3cb5387d2d94caa1b29335a6f31d02d2662d03 92311c57c4fd7d0c8ad678050b59f5440883758e15b06147e54777dee21eb433e682ea6dc5be7bc8 8565f77e85cddb38d315ce6fbf5b32ac1db32c6d1e7fb0782cee3fec9283fa5d8a588af5522e7c12 e4de6f5c5e3397a20e23bbc2dc4c73f7ae4bbd612230da8b7f93aa502b898acc6baeac8de35832d2 cd5a3ce0af4c84c7c58790c94441c032a1c65f5dabc57bdca2c33de62b860ba4fbd72fc0beb3bac9 46aee4b3f99bbfe88ad4f9d42d577b6546e901149d2ea221ad33d886d691f04183e7c19d06d7c6ed 87cd9eaf54164a5c2d84c0123f786de13e1fe4b4d9a8bfe1e78631005f924e5d30e6e755a7b7a49c 1696acb4a5215b3ce8ec44c8e65622e095f19ebf9e4e379ee4f239ee59802a1cd3c73ecd545b4058 6ea0916c818d85ae38dbe8ddb2541932ca8a8ae935363dd2faad5da476d4b443b522e5eb1a228f9d fc9a3c368d07792cca77f270676f3fac31e9559d37f68f42303fd70ec38bb29839c56be499fbfdd4 ec11e24556e3c59b96533c2025e8c49122821d0c0197f21e4f0ea01937c9b31b0e30adef83809db6 c2275b7066f9eebc7bad75a57ca9c52426dc61d496c0d26bc750e8666ee150bb5931a2da657e473a ea0a203e3d004a0cfb358be854962b027f637702cfa01b81a7d5eb0fab80dd5463c70dbe0b92e315 5f3efeace6d6eaa98f8cc1db31b5b7b392956ab4a5a556452404775bc679326124ae9b0b062c0fa5 61771e3c92aed405f74cb221ae8c6ac86f7afdb04b742f88406a0f2c61aabf7e53e40902bf9727e9 1ce43e7161e280e8c4b975675c929e1d2a29c278589fea7877a62cf1aef7b8e15d33bbe25d717bf9 21e1669bfab4b373f3be211d0f83a83898f7b7aae2f5c88664aab3782c4b2dcaa6047c56fb2ab7dc 4459632ccfef986e8529f71875cd8ce84dc78ee9e662b6a6aceaf240b5b3f7fd735f2d00247a6f95 89cb428108a26ae39db11e743b34fed6f1708c8df0dcdb5a60f1b478c34ac5410395155446e56eed 1ba1f7f95d7041e5baf741ce3effb04892180cc390c98d32767a7044589e9b08d11ae900d531955a a0c9c2905568aeab9c3b5d692121f4466fa3d47e3e94c8d369de2731f6e613a35d6df6794a90df79 5a67fc524e1d3a74ef78949fe47076b6ab62b3d6b38509278444979828a2d5d9cc4652098c117065 9d60b351aac2d06ec3b70fb21fb50f48ed08430a92fdc5bc79099bc1922c00ff5ae2ad75019af56e 76cd53e783765fda9d0585a73c8e618bd31d411b7a1b213127843a0f63d1c1df4945c13994b2b1d9 48f2b172c989d1e5729ea2b55ab247d20b7043f4513b076f1f54153603076e1f6bebefe5d9468c82 d6720f9cd7c2ed6c037917250f911e4c379fed9c0f91d27c0f91547284c8f6f4f043ec4271fbf78e f7c1b4ded9f75bcff74c9f444f4f5167c5be3828620a973b925d461b4f08329372df7d6b1d86d020 34296420a2bf4112dee98204b776c6679e4985c336f2da87ad735a495a9d467307dd76dd0b44b5d5 77f3f9dad49b5df68536a673946bf0fdb90dceb3c61cace0d1bd9edc75b49e1c34b79e4c725ff11e 2c1fdb3bb09cd4b73f44cc506879c87d02d8c9a1b8378697ed4c2bfa6b4f6e3e5e16ffa8826a57be b458cae654a2f3ac6f10ac3c059b701b142bd01d2dd79a218463cd5ca0b38d9889d44671bafcbe3a 0c5cf4de1e583954a7f5954da77510304eb5cd74f5ae41c577ad6a2918568591855c198c9a5ef9f2 98efcba38903964703d7285f527a5e41df837505cd8cf4872049f390fbe298b7d5ccee07fdf5987d 07b4b27aadc762c6bc1c8ecb3f547afb287384dfa6486c1e0c1078dfce9ad07dd2a87c469794ab41 937bbe0a6f9bcd8adbea6015ece9b065af3b51ca646ed72f05c2d32b464e392ee62ff8a62012fab5 507a1cbf690979a5db427369ecc9b9cfa33600b6c9e9026c572f22a7d3672797da629c4b1566fa83 df3e7a0d67b1025e26e62f0eda8c992d65f35310ff2c7ee6a0ca71ab91e7f4c6e345e14e20cdcf2f e8d132bf1ea886703f566aad4a0e289376e75628af16f75ce368e7807e775603dab92b0cb4f318fd c140fee06203ed4237fa20d902ed26f3fee0dc02da902900ed16ec03ed762e03da954b1b6857cf16 d006b8af78ff39bac90fc300621a964924af5e108e8faa329dadc461793861e7d168409d084fefb0 edb9806c6b7baa45e61f4843349046ad3557cb650a8a81bc1a8ecf40fb887d3ee668bd80f6f35d07 da2fe23ba001b832663eb82b000cf3830f92290023c81e8099750e80bb4cfb838704c0ec780cc09c 7a06609e870158101d0026ae0b0026e9e307423628734fd03cdb9b9cce83f159ee5b6eca7737bdaf fcc0189ff906414fc51eaa8b3db175a3477443426768adaddf9aa5907895f2205c7901b017de01f8 7a7a0148219f0390865007107c830348a7c20208af6a00d23b0e0164c4cc0124de9e0064f6698990 5986fe04e2dd4005907d27029043e106208f270a20c7830d20a770032059f3f941fb6537a527648c 81cfe472252e6e22c1d95bb61aa833f296977d2ca1997e7b1853726341d14c0d4614ac14613e9433 65ed176380e8d8e73333ab02a0f0a20ea0f4060450ebde04d0a0837fe0f300ba7cf600f4a9780056 ce960046c9670093f3950f460480d91d03c02685c507e90bc0565107c0d2c100c0cefd23806d2a3f b91bdb342b164c23682fcfbfaab28dc6cf5f9808edf06ad229aaf4043eedc841738674f4da614bf0 a569b5d5c9b58bafc63fe57c75688c50b17f8cc7f19cfcc7e4fb537dc102fb8b9860751dc0076f1f c0536b057480e215e830ab3ad051d52ed01934064067f5d8009d2c2d01442d660102f5038060c50c 20b856e303b2f59f6234b4cc75376c742569cbdfa126217c359b16c0e25ebd4f178d9f291a388d54 f49fdabe65fda9e827a9fe6b3bfe4757fd09bebfb0893f826f3806880d9a0264fdf602c8c1f4fb1e 395b06a81af6cbb9a5d0cfe544a12b10a0585f02285d8f006a085f3ed8d63ec8c01edf27e93f62f4 1f67f4ad90c36bbe74684dfadb69bdbf979c9f29fa8f51fb3f0afb57ebfde9d03fb9f79733f1539f fffa7bff27bff7cfebdafec8bdcc0da09b2d08a0876f116072a70060c049063078af01301d5cfac5 f4020c31d8030c09e63f808bff912ff053227f02e5574dfdf3eab05f46eb2f64a0bffbe0fcf837bc c1811e41618edfdf5242dfa3ca91bfe7b59d749b8167ed178b702bf5cb837f63111a7fdeb78691f3 0be871ebb3f134b333343917323b8fc3a74c4cbfd9b7847fc256d6fcafaefbc3813e64939f73f6df ea90d2bfdbe1bf82ef9fb0897f4206fe444cfcde72363fdfa3f2e4799bd5c3c275098d2ad7aa3d6e 5cb4f3b87dde76c6d8d9b8c76476a04326b3de0bfe94710bf9e416f6bde355da5b476275191d8246 2eda47edf6669f1ff41fbb927787b6f28bff0ac4bfbcdb3f39b7df64843594b4f51f7ef108ff0ff9 f417de402bf7a8c4dab7594d1e5f974d637ed132797d86fccf0f0a87c4e584cda0fbd12b23c02f94 a15eceefdf46a1b4670f407537839fe076891fa14d4a25f07acb4eb0f420fa540a277361e5d66f66 e2b56ae3e5c3b1b74bfa5a2b2e225247feffc210fecd17f84ff9f4fee6b9d6ada4b73a17d0797633 ebb1934ed8746d1c89c5cedd470ddfdf9506fc64a33d2bd11a9a9ee2145eac162bb7ba5825573dfe 5a65c3fdcf2abba44f8bdb22c256ef9f4bd5db16e7c2f30ace947c1e8b5391166230099da90976d6 53689bbc22bb9dab45f0f152fde18f69f68bffd224efecdcbc5f52e8f93865f4a870082a4475271c ae8d5f06316db653bb20e22b6cc530cb87d9e217110a488bfc08d0e6a5f1d9982de9a73dab463737 d60b672f06174038352bcf780aa5b55564371abb30ebe397103ba9b989874d1bc1c32fd001fd32fb e377f4db8537e60ac6e56794fd2fb7ec7fcba7dac1d91fbdbcb0d82ee16095c2f16d9d10ebe96111 c1ce752edc2c60560de5520cce84fad42ccbadc8aeb3689819f4b723085d486626575be0261ea249 13e23cd083a033e807f43d74c711bd08c6f9683ff7c5c26beb9716dd87a754c3da28ed15c911b89f 3ac3ad5d9b0da133be710fc3c6fa2ffe96f8cbe5f86a935756986d40af6726446a688bfc801eccaa 93ea786a161f7104a7d92ac476d7c3c46bbfaf41803d5ee3b7f7cc8fd9e7b33cce8765d017f305c8 2fcd1aa8b71411c2ab2608334a554e1ce975fbbb5d61b83582c1103aa413d746f3e9201bd1d701f6 58d61cafcb720e99bbb8762056a23f604aaff0875f953fb5ef8485c67cbd257963496f3366263f00 6c6a16d6749869893cf15ac977d619d097f968cc3e92c8178164e92df9dd7694caf3c348af1eafc3 adbe7b0ccdc63de71efa97a26bc3b99a0b67cdd6c0c5dbd8002fa85dc7ebfb8afd382596cd70e5d0 8a76e6dee2ac42a13faf1ebafdf290fd2eac986a7134316bfbc1070b23f8e1af18bd9b3599d132d0 613ed6e5331ac1af5c7de2415635a0cf63d49fd116ed2db9b13cd2a9853534c3fbd83de2cdb98bcc a9cd6048a9a741a7e27f777d3bfe20061c8a3d95ed57a3d8b0bbf33662c594445bfc6b28f6175a62 f42bb7826f6a12979860657b330c576cf60e4550d5337810e8836cb5d031379cffc51fedd4638d45 8a9693af1372a6be08351c75ca7490f39ab02ff170d94b2a87f2a85700b161bbad8983cbe03470ee bb6268bf2bcda5cd0ea89d35cb4b674bd80e9ffd2519e6fb4a795f3553e7d23475ac881adb599332 4c53127b87a3f3dd5cde43ba6b4f77b78d44c74defa679af46437db207456576f24405faf14a99b6 4789c29d8ca5c2ade4c56f0162cf9be166c98ee6e32833413de0aa05d64b9d363e3cc872d3cdf2db f2e0f22a614e00ca5fcdc6e65a63db12d54dd057e2fbc25cb72a5bb3316967c64ee51f46eba2e57a c7785ce9a1a5b0a19f0f7b44ef302f4abbe79a824619bca14e0ece48cd31e7a5c2c7f845291ae7cf ff7cf46469858181a45e8d6f1b25d53d61f50f4c3ac9ec6177bf3136c66e5eabf5c693a75a327cb5 5110860e5121063e2eb41d66b2acd9dc68d6b2ca932a6fae63da34760fcb37da4a3ceb0d80cb46bf 18b993de39146fda3dc0be97a746d7e892fa5acb7595ed586d35778f0945904fbc52429a3d591ef2 23292d4e1712a814cee2b636a98b90c1888275c8c602ecd0eb1ff8ac0eae7e38de3b936b8a33d82e 6eddbb41c08ff2dfeb66d43fee84c19d7e91765cc1604b8a978d7e9d511a667398b1bd5350ede997 333dd21e4521d2686bb052c377b857d9f4745566f8fda508f752415eca504d56aa744bae665647d2 f1312b6ee77b4d34e5e677a40987acf76926f05dc6bbe36e95bbbe6f2ce74d239f230568c33eeeef f41fecafab036b4d1f49b798df45c3ac19fc4cd1430cdd490eabdf69ab0a828869d85ed3809b8d5a 0f933d5a7be887ef42befaf6df03359f811345e48885526e583b59f1c767297d8def92bedae6a486 f6a8889fe94a536cf918263815b9cb9fb581c20fb1cb80bbf9ed98235fce919d70c532db2d24ddee 5456bc2e5fbdaebbdc3bfbaa1c5d2edba63fec96eef0b998bf0ebb906e54269e91a6d6e06124b2b5 9456b4b9154b682feb294ddd83819ac6341aa8caf53d5911c3a5252bd7eb585a4bd5b9d438c16b71 37608f623bff994e1db7c1770808682b2af1e767d6e03bdd07c2ddf310cd51a620b393e3c86673e8 3eecc679fed02d8aa71223ad7a3453e15a437a5598a574729bad69b56faf7ed8ecdad4635edf7adb 499ebc4f4636beb49d28f0bfab51fdf4b4600cdbbea2bab7e020edd32ed49579eb06cb89c608d27a 669be2ee188cc476773b159ccd6325a01e74e0874fe8cc7712e1c58deb629ea32e419d0d89599bed 3eae64375641b15bdcb07d468646df3934bd9a1477b4d61d17a8cd0c2529837cba54f3a4245473a5 a77ff147df8748771794924b38c425cfb6c5754f36fbf911d3c3a729aa31190229526b5e97eb75bf 293545a02bb627b8fe3b691b6ec08f087bc2dd97d1821b2bdb0dfb3a5f4f6c1857ef6cbed400baf1 92a9744b880e318b73d461aa9d2d476bcbaa416d6bc298827aff87aff7dc5a55dbb676afa58b0925 e78c64300714c59c73d6fbff9031e79a73afbdcff9f358ca781dd22548ebb5576a9b6fc4232e0391 6ade38e1521eb784f6149d2710d7423ba47f8fcaa7afaba7997b8f2faeb31f949873dc1157b566c3 e55da7ca109ee2bd9f53d6994ddf985d56bb65b391974a060bfa62a5d77d3bfa605168685f9aeb6b dad89ca8d39cbf528d43745016547c55eccbeeb78aabc0e36c4ef6732822ed1a0a23d5a88e2a1e3b 6b4fa49ef99ed091ed257f5f7f3e7c581930bc980f9adc2b7accb8572bbbe25ece7b9962395ae6be 71d813f7e15e188dda2aebb7eac14075027e68aba9891ceeb0b67bda636643b0ca953b28c2fae049 525a52b1543428de54d5fcfed451168dec5081bfc45c5ec7ca564651e72cedf69da74490c974e5f8 39c122ad0352e8cc50917f908ecd87e75d877b73f8cfcbc129fdd1938dbf02c9eadd628dd583e184 d52b9be55fe0678b8509be60a42cd07dafe53446ad9ca9b492da8e74bccf56501d5baf71e6613bc4 2a779a29ebf2e306abd3f60a55167b4c514a52e5b7b626fba37a43da23c35022c2cd586cbef70b91 1ebe77c2d523ae02b7e3de7c5fb472bcf8eea3dc50dd701cd8150cb642e80d66d69a8e198b956ff4 32bc63b44bae02da852b63daf9d617b47371e7297ec56da9f28586d92bd877bbd76adc58b5885670 7be41c0f1258cd46ea3a57b93b1554fbee9f25d56c0f8af286d08a52bddc63c553efa08b4c21e70b dd16d9e21f37becf4b4665cc7df2f525375c8c0f5c86deff1656d84af7fa657342b9c8588b0a45af 4a7d952e07d780dae2fa90c29ac71359bff92592acc90e499ab788acf7993959af63b31453e21281 813b5beeda9f831cd7ab936ccb8f0a37c75938b0661e5762aa432b018fa9e66a5592d1139b175bc1 11e29fd4a5cc4b6356e0d48356e1328341c056be931633d7e72163a39f09533c9457b44791076a1b 571e5455ef41e461b642488a2a0a497d55b10996dbf7f0de40f93de6833dbfef3c36e8ee0cecb96d f7b1e7643bc59ebdf904e76f87e998af6bdff030eaee5a469c1fd5a8c3b5e5e9eac1b5fdce4b333a 7794d7b2731257cad0a42cb6b6f93c2f5d9a19d690388881591fa7bd69fc8bffa07685b54ee1adbb 4b1e59b84136ee42485c4c23263a85de0abfbb93239eccbc9ed82bc4f398fcb6493456e71a0aedb0 066212c339925f29dff2c21755243fce77917c20c488b131c6883196e391c622bff94df76e56768d 75781905e17ad6720bd5896b11fb8b5e918882a02cbf6542aabf1b65fe39bfe459a3af64a89d11bf 88cba695c1efcf258a8bf683c5a24d56c19480b6d0f8a957517dd0ec20d36f1821e6faf29b789497 58fe582e35d8576923c7a5121a7f79b89e33dce2c93e0f8bf4a67e2b5c0d972b36ef7aa3d8dcc551 f1044dff2082a3dda70332c56d9d18ae631fcc5a2d7bb31878c6adb8abe819178832767ffeccb042 6f229739c88ff3340a01407436fa1303a3cdb5bc1cb6924d6fe7855210660878ff2e72707d2869c5 73a662175bdb61a370c39761a17bcacef24f863be4a549e793cb38e352b6b22d88d01c6f5621eb24 fcc2b3a0a20765212bcee8d0ecd26d6673d4b293cdc1e376d83cf8ef56617ede5679a13672ad51a5 653540dbaf0cb485a1c2d84614dbef03c9c54d1ca1d71f274f5c9b4b80a972e959764feea5d89627 bbdc6825fc9e26ca1a35f995cd13cd1c64b7c23204333b3ae3f5ee520611710bfceed10077e74380 7be436c1ec03705f25001e002bc17a04f0eae40d707324015c2afb09dc0ec0c94abbf3c9965ef503 37f89dd0fe48316307812a1db33d1ed5b469a169c8f87728f1d16d4533a5fd05211911cd63ea2100 654f9c3d8aedf9e7948346f03a83969a2b40f0e131c1eb0d0881cb2768628030ee02204cc14c30fa 3db70608ab384ad0da01a24166126c6840341d07102d6a0c082f93fc77bfa82498350151c90e938f 2cc4ad79ebf4aade8bd59d970ff4898d4f83b012d95e4dd9647c2bb598b345ba4a936d24fc3db182 69957dbeec3d3050e4f2d23d3bfdd8079026bc91a5780d48fa7b0624a37d01a9c645407a10994016 01d99edb80ec149b808cfb13408e0bc704c32c20271c0bc8edcb07e4743405e4ac0d521d7a5e5700 1989bd04d37982cba6811fbbaf4061bd835bc2fcb9c9b5d4be66a15a43621ccee60c4455a9c65c63 b158d5d132ca05f9c2a33afe668bc5c91590cdd71e906ff60c28c2b8024af46ea90e6dcebf80aae7 8b80ea576940cd0e0aa00e8c0ba8e7b003e83c3a4b105d004d88c5041711247ba20e68b9b202348d 6501ada23aa0596894a0b14f30bfd6baa0f5f12bd0e062d7eddaaf16a8640426920f23aac5c739c2 a36b2ea6e3ca2ccf950307c50b4f8d2f42ab01f60154adf700d46efb06340767fe127cffb88fbd02 a02fbd1260e00f0d18dad501a36c7cc0b4c41030c3f5328d9858690fc09cbf0860be230db040fd85 4da0fb046f18b0c8c5022c3a8901cb07f7047350ed5fb75977258eeea6c0f95b754df3635114912e b3f10b55426a3c0dc46321a928d41f24b4ee6e8bff970ebd28127fd98ed3b0897f84df3f8913d764 24773d001c728f00a77b1bc08dcb1fc05d0f24e0332d1bf08838043c07df00afbf08c057567f3cb7 467706f89af84cb0840388bf15ed6679f0fa2346333975c1cdcb5444b112da4217958c0b73ce45cd 964eadbfc339fed1a15349f54fdef1bf7326521dfa6f9fefbf0386f9d110083deb044408cb03d17b 8b40f4c31a10c7de12885b2103c47b590412f46801095d6c81c49b20c1b4e46ee073d9ec578f502a 460b5faafef3aa3335c39be16356ec975aed7390751fa8f4d7fefa2fb9371dcebf46f29f4ce17f45 4cfc27bf37f5f79ad73d9066e3748d40e63b2290275e13c853720de47d260be4c749064a2eee0005 373640113e1fa08842e16f836c9a2ff09f27f7ffc8a769c8c05f31bd0996b7d74b2f66524bef4f1d 946701f98c8b75eea9adbad2638a74b587b11b9af705d177eef6691cdcd6ccb07ef36ecbce75276c 06573c3a4d2e47b5b4bd5013e57eeee4c7c5d3dd55b453583eb6fe89bdfd47dcfd49aa3f8f6caaa1 fe937dfa3fe4d334a35573ffce992067af080a0e4f6d69dd1ec6d6fadceda306ddbcab50b8ee78a1 7cc5072c76697c29f27cd171266d22f673a9fed5478cd28f11c2da876f4dae1ea096dcdee77bf668 074783ed6657b97d37f8a2caac9ba5b3b5ba042bfb4f28c20f690ec2df5dc3fecb7a9a6ad37f5a87 c9e36fe509d92fefbe405faddb9a82a2eb8ecbcf2ed470b73cdd8d787b7c399dc3e11bd42e07ede0 dff7c64579eeec07f1de7a9fec7753cb3ca1f5d1b8e55717e7505ab19b1bb10cf10cbf7835317dfe ed566a69ceed73339e4d15f331cb8f4fc8ff5f1ec27f0d350b1ed3e2b270f34e75ec72142cf6cc8e 79f9f8b219f310a398bfb36ff3eaa606b4df66d65472cb5eb1eb7c6729ec5ee12222eed1fcdbbac4 f39879cde650efbe9c19affb76ba50eec7293cfede277ebe04c63b8783c7f8a6c9c64dec6e8f2ecd ea70d461ca97e1bde76587a1606487c24b8052fc1e8cf9bdfef7287f3ad5e5c833c7635498dcf6f9 a6f0dae0a3c577d52974734b615b2fcfbfcd64f637e57966ba9049610ac7a432f1b3983ede599835 c65765373e7a6435a6927a2e955409ac33624ffc601832d27828dceaeb281286e7c1377a66067a86 c7fb536356e9e797fa205cb8bb6de894bf97d42afb0ffe6b88e78b3459a492df9a9a483f956311a1 cfe16cca4ea65378d85e4f9049ef34c617bd474c6ddadf117ba825bb80aa15a357a7864411d72106 df7e931ec452240ca05128f7a7dab4d237a19d132ecc63358497af4e6fed51710fd979db6e8d58fd da53758e6d1ce950f7add9bef42b61bb23cd166df6d39fb7eea3fa2cc51fc9fcab4fdfc98eccacf7 f9462939906b4a9f19375f992063da8a9b70a93a62f785ce50389786917c2fcd06dabbb0ee4f55f4 5740f5f313f41a3a79f4d95b3ba54f2f9962403d642315bb358c2f77f14385e834298bed5097e462 ea7043b3cd3eaeb5562897facd57ecac9a4a76f76cc4b6433674f86bd5a781d6a99b98d6fb83fc81 edfebd1471ddd1eaee0005c9f77ef9b036b59f0f3a6e163ea56188cff148be6ec50134582507283e f8213c3b347bc8ea10766bc879d439d66eb30e75baadda1de6b66bb3b7cfa9750f73b796f0cebd9a af21fe730d3593dabbd0f84e0caca1e73da6012de65add2c3dfd5a72d58735f838dc547da6f40d76 bd0d1fd444c20ef0b718a4f08f43c27f182be2968ad1ab4b656ea7431c75caa342245f6edffe546a fcfcd0a103758a3dbf10935d7c1b091dea18eb6df61a3bad905fd49aafc1badd54e0cba01177ee71 43afc0c98c1a13767573209fea05d97bd49c7507d44a9d1d5c0db25f32d8dbb814d4a9b6eb9fe24f df6772d6afeaf4baf627eb3ec14c76fb37a5ee4a3da5e97c04bae17c70b4fe473efdedcd79bc872b 710b1d331158f44aa15b8c41778fdf6f5d0284b7f6a5d102adfbf7813587b992d018dbbad9c87e3b d5bad559ff8e4d6dc57d47b5f23c3fab6e1beca68a7de563705857ef0185363efef9b1c8fbac7541 bd3b8c709ed0312a6e941dd59caf9d1939daa57eb2a77c11b6f3f3bd612d1cbd6b3925f15fb850fd edafb2599fcc6567b2d5f2f650964e52b8baae882ee57de1b6a0d2a0253dc37b73b876de0d23fbc0 eb308c4a354457ac6af5d1a907472bee06d4793df43bda65e673687ee3857de4e889967c77a395fc 769576e7b7f4ed8ca129ea64cd236bcf48aa6217da76d572a1c3c8dc58fcd1445787a251e7fa8641 3cd16ee53440c3bfd028f44e22db582ce5cc384c632f06f9fb58ee360749cdf72eec4acd2c4e64eb 8b4ef37779d6e1bef8aaeeda7d2438de1f827f2551d37b7862d513ef66db1d9acdc85589e9d4190f d72ba7a25ef6f66cfbbdda560d7e59cb3797b5dc650d31b7e58831b1de4533ea6f24a89cdd465461 b69f837eab8d7e759ace9f2b15edd9be76b401fbee6b03e41aa63854c2f96e6e1c5bfde1bb74f442 ffdd55da8ff58069569835522f836caebaef37bfc1c92b3cfc6b1894bd7eabcfbac3ed5e773366c1 772a27ba65cf55e57793b68b5b676cadaacd85557e855b73bbd85d4cbc747c1a875b013228812855 da0b93aeb04e5298f7aedf407b09f24093a3d55e8d33724e85cc9ca64c0bfd7682fd4099bc97fd14 3bcfededa77e57fd6d261a57d67e6a8a6ea9338fa9af8418ad36b28fbc7fd394aff7141e4f7754fa 649d89a152f63c6a29364c6f1ccb1bbf1be60e2ff44c3c2246c651116706b53137954b503b56d8e4 ccd1ef830dd045f8f1db695a141408f5fbd46555974357cd022854cca0bd9317472427974adb6432 590edad27ad2eaff855e106ea8a3b09b1073b23fb006d3a073ebd5f566ee63b0359caea1012b9d0a de60aa665c4dd9ff4e0127cf6f3336dcc4080b19ab92893f03d368dac36ae57259b62b9df81de98f 1c98e8e18158696f8ade6bd1d7b96940af7d547d3fccabd9fa1557cc4b499497bae3c8a5d9a22b05 30b516f7fe3a23d6b1c1cf9f26128b7223813b104e0a1fa65885577c1f5f2fb97ebfbcee04edc1a2 a237e0b6cd57a94d13f75edeace042ad22b017fcf065f954f03609748518b4f5e12a9d0ba9eb0fbd e2e9e2beded48652ff973ba8a9c54dac8ebdfd5cad7060abccfa85b36249f4535eaeacac5c465a88 14847b5edc7f0aa6486a4e5b684d8f0b81b11a5fbebb9625be1b3f1b7cb74bf6ff42500e976076df 8d06c86710e25cf3b74cd48a69b552db8514e7df9f26eec6d361d15e8c9fc042a9e0659c38fe59e9 168dbcded726b4f6be7f544d958ab63a5e313535c7aa1d650eb5864ad16c4de45561bc92cbadcd41 0a6ea79b844db18cd828a8bfe25638bb754660b69b0adf138926f71c46734e86a87772078504f653 1fd5d84f23d3673fd633643fd2b9379f9dd7bbe1a8761ef4a8be536d9a7c59af9e0a38efbd651177 cc53bd68f9872d305aa8f4aaf064f199daae1104d232a24da8b9564f54acdba422af8c83277b44ae 296d5bc59e5455a5583c4cd4b9d83082ad703ef62f024bcfdefc3d538479c1e0296eb06869ecb774 aab15a55993093e3e5c118ad35c7189ef7db698ca1ad42c66027bd14330f8a7691319c0f3af79558 6d38f36225b8b610c11dfb286ebb6a001ba74ef0d19fc5f24b1b650e0fd5c81fbe4af10621b267b0 acb43ddaaa842b6d5b6c8c0755910a0e9d54ee3e9c07021be5267c08ca2bee15f0472ec28227fb6d 8c72acf6bde1ccb442ca4c7ede0a68077e8fa875d0bc523eee33945f407cca7b577b9477b293dff5 a5d6991c4ee636cd91492df175f4fcfc758ff3fbdd3befcc1e25c2c2aa2a5ce1ebd247d332cf9732 8fda77d9eb6a77096f74f262e3bb23840e7a1704f68957f8d0165ceeb5d6ea5ce457bbecf7d11bb2 717f3e63b3c5f39631fdcc9529a002a09d7efba77250ebcf41a0509d74c9daac3f204e307d269ae3 2c4134bb0b87685691ce5f30f2ed31bba5b7e1eee50d5a63b354ab91dacdf0d4f99ab7bdc29b30ce 11096ba336f955ec68f4949279c84da43eaf5f98097fbf7fdfdcbbc89538a5ed50ec180a25569fae 0d66465c3cc63c430d7ac9c23dda85e4a44234ed25852e1b07b2cecc9fc4a9f72d102dd160f0eb70 62e03c8475b1beb9dca3ef550bc1c4af6961e265f77377637df8d8c6c2efba358a16d96daf451b51 b3489faac1adb6325c835a0816ceec894aef5a2ea9c60e013272edbd449a826fdc5bec9cd831e35c 180b1f42f432da97e97219d054502d8814262806598ff45ff31392549a0da2b5894382098e63bc7b f8aeb1a7425d3069ec67d0516e81a119075690caa6dd28cff1f2b26c1d3ff9f2bc70d5cbb38fd944 72a493a0a837a299bedc749e1815d5b7c1a6e6cb6aa3e2c07efc4bf0372ec698d095eca7a4940af9 8c78c21a6fbe8fdc6f6c85314f74593eedc846f67425da5411c26f1db284f77497c29eb3ba800dea 331dfd9c370eaab1a71a9a79147a88618993f27ce56fcb30bb7894bc7ef6e7eb84b79f64a6826b17 af78ccb627454a663ec54693918a87c5be5a3c449b2a8cbdaeb5fee613acdb99d57d50a3ec7ecd83 7a9e61d52447a884519fd4b2e4a124ed952f24f0b0f6e632c4ee4ebb2fe24452d9e14f17c006bbe2 1a85dcdc09316eeab7bc30aa8532bc68a3a5b533674ac8f52cc3bb5ed182f117592d3615b55ba441 7f52e8048f43fe7ea43fb9a86b21d9eff3a46675c5e940d31eb4cf427704cfeac8aa928db5cf6fc5 23a9d9324eefbc53374db8361d046256ad3a4e53304cbaad8bfab0d4a594a5b72a8badee3dcb8db2 f89b29dec23bd988df27ece5a95bc454564b780fb3c9d1b2e06d815b09b77c9ff5416e283af91c18 d67ed74d76ac4e996c767254202b9fd4784b176f644a5b7b080261bb03e89bff26b85100fd8c7d80 5e6a0b806efc0240b77901a013cd4860989d88c9aceb04531b78e32253b3d10d651a3c62ca5aa552 ffd94765ac3642f8813ccd32f333f4218f8e7ec745727642cce8b381f70d7b917fbce3189a97b223 b0954e73808dde3b80c5dc2d410b2438950036a619806d221d60db7c2d416308b05d2679dfae978a 90d89e4fde70004182e312608b230cb0654301d8f0ec251f99a9b50af668153c22a2ef169172dd3c 63945d91a68eaa78b0440bf78c8db0532eca5304bef8e0e200dc108b508f707d3edae49f75e8b74c 04d94a2102b8351c00bcfe9e017c601e13c4cf040f08e00b054d107109be15809faa8d04cf18e0e7 2079df25974d30e3017ef5eb00bfc91b80eff022c0f74513e0333779f36cfcbb49d7f70f7be5ab1b 34b2b7b572d3b8cd5957335aa22635b30ccbc5118bd2685bcfe3fd6ef38b58bbe51d6e0899635eae b3ebcc5a11c780c0b231202ac4041081b30244777a04c404f92430f380d8a65d490071870540420d 2bc1b305c8ac3549f0380332d72a001217e504dfe40fc4f900c8e21c01243cf6005922fa8004ceb4 da6bb36b77fe2c8dac23c1742af285f215142fe9fcdbcb704c19837fcfad11fc892822f3b504606a 62dd73ba64ef33a8d3598054f22795ddf62fcd7708259b39093f2b72f50da8c20b06142953809246 32a0eca20ba846ab0ba8667601a8287a006ac82109ee7a2a108f4621a062f704a8b14b02aaa7d412 bca6800ae5bdffdd093b673d17a726e7f2a1ba14bf81d88ddf065b5c1c0492f5ee249a3f5c619876 6f2097dd9f6fa0bef825699cf0d35f3af474fe4a37936abed0f30bd295155ac72140fb360ce8ce8a 02f486d7007d1d078029277b8961161bc068c637c197048c3f7700130431607aca1330c9cf2f60ea f92660fab72d601afcc3cf86deefc17f9bf82a8b4a06c70669fe36372d9e6c8ac6e632966d4d69f8 f4189773b9e45800ca6cdcffad43ff2468061e95feed394e55df54614da31d5229d3d8a4d65e36d4 eb80dd5d4680431b07c055d81ce082a700b8deb80eb8d059026e2642803be032e08e995e82f10e70 a74ad6754fd5ab29a0e62615a385d71bea30e8eee4e3c3efb652c6f3319f9aa27f9b397e8ddc1f65 fc8f0efdcbc0f847eefd579cc3bf222674f32f436d3172016fbdea803f4ca64030ea7720dc2d0c88 d9920144f4d80762657e49bbc7557bc93f87ae03c499384970bf0171ee17ed66a6fdfccb19cd5716 dc9c1186646f059ac8ba7f708ae278aa64f65b9ef8b3abfea543a75a6f3a92ff1131d1ffd6809013 5aff8a15feeffcde54ee954a3720cdb33890b1bb0d6461320272adff0472dfa5807cd0ab407ec1b3 04a31b500a54e16f11f27ff83af5d2dff2a9edff8f2e67bffc06edf1fee3ea956712f28c8b2af984 3c957d183b49b82f085ebedb2755bfad19d5ba7937cbbbee04ab76c523ab756982b457c4f95219ce ceec7cbf3f8525f4737c0533faf06dd2de9fd8dbff84dda6a6d9544d5d869456f9ff904fd75c3244 cafcbbc5592e7ec953e667b57a426ef9705fe099eb6d9dd454d71d77fe5ef1fe257b697c2ec5f345 3b96cfec7487fd9d8cb0608ef276211ce2e447753fa5a7c6ce7e866e1a8ab03e5af17075f1befb15 bb9f9692e1e0f252b8b0bf5f813fa1083ffcb1cce62704fbbf879a1a909b78da8aeb61ac43e30ed7 c3e086b4a3f6e52876066776a2c7c797434c0fdfa0b4dc4fc9eb7a67df879b2d324c359b0d3ee1f7 eb669e39ad2e0e795db11bf2b9140ee5ef22a2f0dcfcdb119039d4d79899f11954a60b3dd39ac2b3 f16ee2c324f4270fe13f110869c3b0d439fb5fe34c75e878f7b9db3bf47dc5bbcffc998de7c8312a f6a80354f384dd82d1d40d1eefb455a7d83196c24eb71611a17bf398e1aa73a84737fe4e95457b53 785c1a4cd6157a3c41e6f43c4d958d8fbefe4bf08fa97df333ea904b7878efa0c2507844d52892e5 65247f0fafc1373e7c06b13e7fff837f79797fc2e43f2ed9bd71386ed647adb059de9dd369211fc6 f7b97619ff56d867f97e2f378547edd2049956b131be34c87fdcb2a30e6ec8c37b53d386c25533a3 57d77623f9695707df41af33d0bea3a83fd5b7f37e7e9e3b850e6c644278fd647a3ed6affd71c9d6 68fd57a7a5afa96536c53f810d7f84c9afb388fe840cc87bca99e57bdfc6c407c7de189faf47a38b 3f9d8fd8fd7a3b14ce8b5314b1ebfb2016e3d7001a8e417faa2e7f52eaec273f840b638384f06289 f7d6ce89e9219b93d0dd5581d6c58f25a7d3a4b94687ba7686ed0e7fd9b6ee91f069852ac4355f93 76d08cccccf01f836ce3bb380fff840cd8dbd3f918e55fa3153b6ffc1e5d9c19378e1beff43a39ba 781571289c187df0ed91ee008a98463f3f667aa193e387bdb5adceba3b9f597693136adb39d6a563 873af397f6a5ad3eda1dd6fab4eea10db542b10d375fc31ed68cd415dbf84e6e3fd750432f94bd06 b4ac8775b37c5dd71635f35b83cf05beba6e77bcaacf4e1b7f80dcc3fa13b2cc479acbbba6c687df 3e44d954e18d5e4d243380faeb6cb8d08e586f6d5ed8eece7da99d63f5f55bc26b5f9a77bfcd5e5f 8d56c87fbbcde4b80c9a91928f1bdf31346dc41564d580e6f0b63eb5a9533dbf66efb5856f2423d9 d70ad5757d415491f35d0c6a1ce5f8c77e2ff49b32b9f72ef1a1e8752a96e471b9e16f33ee7dde32 53a467667252d2ddb976ea88e92807dfcef6930c307fed218bf0d439069d67fbd298e55b2137449a affe86697c474bb1a1437bbd3e354f763dbf3cf935a77caad7e0eda35df5f1c7af4eab2227300c76 add234c06fe8ca3ff694a34fbd9c87d751ba90c795deb81bf614c579bda6354751a9a993dc7d215b 9f0e683b6b3e353b5bcfe8d6ac705053f93415a327deb74f44c9e5970b9d9191ae119c807f6c73e4 66d512eec1aa29bffb97869e19beeb667e9eaf39f416adaeb719b68a1aa412d48e92e99f948ae7b7 4aad8677ed763b5eb7b28c3c3e779dbafd2658bb92c29d9de1c6793b99ca12b62bc7ecefd9286bae 38a655dcdc7ba6877b3bb3fcd6ca46d53d28068ee4ecbf90f958e9e939ffeebedae85a0fa8beb5a9 963be79a9f49c5ddc68403c7baa562f37a21f82c6ba588bf5531d0ce066470fed5023e73ccf15e4f 2135f779ad38ee60dcaa39df5cabe58c0eb3be03c99bd89e96c0d2ce07d99db5e0c59b05cf0360fa f0b064ec3a6fcec09f9c5969da8b9e7ed924bf791c0e95b4fb6ba269617473b5b079f84da3d2d7dd c246bbd365eb52893eab3edfabce6db4f56af9b986f10d5f35bff0be54f19eb50b1a8de7d23f6f98 9bd73b7859378207a8f38dcf9ca36319d59e0eca965d8049df5a5cac9655d283d0dca0c3df736b26 5a1fce8cbd74dd19c4e87da9b450e2ab5ffb6e49e73e4346eb6b6f437deffdaeaa92dfad0a2ec792 32ee7aaa52e1e7de5fc0466e2a9a8f0fbd8b35c88781d0b95c0cbca94f9c42cddffb3fef60d0bced af3ed7540f9ea89fd76ed47f9e9cf82243f60cad22d6d21a3056e9bc92cd40bb99c67ef7f18c7abd 58af9c3e64b7d25a55867a329b99eadd7b7fa33dc5d9599396d7b73a428a0535135ab45279cf7e9a 8d3cd74a2db9b89baea555bd53943c8a52a4f2baeafd85d8769783f2b017b3d1da0ed7274b6c8b7b 0aaf2f23ae109ccefac77bbc663777d8658e4e859c6f6c8beded2cd7bc7dcc6004178d036bfcfa46 19e4b42156dac440d76fc3b9a3f7d443557b6e9f6d6d109607eae78d8d55cd95562a540e8e8ad11b bfe4f9eb9c9761b54c4adeb6ae8b3bfcd112f196b3141a77222750e15112a8bae0fd058bfaa91c0b 086f85c3d775eaf4ea65556a658a08510bae44d1ef21e2d71dd1c3bb931b944fd68a1d6ccdada8ad 8d436bfeaa9cb7af827e7be7715d70294e1bdc6445fd1ab6a9c6f82050a1e6a0a14c9543a8e4c7e7 5f529fbca89697327c24f7924fab777137ee65452277c085a68329fce53a6cf09c402cb8b0b9cf70 612b123991cd7b9c58feba9c98b93bb3856285d1188bdc2e23905233d786c92a95cb16bd41c803c7 08babfc00c6bb5fa9ccc6aadbd3328955aeb7741be682fa703690a38a26a1c7c19157a21b2622a78 455e6c584776ba5e5d5a3fbdb6e40f0691b887e75391f00f1ba1f9cc5df9abcc029e8bab08d747cf 12fb6e483f1192552efb290b82ce9719e30acf8cf33bf70fe2d7c299fa011f0e668da6dbe117985c 5f4d5e64c03600ec6acb32b0e181fb30abf7c3a97251e5ad1e0ad05a53a8ef41d5031d28e6c029ca 4b38fcf9d3e45267c14b1bfda549e8ec6b89752aef8bc4856a0a2d8eed094cb63ae6bb566fc53dce db332771992f3bec0b65167c923f56f48c4fcf66ad315d8489275d2c9538ba0842872e5c3b7f303e c9995f4a7cdf8d4db72dd365a5b6c3ef942f9cafb06332c58c85c2d6d3a00b93932e0ac85603d471 adccd0e956768bf7a7b4e9a03909cb2aa858affb8c703a372581d1429dbf410b87e76bc71af724be 1d4e1a21bfc7e3d811c4ced94c72e5311362f5a6e7ccbd48174396a5dcf7c821b71a3924abd0fb41 068f3d430607c54a603a643057ecb8eb9d7b6115d5bd16c4dde4eab1b9a33c453b956c67928352a9 5c7fa8835f9da68ea9d75629ec3b2b69b31217e2016a9e8533b10402337cc07c0f2910dcb38672dc 40d455f633d40d562bfb1e9bd9464dc610d67d7a1e5d26b4ad225b6ab5b51f1482c77912a72f14d1 e8c92641099f9fbb1b6f0ffb57bcddae5378db0706ded6b3167e8e517b244db7618f9268af999f9d 95a0338e29375b9b952c747ec854ae33e6a5a9b8719297fb7823619eb814ce2698f3b76169c73db7 ca93fdbc2c88d59cee4fbc67217a43314677cf3379feadd2f6a26453eb121a50ded5ec903bbe3624 f141bc249af0eb825f023a83b387268e85d44b475fdd460f955fd811954f390c8d0ab30a3a782f4d f4259eac61e6340a3b77afe0fd6d89f7fb58443b8553bf6492d416d2fbe7e24bcd39f259c256b58d d0364b4bee05663376f4ec2e18e3b23cd30bfdf1a6e143394ff91456a690af459335dd154822dbd5 88667de6e097f3a68677e2d26f1a853db2dc14132d7f8f46abc51b891fd93292957cb96cb65fcd72 819b6ccb056c502e9b86a426708df2b41718037b5b0ddb8a71f56a7564a47aaa5ca3ad6d2628556e fa084af57279d5c5cf22a5357fc52d2f0cb32b161ab853ba94cdc4e4ee935b1227843913cd7bfd83 5fc55e0ee7969332f670cf1426de3e023a14731aaabc791719bb6e13c96efb51d9c2f7cbd2b255ba 96ca9956010e8c375fdc2f6bd52209c13f976a917840f9621d9d8bc57d90d18b7bfda985bb851c36 67cacc0bae6f5b71e62847a516737d506f406a1edfbea4ea01bef0f79ab963e3ee69453b1d7e4ad6 dec9248ec7ab313a1c1b0b34830d4ec8f8b0fd2dac2039f90595ad3104978b151c2fb9078d87b794 a7c1c165e4160ffaad5d24e7e5b8d086dd7d52658e3fb90195c1b223cead64d5271165d523f2ce8e f23b22fb117131278582d4bd0670d828f32defef6816dbb308da601dbeac692b2f2baf95d12b15f6 39c5a5774ca1315e91fb3e36c3bb93418c66fc6fbfe4695104e3443c2f1e4e977d91d2a05ba13d85 3f05d614f3f9de524772839b4367472f5fca424af4ab3a21637c696616793ccec04e750fbccd3d03 906a9b0348cd6a02c4d24f00911e79e0351c1aac0e21d71e7e8e618dfcaa9e3b7921aa858f4a8cfe 1a93886ab2764eda5d271f3ebcaf2fccac5ada531817fc2c8a782f7398a25a491e95569f45bf482d 4bedecf7526f43a6150c338b556b9671bcd916ac77fb0b4089eb0ba024934fd025004a012581e303 54fe460055da7b80aa3894e02600941db700ca853fcb084031b30cd0dc824b90cc966db3d90b8459 d973e0cb4b353abd07a3030c431567e715846bc37eb3e36574a5b6d4758fdf56cc0ad59abd69696d 43a3224d986136febaf54ce99c6b000cca7513589354b985866b80a1e09240fc24e817938f057402 534b904ca9305c1b014c781e0126f6f200935419605429f90cea7b0658e94200acaca900cbf49c3a 7e137b6994b9557b1cb54a1f7f73697cfb9cc4a57a8b2d701f5df8d0ab5eed46b4f7b303aae79fab d2fa69cc0a57331a662bf2b203b6c5721d602bac09b0733048b09c01ec5bd8011c366f09c600e074 a19cc06113ec2a006784d4da8b33ab31c059f50c703d0727d86800af4403800bd53bc0c52a0770e2 69039ce41bc1c3c9f69c65ef1c980cb9d6f5e1a9202ae54e8e1084dcb7904a51240dc43b1adfad43 29c006ab4277fafa3d2a0fcd933900c08b6a1be0f57e08f0d932d9c2fe3a02f8139d0322d73c0202 3b3e0121105940184d1410d59700889a6625d8b601515793f7d5af7740345a082022dd04c4908a01 d1cebd01d129a66ddd087f5807440006de247b0a6d2c5a55d39c702d9fc9cae2853893ecb4738453 911453a9eca314dccac782806a6b0876b818e08febf02f1d7a6cceff127c41b84abed8620348e59b 869c92aefc02646b5a00e4289f7ce9952701f2f3f100f975c304ef35a040f30da80c4d2678b98042 0e3340a1f30ca0727d055079a797e0b270edfa23b21acb5c4bfffa902bd7ecbdc22b93f96f2982da 9fe332f6e92eb3a5ed7bf62c08dfdb39b37e4d9780d8f88bbf46b4848f802ac897d476fc97c9374d 9cf829acd4fdf60574962a009a9b33800e300dd0e3710dd0376e9460975e3774525502fa010b09ce 4d403f871bc0e4bb45c0147c03d01f6d9ce07c72d6037a9236ad4cc568e1995dea8c571a70f84bec 60e575dcca17faf9fa3bb3d909c7bf0696ead0c970d2cda4726f47fe4b57a5bf3df87f854dfcb1fb aa6c32ebdc54006b9a2dc0cea01960f7831b600f3202d857ae02b8fcbeff77a67007079cedfd3285 c9f4f2e49cc7d3262edc5c1f1f737d99bcddeb9cb15a5ae4e53991d1c2c4278bddb305439e5dfea4 1af9ffd2a1534935159eff4bf0fd3b5838dd4c6aa8fd47f325bf2dc00fb62b20087300845a8b03c2 4caf01e1802f819887b340c43f0a1089651788526d0744b9004c21abacd575898c53319ade877b1f cff823bd4429b5dfaf4076d1fa2d88a663fb8f1ffa5fd6de34e5f85f83f8398dffaf38e1ffcaef3d 0db740925a3920cdd39ec5407a096d2017ca7b204bb91290adab01e4463c0072641c135c32ff5b93 4cadbda97c9ac6f4a62dce727102779b60737fbdf432f4926758f119174934edb78624f769638731 f70551e6ef70ab28ddbc5b4ebd21bdb271adbecacee5a860d50b3566da6776ce0c4e21ec2e8e113a be1e622a53d84fb9ae9adcd541eb4fcead0ff5eba9c29b1a645301f5ffe81bf5977c1a517fc7f4fe 95dff0fc1acfe9635a3aafeff66175b87997c9e55a7d0c1e97c6a7f13e7720e37bba9b3a7412964a fe189545f8f0ad724852c4a23f5d609fef66a9ed5aba729b9d7655d6cd423231eb2085d63224ccd9 e2d5863ef36fd8a4e7b13895fec94398aced2599a6cafe5f2ed9570438f809d92273b7f7e8efd8dc 90366a5ef19008ce17b5d43a09f353f7f0f56783bd71b6a2ed5a2c0e3735708ed7cddc7cb2bad893 f99f2662c2beb1594464753fffb61b9739147a8f99f1ee64a64e2684276be3c84c90a5608f6be5d5 e04febb0d6e35f2908ff691df67fb964533dede61d67d773fb2bbd8f51310feda7f8aeb8f59e4e79 83c73ab9ea145176f1aa9ef8f9b7b9916753beaba692eaa05599c2b1654efc6cc519ef2cc91bd78a 955a7cf42aff09967547c37bab3e1f0ab7f1218a84eb2b92df163a88d5bd3a80a6d1b03fb5a853df 2ccaf77e7e45ddfec1ff588a484d9397c6e3b6fad38d0b1fb67acbb0c4c4f3987cce66c6fdb69dda efdb79e267ce8f31be987f626a134323f6d0c90f43aa09ff6fab6cc8ff63954d5daad385133a855d adb776bf61cf47845977571b5c9202385fe834d96da5433ddcb07de9ef977facb2fff1cbfe6d8a16 efa9af73ebddbbf5d47f3acbf77479bcab94cc985abd7f8f950eefb56b337ab5ae6124dfdef1407b 3de6fda9f2de848bca691f3ab9f7a5b7b61ff71eb2bebeba3bff03baf8fe99ed1c1b996287ba6490 f6a543d26df6c18bad50b28ce66bd4ad36231d0c1bdf99b96fc476e1674b6840ebb55e9f0672fb5f 46d91f52553ff5f2a6510873a8e50be39d6e612376dbce47f2552e0fa081c1860b5d967b6b4b367a c8caf6bb35a4daec3409bbdbbeb4ec419bbd193ff9a1150ac6b425bcec4533528c755301d543e33b 6e5f1a7aaef76c40f345a66ec2bb626de1bf889a83b15275dd68b855e4f21c04bb6efd10d4840aea 1fa38d9a7a63fda65af052a4a912a9293aedb746c4527cb48ae528a221d0373ec2bdb7365e8f2ebe 39653ad4e1516ab39707d10a7fad6d2319288d6f9c351a7ab6e0d6a75636a89b45b4595b78a54ecd 41887e755d4387559fe0a655e4a4af821ae3fcd2e002fc16defda6b0cc7897e851f63aaa22b8f749 ecba62011f3aafe5f3e1445e1b7314e429dbdfdd4d4991fab7579ddcc09a20711d4f87183a995772 e66ce86dbb438e172de1dedf35e5f7ec9ecadd9915a89bf955a1b670ce580ddedce8aa8f3d8560d7 782a017eb9e9feb19bb1fd265ff4bdcb2057f33a32d676ef31d977435d1d3baf99bb72226b7cb6bf abd3c7d651ac6c4deb75d132a942cd5c74a29f5fc074782763c2cf3566ac07472ac5212e4583548c 4e0f773f3fc26f9d265fd82403e4278d5899477533e745b585dd9b57d7fee410ecea935b809fb75f 9fbaaf735e47dc953cf6fd22dc50bdff2a1b578420c1896a79c55144d67440c1f66dbdd568da5966 d24f7e1bce537349147766a9253d8c20332a56f66686abd48971553fb59585ce40afafcea01d42bb ee433a457a6e4ed6f28c8c00807f06ffeedecc3f5a129cddd767437e512b3d4771b0d7a15e500b8f 6172caacc75ea7f7dab8e19bba396f57fb3a6aa653b4c7d508b52bc291b1730548b2ac565eb38a2d c335bd4cb56e96b7939e51150e71e5103d7f0ee20a5526cf7abb5705daed95cc22049751d5c176d6 52be84b953461f1a56b451c4278805456b87fcf26edb46dc9c5944bf9883f3ed9e0a7f1a86cf5dab bb4cb0f3cfdfd5dcebd5a991fb6cad435712fcdf84dd1915f23b7be2ca0f6b3ef1210bc6c392e945 53c2449c0b6754af2fd9c027885169e611a7421db5bade517eaeb57163a889d87ca9468df3510549 7daae8952a224f0f4f452ed06e53727a858d5412d2d03971fdd6f904a690621e872f6dd81fa954af 6a53a5e6b75a00355f2addfccb51ddbbaffd60e1c41e32b6a7c622b2f34e2db2eceb7163fa78fe66 d414e45b395ef56285aef8987ec56256e71aa9d54a7bc8475d13935baf3a445f81aa3cf1b6329685 48c9aebdb96c7193a3b4ec3f3f52e9cb96c5c09fc8c2fe40360492daacf8d3b59fe75bbd12c7b7aa a8906256181ef548432cb673f1e172637a2cfc4e812ad1cede3df1ca1c1cfdd35bd985109e9a1b3a 1c9a68850b0dc2366795e6777dd6bbdcfdad3de6859c265134a20e63915455bf2628e37d4d512ac9 1d479e7da69e5cf48e4dc9dddd43712b2153111bc93f9543682083177faedd619e39cb02d763b755 f6d9b7e6ec40e6b3094e2c3b48aa3f76403d844970bb9883bca972ed307c95ebcef701f9d7edfbe1 6630ec6817d9c6da0c9af7a94162ceb0d2b2d1beceebc4afb8d5fafbe6511d7ec2879a69ef21c580 de25796e015cb649829556234e921048d4c5edaee68a38d1a90b8de1b4c75f32c731cf9ad086eb9d b407fb62a2022b8710c78cde0d9f81346c464fa14f869e3c5abfcb33799d09f4643fe4c7149edc91 d7439e6f0d892f5ac3d00de4f55ffb47aa439b5bd3581be4e638d36fb63ed206c8a3af8eccc74899 b489ad9287cc9b0c13ce47f286ddbcb82b8f11b17a3b51c251b8fe9e8d129a8592c65f5cd4e2d91b 1770a1a0b739e1158ed8c839ae9938f85e19e8a8e468939e32d4a247b914fcda8e29f8bd02147c56 a9042d818257013fba2bb1d5ab6dcb7cb3a21ed12ad95f66dd91b379a63af4e876341a2d76addf9e d39936e8d023152a2cfb4a5e1d0ea4353e9b4bc8b270166b0cfd128e3d0912e8bc05f39d661de739 bacf70e17826b1efdc5267a323f05840979a4c72c20ce859aeb6a00bf6e2a77552cea600911bcca7 48b4f1b289dab5171344cffd10442d4712b5a9c4a7184641d34af359d2bc9380f90cb3ce64337e9a 3be47caa7432d4468be0ce4c85ccf7507632ddbe84bca49e4894ac5f8aa24057c67bbe73d8dfb807 75fd70e2389767df0183b0ca812799b15ce119fd5b57e999deb5e8c27e59a35cf2da2337313a25b1 ac7324ead6f48b9fd6251c67d0c8c4ba0d36c2f852ee89759d038175d5cc6fe2815d5b392ed207a6 d5e1eda7502f1f43d47b21f56c6add368eb5d5490fabf856d53967263bea6628ee5e565f685ecb3d 9e3be4224efcf22b76e8bb27163cba4fa6228d324cae788669cb3ba3d4f2fe49a7b86e1f93c86d51 4ace75cff289faaed5c6cfe226c699516187f5927d803ecd0d8a4a2b514746c8bd8fa8b3f91d19e5 1a18327cce59e483add84161cd596d697d106ab8d2c1924a2cc859beec3e2b97f7fc776c34a5f1da 28054b9c89b5573de2afdd72c8f5eb872eab7a832e53095763da5ade36d48a2d5e280f92dee4d6d4 20123b79307168f57082bc8d58bc6d6c15ecb6b85998c094ebe8a0a70e90cfabbf4234e5f1bb4997 2713152ee7f37bb964bb7ea70423daa564eb70b934ef784c69ee1b74b8cd12760bcaaec4349ac599 0ef9ac595b5b2fbd5fee9dd56cbcdb489b363a1318da1d7252fd1926657abb43179778934232c2ef 4640e2b837271ad1608f5f4af32bce06db3776c75e594c88f22534020881ca1b434062acaa235063 e4978defb3577272c529bcb6ad138cac0fb9620d53f9c2b1f16e149aa5dda1880fe362116fbdc862 f59ca7d294f80fb01bf6b921f8e140c4ec6599c919f483ff23992b4574bd150f5069ce3d2bd290cd 8cb6216d9bd52e591d7f9b78078e9b98985d0cd0a8fe5d20df4b7e87e81c7c46b259f955362d132a 2d56feaf7558c9692f28787d3f8a302a4166b1b6929311edbc287f25b6dbdc832dbeb36fb1416495 51d9c902f631cf46ebdd371b8d6924fb060ada093357a7b6db69421acd620506964f2de66a65615f a4ad146d531339fe9eb39fa71ad3f3fbbc4fee54b98b77ec75138df67aad6cee40a3545a9606f0c6 15a7307a7336c5bae01f8bc42b7a145aca0ce4af9b2b9ce7f12c997d5f81905579da84c603bf9199 c5f12853cce57fe52070ed4e0e944b820cca9972085c2d7b01cb76379729ac36a534f5a64a9f58d1 99edb3b871a66e396dc47c5e4af12a5cc4c32adcf182b29f33c6058b29e4d4eee39717d44187d95a a36c95df018c29f5df4a61be0793d5eca79e6b67d5737990cdb4a5296440ee263337eba74c31b973 02afbcca82f22e872570c5040f0f94f7fe00940ff97d82751e948f3d159497561f9457ca1d9447b3 e4df6280359641f05b93f6a5e025d8e5e509aff4fc435e9dec321f19a9b0c960d9ce9e8d2bb305b5 de7d6282ee697df41def3a65abcd35602c9a05f99e5db4216358b58077a45c8034bd5682c90020c3 dc1c202d7e97a0fbbb6e92d7671620238b00487b290324168304c71140c6de0920131c4ef0a90024 dc0c01d25f7f00d2a01180d83e53ab5f30db9d348ea2d9988c715d9ead0a8afd3a7f24fc40ddb8d7 5bdfd34bb8fe5b23204ec3c318931c6a509e4b5117c60ba091171abc0799859e09501c37002a5403 801a872e40cde208a0356b99607c04683ffb4a6015126c28800e682dc1b40ed0489824385f013aec 21a90e3db22c8076c42940bb6216a0c19a06681556d248737bad4f2583cb8f494d97b6b0bc3e4119 816b7f6f6c764b1fc843d65d6103613429cff7dfa87814b94e4ea12ad58c331cd800dd566d806506 6eaa4397cf7580b1580f605a6b0a306fbf015807bd24703e7fbb903936416c006c82b712cce700db 594f80cd727882930bb0f96e09b06807036ce8c909b68e07dd46b64558e14f52d5df4a9f5267e413 114fdd5d86536bf70755851f07ec05e596e545459b149b616790038d710b6c96b70060eead0ab033 d9fc4bf025ad0ec0a5fe00e0d6750af0817e00f83cba03fc082080bf4c34556ef347111005c149b0 ea01a2a8ae13bcbf00ff0e1940946a3540648c2d20200301f8034b8a9e47abe994160dc7b8ae4255 57953d276fa12d9a9ab769af173ef07b7f73420aca795d6ce9f99f0e9dad0cdee15f639382ee1f1d fa8ff7982f8d01117053407493ab9d18fb4b405cd7074066f01720cbcd3c20d5ef2f71c25600d94e be26d9718780ec82e42ddd610e905545046488b75381b85f3e03b2912100695fab8074f4c83ad03d dfe0573b439d071b4164dd1ec6e6f79d3cd1b93a2f6436ed5e8a6db8bdc91ad5e6e4df03fbe9d07f 0230fee539c6531dfa8ff09b8aaca9abb63679036a861401f51825650a86192029179a8036e86982 dd0dd0660d01b4c5267fb0734340cba707a09d7df266376e005a931716a59c1b7a0c1ded3449e497 9c34ce37488a647d189dd81680a92a7fcd9a1abcf9f7d87ec3fa339c54524dedc6ff08be69ce449a eaf0df4e5f89036cfe9d9ed0ac58eb01b6cf6e015b5f7d01dba832806d32016087a565820704d8d5 5101ec7a94bc6f236d8dfb2dd34993ad85c763aca54d207179a823a55a9fcfe62ad9dbedcf4efb1f 7ee83f2ee854524dc7f19ff6767ff98c7f7de4fe4bf3fdc7e47b0e3dc053f210f051f104f8c5b608 f8f7500302d9ec03c1f6923b435323d2ee7143d203c2393b4d105ef5f1fc13fe4b8cee0c7a12e20a e6ffa3eb3cd71555d62d7c2d252632923382a204c52c26cc39c7fb3fc8ecdedd6b9dbdff8cf9b4ed 9c960514552fa3c657c8f531360f0ed0ecf1abd3fe1bebfd57f5b8df71c289b5f7bf31df6fa6c38f bf37c1bda7f41dc8655000727d5501f27c3205f2b1f1014abe260185561bb17c9640513bcfffcf24 7f970e4b627a137cfa27bf415ec43238c5f27aff943a1bc5ebd97b843eb07bd6bb92b739f9666e70 f0e0afd5cb55ba6c85937a21fba7e2b9feb9da3f01b8ddfcbe7108b1c7d796b01f1588f5cebc159f 9b9576e2d65b8bb557874abebaa2d7273b81bc532b25f1ff3364e0776a6f52e24caf3cc3d4b0f5f8 989be13d4266b35b79df5f2748b555dd9d0fb2783c9d75ec7cbc59d0ed28ce3fcf83b2debef6fa7e f4d999e7466a5bbe57321b6c28e457072b8f2ecf957c6171f30869fe0c8af6ecd31e7466fad33a4f 2d70447ec210be0d9b38f0804a906a0d451390fadf9afaf818f7f4dd5c5ef0eb8ada7117efba507e 67df4e9d83b22af93bf394aa6db0b05e5b1d8a4ab03c97c9e6e2e640ed85b8fdf4e621f51c24a08b dd8e923a6253f3b95a4473f5b0fba91f86cd3e9f31b9a6d0782e1648c37300d787dc65bffd138510 3e433295140ceb1be9fef37f842164d649bdb56878d8439e79db54efb7e72a800660c92d1ad9f9d3 aba033fd42e0d36cef4d45f0f0c44c1c68cf8fb7d6461a938ba53a0ad099313c7b136bc81d86f6a0 cb4cbcf0d99e3542e5b1e9f547ca23ea436362f745aa56a609ba733b45776de41e74565eb0ea38e4 e8f023d8a1b56f6f0367f7efc45b3122c709435d74e16675a69fe27b62f995ed4cb0f16738f6b3f0 7c74b0e9ddb04540dfa173d02d3caf61c81d1efd4f77fbeeebaf75eac72d6ba576f08f55d6ce5e0a 9d957de13ad8fa2eb77d1214dbe481775b0163b79be7f66adee49ef4bd710b6f8586949ad5826754 18ffc83750f6bbf1fffbf34f59b304a52546d91f97ac9f2e92c3164670e1b39151fb9f4ebed487c2 bcdbb3e25b4c775e647b9d55991876b01511b57d9c58b4e2536adda24ff95df3dc448f4dee96ff16 3f69dce2afd3105f34089e033d17a8a9325e1f994dee276920aa10b55a76d318fb3695bbfaf029cd 7bab66d789651bfcb1ca26bc3ca96996a43524bece04f9f547fce495e0ee61f9d3717216dcf6519b 6a05a4cd37cf81af36b94bcb6a7485562578f6bb7ea07c5a417d6404adba91eef66a51a935a859f9 70e2cfabe1dcb7b1f9c65bf9aba3871d0f7777db44d22e799370279082eff31b877e434eb5a537c7 553e5d0595db2cc3fe1864bbe58a99c85feed32ff01b88070e4ac064db47d46d8bdebdd68d5bf37e 0a42e9feaa7f86994c1d8a5248cdcae5a95a76c17e97b8be8db2b2b7f258ddc30e05d3dd0682ed92 17c1750e6dadeed00fb9593df72bfd2af771c795db68b0ac48e9f5d17ece726f5b8535a43c72c662 d92024bf0ced3f8b5214cca192c5724c297b15f944be63daf7d94382ccffc2a707175937ba5c31fa c1a751313e45e7e5e5da5b39cba3bbad2def2e793a0027e0ce5987be4358b5257dc8ca6df0612a5d 3d2b54240853ecd0c20c5bcd71df20cdf2a8c2396503358352e479bd92454e226b1e5c77167c259e e6aad3414dec95518bdbc1b859f475ff50a4d2206d1ca60c964862314f607472b8130e1d3cbbc4b8 6665948eb7aaaebe19c4eed60feacea111741cfa3a8faadc335a56baeaf6603fc787abad66ceaff2 670ea0b281805c195ae7d09245e4c852768f33964de382059f25cd7478d532b17be0157d79d4360e c3f3c460d2d84e3f4f9b5fe7bdcec35041eb3acdb22609ee4c7d65c14d0d0fc54c22f310fb6849fc 4582cc1bb7f762599b22d7b187b7b21d27383af52a5fbc542b5da55db19fa345c356d3c77ed9c81f 27a5a87a5f967269fc98e0ee1a73b310d9fa98ced2cd9878b38714fd5b8f2852d18e331ad45d3198 1362e96d5df2b4fbd6ef68b2b49aaa8361f6a8a6303fad14fd27274f4f4d4fce1bf252ce3bafb7b4 240a39699941b3c945fecd17f8d2fd361544e720052d369e9779cc1cf62e0e2b4a2aecd8a920572f 178d5aa59cee159d52ae5a6c5a152c9eb5bbd66e59dc1deffb626dc9dc8d53b59232d85b23af5fa3 29a18bb929ad3d8e175153f4f4772b893a8c105b8528a9ae9803bf2fcf53f3850cfbd98b543d3a39 d1bb6e05e120956b429097b7b1bcd34200b3a850ff104872e07bd3f439d5ec80f3a356b68e07e74c 5f171565898dcae6c1ef9660177c2b975b98dcf04cafca564de2a605459aea8d8ce6e2b8d23bd7cb 411723fca185140bd44f83cda823ad84aad0a656502cb2c629d9e742936dfb5296561be04bb8c8f7 443f746642b08e4efc8544323c1fb4be9dc675afa4cf49ddd4867d499d542c2724fee73bbe99f534 b25ba93d330da5787e79dbcae258ed3afb6539da42636b75b17aa65fbb348b41b556335a52a6aadf 06f99a2ea195a1a68ab5b93a9a0f766a9a597c874ec51a1f5ff2827840b2fd241069ad1284e42245 4edcb92559a41e6d4b68286397bfacee1d5e20f0889315fbc80e466b884d65789629960f2e934126 2b2683c6d3ea4c6a882432e44f02d5213ed077f3453d52a2971b747ac78a3a5dae4a95ec7362d608 b56f5c9aeb967eb7b49af6521f8e06dc8dab1a877c4f2921c2545eb44b1b19cdd44e925bef3dc4dd b9f7116ba37d4e38e1574c606b8f027f550a122f8c84ef29c0f571d761dfb5418b4d35d763c64ce3 7b7a56aa013abf024ca18ab7aa05745f5d16d079e61d4b1549247ceb1ad33a73cf78a1bc1abd9d76 b67db2d3a7fe0f872eb2653ed47bd1a0a5691af10d6e568bc2d251f2b457912ba359537277d791b8 57f24bb1b02aec85262f5ff96b4f7bf19d523dcd3d964d9853b821c9be6f7b9ed5adb7c64c96a8cd 64b9628386a5dab0501dac37d416a2de14690d28b2bee4bf935b9276e019491bb327591f8b08596f d3707fe2179866579ae7fd351f7eaabda0712ee7f5ceda2c8c579121acf050d35eedb69ae9e4ebf2 92ee3892872b15b12e2bbed0acd6fbfcf53699f2a2b55973fde3f53b0a70ca107db023285e06403e 976522aa8c33d986c7d076aa2f1756e6d22a60c7548df259212403315ea19fc3dd83e0531a8177cd 8b854bb955848beff21defca1f18bfd521b8f7e5658d7011c15e4db6bfe93c15adad9d4a68e4ad8b 5c661469ef3b08d56caad892abe57d4d22f1b2239cf35885ef5c6f552ea4e80efb199863d6485517 4ce435774c3ca65fe879b87cd2b6fd4917561b082ee0244c527e280ae41124b5be49c61dba446b7f ede2dd0b3ac75e827bc5d4fe1b45c7a05d448d8933468d2e7c4547db3087016c9eef3a6c9e0b8c6b 0b761b4beb639b19f96c9139736d88e556a441d22554604f6b4b646b5213b825f675a972cfc9c166 3f87b0cc44eba8c6e484679fb6e770545823e4aae05ccc23b513cc2b45cdbc37792c8fb22473dee2 449bbfb1f8bd9f557119562a9886579be8b8b68cd0f499382325be9f87177d56835100bed65eb8c2 accef0a2626491dcc2c975a8e283ab97cc0071841bff2997cb85b34937f48dae9c83a932af2c4269 1b16dac279d7af73aa79a8b069b555a617826415d665a94aed46419b2aa4bb03b2518ba6245b387c 27b7447bf83ce20fe873c37b3b3e85bd0b6a1ed39a36894ea0504433d6c142f2954f0daedccc517e 232d0f796228667385ee44ca9ee4462757a81b875c6d768772fb6c2ed36a2b4bae86860e5a552c16 94b056e6bbdfd3b84de88d06ddd5485e6d06a1188cde2dbefbf6ea6c3a5c55e945dbb70b048d5864 63bb36894eeee5e17d2cddc6de5d7c800d25698aa516f6063551e7886636e103298b6b085e861704 c6509ccd7b9efa3559e4ea81ef644f976d2fd3e97f96e9fe587f429fccad000dc3be03bd5fbd655a 29508f74bf647c9a726bc27b7597419268168b4aa72efa2b0b6ffee2e56dfcd1e6344caf337964ee 14dc99915c9e4dec6ee1fd4a45c720b25842e6a0e423b05b6bc12b623c80b1fa34ca7b9fe33a7730 eec75c90411ed9735dcb64babc80a643b9c8429f61cf80a068e7a7ac3c3504f1ecf008e01d8f0078 0699b15cbf7368507eba8754d6bbbf02b350e65c6e86a349344bf1429d2e89c5fc0799376c77c0f7 7ad70e5bec2a416123741df234a26cfc995b5818e46906bc6aedd5dce1d42f665bd1d0c9748f9320 fd62675fbf405aed6c86d0e8799a4169f5bd4d4d53852b5864450010bb8ac472150052a95400524d 87b1444780383e0a10d72c03c414a60011a7678048e05d5b710a9f58e24b787f098c5e6573554bc6 f6cba113642eb4d7c50137cc9fbaf4b2810514adf82e214e73150c9ab826bcbaa4e249fcdb93d303 fb26a54ae5ae0a2a4e580228bef5004a645bb1e8c35886cb58de278092268865890194121247174a 2d1c80168cf85d34748ee54000949955018a0ee25fc2982740a15ad60bc20f674fad056a365a1da0 a7c4e645417bf3ad78f27373be2f6843360b4dba05d27d0644d7b03ccca81c2bf09a61cdef99d690 275a5a0b6e422adff77980ae7c05a0e7b30dd00b560318a87663994d00964aad63295e6399410083 0832969e0a309c89df876f238011de1d60599a02580e8dbfe63bb78d650a25dfe60c30a7e71ef8d2 066da0c6a3e7a5d459e05d65321fed444ec2971ca4b0231a5b785d9257b60dcc38e11ebc0e6b762e be499ae9d40096c1721f0a002b774580d5cf06c042b2128f7ff506c00efba4d212f62266b1547600 cfed1e00cfb3b958060cc03e6903e070d80038c2cd6379bc019e1ab20087da01c0eead13c01e1212 4bc4578a9f2e6fd16405d387a732a4ac90fa5d3a14ce475e05f7af1f9a41f0fc98bc984a0f1bc7b7 2f78431ebdec7556b0a1c9ab1c37eb9b3c8093591de0265d04b8a79700dee9b8009f9c9a00bfc923 4040bd2520b0d70910bcfa8e658a0042c0f958c2322044b293006271b90684e441802814c45f0664 247b8de54903022d1be5eace118a5d46c3b5a85acd288eba7c0b77687e6273fdc58a6a344e93c45d 0e6fb65233dbcdd4bcd47c50b7e2368926c0afcd844011ecd2f9c57c1b6fffb70bb9d405c47b3c06 248d6f01a938574096772940862a11cb4a01648376629985806c160f806c6572b11c7540baa33e20 bde1f357fc7189f87aa0aca0c90b86dad6482da777f25283e87fb8a2e69c0af574638d0fe7fd08c1 96db302bb99766aa42bd5d90a41e13c34ced1782ee8d7bbf8cc789c937fd1afea2be456906a876b8 4b38f424fb04d4d5cf81021af75281ab1ab13ceaa0805726a040e4aeb1ac7150c8faf1223d678e41 216fa600f529150175be374d015265035c06ace276fb68b20d9fde3cad2b11eac52de2eccb5f9f4d b6d7f00729b4576cfcd5b61fdc9bd0e7c4769c70d57f874d7cd31d7eecbec337a06b3406e8e95000 f4a3508ee79e8b0e60b2c61ad0cfa4dc3ba05f210be893e901fa232c017d6161405ff152dca4f3b0 f8a84645ad9ce9f14998086b178c34792f4a0fb4d2600fd9c7945ca430e71efed5b09f36fdb1f6fa 9d7bf2317f98ef9f88897f397d951301d8409300fb427cc051f331e00afa19703484000e1e6a8043 dc2ee052d219b0379c021c1e2f38387c393186b7d577bb82429edb126fcccb54a1d1e573d8ac85bf f3dc3c7f4e396177fecba6fd87437f5dd0ff8e13fe5342ee8fbf378913fecd7a7faac77d71ef4f88 c2a10e84f2640184b0f206c2806062b9bb4018cee740588510102e75058868a503440edffec0e85e afad311bb7cc12239441e1631e85d2b977fffcebf0fdf643ffe1d0dfb6fdb428694c52cf2ec1bdbf c384ff5ff5b8071dfcca72f863ed6d3436407ad77240862515c82add4dacbd95fc19c8c30c05e4e5 a50ae4f36802e40b77fe1ff83449ed4df0a959fd1dde4044bf4bb2ad6f4f253a7e1efa6209dd2374 99bb67bdd9b734f2ad7c98e0d71533a4ae587bc85ebc475f38d73f75f97436eafaf156aa5a4771d9 f1f61fbfd3de45ec70b22d3f1fe7b59f55f05580cc8c9fb0db6f5043427813869a240dfc335fe0ff 1990b7c28f01394cd5eb8f51b6d9bd9bebeaf0baa2b9e8e2ddb3f3333d38af4edc64b53d8af3707f 0891ee693f229ccb2ea2d5dbb67ccfddd75b6df15805d9c66bd942bc6f5ee7a24b78f97948770ab3 113f53a6919272a3b9318867f3330dfa69dbd7c19ba4200c6e1d2f9380d4ff8a7947e9d1fb36c71a b92bd6a810673a2c30276ecc7e6fd2c76ea668edf5edb5b459897a6975306ea5652b1fd90b31feeb f390b2ddd98895fc19d4e183a9f9645a910d90ee64554406136c8647637285af46012e9d86ad820b 06b7e68e1c8877bb1c860a3ded7f469b2fb6fb4941f8f2ddc4329ba4cbfed5caafb9f3ba22078764 83fc7e843c67ebad4c2d9667f3b2597491dd691e12c7e74c3fcf403497bdf4c481c4ec786b89c8e8 50c1b011bd496273862d324b0f6e01c40dc4f3470c9f6d580b95076cf6470a56e94323aedecb4eb9 4ed7ce7bd3ceaaba3975b09d0eb7b775b6dc26cff7c94fadb04048af1249acb27fcaaf25fed3df3c 2d417f33fd38522338b44b633f23b9237a4505c3162ef506b79a35099f4d75de8f6fc5eb3e34c077 3d2b851cbb733377e9c2f3f4bde320e0d5c1d6f954db273299d6a19e475a01cd52cd734bfee28726 77afe88dae3c7082e7f0de0dd4b4b5a98fca2093c4c9d6a175b3f72f898f74f799f4e1da8b175d89 fff41f2e590eee450a59e8ce0d5aecc253e5bb5aeb3879d36e6fab65af75f0f5a0451ff576b3c5ea fdc6ada30c1b5d519f04cf5099052a1097f5cfd8dcd58d8c79aa43b3faa366c121e4cf9d39e2c3bb 1be339346d78d879d6747d4158b9e453c09c43f8fa8632fccb287bf12eec26a9bc9620aa04a525d4 efc782ea23ceb145ef9d67933bd7d20df15e4382506e5381f25e08f591be50ea50b42ed6acdca2e4 cf2bd38a6fa3ebef5e5c6fe52dea9e43ee5aee36d8f55cf2b21b3a87f66be6d0cff4a6da52a94b95 076eaad22d4e71fb3947543bb4a386ada2deaefcd99af88f4176e46f944492ec8ba42387e7ca32df 8b64f39e046620eab6c99ddab3209432a3ba011eb39a95cdec7d1bc95cbd958bbe3d871033eeb6ae 202e79560827e015ba7aee895cb525eb52957b97b54a57378b1509f26c3bb43cd75673fd46795499 f5cb06faf856f92d453eba2b658fc5a7356fec310bbec99ab9eae57aa623df6ea6a304a8897d422a 9155001e95e470ffc1a73f0659685ceff9f0f2dcf0b06dd474c963d477e8eb6a5a6d89e74d92fd10 de8e15093c6ff673927ddb6a3607953f732a5f369078a503ad19b214b91253caee25c19ad72dd582 cfb669ae5aa16b62f779b3e8cbb7a17118916b23303acff83a8570bd559e95741e6947da6d837f4d 16f1cf7656eb7a6e66f6f157cae0e655339d95393f261c3a81bceed6777c87bedc2bd5af79b1227e da8e1d1a8b56f933dd8565237f897ef06976fbda59f35aee6cc1a7f4cd5c35f1b789ddd0efd059dc 76d95c917c1510e330d02883491539fd3c696b3a9f9dda5ad7be04ea73830cd4d06bed94cf11a414 830d3805bae9811c75af73d992e54b2ca57b04f77774d29bad8000abf830f7fbee763bfa72e86aab 7dafdacf4fc98a4f94bd5a861643b594ddf44d6bee5d7d73159c9a267679f48bdb4e7e52249ff9b9 11a8f9b5c10062afb78ac459e733ec5dbbcd4ca0497435ab86a30eaeaac6865346fb94aea415e13b eb94ad51a32b2dd2c78584d48da7e89c2f94b02382aa50cb72e3586687584e97c4679e247434bacd f6dcb71fcd41f5b2e8366dad7cf14ad3a95cb62aec4c37ddac2899b89a958abe9b358cc39efec214 8329971b7aeb5cef6977a33fd6a4dd64aebe7aa7adaae51f2715ac3277a5c8a31f259336f372a956 27a5c57b224868f5618a5e5aae0bf57c25e44fcde3968fbf558aebc8478e7d8cba01fb58ebcb5836 df5320fe797a24a3517b1bc643ded8aa2cdcdaa739ae28ecaa579aad41606e4e45a758f74e25a3a9 9675fd6a7c24bd5d5bc9dafd0cd99aec707575c8774235359d448a59d8ace4d9f0b497cb4ee62a2d 7799ef137609934948f496222c9255af20d0dda6c8375f932277aba66a9cb815425639d6d6cc488f 674150d466692bafd6690bce2e63e93ce848e93f1268de6a80d6a9567ab5370e6734231bf2a2be85 ed2e8dc4767d135cbdcb0c4b5a488886fa916f8a3a5cce6515c26e25255bc2ea72f9a4f4a4955e1e 4bd8d69b8b3e55df88e46b721282caf2ce9f379b0fcf8bcf1c777b6104fb42698e553d536746cfa1 cba4d5c7d76d47e7ccc2b2601fa66f6acdca0c857b778fc2bbab05e5eccc4722fd747af9690aede8 e6636c6d5b79f6bc69691eb6fa26251f9bfa9d2a789ada1eda6a9a268b8aa56f5579de0d24693508 0c0947b7dfa153a4cc5b4b088ed981c034c8886fa7c41577f7f43dd723bd2bfb0a3b6f5603619a19 bb5b8c49ef6f0c5d5249adb018971d6a3deb742882492dc95abbf2241afd2b45b04caf4ab0ac3323 1a4dec4e34aaf077e8ec2ee40804ef7d78770f66715b01f9eacc72d676dfb854a74d6da0653c353d abdbf2c2814d694db51471a7a99270f47855600e7e9517906ec0f5daf31efb7aac46ec60f69cb329 26bb658aedec91c914c5efe4962ecd8c5461490748016d8505cabd6f65b20e433671729416c1ee86 335c38a76e589f6fe2e8bbcf9631f9c94cb13eb9bd62fdecf6d6d9e4cf99fae4e3df139fb99daeea 33b32658a17ebf8d5bea78fcf8769abc389bb6444887a258601d95bf3cd322f7808f2227b77326ab cd188f99d0468bc98c9c909e19ad099ddf8d9685656dbb2fa0efdb95f2f4cf9bdcefa81c49172482 680e0311ef44230b7be69e01a65495afed1ad5fde119999c3814c9722713315b97093259fb671402 e1ad5dd7b7d9dadc711ed5bb2aefbf93d859b1d197426df0729b4a69bff124f74e96c5c2626bf29d 2ca370f2f928b27abac733a63cd5e872f5f21d050acbedab5ea8f6a82eb5cd4b238aac4a53f2c0f9 1b92ee0647a2951b3df05b650de1e2e68e62a14471e848530d149a8435249ee90e611bd58ff99577 83f3d8b169e49d427b945f95a9230cf78d6b8bf347df2472dfa9f1f7ca6b5cd85beb173c333a303f 50275ab12957e98927d64b98cd5f37dd22fb016985316b6d812e2f4b7ca17ad324cacfb9167968c4 b776fa3a6e10e7c9a54ff0d9cb08ef36c01c7b5e735b4c3599ef8315743457df689a69e411ab3d2d c0f3c72b1e8011decde36e37ccf987ec3e1bac9d78eeb5ffc859c6caf7b341b4d8650fb7d7b9f11c 0459cf7f330f7bbccfeccdbdfa99e97d071f28b389d094bc56c7139a4bc84e9e783c9d2213e117a5 b0aa9604cab7508e0c822b4bb4361915ef3e0b36f652590f53d75a1303aedb478d477b824c95c112 c9add607b8825feef9753d03e5f18f46e40a6957cc364a6b3b7ddfee9a69b9c07fbd83d0a0b97a41 6067309046980134305333e835a91c82d1d5c9b92c967b946785c7be785a3fe79aeedf437945b22d 919e563d5ebc9e6d7664db261d5f570ae577058164823787f784218381b3c7a39974e7bbad1429d5 6636bc382d3d18e5f6cdfc269d09f344098d72b56561953d71c553a6fdd29ee97eaa9c81dee69082 52f65e491537f1f47f46f68620dfc0cf20df04647c659f5d9077aa433003a96d3d779072d58705be 2374a95a3d1d8cce633b57cdd76520f94caa29f079dde35471653396cb140bce72aa920d4a96f0fb faca6329b84c23f91a44e53df6c3e7ea1666644f0bb29cb96e6027fd3864ebf1c98275a0e1581c41 50c6fc6e8c4999e5fa01cc56b31780f1671ec084270098cc5463190f014c39370017a4028061ba06 f29fd904c020bff5dd35924bd2cccd83d4dfeb2a3c9a2b08ba18fcf072a9a9f8ac118e6cda7e505f 084915a08146081a2161da6cc623655c61f224bb22335da09290fe0174ca4aa7a554764a19c0cef3 368097961f4bd405f00a8a623177b12c9f00be28682c7b19c0d79207e0cd6b0ce0ed3c59b0c3bb2e 0be059b309e039b10070583bbba73dc82796f862abd13c68693e984b3bb137f841e6ea8df699a93d af26549f68bf5c0dd3330f09293b753e4f363e74a6ab1a3894ceef50105f2649c829e2bf4480d418 2316a7124b5407481f0e6329cf63591e0112d220963e0e9001a1c632ad016468cd0032223f00697d e23fd07e7701e20df6f1df4bbfaacfe63a5fc295c64b7f79e677d7b7b2a8390bf1386f0f13233c3b 8ec43a8d165b5592bd5d2cec932aea48793593f2e489e632921d5050ba0e3080dc062840a13b0d50 9c9100cab72c801a0717a02ed98a253edd5077b904a8475c1272eb352180fa292a968601d01ad288 6515bfa1de8affc3361580568a214075fc1a4b2f6b472f3797f8ccb5f15c39caee505d0857a631e2 def2b59b18e1299ab61d5c592c4ac83c40f524fea3d494d3af206252a5324e82a48a18bac20b003d 1b3c403f5d1560f0d30618c3d500a636bb00b34f93df2ee4de1d60cd4c0e60559705582b5d8a258c dfd2567600733f598079d7620288addd1860a5350098c210a5f546cb1bbd37118f6c63ec28d53963 2574d469c49afaa6471d845b803f77bc8b2cb26e2977ec6df4049583a522d171738cf8b3eaa1f803 7cb1fd410178164e3e0627f50ac0c54903e07e7e00f06e690ef0edee08f01df3896580017c961663 6955013e27e3b7ccf727800f9a28c0879532c05bc60ce0ae958fe526fce484c757e55bc104f2fc3b 9a851b656a118dbdfa7da2fbd936d1dcf5eee64e025b4ea78e5cdc5fe596f0ab59226680c4fbf91f 033220d0491510dad5074495680162d8180162795f03e2635c0149efd28064f8c4094922230d9068 7c8993a9650448ccbd03325da00099c9b980787ce25f7ac104208e93a2f1743388968dc89482adeb 0f41a2ac1d63af4a335220cd109d155aaddce932f0a048ea5b7f1af687437f712ff1167e73d552a3 f38fc4895d08a80c12014af0b780f2a017a0263e0ca81bc4036a5b2dc5f26e036ada58016ac60240 f55f02a0c27875473526495a0255ed938072dcea4f98c8fc56ce4a4cdf7c72e9ab74a09a79768199 2e3dcc0792d181a678c1fbd55509874efcd07facbd89ddf84fcec46fe09b388813e6fb25ad8597f8 35f90ee361acc411bfebc815bd58ee2340cffd13a007380ce8c641077473d207b4d3b801daad7180 36ad863e765d5a21a82acc0f71ee4d932a75c6473efa7dee993feacf713a43afdbbf3aed9f1cfa4f c4c44f79bb3f11135fe04bdf4eef5f14f34f31b984f5aa291ab0e1d8025cce4e1e45b0afdb12b0ef ce07b0678e01eceae10076bd9e03763380001b051a607b7aef2f180d9f78889466b91be28edfbbf4 64d29afc3a7cdf4efb1787fea2e73fd5e3fe62cefff4f7268435f1f74ea6d22fccfac37afd0a1060 6508043b7f06823a4380a0b9061038aa0b04ec13bf96de92807ff47fe274b3a5c95f30fa5b61112b f3e957f6d99a1d7ef5dc97dbffc5a113229e70e87f06f6fe80e724c6e13facf79b1f9c7cccbf2ab5 25a9bd3fa4579b01e9cd7f6279b140faac7c2083ce12c8a89701b25ed181ecf0dd58cedbff817993 12675b2141aa9a588ea51f3c9f7a6af8bb2adbe0f40ca1ddf3a12f7aefbbb9a9a76e73d2cbdce0a0 9abf562f65f4e23d44fc7c5038ea4c8fb86fbd33523c8a4b4c3b285bacb4871ab0b72d3fe1eedacf d0aba4a6d76a9e59dceab4fc27ec3609964d32071286fa3f9a9a548f4bc21bb2fae3636295873e57 1a7773adf4aed5b89517b2bb1d9fce5a384d906ad69a1d42445aee4704bede4534d86ecbb7c56ebd d54abbd5c1947f0270175d22759f877436351bf1183235df24132f2feba58993cf0cc6dbeaf1f527 e736b1ca26143501a8ff3562e24f28c2e5793e08a7eca9f9d9e3876769481f94e552da651bbab2f6 3e7379792e39ea42dce25a7c7b03c64cbf1ccda9f9185b51f9d3284f5646c39960d3dab710dad8cf 379aa30073bbc3b3df180d6e8dde72205e8f975079a199fe489bca7d286af5e3667197240021a915 961864139afa57ebbe0c350994fdb1caaeb85d6b79364b619241eca2e3d988ce2c66508bd84fb33d f432c126cbdb989c0f9fbfeb8739d0a04bdbd9f0d9b2e030e46dacffe959645f7f5b4c2fd26db197 8d1cad6be78252675519f9edadb7edb5c923b66c05ecf09364a9764db1d992b5de9f5a613f6ed96f a6ec5fb5cdbe7908c96eeeb9b23dab09061a1d4a156ed8c25c7dd0a56a7618b235bfffe9b45b7dfd 39087b16688cbaf362e37b0a7456e5caa283ad4aebb68f17773fc1b241a178699e9bc54793bb153f 8dae54ce04cf8187066aaacfd447e65eab430bd4ad59686f5ccb6ed5873faf59aa6fd350d3874fb7 4622894b3649bcfd72e82f9b5c7461a59834f087a17e835a7be6bbfaeeac2c2bd7de562cbc4d6e7c b615902db9451fa26293bb84e586f808aa41a8047efd33f27f5965a1a9dfad45a5eeb0965db626be 8db5be0b0f1fde465bcfa1b61777dbb8bd5df286c34e2059ac43bf6e76b5a55707553eeda52ab799 acfccb257bec42e22469e2d84fcbf84f8c2c3c399c7e42061280faecbadf1b41fd33e85deb466afc ae45e63253cbcef7a83f8f27d83ebc7eb0bf9306ae8aebd36fc339b41e961308a94af5dc4f7955ee 930a2ab711d1ad48e9789d1396b445f9b36c9ccaa3ea3555867672a114d52e5f976ac9a2479352bc 5ecc58f30ec9fd913539209c9f24d96f45b30e360ff68d5bd39dfe1864b333baedadaacf8ebbad41 63973c614b27e0e8bd43dff46bf5dcf5de55eee54395aee67ff7b0dbcf8983d8a11910e5cfdc2f94 47768b2b43eb96548adc915ecaeea39235af9f3d0bbe406dd311e8b1893deadba2af9cde450a78b4 7198b0ae1158f8c66072bb8cc1e4019248c24e1384969c69ffc1a751b1dcf5b06dabe6046cba5a6d 090bbbd255a69efd1c5d3ab69a7e0dcba33298960d185f972207df972c9cbf58f31a73b7ec82f436 574d21653a5c3157dc7635a4e84b2e651c06b56f714723d0c6aa7e9eac4b3a9f0375edb630869a84 861b35f411a0aad48a573ea76557f99cddbd326aedde8924bdd9d7ef9d678bde5d9609874e086f02 77e386158cf2c85a2aa5a8d2ffba864ad9cda464d9e4fe3ff8f4da2b6e3b9971d11789b97108d195 11a8859d7e1e1347bd55146eda6d26bcb46ed984d4e7aa9c5755bc4e2a9fdd4c508cc2b528472dc2 97b3777720cdfbe020c19ff6d70929aec6922e3a66b61f4b6713cbf238a2172738e9cd04e87b0e36 aa27ad2c7fa2d397eb72bc05ef6f8ce9d051a1e8f323de38f4b6ba41bfcf65fd3c823c9d4f438176 9b621d4dca63df314d0dabf04855317aa67cb6ca5a3128652f470de52c5bacf792e69d565a829f11 263aea9913b613ac2850d96a9d3fcc1f11cfa0ee9d6bf91992e3a95d95e30bf680bd9dbab3242e3a 316f2707fcfb602518c4a384ba72cbd6429b68663c19178da33162f44b8121f45613e0da6d722f68 522e27a96105d195cf062f290649390af43402d952d5969c43fc50b2dbad898414e74bd199ad76c2 8ebe5d056a8c7c9f7bf28d0c0f73977ac072fc7965b0bd2259675eb3e684d190fc8d1ebb43822e92 950a9d7ede7b749152677da8e73d924bdc4714ae5bb94f985a6996ae554d6273b20cf6c2a9daa336 17d4378653aa26aebf5b1755100e70c518d728794a2c6439d73f16a50afcaa88eb76da17dd22d312 767ba92fd4fa7ec49fe0d692675bd181ebe0b707fbe8236956810d82d1f1f8929af456253af3c15a 05385d9d53d5d2fb456e569defae6fd2738b3eb959bf46e466c32f93874e411869537737e2faf650 349a56b5d6f68ce67c6269fd07a6a91017084a96c219b9acae4969e95650a9f2702971539eca2271 3e9a42dd7855f9d33e57e73905ff1610e43a2b26e444c788d8fedd59319f52ebc0e8cbd19d8eb823 44677b69ac002fe3458a83f916e95351401c9ad894a06fc307de92c502ce7df21ede52d613bca582 653228c59f22cc9c26ae0ccb66207dcb5399e4b0e2e912e8975423ffd1e4b9ef8892c3a659d1cfb4 49e140b3a840b7089c6fde658ebb956a3a279e7a6536d4a71ef3d9ae1bccc8bff718e80d46b4f58d fd9b6fa96dc1eeda57ca19381f72874ebf4fd849cabbb304d3400cbc75b56b582fbb9da02f4dbca2 eae446a26ab47751755a19a1e17dba4812b86b79b630af8a256e54b201df36ce5bd9d3d462bda4e4 b88326390355148e9935c39f3f2592e7f14c12722a491b980d97293a5e9d1614c6e8ca263dcd95aa 74ae51ab17166caf5d4026d301e5661711b90b8e2b92babc8e44c3a45ef8655ecc61f74ba380c9c2 4e433535e721e3b13f423259e80297ec1087f3a8f79d43c3251d1dc0d3616dd16c13eac147aff22a b19a5b4e116f27c85c35f65e5946a2ad2e521b4ae4dbf480e17abc46b183dc0965c6da04a1a76607 a77387bd50a8a86f9d5a6ff2658a205197ac8585ef6322b2a06a5da2b1ae8cf02b519be3427fbcc7 7a9fdd1d7dbbf90c9aaa1708a4787665b89c5e3bf9659919e43646f79823660c9223e6a49d739f9b 7e6e23b1f3466f669cdc439658d906078f4daa0edadaab91ff72682577164be26ed1d50456c425ae 37eab16c2a9f27e9e966881616cf324c6d1011a1887695266b8f8e4c9cca9322c19e3715bcc36f3c 5ccca45a58bf9c0fd1f7999da23aaf6de25e6e5ce1d9b20f6018db7f8f4d1e935131e70ddd4ab61e ad7b194e4f6fd29da8974e8b255b4b5f1f5c2bc309b328d061e3e29c03745dce89e75191395dda5a 2a95f16594274bc209f535eea1dc45569b961826d3cc5085cab38e52c4858313db75fa9323381ac2 71d12079acbf1355f453504c74f8ae3b28a437eb88b91d76e039351bc2f0e0bac83b50e690f373c4 337b6854f39996d864d25d756940a199a9a73ef3f63c65a472af9401a5a5d4881b7d3b2df571c8b0 9e35a46bb53b7aaf4bc8fc3036046edd5633c5872f11bd6c89bf7a96c6be473b9131f332535876f6 24b9bfe828c1d59e79ec591d65d14fb79547a1783e8f58ec5680e79d830a23d9a795772ad0f7115e 1e47c920e777945e369e718d33bcabafd2dd7dfd0c85e7fe0702fc0d4ba5154c04d6a8e9805c9118 839c7079809c7814408eb2e3d7a843d75fd5f06b45ddedd7e6ee128d75395a74e46579e689855752 28807b1c248d85d253912e370596da663714d1cad0282ebe6f303aea065924c7e2507e4de270ae06 702a47ad0d21db206c2dc39ff962bac7ab15e83575ea90966f7553e36a3401537fb506b9e3e70a72 ef5652203df7899b9a3b4f5c90bb942290bb326f90db22522cdb3ac84db1d0a32aafab9d7e4fd6c5 537134d152c75e47da9c273e7f43b725f6a3f23a932df5c442f546b0e4c19e5078b74e6218c05630 622dacefcc26ef122f28db688dd369b959cc415ab744a4c6af0a93ca68ae084a93860ef2deba02f2 3ed288c519c67258837c4dbc837cef80807c3d1e12f3f5b70ff2c1740ef2955a0ae4ab9602f20edd 0679a3f9dd8eedb4cdf53549333744da1e2bf394db11eb77cfe39e5e5462a131aed376be2751db2e c111ad7693c2d4118421a55c17ceaf37522edb588d53d07bc88254e6f94e03184d21b10885586a62 2ceb84a7c158ce89c56a01985b8d01ccd3bb58fa2f000bf1f78685890a60b1d800b084ac014c5db2 002e6c8bf175d709018c64d695b73dbe592455dd681f431acb0ea9758456a9eab39fe7a8cc5845fc ebe528ac4e632501fbd8ab5f2d20d3e886e737a88f649bf42b0bbddf1a00f3ecf10de0412a6ed342 8e3ffad86363392a007e53a5581c3f965517c01f6c1a4bfd00100402b1342980a068f26d90d4b40d 10c889bfc39d8701fce06c001ff2f1fb0f8d939d25dc5bb1858a1bd59291b144a9850edf3d956b3f c87c91c18b14b5ad2978af7de2d18cc115f29bc68ac836431e8146f95a1accfd7c52a10ca1f22980 58251820fe800448f7250064caeb00d9b52a00b99f03803ce83096e62296f70520cf6afc6bcf2b0d 9057ad049037d507c8e97004c8794e00643d740132e96d001221af1276166e4951d44376a320cdf4 44b8b8990e37d0cd3a9b9e1f9c0201a74d4268c80a6ac27d3ebf7967e9cc2d9e9243d07b908b1bc6 a601b2ade5018a4d5180723b1ca0469a0668cb90003a8a2c80ae3e2ec080d94ec82db29c000c45b7 b1d41e004b5df300832a02c0d2a01acb6804d0877e05e85364007a2c04005d52a75876f9e2a594ba 69e9f3732bed36af48108a853e3718769a34ea860e718d8e16068985ef1c1ac6767531d39d8785d4 547e6100850648dc224002f4ccfc66be8acfff82abfd8306b0395606d8b319003c7bea035c92e700 b7a6278097500070b986035c497f33905bf1a8c1d0d3041033ef37c0d1250f70283e19b0fbf609b0 8743ea6f7473532abbd5360932e71f6e71c8ccfc6a8b3cc38e8b8d2a8b328c1b272d23679f7c2a5f e60bbf1a46dbdc2fe371e6a3255c35f998c4e7fb17f89db980e0324d40546b63400cef1b409cad7b 2ceb2c202ed2b7e4dddc02c446690362725d03a2db4f03a2e7aa80a85b21206cfdf5cddd1534535c 3ee4cd2e89d3154f3763cde97d6d5c20af5c1b577baa0fbb6ae91b285dd540b51ec4fdd58fa45fa6 e86f737e906ae2394e80ef37d0e1dfd437b1fbeed351922e9c905b33fb0154a78b03aa9b9501e537 5d4095f24310cf76cf80a2e3fea618c602149a1d030a8252804aa7f49f30113fc35df9e782d9d0eb 343a216426db45d008a967fae78c0d1c138e1bd6d1ad9f5c8e6fdbbe56e8bf6cc789e3f84f8dbbff cf7c7f01df6f31393a3787005d2a3380eea72c40d76b2d40dbe905a0d5d11bd0acc1249e5b08f662 792c41e17eca83c2635752edae92112f8af8604b626e475eaba9190acb877eb6db3e36808b76ec5f 0d4b38f41fd6fba7acdd1f93316526cf6ffe8d7b934c87ff60d61f57ed7f5cbdec6b3406ec893b03 767946003bea188075e51e60cd5cfc9af82a00565a390a9133f3bcb6c35e4911c86fae7a146c16b9 4b77354a55ee6ef3e708fe38b4938889844327083aa1a989d3f83fccf9af4c897f6539fc71d3fe4e edfdba7a053adb02c23cb50342189f9e42851580a0dd6b40c0c22510d276fc5a465401bf7fb4c4ce 44419955134f11afd2eb0ad7f6b30d347b5ae3bf7b2e899848d878dc4509874efaa67cc2ff67637e c749fc23b5f7dfe10ddfba6c721e5c801c1f1220c34109c8883906728a48922ca4eb8305d276ea03 69e7ccff073e4d5cb211fb8fecdb53eff9d40bd33faedec7c71a3fef11eabe6f735207373850d257 ac4d7ee70217b20fe5cff50f849d5ad08d3c76735bfa0b0384bd7e18a9dbb91058ebade1d456f432 1c2f6e35f2390fb9159fc4de2656d92450360cd52cf85729aeff6aed4d0cc837f931ca9ccd076443 eedd5c93ad6bf5b4ed9ceb2fa777e2267c7878dae9e17e84efc6bba8308eb6e55b65bade6a996815 64f6b3650b9e2c165dbcbf8907e2ce7136e23a3fd9b7f1c744762ac226d8fca38ec98dd71cd147ed 9a6434240ede5e542adf13869a240dfcb7a62688f2b2651ae724fbf6284ec5ccfe5325915d4465c9 4df5111612dc3da5e845173d3273e530e066faa5214eb37d598aca1f419d38e9bc36f6730f6374a8 3c4ac3b3b7af0cb9c3b536102fd776188a99517fa416d6bda8e83e7bd9799eeeda2868c60ddb1e3a 2b6f754ce06ee2527df7668dae5e8afe6a60127bfbdd20bf875cbb9b54bc9a3f1dcf9d414da539cd 76f5302abfaad3c94a6b6ec6e45cdc0d5bf8e730e816cee7f0d93cdc42e5b67df647d2ead387060b 28096548c5bf3737a748179e4f898e832c98f6d63dca6df2902eb5e80b5e6bb60467d8b8f53fe786 04223208cd49e3a756d8c85e86b5c8ad766bd93dd7fe0943f8da8d7f00ea3709339a8b4941a73139 2d60839b37fc46b64ed8fea7bd51fa507f63f6cccfa9da9d1b8f7a67553ab53ad8f2d46bfbd87ed0 3af89b718b3e2ea7cd163b5b346e9dc9ba213e87bb205427f1393b9e3dea46669baa45e52752cbae 4936d9f84f548bdeaafe687bd8a5bd77b79d29e7fa92e5b9e44b727f5cb20c78557e5cb2df4c8969 c41ac2ff2fc515ec5b07d7b936cff576aac99dc770a3cb2fc88678bf7f1dc481f2de49f591bed3ea 50b4336b566e5df6e79545d5b7d19defadbc75e06187c5d72a7becbbe4e53e7102112cabe7103f56 7960bc2bdd620fa948595cf9ed925ddeca9fed90fb13239b6cfcf7374a52302cf1f20ebaa49e4e1a 9898267f42063ee17e508b8a9da13f2f4f963ebcda1c3c07df5f3d6c9ffab8db3a9671c933893801 8fe1d5730fa6aa2d99642bb7212654ba3aad244835a2bf6103f17de9b3d0ddb281961ba5c86b86a5 ec61b9b0e60df462c1d77ace5c7551d9c45ec78e897da077713b0ac83ff2b373ffebe54da04f9273 9bf83a1383ec375fc0a70ccf09d89c536d89b9a0720b997e4502d2c47e8eeda51d165b7b5bcd8c2e e55179f42843abf05db2f05eaa94dd8db3965d18c0167c5a10a6c32d6813bb5dc4a22fbd74e33044 2a46a05b4dfd1c85e3245f20f73968b76537a7751d5dd724a2186912c97c6229a613498225128af6 079f2684b77a6eefec1f7cfaa593656831d34b16bab74bd96da66ed954be6dc147f61bcd62ae1a5a 6462576d51f445795d249fc5bd71082b678301a59b7e1ed7de3a9f89177cb77821ad49f0925243e7 24289f7dde5446f5524d81ced3a16c09d451ce3e2ea8340fcfae341fcc16d27c58b8269238efd161 aee364c343a03c1fc3bff069c2772d54177ef029761ed1459f8f04e3d03b18bff0e9f0e3ea2d036b e87c9aeec40b3d3ad4a4bc3856c32a3755554c592a233f093955a0a37e92a386f790b3d7564ab2c5 0891e0e787151d8d3384eda459137c2b33e10f8bf68d0faa25860f1cbbc933f86dce3304b44a2e9e e6b9b65e24403fe9c8a40f2d9b84d8b8611691e06e3987ea2d6d91d76e9329aa49d945dc2df64554 551468cac803a6629048458e02c4932d860aa4791b6f49b6c0f7c5559f1f898e62cc84edc8de08be d13df387e9fccd33f97b9e6b3914c3de76ce7772cb766bcf06f33c7b4b266c73694615f33afd79b6 83585ec324a62389bf488e7972b8adf9468f4fa8d188d65bcb34a6bec85a4e0520935246ceeea340 bb5346b60a6f4c9ab75205c9e6513ec1dd3d54161d391e40b643b624f8ba5ce50f91ecf381558a07 8d45a5c7b52aed317bdb8c97ac44ee8e4cd8002f06a4348436cc413c3b58a42b855cca09a979849d 29db7a521492af16c9d572534b46a1241f3abe88e2a10b71cac6345f34f103f7f5ed068c3ae85ce2 f5c345cec9b9dd1592ec77e32d21d5d25b747c0e88783c1313fc4a19e70fd73ac3336647e42ee44c e3f87063b177f55a65a54daace0c44f4fb209f016161488f6d775998bab54321f758bfa98a9a4749 d72d8ac46ebfa812058619e08df6ea8cb36440629757d688c5f393f1281e80e056e52edc9d52ee9d 328d936d6aea7bd9e295cce14e4955a1f4055d2271ba6685c2b69ee28fafcc836fd8ef17cf5c3e29 ae6dd2287b3f28055656cb0233587b2aa3b587163dc9ccaa74f1996a146636d12fe42f7244a10f6d 4d7ae5c685d89ff7107e7aa408ecfa61654c34065f0285f667f80419aec23ba2bb0e8b0c37981d4b bd997073cf693e5bf6a07e74cd4d355fd63b6d4e5326379b9756c8b190705dbeb99d65b9ce424fb1 cf54e6cdf6ddcd8379efcf29661860280301f11ba4499bae2e14668faa568095aa49ade0a14361ce a44e7af74d873894de63825ee5973877674f5857ae7cd050efa3c8277a8808b414aab0852d86f979 cdb8e66d9a616239d8f9b9adb49213d4ad619fefb1294fd474bdb8bf656c4d99c19a3cb72c41f485 69816f1979949372c31caba64488d11fa7370395fb8f781e5c7f15caf73043ad4a078272688825b7 234c26a934a613418d2be3e793e8e2bc5e6e62dda8fe7db082bea8c90c551ba73d624499273ccde9 707e8176f83ce2a72a39bc510ab3fe0d9c3347694a66a99a6d65fdddb3511f1df31387950e9dc467 6eb4b2575b35e8ac2ead169220304a8de2bad11b65015afdce3ae9e800a50af3f3e45d40f0f283c2 65fa4efa430910472c4009a6dba5f0d62be4707e7150b11e7b33d157e75345b51c1620e30add45d2 d77204e79ede265f5167f79ceb7e72d95a40331956289b6941597ee13dd46b0b6b481633b978a45f c8d07d6bb8b5693c91ac0aaf67df5ae19740efda1b5bb19a4f5df437399ee747258a1d500f8c4977 cab9c242f940949b6ebec95a9e7b100df673c3dbe6f385dd8f540e9359e6fb60051d4c541a4d91b6 881403474332eaa80c97c6732f5fd96f5bb90dfd1ce68836b6cc16e29962ba838b1fe851ebe2a978 b45452da5daf8149613f03c573e30326484948a5ec7bd9afb4dfd38a12df234d5f5e7c71b716ae27 b66cdf0ebad0205e3c27a7658a29b23394ce3d951ce51aab14592894dff815ca3eb0de21baa24310 5c9109d17a2399fe3a0f97e10b915f3a77265f157039b7e953468e84d54a96c6d55afa7ae2bf3005 7a42d61852acee26a557e63730d9227990a57c01640ba407b2f9eb1c64e1590a64414d8ae56e7bc4 e33db7a1ce2a2c06c341a08e8561458aef643adfae1f79562b15e2e577a78f165095ce91b5e9e83b 5527d8a6f2c6e4c3f68ea6d6c1159ecdf8737e7966ee396f2743f1db5c245b3fd5c80c1795987437 5711a167a5a643caa667a7f4e7b80622f5d803d9496e164beb02b211928b651eb7695aad836cbff0 75a6806c988a2558ab20db90bc24f5a6349f87a1c16f6b8132ed372b62e1ffd83bb34645adae5bff 9629482fad882876d87728f6bdd8f7627b737efd81655576a5bee4fd92bca97375723176cab23693 b900598f83b1125642ed9e171125bda44469293459911fd3247fca8eb090e6ca2f3669acee0ca155 5daa7866cec4a1ba3be39d76c3c5fafdb63fbfc1007a4420951eb2309dcf2520e5bd06a4c2a73da9 553cb974814c984b4fce372093790648f51d0732326900a93576400a091a4851318124fb8da21e9a 2fcc32d883c4f399b6628555ce770d69e1493e111e46061139c7be4471631559a19ea6a9d0756963 5cdc14de4c66ed3ca8552ee5926208bfe09d4df610182be10b90f3e0dd9338e649d3dbfefe240379 e0639e14bcad1ed635208f227a549e7c8ed640be8487274e08c8772605e425d0f2e478047233e381 9cda45206774af00d9e1327ddc1bde4d391bb6747e27e7112f5726b3ae262d372f49149422c79fee 18c5f5b12ace4c8db76f19a15673fb418a29d1c5bb6ce318185bd201282d78062a9d7802551e129e 5c4340b5c3aa2765c393751ea84ea8e18935026a86ed3ca9bf819ad32250bd6106a87eb6075443bc 00d50c2198429508cb93f63447619905b2c4c77351cad24e2f228f90b9ec50354de22a2f59a85f8a a150f474a0d8949e0fd24cda6b9cb82a3df1587876098c1fd9135063caabf392bd010d43009a79d1 8843271212d0856e14e8c6330374512c7962b53c71a740dbb923d0a5330674b9a8005da1f2406716 43a0cdfe03e898edfd33b9d9055a613766858c2e13c3e9b3af6fe5ab15e984df05c4cbfd4b27bdac 45c56d950af3113ec3b323d7a1e94240099292bc0860afc4e516083ae58bf78b0a57a0adc11de8fe fa05f491f24a7cc61860c881024c0ce2c0d4ccdc370b72f6dd0326975978b2bf22409c4f92df7331 1a65608afa0c981403c068440218811a03438c2f69b5fc5818c4e238885aa57d43ed4f1e45643197 196af68d971b6f55a017c7114b9cf24060af53e40d7ec6076a9ab37e7e87d13ef3eda40960b62daf 4a77c1031b22556053561a58fb5c02761c6d79d21e033b09ec806da71f9e5c39603bdeb1c1d6480b d8acb30436d7278035da26b0e1c1021995bd2292467cbb8c9526e38116e94e1a2a54b1b262aef359 51745b06f70ace5586ea122229474b1c36925a5e13c880b7c3d558f01b8c56d7dc37f7f18814bf59 90dfba029cdc41b91c5ccecd00d7cfd4803b6c7a1022634be09edd3370a70006dcb22c01b7c2bdb7 ac466de0c6c91d70139901aec3e681ab45b6de2fc029238b7796ba448c479f1473d8aa35a9dcd373 a17b23eb73682667b73552de6c250c97d6f48fb5f965fdec3dbebbc68f8913eda409a14dbb087ce0 de005e2d8c816f3e77c08ff24fe0ed1d037c598f22d699de96808fe646c08bc10bf0e45e80d06b51 81d0d9d941683de5631baaba8a86fbb349a47b677bc8bf2da829b1c09a849622ad534ec7f0bb2e7e 28f9a7b02fd65b8966108746f4f9e338f6331d3e39136cfd77ccb7d501618ecf40f4a6872066223e f09dca207a933910c54913444a5b80f0babd41580d2220ac2defdf4e2a2710aa159f0b7cc2449a55 71199e9c8303d1e21f0d2e35841225f1c10c165c1fa21f6eff61e47e973e9e63bf921fc0f357c4c4 ff34f9226b2fa2991fdc9bbb832cd11cc8b5791ce49c5406393e1b82cce827902e3706a44d3f05d2 b6d803a99bb8835497d498d023ce917e025fc921f33a0a0dec438be60fc72a36159b193486686c90 4dfb2bfe0255848a41d0f2ab889f969043abc77db15e14e3809cabc8d57b38e64125ad1e848f6f64 540e0fed00844b7204c2915315947b6301ca238b83f254e2d18b2cdfc2e4fdb915b4cd66ca1497d3 2e3e3aa7abdf46130531230e8d8238d0b27628c10155e22f61e7fb8b7f6fedfde05e0457bf566a43 aede2f2f2dc2bcd2a10a5122b006cd293c416b0425d012e33c685c6a0491eddb8548d791d0ea7108 46fb5fe1f93c9a338de582d4c8fef05b81c8b08d6cc73f7168bf373f67f5fe29e645e10d73a286c2 81bfa21b12ec18f445760e31fd7680586dcd402c574d414c113a1063ae47d0af430ef46dd6047d27 0efe04f38a3e40ed485fd9b71f6befd7aa6cc8d5db3ddedf66e37ac70a85fb8da8245e2e6dc970e5 5a047611ba6ef02c0f57e4499d8e986374d1e1f7ef4a5eda4d9594b629be717f89b35b7ee156c9b6 d30f3bd7f9482ff99f9e68593364e34549b25f3ed43f29f53b96eec348bd63392b7ef39725b86556 efdab5781a35cef240691e3bc18b7d88afc6ed3d562bf856ab6dcea53aebad31efac0ee95e7f69d3 99c1a2138a8e9dbe24cfe723955bcd324f6c3fcd07e8db84731462ec9de2eaf05c7f94076eeb7a44 365e447851962c62a86865aedf73e8dedb7da367f9d1725228fef6189db677fb776178df4d854e60 53bc8bf85a1891f4c22db468a72f6439efbe5de2bf07e0eee509371d2ae32ad98b8c2cb6121dda42 263e70ad5cbaff6865fce7d6faf17bb1da4bbeebedae89f7261d27475ddaab52936d73fb56ad5555 8c0d4a4168e8586c82182a0a19f89e29e155e7fb508f6e3233450ff2a3c8d639d698a6a674df2a4c b8b1edafef39dea657ddb1303f8c87e7d2743e889e4ca71f77f5556fa4ebbb1e36881cbb6640b974 9c8c78ebd04ee8d92e317ca0b52df3444bd84bac2d9f25a96947d2b1462756ce5a8fe1ceb60c3cb5 aea7bc9bd1dab44c94fd358b7dbe8bdcb215ceedd4104d457ee88bd062d788a1a215a4900f153d7a de7bdbfb770feb9ec92e31c2a40e3d7d47daabac9c6c97a864ae55e5a225db12856af3dce0aca67a a5edcfd26144c77af4b1be157fbf47f551ea3dab63b3f7b266d2c4beea94b86b655553a0c29df26c b9aa4eb5b2708b574a56ecb5289ec79450b4d38b5c31121c640b3a9d48e71fab700ad98d51282f0a c1f4c7c6b7f92186fa491af08955c36d8ed7565f1f9cacf8f3f8ac8f1277bc9ec258b6363559a1ea 14824a95de04229512ffd22bdc219828579540ba2c5ca8df16e5a26a45f5cdda854e0ad1c1fc6396 98e70daa75cc8d8a27c8a54259393bad3d0b5953e10e59e24a4b2844d6cc4789b049df1f0a62f91f 2fafefebfc21640031549fad544adcd42a6fabdb6e5938be67c8a2a862ab92eccac7e2b913778bea 33f92a74124620ff98e8c17c3f93a2736fc7e072a3bc29e6b0b519ce9abc19cd127b3b65e6e57e3e b3b29d5a86bb3d7ae96d2fb94a0beff9c3bbe18a8a2985906ac9f3c2f06f07bd9f17fa2376614d4d 73f79b82801ff24b22048d206fe9d00895bee70bc4d27903e7cddc28ab957329aa6ce7b065b79f9d 1617d32cb15d2cccbcb8d898f47177c894c26b3f422fc35d8fb77435ba7ba685c71d4b59c69d4c29 012a94b4335238e13ab17842676a79a35f5e340d4350e6f19135bd7f420652ea7210c76e8d9b27dd 07921fd8e96fd653942fa0cd7279c3bb59ce4e0bc52802949992ecc819eeeceae9aa4664d2c25d2e a60ebd782d65c57376f23cca759376aa324cb8b3e224d1c9d61de3b1acac8c7eb1bd8fbfb7ed737c 549d7b5b3d1ef098197ef8a04b773a82aae7f57a26ba1a9c1ad152b2b0d0b6332aa06de74c4aabe6 898927c51d121485800e4634d2a84444783ff8746b578594fc7c321f76dac97419c44e7d5fe7c379 460d83791a86c18a66fcbdd18bf19410a9c4a696528f998ad1d29d96ded5f39a398aae7ae62ccabd f32b6d3bea9f3411771e112bfb24230a6d28aa5deaa7d5082fd8e14e7dbe09772c6f4aa787673e4f 0beb6a6ae28939452e7304a311d0cff753541255993a74452ee14e1a84d1cfb33e395dbfe2d86ef0 8a99d2108b11a713ab3bcd97a8d3ee2b1c5d75293d5a8af1096d3b64d35a3519f62f9d91c3542a45 2c335657cf8b98adda85f220ec6e1ab3b02e8cb7cae3f0bc2946384cc8a3764391533a9993a6834e 5f3293195722f178442283c1aa2789363a833e30daef2642e5df39b4d7c142303eb28fef98192bdc f5bc1abc4457edfd29ca3d3657ad6a6c1e9a086f3c62a5df544421e8907a7664598d30821a76d7aa 1ed67935a13cf64953e9d72b25f97daed7e554a4d791a6dde54432e3419faa8bce28761719bc4d09 a5ec4be3b7ab7299af96c5392f0a0ce1c936c68b62b4f88d497a8727ea6666357f8493f689091990 4f10fa223678474353f6a689fdf939625d8a07f57c2736aa774af98038ecae884bb853525fca6317 c1947ecd20e5f7c960e551d314e4945a0d4bd34e352a114f2b213a8b555e64c28fba50ead05d7e47 26bd720abd53c8dab801ee22c6252ed2589a6c67ab7559bdfa3c314faaeb1fd0cc9326a3e84cf22e 3f11ef9cd63746bafa3eab891887f3b16c9a27d08ab49146ff7e53b574fd1c8ec5b9bdf25c9e37ca 4075368a319deee5d1d3b9caa9c1e129cdd81b2691951b292e742624320bd147aac2baa06b42e89a 4ff2b58c9d0f1d9d512d1456dc1ea7656887ed3a99333358773026c0bf653aa8244d2adb3a75294a 281fc8c591a73d69a9e8aa89be85c8928d7922150e39aa11e83092cede63bee736729a363135ca06 9eca6bdf7695c0397a9683dbf35e9a8bf65aca168c9544b1a58358a874eec2fa3106a1ec1c49dedb 9f102f6d1825d48c2a3a777552692eaa5825b6d7ea3798e17c356030fabda4094df52f9d141d2f05 c9e2e810262a73351f9453952ede9c070ff83978678372b41045c768a1b35f144c36304926b59bab c7e6ce53d2f66f8a565db18e2b6fedf49427efa22be566d8495c3e9dbdb0b9a6d642a522fab983fc fea0ecf8ba55ba85ced082905a1e105cfbbee0d8477c2db17d06a2ccbb8c2718acc7e4e8cc3b52a3 9c52b547ae6a9d39b16dae8e414b3fbf71bba8f058c7ac26028f25d60c185c7b1de8a78a44e0310e aa2804a87968e407f15931c34f3619e3f59a4611328f9c4b4126dc0fc731059bef5f523e147745ee 783e09c2a6bae32d41df84ce795872ae7d5973d1dbedc0f673a1076bc8098c19d9698ac1f1324f9b d99a7fd74939a7418c62d445862c4d9f15a2bac45bf84587111e19286b4c9fe46f01c31953306643 3ae0997e0d7033b7043ca6063d99c8e51db66de6d2a55631753a0c3306561ce9d16d7921a9eef6ce 28a3b68ca1f80f7afe124b35d915c4f0eac42bd1d23e1431421bf689cf16ac516b394c6ad8ddd233 6ee7d26475f7a4f2cf3b4eae8b41860c6d2981a8e9e148301c60e3782ba365b1ae95ae0606ad423b 00f7d91cf0f7cd7f9a0882502320185062805fdd06e0ee7c0bf8ae4903be0f6aa586386a6673a171 25e92acd4c9cd8daba76d80fa4f0935cd2f2f441631253eabd84ea897679a53d3a713723b2639fa9 db9a19c7064b3aaba6fda789a882195b91e5466c4fecae59979032b5377e659218ae953314d6bdd7 85c02bde560381d22401e9dd3e0fc13adff4a43b85a0255e3c59d3106c940c0816e516044bc12304 53571e82e9ac516c0dfafe7d9a59ec0eaa89c7b86ec618b1a4472e72430a4361487f687968567cf2 0dd6bd86220ffbc47993b63d0bbbcb86ce166b4b8ad5f939b157ee73fcda5baeb05e6ab80fbc6693 4b2049cd1f30691e3008ba6764860dde640908bc1df7e4550022986d7972718020f22e1024c979e2 a420f8aa7421f8ceba10bc680a04d7b34c21ce4e3b196159ab1aa3793c13dd852351e42e57f04d93 16d7a333264854d29f14862e1bd7e5ba56f5c40e5ae29e094aeb0d55d86697c4be4139f8753f9c61 f152d18129155f0351cc1c3de9dd3d396340d832eb4945f1649f00621c2b7b32f3b1b4b8f4c47900 313505c4a1bb98e9c97e08447decbd9c1fe84014c4526e4a589dd4d94d57e3e659c868c789a28707 ddb0287b37b4b4c8a7ce38df58265f212d7370b998d5b930e939bea796c5d99614c8c40a778373af c455d1f7dcc234abce817825d74052930b90d2eb05a4ac129e544240c64f1a908694f6a455053201 7d4faa1b2093c4db93a90c64245300520bcf8014580c4892f1de4c7a072803a94e525fb0289a85c5 b14ca4e5405499509224b15692166ab72d1eba26a32fee9e79dcd9c0b874a1a9d7f3400a66638bbb e9fb0233e8de1ccc9a3c01329b5900d99dec819cbd5c20f751002ae8cdfda9e055028a40117adecf 46d6939b0514e94deb29f2b0078aca6340d1840ae47b5e060aba4b20af0d0ac8ad5d401ee94c2d18 e9a21499e85e3c675020cb8797579424c39fbadb20e23becd0dafa1092cef5f40b59c58e47bc5361 d718c89b2598b78a03e4adbd042ab4d902a5054e40956377a0c613afb04d200474201b01eada4d78 f22c02e5a65a4063db1950b7e8d9930309d4bda20375e0eb686f8eb8f78b9610026a44d480b227f3 546bf8ecc4a9deb2a2a9ec34832ce63f20f366ed4070f18e11a09df4e841564ba12bdeb966f781d4 fbb4f60a9b7b52baef805af347a02e6174b1a199f20d6863f606ba1124815e9425a09fc718d0af90 e949b5e6c9b90ff43bbef264730706321cd0977b02e8ebd8db876df508f4ace4fdb35eaf0db415db 2586e6a2a35746c3aafa88da3eb65388da58ffe632579a3c42e68c996002e44eb69e78b7b0b80482 860ff47d5a8e383a9d6fdd3ec0977edcdedfa96f8c00a6d3e280d95e1460f15412d8d4b604ac8626 85decfca0458feb40796488227271158b2e11b90953e30a7f71598d543056642f580695fae71876d 74b473b7520d8facaa293bdda32195b858848f684581c5ab239ae209c0f1ee8b79c0bc5f43b8fb53 dbe1f8fa56d68743fb5c15997c7f02bf6753034edea681b33275e0facb3e700369055ca37105ae18 f0c3261a11e052a19227673ffeb8894c7d1c51d7817db606c09eec375a55f307ffb6e3cd7604ebc9 6b1cbc93125509582cde9b7571c83d76cf1f0afb98a27f321e7f3cc7531d6d06053b7c37fb42a85d 2940e87a6f02af3666c0a7e0087c240bc087763cf041afe7a1fdb901a1437b05a1411c875053f25e 2b896308a575221a86584f7d2e9b8d709a7894c5ddf2e64348ee5509e83427d132de4fe3cc07947f 99a2bf38f4a7922ffa7c6de67f04bebf997c3f28b336ee81b01717206a2e6a9a1835191085431444 325904e1781a80302c9f4028f12c0869c881a0b973108837a3b52e85215a5a535e1aef1a1f358f59 863e5f0d6f36e628df0612350d15f6dd73fc4711136809b9dfd2257e4814fe0af145c47040fba9bd d300c8b5ba0a72f6950559f676447adf9620adad374893880a52e681e8a0a46f3620497336aa92e0 8433c5534f38d79d3a6b4ec679a2b51c26a1982ba9dfc6d0f768ff4053fd723e9520d68b902a3219 e7cfdbaf2c0764edfd09f7a28c822f57efa1950095349a103eaee6101ec66f104e9e7908e3151394 7b7800ca015c50ac858c60b4b42337c310dcfa4df234ae95027936fa319123c3b6efd5fe0c1e0a71 401909a8224453d1ea75df71ef7f48ed45b9097e78c307aea2b484d8aa8490aa769a4134da7a8076 f4aebc5aa196028d63bca9c674b4834836454124c72595457dbde6ef95c984deaa76079be1ecb7a4 68349a3fbb7a7df7f3a731ef36fa0aef53cc232c7f2be6a7a5d9be0cbd28b70161decbbeff6d21b6 676301b1ea650bb1db9182d8bc1487984537206678031e0b1483a06f59f4b1a677d68d3f21a70cf6 dd257b887eb26f1f8966cd936beff14886e71f576f7c5a3bdd938b827bcb6c62773777e09fd7e225 f0be546ee7c0591e8e706f3327756a1187472145ef478212da3a91a7bcae62ddc8f25ce8a617d1c3 ba317fb7edcb1c1b4424e4e54536de8e53a42fadaa92da2244f93f17369b3e913ce2a30b42aaf171 8abf4de9887a234a66c213a7782d9eacea591ee0d563743ea8ed47a1447d473403d686eb37eb3fad 77e6f4c56d673e0a3b83d9343a9d4ef3d0594eb879e7381656abf7f05cc7fd1ba8816b77f2fd7ebc b2453e63e4e0457c1731d4af9001541bb2f122c28b56e6fa9e7debce7699fd63b729de46a79585a5 aecbb3597a3b8f0a1b9827cf7bbf6933a2db23a6f4b0424db86992190b0bd9cfc025f84f006e477e a8fd7ee410edbdbb6eb2870ddd5c9798be6b1d7ac9f5da25aeb96d552522681f6ca96ccbb7db1245 20fc0050fd45b9bccd20c88b8cb2e7faedba446ed94fecadbf96d494ee5dc2e32a3ed3471675cc8e e415591d9e4b7a7bd091985eefddde0d7a587f36ee9a309b779cf478d9a1e7834dbb44b776ad6da9 756a09bb86ffb1665b52ebd9b4d526d6703b43c67a0c26926504ee463d45862bb5696131ad3ad572 a09af78e6a541bca92452015c50da0f6a1a4810f43f56d933f8421f83e3a3f77909e12f736b75088 96b011d94f28c2a1d28f35cf969d6cb8ad52a611bd97f2563f9e2fd5df23b35a4fe129ab36cd1a76 8d581add6a9e3386955535352b6f1ba97559b85a9792155d048ae701291423818efff045a163c647 059d6ab308ed7e9264b1635bcf9ae18cf68993f539342ab09b7945dd9fb36447c6d2b7ca2e67de8c fbbeab114ed8ade699d8bbb22a2783156e9fa4914b558e73a5831d174bf22daa14cfdda456545ff1 58a1938c260a3a96cbe6fb66b1947b2fea562ec50e7bd969e5bac812c7f0d5cc87877466d589a532 a598b5ce70ef2af731c88ab811424b71a190017440ff96d18aa2109005d55f88ebe3924541adb676 2917dc3e340bbaf7b9ec8939cf3fc6d6266f04ad43ee3def5c7229ba73cf4e4bf62b4bec1a01332f d582267d6ad19992dae53f91ad03255dd577b1d461f034530aa6d493e76967f471c9768a3695d043 cd82f1d82f8ec6e3a06146dfe20386a1040199a23fab70f94ee36a9e7a3751040282bbdff3058afe 97c5a6531122665e881b267d68e6332b6b52cd9494bdfd096aad6af751ead0bb4e537ecc70f23cba af93763ab04fb8f3c039d1c9b177e3b192bdad86a2547c54cb0871ecd4d663d3e6ab10236ee9aeee 749f3ee8d2f3f18ba03301aee3c9ebfc255ff814c114d4be8ffbd4a9706a863b7744442793e7c191 4eda890bf77972bf6386f4844eea294faa79e3b1b0fd6f3c0c83b5ebf151c56ac65342a7139b5a76 3f662aa389eeb4468e9ed70ebbe8aae75ea22120de5a351da13531d88f44acdc3ba79ed7c5ae6a57 b4ab6a576b866ad7ec9127db051234e08897fb471aa268a84adfc1fbbfe2d30d1d23ce4151776c5e d5f3112d165d75c389682916cb68dba196d3aac974397298a6eb11cb2cb7d4f3a2e6db7954bb309b 87ddcd6e1bd645fca63c8e595231c20b557ebb46514ee9ee444ec51eb88c4320e7c9a087e4074ba7 dbab9928a801d977133a91c0e3a9d0f8a93b4dfe8ab0a9efebe41ebd8356359a274d84e12d72181f 02112bfd20235646f0f12927aa765e0e87ddb5a0853be5a8a13cf6d1b4d2af67f2f2fb5cacca23bb d792b1db722499b1fb4a22de3157cca78694b09a5351a1945bf8d32821c4747642885d2942886b57 3c99d4d1d724e8bc41dd44c3fd21bcd8aef5d0e9ebfdec1596db4514fcbd52edec6aa146286719ee 14a79bb0ce6d8fca63fbb829fd1af1560c29e47f10c8ef9342c92955e164ccd545c9d4f5b0443cf3 31319f28a54426302a0aa5ccb6c16f9df780175975153a6c268f9022d21c773eced39cddac0db848 a4065c4433a29e38b91f98a4df4ddf0fed33fdf87b7579ebf9a676d3c47cfba8da7bc22bab683b8a 618a13f97db80de594721ccbd8e53697a66d662b9951f128110fdd6b901179888c778f2294d209af 3bc102c36fe72d9117e94124649566fe73042185870267d7c506eb9ef323568f383b143148bfdf7d 851e8d93653a95611774ca64294f06da8749fae7796655dfca895b6041c7ccd9ecadedcc80abb6b4 e401715d796c9a8e344b809fa52a2eb4ee40ccefcb7d91911303eff33b31164aa5da92dfce0a1bbe 9aad1d428765ed12b29a830777c19618674faf347b938222ab37798d1960e9340366ab42a78e971e 35539515b910f33792691c5962cd21773751da095d2294bd5e82db7734f8e1e6fed739e9aadd918c 81d0a5519456a4619ddc702c271fe474a7bf96a884e288acb69b08bc5c18f0b512dfe5259ceaf1e2 8e1a841a5470125296e115d752357fb910f6d6d14e6cd72cdc99e7a202cca031a79940e024d241e2 aa51d9069f210b6dad426c886e3f2895bb0b3cdcbbb958eb12a303776d1d0f740f6623106b08bb40 acd07e7d0e506cd1d4530ddc96e369a34c233f74b11750b5cbe9ae0456f249cae9ce4ed84c9425bf 9f5fe6a1d3a836e6ae3369c0b563c13ea735965df61ede0fd958fbee304332b8650203e94c4f0ad9 271dbcd6712a176d33e4d259cac4e6728a05ebbd8d3f5bc39bab7b196befa976e021a7e781b83dbb c0f02ed180c98338604ade068c8d1e3dd9e2e8fa9925c3f558b2952dc9b11cb4384dc68678389ede 3f6422229cc5e2a0bfe30f04b30c3557e319d7dea5c7c8767d0a0e9837bbec32c94aaf4b4f0ecd3e 9db12653ca81d386a2cbf72359dcbf5c626b70103ca469023fd76916736d5ac2743c190f189437a5 1815871660b7e004bc33f80cd85e63009bdf928039ab9e7f0f8d0d7a2e604399f1ee535ad9cc3a93 8925624353d18be9321751abbda0929ccf1f125d7d9f846ac0da7de87327d49ab17d491b33a3f07d 404fa35e89443fd7a6e875b44396b4e490a8e68a8ba012527dfb286e779307cc7d65ae985eacbc02 fdaded7d2c3efb1ce089ad0a7852cd7832b1002fc56680a77657c0d38d10e0d18809b8488c009702 6fc0a9ba907fca8d5c7a7748c68da49a57a315ef12a146174574b131e9de03d172de8aa6b69c3bda 2dd87e363b6546456a444f2bcb3ee5f4eb5d72b58eb60931fb68e1b63b6a635dd219059e8d831330 ae87358ca3e703e0cedb057ca107003f8ebc3a4f84e649054d71f1d3bd09f8b9e6007e61ee9eec44 c0377601f0696e0e7837e9bda57b5573e35436973a59c5643cd33154cda2354e796f544ccaa78a77 84ca43763fb2e5f4d46ec902959d3138478d695258fa1f6b1463d47bc44e533b784b9dda584cb4db 814020df83a0509a78325a4230fa3e425057ef10343b414f5c1e82d9b80ec1daa600c1bad6f664b7 8260de7c817749532098385720a8cebdd7221b0e01623e6164b3dd443ed91ec793317a21aa914884 e5145ce030b1344bde796b333d713745dcb2fddb62c98c5e85393dc3d809953f2c87c4ae9ce9e197 e5b98575a77d3b1048e4bdadb64dafcec97406c1cdc7031574e3174f9a5e1dee81040257bf63699e 2a0111ac758120c88d270b0c08ef1882e04bb52078e60f105c4b3204c7eb9cc9be12c5447c2f24a3 dbf533aceadd0027931686216339e2d095e999d32fe28e85da78c9e0cdf49c5a988109c9436f885f de58078b4da7ad4060906f021128758060474320128f3910cdccc193b60bc4d00d003192192056ed b027af1410eb6cd593cb1071e84dc97beb14f7fe6fba8a01d16dda40d46b1720f22dcdfb6d4a255d 77f9627cba7c2735ebbe0b876171e224e6fdc6f9e3527d8434617a619f6b65cf8cb3b3154d610987 2cf7ef53fc1a57fb581c73fd5c75982c3a6defb7599e0cb603204ec414482ab9023236390159a75e 9e24094f7abc27af289056c2f4646101d9884c3cd99e816c5619204b4212c8d4ab07a4767b002992 de1f49c7f7aa27dd67b014633a4e22a2c9defd8c1fc8f241e6e1a2fae4ee84736507e3c8819ebd16 1bb2fce41dfc3aaf8fb178491e42e6b8ea01713c0fbcc2b0119059d52bb15b5c00b99bef800a062e 4069c50022b7e5bd77985758c59382e1c9ae085435d2f6c45900558bdfbd93e01c022adecc00a5a4 8740b169000ab37340dee2a3048cf7a568ad6127c243bc169696d11e27d4f173f0c3cbbb8f959f96 c0a4efe291e2b0c136a896af4ee06d67263fd6362b3a40e1c3155091cde63bf80d9f8186da036865 8301dd488b400f66fa370b723355f3643500da8e6e812ecdd103cc743a29019d61fda4e6dd146872 e7ede19b2803753e2de2de3e1451348b9291d26171d3af73283306513b1683d18d2aaef0533012d0 3681d1aae7784dcbccbf15963a6d11f0459b8198579b5173bfb9903fe0f705c0e8693f76622c02b3 d3fdc4894ed193770b987966e6c9e90c4cdfbb1431d65d03a631ae01532cad80891814307cb10c0c 51db47f733b580d6f7b4f4a49c1b2654b1e24c0441d6389a8b4f4d8ca6f7d63de89d4fe7c0381fdc 7e6b9a0fca3f301ad5f4057c91fb785a2081715bf437c2fac9194e6908a9d2db0c70f95c0338633e 042ec16f800bb71ec07114071c364e027b4bb5809d137b607b8f10b085401d251fa355353f79271f f376f42e8774eacd306442c18946ca7c062644f9fcd534bfb61fbcc7a822442eff286cc237d47e60 21a299ed7a11426eb00d3c3758028fc30542b73201a1ee310ca15e15a58f862c6e08216373855078 284308eb3650ce8477d27065657a314d399fdd19fc45398559dc7d8448c9f10acab46fcf6f4dfb3d ebfdb8a091e7f87b25bf8f98f8f855116145fede2fdcdbdd0d410cda4710056f8f45c2124138bc53 200cea4d1092c12508ea3200fcadac03bf373bc0f793773581456b685d4dffaeb3965c24b9c47ea3 918780237c8b10f1c7924d1e433fda8e7f8b3afe8a71f8a192dffcbd3fb05e1450009de56fa9bd32 59b983dc4d0a20d79606c8f17015647a3101691db98054be844012ec2c48647204e2527aab2f6ad9 92d9d6b41cea0d5a699abbb5756c9c4e7d3745fb63f801e49b4a0e71684453bf421cfe08f77ec539 7c1156b44cdbef971d0b3f0a5150aba9fa6749b6f02b7a86f07085be5e0da7320684f15713945e77 0b8a91a0c204b7ec0ab6dcaf310ba191c35fba1cfb56a07f687dc610f9a1118746a659b42edba731 bec918054a7cf717236b2fb2187f05f67e7969fd84dc1f482fca48685d2710dd8c6f10ad4a1c68cf 96015a11b740c3ec054476118048d78d48d5c564c08d679d26a9f26994da5b8a62896f057eb97abf b27afd067d7a8310f81f5512b8193f625e64e8dd73f56f910dde740291db4162063171bc82d88e39 40bc74c121aed93ac41e5c0d62ce6601b142f60d312ea882be1d94ff1c9ff641a1bcd71cf57b7843 d17b7362d942d6de2435feb87ae3d3f4ee9e5cc44eb729ab5d5d47606fd795fc7a5c2ab7e5eb5c7f f5e1a44e4bf8e151d0c97df28033dbdc6dc1af0e66425eaa9b84e13cacb1354f3e52a79919602414 86803cbcfe4a4bc225b2f9840cf80cf5077cea870c200b2a124453ef587686df322b105d27148aba b99d99b9164f66f17436d685439f8e15f658f555dcaca2c5c2da3b278a2b79f6f0e1fdc22d4ecbce a3daa9cddf8d9235c7da666bea1891dea41454a6e32a6d6c47f2cebc0ddce680e9f7638ad91ba5e4 35e2d088f05a7d931a7d18aa53e7ec2ffb2ef29f9eceb189ef7e403f51fcedd69199f67aabdf46ab 4312964bd5596f176e01aef3777d709b656ee9c7941ef2af0937250263c171f191bc5e114375dfa7 06d17395e9c7fda56f473153ea4e93a948c73153467b55c8fba7679bdb38764b38128e6d859d40d3 d6370594d15033f9641b85c87e9206fc9081cfca6bbe05152d6c86acb25fb1b7c8eb39b4d92b3f70 abbc3ee888e9cc207a6cf904aaf76e17eadda9a1353b4e9a6f77e839db6b73abc0b055e55e13fb50 bdcf6df9785d36edf075db70dbf75323fa78ddac7e820ed4471981a94df3f1708d58cf7355fa20f5 2ba530f92a6f3b911c2a0c19647d97aa110a241048459017998dd1925c08fda1b4d68f55769bcfef 5ac25adfd9f25ebf346db9f60945f0e42935a2b78b6af563173f11616dd4a6e620592316dd4c35cf b6729fa5c31ac50a776854cb55a5d1281d5add6e49becf26453b7e5c17dc31e316f46081ccf7f3b8 917baf575daf2681ce4e1b952832c8664a7147424b71a1e5d71087feca92458417e5dcfa5108fe62 579f25b94adca155e176dca4bcad25d765e1943978d2bb9564b7fe2cdaba1528b8836ab0d049d874 fe31b1b8bce17dfce4de4e5fcda598fe6f81b2abbc99f7fe4966d512d083ffdcbdb84e57e3d777ea 301eeaa9c39459a6ac2cc3226fecd7525c3f2f71e587dd22c28b66d708aaa147cf11bac8a5c8bb99 c396e19a2765f4c82fb69c8db226379b994e75ecbb65d7dbccaab13864b8ebec9cdeb6d7b7b4f058 bf5287fe3d9852e0c524ed0c25255c27ac2774a69035fae559e3bb4bf60271ec7a48c7b15b78179b 766bf718f18af9779de8e757f629e2d0c8c6fb4941f071c57783ec544a0b373d943a740d3165c54a 3e0e5cf949036e3e25bfb92a4a1a48b833ba95e864859ef15872439434e0e70bbcb7f2223eaa2677 71ec98bb7c0f6aed133afd3888d1528233a2a140bbaa55cdf0421329898e1c56a546e4b056371fb1 cadcfa4fada7a8307f4680ecb13ea0340c260088acc547e520114ff13ceb4939ecc938169bd60789 98292f4cddb167799dbe8d4bd15577518b72af4553ab26d75d4dc46ee38895c5971185d24faa5dac bec3ee76c12233acc4e8cae3b4b294bedddb2b86768f2946f4d9570c9d1c2141c1120899173af16c 067168844f8d8713c63e60d2c7a7884e464bd1f931ca3de6676ddbdfdfb56a02f3fd69de4f95d6c4 403c14b13271493d3bd1b06ae793d1b0bb4e1ae14e3997561efb625ee9d7db55f97d9eb4e4917d1f cbd89ddf48d35eed2e11ef13e37df49713623e13e98a0c197f8b0cd5cb8b0c1d6822f1c34c7eb074 92c318f2c8a22abd2b60fa866a8b1cc6f2463dcfce4b35428d9cb0bbb4e7e14eb1bf0aeb9cb3571e 5bd7551e3bfaa5f46b2226bf4f21424ea92146c65c8597a61dc337f549c43315159d41352932814e 5e28659cba102289015f2dc457bcc82e1fa1c336298414f19e0b29d273c19d2fa2e8c9a31052c244 11e5f22277391a7334d6a891083d23b88bb21f8aa5996288de557d64ed063276e9f6a469ab3b904c 6d30f6e4b8908807ec44a7cf9c456f7ee90aab71e82984822cf0db7998e0abb934173aac0a72c82a 7575eebc5b9b9c5d276bac7b36fa6cc75ef9d65ee6718f04987ecfd518c358da8c91c4df9edc74c6 483149749e7f98a46fdd46401f998c23569a5e7b4d4b4d51755e39d98e988f53b6b01a9e1a4229b9 6908216cdfe2b7d3933f93e6abd9e0841729d60959456a1552387ec79db7da998b88da8d758f3960 3b8d36c53cae439ee9b7ef51fafd6433742a91aed1786037a64c3372a248f2c290ce726492f9526f 4ee6cb3d82cc5778158d3a5a0eb1508c7e60b40ff6b5add33e846fd261a580a9cf24b23c18086b39 dce1abdb792374248bb5909210cadcf94a5738bb42d4b888403758f710edb11d2b396275253763fa addc9231b4fa8e7edf877ee2189d8acf9f34f67e109499a679d299c7a3244337b344a974b583d5ba 34c72dbb75c3152d2661e70753c1ce4f61efc98140dc1c9d46e83b921829951fc80aad3c9bce4a9a 15c8a9b0eed5fc2b342f61af764879b61b9c1688d6d8aef62e33cffaaac40ce869993146a32a0391 69831eddb61d3a357b8fa819f59852e68a585264493d90f95be64632dd0e46848623163f569d30ae 1cdf294cf3261e81ae39f459273c87c1130cd41e0dc62ee14df873cc10203e3aa2331e5d308dc723 1d8cae55ed8e90b93cee584bb1d0ba4df9da28d60f85cd539bbdb3c506f312b81a13a09d129d4e55 0b7410af96a859d74421a759c9ac928b66c9260bc94e8f64b1e998289b8e13ac359c0d7e0ab817ec 2abc5f98d60891815e202fc3abe224217124aa106834c71028f21708244f210844063908f0d60c02 047145d7cf743555f2bf618f8f997050db75f87bb83b8f1fa539595d0915fe390f352389217b1f6c 3acc6b52b1e9499ea8d3c1d5a14ae5f04e995cc6f265926d2a2562a38ae560edf1ace2a786d8c655 6f63d875939f61d1a8b50ef4fa1d3f0d0edeccf609c9f29300cc3be23c39a401d3330d4f5e0e6052 f709985c500103b106812bb787c06684a12b534ac946f8d8accc062327927d2889b77a1297036bc3 d7ebc739d7ee86876c3cb1f1cf1b062332364d308445d1f8a84216b35685d82ccd4a70bf0d95f166 7f57c4daeb59198bb69e76a04fe03d78e7f111242f21af88566a0fd8d879003661484f2c05b02d6d 7a326c01b68bad019bddd1fc06eb8da38079c72c6085a60b589263d18306c9c84911f4c59d22d4eb 807fc999147712b681fc06d172f621f14326d9ee75e84c3f6e53b9d1db228bdd7e95d8bcaa554248 1a15bc79b814b1e87ae04fd80346b0558294dcb5007baf3b80038c0067f485279d23e0cafb0578d8 643cd946004f26739e5cbdf7456a5bc0b5701070010cc089730f306f0202d8a9299b4c54d68ca70b 7cb412a7fc4961b8b709bc90c19c3f44236bce2d8de7ccbb191cd253a1d9a11c45b5c955c4b548ae 36ad1342d3ace036712b603a8cf301436b150097b345c073e33ae096d752bc6f4c3db1d79e1cce80 2f22e8794ffc380d017ee2744f7adefbcf52df93dd01f0758d027c62a401efa863c06b4902f0ec4b 4fef2c2e1a9f98a4a4d5872f4a19f1d7975822dd53e8dc0fadd9c7ac366352ec6b48134ea543e599 a83f8d225769ac4954a57e0db74db28875fa9742c0680df280d7cb5ea9f37105f0fbb30941213584 60b4ef7872de41d0546f9ed838046b98e849d1f0e45e8160bdecbd2f4f9e3c3972104cf411550faa ed3904434bef9560219d0a971ed158ceba491175bba764539e3c856a617bfa18cb8d5861c6e05860 44138f5e8fa22f6a8bd8cece0dfcec1a154cb76ec500b4665e75fb46defb7d8b320433b88536d3d6 db10bcf6474060d81a082e79f6a4f7f2e4410111c9284084962920b4581d087e3d014228f9466951 80e0f35d84e0e9b5fe4ea42fc5c4fd8ec5756ebc96c28fe78492f2a3a14f073fc85cb79feb0f2d9f bac131e51c867d329496dbf825d9b130fda9540370b979b5693b4fda780d821bd10282cdb780488c fa400cb11910a7e21e88d7f206c49bc03cc9709ecc2328620278d393b10d6440713c393f81706d19 885da602442fb103c2ea6a40144ccbc09a4e4cb37a5d49496d8a24b298875a42e7c43e5be496194b ad053de3a429b9367a83a0f2725bde9d4bb58138342bd6bfd516309adf89b4f31dfc4a9c6f45ce2d 819c6d8e40deb5b7274dca939304e4231cf76450048a547a403ea71b204f3a00b97c47801c6d2c20 5b3b84ed48933480d4dd6e8cd1ad987ab30b124a6541812cdcedd039b38332b343c89c2a98ec2cd8 98c7fb586cbb6a43269cfa5d61cfc710c89830f9705572575f20a72fe2d0a9eb0ea871cc051ae618 d06189f7a4a679723181e622754f9c31d0a1e811a8db96006a5f8f03e524da4035222e50c552c2fb 2df549540a18b170a2a649e2861148be71cabdb9bbb075519e8d8fcca9646345d499eb0c8ba7b801 108d51f7c7dabe60f477e6fb8d5d7ec54e1c2ebec9374601535dc8c0d4c4a427560998ac8b16afff e44dc44f37609422070ccb6780be1efb406fbd4f08baeba680ce53cb88d6c775799e0c8828c39c0f 9fac40489b120f2638f7ee4404bdbcc1e2efe6cc1b4d7afc238c2edd77a86997f0e97765fdc67c11 5c459c10814c1f16fab8f7db62724cb2091c4cc6e04dd00fc04eda01ef4e8b5780adee8ac0c6ac29 b0740a80850a4a86f53ab50d0f47b798b47483f227c8fc343b136cd2729fe4b6849fb1fe21b0fee6 2947f01e15f6653cd679ec9bf1f827e0ebe74cf814f31331e1fb7c5198ef07f7f62a1072b52184a6 ad3584aacf3b84b265164239d28f151e3520c4e436c06d3812b80e95032eabef1513bf1952090bab a2600d05ae9f9c92d43ab87aa39413d43434a008dea3da101e476521288e10f4177d4611131fa731 4a97f0031dbe582f72d322b87a171d10f9c31d84878220a4e08c341012440104763804014b5c809f 0505e0bbded585cfb93b85e499944cbf06313e526e4b0cf9ec33f8bd967a7f3af719485418a2a95f 51c7bf5b3dee27dc8be0ea4fb8f72b3ce1cbb9da7907419e0d359073c13cc874ad0bd2e8e9fd2d5f f0ee4e1e211d44e7d000b130f70d30ca4ce9e4d17a95ec446eabc459a886205f95b06fc7176a1ae2 d0be15fae704873f65bda89cdfa5f6a278822f9ae9a725209019e1f26550a7db01a8317907e1d308 8370495041d9f54aa0e4b41928f2f3f581d1f144394555b3c568202b43e85b75a873dfa38e118746 bed99fb27a51a6c46ffee21f027bbffcc55f7015197afd85d89073162d76a6878323d087801260f4 649983a87b4b40346658a015ed296838778748ad2f08ead9aa324c3f9bc5130b42ffd63e14ce81a2 8e5192b00fea3f1c1a15865cbda83bc864ec7507e16ee4eaf5f31b7e26bdfe426c9f705e14d9e07b 79636477f99df05a2e184a08078339e810df64aa10afbce610271b4f880d290962c559feff78ff81 d12ff98ebbc06d5937f1c6a5d5232a49e744d1e52d4b4f3ba7146bcc6f6dee76c2f67c1368ef6399 e3e3d22aad3715b39a5cabd0cb61913bd98a469b72bf1e131293457c555abc1366fba8a660e696d3 ddc36b6aaa348396793d8445295f4aa9f9a2b9c98eca702bb9951e61f135556a9bf58331ee374a45 e76493ad2dd31a6fefa94eec01edae4b11bbfec152c961691833c6e43ad59c8cdddc7a160fd6f1b9 2bb6a20b2bdeafad428da51f68b25e8c76ef6d7a7309efe31a513eb8de87d7c9aa8b8f4b681091ae 8b55b2704b5fb3a3fb1b2f5d9fd17e08fe96fcffc6fdffc6fdbf6d1c0013f297110be89bb786b56a fb227e52c67d8277ad1d59ec9901ef6e4995191ca74cbf7d2dae5b582d43d750ef2148874a48ac34 130969a58975857862d3706a78baa883f49cfa3432bcca97f47a253a886d25d677ac1af4f985790d dd29c9716c944dbda0dece68d3f4ca6ce69467f6c0927c9edbbac942bebeb48ab37077e6f7f55a89 f50dbada4e0a7aed1c0c942dc1390ebde64e0fcd056fe32dfc980bb713b696ebf4a28c7f33d5bd3e 1febbe3cdabc06d5cc5018aea95acaef746392aecaf3e9500ebab3fbe5ca386a6711f3dadda92c77 81e268cdcc62c74d36cf07b7130ed4dd6b77c81fa2d6a47bb4d5c6e674b89b6fbfebe2b590428bd7 baf3e0bd790f2cd6ce235eeadf72fcf89cff3ff0349765f0bfb0f68fc91f7fa25efffd46a3b57dfe 6eafff7ea37d07eedfeef5df6fb4b799bfdfeb9f1aed2f121284fec30af9dd8c6091573d855d76f7 12de5817eca0d4bf8c7c5f24bfcdaf88cdd2bdf8fd052ab776599a58e4c20ce6be52cce85c2bb303 81e970ddc4681ed262f2910fdb0710ea429bf77b6d48c5ddb32c3bd67618c6d5e14135ee4dc2efb5 ee5d204656acb2b8fbe4d3c8e7636c729cdd9633dab676c80983bc5e24dbe545a5274c23f5837599 d9e4d8e5db43ba6bf5d45af4ecb56a228df2b767791214e999d725ea3e8f2658deb1b1677271986f ad5528d11cfb2d3a6cc8f7dda704db60b415da059a4ad46bd536bb7f40c63ab41fd781d7afdaeaa4 50cff359d46a70de8581b9ac4d3b7c2da8ef844bc99582dfbec66d122e0fee49f2b2f02e15e6f1d1 3b6f1f3f35d28f6efc835efedb8df47d2a7fd0cb7fbb91fe97ad7fd0cb7fbb912847c9ebe553d0b2 e5a7d056a64f61e3ba4fb179569ed283edde3b461b6ea0cd33d759eeb4b82c2e1c77615bb9f2695f cfcd8f0df77ddadf2691dbce180d031b33f1a656c5b5c22ff6cdb1fe25fedefcfe957f28ffb94e6f 33ff75a97f41fedadefcd72df536f35f97fad7f6e6178ffebf7608fc853abdcdfce2d1ff6b7bf3ef b4d4dbcc2f1e7d54273a047ee9e8ff1b87c05f6da9bf14caaf1dfdff756ffec513ca5fa3e4d78e3e aa131d02bf74f4ffcb43e06fb5d43f047eede8ffa7bdf9b72fa7ffdaa7e77faed33f047eede8fff3 43e0efb7d4b791fddad1ff93bdf9251fa6bfe010f8a33afd43e0d78efe3fda9b7fd8526f33bf78f4 ffe7de7c2f559c7c2b75e82a4f9990bab7c7a5f676f15e2f7359bc4fab7345a3f953bd17ad1cd592 30dbbddae27a339b49bbd55a881f16875dffeaa85378ce7ae31e89c47fa2fdb73ffcd7f2639dd787 57e7f21d7e34ada00f206e4639fbba66e3edcc79635f37a7fa1c138eea5baeeede61a3b75ed6d8e1 8a8f26278b46a83f9f7726c7e5749496b613a2b8b9fe55f136f357dffa14d2e5ca539844a6df4a7d 73eadda55f6d77cc675f97c2b99d39d567f3f5517db9c2414f11954deedcaa2ff7938aef8872ae91 69671e639fbd696aad0dc6ce723819710b79f92f8ab719f4f38752450cbb3d255a8edca3a36bfb3a ab6bcf7325d5348feaa9b9dc3f3987dfa506566e251cef05e7fa7e9467af66a83a4de70bf5f122bb 690c77246d0f147ed4ff12ff51f3dfbff20fe529946bbfab339e8cdefa917debb2a463fe57c727b9 96491ff4647db51b8b5576bd8eef8c855d69a667ef583e330d1ebae6b8b0bbe786bb6dbc30502eab 72ef16cf37fe96f8d6d8fffd5ddf4add18b3a7c8d1f7a7542f46dd516dd83c5742d4fde02ea3c93d 985167b39884a8e5b11b53e7fd35169d665cd0c7c5b2e4a7740cf7cdb631085b8f64ef76b7ccee20 152afcdb826ea05aadaa778ca6bc5255e1f168d299e8757a6a364eb278ba7a632e27b759c69daef9 fa115f68e19330c3b31d69bcb2baf2f020ed9581aaabe1de7dec44ba43dad03be9ea2df193f8110d 7ff0f2df12afce46e5abce7bbb14d52e4c47ae1ca293ca61973a1cfc508375b9670d97a7d118e689 7e919a1430911e899ac60cece490eb3d0a44a83bdcdb42271361a436b558ab7f5f7c13f67f7cc353 1877ab4f31909f7b17fde4eb16af61e153ad819576a3bcbadf30fb4864792a45ba8b68d670a739fc f51eed0eb7c0a04507b0de53cc05bba396eb2f53d3c9bcf3649baee14c8b4b2c42ffb63c85f5a08a 6ea0c296f3145f365ca789a9b8bfef47d90d43d9eb65a3d3129c077969cca6c2603dae347be7c135 6d5f7b2f6c77eb8e0b8947c7dcef9f6d26957bb7421213b045f642fc91f861207ff6777f419ec265 ecb534613b8f70d382d34e61988d23e1e9a5f5783a4e6c433173ac92f5035c27c5dbb53fb4c9e8b2 f7a6539b6e70d1dab49702b56baddfcb83bddb154fcde390bb341517bbff43f11747fcb3bf7b8aca b1fe1407ced21d1e89fb2ec969b0128556da89a5bbb369ceaec084eb6694a14db8b97e7c6fd91d47 3f76db5cbb306a09797662cb91e5b4d9acf5fc4961e33a2cae1b9a9b3cfcdbf214f3b7fa53221fbe b3e37854778b55353809385d8d4f4d7391607f64057abba1ea98d07bd749c9ab726cb4b69970c6b6 e87ba169879695865b9b341ad153af6df5d566df8abbbde99f0a5ab5f03fbde14fe5291ec9fa7572 3e76d75caa3770ba780e9f66dd666c244fa2d57e3fd4e974a76a68de5e19b7cf8325f325ee155864 1a1d292e5a8fa6ac78bf88d3ea239d88d7934f2e53c70642febf11dfeeff072fdfafccb1b2990d2e b4d3712bce34bb790446f2f8a1f4de3552ebe41e85544b985d8b4d9bdb741bd1e3dcb1e2d7deae9e 7c54ce75ac5fbfd54cc83d6bc4b814a83ae92a8956940cdadcbf2d5bac7cb93aee554f4da90ce11f 69c3733af9e88d44fbd55ec54fb82d2fa38cf5b056623d79b7f55ae6ddcf57f3b85dabd2b38e5d59 65fbbd4a89ea0f2bdc72342d6f8bd345b9ca399bff2c28dde67f7dd74ff2e4b3cbd493ef159a4ffe 10f26e573a0cfe1457aaf9949cf0dabbff1b0af7f67a63dd7afbcdc17dcbae3ff77493a973f6322f e69ba70db31e1c6b323b3928d59eb37337c276fba8bfddcd3b720b2e8ba01dfea1f85facfcfe95bf 5fea5fa8d3e769ff6da97fa14ef441f05f96fa17eafc9f4dfbfba5fe853a7d98f26b47ff7feecd2f 197d54e7df6dda3f6c299a49ffd2d1ffbb87c07fd3527f46f06b47ff777bf3eb46ff1f9d37ffb0a5 fe776bbf76f4ffc6defcd72df5ef6c7eede87fdf9b5f3cfaffda21f017eaf4e3017fede8ffb5bdf9 775aea6de6178ffe93afedd3e81098d7ec27ff50564f612a7a1fa6ef4cf6293d129b876acdc57b74 7d6ddee2ff97b6336d5b14571bed6f711e11e7791e11014566046550109984e8ff3f6055efdedd6f 579fa7ba767f5914f1b9ca65124372731383c0f1c7efb0e6cd9ed6da2d6c68d6868bcfb3b5d7f3ea bd0df09b01ba05e7268a8f84be349209e56e06d6ef8843437f2cf92944aab7c96f9ed5a84a8d4634 e21736cbd0b6a6b748f1003f87152d8e72f8e2e5e57973c4aabb2bccda3a5099131f4432a1dfa949 de30fb3ce5dca67527d05702f4526177f13afbe3c3fda71047077ff4daff51f57b916a1d5d860765 613cb92d0efbe2fd72f0ce75cf77d549d872b635077b3ce63bc96a5754dd78572fc675f116e39411 ed6aa72db5aaac9d0b79afbde461b57bffdfe0bbe7fd78fc0453b26d1d5453932ca82dd955e00ffb b76722b381234b6aefaae2dd77b6e6bb6be38502691dd7d578f3d8d2e5baca662f1a8a3514c5ee62 faf969de6e32d00220a54785fb9f102f71ffa2f80b0015c69e7e54bf7b96573950c3a575d0759b57 7fc28e2b9122be77d0f42d5ee2da389e1d589eaf1fccd7f4295cd5d141d076087d529cd1f37c0edc ca451e69075dcaf4504f5c77fcfbcf230e42fe45f16faa7bf2bb6a6b970735565b3fc36be7e69d07 916a8914481b676ee1832ca406f7013fc76fcbc98289c3bcbce2f67bdca50733a23c36bd78ed79ba c86355dc345bb65061fdc7ff06df3d0326aa527878fdcca137743eb4f1dbc67f0749cdcb176615fb 7e460f0f327f09ad5e59ef1bf22cbdd551d5dbab4ddda72efd568e914fc5451ce695c438267be1ad 6ec1e2ecb7effc15e25f91fad16b3f04a89c83d9efaaa07a100b61f32dacfc09e6296e495c561f4e 1f3d58f44108ef892a116f3b734590ed5c3d8483cdf92554b7f274b5d89dd6798910cc2a7ce41b1d 85e7da055963fd6bd9ff878843aa7f2c8954bd298033270ac0ddf90d5405a51850e461e9cdb5cbd9 be9b2dc8f2b5e5fecef9f8d3c851c5baf6c8e747170e92c7f2acabcee230ef220ef3ae7e0ff3c649 8a9cd9e698440d51ff370015f335fb6f4fdb8a7fd8e5c99ab3b9bb2eb37254a170f90eca03c29ce6 66fe157af390f244d9ce39a50dbba7cda9dffb1ee6e5539f30ef8ce191c69a9ebc1204756ee5d81f 220e42feed1ffc09df552b2a0d607467805a3e2cf962ad3bb3ef555cb27a253f6f4eac70775b1f43 4b6b2e6ad94be25a8857d252117e54056b11d4f8d6bcdb60d9addc62464eaf4b4f99c488ca97d4c5 713d53d15f411cb3f9cf49d44793916ae71aa94ab419da8554d9cb25df23cbbbd53973b242d2b752 0659eb3571ae297deffa96f3353127568f8b2cdf2e5379966ba64acc9825ca741682e0a3c65f1be4 0d65e29b5e6445398eff37881a3e3f07f0f4c1806ac97a0454cb2a391027c66f638a0587be221a07 34bbec4e95e0e19eced9e2c83d55e87ac01ff1c68be5738b3793ae58494a1157e9a38e15b3a4317a 940eb885417ba7b8aeff3de2e8e0ffffaf3e881abe14a96edd48f5908d6620fc3c6b75eebde6553f 540f5a03549f979754eb9c674a9f3c197d5be73dd73059a1fc8cb3ede8b3d771a8f5e0e61db7101a 90bb170c0ec4cd79efddac99ddb787e7e22f23fe34f131aad0e6226af8041b3eec96eb400311dc96 2a066b0de2b4bff001f39097225b39a1afd94c68296f9215ebab13bde479992a676b2a79276f3a59 3b53c6a179e8def74703768820570888fe308c6f137d8ebf0c001ffb0b504d56795fe0c89b21a17b 43238852edc2b3b578a37d4933f3ba48242f80a78bf34a64098de925386d8ebb6c1323ebfaeb7868 9937764fd72ee2be2b081782f5710d7f5737262ea81bfb0b888390ffffbf02b0bb5e822a2045cbdd 65f61a31c83f2efc42a8499b1eb512c86c9de106267d61e4d6dca3f45125475acb5bf54042875664 b78c6fac10e1613c2506fe6085bfe9318a8fc180c053fc8cc6e4d146f8dfe073f51c322b2f3ba92c 75987ea39721d05c69532d4342eba2f7d8b19d98d2056ebb3dee321df6e020257ddf7dd83631f0b4 1017bb72124f71620e938772099b27f41a963d19ad9d327506bb55269c7ef02da4fafde41f226c36 b6131d71cecfcb3074e8d38de8dabcbfeca5d8d47e9fa5cae23d4ee739389b4183088970808b9d10 c1e4814f6259d1e777abb425ed0a674745f5857f4391bc7f47cbdadbdd1a4836dceea062e2abf884 54ffee0f74cdf7f4cb60bc7d9dd03ac2f25d0dd19999fbb81e77e9da7defa3ac1bd995c39d325693 68593d96b6bbd2a9b5856fd72162a1e61421e047bc890e52b71e9b8d433cd00880d890cdc471d3f2 12f4ff060012e0f8de1a80ecfb1a40ef7574cde2868f688db2ab47cbbecd01d48a193fdc1fbbede0 c80ec8e753efde9f3db391f341bfd7f713a9dcc69557e7bdb3bcf4195b133327bb24df95c7f671bb df1fd6d8b98ae15ed665b716c7053ec77f888fea173ce395f4afaa7e017ff769beaafa05cf7845f0 abaa5ff0fc74817fb5f57fb10bfc5495c6d3c17fb7f5ffeed3fc0f5bffe31977817fb7f53f9e9f60 cabfdafaffbc0bfc7c95c65de0df6dfd1f7c9aff75eb7f3ce390eabfdbfa1fcf4f30e55f6dfd7fd4 0500a4b5da007a791b5029601ca828f3687ae51335507d5347505b75c2c0ebb43a41879d51cf3ed2 7af83cd38827b77e428086de444ea08e72da92f6e69de11f375f93ad3b8feb564d5ada4690961d3d 77cc9fb42273e77f1e71a0eb3f27ff4735245c502d8b0d501b685468af4620a0a6b3de7350e852be 3019b89e54ab42eed9036347e554dcbe76d6dc6337be9fac3a3f53ee07a97537a955397e3cee9a81 1953ade40551a9452bd75f41d4f0834ee469af41a57ee4019c2523cfb1f1990bd496773a6c259ae1 33cc8cfbbee00c184f325bbe7bd1aa3567039b33bbb25d13d6e370a3efa4da3d99dd40576e6306d2 afb34dce516fd3bc7169ee60fe7c7ca4d8bf421c84fcd16b7f42a4da6d834a3e88aab4bee7000c73 91eac66a821afb62023fd10f9f83cb78e08f9d19e765996ee0e8ad5cdd8657e7f886a475c87b3b93 59bdf7b789081d75853f329a3e1b9d54a3d8d4957aed66c8ecd0e1e5beab30ff10d1db7c8eff47b5 2b7bd1dacf6f819a956703ca1d01ff7d590e3d595c0aee0a1b03db30724dcbd92ee666202f915ba6 dedbea4515dba96636b3572cf1425fc8da413ed3293cbe912f8da71c7f92d839f52bf86fcf0113b5 fe4c893c85773bb4b94cfc699ee171f4f2c50b3af2729399e020afeeebf198a7db777a999cdc24d7 5fea6bdf5da9185ada280d91da9d9ff9fa5e0efd0227bd1fadeb29bb9c73e252acd23f441c84fcdb 3ff820eaa3e34e549b49e4bb2a6af8a0ea943b61abe0d1cf61a5197af27b3070d7d052b4cd1792b2 5ceeda348787fbf85a6089c927e796dfcf95267e5f9e9905bc96873763274d08fa789a1f8d8ba093 6d5e40a022f32bf84ca0fea44a39cf6884823b81f70ee8a8364b8197d3c743676be2b25d7b71d97b d0b8b66e73b0ec6b86d81b2907a9333e0724359747fbf7524a5b0422aad3de5e2cb284c4ef98c427 afb35a75e95fc17f7b2e4f42b4ecf7e3fcd35637e82237ca4f0f32f10365ee9a9c0d6dbcbf912d3f 354f9be30cd6b89692e58e4a9cd3fd4bd7ca8f6481198ca50c25cf442d315c095b3c87f118d210b8 7d2d2d700dde64fe1e7148f56fff20eaa3cb6e549bf9ed77d5db3b8846d268fce4d297a3377f277d 078596e387533b9cefe1bc9dba2d914c45bb77536da5ad5ebbf2dbb9c7799dd2ccae8f44cda2a702 5a81967c4d7051aec92458b6337205e6e9a9ecaf20f29c753e13a84126aa555c1541355508a39174 d3f7df0c7b70d79b976fe3cc7864f9dae16c4e1206d08daa54545b05a5717e25f9b67486a4aea84b 7e5f80aff331e7d4c08cf553e69609c093a15f927ba25fa8195f6f3ec77f888fea774ff61a799661 10eedfe3ae9fb2378483a65edea365b7e2f0c31d9497d26db59c785a23b9cc5dd827529567f0b175 2ad50e6d01cb3a3dcead0c872c7d784e9961eeb4a627a50745c9bddc99924257fc02e290ea8f5e03 95da266afd65296afdcbe3145d9fbaaf809ab43aaee2b7760ffb71b1ef2055e89a6922e4f49de1de 155ac9266579932e9e36d76655c037788ba38a46fc63556c7f39193251bd4fe9b4ea22544e4ed0c7 159868c7e22877fe157cf7c46be8b7bb5169391a4c91c473307f371de89142ee21ba31cc092736af db41efa01e47987e3e4d2e8154449f59c1ba974a1c9d6b41cc3b43c3b444b69bd402989de3fa761a 935b8e591fee09c01c30a978fa208e72fce7e4a7002a7d3452659b6874712acaa09617539e3c526a 96b73556c61c32d42b24a865ad393fc5bb105e465c5e92547bf6106b450e70cfb1966413c9549a9e 17906c9c255bf864b5927097a91feac56d6fdfcaec1711cedbaf220e42fe4531a82cf048f5d2df45 23d44409bc16927010a5573653edf64437fcea593dee6bb9cb8baacde4bc4951220ebf55be735fd9 ec68cefbf422130454913cbc48e3957dc79566798ff4de5198dcbe9d6060a21b1c3b788863dd5f01 a830421f54820506aa2331de8bd417d4547067f3584a37d0fd506dbba7d325b9dcbfe425c97644dc 286cf9ee96e6d831d950e9bc37d58eb7416092d879631ef642cbdebb46d6df77b2ef17d173af699c af98657c78372bbf230e42feb1e40b0015f3d2077005fb96826a1befe2edbab9aa6f9574a32bcfc9 29b152b99f7445226915b9703b89a3ea6c8acc6d287db4df1f61a5c992f553f274686197f39e120e 1a11f81b8318e4ba162e8c5a3e9ea4da094ceab6d33f85e86dfe5402e0f47d00e0eb75ef8f8709ee 5628088cda9ecc92e793a60d4fb7077b105a0a7ce606a66233337f9ba19064ad461205ad7b202161 baa76bf88ae05acb1d31f037075cec2de3eb0d3e06b884c9235cc3e649c9c6b292e4fe0a003c0906 e1834fe3c634070a6abb358bb31fced3712a7942d7e5164f17e4199b3aa451bac01de9e32eb3d50f 0ed2f7f7ddc7eb4d0c3c2b8b8b5da38ca738a38ec943a78bcd13e128facf538b9d322b6f77abece0 b82b285dea4f8843aa7f51fc4300d8ad8d8c54e7eaaae4a8da97cfb7b22ade5df4c585089467661e 56391a13bb7570369b516437d8e26faa457f726e39588ab4cada4e99668d5de192b2517d99f523d4 5f2852e8a5d1b23e2f6d0d84a847a09b3f8f38a4fa9f130304254525f8bb212fb6d046ac4f44851b 1873935e06ea83b416156f4fc3d21b173bbdcc4e190308d517892e5ad672d3adb181d6db5db9ba43 ac5d9188d0382244b5c120f5473b0e776f9cfdf0bc219b733d0272fb15a877af2ccaa75341159cd4 63c78af09ea2362f9bfec4a1d7339618388688cd5ec8054572b92b42544ef78d835f834dcbb9a7d6 74fb9e5f779f5e65dd0d92b555c8269b114add153728f55783776914a1335fbec5da5f22de30fc47 affd09004a726500955b4300b56e3b00e97c74e1b2cec968c52f8f41b53d8b2eb06d25111298d10b 1ccd39042dc3b83efddef3b35b4277fdacf9610a9af9818f90de6b0f71dee8f83cb9a7cb4373d3ce d37d2092a4992dbbc75edfab2df1f38897b8f1f15754bfe01907537e55f50b9e9f1b2bbfa8fa05cf df2aed5754bfe0195fd6feddd6ffedd3fccbadfff18c6f16ffbbadfff1fcd605fecdd6ff5a17f8df 5469dc05feddd6ff787ebac0bfdafa1fcfaf7d6ffe46b5244391e26004a0b18d01c8399da3a9ff23 152dfc8c09a822a8143eda56bc9f7fe0f6ad41d0de58c727bd530c3fe4d594cf25af0defdd7d2c3c 4101072f79523937b3dc4ace0299eab6ca2cdcfba358d0aee26a4de9d97380fd3de290ea8f5efb4d b53bfca65a29984a34fb0769509da466a0ea5c2e61336da5824ede193d7bbe42fb4395be7be20dcb 78296bd17467ee68ee5c8266dcd39cfcbbc4d99bd4ebf2b8e56cd3c266b8731b52e6595b61775285 ca4bf41f02404dbd12298e479f2eb0097050a93f22d55d310baa64791e3e9cdb25389a4ee6d93fe8 237fb493596f3a226d77919de69dd529dfb691be367fa0c60cb5f07596bcdb534b30bd82a81a415a 77f5dcb124293b7f7cb83408ffb3be898f3f8fdf55bf7bae1271aa2c143fccd15d847bc7569e0166 452b81b11147a03ca9a7f0eee286b84e69d82c3e4cecd2b1885d766cfa9dc9c218b44ce49654f7c4 35834e197da16ccf2af4a284f371681132d321912f200e42fe4571a4aa44adbf594693243249800a 918e53507bb9a8e1c7cbc0436e97278bf9593f55b666eee5a188ce66863cedcab554b69ae556cbec 6d87bd5b4ac686ba92cc4c3f19c44b69a5de480157ee98c45df6c64090fb539a9012efc6e61f0240 23a3f2bbe7b7bb51451dc0fe24ffc9960d28c456fc77cdcf7a72d99fbb055e976c344442cb3e6b25 93b1acfa2d2d982dbd587cb55563890f14dc84a717dbce6ece9e913dc8019fe2a5e4724288971458 7f100721ff73f255fcb7eae115b5be02c5f9a7f3c8b38eaf82eec4537cb1f6ce7959c3993bc85a8f 33211f16370fef9486166e52675ed1d7bd6955dddda9bad24867db677f2d0fe5b04bcce5c18adc49 c2d511c40b52c405ada7aebf8af881b23f967c573da013008999a8f5adc615545b4421b40b87d533 6c47aa937a36efae687f619b4de36c392ef43407462b7bcddbcf8256199520a5a177e1f333a73764 0e9dc7c114496c40a3935c8136a72c8e30c26667eef81bcfacff217ef3dc8cbf7b86bddbe7ea39a6 8bd1d7095f3d3928503c597be7dc22935adab592afdc9f9d847f930927a5975bd79c624fefc5f3d3 7897653eb982a5c92add3c659f6a4f5cd51e4b414772071e7d9c31ce4a629bdf110721ff58f2f700 d0da81ff4b15ce0e6fa0bae18a6153c797cf613af2cc91a99cb3c5caebc7de0b55139c419c70795d 1dd9b76af5f6990bb5a4f232ff508ad2146e40a71ca2d604fdb56d0b506b3fe577f483e0eade76c7 92d9cefaa7102fd8ff73f25d55c0a3d6d74b7b00c37303540f7231f04874e94ffadad95d4fb2391b afd53716357354632aa89e0ee1835021479dc479800cd2d22c49e44ef961ba246cfb6b88c7ca9d78 b5c6ed73cd11ebaef73bb68d62044313cdcd3f44e46955fedb73b633415533e21fdd09bad072e1c9 b6243968ba9c7f1cec2e62be8b847cdd54a70ff521969f97de2c01e414534e9e0af02423a0905ae0 6b8711c435e7a53adb6926fb0c43f5373450d67b7a78ca6ffe8438a4fa17c57f0500edfddfaaf4dd 3c001825efa0fa0acbcf01d69db92b7f23d8f57e2a6b75fdd4d2c8e2395e879b794339faeff821d9 738275c39362a71282b16aa4f8fa82c9b29e512b32ecd0abd242e7dea1934466494dafa33d95c9e4 b73f8f38a41a1fbfab6ad434ba8e8e48004ba2158d5059c81fdbf0d4a95467bce5effd94f90ef5d9 b5ecf3944a666eda59e0155b5ae8d740846e1ae01bdb5c82a50ffb0c3300a5029ddade202abbd4e2 3c9be3f29a591e0b538c2237f50cfa0f0120fe5d0590cd469ecbf5115493961d78d138ee2ad2677f 8147d32b53e6db42c14dcdb4071aa14ef7170e194b92d29d1b228a610e7f80ef80652eb9049330f6 69eadc2ce58f2a9c84c96b21681fcc4c667ed85d4eec01d63bc45f215eb0ffe8b5df11a9865195be a419a8b07b0a54476fe7c9cec9bc6d0e898ef91e58fb5b31cf397ad5383615a6136ce5733ec58986 9b5378723c8ad31258ae28f9b4f42ebfa8e55c4d1fcb49ac4856ac51ed50bbf67bfb43e5b8263ca2 2a11c7c9f1f80ff1994069a92aa8e42f71aeb4ca445f27d8f5e4523b63752e50ed561072a8f6a816 2de5e9252be729b2999fb6337a2f349ee0c482dd4867d253cda294371e1c11a69d24ab493373a887 a7d2be75c5aa44f7b28d2f047834a2adf1c1a9c1fc43440d9f8d3c6bb738551670617353766ce34d 04b70b589735cbe2e30eadf42bc7db39837433a7dbc8ea0a87c470c3f505f5c84cf9fe89d2b2c9eb b13261adc343abbb7b970a837ddb721344ef145d9fb8e05cc113f547179b1c13b31f220e42feed1f 44b509d74065e12d40b5d416fcf16a7735931276d52cf655567a416d235fa0e4f984ee6fa1705076 303758802133dbf15b6a83560ee4ddb9c7b7f00ef6f372de7b1aaa1301dd3289fe037670a1540ef1 e40c4a6353a20a6199c6a4f50f012afd6efd3381bae797e1835ff18f4634aae8f025632abd330bc9 9794b414ade486e5e962f6c68a35f9452bbd054495a55a8f240af7e9a165b29b7dd7c671826be134 fea6f1531c3b55f1d49eba63b31be561d9991eaf6f764b4ec9ff43800ab36c00f8ba5d7bd9767ea1 a3b2bebc80f9e5212fe47a59c4a5ec900b113aee028cdc4a1fe90277558ef005730e0eb24c4582d9 3c31f0421817bb7e0b4f71fe009347a939364f16102c2bb5f63b65b611760585d2517da5d97f8fcf af16fee03500a7a9e6f369da1d5d7f9ffc0b9f95f79206cf6cb1bee572dcc0cc5568a5bb6e1d7769 7b78703671f53d503ab2432ff8381c5fb179a26eed9429ec7f4b866dbd7785cb307ac3e5bc842285 5d0d2deb5c776b20d67cbb8342e41f2224da28a46bc63d8e725c18c97e482576c708adb37567c5ea dca10bac199044be95d8772d25878f832184cddef3fe6e959e2f507d3147d1b23624b6c6664e6f77 e515bf850d4442acdd4e41882a7943ea0feeb171f67ab8219b6ebcf5e4e7f8f3b82e7a307bf1ad82 7cbaf5b1014fe700c5ccdc297b34c6367f68dd6632fe261d05cb0a44fc40195a568b0fc442132152 b7f2d90dd928419b961b4d5e5aeea8b3f68fd3c19aee4cc7eb6e309d45c056ab90dd6f57dc802156 83377ffc02e2dfc7fc8b62a555df5127f5d060b9709d19d14a1b9f9344d65a4413b5f6129b81c706 45b2d80eb1b61d72d3b25fccbaeb7be28aeb05eaf2cd07c6521c27ed654a0abc853c0b9e9f5f5f9b e7df8b79ae905c64d54226423d3f57d6adf27c556a55fe2140b99b4b81f28cab83f21c8ec7345036 6c12400eb8470b15b70ce01eb701f0a2a002b83f4f87f866d50b6b068205871c710e9abcf27c7a25 b7f83ccac5eef3b8eb2d9f6d30c19fed877cf299a47cf512c5b4622be3246fa1644898adf2674ba3 cff1a7f027d52f78c6cf49ffaaea17f07f3fcdcfab7ec1338eaaffaaea173c3f73817fb5f5ff5117 f887551aaf08feddd6ffbf9fe65f69fd8f671c52fd775b1f5412e96f8fca5bcf48752ec6aa592502 920df103d20f6c7a8e054d752e3fbd11ed3f8f9e597a767689eeb3d36bacfc68f94cf84c1311fd9e 445e5d517faaf62a69f3f7fb53260c8a4eac7f47bc7dfb1f4b7e88c8124a83322a374079579f0108 ca50a0d2801e00ae4230800d1f05f073760bebf6241bb4e4f6e0493d1ac4b3db6cc7dbe6f8ecac1d f87d6b5af25ec36dcf1b6db74b2f715f63ee6970e4dd897ebddaabf54bb9136985b90d883da2cfd8 ccfca7108754e3e37fabd2dd39802635065456ed6842306fc3a00a15b09038cf6e417bdcce3e697a 38f6b95191f4deb384ee8d976ee84ac8bde466b04bc7591c84b993f359d45ee758da2eeeedf8227d b7dcd4e5fa26b6074de9db1bb52ca3b39f47a4588d3c29e5374fa41979727307c0f2aaf609a6205d 3c7017e3db9329f7f23e9f6b4c3cf196a05c19bfde1ca57d7cd99a3d28db2539d57a5486f2c8c2a6 e4eabe2fcc71d36dafb81b7703274d616c4231e4d9ead228be277fc227a4fa7f8bff84ff5695c70b 00892336eaa03b17c03e590f1fc898083ab599e1037f58f04e7b68ea9ea78071563a6fda487f10df 907cc033b970b78fd5aa79b4d5ce0d80e9e896b83597d7697684eb8bc456508c6836787613c65ca6 2fc4e8e7f1b97a52ed0892d18c540751adea0b0ec059dafb64f5864d68803f197b6cf8237952f232 2368e6a87797b3b7edbd65599e06eead532e737ba74685ab34f4607d71a15ada9adc0cd5eb195da9 dbfeedf8a934ce47e5de05994b43b131fc79fcb7a7318baad459c7a9b262e4d9521a81971ec74bdc 67df5e1a9e341f96dc65a9bab06fcb87f0a8d56ae6bdcdcd829b7840ded7ec504b6b9b413dafa2a9 b0a2600fa375b193d6e4d29ca7b632332551292142b3535a7e0efe0af153df3f7aed83efaa37a7f5 4db552380a009ee93ea86e9c56707c8e715f382c4d2f5b9d949d8d545d3deefa59b87b4dcf3046d0 dbbbe6aa897802a5952f4da058293d75b155a278a6e0062c07ab5e4fee9fe6732959686cc5b3ecce 0495677a3f8fa83687d94f17f85db5ce44aaa8f58cb3655bcf00eb62fef8809aee12cccbb68136d6 9603ed79939d5daed7c566656a88cb388aa505cf4b4b9dbde53003d2d2cbb996a471fada3c497d77 245edae9787d23acf9e384bf25a379f04f2352edc59e56d4fafe66092a8393f8c93f0d1f9bb0f564 1703dc935b9b780eed160002d9d56e637df7b1057b93ae5d452fedaa37b51a8cac4b7b170d07a194 0a24b1c0274ed2739b172fda14160ba96d4fd8ccdd2d5f9146130e0b6bbd1f228e0efee8b5efaa9e df02500a8d5497e748d54885e17e1c369f83fe00f3e62ddc709007013d1a5c6e6986af117b5dde53 b27a279efac57de74d19ec078f4f1cfaa278a73934016241c8a7858d91877823576bf3f07abfe2ea 7067ca1e8699decf23aacd49f6e3f9ed86e461052af8f5148d50d9306cb6de2d5fb05b3b574d1d0c db54b98a755c1597c6447a1df5b2268b8aedaaca994dddafd2894a99a7b943d882fe003e6f48b704 677197025777ed26eb50fd39e3373e375698eee4defb79fcae0aa01615795e5c291a9eeaafe06838 0d7fac75776e51c66f0f7bafc71bcddcd944717955572ca15ac68abd500126cbe3dc413d2d2ed64d b89edb168f25558723ac2360c92b9e659e19b6caf496c198e626f6941e985affef118754ffa2f89b 2a947cb7013416d6a0727f46aae3c1fb19cc8d86276f5a5ba7d23bdc2ccf132133699a337d7bdc60 8adb9b30e761617492b2a38d22961c51e7313b17ffd423d7ecf236f3bc8e021ab0f5143d74ba65ea d4a67a547aa1cda8d48bedff3ca2da5ce5be7b1eb4cdef799d87d9fb39c8e9757785be578fc368a9 dd07caa470d5fcfe50b5f1167ae99772a4243f939ca8530399b72042e55aad84c1b0c3ad450b999a 474dc5e4fb787e160ac77c7fd921554d9b93eb141e0f9d9fe34f21529d47555a4e45aa9a8d00b80b 9dc3c70149f8c243afdae64a9fdfc36e5d36e46d3aa3c36a320edb29dd71b896a50e389cd6bd172b 5489c2896bd7266786eb9a1a9dda2eee547650f38e0528f922cbf954fe806ec6ed43e5b30344151f 7d01715ee71f4ba20a45a25a6d15e2474f5ef1a6d2432570fdeecbcb4aabb2456588b121b765fe0a 9587a14a6e85c659ac7253a9505731016fde29ee78cd0a0cbf99c8749a30d5e3eab288c73412216b ce6187955e7b6253c8118eb16c11e4228910ad3535f97900a8b48b3cc7709c7b5e424135b98b679d cf600787b6597866cd1494ebea06f5a454b761ba97a1089725e5361c0816335d737496dd3382f966 a98bbc928ea5eb533ddc078cb1b7a1b54b78f9e98be86c0e459c916f1d0cbc463b0c8c93cb0fe220 e47f4ebe000035c93c8036f53855768481ea5ed13dc9159d7b5f6b84d7b2b46dabe4754b5e4685e6 5dce013923620537debe9d7b8e2b1336d1a0b674ae5e208fc85ce40f96d43befc9156410f434e1e2 219b7e636f315fc0c4c9b68ba52e4562376b61c8571107bafe7302a0111da90a8378ebf32311b81e 71b1ab9bebe5ba3961be4a9e92adc83243485af5a28977180ef80eb62fb1c96ab2432fb6c2fcb825 bb3bb2567d1ff7ee4de689a7c5293870e938aa8e8f3a72809dd84716cb2472f5dda2832c76397cb7 fb7900682f17a20ebae9836ae2b1f724fd7cbce5dfcb78331395249be1659497da9125be15ebd114 8befb6e53b9b4a751274216143477449750fb6d99deca9f57b45b0a883e32fcb67f071e32d631295 35b0597710ec2edc2ebb2b248c16aa51f5e5ef88035d7f2cf92100a41a4550396bc3b059bc6e8df4 fe9c55c94d8b3b9fd63690d600ae0b0e9c5a700373b78f776b3d51fad0bc93d6128b37653890d0ac b8efdae52a31f0531d7c1cfa234c1e862b6c9ecc6271f6291d679fca71f8d44291a29340cbb741ed a7f009db454750c98525005ff3839b74d93f541b32dfe769f12d9d6e2b34145a2c5166c55aa219ef d63a3cee32c9f5c1d9dc8e7bba7a391303ef6044764b079b270660a74c7ba9dd2a332fee0a975d0d 450a44f7f3367ad44b0dc4d96e77d1446d0bdf71ede7012a0c5c36782c38abc45ad0cf69373113ef 2277e6bb9a627ff2a1f7f390dabc6fc978b7d6fcbe6b59302e76b81e260fb8c54e997078bc5b2b85 22798247cb1a7ddeeeca64f49f1ba285583bc54388aaf542ea7622bb710eedda866c21fd3fe11352 fdbfc57f82e9330756bd9fdebc7c9a3867b15e3e6fb8c1edcad24a67713ac2b277defbe846c7dfc7 ec1dcb0aa28722b9f96bbb2b2df288856e6bdff617d876360eb11f6ec8c671b669b9cc6aed1ff9ed 9aee9cf0080f6add0d80b00ab9a2b2e286bdfbcf239eaa6fa7c4417e9f7bb460954595152bde8cda bcc6ab0359d437c4c099a3d8ec95dba348f64c21d676cd6fc87a475ed3ed9abe0a99e663c5f547cf e55be880a5381ea79693d42c17012f2d53325e59c8f343fd5b48556a2fe6396db0c8aaf6ece77166 69408890b2dfb329ccc91f8dd1aeb6ef9ae71a3603a53a5abe5ce2e920424087feda3f0c462bae57 9e2f278970bd90a7e16e91bd8487b9b24a31f3821e72331d799f6608f4966765f3ad4c0d3ca54f77 f59411017e4c61a7ee4e2cb21bfe15a2b7f9e16b1f80d272e54578e64069b56a81d2035e83b25911 0174ca05a0d2309ba0321a62a0d2e4a25e5d7aa64293ceb542acda43c2aabae38347938baf9e0171 3340d098278a41a30a359f8e3a9f3dc91181fa3eba23bd703564dc547e8ddb79055f5968fa3dfe29 7c3ce309d4afaa7ec1335edffcaaea173c3ff1b45f54fd8267fcf4eaafaa7ec133ee02ff6eeb7f3c 3f5de05f6dfd8f67bc5bc22fa91289fc774f505f0328dd3901c8ad86a0b27ab64065bf2222e89f2d 5befc9673ac476e94ef0e07348b06ff684a021adcca7dba5df4f5239159feda1d97ab64bb9994fcf aa3b2f248aa4fb465cdabedc02dcba4dde8b7bcde6c63f44bc5afb8b6250c277fe775562d706e5de 2c9a5e4dd652549b13002a0fa8032a803b8477de31031b35b34193337bcfe3e98c3c3b1593f799e5 f3eef7dcf4db039d7c9c67e3719752d31b0efa53f7adef0977923eb2f6f26653169c36b6862717a7 d797cbf77e0aa0c4924f50e2d2912747b43fc1941dba0510b997a30addbd01dc9b47d3c1b9750c9c 92717f7ae139f7ec26989ecf5618cc7bd531d11bb5d777f7b458bcdc89b629b9e916de76e6bde5c8 c93e2eb85d9004c68227d6c1a028b0bea62aa378e1a12d0f41f7a710a91251952ac5c2471594650a 05907eba808a724a00982607215175a8a0e519773f5c31f1efadf9832d36f0c6f53ee64e9ff99373 06afbb93f36ec05e8572e1714dd0f50794d9f52d03173777224d93b7103eef7429282d54bd7c192a 4615effc3de2d5da1f4b40e9423fbfab2a7407940d219ab4beaf0aa884d738515a1c06cec3a29ebd 9962f9c30653f0526c7fe0662779dcc9839b6497d0a5f9a8140b81553d5fd3f7c6188bd3ae4d9266 da06bd6726b721cca3badca9a1aa3e16e60aac760767671b367f0adf3dad6af19b2794d2be0d9d75 570530eca6424b9687419b77289fcfca963739ed8b6eaede1fdbfacede3f50692659355dbf995e7e e21acc2efdba258e6cfa9a5ee2657d4ee22d4d316f51456adaf662f185e9b99d91bb72af40c4eb9b cff1ab00a5bb187c57b5c46eb446b57fcbea9da7d361632e0e9eccc838fa89507fb88b0e56728a7a 278ed93c767b6a7f77068e60047655b92589bda19f839aaba94200d4eb5c49ab106fc1cace7bf72e 4434f6c9cf0c3d96387dd23989e75ced0b8843aaff39f9ae1a3622d5508e54c7419cd59b8b37c06d 6403b7270efc57c93a7a53c5b01d2db32fdb9576656635a82e6e84e68ebdca595ed2f34152516fad c34dc1f89673d9a3d9f8427076b962eedc761b3599a9101369285687a7f4dc6d8973d5acfd14fecb b30c5bfd688dfa8a3c95eaf5db2e23ed4cd07ed07d7f74334877d9501ef68dc5ca96ddb3c726b37c a3b7f43877d4f46d85535181952f369f55cfde5437e5a0cef9d22bbd4f48a33e5f92124eba2f9ee7 d781a0aef9965094b0ea077148f53f277f0f500ae4df54bb4ea42ae6f7df536553f3dc93d9933d4f a22cd2d113fae36115d6e5bbc75f4646b22dc74bdc78868aab3bcca62ecebc259cbb534e920785b9 2e8d4cc83a4db562289efd4a56ccf7167561bdde0ff8ed6cd4e04ca5037f15f1823d3e7e532d677b 25509e85836884aa1c3e29a82141f732cffe9aee795958d93be5ab6e3deafaa264b2b7c3e0ba784d 965a25314595c6813a9c7b458b91decb8970920c70112ffbf34dd0d0533c4f134adc25c9a3895785 bbc3b51e575be51bac9dc8c03f0550cee8e1774ff41d793ab5c8b38b986163d7cb7c1e924dee3aee d252f6f6ee148d56edf6a868a4887a472ff9e9a962674ac8f9498f09395121c9938ce45871f9389d 847277a4f228317d70f8760ab87ae4cb1e86c93ee321f73a43a56f95df118754ff58f257f8ae0a4f caa04ca706a052e8939f14d4e098aba6fca484b41d9d90f1c763eb5bf71e53cb5f15326caaf7501d 5ea8fc3d5eb0cb3c9f454fe7fa702fae76262d94bd05cf59abd28575da2f836de1f980a19c418ee9 22f71ecd3252831ee4a9ca4fe173f5fca63a8c6ad58047dff33a29fa1eb4a3aeeacd66ab96033564 cc7203cb3413ea2ba3238c5c539cd56970660fda429a5e1d4454c5062e403c43728492675892f424 865e68579a1b6bf1e371d45b7ea729b18fb7a9b18637a8546b59f9298072c502d177691e55a95f8f 3c975b0ac037f1fe647828e52ad9799c416c63451dbd47f320f3969f0949ad2a20950bbd5d77e5b1 7c989ef281bc162a4c62c7eda929c1b6df2f9a069820d28931ae52e91efa38ca0f3d71ccb55af563 2eb56992cabc5bf913e2e8e05f14ff8e48d508bfa942e5c1f87b5e67c97af8420224a286efd52caf 2323662abd5275b431058a6bb48be721db6c9d947d7724187572f9d91c38ea206c271c11f46be251 d449c3b9e3623ebe902abfbc9345ef000edb79163e6c73e3d60151bbd59f0228771cf0e902d18232 1af497135009ce4cd84039d393b6c6cbde5d8b90f92e20cb5b619390543bc8f897fea39897ce64b2 26225ebdcf37daf319dbf54e2b5a4c5476d46ca11d8e797fc31e6efb9674a8dc06c61e870ff1da93 b009bf4cd8cd718778dce0ea4f0194a7e12bfadaef224fe1300570c7648276b8bbb9cb3b17581dc6 2cdc0aeb733cb9d52ceec82b5d4d7bc8920bb2a792f1aaf276b6dc63997e7b4c9fb2cc82ca752a5b 52d76efbc3ae8733fbba373811243cbbe13ec10678d77d97f02e85f7f12e3c6efe15e2e8e08f5e03 e5ed3b52a5c80aa8e4b5f9670bd4a8e11baabd13abd6eda2269391e274a8d0f71a7d9ecc37b7938e 736fa1cedfca6c58ccb668e9301c5279ce8cbb00b9bdf491fdc3b8e184ab0a34ceccd0130638ea86 8d12b7004b4883f2ee04dfc73b91a33a3f854831fb066589fe8c02152c58848f24c4ba97e75530a4 595ed4ac259e5068d3eb9fa729e920154fefb34098558705d94d9a493d1df8a8d3bd2e095fcdc99e 2cac5744f75cc3b0f7e84561a9a84bec667cde4095643f449792164f07d142753445f3ab57efa710 5916de006a3a3500674a8b2783efa3f168462d74986125a587b552e769ebd03f5dbdece7bee75e44 38ae3f726f4c26310aa9e2cbc991588fa8ed5daada2398ba3bc1f9b68960a7fef580a50557dce5e4 a78eaa8b868f16352cb7bd4ea0d116d981c90f113f5cfe17c5a0ec351200b2e10680ddc5c2beb916 acdffc76f302aa03453e5356f28496526da1f926e6ec4b0504935130912add6bf768f6e981bdb7a7 7304db9d40b830ecb53fdb184cab232c7369af77cbc2648f6ac84e40379072dddeb0f0b945eb24bc ade8cef0a710d5e624092accac6e6f6b62fcf5d491c9d4be44aba7859c87b3ba783f2edf3ca50f2b ec587df6e802319d1fd1204f1c9cb12deebbaa70c3df28eae1296ef9c6e689556ea74c57955de182 b4e3f0e9182deb4a9c3eea1cb7f0bd7e462c9c723f81ae46a1f05300d07e97f2c68ee1eb9bdc46be 3c51ea26ad590813eca7a8735c59f19999374ac4bbb516c8bab68c27b7fbee23d5c3df477f89a758 87c0b2a2c1ee0a67594291a867a3654db7b6bbb2ee6f61e399442c2c5744885aaf81d46d62b4215b c66ed37a96b9bf471c52fd634958e3c7cf6b4ef1a98bbf3404298f39aad0da3d0fecd83a8b74815d 5c8ef0397f8b776bb523bbfa13cb0a66625790cd125a56afcdedaef4f8ecd63a4708d84590ba05f0 4fe4b6f1fcecd1caad7d0a3aafe96efbbaee862b771572e7d48a1b252a3f855b8a2330a5e9f40e52 6e93a3793a9789efb03333e7b23c1ae3f1f6d0ba3a183170e803367bf558549f274e5bf87aba6c1c 7c6f6c5a0ee3ade936f38aa6a96c66c5f5a5e26af0d2e0085e73f91682ee529c2447cb493a3f5fa6 ce4d64212f1687c53c7f8cf71afa1cbf0ae5213c51491ea904dfecd4705a69191592c8f61a045753 dbd80c4c7b2892cd8f100bb1679b96cdae5721bd443fdb18f0e3fd72929c310b79361616f3ecf23c 5756336dbe2a2e6ff3c27563cdf42deace90ca219895efd43b829e991ab8539aeeea21fc05446ff3 fb893c6e6eb7fca3fddad2d9dc667b200bbd101f7bfc0b453285045237e4f4baebadf24b71d8282f e699343c5fe541638694ddced4d83d06d35df5399d58fbc77c4234ddf5a4ee3db763877ac63fee38 26bb8018b742408e7c3e418fe8519a1b8652e214a1aafc144071a35e4171023d41b17dcc83e275d2 032575fc59499799be06a04e3e05a029d38bfe050800c16d15405932baac598f6a68ac73937097ec e1214cad841046e55b60650d2fc0f95c26a8371b95e761566df99ed218fadd4475e6f59fcfb973f2 2b03fb1cdee331ed73fc027ea8fa05cf389ef6abaa5fc06f9fe65754bfe019df2cfe55d52f787e32 53fed5d6ff892ef05d156904df544b09a40f4a002540d95ce800dad6d2003a9ea2d5359ade036836 b946b2e43bdcad4ed5b09a70e3604a6051c57d40641b62b4c89fdc9e4e63e73f9dc439aacde3b3e6 7b56a2ef05fa63ec0dd2e6d24d1ea6737ba16883c7fa8ab6bf8ab8d28a876ba44af6c2a86af90228 0db92188e69c911674bc01e83a8ae6580937baee7a6d2a34d195113c86a344405cb7f5a71360b327 899f0ecfd6fb2af93ef2307d3a9308fd2e9ecf7a6161d1f206ede930be1531ae75e6ceac5c5a3fb4 c6726255d854ef5ed3acf657018ac2e3f6dd13391741895046d11af5460208510d5029629fa87a65 5d1c87f7e4840ea265b4f1744f95e4b37daf347cfa5d99fbbd49ede07158e5e40d5e1dd37d6f4681 2ba6d09c3bde5d5ace6cc48eec65f7b07c94aef0f28ee7c59141f5daedebeb64567f47fcf4ea1f4b fe84efaae2305225d538e0678da335aa770490f830416575ca838ada9b0636daa69fde1032fda092 49fbfddaabe50d7d6dee8a4088efb0bb93b5263b322a98cecc629fb692e032f672ab571f9ba533b2 d0b6b2b8d75ec4fcc6aecc9e2eb18bba76116b95af0214b5a7f1e902fa1c44be660994c2f71440e9 24f5c9950615e5510cf17d6ffaf44288f6836bdaf45e989df1922da1e5a641a491a3e0bdbde252a7 874e5ad74799915d0bd5cec9fbdd57cba6fd4af40d0abd4e3e5b4c248e633d5bc8b755cdf7eaca0d 08d057f151fdeee93ca39574b5300350bb44c789d2916754d301996c4c3fbbf32855c67b4fd3962b 6997acb3cc8ddbf65a4a2d1fdb3ab7b776e7367f273609c524bbae65d08d47781bdcb5cc5578bfea fa8cb17bda52b0476a794e752e5673563b3b5d18fa13e2e8e05f147f008a3688541de4054ac53404 caf3fa1c40649f051562688777902e3dfd5b63e20f1b39da9531f3eeac5254ce2e370b6deb3e2016 f7660fec8c27758c77b5ba25a649fe9aaed98a9e4b5d4c6d55d502ada065f36a59ae371598aff7cf cef6dd94e9ba0c49214917bf0a507ca7cd08bbd7e76e54a3182dffe9c1e25b566f4559d9619d4f14 9f3daa3df126d734e5a8f9ebdd86aab39c850762d3f4cce2f426c0a3f53503dd304dddcc8eeab55a 161433f156946a37fbb81044fd7d76ae042cfb17321e6ca4505fd54f6fb35d3a4d6a50eeabf85d15 94560d38fa2e2d97007a637cd4f03b3b2087a0e00fc3e2c83d9fa078bb29fb2aa8f747755fcf98be 3eafdfc6d2b6af2fdb97a9563a74378a6959f8e571a4e9b3bb5c9de4677ba8cb3d0cf124507b16a4 44ea593b4db074559cefdd92986bbaf9bf42bc5bc25f147f532d15c8372871c3eaf714d4c29e0770 57749fcfc433ef9d88c4c0d12ae9e3e35e3eddefeee99d3212fd44455756f9967a7b8d874a2dd4e7 67af33fcec64f17ca70909182e2b2578553e4d3cf77e4aefaa2f31b75e4082da3c558522ba2ff088 b5ce7c159162f1fedd5399d77ecfeb1cf04268c1916aeff8c87b993ae8dbb74bfa68d9bbbd613277 23715d2499a2064197ba42a4b2bdb39fd84de4fecb5d482399474fd3fdf6282e66234150b9b52e14 5dc6e7b795578187c27ee5930cdba8e5b9aa5bcb7c1591652eaad2a8378292b5aa01a8c5ae3ffb8a 867585747cbe63e6dc6522d1b3613c47de3db08ea3ea37a93d0ef452769a531eb54de5ec2b724b1e 5c8a03299562a6e265dd5c095a3783f3b78247f39569f2c2616ccde66a75211b076d2b6cb31de4d8 26f44aff107102cc1f4b224be8fe4db59c251b00da5c369ffd3aa33e8ad87e226a37673379741e44 3dbd37c3515fbb2efb054fbd174be98b7b18403228120d29a5995d51c94dc7c2661bc40f5ff0e840 43391c628fec214f896c53b04d864a0f5211ee153ae85ff27450d3325fc577cfc129f284e9c853b8 239fab6700a4a0dd583dbc29cea76c236db62cf2f0d819225a52f4f2c8b2151b7925cf8c5c284aa7 cea476ca954f6d016946dd06662f53d6498f578cf7ea124cb73513687677b8d2835af245bdec0344 096334be4853420b4f7f159165c3faae3a939bdff33ae1b41475d0f6dd5d38c8db864db371675257 f4aa3882a8c1921c3fe673a1b01390478a953b2d063948b8f6b60dbe3a0d7aaca31f260c5d839734 57ce629498cb32546a54578e7292f58f33b9553ace7683dc71361fa7ff1ef183ffff39f9aeba5493 a04c192d00bd82487506c9511fad9a8e26ae5e8fc6e25a3547d5e346479e33567194d5f53c10b0a7 9471c58c583a80328feda775d6c5b50ec3a497237ab849c55d804a678ded71deb02872d509cf6421 1cdb079df0f36481877364615f4f7f159162fb37cf9bdffee4758644361d3f82e59d1870b5d16723 b83fed1374cba3a59956cbd7a90bedb6353965f45d51af1049fe6e9805d6434b30c338528b4ebce0 def18c5a13521d8aebc3b5c3edf7e6403fed776ee1beaf1e95dc7e2765f3fb1d9a8a2bed73fc0240 091f3e225c93df1290e3dd5a0312770577619f9547a3071c3369ac0b3a3ab88f146f5c20cf897b42 3ea9b95a9c122fec9633c0b51e7c9661ad4c994e3e0f8de3b25ce892254e1f1f2a5b7ab5af2d4982 6822a2807b95c21d3f5eb834ee5ad922ee72a9ec17108754e32328b1733bc2235a828ccb9de8bb34 429f3d9d676c23b510cdd788b95ecb9d4b4a3d9868effc0a754c5abc9e27b1d2cc9b9c4b0e0286f3 b0249d2e664ac7550daf1e8c79b6b5c75d7d481cee621c52c5299dc331d654786cb02c18bb7741c9 6083461dc2fa6ea1f05580d265157986e9785b84710fc05b0cf5a65809b35cd8fbec9f5686b4b34a 42dde465982a77e4ec11d988a8a1727cab9bb9312fa6e5d199ee31715c4f33f98379a02afb860a37 f1e7d41f606060ccb144dbc0761336c5a3f27b6ca07332cca0f26913adb8a6e3f2077148f53f273f 0428dd770e28a3bddc675be1a8e12b4bfbe60d86b7bcad8c54b285ab97e1c84fca73ce699ea0417b ce937b8c648785fb99ce0af5b80b1ccb04070ef8a59621fcd41dc259fed0c4c6ddf160376b7417e8 b23326b61a4b9db69b61f2b12d5df1dcb6740c1adbf554ab7c159f09544038511fdd4553fbe7a5e7 27fc59ebaab0e653259bbbe5f9948475491d6edea2c90eabfcb1771ab2c3b0b4a573cc813d427441 3d10ae6213cfc1ea8573cf5a0e9bec83ca6e4ed96d74c5d993ed35998c43435b68d6641133cb5c91 1d5f4b21d5d2b18ec0b77de3ab00e50ae70288570a0159ab97af97b1a7a9c42bff384faebd9d547c ebf1aebd626df1065cd063f28c04579a94060913f2de1a6387265fe08960ea6b38ff161fd8f48c04 bb4b1ecba05a790da1a5ddbebddd3e4e33e4de7c6008e65705a4468b8f8ddd6fe7377b6d5efd1d71 74f08f257f02286f750f5468277d9b182d56ad53b0744e1667c6a982a40f42d39e68dca03a71992c 9c491c6fcf43f1e094ebcd7d97784ff177c742f1d4c088d3e0b0ec4496768505ada31b5bb4b7464b 065b98be66111cb8305217aa838d3346369b96a1506b1f9bddd7fe4208bf0a00ad53f12f621a5cd2 dfabd89d389e13698217b15bd0e2c2124f3272f178a2f4c1365a212d4a8fbd8f6a21fe3e4e52d8ec 5d2ced56e9621b45f2b9c9d6d8e4d65bd8c8e108514d1f91faa32a6ec86643ddb4bc89b5f629320e 77afe9ee23bfee826a6bc58d907904fff055dcf70f6ba7412cb493df559710a150a5b8c1e93ea497 4f767384e5317e68dd0af15378f16ead3c26f70919d5e790b685af0f13a9dfcde786ac7ba9b54f06 c535ddc9d4d6dda0d459856c6db8e2069df96af05e6c976f717f588a9333bf9ca481b690970d2fc2 3ef5277cb254ff6ff1077ae1ecaecf6c8ed98a08d1dfb17ddbc3a80de06a07b2586d1303fb31c0b2 3c3641916c7f11d9d5d00d59f38875d7bf1c575c4f13966f5e3b2f27ff8fb4f3605e544bfefe6b31 0b62ce392b8a2411c922202a391c7dff0ffceedddddaad3bf34cd5bf6aeac3c0540d5f0f7d42377d 9aac9e2e073739d9b1d7abe2db5f17ef3e58ddb7dfdc6a5b2996569059af2e8de3a0bd44eb9bd112 b184f5e24998c73f85da2fadb71244fc544ebab2b3fdf67cac7a063978c1dfd33266724744ed96d0 d633aeec2e1da3b11dc75c679393d0febaa8ae26ab2db45d2d8de4392ed12a9afc97a72db1c09a28 3db7a82d3b27ba283f6f058432732f176d460fc5b414d8ac0beef6f432358204f1f74f21a9b8b3e6 7ab3eef2bc7b6333822b7e440c128f1a4a2045731b9f2fef4d4e1c3aab6db994d6e5582266081675 cbc9ce5b9e5e98d17d1d9a065715995ec66663128b6a6bc2cdd5cef87b137a63712d0fc7f3b23a19 e7747d315a21f26a547caadbe11d7b1f87db8643fc1392dbfcd365000d221e408de903946d360010 7b42002cd10b50d96dcea0f2a93d01d2540b0069a5dbe820120395af2d838ad77041e5714a66e387 d70515b9ba88cdc1301d3ae383b565e2c3fdae466fd8b5a293f40d433bcee7c2ce245f0ece3440fc f02541fea8792879c059e4ff00ffa3339d08feaf52ff4067ba4efbbf4afd039d3fd3daff51ea1fe8 4c035dff2b75f9f997542085000aa51a803d71092a3c754954f65f0039f925806c4663808c593c11 1dde0002f782d8940ee5f8d8977a71edfd5ac4b57b098bde93763a1144276f7e8b9a67d10949958b 031fd20a417f7e837df676ab7be0b3ab7ad359a7ec657395e21fe06f9dc3b509a0d22d0d4bff04ef 41a56dad4105c86ca272fd0648548101f23ecd00a2bee8f839c9e871fd900f23abd087231c5ff4a3 16745a858ec8e32155bd5dc3cecd56037f5cf5823eddc9f8ecab5ff678a759f3b248aded2e8e9ff4 36ce9a10ca4e79722dfd010084150400ad7689d4c62302300a9aa0427c3700193a5780bcce36a8f6 fb9504d2227eaa361359b49646d5a3d6eb1585d4e70d87dd7dd40f827c651330a72611f43f7dde8f a8b5ee5f1129f03e2bb3e065a72ae21606892bbd26a88ead67f0a67578d7a1f7bb19177e8934b5f7 df2700ba54c444efe999e8f562003faa6d50d1a02d40d8020faa552d596849876afceadf96c9aa8b 3d875e9e3582403f44c1a54b55fc78450dfc3189afbd6f96213ce128f0deac68a6bfc6cb121fdf5d 98f5a253dee6aaf68e8e5b965943baeff738d77afaab55e9d9b77bf93f0080945aa2f3727e01e856 fa804aa6d7014869f0b34312099b02a8d2c003b50c5b8b6c955d86e7f9e91cb0e5a9ee83ee14785f 8054bc7936d7779542b0728b2d17775431e09cf22bd06c7d9c73ec0adacebfdf6518793908dc7875 9449cbbc4ee1fa23ab9ef33f45195abbcc1fe06fa90a9b480d1b5f50996cff95285d1d89a0ea5583 b8793855437f7c5e06ec6bcdf8d359f3e1cd633f5d72b82b49419ced78d5b38d5e636123a31266d5 b32fe6fdbe646eefa65db55e1d7f9d35af70b9f898beb39547f6bd6be8aad847345df9e435a4a47d 7e8f7443597a04d0b323fd2515ee2db27fe7745fa823a8a28734a77b12469d688704577431f767e5 3ee3158e9f87a33d18603fc60dd8c22e5af74d4c4ff357f7dedc99712fc21fdfa67f7dcc66919a36 9a7cab39ba4a0db29a8e75cbda1e936bf7237fada8b8bccca9ad65fff3070050309213b1d23be94b a7fcbfb28faf69a2349b16398d4f5e2f0899ebbae28bf9e9cc2bcecab4a3dfcd875dc391e84d626c f9191ecb0d73420b8347ee339e1b45a5b8d521ee4b68fbc7f7aa21cbae7eaf4ff6a18a9fe9bcda3a e62ab7eef50b2903d1c8c831aa7e7e42aac9f1f7f85b6aa0590016d9c4368bc6105411332d7ffc50 23aa0f7b01078d204f614753c76022caae1f36da9bdabba1f9597cb23fdf5bcbcf605d9bba0ded51 e406f7a7872e544b9da06acb1d5237baba959460ad58ca4008d2bb4e4af2248f9524718de6a47979 f6f903fcbc8ac84dff25d5bd15ff4e419df938a88d6d35f4a392eb8b74a7ec6acdfec47e151df2ed aa899a31bf721e059704fa4e0b0af7d77d5e51ed03dcbe79236fa484396929c7803dc8938e964e04 d2f799d7a49cd60b44859472a222948ac25d763f09e23f018091b592887d58a002bd928968198c7e 0ae0c6ef56a27370cda4bea7a784e5a2f3106a43cb2674fc35a462f9b12a78a6be676bfefd7d2432 6a0705252574ce3519288bae9cd9d526d25c6eeec4155121c4527b2709dbc3c311e07ae32bc04ba9 c0a377eecb572758f483343af8ef937fc2df5211db06957158fe2bafb36a9489b8e93e6ec1f8f37a 7b25369bb3df48b9ff4e1c15cc9c038c336088d5b53aefbb6a77d24db3ed9488d50a321f8c2ad262 fb698aeb9a3a107690b0e41f3de1c0574f6f967bb5162fae41f3117782a7f9abbd688304bde80f00 e0eee1f663023f52b71928e94ec80454bf4d32a2505ef645417bb9955294b59bcf42f73552c7fb47 39dbbb68afea42555df9fa56e2791ccab3d922231538bf24960f428d7fb8e3365fabcea65ce3d3dd 5ec9ee26ad03756d078ec69e9b4d9ff56e7e963daf41cc9e4761f807f8919a20744085836150cd75 d3fcd33e1906d5b3e4e767d2d339d8515a9ac5ea0ea2b629efe3b56e1633f49d6c56e4dbe5be3465 712dba52710503417fc879dec4973087e7a1e695b48321eb23b90ddb5fd5c90bcb6fe4cba8e3b817 56173297d18c8f1248c17f90d617f8ef2bff018067a77f49bd5713a9c8204d419d92e1e0b613bc92 727ad8f8f411bfe289507f6c3065aebd3736a99e85aca84cbcba2e293ae188d03dfcb1b4e37b93e1 701a2a5dbba5678dedfb5cef32b22e734680ef47666ad685b35ca42c661a1c334c76b50e131c823f c0df3a67c0f95940c5bdca5f25c56bd735194cca7dcead34a69a4599acff140f7bc440487472a7d0 f3f13692754e5ef47377711b4cdefcd3d3028e184fbfd7ae12142f71e78230dfe2b6cde432abe979 d927505abd5a69cd7b7ad39ebd68556b7e6995ec84b4baebf87f0000efcf6a32e8e7ddbf12a5d3f2 c7313e9e12bee83459a7c65794f7b0beb61f773d48a3839ac534062a139e0e4ad6622e52796ddd84 5aa96a72648df5ae3dba092e1c2fe618f188c2e755a6d1a03751754419ade19642689a219f35c8a4 90b303a8bd5c8e12d4c2ff41ba81f91f2eff2dd568fbc908b54da43ad42ca2c4f2d15b671dca7233 0eff9c8b8e6154312273a7ef99aef2d59b1be99e595282292812e74c33fab5bf39d8171efba9d4c7 2cb25cf65c2286251a5ec075f2f9017db2d183d704719a9044bb19e804815f43c2523f31615d3fe1 1f00c0149be8fc4e0280c874f5e7554453988583a7be751ef1067dc5d10c7fc0fa40d16c7e1cab43 f7d1909520bb10d1fe00e75b0ac15d19096817019dbe9805f804b45edc03ea70ae16c8139eaf121d c4eee2e77566710af509796267565ac6e0c4cef701de337c80f712afe00f006041ba2723291a826a 56a9811a6bce7cd1decc2de7eb4ecc92822ef4e6c0e0d46899f3152964d35f23c1f17bc2db63e870 0d4b187bf9be6d8529b21d83362a8e4325737c4c38ef6a0ef78d7ce51429a0837df4d21c9be52638 9655823b9665f73e26d48a198c0fa3f89f90d617f8ef2b00765e3aa8a862fc53543ac64ff2c8dd2d c4a6a9308d8f7e7a78233592b6ec2d07198ea4ebc9588eb7cd21d717911d9bcdce29660dd9228d7e 461a69d7df6f8296f074b0c187e361e634e935606cdeacb68fab6e6f762c062271b8b363fdb02d5f fd63f13ace1f8b9be6f70f00e08fad03a41f7c40157cabe130974b43aae6e25b3375ec3575d52b7d 1a2b4ba3ca49c61ab305a251c97397c2b5c3ce6ed09a810628411f0f199e74045225faa0f93e7d0e 4e80495fe67bbc8558f9a07de8cea1d2bd2fd0475827d1036b3ed01a5448f778a087f1358f3e36d7 ec1f00549ac00088097d40ed86c0a6846c04bd3e5dded4519179c9f7d968293eb5072fb457991737 3c6b5f36df6ba50e3b03778c11dd684fb6247d02141e07bc7012c9b58eadea35ebb86d97e303da87 4a2836469a7b8b1e4cf6c45c3fed5bd1f4be733792bf6f719dfc1eb7d6a55f220da9fefb0454f0aa 09aa64377a0da822a51fcef78b3af8de05192a6c1cc1ee94777c2f3ef0d769fff0b8acd14e703e90 5681b2e34b8becf787539c1733bbd362f522b1f54ee38fbba3a41d1ed62d7db786be3c29469b7da7 bc27b94267df99ec563b3f639f7767193577fde23ddef5622ffb0700c862f5b00fe28632e07e0f53 19b49d267756d3115a687b83c6f5d33251b670722ecc1ef16f343e15de2443cc42fc13c185932c80 06a62ae1e8a86bdefa60fa3676a80fec338a739eb87733c0d8779725771714d7f9dd80e01bdbb893 996eaf3e4d6dc797a2f653e4146b3dff0011a1c2caa3d80bb7aa1768077995e5719e3cafe96b96fa 7618dd196c69ab3120c9e0584f3f77152b2765a49818a46c9d9f6f7aed8600b58efd12dab246f53d dd9ef6f65d6f37db5dfabbcd6e109d4f5b6ecc33dbf1d75636e222ffdacc0bf3cf26a7de9225dc76 384f40ec7e8f9f906a72343f93ccfa4e3c0f1b5996a7286f577d94cd1379fd8c65b136191c1a23fc 4b81c569095ebba3b1648f070c3ed07b176fb2bb80aec8db982d1b5b6ed4b4375fa1136ee6b96e66 935366a5b5b2da203fc9b025aabd2e6aea6475df45db15f42ce04b035bf04bb4a13d1300ef0fa061 b9d142c92eb36ba11e0a1b36038a2bba75ad3ac4d8fe713c4e454e2e1f11f558435baf566b175050 7fcb0de2f1469cb88bb5b2786ed745d5c256f74d40ad2023609768d51596c82b4efe0d2f1a8bba5b 7ccf2dbaeecd89def8336f856871e6b2626d468fbea359f75b9cfd845493e3efa1f00b732e3cf3f8 929ddc9f330acf85437ce639b7239ac79f7b1783ec6d7cb682cdeccb8175f146e696c67e515c3cb1 11bca8dbd334e7764e74a6ed99cb8c7a337ab0184d036e34995e268be5249646eb09b758a09349e1 808fc5cd8e1acf21f23ace19923c5a55353d81f7fc03a4ef6ff6d5e58c8d2d714cd772b7ee69367e 370e756d88ed06ae77dee404f2ba82b4beb8a8bf2365e6d26f7d7a1909e6849b51eff157a1dcf1bc 84072365b78f4645730986dbfaf83b84ac796e80b6878501e24dd204983ed61f56faf5685eef11e3 61b3d7faae7a5d57c4c65d7a8e4dff00a00c7749507aec79503aa8064886a4c45b5df32d00d7b035 8099fae567399875dea082ec93312df71e0038aced00fc3ab00992090e56abc9f2513d249ec3358d 137299218089e13a7eb46922ae1e6836c2dea212da9068846d547c05f40b7d06bdf5f0e187e586f6 839f90eabf4efe097f20f50f74fef837ff47a9ff3f9ded3195dea6e4e1c2df5279ed0b20566b0378 c16d016c0e5850e97c12d775469541a5eb0f41a59af8a1953cc925a2ed0780a376b27a781f4af163 73aec707e8318c0f99fc3aae4a7332c2a21517da2d340da6846d1a33033a241c3f6cf6defec5a9be 3c30cb187f8044ea8004e50c2326edea3c0054cee60014865d004bc62e9188f3a022d4fef2080401 0215ba30011574710295392fc68fd07ec7073c0fe21ad484a297386a46a73939891a21bf09ed5348 8764ab2107e72aacfbe11a7e7b609ff73c3eeb795ee6a859ae34b899ff411ae8faef2bff01284f66 5462025731d11b255247953c80fba51ea864e2a4e548410095cfd40348d94140e5db99c526bac0e3 daed2047efe6f91d3513c3099d9c09a58d465efc56d86996a781a737b7c1797f3a07bd4052922721 99dec4671d57aa9f43374fec02e736593b4ec99bbefe00a0bc5d263adbf2cff7d6cad3ca3379f0a3 2280cfbd01a84ca103a8d86f0920895305904db6169bdffe227abf9a44d42af7e590eaf6adc05f75 4070061b38e89378c78fb2ccd4671975ef8fea3eedf1bbdadd9596e557222c9b6e8c714af427b2b5 432db6e128eb598753f4fe038032b1a7937655a5c44047efe4c1ef4a00f6b62350e1071840460505 2037298c9f78a5f1539aa55a588414888820a84472301844b67fddfa5fef7376614f28041d2fdb72 a7aef4c8eddd451fa1ddfcfba8d91abab4acc77ae05b55671fbf4f7b2a7e39b9a3ffea60b3f7ff20 ddf8ff0f9713a91bea6fa9e06003782ec17f274a978e3840d88e0aaa552f8e083daa85bda1390b86 1599f0c731a37862e668b9b23af8b805a30a39eb106effd4e55857a7f68e82777625d7a3adc310bd bd9b59ecfd6ac75bcf0c3dd43747072e7c0805da49ebf9bdfe00a0cc1d9326ddea128086bcfb33d8 d08f0aa870c61420ab2b01aaf9dd3d6eb08f30ec0572c507e475ea49c50dee2a525d76ee23eb651b 390e58263f2d5935b3df7ce3c5d1f8457acdf5d3ef2f08336aedf807ffdae986244cdf46a1cba6b7 d1d55e26d2cbcfd0d1f48b67fe0140f94e9d93767dcb00babcfcbfb27a2b713c0788a293a08af25a e8e96c108c30a1e249e63e4d22774b1e74b21f4d5148e6fb86f9266010bebaf835675e6f03f82150 50f3917d22236359ea6e75753a23357d40295a657b35efc74ce4de6b7e3f50dfe6d4515b83e62385 f13f4813c7fefb0a285b4c22957312a96126fc3b55b6919b836acea7e2c6eba00651f7e4f9599b82 dcf2bd35b2cdbc75b41ac1907ff5a49b6e4ee665eb91d3d4c82832745e872693ca4fb8bbdf6bdf8f 9bf6587d8bf39d8acf0df646359faae28bd04bf1ddaba730866acb718fd1ff007febbc27ee235c85 a39f54d9bfaa8cd417a03a2f9fa34e73a4f8c26a64bbf7ecac689beeb76f351d027d46ed17f35874 3f925122714333e2ba7b7f8a2e50add3ad7073672472eb62644f61347e21c7d907268f87555112f8 d543123e3fd90f92d81c3b524ea81b29f4df0394e36b22d5cede92be944c35156e584f1efc6009aa 62f31cf6859ae4c976f3ed18c76ece22f246e735c01a690ed4630935715d030ca755e39caada73ee 79a3f59ea70441f52bc7f74f49fa9e9dba347b474329b7edecc4a574be0af7c2eb2edc07dbb758f4 bfaeb01d18fa2f913a1eff3e0150516692a6fd24ad4a4f4062a3683379f09b55dccc67a9609cfd08 eebd937dd81857f8be7b4dba69e6cee7a9ae3f4d54ab990346250d45baf5cb6d5d06c7d75bce4cd8 50cad7f6e9e2565c8d3715e14e1fbb02f412963cda2ad0dc73de91f96a26ffe2ab6dcce19ede4aff 03fca5132a95932635b61f8014b13455f698e60e46e4c521fcd9d2e41c4372348b84a2f8c9e77a55 e37e9d8eb4a38d6f54e7fda56efdd7969779f3a54812419ae2bad0499acacd02fe91fb96f8eaa8d8 e430aa35b95acffde1da2e99c29560cf8fabdd1fa6267025a286f10700505d4da40e9b69c887f802 64402752f7d42af48726ee294599b55f35557d9f513d30f377afaca362a17727a6fbd4046e8cab9c e48f5365a585458a6279d1d1854ad97ff3d5400db9864667af6496abb2dec018b03d32b7bd841d86 bf84b3a6cef61cdb4ee019bf47baa7303dfe2d159b6a7f67f51ec516a80ac23a8848fce0deb77bda b2af94fcfc34978e9138fe05ed94f33b6a6f595f28e3103d48b2ef9d4528b31078731da9dc1bb99a 571b6c5cd6f367e977d82fe1bd075d469979870192b16626718b65a645f3ce8080b51248c61f0040 03f3022065a7ff9d2a2bdf139d1f7ded7ffc71ea143a863720de3e34e3cd85ff7dea87f73a7bef94 84860236c1449eafe77b715b9448015934afdc5bd7e52b35dc3f58a6d2752e6c5c8c19decb14996c adda3a2f36f3f9b900c5e77301c66f67092cac0487b4d17e8ebf4722554f9a34200d50c1b544aaff 6cc5278a59f8b94171659ff2f1e1350a82f3a31c2bba86b7da1fb54f327525631823a9b487531310 d02d8973169ebd5ca98091d8c1aeab5dc683dc9b994156785ee61f797afd79d529bd971f53bb9023 a95dd492e9b2f479d29b6cdff803fce4722cdf170023fc0354de610e543b713b2283e5d44b3cce99 d519d14b73395d9df4a334bfddbb5d102a1f660c4bb7f0d2171e4cb8e41bd2fa78a575e7cc0e1b7b 9ef9324595c96d9e4ffa9ebf7894eea0a909504857aa9247cc1b116f678e116fdd15c8e3553749d3 ca1a7f00001d9d44e74c33936e9f4974eebeadb01f7ffb0e2acebbaf7105a495fa8cbdeaedb5965a 126ff14a7365f98e94c47d13edf027eb31bb7ad1086547399dba647633eeac445f85dea29c41a123 d421ebc11010446307e32e79ebe33452dde3dda7c2e32e4d3d08028afe429a38f6ef937f02809828 f18704eff9d7b704aa0ed2f2bf6db169d9cf17f4b8673a8866d1e45abd5037569977762f69f3fde6 85fa7ed2e0ba303f62afadda36ed37d9958c9fd7c73a43c3dfa748be585a23ecfdfc8d7bab497c0a a7d3d26928b35d0cace01d362151f6346437c629acbecd148fdf23e94bd9eb8fe391ee9340e4762e 7e7da0ba577c85397361141e3ad664f2ea458b268a54ea9392566f1ac22b6f7e381aab222cd73f0f 2eb9796179de90cc8132ef759ac47b4f9ee838b48af795f9f334e227213695d7e93b82a314b3ede3 82af6e8f858e783d2ea8998e4dd7ce3385f97b247da9c2fd95d35dedac72a1cfcc33a694db88bac9 6f6e6a587c87caa2b84c4d40d2a4f751acb53e37ced38f1e0bf87ce992c7c9ce79cb1666d4d36476 641b6ee178c0baec89eb7232263608f3a85450ff70af9f0b0788f03aa851996fd0bdec5e93f179af 1f54b7f0fa0fd240d77f5ff90f009cab255223d406556d099e573fa0f48774bfa8418bbb2a79d6d2 45b367d585e677bfe7fcd094d84f80bb978299cf9f2b22d9a04e4e6544f845799dc6a1d9f3ec789a b6a033967f5ac2b1a4ddf5036c1a2e7a2882fcfeb51bb6f7a74a80ee1bca92df6381a3a35574f4fc 0324067a4abca4339dfe9a37a9a5dfcb5af4b07b476c904ace812f620d1b2a7c9732bad7f83438b0 e2ecc8317772609e91f7f743b55e244c5c9066e7f4ddbe679852bbee8e77e8481ef6af198f1ef5ee 7d6f211b77dfc2b8fc8eb2e3b45ee7ae4b51bb5d775ee177ddde41df51e8ebf5070095f54d0c7b33 5e782ce7dee64e22d25e99ed584cac1224cd5d3adf2f2b6a97e1a5b47ca69929e7c73477a11bea53 25cfd58b83036a913949c366055b670bdde3aef0991f0e90b3434fd537b9b74fbeb0f3a8c8d8f5c2 5ab00d590adab2e3ec703bfc4ae476f88095edf0d033fe076948f51f2e03a49f119f2c975969d8ec b45684ca6827a2ed327a0d3f3e7eb9b5bdd2f971ef0de9e662b525cfdf2d418c6e13feb408f277ac cc79d6b1f2083f87dadb2fa3782b4a372ded1dfa33dc77a2ec72c76c6ad83632fa97edb8ba53379f d7c3dbccc6b56282d76893cb0c8f9bac7417ff0056a3d35eea7b6a3abf8d2ae5740125eadff7e61a ce666b66bbee887453dc95c84193efe2c2f3363f2df9077a34ea3c79c032676eef6efad2beebeecc dda577f2b7314b7eb7e30f53da7c05b9b61167efde26a7f8b3b5b2ae1dd6457dcaacee7be99edc66 b5ade6bd15f422a115f4eecd120ce7bf8771fb34c7b710766652d960165c0f81e64cb9b09d907e20 d9b8d8890ad87d62560fcf0dd542892a3efca9fde06c66bb41b0dc6cbe7c0bddccb35d72ad2c07ec 7a555c4a69a581fb0a7a6c9e4be340f94bb4267f174ffc5e5c60ad6f6351f706e3b975667673a2ef 9fe7ad786d24d83dfe0769acf3bfafa8ee561f4b37773fe5dc592ef9d1efc680f0c943f754bccada e1b9c6edbd8b8dc36dcc14bf1b719229ae57791b5e6dcbaffa12456eadc513d3fa0bac614fd24a03 cb39d109d38920ad33709c75e3909c067c3ead2fd0484b0bb4ef136eb9b5269322178dc5edab309e 57daed3f80bc8096631e27e3f1255f6bf4c94ea796861f8ef79ed0405b6683d872bd905b2b33495e 41da495b3c8febe79c68f7ed59372a7bd3cbb8124f6211c94c2679283f16d7c5d238a77da191b22f 262be867ae31dcd6bfed2164438301da812603c4efaefa4f66996ec1ead7e33dd1b338994ba0f2bf 8750d3dce1455cc05dd2de956b58e1d68276de73dcdce47866bc34b6cbf5dcc211741a5cacd3849b 2ad4785e24d26da5a3e2637e1d2646260cd0565bee3f9311a15f0f61ad478c327ad7e5c347b797f5 5e9dcbf265b5e3fbc36d735b3b6c7d1ff6a735afbdb3addc3b2a3557ed6cf597486ef39f13507c61 3b503c29242836031e946aea13942b6a1e9475b20b20a8ba06d02659f442dce69520713a207ed048 20cc0024648f00a246c90a377d4f0c51de1b4074234a70faf92a0984da2d001d1a9378ffeeada267 6b7e8ceaf48a0cf1b04d066e233c0594fd3a065df286fe1ea018d2fbbf758ee244e7e2fd02e5e9bb f0134ffbf03d008d7b1b005d2d1640166e25f8e60164cf9a00d252a97a014bb0e612485aa23cb401 24b64102b29c007463b4509a46cf31bc89ea62fb1412f91e1db8eb121d74f9479a0fed0753094b71 fc3d4029cf257a598302c52b24809259b64159c89600347af401a4a23b00178b1c8007b20de061bd 08e0c6b193c059fd8486ca35224132d1411fcd8c0d21f462b457cec44838821348bd081bbde6a145 79fbc0bdd878d0b56dc6bf746e8c3ff0d9b3173327c2e39607fc974873a0d26322f592b4eaed9948 7dd785e4c1f7dda4356b498bb06008e09a7200f0712c029879bb00be8ccb09e4018057d1367e949b 748c0a0b39ae760f66f40ca420c230331b359ad94a6839fd74677148ecc95540af4e073fd0ce9417 9be4d59b141757f76b4d2fae487428773e6912bf072855c5a4495f2e9d186822a7bcdbf9005aaf2b 002eb4273f8d76b08e0036f712a8e40a41021c0170f01ec755dbdc45afdde71c3594ac1cdac3da33 24ec5110b677fb5ce055ae4840cb662fe80d2b1b7f38ad9dbcc9a07c4eee5ae0dd7ca62c3a2b34ba 3aa5ea3bdd7c61df4393fc3d40a9a7a4d61a2452cfb80cca361f02e876ae0278319f02d82f9e4065 aade40851a45b18930d5e85d62c6a1ed1fb761a74ea5b7093cee2a073d4f78fae1410f7cb610e4fd 2157ac7aa0ddea79dc83d9b9731c27126d47d6bedff6a20d8f18d97a9429deaa529bcbfb355bd2bf c7cfab88b971484c204ea47a8202a0c62b0670f55107f0839e272a0704a868d13d3ecefa51d42a8d 91b0e3778741ff8e6c7dd643cedea750943c1e479e5ec6400257eab5f36efed4ab3ab7f2b6efac84 7beae2daf05024ad6a87bbbe31f929beecc15779b5ef16ff3c4f8c8b194a34f97b8012fa4c74d672 67509ef8f7c436f31f009f334d50691bcb44e2317db1129b4e418b5a977210049d22ec5f5577e009 b4b175e5114db985d95674d6d8cab4357ee2dbb0d9cf5a87e208b1aadca9ff3e656eab1711d98419 f6d8f303ac6fd7c7e4130a864479576331b369a380bcd251e0e7f84b80126d2552fbc5442a5fd6ff caea85bd6a0b54b8780590867c8e888aa30681e57bfea46694bd1c4df41cf5d35ed9ba92212d73ab 893f69d7b593fe72a09df3eacc0f5f33fc8c8be6a8bbac3f780c1f19d24edeebb7a54ceaa5fbfba2 ed76355eab4cfbd7bb59e9d077b333227f0f501283e38f091cea0c2883fee3efacdef6b00d2a49d7 8a8f40a0c3de4090fdc943b0dd1b84151cb859ed58cfa3b64c9cb9f5e9e97f3e1773dc60a4c72cbb 318c85d5b575151e00bdbc9894b4dd156d699596919698b8d7aa01a69e70e4a23637387f73c6c2f5 46b5e4f38d1aebe4ef014a4622a6247599bf32fa61fa94031562db01c86abc8d881d45f960bb49eb 407985c2f1e5c021f2b54ed947e3453ffa137382501b43e940b8ae46cf8ba61b9cac5542e271af39 a8abe208f6bd392bb6722381dd53ce01bc95a3c69094af8de755beae114e1e37215a1eafbbe40fd2 d4de7f9ffc0ffe925ace2c2f7fa59fc3ae9438aa16dd0588b2d986bd63f7e449e7fac5d15b8387f5 4e4ce375e6d6c8439ae15da334f9ce3543e5d03b56eda5b751f14f9ebb51a6ad2a7efc782983f62b 94af07bf207dee85ba34cbac17a2dc4f1c0af9d8be88b2cc71a2ec1b8ca8642df2f70025f79be86c ef7fdee2fe64f55620ad0090bad28bb1d5781d0c63e8e8aa85dcd93ece73eabb9b1503337bd20b46 b9d16a69879a3851ed2abcbed1fc03532ecbd359e6da2341fa2e3a9a34bbf61d5176a75fb1583d56 84cd5a4dbf7cc1eb6261cfebae44f306dce6786332677863bb257f8fa4358b18286ff1cbdff9a71b af0890a5d18b080d59fad3b1b573f6cd3b61b5b0a79c26277ddb55cbd8743719ede03dabaaed0efb b71e78cc65b05cefe50c5c26a45ce8b0e2aa6c2ac236279bbc31d4031e218322f744ba6daebeba6e ae56b34272f59bc672f510301cb6a852ff415a2de1bfaffce06fa99c70fdc9974c7a7ca60410d9ed 85e78c35f50a736265d767e8f17599a2dc63196a0f1d1956e27b3322a05bffe2b5e54ff63491a46d 792dae5ef2f127dc2d1c93f67a2e05eea90d34ae919db957628066589750ea2c5d692dd9ae2f9f58 7abf6659f7a35d58affca27e0f5086a144a7a5272b10baf4f336aa629512a97edc0bd8c565e8965f 9399d57af676a6a882b3017d4edadd9a38c12d802645655c7a3425b9d8198a6bf4bd14e0cf71cf57 953ace35f81c7b251e5f95a5d5e0750933b9f87239af10268eac349783e188eee132c42a97cb2538 5c2e2cc2d2bfc75f52a1a2c303d8a8ba0029564ba05af9767d5e5df51ca4991fbe2edc6bf158892c aebd729d34c15f751fbaa7c468232fe72a644ddc409f9e50395153be361b6fafb61563ac27ddcf6c ef2d4a97a1c1990c9fbf874c6604ca67f14b8ccff99eb53f4bb04c3319bec630d3d294fa1fa4bfe6 bfaf24dda97e02d0e02300f8dbf300326897e2c6d168793204372d6b2ab59f99c57a6cc0b7c5f16e afcad2edf2c62d59c4c28c543c2e2ac29e763a7cedb31f5f9d5571c5faed676a0217564549067c51 81c9bc26da39afcf5dfab6bf15e8d2add4a5b4f56547af6b1b8abe4911732e542ad4ef914ca1ad44 27561641a535f17e7671ff7c5de18e56dd0d22c16f9ae9c38f92f0ee6a2f21bb53cf5b9353f8d6ec 2929f63d160cae59e6eb37b9757591c19065086b71b92e2e7b66ea0e89f3e25565e9db0756292d80 2daa325c67c987fd6a9135a4978686c8c71721a99daa32945ef3a9dfe36fa9cfbefcd7670f107355 0afbed1ee420ab51f6c9b7d88ffe50f0e69d94e5d56d74a02ef2625effd927bdcbd221ff82cb45ae dd64eaec6558ec5dc60f65caccaed8ee5cac564f7419840c55f16d99ac59f693c02b2540344db28e 3b2b7849349f164ebc45ea421ec73afd4f487fcdbf4f92116a80ff95d15fb9a25e62a368d1fff41e 19cb22ceaf470938aed6dc41c82d229673257b9d5152198b55a136d8ba1c39ffe4d8707fac5e2644 dc61f285cbe45c74e76b2afd42687a9be7d43b13782c09b8db7e9878f798894f8cb5404e832d589c 18ed82e1ddd398c1dd1e43ff1ea04c8cf1bfb28f9102e783ea0ecd39fbfa3acdb331f3a5dc55af2d c7a6da8fb89232ebc5234935a093606a4f997388a175edb3afef857f6e6026efb94d5adb10430a0d 1b73b241867ba2bdbc11786fc2f3a7619fd731eeea05582633aa1cc5b3373f7e7d3addba8871548b 390d47c7f3ef918c50fbc40fd2a544ea42f102765e35cd1981e13a1aad28d5a7695199d1edacb471 88aef05c293bbe25d3dc757086d328c745d06f1153b0bb057a5f30aa544d24fa84d36c4cf1b3e96d 4e2c773b61fc99678fd2f5a61df36ee81fd6fd51f9507a87d3c3ba4e1f8ff967f7725c2033fa9748 c30fe931e9f6140e2a4dfb0e104f79bffac7d5de806e00bd7790d75199ed1e9404d9974068d4418d f32aabe575b8edd11751cddc9962c0ba34da1964496b6355082ac45af8806a8fb06f1d2cb11c6ca7 a3c0b158783087f2f7a5a2fb45c94591020bed4dae37db9b847244f78d1983ead89efe3d0064aa89 ce77e6e66c45e8f828bcf8e5ddce9d363f21bfb4df18dbe6916f5fede735944888e5af607c592c4a a7b326bc44ba0ac817490e9a311ea246e1c497891a26e5e6bde38aafce0e5b19465194cbd3fb57a6 77db3716b4b7b38b30bc23b8fb7c47eca0e3ced29834cab1af832bf57b24369a25e3672d2f993ccd ceb463d0f92bea27412d67c379650165a7a7b2742918d91c03e5cf7ddafc78070a576c8e08b2e774 14c0afdcd43be50ac52c56f80495837e11dba8c973d3fd9b21b6fb662cd2bb6ee679dbfa72d7df32 cb27b2eddbb3f9b6cf0b87edd9299fb7673d4bfe1e3fb3e76841bf894338d3f748e1efa89f3a5697 7ccb192fd98c56dc9c0d64faa2b14654a6dab9668fec0d4f6b9c93a7f869beed08d8ead07c1ce156 c13f54bbdf2cda1806957d7b6ab577b41ccfb661a988fe446e770376c3f9c26333b9d4339bccd01f acbf9ffe76fd755872fdd549fcf708982d757ce49dece027e4272f36cc8cb7caad199b695f26f4e1 bb4acbb4915ea7e311ec4080f0cc436e9ff291333d6a86b33b1c58934493c140d83b4864ecfc86e3 ecfa64f4d9b2feb7b4f9ac90c64628cdc69bec0edfad17fefbb22e5cfac64a1dbe33ab4df9d05dad 757bbd5a53616a693fc75fe2197c0fa3fbfb2e0c940cbe1f0bb54f6ecc72043da20fb3578f600f7b f1247ddf1e56568b858329761b287eeaf6f794309bef06d860b71d93dd34cab111b3b3cb6626e3ca 5a29528f759110dcd5bd6d82d5365b2c2f8d65abb9448bf86cf1249cd302eba4a997fecd5ed48303 9cc0eefe1e9a59a4d2f7370a771d0f04b3110fd9984007f4a3cbb7f16bedd3c0a02a221feac2d2d9 d38de56737f0e7c5cd971b229b9c346cadee1bb8bb828cc67869a093e51279cdf70bac393f2dea0e 7a9e135d2c2d3220de67f4f0fefcabbec0379c06c2a4341de6885682ef7c12df36c4845b3bcaef71 1ba8f5aeb85bb9fd2b735ff5ce70976ce2ecf55a39ec9d7a65dfb5aacc761c65a49f44e599a7afb6 25db5a22a6e62fead60dccdcf3e93beb4654611a5c05783af80ab50937175ae3efedda1d8b6b7594 d617988f94bdbf1d155fd9d3f07eeab043c859280383e2df03b497f90c90b05ffe41729bff9cfc0f a475eedbe7bac555f77ca7c8061ee2fbf2615f11f3dba88434d6ab2c335fa2307e985bf8929c75c3 d665120b457efc95bff2387777d551f1f13486f7839bbe261a18f8dd1a20aee8f4b11ee7f72cf612 f55a9fcbb74bcfd87c275078a87359bd1aed58f37a6d6e5f9ab6becfc6a6256273ecf7e09b32d665 960e99a6f310f47b5f3a68fee6bb89cb68b0d4bf68754e344bc87410bfda6371210d4745e3301d18 a7e9b28f752beb1e31cceeba2e9f3d7693a6c13bc38249b4b98d4ab54494a25bb9d79169ae9afb6b e34e6d8406146cd2c56d1d1d6eb4da93df3f6b8decdeae12cb6380b8f723f83d4081b7e7a050f1b6 a0803470502c070228be4d07948e4c1194944a1b9482db0a94ab73e627a4da730d505e8cc3044a09 9497503bc1669a40de25f8249ed9aacf27386b098007caeb6936d69b6738aeeca5767478de06e1bb c10ec326b9ee074e67d40b48bfd949d1fe411ae8faf7c97f90487dcd40a10376a0b09f10a0b88124 501a643d5032f53228238b1e28775f89549abe82b254341324bfb02c593028cbad6e026a91e09db8 b84ae5fcf36b94b59420cd17bd9582785f1e152273bfae46b527d90df1c67914509df538e8989d91 ef1f2b039fa967bbbf0728685ed2a4b34ca253477f2682a23a5040896906a05c891231143b0465a3 9ab4972b5e019469bd00042b99e46f5f0440d9d92081b04ef0c1635da9b1f17e355512f0afb8021e 41749c158ae15b69d6c25669da0fba95511aebf4996367ee0f8a858917a9d1c81bb7ccfeef010a76 f82fa91f2271ff6bf81d94b3bb3891584340d9d146006acd51008d5d0140db8dfd1317d85a7900ed 5a8dd8c82ec7f15e66b631b23489e859fc70d1912fdfa2fa74f40aadec290c71d929074efc69fa41 d2cdbdb8f09979e3b1b374bfd07beeced0ebd8cd55f1c1ff20cd1dfcef2b89c0cc021428780f8aeb 7b1a4355f5bf72bacb9f450d409bfc144057fa08200b9100140b5e6c10412146fc6233c2eac838aa 5bb35d48244bf8b0251daf81bb10d2ddab015d70de4197cb867e30d9c0fee08cb7ddef783f726722 b574943cb5718aebd9c2be37c7337be3b446bf47d283f2894ea18182e2cd4ad6d5998ff96369b693 3cdff5b901a0776f0ee09a95ac61971b35462dd98fb0c7bd1012aad208dcaf310e7a3d63e307af80 f02f9bdc35f92588ea71f3c1db1b879bc8fdb206ece628abe3142feed486c8e7ca42b371fa6ecd42 cc60f5c606c6fcdd28de272febce8d7e09506cc24b50707a68f2e08b4cd29acdc40e47e534a7db4e 73cfb1258065888c6bc5e52db41f1b37f0d46dfe27f39e9ed73d902cc7bd89d05bbb62d03bb9736c 7e75146da53aab29f6768abe18dbdbfaab6c19bb52ef8d15c2c98b68e7974ffad0d89a17162c1ea0 fc9a3c38e7317c4c28a9ff3f487307ff7d028aa3ea32c1f6903cf871d2bbedb505a0dba890a8cc77 00ec199bf8c0206448652029e8ab88ed7dba20e3654b76d5cdfbb78153ba9f57f6d63b61d6a37662 adea7827bf31914c9fcdbbd1d3c217c146c567af976d3d62db1f185f3f9a1973acb9d26fa5ce425f d9cda9a64d0b8314fd5f22b1cdf60a14593a8df6e169a911d60170f5f0f3d20b7ef47aa03289b7a1 835b273f0a9fbc379bdd9f6ec1a63fb6be9c562c13aef4de27d659bcc8bd8c3ecf2b863287feecfa e0eb33e591218e6f63fe6022fd5630cadafd1b773478814cef68b459deab6f669946395e576aaa9e 36f83045ff9700c5532f911a8a89d4ad9ad6c5d05c00d3521954dadb7ef43e88cb8031c4a3377388 8b731fa2868d1c1bd11ba7f5342ef0f4f54df3f121acfe23bb10e746a185edf5358a129a76c3380d fe10f7fba1cbd9ea0b33be6ac369346eed676fa878176ea19c3177a9f457c154e963df618ac13f21 5d40a5c7a441872b50ea3d3150befb328073204d407ea41fe6bb0c42eaba9d795f76b875eecf3669 3dfdd2ede51a926b72ddc2d7503204a46f368da6b6ef82e1dd5c792bf5dd9150b57994e81b79bfa5 5175c5cf3c0ca5df0f7d398c77b004bc734fe2e5cc4c9a32f3953435b1999465d0618ac12f91f4f8 f91a9450ebf8775ee71c0e40a51542f17182a6cbc160605447ee6ae72decea3dc45ede86e5cd8990 7d1825781368065ecadf8fc1adaab6a6d3ce8d3a76260ab32dafe5e89cc5e4d13bcb48025456a4ec bcf316e5fc352bdc3e7e53588bfd91b0fe7a2b610367d2789ab09954472986bf4422759234a9f1c5 00b4eaa8c9836fa4df64cc4311fe98b4bd6f60f71c9861276ff270da9a3c5b381beb3aa36ae8eb6b a95640a7b51f6ef4b459522ec96a56be7ef58124bca8b9942be37b71d93d92828ae1bc50d68407bf af441167ae6655ee585207dc71345d70e64b4a106a23ee997ffd1a3fb3e76bb54eba13744a6c747a 4f1efc24fd305f150abb2f507315706858af7bbff78cc6b5a5a156aeb8f6f05a926a0be2f3d6d37a 910c6a4e5efaba0c22e56eb3b658a490a4759ed09adf73c19133bd6f1aeee6ea7548bdbeada37f6d c12ecc52c7568f75b3d1fcdaa2168b6b4bd88daf2dfdf46b247d09dd8072bb8503283868a002ad42 8014db50302cf390b3abd4d224f277877e371ef28a1aebfbfaec703f8501773b2b5b43060fe04952 8bc988ab5bb72c6c77719d47074a8f7b12ca9cab4b97fdb575a52996724589ed9e73d685796f0b4c 0c29adcbe04e4d2f97fa677109944eba16488fe35f2291ba4d9a9458920046cefacfeee8f8596c94 fcc97e5db00f8777e1791d2d6163336df5b57a1bdaa99d8f7851465a5595a4b76489ebde28ddc02c ecca5089af1e45846b6cb1ce9538afc62c8d0f379740ece149df9cf30c37ba984c32cb7fcf6275db 3c7fa5d69899b4f525c371d184e1b4ecf89700a53cb6f9993defc7c4f39ff189549c08236bece5bc 7cb2b6b39ad775fc90652baf238ba0736fd1c96a38cead2879060359545ba7a7b0636b215fab3c73 5c23a62bac776837d8de151e5c86742611f1ca636751cfa45546cef3d9c4a015458ce9e207aed3ab b5353ae7fbd4f23c97d4498af12f014a552279fa314303782fa5bbb8e92074c50b704b16ebbe98bd f932a0ed3e4d1fbd5b9b4be3169ce8b9fcc586b8b43c7b92a08b3b93af69917775eaf887f53393f2 257c97ea0cd0bd1e93b9dc66e7b97ddbd32bf376a6b4a277a3b6da3c201fd91792ac89e801b5fd76 977449c5a729c669b584f4f84f00a5f96503a0ba7c06b0fe320052108320087b9efdf0caba3923a1 9bf63c20914aa393aac209aba99cb3ab98085d798e3fde9a3ad7ea382eeb73fbb4cac8e55a830b0c ffb191b314481dfaa69d269426af37141c9f28f2d09664e2c5e45de254e72bc46938ec93b5c43d25 0f686b423edce5af014ab498e83cbe7f1c0ff8e325521777dfe780a43faf45ec646cde18776f37ef 9e021a2124cf1578286a991015aa10c97244b3acb2c152b02e576f1c31825aca9f97dd1b4c6f4aa7 16b5cf2e46e4211ead8993d7c771a7b64b27029c7c7bf6c987d7e504a08b93b632239a6c7142346f c8af014aee730b2039ba247d2993e83c5fdeaff3a0b77b2cabde4e7b8a19f27609862f59eab06966 8a54b2ecae70a8dd369c3d5f9fafbd5d245f380e7f32f35a2b3817053b4343185ba690e9a24ebce3 4a0f77bcc202efd62ac7d3006a5eb1c8e05fd8f5302d61d785ddc12293989d18d29d9c7cfbfb17d2 bdb8ff3ef90f40b9e56e926e5f6141f247f5e6b52b6d66f3e45c4766eb954a6bd3bd92b98786a859 9d8c7090c826e74087c5b53faae117603a02b3c8ec8d7309475cea9133c04f716072572088e3a88a d3db5af774e97da618372e1c8e5fbac31e67e1d93ce6f066fea0b46e9de3cc5a4d8f5fc6986063df fd3540f91a24ebcf7d8d490c349fbab8af1e7e9b1a65a133bdb7accf4c9944f25cd40b0b59a889af 88a3c615e43ac837c7978f503e320b4b6669bd3056a983987b93cd8618109d0a993f855c13c2c0b9 dcc232a7cff898a70bbbc3ead56150adf9131d44b7ee288fc273be896af0767a58ddef9314e35f02 4045649d18e894718b3371fd58342bc3fbdb52870aa85163a9dceb8e797cff66af417964b35c4e4d 17509799e6f498e2f1bda1f70f8aa2eae3ae4838c1f78133e4dd3b8d8b2b80cd32fde27111d5ea87 f5b73244776165bd37470b7a5f13347d5fa3c9fcee5d05ad7ded404ff766c698a418fd076948f5bf af2436badf81ca69473f03633ad02b47b57fbbf43703e986d6873cdeb90fd878076197f940359852 a5f23dc3837d8d7ac1d48c24d4e591e8cdebec2906c10d1319f17554b84b5a65e4703f1ff2a8812f 90fd935ef4f6f5d761b56b393cb975e986bea57b617e4b1756edaddb77a7bb16931ba718fd12009e 0a3bcb94f881719bea5d953a8fd30554f2c0a1be5023ef7d366ef7bae7fb179ed0d597cb534d68ef 929d795026ceef411f1fd1adf5293b8248acd0f90a072d7eeae8c313ed7d62289f7db3c597779d1e dfda9e597bbe09bf0d62c38a777d33ea6fd322a79b51cd6d6e46c5ee68c30e663f18fe12a0d2ec10 66ac449dfbbb3d6e2b9316d2132b0dab7f65b84997297eb436696b871e1e3d57cce9db2c5aa7dcec e736d8f2f96c1eefa3e2ec60cca003fa5ce5ce7b6bfb9577ee2178ecbaefafbf1db820bbe1fa2564 fd55e7a3f51ce2d075ee5117570a53f8ac94c1b1b652ea667fa514df837f421a52fdf78975ec1f7a faaebe69dd0657a8236a2ba3cb75e249875146b726694f0bb59340947bd86a8a4a477860d8872a09 67d1863c44f66d6bd5d99ddbd8741b4a22ba1d15eed486df18fc26e306e96ba2f5a2f7b5d7f9a8f5 59ad376865a919ca60b93b770fcb4adf949795a59a593cee9feae2b12b347f8f473ebb6fa8dd42b1 25ad14a3c3398349fb925b4b695e27d5d87e90d3141d940f8fc303439bedaabcf3fb5b6b1b6db8cf e6436ba54df6f6aaad0b8edd49c6b3fc7805e51b9ba57e9b604ba489320bd3e1a4457d6618732b9f f3e7ad752f3f739b4a7b467dbb9b5957542f3f2155e9644f837e549806f56cf997b837e7715d9e77 9f6dfe9dc53a17e1a535286cebc0a78ce2e6d05a3cc9ec189a2536dfa626ae955ee6b9da96f3fe12 45f29f9f6f7a35b2f939d1fe403397c9d666f4a0d699065c6d30bd4c46b3492c4dd32203a7e3f8ab b2e7b1b8b1e571ee01bf46ca01fd8c56351b19adeaabf1a86817e904ddeb3fe1277d3439cadffdbd 291ca6589b1d57b53af524ded069f2b4b2688d83c086efcf4beba2acef96c8e34acc2d9c6167740f 97a697d1f13e99e4c6c6585ccddee39cb6f446abca2c1cde8fb3cfbf8a0cccd331ad9f78ac48bf1e 6d9b3debca0dff1f5fe7b9adaa122cea67518260ce1173c2009241821214030242ebfb5f98ebdcbd efd963aefbe7eb858335a8595ddd54575717edfad799b6d871bc6db591aed80ccf92d50ccdf23b41 fff357a8796557973aaf77955dc701be9743374396eb6e38ff4239efa7ce6d298f8feb5ea33eec82 5c7fa04ede93fe0cbf2d7bcbd265db2d7adcbeb36b714cfb217042eba5aca4563b3b3f367b68476b c8cbe6a9febd34edfab8dc746b06d57f554db61355f1f714aa90fd6dbe52fc9cd3206479370a88f2 6e9c1dff15524064aadcc2c0f2546ff4ca909bbae1cf8fdbdb7dbcc3229b88e57276009d0e710f77 07d9ceaed9cab7eb315e6e866a586bc42737fdbe67fd6b1b9dfab8a8f66bb3ea6e505d3609a24276 4bc3b22be727a5870a66a506fc5e16d939d81684f593cac7ee9bc7bf4f5fc1c7cdb78119027cc166 bdccf5af481d28b88bb400646283e45f873980c3dc1e20f6f704d0fad10728dd45012a5f9b00b5fa 5380029f01e8a7a7831c663e400ec7418225067275a706720db49f60304dc06f12443cc80d57e64f e2d8d07ac64bdd8f23278ea0f72d1fe4ded58d8e844f84ca86f47991f9ff03c0d342fb7fe4dc8a73 808c880340f18a0950297803f4b1f8f96e141a472d90ab1e168901c02cc8cd6933c1eb09728bd617 e428b990206c801c5d4fbc337a9bdc47db14c81d3029b6861b3bceebe23322e353fcde6774287c6e ae78d8385368e02f1670c0e18374864edbec5f9108584e447d9409005fad05408e64a2346a6a27d2 2111c8758f79909b953b20b7d39620776a0b2077336d90bbe37e02124ae0fc14cfca3dd07682d138 b6686a15af08fb1017321f39bae87d3b2aa39bd7bb96d980f0b0911299283e1f74f005e68b641d49 01fbfd120efd1500e69b9d1f13409bc49f603f8a193c4023e90a72dbe107e474af08726e329c30e8 bd0658899401d68eaf00eb0ca204321aaf24b31c17067127da68d0282a4f2acbf71d99d20974f9bd 3f4756e8e53b69c18c20280f327e541fe45e9f76b7f452de95fc6b28a2d82bdb8b514f7b3ad05f01 60bdd349f4ba1a0184c9ad002a7e2590db0477900b980cc05ae50ac0d6da4f6a2fc6563700731c35 b63778e223df5b71b4ad8fd1f79d25cbefda8d6c85de3eada25e739761f3051d82806dc901dfde5a 7eb4b7c3d78771b3afacf0c0bd09082bcff3d02c3e1759197fd8d325fa585766a9abfed3fe86a4cf 137dc1d74322eaa7b102b972f708b06cf909b0830703ccdbd4018e67c600afefa8b83836d4a8b27f b8effafaf10e1917204188654a417794493d1b3f86aa235f32c6abd707cce8d75111e4d7a8e9da9e 2e14dfcfc5300f3d6cbe9abf5fcd46f95e69544b378a05f8ad3e7ee75ce67482ff0a0007932e409a a7f1cfb8d9d369d224a3036c3579011c2be7003e3f35012eb5a791cb8e776f7a4e2a61bb485e825e 6e19f84476927da9dd79f105d1f3a637bdcc090f9dec17cfc559a49ef8c0921f6bf56d3d8a1de27d bb7ffbe9b8491edcc1af5da659b8c850b3e07c7758ce193d3cc881e47be6af486c73d30388e24f00 fa36f700cbdb2780013100f87e8801fc0dda91fb2d4d423ff1d0534b138b25c9cf2cdfe7173cf13d 2f57b73ecf7c49c71f645faadf5d46eadf2ba13ebbd59fd2c6659b367f152ab676890fd9bba35690 d836f22862a3da286f999b6dc1c2a351ce74ce03d82417b5ef0fd205fb3f17ff2251e8210df6e792 3970031d00a62016c067b737c8c3073c266fa0f56e843722e80f2f8bd76429321e861c8cc766dcb9 ddf7b5727c6be82ff82a66d8444511991e28bb64ea64df1993e4d4364ebb8d9dc364de328faf9395 8707a159fc8ca1f36e2de74f8fd02c9c68c3c89d1a3b1536fc9e9cf92b0032e41239e7d5444ebff5 93dd9d7ecc2eb19c4cf4f361bea8fad16a81f8dc775f9378327eaecbfdfd9d2a7e6437181eed8be2 e30f473b85b17d622cd8b27662c1ca2b8786490687fef9d65516e72aaded4fcffc5b3935b6bd9bc1 9a74ba28d4df8d27a68bd9c43deb056d5c037a03d194693ef35700642526a29e7a538035a65ca2cd 8903f2ed6a1c5f580b0fb970527ae9934fedb966a3febdbeb6d37c81aba4d70f8ece3a477b81d117 ab20b45f66790583f39e8bd0d381ba970ddfb9b68c0e7c1fea6ff1b3d67bdf1aaf29edc949cbecad 501dbf10449de0625185bfa7bc8ae017f4787a59d0bf48035dfffb17801cb4343a354b44a599f420 f73efd26633b8af6a70de27fcc28e7e103a978a7b175f32a0ff19133fd1a5bcbc1c6a259e982d3b9 9ebfdc8d40da8446c7996674091ee5d3b880e2152b5ab658eca89a3198a9486e4b1de7c3e3f18841 cd979207d3ac52a87ef24a61de2f2885fd24a714c415fc5700c43a256fea313d0358b21e067948bc fc04535ee3e8ddb4739fd77433ff3c76a5227c8d63b3e8cc28b66791f3c6e2fce07de6c46638d5e8 521d5b1f8c91a736ea05b1aaaf1c584574a774c464b5a9d80d77a41436e146de98b8289797d24dda 6b41eadc4afb80c7252a1b1424aa856012352a207f0540737e1af291e700afdc25906f691750a893 51105d4fa187272fbe1babf0f1059eb473d6a55d6a98552693ee789cdac47ea7c7f7a6a4a9a3e8ac 1a55dd3d9ad96d70c42bb38c52ccb731793b6954a5bbd4ee49b5d778217aa4cc8acddbd714787c11 0b7c03ca0981c91785e071c484203ec3ff41ba60ffe702a03df07f433ed348fef92267bc791181ff adcc1f0ff7c85cafc4397cda2bec0999fbdeb372e2bae250079b3ea9a95ec4a9b3d9513f2e9bc9ea 8944eb9ebc7dc591f428bd7fbe50569fbf4b626b746b09c11b9e0add5a83e22586d6f9412919049f b78cf0834db7c84bfe1ae7e50c85fc15009d8101c0f0700570219bc8693f2f11550469d99c97518f ac7b63fbd59c13b9bb5ae522fd3937875cd1e813c39e9eb91756ea2cd40fc7fc6aaa28a56ed194ab 7878931ab0198a6c4dc90842718ff3f1ecd0e0078a38e48ec165c38d84c691d59f86cfea9f71faf6 e4200a2a729054c4b9313240ff8ae4450f27a212d01ae02e268302062e6f6677ba78cbf6f6e8f62e 6dc62e60b079f6aaedc888a67d4c1f6ae5b67af2eeb39f38f48ca794d2b52e4acfc1cb10fd927c11 de18e909bdd618f044b58972eaaa53e5207dd86767e861cd98c44d629659d263701d6419736d1418 f318e22cfa8590df908654d316a0b77c22aa5cdf807cb1a980421fd861775538de58c99f5d26356d 63de0b27fdc4e77d3fe9e52ca221a7a071b45bfa5829af061be9197d39d1771c4d788bb4cd8375ef c16766a5888387d9d404d8199f2d31e6a3d063f2f9ce222d7f291c4a68f54ebba6963994aa63fc40 1a067eb8402efa5724d3533591f3d14de41cf51339f7fee9e954e9d595103ba903655b616d76662a aba33128730f4d77ee908a63e7aab29d6d09b916b448315884ac20be8523afd8638bd384fc8d3dd1 ef80b136d70c9397ecc2a1a45e5bf4ee1d4de92a356429faf5bc520d6ef6a19ec36a5a3c8bae5a3b 9cde5755f4af4866a85e1a45ebae41fed817dfcd87c7baa13aec39f3acddb1caf29930e20c2268fa b7e3aaf87ef551dc165592a9e762208628b4f88943b306cd1fe73b999b8425839d3fc0955969272f f1993940df5e124e3d6f7c836a62e7f19e23b1c3be53d2ecddfb5c05fb4ed9cfedfd609ca79a433a f757fcb8ea6aaf9b0cfb6922aad7e79fc58cd5bba896ddb42e8f75f3dc42ea6d7d74920eeab2f8b1 15f7588b647a36c84bed6cbd23c87577c28f6ec28e9b1ec7028b57332a53ccdfec4339129f74edb5 89a9e66d9a5adabe638f2a3ba9c810dbcfecbedb1eb1f9693b3c8268fb595c72bbfea38aefa44a0f fd2b92e989e800fcca90d13d1b51b7168b356dab3bad9dbd095a33068c910653d4e5a8be55760a65 c88deddd17df430811e428acf363d61c71398c2419c7a832079706479a2e6b26c5e2fbdb5e8027e1 4efa0ca0edb7352a6da1eaa2b7991ecee4066d0d75f2ecfae1664a6ab98deee75213d8428d2afa57 006cb0eb827c5327bde40ddebd8c0b8b8a79bb401523f28c8a865aed92b27ba87389eba2b2d8cf2f eec2b0636678f86b56584b64fbcca645fc2491579fb93dddd09e22d596346307daabcb2e53ebbfb6 e375ffbb99ed460572b95a74c87ce1be5e3bee4a5b9373385c3ba282927904c6c9e5a484fe1500bb 9b3f1b9279c5dbdc18e653b79766b17c0ae666598776cbb272439e05a9d3c90f84a1cab23cb20f6d 0e535b6fb67023f1c37d4d34696fd618537c0126f712e273bba12a2adb89b831377366f2202d7712 af2fcf459a06b72e378fedd57e1b2d57b52aa72e9f763558553f16b2da0f72d8ea2655d1bf02e05b 7cf48263ab7d1905dd92592bb925fd5ba4cb4727c897248e3ce479bdd54cd79eacfd041b66eb2ef5 03051c8f7ead4b1015eeaa95bd5cc8f4766a19ccb7067edb6fcc9225920e261a6b17da5c568f6455 b6aaaf8ee892d1ef8d4558ed2f17fc13a80bfe28040b9ecd218bc069e752a473da4ffb1b928ee797 378ff1aad6050f8b4674644baa796894e4c3e05214865e1367dc3c83d02c5d1e51c2e5c4ede5386f ef33cbdd7b07731aba9d45b7fa66f9bd103fc581616bb9bef54ff4ea39d2e4a53f53cc65dbb29e0b b11065e760d7acce89a7324bd6303b6d9e19c5de3cd31e67e604c92229a0bfc2cb5dfa35c7e8a2e9 2eeeb939978b9a51254aca1e0125e1339fe4d9b5aca354f842a11d6cc5858d159e979b82383f92db 9aff58dfd5c6774d915c71e5dd4eed55abff9c2ebba3db6621194f76fecd016d3e5ae7ae33ddedc5 33b4a7a4255ba7e74f69385dd8b1345db0fc73ba18079fe9a283c2d345e99bfd2b6e2de85c31f7b0 9cd78fc6b87824afa5b218b5c52297785639baf5f966b6a7e608ac29629c66a6ac5eab23b90c1317 61d9abaeae0b62c8057355b7b3f3f10e29cce1deb03533c07e34cb0db9e5d42a3c99697e0b8e1312 34ae9392c2c6e3db3a5f1eef8bc66c5c7df3fab82a65bd7175d104ff22ad2ff0bf7ff9818d7f4ddc 905ab382ba5cb5cb92ffbd94b8b3fec168b60e2552165ad1ba46cac1024cbebd45c6a47673247357 6658256b4f0b04f6986cb4da7b7c2f4fa1f13e92d2be1979836375c4e0c9caba79294c860137df0e 3bd18127a2f3db24247a16128346b270fd66e9d1e06874c5c151b02f83e31c09fe8a13672ff33f53 917c583cd371c34f8b0876a89f2bd9ad11242ed6f3463ce7a3e9f331c39c7038b99e8b9b31a576f9 11735be843feb3b609b9b8bd0fbe3b2618a8bd7332792a0edc9f155ef99eb90d2bbd65156f779d43 6bd4259bdb75c7e5cfdc4fc9d65ee1dca9002ee8543eb57cfb71bc0cdb0f7546ff159a916b1795ea fd5d12869f7efe4045f5ec0e2e141311c5ea7da62f579749b9908b466cabd12662a59ac60506eab4 b4ea1bcbd2be676ef26cd7a160b15b7cc1c7ceae0d1b6dbaf736ff6f9181f7bd294c81df882d1834 884219a98f2bcd52cda0579ddaac7199d766cd1e5b4303f55443c3a2f72fd2fa02fffb976389cf97 7f060fb3d5a6d06e3cac2452eef3b799dee8db232adc1ac48055a2be313f667bb82b153aee81a9b5 e9eea2d5628969bf294cbac386bca84cea5fa738af43ee77f9f398e7635d5d361fbb8ac33f0e6557 36b97235e32aa506e29e8a6dfc712d081b28c8c7f746362f5364214fd48de65f2146a543812d3628 784fe446ef7555cca569d7333d533647f5594bed8f0764a2af07736fd31dc8698647f7d620724650 87ae6c5c431fe4b78afb13a8427626b972e58be54a0d28c08aeccc2b1484955ecccb5bbe82abf4ba 8619dcb49533a5511f25c7f5349d07a962f81c6e14f10dc4eeea4c367c76a5bf0264bf8322c89283 1a8006f72e80cce618c03bf800e0d03801a4d47d01a4ed242bc34eab0690c979049069213d4d0490 ed4148e0277709ed5b023a4c70cb02e4542d2458d613187d809cd10540ee3c179fa9ab1a634e6047 859b777b6ffbd77b7867a8eb1f50c591f31fa43950ff5c00089f9640d659d70144055d00a3934922 658f03c828b60042ae0380b0310a106ddd00c8259a00e43adc0124b8c80009f32648a6da67022706 68299f38e6a5592981dcfc097495e22140cb8375bc90782eb2bfb61e15db77e75d99078f90b2ed47 f082b66ec018d36bc008834bd0ead59ddf00a0fa2291f34b25723e901e80792df9fb47b4f8a334a7 e200e47b8e009aaf63006d9cdb006d9667001d8a3440d73935c1cc0128e304006573990404169bed 5d255ec24627c64fe1387216c57544e294f0de952423a46b9213b02de9e18702f5f3611aa1b778f8 dd67cb7dc587e2f5af48fa7c5b06d0426f00b852ee01a400d600f15c05a083b50b50ea03002a6f0b 00353f5d803e374b8002c001f443fc7c02c9b405375eaedd30725c3f1391fb1c1695fabdcadbfd90 9df7eea88edfd5d16b1d3ee18118f8d8f4e40beee0f202d585f7220ea397a706c5a737dec4370f2e 3fdcdf90d6b64bdb444a2a11f57c6f02589f0f006213bb44c0a206d0e8fa04b9ca2003725dbb0472 b34e1fe47617325e7e72427419358ca8e4cedcf7beba0bc2e781fd8687a6950b1b815f0e7c21df09 daa03a4c95f656966b5f1cbea497328d13bf371fbacfd3e6fb7ae6ee71f0b0a8c7eb917fc98ffb85 a56ff74d7be9fe86c436f94aa2cd34b1bb701800b42fd020716e4e20b7457e0ec9e6ae120c722156 4db0194497a7b27aeff5131b1ecc8b1a0479df093a1be0f9e22307fc3edd445e8adf2fbe86dcbef5 ca76b4a137a12e2b0f69548527d621ce8fc26076bf97c7b3d7ad36eff96e93815313b872c1cdbd44 5dd1b988f1defe0d8936f56aa2cd66fb4f0e3a7a7b3020a79d2d80158601c056510e60ccbc1195ad 662f64a6fd79c02f27b43f28a7257eeac374ffc6d3c9c9dd436eab28f142d7d013b3f9c2630d49f5 47c1b813f72d965dddeeeb26e332d5897189b665dbf9c89dbb33ca4f3c5bdf946ff6b48c5cacf3fe 6ea5307f43aa34e86e27d34f61928c967e7506729bba00b0c6272d7ecdbd7f4a8d44951e52095b1b afe5cb78347941fbfbd643194f7838555d7f145767f75e994afeadbee43f2ea309e8953fb3a58be4 b0cdcb606ca626e01c4ff1c6d69d966c9d4f5dd3c26bd4dd5c1ff5d7d985b6b7f3d65d5fcf95ddc8 4e61fd060097bd44cec3a19368935c829c3f1513110bcecf57f922b719a5abb5b09d39e65fdfd7b4 e2e56ad3dec309a7cb7bb559a45d967ac997f8c61aced7262fce185a7ab6d15bc636aaec516b11ec 8b16ce5b1d731d83d9d96d56e97385a78d535d565d83cd879ed1f2c3871e6aaf74dce8c2c4b15358 bf01c09b4f22ea536c835c5b5d038c60a444cac915e0a74c1cbe1e9b8f4f4875f869f5c2fcfdb67d 345caec38e2e4abbb172e0c39bb673db9b9cd6b95d26da32494ebc261ae65ee76a5df99e68f2983b 354a5e2d59597ffa461b9f6cf41e2cca9a3cf71d8da8f79eeaf73b7baaaa3376d5314b3829ecdf00 e0a05cff99a1aba74454f54102ccb415800bfb6be43e353fe85db0c0cbb9d4fbbee7c7d055bc67f2 8e16deda36d61427e6c51c92e75b9a69f6940aaae1af03d3681bde4d17c03dd07b4728abc9215ad0 32bd565a6846559fd44c859b1fe138db35ad63aea7dc15abfaf614ab9f78924be03bca2a077e4562 a0b51a4077cf0ec0f2e8f6cf81733ce42eef065f735f933ee53ecacbeffd270e5da243675adc23d6 ba39ac9a65b33c3879e3cfcae0bce35e175d4ad0005868dab03975546d3b7aaab0398a8ff3cc0c55 ac095b5396913b54f27297924b1ca34bb769d6956efcfe25dd74ee21edb787cb4f48757b707e0340 56c33a40dfb95ed2f1c5ed9f2f5de65b8a13bccf77f3696743dd0d24e2ec4c87e061ad0f99eff91e 45c553abadb7f5c85e4fb48f31da68d9039e9e6157914d2c1fe7e7f0ac582fd7550a25c797377b3f 2b97f164fdb827899e548355523c2ceabad8c425576c12b54008f2e58710349ad71497df0010655e 4f0cb43df8d98a90077b801b98129784f9d91f925df9517e8cb9cbd15064cb410f97f363a244a796 c363ba8c4d1bda482812ea74192e8e8b894329eb3acdcb5772a7c9e5139968e2b37c8a5e8b046273 77c084a0fa4c4d40e8ac4b0b5e746599efef718797e0eb8b974ad193973ad95b8aeb6f48c6d22611 9524d228da6807f2504d89aaa0a0be909d7bb831a4b2b2cd6693372b7ed3fa8943e31d5f8f6f0358 1b17f18a8aca8fde11dfc853a5b8d86de54abb7d90a87d431199357c120203be099dc4dfe1a56601 e6fbc9629a3b56b409377c1222ab370d9bcbdaeb1797f5a42737ea5f6f29d259e0a7fd0f009a3b24 a2aae33ec02bdb7dd2f13529f47deaf0b84e4ec3cbb779272cb2131c4eaf7760e8a0183d34d5cb7e d55cc52d1cf319b52dbbd272243dc8c64a7ccde1f484a4d83af882d0dd5a1a2f992787fb662c9f1b 75ee595697910a3b253663e6ccf92cb3e82e2de66c143de6ec8e9e2cdaa26e2c3ae27f05407b5c0d e4bebb4162a0eb1faf33bf2e702fa49a1f5da3275373a6f1a0797e7cb75b43f848aaa6558fae3a0b cef1314fcbb87c2b930d89564b0391ddbd6782b0b2b67ccc8b0cf7a5370a37baac4cd640560f165d 705f66a19e8b070741524b3baccf224dbb19f87c282ecdb45062d13b38bdc1ede0cca6bf2219f65c 056075810038bf25dfcfe588bc7923bb7281ba68d172d4b07ae2e0ca4a1f4203493d1db6979f3874 9709e59bb644a527d5ab8a1c9ce9090278ce122315494e355787e4a789cca251f3c42cebcde45964 3ff1dc4e6b9cde617697ae38fd2d45c3964e3d36bb1b5df1438fde0df15b0af7076948f59f8b1f00 d4d51251297308f080275fdaf6ddbd4aab7ade36076edeaccec9bcd1cf3d661a9283f8e38aaf984a 79397a491ed286444ecd950471e777f861961b71e378be644f8d616a028c55ae89870b9ed50fa531 923c4bc4de14cd1451aad162da7b56f8acf7ed1aa7ed5989b8ed7dc4f1a8c6e475a31a89fff71b40 0e3a9501767687205f90d6f73d734ca31c17a8fcc2ac12bc444f5c114634edba1c1e57178156cad2 cb90bc0dfa10f97a2e2348a54c911f2a5a839bf007829d2f870b663529ef0e9bfe87a3f7adbb4a3d 777767efafecd7be7dfdc23b31d7aaef7a969d0688b760bdd1b68084af3bb12ebf52dc766fca747f 03c8b5ac12c0917018b2a765dfe57383bcbd78b1e899f6aa880e620b523119ee2ae588d84a0c491f c56edf48f36c84c1c88df82cd0721c121caa2cf698f70ed709363994a52c49d798fb816aee7479df 59f1e75df28ef5b68a79fe6e87ab4a6da35df9d966326bca1bcd785fb7d912e96d87247b4fe1fe8b f4aff9e702e48eeff24fc0ef71995f8b9751e79133dd138118e2f40669c8669e512aa57d4d6a31fe 5288e70d91575b4b9b337a6cc09acd3dc4ac1ffd6262bfe5164d9db3238a39de977b5ed05313d849 0746d87e02feb4cd26abdbcd34af7fc8f3e65b22b1c77abcb6570591c4ac8b432eb0de933ccf6737 f2bc27dddf908ca5763d31d0e9ecf60cd789369d1d72f247701a1dd4b384021d8b78fe2bb5ac0813 88ea6accc3d90bc39acf4ce2080d8907539a8ebff43353c3a95784d4f7a117f477b17b9e6ebf16bf d942578adbcc504a239799ed75ed4c8ed1ba78fe1656ee9219ad76d9669ad4b7da0696bd725bede7 bac88c6f29dcdf90d8a8d079a9e6bc7a394e5cd4dced7b88d1ef3e61158b2659b96edc3e42064d8c 2ec7055de6424f7687fd8453e8e7f999ce0294ef67c2fdfb16c13be079a55dc63d37b793aa3ddacc 0bd28a5ca15b6a7d59f2caea36a1ac55f57c0e96873c8e2f1b9723b1f0a7137ee1777d7bd910c68f 6563d7bb2e6977f65fa407ffd316e09d98b8df18e67f14cab5728836cd28b05251cb590114f680cd dff8d7a146d62b54303fcef6119b65f7037872da1df7ca63ab331ad822a18d6db0e7b14aae9b7a6f bded4ad3d59d96d212134b8fe3f965333a9f167c3df0e6913640e7d224e8cffbb1c2cdfbcfaa35ef 5bcbdb5c04e435c5e53704dd40a95d3edc0335abfd36a22bf1133926530022b567515a5a9f3bd1b5 98f646a3d7ee9bad7cb606fae86ccce180dce0575d22c94a649145d07fad77c3f977f5b832f955a3 c2d7972c7d1c2c840d339fc7f7233527eaf7e34ce53077369e48d00c463bdda9a1c0ecd438d0e9c2 636aacccebd418aaced4680af66f78946c3e6f39ed316c885b1f56179c86ca8d7107e1359ecf1cee fd63b81f2cc327498e1bb7d5b3ef2792384b62c98dc3fde27daea43ef442a41867d11f78fe5cb920 dff970d3c2675ab468cc26124f4c4fdff37a8a911f7e629727f6a420ddc0f8bae45ae36b734c8daf 59531d5f42c81e6fb60f73bc993ae7df904e9d23c4f83388b469e3822a557c850acaea0e31d70902 f69205791bfcd573975c41bbccf406f79ca105a53e3d878df9149fb1ccc4a9c6fa64fde9ba93e271 1b24f6ef64c7bbe2373faebcbb8dd1e3c4ff7c6cab81d99be1eb5596862c2bbac3f6a48f106fb43a 21c2872b10a1553913c2103aa538ff0673376fc0faf0e1a3c762a461a2382fa3eccaaf7da8eeace5 ff88985a1a973d58b389869c27e4b6868e77a6d81e3d87f062e86fabd4b01dcf244224487d002ef2 65a070afd760d845c1208b12687fb25c96fa48c3e9f6ced9caa237d719b687edca4e0f1322a86bf7 2fc3ae3dd8a50930dd952aabdd15cb1e7f83d19fbe91a333b130896bcf316efea0b25408adc3cdb2 3ebb2dd960634e4f375d1b5142571d723e8613fd68d21e8cf0c3a43f6d2b64ef7cbaa4a7bebbb677 13bb6b06681d17ce599dca3cefb629aff16abd5abdb8d532f66853a85ad566d7cb138df8c86e1b32 59d41a4449f71b44795aa87f1fe57982c9e637a80bcd4a3724e586bac178647a846886e1df1b73b1 fc1f29e773ea38f41e5f79301c6eed3e6a4b48d7d9e995ceae796cb7e9ae3468b1043f69869ab46c c4678a6c1038b5afab24c5d48cfd82afa1def6585db698f39ffa02ccb5ec2ae7a05ccd7ee1123deb 56135cc7a506d6a1133ccfc5970d2ebf41a92e145cc83603e450c7ced1761ab1f7257b9d9fa7f3fe 5a19b6f286d0335a20555a677b41ed763dc26f4d610c850d799ecbd4c7051faecd2a37ac6ad2b762 c5e1b472d995b85ae9a1ae1ac5d769d82e8476b357e8153ba33c516dcef171b3b3c566dd21975b12 2b0d7534e58a9213f0465cb399ee7824ed24ff1b840f56ce31e5f707ec20ce79ae1ac383f9232511 5c04be6bf6947dab21239b863ccba975e87210ab2635942b64bb63942b9f82556a40d94bb18d0669 89897c7c916ef8f7be7de0d06bf0c4d077e595c33f6888545139841bc9e3b2e17d03656476826588 b6584b000619a2432c13184c0297ff0d20792b422023a83f7bd2d9365a0459765a07501e990068a9 d000521a7a02f90ea023fe4db02b2478b501a4b6925b1c699b20e00074a9a900f2d3e8917f790228 80a30443388152025098ef272053072a9ef5642a5a0e65feede8b2fc2ea14b31744d420877cb061f ee8812f71b40c6956190095d2c11b19e0652f92680e4c90240f19b05706e79027035f4005c1b677f b2546b4e19c0f54a2f013507f020a0004cd4c4041b03c0cbcb15c0ab9c9f80f824107309ded5f854 6c0fe29c40afa2fc81a5df259e4d048b5925781263293864da62d0d04ac92294cba507ca7edaff20 91d248b439fae28994d33280282f59a155943580855ef27cc3b1017caa0709e444ce7bb19e801a26 f0925b1e3506c0f1e198e0660118e4d2b8407cb27a613c5f6d32315650b0c8ba3eabd14aa8f6df1b 65b60af73a4907077327fa419e56fdce865012efa624bdc4332cfe15200b99899c2a8e0368c154fe a4f696fb0076333b8014590520633c59bf8ea908201b084fb06e25b84f00c27776f1b9bbe163cc14 d5689dd7cca870b9b9ef2b9f0bdedb6ee3f32ec7ab5c7897954a482d412f60b68d95cfd3bdb4c8e9 4be208d1fb4433cd1b0daa472ffbcac84f9df1a5e7b475117f03c8161d044078370fa0bb5e01f095 1bffa9df8178b10e90efe20e50dcfb00343f2800b4714d4facc478c79b44c56576f3de5e0a4cf8e8 74e4908a56a7e0b5385c02163fbd8296e3033fdc6088df8dfae5572c71bdd7e0fb5e7bd0c9a79fa8 fd161fcb12acdf9d99a5dc8bd641baed0a43e156b936b91fa407cafeb9f801c832310a20695b00f0 24a80264e5cf005a33388052dd3340cfb60750b39289978d75312a46eb66f870a861f07a4bcba03d bf52be6007e20b143ffa8bd867ed57a6df4923b7def8388c3c784441cf1364979eb9d9a7f358dac3 d5bdb41bd2eeb3d911ae6c5c562ec2d7131d303e728e0233ac438853fa3724b6594c6c1e39160192 ed34018a35963ff1b43b24825c99b1406e8af8f1f231c8be77119a0b7c12adfa62bfd87b0d8bc5a9 37e9b5b6cf9332609fb960a03c56dde9f97e91d7eebd9ca1fcdb7e227ddde7d9c1dd261ed7af9d72 6bea80576be364de3fdf29b4116c2a59f3754bb4b062913357d3076d165093fa0d89790e30006fec 024014ae03d0134d82dca42581dce5eec496defcf90a611c744642f63554d8c2f35ca1d3c3170f9b 5047f72dc22e6ffbef6eef364f63eedab17bca452a8ecfce67465c9ca1b5f46dbd407fedc9f556b0 ced57cc7b49bb3d5f9daddd1e7b2ac8a272a2f8ba7da85e60c6fb3640ca64c50ff223df8ffcf0580 ce8b44543f48acb0faec835cdbd9829cbf960136cc3aeffbe2fef0a3e3c0f3f4031a3d6c3383dcee ef67f11a16b5d665b09a0f9cd1763eb77573b2b5ce973e632d2673c5b4cfabf4ed6916f1bd7bde92 6278aec077f8442db0aae139fba91e9cc49dce53b0a845df9ca84947c06983f59b51bfc51bfd1b00 5ce693013dc48a89364b439053e3b40eea31ad731b177ccf0c78b1627a7a6c388f22357bb86cad1a 5d08f403d9c6e65eb296f573cb74c8f3f0eccea5c5b972e477271a9238e3d5e315a3a51c4d3d1c7a 4fbdcb47408b7b83823650f4817a0c3ea40acd86e9b8391a55513e4e01271c5165c328e67042ff86 a4e38f58d2f1e502c8958811c0f2c53dc0ccb3fca6ce8be34b6dcaca83ecc0c76b0838d319d39387 652193d8249d519a0073aece2bd55332d67a467bfa1deb821cafb4d80f298d28bf78f5fb400d759c 472f2a7419bd8fb3328b1ed1fbb7a52cf3d395ec6c744626bb9822975044904b950227b98fece13f 4843aa690be0cb150768ae9f889af8f58942ebbb9f3cd4206462ee699e01e5fac5cec119df3dc5ca dfeece793ff2fc5333f7808c4ed629eabd586d6a844f0f54adbc9babf062b94916223346b16032ed 1b25df274dd9f1e4a75c6a3dbe92fbee55a56adf998b348bd222ed1c65b1c1ea620a4e6ca836f31b 0092b96300dded4b20e7b0e39fc0645ca429c6277ae33425fe7e2bd526978cb65e592b722b9c6ba3 e9c908aad4431783ed471bd2444e9d6c1ad5e37c91eb28969a192af9102ce54df5bd976ec9442fed a18c2e3e6725576c58e358f087a7a2d0361a63fedd13f6bc58aba407ca78b13f957871bee653b0bf 01204494886a886580e1c2f027bdeffde0a0b567f8879ecb35470d1babc593f3fe19d3065f45357d 90ad5cb5ac8cbf8fe7f61bfa290e5cbb1494c2da6ac8e599d897f6ca61223e833d29362b9b83c0d1 0745e8e44f0e2f6ede2107cc728e5396a73e97b956775c2670442e0ba112972d168514ec6ff8d9c5 a50304e420ab02b08591883a1dad7ca2a2b56f0d0bc52e99ef0e33af2363786a658d9d3e6898b2aa 9f1deb78761e2f653d57bff276c46112559d54446fd56f8b4d3d99d0b8b8b6e023a243f37dae932a 8d53eefd3397adef3d567b05088b74fa6d664e5924738a770273461f3273ae84420af63700c48c12 bf62f4ae014c7286efc79825eed519045f146a9cb1e7a71cf4f339c45cafab4b26b1d6a00e291c17 ceeea4ac6fec43aeac6791549ff51191513a2581e7b1061f3d9001f771bc293782ef1b2e2b461c3b f96227e6dcaedd194cdc660f76ffd33814d8cdea50d0f2dc619d97959f906ac3105370bf0124fe49 222a9b4944bdbbc4eb2b374b2e9757330ed2fa02f3ba9281c15f9e0d6d74c9cc8fe6a0c4286489d0 e58ad7bb4a75b71d8a2dab90ba834257fbe4f98174af71c797dde5a0923e66a7f3e39a598c18fa60 1bc7e3a100ae57fa7aac7de80ac457a9bbd85c52f7d393a52be5c4154e163c620aee3724933d06ff ece2be4a75809743e25182702851680b5876cf789fa92d11ea83fba1a2a2376dac90a32725576b19 456ab4f28e1026af0c3ef6df5fee7b3ee7b8d1f75866a7ef6d93316b0b82c199f5f2b076d714bdb5 a6a909508f02ef50941e44fb576e50da3376769e4064f6cc2b2fef5f839e9882fb0d009d15a0642c 35eaef07dc68dd9ee82b9b68338ccced8a7d1b21917969d0ab99e64029ce6745c8d59db815fde649 14de53dfe401f17cf2998e1573705d42d85c695d6496f369e3408e5a7dda15ea33ba025a3baa1ed4 c53ddb9d5abb90e6839dd0c40bbb6e709d6e636178d8f520a0ec84712ecd7e485bf63724c3be0327 6369d67ca9f8277f153b8b8f65cf9ce8f4aab4039d80b5e77169dc51790f15baa27fe8af0471bae1 786529ea9cb6516eec697108196bbcc8fe1407ee77f14389a9d7e8aa8077a9c4bd9becd94b4ceede c897dbf59695d356beacfd2db189f18d6a52e38d6a95e96da67a91b7c4ee2da6e07e4332e637b99f 685fea7290ad2274c9e215f047a1bd4cc6570d6ff5906fabc957f41fe786d09f44333edb28d01c32 191ed9f979736156eadc3f6cc4ce87de33c51cf574d1f2de7fc4edddfbe20e777dc45d6d9545c86c b471985651dcc0e7fa833c2d0c949ce7d74332a7053b729edd8be46965891bd87af2bf01e4d65621 baaa44eb469b4e32d8c3303ebda6b3b79e293d7de5b22bdfc500b9bef8cf66504e271b7d208dd845 e9be63d6b58f78b8aa4d93be8b853be51db1681ff021b4ef3c5e855ddf771bdb63d9ec6f745c9e6d 10d2a4c8f9e9a1ac6dbc785b1748165a5dcb83c1ea8a04bb7561ba171370c27aa56ba92ff0d3fe07 20f7cd573dfd4d211770ee2683fd69c6468f68bcd539a6f9726df9b9f31f7570e550ec8131eb53b1 7bd87aeb154d85124bbd669eb60f57de65dfd53d7f3730afe911ac6d3271601b0331aa1b74a07649 bccd4ed66b51ddaeae9f87b4aab4d1cb9212b7df657dd0ea2eebf5fb6649d11b31c1815fde1f0af7 1b7e0cf467b8dbe7629cd6b339bdb664a4691df8ad6cdc8d2f76cbf69d3d03d73cecaa950fdd28c8 f57d183fc6fbdeaabedfc9d785b4cb7425733b96f5c766a6496fd28c0488cc0fe4c2bad4a61bab9d 2810cb8727af968dd6835fb062f1e7a87cf861e2b9a08eda7381fd90736133e7e6e185e3e7a1b667 7e43c80cf7a5eb7bb3fefc6853078d7674b43eb7b7e43d27013f7a31f743b5429ef72242dc76431c fca45d4f3ae3de66be1096a4e5b80772c555d4f525eadaebf260f45cedd569bcf460125d36e7bbf2 82b385ee3c82d4d9bc3f7bd033c5ae18b32163bf67d9b6d49a6591da7a9689446e96b9736c8ac30f d234b87f2e7ef0c48f75c84148149cd8e92ed610241f2b95e2251662be1cb0f97eeb46753284b999 0b932359b04b605d81c4ca8adac3a3a5e7b5c9656bb4e31621646a0bfe065de6710d4ff36ce692d7 fdcebead31361b19747d6ae454623ab5be9b89591c1d270b37784df0bd5d9be08bd57282d72fcc04 c74f87b1ed88d46ff8791b9995951aebc30c018e85efcf897c31540e313773ae3e7dd8bedcad8ef9 a7d53d409445f7159973a273ccce54b3509fc18dd5686af8d6663ad373fcc442abea64f9209d499e 969fe30b1182f1265bc5c7a52bdd1aed2bce6c547db6b8e15309d258e7f030ba968687d264343cc0 d67648bf2ffb61632f6e7f838db9313084070954ecdcf8ca75d7fff263b3f03eec99ee73378cdaf6 7aab0cb579ac52e2c46aa1e2e8f61ca55b11a3da2acc0c19685a1d36436f440462614df0f3154b74 6b2775107d33ce40521bde6040d2dfc180834bfd6f67d6ebabc88bec8f1e8cde870e7dd087d69d7e 1f6a89ab3e54b4763ddd55fe1f248ff9f7e2f4d205a02113e2fb670382b8ee3fcc463c06bb8f465f d76e6e6f2c7ab82e4fac66cc0d0f1a410f06f7d0ecabb4f4edc3996fa937dbb63a5d335e8cbb4b59 5a77f3cb80e95c0a581a55ef90c1c46cdfba9b5b7b8fdee276f5d9c45b4f45ebb40ea306d96a165f 7aabd97e659bfe5d279a3e8d6c9afee243fe063d9bdf6714b7de87c49e6267d842db4b1c60c24cc3 766471269efe48b90c1e2c1120f97df2c769bbae75c16f9d2bb2cbb4ef7539dfaea98f468b297dfb cd804227cdceb7b26a48e3faaefeb98db8faa8b63ed6f4a364d5d0a2e75517fb4a620e7da95a71f0 667a68a9e23460a5b2f6d557a5a8b3b54a5180c6bfe1b8962690d4de67210ecb1762aa13c7899941 3773d1abdbca24cf9b0cc19d33bb1e9a9b93c96d07bad53aaa9766f70da5735a83c0f2707d5ccce7 6bc6ae54a99a875ca3e270a05d2946a05fae7cfd51891e8345919dbf3785d0c9b1f9f85655f1af47 5c709531637cdc6e14133c4638144d0f093ee67f903ce6a7fdb34d82a0ab2fdd0cbbafcd4285ed85 90bc717ea41c44b5e7b68b37cbab26db1e4deadfcc84ad199b8d585dd6e75aa5f89e3be5ca67782b d1a3c9abd8ce15fc42af907de789b2970851b7d31701366b1b706ed95771c4359645985ecd1a10bb 2507d95e835e6662ffc56664be7bca105de999217a68e637fcd9c9a9d954b04507edcb22bc7fd49f be1e889e95a62574d65cb46808737c549dc5f4a0520cf97d891ea2eb621b5117f9d8a149fc7b6313 6578249733852a8792c49b871f678e8758722864857d53ce102d444d30b413285e866867be09e685 04b7d4e5c8109df63c81402588e5df107ff3c22bfecae11b64aaad0fc8f49669e037ae80cc9b1982 2c82ed40265a49099e36c83c1a7e0231b9e5f12e820ce8a587cb93561825782c41e683ef138cf804 bc0ab25860832cdef412705f90cd23a53879ffb5a3d379387a5bce70f12e94cac9bbe986ccc2cd2e 9a86e5ea7df21bd2f0c397300390c915e34440320332869cc6a6474d9025bd29c872133ac125792a df7041764bbf13bc5190ddf5aa09d82ec80a61729f58df245833097439417402d97327f91f672ead 2f10eb8b6b36462e58395a209dee7b8df7c761f9565b0489e3bf0aa87e6119d43ed985ef1deeb3df 9068f31582cc6e00401692b38974681e64bf760f408dfe0a4084f5531c181a564f006ad28f04e107 402d229f406d26f80e01d4ee2e0134e6a9d880cf7c3c3d79c7185d64cf91596fbad1e2b509239cd5 b3eff5fb530eb7df4237a0e1dac467b1e6fa15aeb1f54b287cd203ccafeed55b7af1d65efc86449b 68d2f1e1f603b26b27fb27651a32cb4300a3d60ec0b9929480b5137c03007d0828815106d017edc6 33b23e8ecc5b378d0e46f9fe62ff763e0cf7268fbaf22e8deea770670497b0baaff9c1b3b6ce04b4 e755fcb610743d30f4c61ee162e453ab7c364f78a9af1ea73cbf7ccc7c26c165bbf80f920e1fc43f 5b11232911f5f34de4b85feb00de1ea600764b69fab9a025f8ba00be8da2f8b497d1c8628cf2fb62 24abbd9be51261adf09a05cfb0b80d9abdcac1f7c140f43bab85f68a0abcfd12afd7a7f7e921e94b da53c0b6f49ce84ce73137a5f17d7539ae6e9bfb76eddeebdde5d56bc5b36b533027bf21e9f36dd2 f1defd0b20a98b0098afb501d27c2f01b25e264b6ce66500441d3d52a5d9f3c1fbbd2d1250587bf6 f2412b3babfb5d74d67d49343bf13ebeb0f4461373ffd4cf0efb9c2e42f971f6f1f363c10d6e773b dabeefeb738cbb8fd2b7796d318ffea52bbca6ce4031e7b69aa5a736d46da7dbab961e978696fec8 11ff41d2e71200d022fb057047c21329f73d80788d35406b8e00d041fd1cad4fe835a4aeeed30f1b 46f41adc2dc48358adf0444f97c663597bf7ef0e9b4987e7cd3dc1ab5b15cbed5d7a5de2aeec2eaf 5c42d0332fbd21f974645dfdd8aa95a958c6b5d433cd47756ce61bd5d99964c1e45c2a5d86a7dd9e ebff86449b7622e7bdfcfdd98af0a362b2fc7f1200ddef363f61e9c87170236c3485f32bf61b8e07 d7abafc7f254fadc6e2d0471e9f853b8bc37f7daa577f73a0ef1ba0f9d8c862ced09826dadd3a2cc 5a737ca29a16b9b7cd55e999ce02e70bd5c99ff6ed61d3788adcc8687c9499ce8d5663bd030d87da db68122906ff0180fa0148b4d9ff02e4d0a92422e64720071bdbd82a1785f09023859752f7a567ce 3ea4abb5db4d9938d74e237b77947bf66d6b2f14b2ce309eb7b079a56eae8ec5cef90a5586e7f2ac 3e3fedadeeee545b6d05e370950ca3b98dee3af7ec66b4a8a9d7b53eff21540594e76a9688c6473d 731f1e27da39cd4ffb69ff83c43cb389a8da3006686e5f03e8bb9d88cabeb6efdb91ddfbfdc96df7 c46a859deb35ce9cf321a7477b8a2d6c0b0bb7def9aa4ce3d35d1fc3a73aba2c18ccf2a7c4841e68 cbaece4fd8b1169d859536c00d5afd38f1511d958b8e9abd33f1718ac725e5bce9f6142ce267b22d 3113793d588f521072e13b1afc0700466a1f8034b80f40775afdcf07046789a8df980c820a987b08 8b8c6fb54777e21c8ff1d65a1c5cfeeca2ae7ea2fcc7c56091d0d7f9efe3abc5633fa70d4eefb27a fcf84d151af9fda3fececc8e683fb75116c706afe0a3dd5976a0979f6e7d17fb042e6d8f6a47aa90 c5a9f828d56722f5ae8cc5ba8812bf01c093660c10dace0034bcd440ce2189e80ad3d3d747eb77ef 57a55875beb559dbc215627eae3c8874d3cb602784acf7eae3b346b4c777551597efa3c10cb34734 1c62cab2daaf28f873d591c9c27c28b9dbcd52da554e8cf858de54912e34ee62fd7a8205b6da680b 2d604cf95079cc7961e54e528c52a4b9ea3fedbf0030570b01127f20906bf93580e1642f640a4ae3 b9185592218f435fcbdc3eabe76ac71f196dd2df68f2e7cdab1a9cd18e270cbe2a5631f352f25300 64d2fc20d22d13a5a121a93af934445a8a7a62832824eb4faebce3c36828f18274baf23da299e164 4e6b70c4b43ae288adb0e43295dd84cb74d9718ad17f900cf77e08d05ee14fb42fdadc327ff66f78 29ba8635c373c6b9596096887dd1e0f0435f53fa87a50abbdce1987b6a8a920f34532ec5ca5dda37 c4507c6ef88cd8b8f398c0e14c857f937c9b17617dcc81f975c311c780e732e38ec58e4f4a949e5e 8517c31a73c22f43e6d4e82d9979a63d4d31fa0d00c90f03800a9dc4401db8ea4bf811be5197a2ef 7cddd9c3322feffbe9502ee434702bb454a4d89c29d6b393ae6fe46b76254a77747312bdcce42a36 27735fe80c9a312fb2759803b7629e53f6dd0697b1bb0376529c2e99d38e619879353a1fac75d33f ac8a41f990bfcd0787fcfbbd3814107896629c62f48334d0f5cf05408879226a3847938eafd61eeb ca22b8763de161cf0e99ab5992b88bfef6f4acaa01af9e2cd58391bccd221b8942cb9cc8e46b9ac0 2398cd478bc293fb8ca2901b0ade97d540986eacb048f355614e21da63b02a3e3bac0e5d9abe0494 466ff8cf8b2ef7f41275f30a7deaf6d1e6d41dbbcd528c538cfe83642c6d02902b1eb0f70dec0bf7 4aa3ee3baad04b9751e665625d920578f9ac1da1d5e7b8c8326579db9006521db157620bf70e42b7 14c8fca0e69db9e3faeeb2faece8b188c247cc222b23077baa960eabd869d357e235a1cb5cbca5f6 71eb48d564fdb167b283b466cabea9df7b7b065acdf64c51fcc138c5e83f008829be7fa2687e9f5d 645c168e5ff6cc2addcefbc5c1d1a36a7052112b1b2a45b45b90ea9d414708f1f58c8f4b4c6a02dc b77f14b811a31bec54902eccf9c97b0c5ea0a2c37a4b43f4d5dc15e84a9e6d52d44418eebdb349ee 9bdf58daf19381bbeb225f741b997c77d7ad766609e63f18a718fd8b34a49ab600456f00e48e7ee9 89e94c78896df969e52f6ff7d464e78e766cea86625f8e4f897abd50215ca04d5e6eb7269cda1f6d 38e8b86559d4d8aacc02acad03999dde69b73b08e90addcb5094df4b4d60ff2af76afb16351bec78 67b7dac62553d80e28dcd97ceb26b4395e06edcdff61eb3cd755559a767d2c98730e284a54498280 20088224036640ce7fc3986bbdf3dbe35a3fe6dd322f1d14d074573d5435c9a088d3c97c4c309086 a2ff85b88417811fb5ef7a0a166f0f91f1e07809fdf32154ba6e5647502a9107b5bdeb79caf023e6 b6aa70ee4ac8e43d1371b2ba12966c9bdf783750dd34eb438befb4db278e5f561feb87dff8ae47c5 5a999517cd36a322e5319d582382462aac48e7ddc8a54c4a4828bcd8cbe29bb47551cac48a386532 75e4bf1097cef3e27bb3cd952e3da3fd72cbce28b084aa7636f66ed1d528003c2822e9995bd56d47 52015f360467a04e363e68e39ba6faceba009f86e05b6e534c8cf57399f7d6233d0a58a590448cfa bde519007e366844fe0c29f35bc3a8328cf0e4d2342db24640d1cafb267db206ed9114778c5ceebe c87fe167f61c2ad57b7175fcf8d1ddbcdbe988783d841fe2a4970e7757ed8c7be6369e20aa6861e6 43a8e780f2a6d59e0df96e9f42b8c19827d7d2d1dcb061d5d3d829621f19cdf22e4cae727ed328ec 0314813b993a483a9ad323d3f81a5ed178875db5cabab95c93edf7720d47dde51ae1e0142eb6bc88 27f8bff0d3410392d9855e815ddeac3b77b91a7a777ed6bce8e429522f9fd5148a96f4dc6e68bf7f e6bbbb0db07e514e8b0d77c0949d261d82d953f89ac95da82d7d58ab265dbceb3eb568a87792ec88 d1eab4d14aabd6c9682fb9f665bab86f6ad462f0a234e225579fc48b7db78917b1c80affd3768f2e 06968b64807fe1bd41ef85f343eabd9dfaa07f3341d10af4f2707051bbc0c1df6aefb3b9a11349e2 86dd8dc1eecef19bd97fe755da6c4843bac43a30b5886332eb69d57d674352f9d16e75c667d68a2d 539765b0e2dfcbde49c92fc492d620e45ada53c1537981ab6d4ac1a15b35c092cda38125e402c2a1 fd01c5216137cf30fb857b51bb6761946734d487753fc6374357949b464fea57657c647ca9189b16 f7686e64068e0e1bca3e4dcfa4171d80d5a51636571da6355edec035beecab0abd78ce2ee242aa97 f7c487a9d984d29e0478cce3113e7b6eb2c431cc185b3d0c89ca286acd581125728d338abff375b4 cc2e40b48c6fe76879c241ff856bdb1622a73e9fdc0f9178b9ef1d92beefeeabca6d0b8bf0697366 288b55f7d32ced9a6ceeaacc72d084b4c598b8de09e5312ce0df11d3c6f785eb14cf115f1c3bf406 0c863e561256d40d1d3d224f175d146b0fc45d8c1284ec694df8247e209819535ceafff43cb875cb 97e7d715339a5fe7bbcc879e5f07c2f4bfe01943f3690976f96114d6ce53bde2e9ec1d8dc3bb58fd e6fdf567f431e872106e97033262b103d327d1851a988857879e4863ab16e0339eefc06da79d2527 cd830d89cdf9c19e9ef75e5761f630ebfb743c43ddd9a8633da1f7bd9887e4fda6074d90da721a37 1f87695c0cf2d328807a5375b900a7ea0c1eff17ec749c7a1e6654fcca2282bae07c94f760fc9270 6a7be3fb36eb30c53ca1adbabb8580c37396461ade6d097d1ae86a1ab3b13e9d7d91eb1470bd6462 88cdfa041de103d0b2c4194810de02acf44adcd8493075bcf28eeeb84e57b2dd8c4ee36d65746288 f98899f49551ebde8846f45d6f8e682f0d4fe97d341ed1c263f40be6a4567ee995e927dc3d3a52b8 4516afe7a6f38ace8c71770e2baeb5dfe2fb9bbd461a8f9fd7ba419fe100032d63070faf9eb01c06 fd8d39ec19b760f060fadfc130202a8361e4f7fa6f0d98f6e5d584e84feafcbaa76ebd5d6f86b7dd 1ed0d97dbafabd51ef22fb2bda45bc8bd12dd0b762b7501a0d5220999ef6d3fe826e1d81500df297 589e31d350687ee11bab854d9b6ce7722a9ee0c90669d6ca14a434c638586991b361af5982fa4af3 c5f7e22361f5e63dedda35f4cba75b6c7eb307921d822b56da4ed26db7ebc874d4a22fab79f33ad5 56cdeefe26348466e7d0187ccc47fd759c55eb5b3e0fd7c1beabd4c1811fd5c2f3bc5d0b4da2f70b 1a23c45f6532dfc462edb3cfe29bf5d4587a24b3eaeef1645d12e0f3b3404d63b98e8d57476c3618 4ece932ed246c69d05b9e7daa4fcdab7ce958ad30ce041d078e0ad6763781b4475f0d5066a2a0655 aa3a0fb72aa6040fcaf60e834a9ece64799d459610f842e0be0e059e221e857e2b57cb3faeca3c9f c6e8720addf885dd687207a4f2e3fbe1c6aa7da61a65c220d42628c117a0464f35bc828d4e1034eb 831b15ec94bc5b1614b65af4046a3c8c60597fa7de514da5cc4d35b9d872c51444b56ccbcb7d899a f78d4270fc98f987777372f2da3a01b3a172fb57395d35fe4d906519600696f414c215984dda8514 50e547524ddbbf90ac653de4077229a0ab79cb2222919491e6accf4c35b6858f5a8fe1ac071dd20f 9e2575ea6274aad5a0f70daae66fd2a46c6f5bc392b70b8785d45718e5457231cd4d3a6de8673783 fc3cc5904841d2290e428aaff6afdc6b3d53535b857fb27ac7352485b84e91a45f01abc62f44f1e7 e2445f7f748a1200bf44df27fe933e9ad482384ae6cb46940cce60940c878b28696c3751927feda3 a430f55328cf28697e8128698dab29d69d14ce38453c8f92763ffd459b6253d8db2829e70f29c86b b477dc383c1c826c22f81ceda0f9767db3fd2625bdfd6e8cc5d60f5ea7906afe42f4a5cb5e94f4a9 4b942cc56b0af51625b7493e06ca41374abebd598a1d1525f76fba436f6e4589ae5c7e8ec62f4429 e6d927293da4e7731025af269402c753a8748a871025efb616e92c748cf237e61a965e87efa71a9d 2bef34566bbd3a45bbf70cae66efc97372f70ffa3db6f383acf0ff7f1be9b944ce51a2aaa9955170 8f81493b8c01d3abc6c065384cb1c762e05ae162e0b8d26260979e12802f3f52e0490ca87e2d06b4 4237c5048cd0dc6c1e150e14115a8c9e15c68444fb2c84e520513fab41e3f8a9bdb0ebbb196c9357 e769561fcfa3d9798cabf6f0fe7998c3bb22ca83fb74c4f66fdf02f51b51c2cbd718683f6e31c0e6 ef712e2f03714e047f527b739e0bc539bf4bc6396323c539e97688734cff92420c2334b00ae1b1ef d7c28a74eb7c5cac3cfed4eddaec7d5a8ef0375343c857f630fcc58d75e5d50ddfd65398552f8f97 c10277a88a576e7b51cd6ecfe0a0c8e3009bb2a3ebd14586d76aa3d9ff2fa427d20b6240cea7a6be 1bb738f7c817e3fce298e5740fd0140a1be7f98f12e737881395183c08abfcf2f36948743674bedb bb74e2e1ab52ebf9a0b4c17378d1c0c77bedc30ff0fb5cdc23bdccde5564b2bd2516aedff4f5f912 986235b92ea7b5caa5d9e8744fc1ad30f445c3e879eff40a78f26edaf22650b7f917990395b53180 bd8338d7e8a556bac22bceef16d5b8d0ae640b0c6b8bb8c0027c5cc0917d58639ac737eb7dfc577f fdbc3f3ef033bac7f82b7f4f1db2dacd1856dab7825c1f044479300dca0e8a5e57f55536ad5dfcad c25f9a91b33bafe780731ab4476f2f04d082ab1dd186637868cf29365b1ddbba14db76a5ec36fe0b e989aca676eee120ceafc3282ed0d7665c78a5b37af6b6c39ffcb4e14cf8d0c66dfb1a20b4fa505c d4bae73a033f38e606f7abdb9ec797531fca9faf28543909d576c37f8960cf1f7fa0b1b7231673f7 eb720b772f5c7937f7296a0e46accef682b4be4792bd572d66137633a1ebfa3ab64d8e119a66af4d 360ef700aeff429c8387a99515f21a17c670b6606fab13a77f701617ef9dd5879919d4f3e50dd83b 5c3ff2c162b6db5d188e3c9c8644dbf1a261231b05dc04efbd5cd819c68e39400a4e698b576d1b93 3b76d5d647478f7fc3c7c6ab445a2c98fae99dd2e168f6ebc5f74164e09221f34c5b8f7cb0a9ab74 bba1cf5aa5fa3eb11ed55ff8b96f5c2c3555e14e71c1dfe6532bd16e5cea7c662179f2d1e77b9143 eef9158c5c1b7ab43c0ddf22ebcd06ecd631eda56edb4fd639fa45fe726c2ee4a7c53eccd8ea88b7 a2c97f9e75b3bf2b740f6252cbdcc1c3c85b1286d294d7c6e492e8bad623ef7b6364e6f705256a6a 1694ef68e5fdb9a93a43b5f65f88f35df212179aa76b5c5c9ef2a9899bd44eab307df3cbc1f88e4c 1abd1ff1de437afe44062037f57a16f6aafae4ac8b7291cd9bfb34cc4113700f9252bb1a9f6ff36d 4ce14ea27ff3f3b23ec7d1a69e2b6f067b74a5cdf6c5fa87d4acf34cd12a1df3a4ae840eb0f3df54 7d47cb6a36742a9778d9ccd050d6daacf60b69f724fdb820e56ea947fb2dfc247786de4dea3e270a 59bffac532e04f5e44d921d069ff48eb2862debb73f2b0cda31b63d7a1557d3f581d325d20b7a5dd fd01546efba2aa7db4057000b48a71adaa64f1d551eb767db23b2d89c5aee56b82724d8317a51ba8 b12cf4e3aa3c788dbadbd7b6d9da6ec1422343fd17e2fc45f859e7b6581b3ffed126f35ae33512c7 c9d51ff1a793043e4f8e955c8163cbbdb6cc417c9d183be68aebf0ecceec313a14b56350d4b46abf 725449a97e561be3c163c7ecc6b1727d0cd35e252e1a4a6fb4cbe40759504ea83c845adc76abaf0f 5b106d7ea4dd715d9620eed31593dead9da129ee3da3f60b71a1b0f5fea86825b99b7ff3c7557ca5 9bb8731a6386e9e5c663eb7852884ceb3487385933a0ed62b0376b24acd9e32da97ad08e571b9ebb dbb185a3a10484eb2a3dd7bdca62e3f49687e63dd9cae5a8b205dd764f521bd04c82ce7b5ad4bb39 4d84efc64b304795520aa923e005e5e7051b256bddccd0f885b8801c52530323fc11266ff8f5713a 6fdc9aede58cc1dea9dcaffbcce5bd1bd0332aeccd65a9a355c3ea546d5687c4aed30419855fc3a2 fcb82ff6d9491b0d17d65696c99314ddb09ba46eb8484c5e72514440b325e6830728e083c65228e7 f6bb8d6d4eef9b6a7c286e6a95719bf7dce90f9a191abf1017c4eb292eb59fd993c2cfd94f2fb3af abbe3f8d34d359acbfdab18d63f2018c848b5e289a80561b998ddda5128c15fe1c20f2739a27b71f 38cf6d27664996b472d5908079de168d5cfb2a16b0de5bc0d50e209467787db334f8ac0bf07ed1c3 79ca1e6ef9e6f274e2d853fa7bf6f46a71ecbbd5cdd0e23a72b1f10b71c1fb5e7e54b4a77c3e3d2f dd35eeb9079f38d88dfd5533c54149d475bfed6a4b0fccbac0ee42b03565335487f2280d67b6cadc 5e481a663312607ba288d63c55b0e8b325948fb6b771168ffba6e6455f9e6e95ab7cf338ea73eb2a 8b721def2dac372dcc59f783522ec5aeb1ee47613743963bc8a63172e317d27ba97bfbc9ebbc5960 e9721abf62dbb15bd383c5b35bf53041dfe21ebf0496dafce45fca86ec94e4f164d695beb9d9449a 333c2ea23c4b0a96cff242a5c566bac066c5f13aef3f7887a729f9ca5d2e7ac875bb7e69bd113e9d f560348159499139760cb56de6f338254c08908d14c76e865686e62fa48328f1fce9a0c10afa596e ca4b4a17dba6a0d6c11c6db09d6ef47441ab7d594db9c5e64d1e6f3f79e93b079ae2a15d1e89457e 840815a9bfd8acc209cb9fd23b866f8923955b7f4616d755f0f37af3a5deec6bb8ceb363594bf73a 0532058a99ee670cfd851f26bd2fecbef4bed8aba7e03a195a191abff0732f7d5874135f5b947976 cd9a723c76a8dce130cdc1ea9e40786177cd37b7f2b64e5da4af2a671d5a3cd07e5d58f4fcfec64d 4307fe740470be5d2dd31c47d504ae5768eed602d131d997db3fb16063f46476f824a1bfce2a3567 e54fe8dcb9465107563728acbb8828ac97afa5c0ba195a193207eaa7fd8b9fdbe931db60c1599852 276739a68fe6933d19e93dd153b5fa1a179441e0f0d2372abb22468f3e42155a95370d64d3e1dbe9 d4c871771f5d3f86b7d57a283fd31b625af8c920063771f6365ec0a59349e146c35af34b1d5e830a 5502e911798c9e4b72a151dacacb37de29ec0a594586dd0c2db23a6a377e212eb1f5dccdc21a571f 6201d76e2ee659fdcd41718e87fd715652775777266e77c69c1231cf30371ef179f06cb398e3f8e9 b8b916e1f9907d9bab192bb3c682896ece9a990d1c89d6194ba3f38173a4521720a04a2f202497ed 7a79e5f1f3feaaf134b2056897e7ed68b76427f9d792ed88e514493b433343fd177eeea5a00e4557 d75c5e5ceba68f6d63feda1e34524c3465683645d1848ae48622062a77cbaf2f6bf166c43f69d7af 6b858927b92e33730653daa84f511a39d12465b5d73c55e6370ae9f40d8bac49ce79455d9de7f2d2 0b0bcb8ed4ea2cf8708d2efa6a794b3c67c19d780ea8528a479b78d6cf8d0cf51f6492eaff365ea2 f07d5d7a05fbead450cd35c74ac5d64b6dce54db9f44dfc6e5e676e3bddd15d73f706b56597e6d66 deae3f6843650a9465080daad27287a4730d6764bd9fc3572760ccae6883c80a31979790d82fbb10 ed2e36fae646bc0a564248c762130fabc41c57bc78834f69ef8a4f31bc804f41af95a1814fdb66ed 17ee687d71f712b6e61fbbd5956becb5afa3793c97750145bc9f7509ad17646e908317ccb7fa26e8 2207a95445df9fc9fae515ad985ea3bcbcea7067d92b90e3c5dddac10b617d5f2d86bd22476cc58a 4c80a3a985ef14e28c7dbf5288edf7af2a962f8c41f460ddd6d90b9db0857e42b1d92c87627db389 6235b59ea1f60b415d6ddf1d07b8f9e6b639f7fe91f60dc6d97ef3de41585d2ff27afcdad254e58b c0ab362e72cbde3e6f2d8667285b4893903b5c82479a5dc5d579d2c3678de1144b036614433a0c89 e5e3e30635b5504571b8eb22b6893e90257e2cc2debd358029c1a1e0669377e066b1fd851ba15c87 1b57be363f2f90f25f645a67d6fa11b9bc1cd73be364e895f149a382abaf4847cc95b0b26ef23d44 9219d82699652077103c2e2a3066d49a1256904407258a8f27e22ccb00527b4e6ab02fb13d981eeb 13b8557c63f38b5dcd8a96e6eb252ccebbfdbd31bb3dbfe7d9e640c4d0ab746a4192436090e4e226 341e3eded0b88954a071615286469f7ee9171c67009c4d70b43eef9d7cf7b2bbd7ee97ed5e1c7a9b 3338cbbc4e56255b12e985559af86c6a18b2625fe3f9fa7547667776bd9d0de29b0d6dc9e61d021b f8771abe8dca149af8ed4952298c277b77844c6092a526f9e159020f9f860962d6f60e9616d5d2d8 ee9da0f1e2a964d3da7871583ec78bedb3345e30b714985ffc05ebbe702e46a14207ea25ae077254 3c0562d579b8eb4fc1df534e7fb7593ccf2a8915171a32e7e62e38d10b41776c0be939a835b92c2d 61e47d5efaa809d5fde1a5ba7a0d596f0f0c3b62581bdcc6adde6053a46783fed159f59f7c53ea4b 7dd1ee8f738db0f7393d7a3d853dae7b0a615c7b4aaf9cf4946ab7d0fb4083dfc85e49915852a0f9 67f4aebc73f5878437b10bdfe791236db12d65b9f14a6b0c2bc6c4fcfaadcca633001c8dbc37deee 3f8d4fab3b4f27dbce019d2c3ad8d8163ba552c34cc15cdac7e014b6175ab5d8aeceb14ce56879f5 e3b445beebab5643dec8cd33513b35d9ae5f68b6131d6e04867b6c04d8346cb6b7fb62b3cd6c0bbf b077f6ca73778f57ef2dd273ef9bced6f618145eef977708da60876d7ff523de7707f0247942e351 3a8777fbe3b1dfe8608571b5d570c7ade6a5da249a1d11e71a9b82bcab3fd79e5597bad1a9f6d11b cfdab40026556dcd56abc0d71b5450aa81558acdad50b6c2ae5b268e61be5ce12f592e47c91d0487 d2ca845fa57a078b7f41e558e923cfda9597d0dc372eec9c789a2baeb895f07c9da2e70181a213dd 87c1e1855d767bcad56ab41744ab9a1effbe5497ce68afb6c3ad6c37d5efbd4057f3f9025f2975ca 72b93a00b42273bc1c0adce3e9e585cf33c8c9d54204a8a3710598159c31302b2214306b9752d7a1 7dfa00b3ceae07ccba44fa7fbdb2f10bcaa4fe1378883585bcada7f4c4215bfe5dc1e18dcace0365 8d4f10039b0cd738d5ebee21a3d922dd62b52eceb04215e0f2f94a097d75cbd530ec1759e430ca3f 5c669a7b9f1730a06e7a28301bc0ab143b3e45a8fe48aac3a993c278fcab9c5a0360369e2c52bc77 c00ce42eff2abdbbee2f88ce68ffe0c64bd9a7eab7f11e4f266f1eee148cc5047972d0e05ea6fadd 5c6264ce6db37d70cbd5b858ca95cb321615b81595cb8b2baf929bb491f48ff70b408a5a3ec5243d 697dbe9de29c9eb941094e012dff317a98dba7586502f533fa479b4ebf9582677f8e06cca75f0031 ef17a2e85b51a29818fc8b4a458ee22e6d45f134be47710fcd45516234a3e8098ca3c847f014e63a 8a4e7925056446d16b774a3f5db3aa88283aa7ffa233984fc1d552b8dd28bae4c028b2c6680a8589 d469ac86fa08387eb0f8737a57b74ef0ae9e3e8f97b77d3e5e1476fb4fa4d68d77d1b7a965efc58d be23438f62659efe0fdab9445f5aff465fac508bbe6d6418c5810847b17a26a3586b8b2998f4cbce d58b62b7728be2fd344cc1e5521c2b29beed28d607a34843d059342f488bd098fed41184857dacbd 9dc9c77ed1b9e7e579616b9f6727063e8f9b1da7b815c2c7e6d68c7f21b5ce32a2af179bd1f70998 5102749c9f64e42859f58b5132173b513278435192ef2d7f8ee6cd6d52bcf4e8fb19a401d2677d4f e144d15e79e5a3dc37ac8407bdd80c31a4dbff1cadeef8b32050f853ad48ab37495afceb6401c693 230bfe63d8293cee5b07cddda2c13877537383dc0d3a7533ad3348980af00b7fac4c661d2b4a961d 33064ae72006701c4861d763605a1dc5407781c640c565a32449bb8a2e4ead28ff214e61e98bde3e 4b721d7e6a8d7dee4db1fb6c319377b3e3d65fece6d4797506e1e819bcebf3677f32583c448de01e a3f9c5b8a9f6e514e82fe07db54aadfcb5cc570a979a7ecd9de982929c2e0411ff426adde81825c1 2c3311377f92c8dfc3679c831fa53847c1bd3887eca03837faae22e3d8da840452543ef5f2d778b7 1a39f7d56d7f2fcfcda69876b05c2b7e8cb176ee1e96a0ca7dba849bb76f8de9dfe6d26e9a064047 2c40d5e2fa7a04e65959e9a5ced1a753d79a3fbc9740241e9887017787d7bfce37bd9ece7e790a7f 21b512b162005c1e62e015b871ce3d4471be0756e33c7418c4f9591d8d4aade2ead368b8ec8b5b09 d932068f57b0d8ddc3e7e2704b8a8c77cb57c96b8051ccf37afc6ce36b752ae72e0d982f9d99b3de 3c7137b5e73fe601e40fcdfcc2933bb0e0ce727bdb414aa79b8df7bfdfa32ddd936335dcc7160591 a1d548e0ecbdb83fed5fa456b2a9a927d148ad842e719e2b26713edc35e24229370a6d2f9cbd3b0d 1279bc4f25e296783619e0537373ade555e57cbec9c6a98f2d8ffe6889789efca4d28345f9ec29ae 3b3b8ab10b707ed1411eb7ba6da185be4d1467f3a3c3e9ccb1f66c18e6e5089ecd6e55880e83e6e2 6b3c2f506c8cbbd5d01857f29f5f8881836cc6b9b9acc67982bbc48531f6676916e9da88887665f8 eedcc1fe63f254078135ea8017ba1e23fed3d716de149258774e6992832a86665b5fcb4c83b68b77 5c9d6f57eb34f93c2dfa5b8ecd2b39289add33da3cdc65639c5ea798305e2e9b45047a783ebb3ad4 6dbcf7f0b0fadde74eef483b30e750c3dae6e717e2dc40d7e35cfcd6e342f37e8b0bcf5d3e2e0e8a 8d4f8b5ad41f531e2f0795a1523a6da256d3fd2ae1d0c11aaf6cf6b4abe5efd23abd42d66a4f8a92 d95b15d5c3a3de350fc3edd835de207135407b9d0e124b23d121ff54de27db566f0f47c47c9fb79f 6b0dafe3966a33e65daddac17747d5d468d7f0f95039a3c8fb0759e0f1bf8d38a779fb38bf6e1fe2 8251b9c7c5f4ef4615ff58786ee74c142c2a93bbffbaaedf6e7e8aa5d6c958db6a9fc0f1e1a182b0 f1f6e1a53119739caeee04699f245b75af7baeb92f344f59aa956686b940c377dd8f6a27505e5d7a bbd6ce6fde273bea3aa5944bef62281da97395f97019c9fd1d1e6d9fbdfe672b3e6aef5f8873374f 4dcfa6e0c4c5fef4111d792d7a6e37d56cd1b9a05ad6b4d303c8edddbc79bf1e29e6fa35fbb45735 3ed0a3af6bcdc7746f74dfd8bea097288d285439d559f7b6eaaa0bef77be009b3b1a164ecac5dc3d 5207e29a28dd4ab5266f4878280f86d7e55652d06c14903e5fe722297a3392a6a35a2cc61f201435 f9f9fe8538df796b7f04bf4c437dbee28677256365e7bff5fdc6d5c9f6e6489d40e7f094c09731ad 2285bda1204d8d20f14cb6539db738dff9318fef5a2b9d56aef5f346e932e7ad7c6f47ba3cd04ace f6351f06db6d6e11a6e19e5c9276a5774ffcae86a8b8af9fb6628e9dfac2a1637d04f4768985e2c6 0a37c781f2fe8b4ce8cadaf4c2378cb808d483d095c6cfc06ec4fb936857b6ae5101d7b6d33eb3e6 66111cf46f3708f6683a49a86ef65ac6135e4b7dc04d67a2745f234416c0d972fbb297ec16ac2d05 29f44555dad1a695f969dfabed8b7b2e7a89b9672d271ca4654b2886f66c73b4e1cda60a5c5dde3d a16f9e64365f9ec4e8906f94d2cdff1f71fe025b713a763c5ec3c9d0bf36cbfeded34647d1760e49 36495b6b7ab2347685a5bac71ae259753535dcb5db4649e9f59d963c442fc32db8b84052548d3109 92caa4b80f6b9c08effa5be1908c7401d397eec62eb0c16661d95fdeabc4359ef4f009df109335c7 5cb9ac48966b73df07d75689641df4076186cf2ffcd347d5cdfb9e5fe0e6e98911aa5bec0ae2b1a5 9ed9c30b02083d772f48aa1b779c5d9b993ce5c76c95dfca45a226a9bd75f660454c440914e18f06 0be654c305ecebd31b9b7c0a9b65b3a0f1deba6cf1547772e5cec232e2d8a1535d074a195cf35391 5af712c8661ffae9c98a68e59b21624785efe7177e864ecaf57f5250035b1c99feb456516c175e88 e6ddd759031a5e08cdce39fc2ef5782cf9a1166edb49abfc9566bd6e454446dd96804fbb838ded4e a79b25b94279ef4caf788a95d6dcf9a66c3976e367e50aebe075f3d6bc5c7bb18f785e6445cd19b0 23122119d97c9bcc8458dc98c93a4cd24ff7888e2bee27c3fb2f7e14e94fbb720aaedea7697ae9ac a41c1916140fdb9067f53cfe2374a954694e2afd96a26f2794759180ae1f8a05382c0a65fcdbd8d4 56df1e4f9d7363eed229ce38f6d6c2d7b741975a6f7284b8ee1f249d954a9ac38eece78351eaf53c 33396dfab4d68e17f4ec661a34200cb39593d2f6f04d718c6980dc7f7ee147917e44b58773e938cf 835bb45e3b8bbbb545435db06bcdd69485123cbec4f603a1aa04ac094fb0e0cd6be3e0fb1cef6fdc 1a77d91eb3d893eb44a7e1faa686d07a007c51f679a890ecb854e7998f3ddd31d31a71a4635fbbd1 f3d625a18160dca1d0998353c51cbb27ad6d72252d998953705186cf2ffc28d259d1121caaf6690c 6e74db750f8a394ac30e1d1191b54a9de8852c6559eef3c65712ac7dcbdeacd8e99d6f0da631d705 e1e27a63734df655dbf659c93f4e98b0e5c28c727dade86f2f5cd3dab322d3b97137f3d328235c5d a922a47d49420f9b6405a591955b1aa9ab957dbeac560e14a5c05268d0e7177e3ae80d5fc88eb7af 4df747e62d2887099d487ba230e376dd12b1dc4ebb6426770b56bce53675c635b8eb2cbcacef58f1 c3bef85e811d3fa13ab31b635dfabb5b8ce83d24cce8797220a803756229ac196ec923573c909547 f7bc72c575b4aa7f4ef5e569379f2f9959595eb69eea79d97a35b2d8336d87d1b2756a7f7e2124c7 adc7d51f21b68b8d5b7babdf617606b00b24ad3e1df1f26b365f89a8599d6eeaa71ec5714b525d0f c73b8f0521f7c940c835a1f75654a6f3bd6a16145287c770409546e0843c7e488c5c285b6ae57e77 9b55833ced97e7fcd75db689ee6bc1ddb7d5454fac43c463e40b84705dfa29de6f4238266186f72f 3ca27ee49e6ff7f2cf3a505ef7bb3f7ce6f04e2ff61ef2ee2a7436db9d342637aefa9e72bd858ab0 72312f31d0a367d1f007be52788509499b94f364f56cd6561e1b7456d4b40aae1adf21bc646170b1 ece4496ec1e382b2e8974f592d2e2192b907fe3ec3455c66ef593db7c261d163e4629167bfb0483f 7f32bc7fe18e8c03efe731d9b1d338ed0ded006b9a2b3e764af6062e090ec734c717e5091359852c db8e464896a5ca1353236bf3c05d51e7c67379e90cbfcbce06292d6e73b9b5e81fd43ef1c4ce5342 2a452831ba0d195c19c0123e796d4d4c034f576c160f73a831dff751343fa750e4533ba2c8457ea2 0542ce66cfac7dfd42b0aa9abe5b42e70753ea1c741d2bc1baca7c1e9a3ce9d424c14e5a0cfbe989 10650db4fe8aee1be872cdd484c5869f18c40b10cfc418355fb8723c27f8942f678963d8b73f6a63 da7339c2e6863a470f457b81a2c784438bcba18a104fce472a5218c32ec8b4e155dc5dc2f5cdf300 d749fc06d761fe3df7dfc8e317ce837dd3b19bbdae697ce7fbc3cfd22cb5a1a13cbede5e32e66579 d32a1459e65b3da3abd672da235e4438c077d48ac4f66b6d8be5bef111c590da05395e266f64b15e e7906ad7a8c2eef7d38549bd06cecf850e3c67ac25396faf3d6916f41a992f30e31ec2037aa0d55a 8a3b0a099196de884eed020de7cd3b341c946fbff0f3f8c97c5e72968e6d0f479535b0a33c6dd886 584163851bdc5e6bbaf8d1b18560cc87a8d901b3b5bb618fc12670b374a7e7ecaa2ecf82176acdfa e0fa0c3d4bfe1b12ed2f008d56adcaf453473bd3c976034e62d0c7266a5c66273387d526c0ea7d06 8da15800910f048105aeae8005483981859e7f070b552b9b087edabfb09b3bda31f69b97ad51fcce 519e57d8910e13d3e4af0f4b61f611c3acd8c60cc567bdca106ed24963fa513aa589b6d727935c33 bf028d0b2c82c5e9d6185bdf933726bc4a26a68cdc662b1aad2e58715457f6ade1691a8243a63a25 862dcf10065cab600dbaa11ef6efeab2dfbf6f48b17f278b5eff3eec06fd7ba37efd05737c19b87b 7bfbf276377fef6fbf1e98bd0249686013838df65d89f4ae798a50801b8c38ec69083d13bb01160f 97e2e01ab672fd474fe9f787fa07e9bde909d393c7b2dc03c3c0ec466ae9d49dd586cf4ee2b34047 97cef50e02b6c79d4269b76c9b4e25d3a1dbe5817669d9ef55ad657f64b65d2e42c77629dc5edba5 2b77f905dd842bbe7ab1bf973f8b9e2c6e81b37e7f2c8d726a02bf18c5130225c429341bd88d2e78 d88f6ac3b68466cb4d75a3d4036f97b7f5b0d902f2b9c6c5177a8deef834abdf4b2db2beb109a13e e02dbdf69a17fcda3687bc6be3935fae86e66c54ddb10fb20a4d05a39254896f05a6e7f3ca9ee9ec 2adfadea55be8ceaff202bfcffdf86e6cb834079f9df9b8417a033df9ff54d1a8f5ed2e2f93629f4 d8539099809143f0f0a59ac30ea095bbeadb495aceb615d6ef4de6518586d57b256fb2b9b2a9e4bb e57205844ab6cb649e4d919219a6d8c4af4a81757356fe369cdff29bfc299fef5fd0414eeafa8bdc e8c1eac04782624069c33030b1bf5b60a24dcd5fd8dd9de9730b27bb60d3617497411ba8faa39767 834df178c367c2610f82d8576c0f7843ab7401d9059a7e540c6b2f8b7b5560337f2f36c2d1a9b03e 35bef94dd42fe5b635a40decc6f810808a3a9c222101a88449294e160095a78f14f7320055e82c39 0980aa9d758ac40320e05c02a0dc3efd515ed27e419e95724fa1b94a4eecbceb18ab2e076f302c0d 30668fc10b024b5fbddb7fa25ab583e2d77cf656ebb81a759977c95e9a599d749e176e2e10366b57 007a9c63004ade0930039aa5146c27850702b35c094db160525c77c0ac31f780d9f01303b3d1ba05 ccc61d2c45200333701b00b309584ef185fee24f6aef17126b04745d4f5b658b6c49c72d9ecfe1d4 bce7b660105fbf07fda7e4d7dbc7ceab587f00c56f05c90f5f8573885c800fae38c074e2f800a4eb e1bf32ef2f8cca3fbbe9cbdd14efe9bf72ef514c4ded5a299c576a2f5afb57e9cdd66d001b871487 c72f44d19c63a368c5d0e9278c4cc13029ae7214c163fb47b9858df45b48ab9042eaa488a1284291 550a954ff1de4511d636532cfc14bb5b8aeb278af0129002aa446a996c85c98a1b84f0693ffb6081 bb7cdb83a2f8ae6ec7c68b8c886cb0793554faf23ccfd0f3ff010b8c4f7f1145da2e35f5b64f0dd4 98cc543835627592a2881c58a99d52905a574cf78ad0cd143730b5ae8fa7e0d39fa1e7edcfd16025 3d05e4a4e02e298ecf08b28f61982c1fb910f6dfe58fd94a9a9f12df18beab526ffe6aec18f6c924 7bf91120a743eaf05c9d1fdc1fc4d1be8b95ddf117b2ddc49d3b17c56cb88e62fcca44f1e4956e2a b41ec5ebcb298a179d4f14f7e8d49cc46947910fa4a6fa38f18f82ee2772a4320d3d4c82861522fd 861be69fc3cbc794b0e7a71462e1dbde89d998f6ae266af5458dccce939d7ae0831fe557f7e12de2 6e721abc067183b3032d2f3801c02eecabd1818ebf107d0b7329fa522b3105ba89be8fa71e253524 cbeed6dfd1d78f0b3f3af461d68ebe8c3c89be608047b9fe990a5123113ec43152de6e2d6fbceb74 fef83ab51afeabc5b583e7b507be9e5d118a1f9b0f927f0c14b17e97be66ff160e1ff340fb96982b fa067797458773ce8d3a996936a736d8f37c6e97387e0f7ad8de3df18e7f117d3d4d8ebe89244449 5549112ced1898e49f31d099015112cbf5f464068328bf3d439fc55346df0d8e5abeda22bb7ef62c 5e7a8815317b7e73971bac7107cfaa73533bea2948366a10e803ef1de40f1fe08a970b95cbb25eeb 9c3d06814e1d80a73d71a7caaeec9d2d472b3c7c270dd75ddba8888e8db8946d1784e5ff419440fe 4f4f4b388d8b81524d8981c3d98b7379f81de71ae7529c2b54db61f9a4f75e177a327af6bd327457 3a4ff4a60d3e6480443e7b250047b838f46d77f6395f3f5d44c3f46f96e6f8838a71f624e1f274c3 02902d63e04e8956c5d1eef4c0c90d6f845d5c4fb6960b2f2dab6eb2bec97410ef70dd349d033728 d8bf1025c7a71403458b898117a5c63961788973f76314e71ea5d2871cf095e79dfa6459aaf75d63 d30cd053ab7b716715f0c475dd993fcc1f300fec1c4977b771d7cefee08a0ecca4d1e321080dbb34 2f3a473b37b81eab0cf1b1c840c89be7d9b595ce67e3d9816f5bbc21ce01437f1ffa277d5202b251 60af2e7ce70f6655c3fe8b18683df918903e6c9cd3ce7a9c9f329738af7ca3b02a6fbf4f21d1c3db fe158717afc801be582995dc48ceb71ca49f0c6d1c2bcd8e4bbe8459deb39a3d5eb59a686f6db2c7 9e6076b8857ae01fe4e1d0d7f79e2115dc9b31ba56bebad283aafb583f8cf7f3e280d10c4ed1544b f43d951839be5afe68dece59d3ce6ed545edbf88812dc0c5b93922fc2ccd42b4cd38ffd99f23f372 7fbcfa63f57c43066bfbcc1247d79b6cc8bb535017c971d5c22b5633c1db66b7498d0e83293b3324 57c6f5b0a192baf23e73fbefe42ceeb53852f739b26a69870678d28a3293a9eaea71722ea815b7d7 dbb9e469a99cd6755969dd095bbe0ab42f77df63f707db3b5bb1ff22bde6e5759c8bbd5d6ae2e218 59cd83ffea1bb3fdcd287785f3a5d0e65c8d7feced0a149c7ed2aea7f6eb200c9f79032c7fea3a54 ff74f730188fb583539e6bd8aa4fa8c7539f54175b9cdbb9d152da91b6aeef1acbc0519857e9a6b4 b7582207d8a32df71c10de8af59328bdd9ba25814edd13a3552e138845b5f6727e21ce0d269b3fc2 64a64f3d3eca737b5d4907fa34603ddcfdb6fbd8d15d8c24b3379d1c0d901d05fb6485c79a09ae4a aa3d5b35d4aa2ff577544b0195f3ceca0a981516ba1072a0df2899a7cb1bb9dfea2b5b71879bdb11 249fa44f3509a5093dab8a71eb3c15673cc60bfaf3680888f4f58542ebe46eccebc1f98538b761a5 9fb3f9a11ba52cbe0908a7c59f86cdc6ca859919622f106b6672ef0ba747878bbe4766f159b5aff1 7be703959c7229562af2ad576fc97db13bd83e47692832b20844fa10eb85347137b4180bbe286ac3 5813e785892718049b693602da8dcb42e1418d37c4b8c4f1ce8ed4f915e4fa7cedb175395f641cce a716f65ffcb9f059aaec5ddb1bd285a143de4d36e7a5bdecd710abed0e46c6648e52fbc28c50d465 b8717ed2aea9cd4dbe2946bc7dee0f05e9733ed6a469c76d8bf1ed3514e7700e1273f93a2a18e678 2564f5671beb26291b627339f26ebe76e357e63acfd7d7e52147df75866b51556dbdbe606e8a85bb ee12c3cce5f869ff22bd8d4afa8f787e43dfcaf634f484b5836fcd95c57e9ec8618485c3bdd1fa12 ea6a5c1195b5d832e50131be486163f092a6f20210d3de59120e39b2211419aebb39b6f7992eb0a9 f07b887767779c2773459aaf1f0622c7b45706d70a9ccb3a1d3593752fa774d9bb392359017f68ec e0db7399d7beed325bb864ffc28f22fd69bef356502dd35b4f55a04c87b66bb6b83207dd0baa6bcb eb48ab54859972ed9e787960be7469377879e21e2f3e84c3aa126f8e4c29bfa904d50a4fce7a2dbe 01407d8e4161906bb704647dbd1e566b8e7b6dd6bdd94067d3cbefb343d4cf6a7199771b6d33201f 2fe8a8ce2af4eef4b0e9dd39efd110f37668080b8e7f11170a8cfd50768072390fe65b1771dbac75 792e5746081be81e35dcf1ee34024159803946da6d384584b1bdfd9376dd4fe793eaf8f8e6c9dd29 e14ec9a3c4b591476dcd59eff6bac75587eca307ce58216116cc1bd9ae19b970dd31e0b5ebd26a8f 7ed3d0e35ba712c3c0281d9cc8141c993605076797caf3e9a70c99d0f5bf8d3f7d745f89d3033a7e 45a7fcceb3a6509cacf43dbb41b54a7800955ef93d90a24e7b25c20750dad87de8c07be3c5893b2f b8e73aa09868ddbbf03956ec8a657678d71bcc5b3867aa3a2323f9091d59559456d71843cfba3b99 4aee2f9b4290f18b2a14dc2a691ed13989cf0a125906282b85eeae6c893dfec29f0e7a103eaa3f11 8f825dc792f561cb8db272ecfd215e63bb735500b7ef81da11f57e806f6ceecdf3145ada739d6ac9 59f3542f60c575efcdbcefc32f33194e0a749ca76ab46aca1d7ac69ec7947e7bcc2944a89254015e 48643a56996419ffde574e85c92282d5f2d1825635ea222efdd6c45cfa45cacde02ca92372fc8bd0 2118375898beea01d656b0ae7cb03676499fd2ec158d2b81be9d4a2a46b58572919af394aeb3eb5b d754d8e7f09c8d69cc67129c9889163d68ad518d69806de629a333a952c86dd926ad393f24895c7a cf940f8fe5ca617bc2aa765b184b7fe35e97f4102c2c9befd76471513461b1869ae622edccee625d 1dd98b8ed7b47e90bd9b286b5f833eaf5f1b71eafb587752301fc099d375aa43ab7e6ebd90c50b39 150bfd7e8ba71ec0687d5b4f56ac045312332d533aad6d4c9f32b6e68d42c147485aea07208979b9 bc724e9de66ac5ccb2156056f5363b5dfa914d2c5bb307b7b8e67afb451763cec4bd93cf119bdb7e 440c48944b111f8801d271f157a16ce3cf0f60fdc563d742f4739f41773699878483dcd2b9bd55e8 b3bb751bcdaa577ff4e8cdaa5568ad6f67a5c384f51b466b7181a30eb98e4a1599a54356f8e565e5 f6e5d7aa2ea9f1f234f60bcbd6f1595b5c97e5ee82abf5c78bee93c588bb64b3c4208c76f84b857d 1cac9d122c3c2db2a3c176788bc5761343c77653c0c1a6b78bf50bf77c6f6478b1f7958fedc95034 d4a3b9d156d3e65ad9043429cd5d06e6d6aed364c255ab46e76c02228f9242addce365bb6ad4804c 1d5c3274cd5b5cc3d16dd183661fe29ed00021e84a9918227e0b7fb70a231ce49a732c82280adb25 9e8c2668de46f5a210a1b0ddeea0f9e59746f3f3cd1e39bc2c1b399c34eb17b2d973396a1edce2da 954d71de9074b46a8a6a4b287332582328e178c61076cb6e9ad4e17a2cadbce67db064920ab6e09b 738e78acd73b62f8b58eb80c5fcef8241f3f30952844d8ac3b2ea0c97d55477541c9460114195ea6 886915080427101e2957ec036c3fa0175c1b050db8d63aaee69edd55e69ebab6e65e3a89cda9fefc f017e7dbca376c727fdc1d268786bcaf50e676d71b760429a109664375302cbb6f54dcefada8d1a2 b8e8c3ed2afee970532c9ebb2b6c76ce09a8d1e9eed1c2666a23d680be2044ce78c10ee67ce15529 2cc135a7de9cfb1b62346fbe246476d93e99d91a47b459b71c5ca19bcb96a10d0265bb8136a3d716 eaa78e36d43f560ebff0f3c0e4e729846e04d59d7aba988a2ce34d49c477e89abbcd618236a685f1 e2b6262b98c67309625dcd36529955e7709d06c9397d2533e776dedab9fa6c9d5ced59d7c85da03b da7d4142114da62f8e2b4fb73db7331d3f8bd34968ac5613a8987a795f7be481fbe53307c2756f0e cedf9800cecfee019c9b0703cce15bfd2f9cea2297d5df1ca6f585b6af02256dd7afd8da7676eb2b 1b5f1f736c586a10a45b7f82c4d865ea48e53a4c661c6b27d090ac75a6db37359f44b8494e2027d8 8009595241f8ddb3c0bc8c9dc6e664fb1c63b1fb1d97dc6a16df8c960db037aa9e77f0d05b17d7c3 c6777d189cf54e3860d11a3838d30137386390313883437dc0448dfd5f587dd9fd47da67c7554356 86a62e1287f28e13ba398ec6461ebeeccd3699cb81e571a8310ba832309981b5d7c829f8d1d08706 cd21956cc0c185f2f1c1ba053083ceb52ff53710aef7fb89ecf69efaf9d693e8c6b7376e2dab5d85 3387dde9acb7e87c81a3d29933e8ad93eb0cda9df9b49395fc76e61559ebccbebcfa0bc67775347f 52cd95e7ae77dce658dbdcb4abcebf62747e84fc11a32929aaffa85023477fbefb0326bc74bed2e1 dec905d562fba032592166bb04ec26ada31f12ad05d3625bd536ba6dba9161361bb3dbb971ae8fa2 067352aaf5605200eb5c2c33f59e0bdbb50705966b8ff16051efd137a5de432f4a9d936eff077b1b 3cd8bb9bc46511813c7d0c5d81dcce0c3612ea224911c18a9848c60ca9395c6ffaf92095716539f9 f60598787660697769528dc8ad8907dcaec433ea56d16a37a0327bf51b15604b0dcb06e621e534fc a54ad606964be597900d36c515163e0abe03150bf4ea3e2eb4861a9fbf7cd86b7ecdf1fdfcc506c9 423312f85f502ffcf524873bee242e1ccfe6460543a59606b6fe27791bbf8ea0b16e37c78bb69189 297dc135c24ede38dd9acd51e3541b9d28bb8c22be5168c9392527ac2a26f07aad6ec0f6e02700c8 d66b29d6430054001c002d5e00c06bf50880e1f10380d1a20580711d072615404f1125c0a49a6459 43c00410f814aefc17ca4bba5e25ecd2f6f97ebf79a0f1e02a2e24965fa115640c41db59a93bae82 b7727f78bf7ddb387d7f343a56f55c9d824ba7e84e1523778f732a00faee2efd7b2fed673783b90b 4ce697373021c7e9a660748089d79f039387cd02d31aac03d3fef70e4ce7fb0a30255773604a3545 602ae58314411d986ef7788a2ffb17ffac130ef23683bc0759fdcd7223bc1994482404daaae0605c 95cbb59e2cdcb2f503c2577d306d5c2b399bf50bcc5e3101b91f6aa961c5f48c6cc62630b9d13630 2d2b2e30ed3827604af43fc054f1abc0f43a1b0150f1b1002090d9fee8d0f2e00440e62b07401765 08402142035054328159c9fd00b3f2a299e230fc0ba139083c76f655f6abee02e47f52cc67436607 8eabef49b32727e984bfc2ea616d3b851f251b58669174ee21af52b356dd0330d1e6163089370e30 95e30b00154677001ae14f0042a8170051fc1b80443d0220bf5f0366c07902cc060bea271519980d d7176036aae753f82360365e52ffaedbc09cffa4f6fe527a17540a6899025cfc2bf71e8428825b66 0ae5164548259762dd4af19c44113a225288eb1ff9f45fed14b45330e708b299eca157982cb7ef10 f6b77198a7edc2c76cbdeb1f2c68f43f257e317f2f9e2efbae6e8bca8b8cc6c73f32ef8fa4db365a fe23408bde2ffc6b5d1b4bd140530c891fe576bee3532b8ba909f0fa9222fea656a2b514ce3035b0 86a458a647889ac28f2c1d268b400f612fb03e6633703fa575707edbdde8fe5e3caae1cb1b358117 f941aacff394ef3d996f94ad3efa08905e1ac0588cfa47d01dba9c75931bd0e10fc073cff88b285a 0df11455388a6611f4afbd99a03e7fed5213d1f46860ff9d9a3828a550ba3ff2f9cfd1a093456ae0 880ef354874b6d2b0befc5bd2cbfbc61597b35e4f2e1c9c465fbd9d6fae7079f9b3cee0f0c8fef43 5b2adfdecb672ff87f7cbde7baa25ab468fb2c9873262b39671045011523624251dfffa8b5d6a9bd d7b9dffdd3b066f94dfb84ee608c46a793297d3ac1135e3dfa7d7b7ccc3b921bd30fc8894bd3a6fd c3b74af5fffe237b9615367b76b84f80eb12963dd150cc9e58f1f31f5deaf387bc66fb2c9b9f6e59 26b5f29957a01a0f6283f5eef40143d2a84311b7ed0063ae07a4cb5fdb5e57bef4828a76b68b95ef 5ce0947215f7846cebf3c46b7657c7b7368a8fc4594e633a9d170fdcf335d837d22eb54d9e82b6ed 0b98bab90e607563df5aca060aebff03d92bff92b2e7624c7d5eedd9ecb5759cec7579863f0ffded 3bf292a55bf66a05b94721ee16522177abdc9adea6793951f3def9aa87c819baac46a709b46192e7 642324c3d7463906c4c688974ce8c4e5f5cadbefc6e162dfccf6bb5db7f6ba6d4d05296ee0e8867d 17ecafb02e44b94fdeaf687e60ad4afdaeb55c5d2be692b35fc65f642fa928642f311c66ef2a2c64 efa830cdde99bacedec9f69411d7e89e6e9b9dec13d2f27d4e01bd78c271a49ae49bc8f706e67875 6e0c0ee23883f76d3ec177fa35a5b6d6e3c06e522e95376e2d6fac33a7e1ae87453c8c7c4edc45f9 4b785b31d0bbb8e4751d5948db9b18aa8fd63838612f27e86f8eb67f1557d65f7cab54bfdbecf5ae 30d9ebb5c7fed1bd63d27b02e1769391d9e1987e06e4f80229587c020ac7531c1df474dfa11bcfad 03d48a1bf458aeaf6778a71b0552078a0a71075bb118442ed79b21b71444e93be558ec52db5ab450 6f1a1ecb591476c5f629305323e7dfd8d260feb0446e8ee7f7f66cce6cdc596e6d395e2888f67f90 bdb91695bd1f02fb04f665fd4f696f4e4857a9bcfca5c079e21cbde3526b78bbd3d50837930bbf5f e734feba5a89ea7b29a24271b15f3b8df07873ba610f71c0e0525ae301c8ef283fed3f051f1903ea 3ca351778e474c307b1bdb7846dc4a4f6f41ab4daf74aa7ee7d0d3ea8d3026dbb13e9934327cec1e bc9ef31f64efd567f60e0477e58ff5fbfab4542197e6054ea6e2b1c2f0ccf6e6df95751e8c27abaa 19af166afb740cfbc0290da0d603f81e1b147f94e6deaed8980fe56e6fe63f6078464ea9a1b7acca 8cc76c27d2740dadcd29ff0066931d076d2672d54bddd82957ddcec31b8dcd615e73ae416fe23854 d575e0c27b6cdf9717e72f7e25f12d87ff49e967be9c7857d0aa88c7a8eb63fb64b8eb6fc797413f 62408c5bc4b981195c79ccf72730b59d8faad4694641c2c35b2ec779af529956a682b56a4d76e9aa 3791173764d2628b84ab6d2adf11daed5a23757c06cdf178b08897ce4d83ce0e7c9e15ed29d447ad d7c493ad4f0e4caddcfce09a21b118ff074f40de49cfdc69314995cbd54c181967f6fd751fddcc7a 54375a3c57df1aa8456cec8900c637ca7c76bf4d6745ebb19a6e98c761b2b772d7492bad3c5d9de9 15c6e735581d5b02d1726e37aeef8cc716ea20f486b2a7d151b071a1625aef81e05b44b88bad3c03 03267dba4246d440bfd7080ceee08ff56dc776ff405c33ce5f3c73b9a6fecca3eff979889cc5b8f9 2e319b79f18e4615add25db610a41ac02b0499e7625af038517726fba7e2bb497dfad9dfc83c71d2 e1f42bef1d64b77a7d16b5db828d1f2f55ebed553b16095407567e470ccd85acb366e911aa46e4e5 a606cf0b5b7dbb8b335d1a6b5dedf02c705a67805a6a72235d35d9a363d5503ace5ffc72f37b92ae 2abc9b2c871b6937ee2ce875a91a63cbb698eb07b7edbb3227c474e0553f3b61a276dac6f81236a6 4e9a202b3bbb227b1b28e2678be488bbb9d8d26f93b1ed9211ddddbac14f161da3c666882e6df35f abae376d5cd46268ea685ab1b8524fba7c53cd7ea5a1f6af3b4671da655381928e2bdf79c0f90f7e 07fe0abe28f5a7f637efba41afa4e6120f6dfad8f7f1f852f522466dfee4bdb220c6f62e921d749a 8c6d40ba045641bbac4de69cc58690cf2e466d59cd74b95203f4e67958d13470d8d03aa9da574f8b 08574de6cda9832e612ace3c0c15986c9de547735f93272b99925ed5f8fbd44569e6dfc7d2888c9d ffe0cf817f12947ed07b9cb4664a24bdecb0b361807a9bc16c71ded6a6cd51a73abef20c663f5792 6005b66d992c66788640180b5d56edad169fbce4a7bb07de553ddda24c35c36741b99581aae29c3e 6b74d8e411f9318a18799acf6b327610e7d2bcb33f4aa397529672766b2486d04c13a97837168b5a e0fc077fe2040ea3ef8d98bbf1b22444bc3164c2aba50d7de01a829ed058d45d133e151d0c2e4056 70e9b36685ed6bfabed1196bc74edfd7ba3372ad5a39e6a0dc68e6a4c0119fca0fde78c9d3feae2c 63d77b537a857d489a2beab72da8944b16b218ce1a9e488fc607b1d4404a9f45e7632870aca00895 d76ccc6fe6a6c36f2cd9fe8b079ff3bd64f9ec1a5bfc940aab66d06182cf8472342f527368da62bc c618e626393bc7ae7b3f79bf88097d6f3e654d47ee966a0d9f9e32a6eea1fc88808d8cd780833497 819344c0fd87942f0a79315c4d6a22ad5ffb62a95fc385085045810b1257a82af49edfb6df795e3c 6d70be217665eed0d0be2b024e598cecffe0d62faccde34668991b72741197dd5693f55faa40fc91 d1dda7dfb25fb69c3339906aea8a29635a0f313925ad399a9cb5266319d797be34bfac57521eda7d ab86447a793d89250e4885a8370004eec25684aa1f74f92d79c778a985f27ce3e839dcc16b6f3975 b805b84ece42d9c4cd49acd1c11cb677eada6c6f5db7fee20a4d7676dc866fc6baac445f0fbd184c 6b9c9f7bb2a4d7e0e6e838ad1b2d8b6607807e28dd4aea85ae808a5beb52b2078f64c9c7595b5c90 d454649a93a51069b3ad503d07477e6bc5375e220b6fbe59e896b8432cb739555b225c072f7f4f04 6cf2e62db62f5d22e6da129f8c9d20306347479181462d87bee78a369d5e5fd65f9c9fe7a5b3072f 533daa650b2944aa657e5e12286adabe4c31270bed8eb11d3cdf5aefaa030ada5b757f65d785f350 2a545ebc5886ea86c04f06137efbc202be498c222e6e9831a71e822bd751e3277bc28a45b63f6f35 98ab28438cd358530c74ef1bf47de22fe9090ede690c280da8a76371d4ac75b63eb87de7d0d4ac78 b4fee2143666e3adb7a4b5e5b1efc9016e1784592dc732eeb5697dd76a6a57efb4776fc53db61e52 d0a41b22f3b210a1b618d3bcdc39aa5c6c3e1cae9bafcc5993a92fd94119fea600735db327c611cc 3b7dbf85397a32bed5688c190ca8575923a8d9faa450238b0fc820bd5c496ab2e892944eb064b13a 373f87626e114bdf32ff22a9764c7753d820fae2f3f552fcfce1d719d66b8e056e7c5f8e47163bfc 8c864e3f04a440dcdf0441381479d96df4396d4d10ac795344e6868c0d06f6e62efde06f218ded1e 6beaa554626afe492b6a94086f321c8ecb240d1c3b64713f18122bc5f9de2a4f5492fb74b499b9a7 914830cd91d8ccd3a3fa92d03f10ade11e438cbf3824e3c138120e6d23448eb6365b779ef2e49450 bc33af8c495304dccfdc34d814844d677ce3f436f2606e3bfb5bd7c920708ad238fea6a9f9aea750 f9166291e151f4c8524f5f10abcb7c4b70fef94454a9e26324aedaf95183e31ac3436f8f0cdb4e91 c513d81ce346a973c07bd1bd825d06c608bbd4df2a7669344cec922f7d87cedff687ed53dad84bdd c80cdfbf48ba279b4fd51df729d10e372163b4651f958971aecab785e983ce483fa188d7b34032cd 7e8788341e23aa2f8f1d4944a40c0f8b833554b5d26cd8e9f6be060a372c6487f741f18c5dd2f085 d9cb7c15bd577a3dd4dd8c4914b5eb26e2dd276b6438a5f2c8d0e80f91e1d0533ed81af0fbea6a7f b15e55c64e9806a9356721c99c766f4f7d8c07c277aa6e55d339a7d9ba8d49e5f667ecb2c3f98b2a 60f669243d3bd761672f94701359f7b0ab77c530b85e63d189d29151ac4d5ac833313c64d4771748 0eb81ce020acdee0620902a065e43621d6cc63e0fac67d9f4d040a6e2e04ebe8fa09d6bb3304ac57 3b12587ba31a58bbf4d5bf58b5557afc2b89f7f69aeab8a95ab39d424bd30df569f1ca8ce471411e d6dbf4335be446bb943963f094da20a33bf52d8685e9cabc00adac471be2a02e026e2618098aac29 828dcad21828623c19b49b40d83fc6d0bedfc3d4b4777ec5c59ee543fd6edaf499eef8884fbb8851 bc76112137e822038deb22f599fc17dff2d1ef36bc0f90c9bce22793696f634cc6deb3e35a022e9b 1aace0bc54110194bd699b16b9728c3c6ecdd02b9227e1dd402d8141ef1288490f6a6dde5d77faae 76d121d8eb3c0111eb0ce529d3015afbefc7b4fda4e8b449030b5a4bc0dcb798e0fa68aedb58adc9 273bacb11ba05a434aabfb8614375a8d5de14837b68f9cd8d8c60fe12ffcb004ce3cb590cddd3be1 cf9de2a0efe967b6a8cba17df97580d1bd1942130bae396a6f3b39e49d0157b0393a6fbb77391fb6 960b68dad8e73acb464bb12f752d59bc3e87b050ad9d4750b766e524b47a537ca63a6e675a153e21 5e653a1a6f2b78be98955f8779bb4c74bbdf47879573ef72580a9d5ab99c0b0e7879746bb0e579bd fd3f3013afbd6072e54a8bf168b15d98ad77e02aefb6fe4746d3eff36b303a3ca22a3aabdb2f7037 d1ce3d98b7b6ad15e806f5e3eefe7d5a692583ba56916feb66a109cc3f235df0d8e55505bee53b6d 2b9f4b927d2367987538d71f692c60873b1380186809406a7a0720cd6b0390ae880064b06b0012d8 ea074f1480881af117df45a15944562eb66daf2d51817c35138bd64f46334fd8c1093907b6d1e7e9 5900f7cdd3bd0767b7636bb5c9a27a2f2ccf2b4388750bbb92a8e5faf78b04c0958b0ec03dda0760 7cbf07607e700760e3779f3400cfea3d003e0514805486d607c90a407a56062043bc0b20a3a2f0c1 fdf333709d0310d2c300842a097fe13e74716773b3c54a07ef93a9c43bb8ca4e6a7792d8ada7030c 9b90df2b1ee0de69beba59b9726e55fdeab6668ffb61998c85693ef6140380b554019022620208cc d900e26c660012b63600725bde01345f2f022838eb01a8d4a700d43f9800bae356007aab3c002cb7 6f03585efb661a8035491fc05a8dfb07c9e7c72585f90b875e56b7461f007c99893616e730124f0a ee0bc1a690df04f7572ad7f53af0ad29d9e0a13a6943cb52a5427939ab667d62a214e727a3ffe8ee dbff72be6bf613cc731d035877f80630366e00d89444012ccbcb008e6c020067d80b806bf51a804f 4e0480afbc31802774fc419603f093defb0bb353627e97bec943cbe54d319129762b8c70a45fed41 0d23287587a8f068367d2ea9bc4accbab09f6bc167a74ddd7f021bd5837f5cef5f19ad5e0f00b62c 1c01ecde39017873f80947da013f0fbd537bff9422ff1af68270f46fff06b30d0ce13ef3c1610c0c 117cfdc1eafa17ff5ffaf45f775a093e308f5946fe6a073f5ba1f1c10eca32aaf9792b25c9ffead3 cccbbc5229ccf0a8113dde3cb87ff835e2f4207662fa99958fdff7452b2adde9e3bb7d2f19209272 579d4babcec3b989592bbc1e86d8eea779bf7d6eff9a5ea3f0d8fcc5bf01e64759367c0f3fafaae4 bffaf4e17d4215f61f9c1e9f10b1ca07b3de2730609879c53ef7f0ab4de9416c51e3be68a2df25ee 9d8ec9e9bda449411a75ed28e5cefefeb60517a79b9826f75b6352ce5f9557bf7e49081ebcf41609 7fb62b25f3948ae0ec6773934cadfa89d77ecf133cb9ccfee29fe88609f2db69c32df46fbc3f83fe 4fadf4ff2a94d65a0fbf220ceef46184fef4695a3559f626de58f1d618cbea55c964f3daf61cf762 00ceecd20b82c5d92eaeb7677075ff5af5935b2b6749268f2ac7b73e818e797b4dc5a5c95d3d54e7 b1b16f2c347577605865a79631f92f3e811de00f16830fecce0729f66f46cccd4f88f5f04f0a78f9 d3b747d7232f84f9b46a70e5eb01c61b970447ba979e8f8067bb8063a794454627b742334926127c 821f08f9e87768e39837b9714cdf74ffc065d67adf08ac649b709f95d7457ab736e0b187acdd1e41 4599ddf80e9d9107e547ff41969df2489679c6270593a8933d1985ca9e9df967979e8e9f50ade2ec e1976ecb346a85bb5bc3b64e97de1cb99ddc72294bbc7a2177f45bdfa776e9cf5a5cb24f5f2f70e0 ee7167df984f7a3b353f81b60933c3b7bd75406decfa4e5cbbed931965b3a61f0debee694522cfc2 9299f183c5ba6ee30bfe66e0e18e66b0ff207b22ef7ef62c1995ecd5107f29f0b2eb62f6f2353b7b 9113ef51d83fa637e9084e2f7d663b3fa18abd3a16e0cee6c06f0bf12e464fe7ed6914dfb7fdfdf5 bd719057717d5f176b6bac5e6c45b371bd1f8d14165f85a509bb2a1a3b75c95e9fdf1b63161b7ab8 5ed493e81e1cf79da67f29d9431f347c66ee02063d47029efa61965143f287ecb9c877b21788f7b3 77e536ccdeba207eb0b3efab57d7beea32629ca61d493f160dc0de1d2ded7b6d6d0b76b4c57aba17 b6d13cd34eab454d4b572567f25ef2c5b0b8d8ea9bdaa271d9b4c283ff18849dc20b0d0cbd4e05fd de48f26ddf71e6f7e675394775e832f3ded78617c028ee1556063f65ab43765adef6bf7381c95aaa d17f91bd4411fc4479c6ffd1bdad89f0585c28f16a0cab74e297aef8fed02c0e37e9fe2546fef566 adcafa6db6d891b755186b8f7d709abfcec1a05979f8cea40df830867ccf9ef3c986a8cf5e0da23d 9ba51634cba1d3a117966fbc57142bc674056ac1b4b2b89f266245abba0a7842c6c725248c75b6c9 8f7b9502e79cd777f62fb2d77985fe13a273926f5a9dfe5ebf4982d1ae11af6bc5d22e4989f27a46 13e0aa0213f4a265e34a607589b18f34b8608e77e868369fe9f12c5f9f5e3d5a993ebc1272c84d39 ef509a56f9776322eef3dd49638ca0ae92d194db8e56ead81814bfb5eae3de4d3b3836532fdae97a 02db2808f09637be0ad610d9f1e63b0b38f39d4cd81fb23757217e51a6adf29e4a42ec336c1f7754 63f35820c07a1484afa5086ddbc1a5b1c57d647d11663e9d7e1f8dec2dccbc378dc2ea72b24daadb 49b3df38ba6a00dedccfc8f51c1b2b3237eeeb46c5b9f6fc9603f907d0be53f9918d1e11d17a4e43 d71a01c58d05ec662f936ac30363694c18a37c9d08fadad1f81fbe76508059ee872750d4b85f01f2 1907f278dc58f4a18de7e71a1133ea15960d1fbc073681d6e643610879cbb6c14e6b81aa4e9a4777 ec76debe3f1e34fd95e3688b4f6e9fa3c49eccd3d47ae5d3ef9866cde25ac11a6950cd0c5e4cd7a4 e62e6aac1a096bb071cfd437d874a90bef56aaeda573576b1d07a47ae42841d5ab30ff07dd6d8bfb e167a4d39656e112fa9e8d76501083d1d27dfcf1d0ab773170ca593a1f26a7a2572901fd891cd488 f1b957139d1bd937ed87864ead970505169127d766c8907b932e4b2763651aa9c10dfcb7be097725 5da49f4dbdd181204d3179526b8f124d4df2c8b7a591da53f717e5d2b55a0a683d46720a0d44791c 7f96ea88fae0fee267a42f63654cc7bb4a4a6e46dc045cee857d3b1c5cb2f27c9ebb3c3c36e09f93 d676d21a5bec1ab31fe99ab5e6ebf577516886b7d8364bc8d53338ef1eeadb7a61a33794c2413bb4 9b67aded410f3519b239d5a8cd6aca455e77151ba90d15309324398d324f46074a2265295897869d 3126be4f0fe10f7c61c3fdf0155ddfed2fd4339e8cd9bdc918a3682569d0420f571d7f7a4f2ab352 699b4d14ae9c8ead98aadb5e9e82acfc8c25cd52288b4635310dbd71d5c69a0a9b333559cd966abf 3adb2897ed3a566ce9faad8796d307f094dd69b728a39cd6969ebd1895864e999300ca75c5a0d4d8 8b64f2ae0acb3a8108cc7e23084c18f242991e737ff1a8e83bf554a8e9dcf63122472b7129c02198 cdbb73124bbecff49a6e878be7f842accfb637ba95acfcb3d83378a588eb92d7e5b40e55915563d5 32956b0f9e28908df9b27bc796323a61b7d2aca225d27013dc25c07ae4c5006ad545aaa0416221be b302db832ca1fcbe7cef8ae0d78157e4051a44f85a6671dcce10ff8b9b8eb584a41c58c266aef447 4bb5cb22016a84fd19b339d4276ab0783bae3b395985b60e18fc2e6c69311ec2ea493a7e66d1ddcd f78a877cb78e9a8ce51f8e34631e5309583f7c31102b914881582c1652e12ab0cce42d543ab72abf 3e77fbbc309f507c9d2c18dcbee9af39f9c8e7b9965182d9638d66592dc0b9ffe03b1db4ce5d29de 1d39614d7be5d1c29853a83f02e6034f00b78d5fed860d085462084031d5e2295e55074da2afc098 3294314267a511a34862b0d675b158b71c8155aca95041360b7e537aed78615dff4ea0f8bac93eb9 fd605ae294dcabcbb50e2cc11ed55863754c5ab1dd77fbcd9ceded80b15a6d86191cdb1c335855d9 bfb8a0f9a2b43f9325ee57121f3a8d1136a7e42938950f87d64f467fbf37cbf120d15bcdcd591d88 59517e60a5b6346f341031ecb449b16882bcc0de7095df2084c5d7236ec2ed792be094fe66c3b5ae b7237b0cbb0f56a7b902db6b6fdaccf9d91d32e0c854e8b4d10ce9f1e1f0ed2f4023acdaa311ec4d 538f73c6fe07e7a15b517648e7c0ae24072083898be133f63186263aba6d3bd87b563084a47552ad 8a18cb53d47a4b447d51178bd97620548103ce8bf2fbbb5ae3f64949e614a367b2c76b6fccea01e7 b33dca8a984b313c30a07ebed1e9b09da35d406cd0c8ee8c52993292280fb9fb14fe7453f26d0dbb a4df58901f5c3892582c98bf38154e39f5d75a5f88b86527be53febc0c0f3d91b360d75a471d3b0f 3945ede8df12f991e6d7527e003e84a8c45778f1aa76b903eca16c827b246b542f227391528db15b 25874ef5aa47bb38bea491b7b8a3b2edea3b0a509ef47893ef075827fda90591245f11c8426de211 8b3d7a2118066811e5b24a10a5a7c712a593cd8c2253a27f386e6a0f6dbd087d7e31c0aef43c9c61 c454798fd1b13bdaf6acd23da8a89ff1f59b691291246ba1baf213ee1000793639769acce502810c 44f223fa5e70391a653d99f2ce91490dadcb8402a07c40faf7de862cb0f4895856c619c19cb30a51 b6f1c188bfcf99516d05bbc31d774f8632107c5de750baf587c3e69864864d05a5ffe2a0eb5335aa 792a1f3cac989971b7013931ec29ee0cc3edc0ac69cb9a82b2b7ab105da30dd791a02d7365dd277d b7e22af574af5d6a5429a3642076be053024d5180ac4d2e53582456d87283fa3f948181d56a37aa3 140f7707fc3e6c75d4021e3fd32eaecd0912ef12371b3397f21e33576c0d1be0270c1bf4cb3436a8 01d45f7c46f59ebcd402e63bd8f8f334e63c498169d78156844dd117588f5fbba60c9c5f775ea60e 7b06eeec426aa43ccf2435eb02c4aa2e3788ca38004742b6c64775efce0c951c200d5b61d3c0f536 e4e2dd840fb0b3e16db1c175fb6d408bdec226808e69b5893ca2cfe2666a8a3a8283ad2dfc3a3cca f06ba921f06bb2a6e09716907fb15ed63339bcad5071cea0b130ed64236e8c4d569455892ea86611 bbb654dc0ede5cf7917d2fe151443d588c36011c0df78fe08e1fdffb02de93734dcc3ad6071838a5 86e8f82d3028e23b12927da60208be8c27f09b2b2c60bf47c670deb61fd002ba57a0d26a08811177 9140be365e82d590cb835537f7ed030556558c00abcc60f417ab7de9a206f8059267b5f555fe7514 71f2953d6b36ccd248450ea79eb025e902835c2e6722a2971176995b531445f77bc4db54ee08d040 0a30e98a0d68f1b4be45e410335b61507974a341fe5094c09a3a3007d269341d34cd49d48f07f773 bfb38073bd13b3ecf6fa9b0ed3bd8a9759d769ee5fdd2bacf4bad7d67ed8bd1657f85f2cac60a1cd 974d549b6ab7d3771418677d47b522eb2ce88693113269ce7a7c47234a54d04c6f43ad36d920cf85 3e03d7abed64203bf96dff5844aefd2ec7bd7be7aa57e90dec6dbb7b83de70d779b489ce0347f8ce b46a191d6c7b9cb5e7ade2b77cb43d3a0a692bc4cfd5160d0878734597ed269b65d7e6aab36db58a 2c88b58a581bfd8b60a8fbe69f66e1a05bb61c92327553ce3f6415ab2e086117615d6612be8b8430 f76f18bc12be276988abe3f37eafc9d8ed773b30dbf9e1336cd1fbfea15552a4b4c965b35cb3ea9d 6a8deda8d06d34c20e5a3f282a5b57db5bbd9698f959cd1870fbea253cbfab5099e856d24d45aab8 52f95049ed6ba50aee78e8db4b150c48f02fe6ec670536d5cfaa3bfefceeb15d7e04b676abeba2c4 9e3b3837b83f3a54f1ea15f0ef8362e045a1b5ed2746d5ef0cc1cf4ea85103a5d63f8cc5ca643477 2a58fd1596676afd7b35aa3cea50d732f09c03256ab4af1659fadc2dacd715bc20088298dfdde249 5e7687fb5cfcdc01b92ec140c0b9d033735d18bbe634f956c9c561abf5179e121f3cf70eba338712 20cfe82e5bdf0b2b4aeef2e6fec8e85f65b401150084c477e781ba5f6d3a33cc9f37ebb5a55d1b88 37a59c1b97d8bcc2f609c0726b020032bcf5c1d60740adb7ffc04a3fc88a0038157b1fdc4800f484 efc700e0acb002c0759401e0c6e803e096560170c71e01302c963ed03e3f1b9be00f13fb7509c623 77119a0abe9bfc0f19ed5be7fea8831b651450c86ca01195b8333be697cdfaacfc9590d5db05b34a 744848b944633e833c58feca639405206b6b0290ddf601c8b77600b47da50074e58a000cdc7a005c 172800060bd607de1a8049e205c054a9ffc159016065bd0160755602600d827f829845d11f5cccaf 46964877e61a32af58a2846d79c63bb2e828a6f3cd9f791c1cbbe6b533a7e86d63ff64822ab215c6 c55a8950806b57fd04f6b27900c6b6d21fe10baf4a1a8014ad5f9f5b04a9ef014471ee00b2a89401 249d0c00b4d96701143a8c019452f6004ab7f300aadc600055671a804ed42d804efbb90f92cf9ba3 06fe83cd99de5207637322f132a8fc3a8910f248fc5ec84767af6679709cc48f5fe976a3bd961695 21294f0a4d8fd600b8bb100178f256fff5d053f79f02e4da6bfe8f5735472b005daf13007d517900 c39e5d00d30d12c012d002f0d26bfb53aa9d1000f02e0b0238de513eb82c017ca8a4002e34ea1f20 ed1f8cdee53e9719636a704e67c8fe9a6fa3af89541f1c1f855787b81e4ff5e4b95897036832cb77 878a09202dc2fa05f6db69d7a3f76f58ff7a68ac136c018c89f77f8a7cffd1bdcc0dc0e54205c077 110a0ceb9c0c0cc1a2ff6f63e15ff386c2e88385050c1124fa6073fbe096fd29edbd65ff7ffaf435 cb3e23eae183ebe77de4a8fac172906554e1f3666ac87fe0ea7faa7abd623acbf0d53b7cbcf9ea67 ca5f6bef1fc4eed7befd9197c5c77dd17273f792ee57d2a897f5d2aad31edeb6f05cbb35a637efaf e9fd1f65bc3fc3fb7f1b25fcd5a7ffcb9dfe8a91cf933f2d26469b0fd69fbf89ec16fff499f00a3b e4f166afe483d8dcb9475e2ac8773a2e7c4b659b4e1a75fbd3943ba37e5ab5f8e826a6fceed670ed d355797af74b328a0b97de02689eed323f3a83eb83f253aa8dd2e4afe43dbe8dad77f4fbfef4485c edc90fffea53bfff81dafae082feab4f57f6bf85d24e9279f9ebebf159f795eef4fed64ca3f6ab9f 56cddcd70edec45b71743d2045fadaf672dc251956a48b0174f5f385eada6770d59d9cdceac04f32 09fb4c4962e178f4bbb32c5e80c7cae1736a41f652be4bfde4ee36112ae2b6b73bf39b8bbce1fee2 8f87fe4549d63e1856ffd9b57f32c2d27e29f0783395751ab536879b78595dafca7df3bae8afa870 be1087da296516cd13b25e74934c384009bedfa247bf1d0d8f7923a2e292e37d5704fb2d2e09bb03 c9abdb84959c6d6fe3051bf090c46bb78be6a2cc72dbabb71ba12be2e9a2abfc4c45968b1103ffc5 bf2196731fa0957fc3fea500216478f8eb739b56b5d4b9e84f757a06436971422275777ccb4c122f 7afdcb21829af7bdf8bc673b3517bfb709bdcf6f7bd1a6b4b16b9bda3a9537ad3572dcf723fcb247 573ef4a057f9695d59d26f76bae00bbb38dc72cdef158fb0b1a56a81da1c3583763c68f88956afff 4596f1cdfc27b60290651153ce9eedb49765cfde30f3ea2532e562923adb804625f867d914d3a797 ba6f7863636b1469676357096fed36916f7bc328d39055849f91edca27f078955fe0e72553c6d345 64d2af053f308ae1365c34c366290103d56c507e721375bfafd63773a73c7bce26a357dd7bee1f4d 6f36befd83117dfe832c4b98d2afcbc84f4ae7dbddec05d2e0e7958ea735a93338db46a51d2fd476 7b775845838d1d7bc3e849d8ec2a405d65b93417f6b252bfcc16753a5b86f229bf0d5bb3f631d0eb e0d53f2be0c3b710293fbf45eed772cc919adf993d6e797c86d3b8e0cda3b9ebe50783785a525685 09f76cf5ddad5019b852edd5771bbb73ef2f3efbd0ad662f821efc44ea9d1b5eba17abc3e64e134e 7ec4659abe6e07bde6b79949346787dde5ea40e28b8640b24142338a7f3124db8700db9bbbb217ce b2a317cd86f862efbddf8793474a97d42bc03560ca4c9ba56999e3db137e6bc3ee0ebab0aefc392c e398db6f9c9343e79c4171ffad1ab21dfd0cd9706f035af7cb7cf017d933cc5a3f237daf9a0df0e4 096639e649f3b9eb7dbea51b04b377abcf12165834915333e83b3134bfeb7772369301d10ba605dd 2b563bee94953ab39feebe438b491d83d6eebec2c76e4b942ee323e865e3aebbcc3b671668388373 1fb4c7204fda483e35aca9c6adccd7a5f8360952ef18e1720a1b34ab4346a9c2817f91bd3673247b eb2df2d7cce4de6fc78231aa6e61b7f28c82633559ae196813764bd0dd871db93a1b5d849e578c9d e1e4935cacbb3fcc14b78daeadb13edb4d9c4bfde43ba0725ada295ad8d9c8ac70b432014a2d7c3f 7c9befb1f36d3a6712d9ba6be6d7cd91c10c0c558fc659a0f3cce6a96d37bd96d66c70887a38e0b0 aaaa3de82fb2379e1b5d93c7619050f35a7707b2af6ab4a83ddecb8694bf04e73db09e4fa5f8ecd1 f3d7f7dea8c916abb45d15ec22e37e79403950bdcbdb2832502d2f1ad916501b4d4c7f27f86601d6 56c6e231d919656e79d2d7d5eb43af399582b683474dadb99a206a6cd44455eb7b33e51412a93228 1deaf2750da25fd1e5080d4486eb795886810cfa217b4f07d405ee2570dc6080fe06c8ef6b4be998 e4826b27bdcdbde973e731156a376976ccd7d8b8adeaf69d0ffbd6d3d9e316b03a3366617b140d16 7ae8fa7af2f8168ee9b5d763a2edb6f5406b355b911adfa958d526da55395782b7626e5e35f906c1 a03c2eac39195e41ae34ed656709bbda55710ea7b0383a1efe8b0723bec8d3cb4fb19d2d3bdf848e d872580f4fef43c1c784ece12da5783f698e1fabf12037b8dbd8102959b916d136966f1ad6374d91 d0f65d9ad15a734552f5bcaeabddc5d451ac4ee82983d33e94c7a3db4646f295447a1ca84cc23bd6 77b0115fcf474f9c6f045a24c4a7238477fb20d053b42c947a3ec8af2e0b84e76c17e639c9807eb8 c5230e4fe8e501df3c75b9b7dcd7c78d604c1e4ab35049b2697db28bc766b459d8d8747f36e942fa f56946e5926f68fb65beaf1e37795439a7255201d12a278f670d594646a0214df7f858c2156e26be 91792412deea20e6855c2a2cea485ea06f61878f98e288e7cb8ec157cfe896dbda8522d788c9fe07 c6f724cd1e5812fa8bab1972a363f5150dd7c196ed2f745a6ffaf8242a7b5cb47dbac7f3f2683f02 2b3469883ee89bb39e696dd52dab3dd36f2b60184132122d70c9eb1f68f1ed2482483c92ef9443cc 4f3353606aa58950daf5023e72c42dcfc3f3335f2be5016e9b704daed95f616c9c1b69ac1a3e23b6 c32c73cca9d2ef31c61c85ff83cb1811c9837a50f168055183d04a94efa58839595954a6d2f0f01e dbe0f264112b71a16f3bd94a6b1f7a37c51e4179196da37569d847bba2ef90b05828aa84c0702ac3 475b55e079c955f91a7470386972997da26a446cdce31256bd844fb61354bf2702e6248f51c66cd7 15fa6acc16b433e0df3474bb7768e8d084a9bb5a80fee23c8d386ae7087d6c25b28341e00ec5f68c b1fcea44bd9e728e7b585fccd29b5e6aea68112ab6979ca4672bf95ebf918061a12216a8424b2873 8501cf6fcb28b7839a04d79c341936c65089d52aaac97636fe943959e99231c95ecc0c0aca83bec6 970a0de3c82788f75ea426811050180dbdc86716b4c8e7f3b3ba9a19f157a6fcb63f9c4880a0b7d8 b88c2d957317f4bd1dd7f1d64454774d2acedba37270d5774d70a95c1166263d65f520926f3515d8 6650e0856e50e36462dd65e3c50e62bbe518674eeb27cd9856f35b70c90c4054a76f796342c38c1b 508fce65474dceed1b85cddd12f922de7d72de1cf3e4e838989339a39011614d6b12d46e0d115410 807f91300ecd6e72ab3bb6308816e4038ed29beed2edf7d2f7f8d6ba162d6ab64c358d6daf64ac56 70450aafadf9f5bd7fe1762ff4c51e1b4285edaa5a8b314fce80be0d26283dcec514f5a0638e9a96 9e0a859dfa0ef9329939391ff91b9268e4af44a8a2798246b7dfd51a517c21cc6835bf4d471cb9be 8f2a8f41fd830934dce8c2e02fe21dd760a2d5fb848776da45e6c5963e981c99a8e53c56f7b2c90d d60f7530af46528e7c4ef80d18f85c6b1dc76c37051ebf9df6ac14e8b10037a8c71eeb52789b82c9 57e610e4dc0b58320f2c2522dc3d2d82569a1e5142f868c4796e32aa0a0560b8ad4beda198e629fc 805a63fc8069375cb9e46ab8b2a540bc4d76fb3f7c0b2ebfdbbd7938d0abad140f03ac5c446695a9 024e7ae8a56bbff167d510f7bbe74f460b1c7b9ab2c7d3c065c0cf61a011c3b9501e700448bf782c 93445269138b7e0b24e82b868f228aa5477cd1fca6c0a8ba0af4a1d43b4d868d6b79811f1c32c63b 45ff892551ad8119863f422fb79e855ed2d619b5d5b082da6c6580da58aeff17dba9e9d2cb8eb61c f9a35905f31a3d1d76c153dab70b40f92b887555b901d27bd1dbf352afe033e0f56151c35a6e4192 f0e048301cf5184517ab30aa41f3fa707bdf77874dec0ce371b542e01da9ce62a726a660462cdbe8 150b7cd47e3d77c85d825264d20c2a087aec7fcf9ef0ac1769f0ac1fc6f0708f14e16138ef7e10f6 e0e178dafd61bd40183abc9e97e4bc94ab0cff14713f07296895834e5b33d14a515c769023d7f16b 0b6a563cdb447922bac39d385f0f9be9e35bd789779eaf2776da80256cd0c01be8f5a0f451b8a323 c8fdb922116cb4e7e1573ea7c3b3053c85731d2d8282d3e90215c1561e5ce5d720586187f260536d 6f07ebcb1618acaf487bb0de219dbff87ccc6a578398004b2794b7a52b847b85a6430748f388b12f 76baaaf3ec56c40a33bd31b76627228bf8cdc5b57da2a1b7e17b8e3c94c6675e98d117f8e58d5f30 518b4a706e973621ba95f5a062d24041aedf237fe61650a481484dad41a378f3fb8a3138f4dbfdd9 aba78f2bed5e0f5973ddf3d308bbe733f1ea9e378766f73c3fb4fe62712e76b8f9a2ec30d3b80d33 e347614fd9c5456fa81f8f2428bfebadef08cdef4fe183c6d9fe7624da0f0f1d47be0a85efb20a72 77720e5627de7620550ee7fe414cb3bedaac157ac904aaf70c8ceff57a2f17eddac482ea42f9b7d2 7135d06d676765d5f6acc7adf5bed3d596bfaa7ccb795a241e4f5b647f716ff9025a6bf9a361e32f 82c7bb26ccf8d9529898112b38f3c98333b7284169e07b084ba5e3a3c65ef6d327c910cd3ddeef6e 663029db7a3fee985cd729c172e76e4cbe3ead8301f1aa3da3ee87f6a8d4bcb502beff6a5135a1dc 2a5c8376938563b859ce6a7443180ed5fa6ee7797559a91e6a716200b5eea00a564fb78759eb369e e75ab720176b1a34ab7ec656a7f2835f28d4be35b7d3632e56c61920c836d3ab48466704d20ac002 101f93618d9e0de9e7a8995cf7c8f3309e83f59e64769d9b20b4a8bb4e34ea29ced55b4fd1a81dd7 9b59ad573fafaae74325ae829deead727b2ab90a329a7e8f4d39cbdd06659c6991a57767a297f283 935f2ca1fd53819bedaa852ae132852a642d0babdde851f88c18b9c2ca11811f66f56e479f80f4dd 70fc60665835bfa5aaf7554a8982e20c9847a35d2636ddf43b7462f051db41951b32ef8158f3eba1 0762fd20d254c59525a458467748a136ba3379695156f24d6638cea96bdecf75047f0b18fbe402f4 5d28ff81df06fa931ef1c1c600fa533602fa5ef3fdc1f32721fb663a06fa5698007d29f7fca097ff 61aacf49e7f7ac459b85778e3e10a692b4eaa2c39f8c2657815bc04101bac2252db7e95decadd726 e9d8acc7ddf3d743e7a9c27a71858073bbfb9ddc02038f423fd8f19f2f70c7fc409d018353faf93e 9f91eb078b02307883bd0f5614000298f5c17d0380b9190080790901c0266d0360113e7fb0297cf0 aafde0de1b86e7046bf2bb5a33f4026cfe95d1bfb6d1c35e9abe614632937edfc0976d2a0f4dea9a ded52ad89a130af5b8830320227ca5f1ecf331e11a03c0439303c0d4b500289fcd81cf94650b40d0 e60640d4af3809809ce30080c6d427b91680fb81bb07a0e5b00040c7060640ab9b0d40c9fa084027 b9fcc1bafdc31f19bdd7e3898ad39622286987a07d71df1b1afeb088148c42da1f94e6db7691b0bf 2523f5ee51b3cbf3f554c92b778902a0dc7d084062fff36a4e320074563900ae78020077531d8071 6206c0fa7e0bc011960248e9560110c6840164094900125fe600f2185f0024436a005a2e9200dab9 fea9b9ed7a6700ed41e50f22f0076b13a2be8658395394ba0b8ef1562364a415d3064221fceb9333 f5a45d3ca7abdaa5b89e9416a3b996332636fb27b03f31fd5cefaff098529c9f520dc2c93f5ef58f f31dad01d4bcdf0174235700f45d80000c8f440033c43980cd5a17005bdfea00760e28007bf32e80 d7dac70f163900af971a3fe8e05276259e6acbac7bdd7d1b6610f280eea2b9c1a9d03f5bc2b55dc2 7bdb9a8d00f3e23ab7310107f33fb1b189fc4f6cb235fe7f6a8eff96f6fe75bd712d01f0a6fb0270 99fc2534be2f31c0b07e9c0043d03c0243082e7f70c7fe6dd3db5b7e303f7ff07afdf0af3efd9ac8 3f0ef5e7ce44f97f351988ef7f9b0cfc29edbd63ffb6bffd55f5fa66e615b76e86af2ef3c79bcb16 0fbf965f3ff2d27b775fb41aa73b7dc41ff792fe4926eee237d2aa93476ee243946e8de971725581 4af41fb9fbed96f0b711ee17ff4b9f7abd7ff5e9afe3c4c6f94439587d303bffdb67426e66f8d219 3cfcca0cfb96c090f74563c7dd4bea4e4aa3ce554fb9d3f3bb92be6d0777f726a6c0ec7a40816f67 84cef6928ca0e462e48ce7f942af2b67705d834e6e5de5934c89acffcadd5f7b84054c8e7ff837ba 9f98fc39c92df4a7c5c4af6a7aacffe933e1e5d9c3c32fcbb7fba26e0169d4b6ca69d598366fe235 ec5f0f70085fdbd310bfe8ef3971be9053fa6c1742fe94b273e9846c423df11a2be7f856f7b32371 7a44319d16935f95eab3fbdc3742b1b94df8f1675ebddbb31bbb356737e0d162d6a92ed2bfe2d91f fe95bbb5af98ec7f8eefd0edfe35e80f22c2cd346a81de6ddb23a2eb01e28e17fd85ff9ee361e791 ece496a15c920950e9f8569ad52391d41af1a25f6e1f22e8dedd6fb15d7f772096f0bfa5b24b7a63 d76369edb6ef7694999d70f51e1b9715915d8bcbc5a8d95996c20eb8e08bcffe0fdf32b8eaead4fb e1df28bffaf4cf2ba6fecb88c79bba90b78655202f7af614cf60f034938cbb4f8f7e330ee2923559 edc5e768bd6b87f5edb617e50e1bbb963bad53e9755d23f1e3feab20ee3e5eabb795e55744faae2c 1758a9b52ccddb9f60f204114a65450a0ec2711ab40fa3a36f7442607e31f795b93d08cb73f0362e fdc5bf21fe45a7f8d3fdbf4ceb98cd534aae5ac7b76881876880e03be59d929b0bbb60d76ec39522 af63682b7f2059abfc5872977426cd16d1900f16d58059865291d9848d951a076ad5b8f889e4667e 2f5e16e676f7da9ca576e73b879e217755f03cbc1a4cdf3e779c92856936cdc7e36cb29858ff03ff 334a0f2afc70a737c3cf71cdaf9278d126eebbf6dc7f6ec0b5568cf004abaff2cea0bb88f0fa37a1 c3add41a0687499d0a3a9581e01b16acf87d9032e7f6427067f78e3c9bb9a7c9d27b8e963b6f96bb 9e3d40adbda6e493ad4d96c21a9e940f4dc11598cb62bcef5af7716b93149da375283a3ab82838dd d4fd9adbdff64f945f23fd0800a874b68fad3801507c7588f4e1746bd4033fca76da76b9789bb770 c78ef38126ae1afec07d4073f85c1ccda6448bf55e8781e4119d81360d67983da547ecf4931df2d7 aa4faa8819b99b2c38b80dfe741b1feaf5dcb83d669b8e515a63f64568ca36944bd796ab7299993d 17157328d8e53f00ea52e987cf3e95cad9737c695e06270c38d2be70df5e7ad9663d9d03dfb9c02a 042af642acc2eba0878dae7337afe4bdf74c6f78f9fdb83f2d65537452abcd49577256dc382eeee5 b1baba19cea9f71a3bc6b532b3af546b613bc5d1cebaebf2d942dfc1cb7c06b99a396a51b0114ce3 efa2d02802d24267c7a5bbb6cef8aa2678c38a561ff6cb7f913d0ff9f6adb5b997bf2d198bbbfefe 9646017bd82f6bd1350c3bdda3e543e54d381b8e1fc994cde55e935ad6a88ce35da3ed9c1eedafe5 70061c3eb49d0b4d5b0f88e62d6cf97daa166b99e6e83c75cd9cb5f10d3a7f8e8ce2b272d4b92ef6 d03673aba489c46ba0ee63815315bd3c538eefe8a6f4e44e45beb43a35d93a96abb2b5022a3ffc4a e2097a70810ae5465c072aa5f57cb8792c45224982b3735acd317735f628a4e74e849dbe1f1ff5f0 e1586454b0916e54b770e2d03309f5001be129191aa5514aeb513e2fe855b5aa6adb4ecfd21a33fc 3be5500f2365a1b6f7d39d9228e7abd27bb6f3f2653dedca50234749691ab812caf217d1dbc42571 285e6be29088ab22905f567e4845ca6e9d862fa0b5bd6de6c5550487cfd06092cbdce3e28d47aff7 df4bdf93fafb628d7b5c23b2dd027231df73fc6de60f7c458f664c4bafd5d581262906aa1e1263a4 76862ea318812b287d2ad2e46b2b7564487fcfa4fb10594be84e39894f79ff168759bb2902d18a10 0261f43dad0985b49cf04b7752e4d94e50e3cb27a7fa17b7f6d1eb1e57f56767831facd2b2c9cddf fedd3cdc66057bb79b48e16aee5c96ae6e65073f30493389f5753b7d68bb51a1a0c64aadae9c8cf2 7789abf4af355076a80e26dd571d429a70282b3e2fa424ce6cd51073056f2204cb6421507a3be697 6f31e3593fa9f11599c339e1f132b8bab78bd9fd689467e59b5cfb83d698aafe70b9e28bc1f7b4b6 4fd69d687112cbe169ebe7e6b3dbe13edd144eb1db6def431b3ddba65930fbae5ecb988d1aefd48b 624e8d97ec6c2725e9d19cd5256cb2ee8ab3d70e1273c40e13a8c389148aea93e357dd9ac2b32ff2 3b1de436a2e1736263bbe5eaf776ca2aac5166dbd53ccc1c2f2b8dd11d6dc7f4900a409f33aa4e5b 46b7fa17e789ed81bb8b2cb757c240ac06377251f8c9e8c97ef93c393763bfb472a0fd93f775f864 a9dab5bc946f5c39961e76ff2ebe96584ecc6de0b2408354935fb96c8faf3c5988dbcc349c138580 66f7fbb5c82a2e60b26db4ef3149d95833fa29bdd217022dd2767e0bd260ac2854aac3dfe224caad ee5e14b2abd77e2033f251f9e1346ffaf016e5b0f6520eb99aff88fda217952e2f37c1818b3dddad 5606874a8e7ad467ba7c5b2e7d0927763b9150ef578136ef4f9e1bdff2dcb69cfbdeecc73584628b 55f6c51edb565a30a367f088e94532475ff8994adbfde7848672e88a4a43f744a1ed678ecc9e469f f4e66d911c12af88040a6646f89373f51f68fbca0f09cdbac8667881bf99b6d0db747dfe6617e5cf 809fbcc797cdf36af9d466ad8b27d5554069244bf89c9d0a8b0ebbe2a39193705bda49d9833179b3 ed6b58620cf8b3e2be4cc3166d733790866a459c4aaf3043b981a690cfe2ec9b02a4777c86e4d040 631218ee0022a80fbb444149b8d1b2335f8c9615ec3e6236abea8899cfff0f5de7d9b6a8b26eebdf 52e68892a3244190288aa02826cc39feff83f69c7bcebdd63e5f6efbb2ed7eeb45a09e67306a54e9 1feccb3d9d5ae673d5e6b417f2b571f61495827a0764fb7701dcdcf8b149ac66551dea2193ffdea1 3bb1f170d54a263f51ea0d78dd6e72cc594645e62979733e23decb5251f4cfbdbaf022068830ca6d 68819b3d0401d8b0c64f904e8f173ecba83513c1a6d586f457abf8cc35b86518ca5ca7a54db86ab6 f4dd378a5dfb5a915d9bcabfb0354d9a5a94861032f12b6c7d248f179561f3f2caf5997af1e12ced fdc6b84842a009ee5b572b6a60b6cdc73c94ddf27529f5eb8fbd38841f57e115953ec248c0bfbda7 006678854fe75c9817e101d99a8da7ad569b3f29ad521d76b8e52e9d80aacc3261d79ffa9ded26b3 1a0b753981b1f69f80b10e9b0b632da90203b71affc2ba5f05e46cb3ca20d10b68df53e0af449169 aee0b56695975355defbeefd298f3ac5ebdb685b22ab4997441a88c3a13615dea5f146c8d4c6275e f43769cd4d3fb3adf6e8536a9534a4c1ad6a3cce557d8d65377420b346f161317bedb7691063e3d6 92816f972bdd6398128d975e1c754de201351095233520cfd9149f0235806ef91f56effc8e8c0f7c 1e8b405781836ebca80f0654b1d49398fac76e544a277d8c7263b5ea976ce9ca3cbf8b9684b77cb3 f9295f1ef1f9185fb44a4776c7ad5cfbc6d532c187dd4c9302db286cabccfe984518042b53f4e94a 09746f9a9e6ab7c2dca3064768463eb1ce910cb39902c9c6439af8b4851e31fe1476c4f8ec7dd77b a6af718a24ccfdb094ca336aea0ddef868b657d0e1de3f37fbc1b35975178c98b390357cd56260c6 4a43263d3148b26a6b7e95552e29c403768b1ea66ca3ff4c18a7503bd1671579d05e45f83e5ba36e 7dbd44f9a40f51d423c1c9903d73245769a844d4e51d4220e7111e87f91d2e73bd0cb6d854714c35 7626a6caf1065319e48da93097fd07f384983013faf926c395a2e1fe99fe7c3b022f0a69c849dedd a249f4c5e7cf76dc46692b1004a2acb3267be4e9f3ee65d084c205949fe833f245f81b921b462712 30872731291773444eaf57f0599d83f1a22593d8928925ac323e1b683757fe9e02687d3748100b7e 3f91e679d0848ffd96061f0d74091fa5c523c52b031fa90bf8213e965c36ca74f24c609c6d6ae08f 3e786fc6c9b0bd5b8daa0623f680ba9edcb6d27dd91fb79656cda42fd1f1abdc92ad9a2811b99765 e1edce32c0969b638c75fcca06abd2f419ed8e84270a75fa39c4da8c2a086c1c11d83d82ef2698b4 d21c64fc5ee321dde24650542ed0dbfd54a1716d2a43e3fa64068d1ef5f43dd2fc3a53a037a4be7f 9892b8d51a29830c3774a229d71ff570c65d553cdceae133a89b99f7b28a0d7d0e6266d0893868d5 7028e67e91310d9d5388ad4c65f8747e58304e96fce6f5414e9a94d2491acff2f0eb556f04c9fa01 7deaa70c347e405528cb32685d8aac56bd201cbb35654f84d50409b755ed42e72b6933cf55b68deb a85a93fbc76a8d5e3faa9ab9fb8388a72d31ac3720d12789bde84d7985773697c5b7bf31297fd6d4 ca9e9293079fc3912f3be294be491f97288cd70a92dee59906d73ce075594af87ab159d66baa8bb8 b54acb48eb80696f56ad4bf34d6557785c2acd4ee95376ae6cb98c0e02ace43daf62f1dec2be014d 457a339b155ea6f02c70a71c5600dad32b84e7e9baf0aae42fffe0cf269bae432afdf7eeadf4945b 4fb6d17ccc767399764339bccb5951b0fc237b84e0981426b18beee449a7497f06df3d246bcba987 968ff735541cde464491f12f42615c2a6a85d68a76f3e2961ee6e64d35ce955c6f935d61875bb6ea 57f299ee4b6866a0d1369d11b28803e078b304f02cc80278dee7011c0a43003bf122c5ed97d7f97d 0d769b89ee5368b7ebc54a5577eb72a01a2f5d663a5d340f49af45efd3eacaf53df5cece27d8a66c f69a0f8951eb4a89e22a08aae2850f6ed5b3da635c06c84e69a458d100d997e414edafed1a208ff5 1020cfe622857702c80b6452984d80bc0b628ab107904f6b0d904b29fdf0e5de02c8f53c02c896dd a498de7f18e2d4d1ee7f2e13a7d729b76c8b6c94656d795d126d72ac7ce56e7ec57c9ef463ee6ef0 d5141fc3831570ea0be2a15690e78d2b44ab1796ad771f55809eed12403ffb26c000c602ac325453 5c6c8061dc2845b402185eb8008cf7f200130a688aa00d3011fb192e31f1b20398312b014c764480 b5a5f41f29f9530a23f3c31f317ad6bf0c6c7739ef76f3439d577a621511e6eb28cf7c77e32294d7 6909df767e505f5c44b382c64abb20220293b1a6cc1f1d7a516a02bcc0a0006f8818c0a9110b70b9 f875210b0ec0cdc508e0c34602f0ddf806f047a30888fc8a4c1be19696e23e0244333a0302566b80 40f03620d07c9462770704c6947ef8766b5f3ddad90b62cfe0d1b2fa8f18cd7a74fc2216bbd60ebe 0bcf497de9acbd723f0eb47c29525ba0677471801b7d12e0d7230b881a680182af0b7fabbeaa0288 d5c202c4871aff1cc4e57102480ebe0332dc9401d51c718062681b50edcf0250f6fc0d28474301e5 625d408d73f314f1135091dcfce1ff2b4693456f76464854f9aa83f555b11e94a9ecc3ca695128fd 35b69f29ba474bfff61e7f355ff2915e237f4cbe5f71959a6c4680cea4e7218ddd5f80eeb935409f 891660908f0b186eba024c47fba525303d8c004c044cc024c902306bfe9ee25cf9c11a9caa3d2d69 2ffe12a3cdc7a14ae59edc0be93f37dbfaca3727bffc90ec363aa8ff1ed857eb25f735fb3facbdff 1931f18deefdc7df3b432f80cdbd2a80154306b01bcd06e964b9041c91072996f85f31bd5421fa7b 0bb902f8e1a794fdf175fe16effff0957b7fbb9cb53683bf43066697e753a866ff8ee9dd917f67df feb664f3ac273b77dcc747f1078fa81c850f3e49a68fac7e58dca5dd617d2fd8d9afb5f7b640f0d7 adeca9c56b422c906b2d68c817e363f7ce07611fff23f2fe5fbb9cfd2d9faab5bf35d4059e0e1191 ff2364e08fb5f717d3db2a3f3eed56e3c1afdaf83dae75987bc1ecf0b745d36ddfcaaed5b92658df b86ad79173d952a3fec5782ec3f3815b4fcfc8e4929cce52fd7cc2172238526bb776081b397eff71 04f327ee7eb3547fd2ee37e776bb6073bdadf24e1be52ffe1ee04fe1abe7fe924f7f96e93f1bdd3d c29f4bf6c12fc9e3bd60608f5bd9c13357edc2962ec603ff2622e0cd331221e84f52cd3588e3ad5d a38f7e09e70f4f0d910eec1651f7511337f65997eded0a033edce80089d7665eda274e39c9ad3c28 0b2f6f36db5a52675cf88522fc5718c2dfdae99f53e0e796fdc9a707eac1cf17c2bda027fa3581b7 de658bdfa2f3817e2e4f5ee6b03d5269cf7d60d7e175cfef9dc72eede8de5be5ce808df686b26b33 9b2f240739534e9065a6bef2aa1978e9436fe2e75275aafcfcd3873b73fed1f16631bb9ec70bb179 8fcb73b73c4d94253cad256e63b2d53bd01f7c9db3ff2d9fa285273b911b57edd86f9eed679b3ce1 938e7808cbad6f1bb5e777a4bd55ae6f6f6d82202dd9e65ab0c2137ebcf4eb7cbc785aece22fab2c 8e6fe7591f3fcea4177e8d172dfc1997a75c76aa17d4cac4ac984874e82e5a11722818630f0d66a3 5bff711af914fb2d6ec367d8c884215700ffe0bf879a03d7a471b99ff0887cef3f5ab6b05da07135 39084d6885af72d8e269dce97984eef8594c2ee578c18ed4b81c8dba533d37fa961c936d7bd89b34 56fd4174d026a308d98e2663af39598c6eee723ba2aec74b18d2e0137c4664351032437a18cb1573 2817a3d85f749e075fad7ece7e79733e0d122339fef01f43fc99c80feefee867a9e92e86e2d9ba31 aeaf9737f5789c7fecd17d26dd6d304d78be30d9ca7c2d3a74a8665a1923d8183f20d4c8471bec88 ba50621892783b641fbc1e442c6f05d988ef0d63c10e878559f0cdb3f1d5d226ada2f5fc7350db29 d5bea9e405eff0990c3cb45b59f52e24b1ebf5f3e8ae47cc2bdb1ffe19e5a5d1afae0fecf3136df4 ecc85e9dc54d6711d6037726dda460aa679038bda396bfab89c69ed8388f681679859cc7e583494e ad0639cb6c0ee59389f94bdea3fdb46fe4076b3396075dfaacf777cb8fd3b73424f04e596dee61fe e6d41b946a991e799aa26e20309a331e5ea64eb6cc9dece959fe66a9da92475fec02919e845ffcf9 baf5c2e4786410e8b081f6cfc592ad2e87b3793d6dff2a86274c9aa2da1d139f3008c7b9cf3a103f e5cbb074afbdfd5a092d0c8c1e57eb1fb232dc476419f77a47fd9b0fede1235fe8f919bfed3e8db8 eb868d83eb7cc24fe0085564ee6437c6c196a9cdc75a2ca9a6a5ba0fc5d46337327670ee6498a7ca cd688e8bffc233448b9933e6165fbbb255ba2c5fb9ed77c5ca4c0d67e3c9e13477c7372e6a87efa4 a80485b6ecfb89162d07cd6274e8a358f2f008e392edd14edae18cc0bdee0203208e700084930b9b 9cdd064dd12eae858ed58164d35c07dec0ec2a87a9b15b17be55a761f58d57f7947f425d4c5504bd 7f85429d9cae8fda437a3db4a070bc6bcc6279fbe156d1e4d7e123e541723136d779bb176da6707f 198f87dbd52014a1853eac3467e2c0bc153def3242a7bde140f8fe18b7a50a1747ecab6fbb3defe4 ac15e256acaae742e68618a126349b90865d9ab0067cbac8dd1e7ed7ba780eede9034b1deb141c27 5a10656f1aabfbd5ce785f6e75f8e011a83168ef5469bd7caa05b1ff7d4cf47bfde162e3ebc2bea8 e673cb7125b8c59b5b7288068fe3329c1e57c15063b756ff840d85de03ed9b6eebb10c9dfcf9b2b4 56b3fbc1dc9c5e37b321548181b4f3f96eef54ad74f11ed6d0fd2cf99d08742a6ed35a586cb734f6 38523b516b6d75846c6ea8c626b750657a715116e37a5151b584512ac4d86feb01b46dd759ebd9ae 239d87bc3db7ee3f9cf1f858de58c369765120078f295a5c9cc6a0bcfb9e0281dab94603875838bd 2735949de9bca4d8653d3dbcdbb6151b0e6eadbb9edc3fe9b7e3eca93daf63a071d438af81c2beda 11d47da393ab3cd2f67d9067d42285498aba1075a5e24cfaed3506e2767d2a7c9b4279675cf2b245 3b94741ce33dc9e54f6b09cb31cf1fc4cbb07affe1181e77d564c01bd959120c5ed13d6ded47b90b d80debd777dc27665bcf15d4be6225bd95681c8e27bbeb6df323dd7fc1df72501b55c97d67d280cf 9d5cc83cd536e033ca52e40b4a07b26bedb56d37db5d644cb4eb9f2b2f5bdd524786a1962b1d1fb3 8984a9b99d7845fb19b17f6de22239fdd842501cac84e13b7f179871e6f1c357b361dcc7fd205697 b515eb2bb9d83cdbeff13b93dcc2523777f061bfb4e8bd4bdb8153bc8c34b3e9dbad2eb11918da48 0d069d497f355567c16aa52c93cd5ea912d76bbb3b7cbce4ddfb9991ada85094611dff1a2e257747 211236b419f1dadeb4c541b9640b0faf150a81b0de082cc47df8b1bd43789ef3bb7ce64acd53ec6f ade960ff684d8dd5fd877d494a8bf9ec91cf4f5ddd03e3d6f5f60c6af9e2698037ab5f81b897191f 86d6861c75bbfd11cb6a2db5a176f222df534bb63156aa9e316f43436d2ddba5de513a69839b846d 072f71404759917c6dcbc263956d0a418fa204163744fe93dd9b3c6f95867c16eeae5ad30ff82e28 6b15f47e935b4084ca2987c28c5316bd4b8ae9832b73f6ed87ed261c428b62472a4eae44981d49d2 f9356cd8b9739f5a426bb7c03f47a66df70d9db9e25c271f1d0445839f66bb91fd0c64a494fd3a53 a45eafb11407e9f4273c5eb5a3c0b66a57fe53e5d34375f58a3c3f88eb7c567ae2adb889f32de968 ebdc222d7dd34a135b70e5edecce264311626bef579bad7d0e5366dba74e29b43bb3ed4ab71fbe51 c767b2559f57ae7c297accfc5cb8683c3fbea3346f5e3862764ef9f6890caf3cb4b42c85f38a36f4 38f9508b74a9770c5dd1cf2c42e1296d63813d3cd77c847e0e7c36ed745bb184bc5b7281cb710b47 af7e57e4ab688870157063d964437558dd1878ccf6f99831e6a87d619a5abe4a3bdb91483b3b2fa2 d1f6e740a30a72a351b676fd21a111a83e8324b63cfec05e3e48962033b820c4a337492f155bdfd5 e2ef72053f1a399dc28112e5438fa024fc45b785e746b0f8cf53f77981f326ad78e22f5a4528de72 aabd387115e4fc60d7a09265f50951667690dc64ccbd4f31cde0d9a6dd0aeed0e83a9850970174a2 88e7f8db7b92f751ab450ef966480ef17897e2792587b5f3e587151032f5184e6bfb3f66f3ba5bcc f5ef09fdeae533edd3cf73ac73f7beabac514a96bc6d9110b8fe9ae385454e6fc9eb4a8f531fcdf0 2777b3e894ed96f535b3d3cd036311f19d3e0ee337ed3297028d95eb10d5d7099c22095f241fb98f 45320a3522dec8664fb4fa6c9ec8906f86c850f7213e39ca1b7cb2f4cf44a6185f7e58cc6699c6b7 4ebba154ed8f6e0e572b859f7dc72d4dadabd92b51abaf31bcaf405c2ffd83d9a45b33bb4372d59c 25b35d6b6931d67837a04fd9cb987677f52575859b5baa7fe2cee483579f6490ed6749265e577fca 6df38512ad13d1c2a7bca3e352f636c4e616bfc614f899c14ae78044577dab8fae8c6282ae24eef4 0fe65a290347cf350bfd493ec1a6f592c713ddac53a3dcbb712b19db9fe7f8bb5c01a9cc4d612cab 3cbb11aa287dd2321c8dddc80e451545870c5c6b48b2d8644a8c07b325c14baf033e5d822b2e759a 1fbc80754a9832701be8ea39a3506d5951906d55ee21c6f5b9840fb4f284ed77ed3badc1f6a7eac2 f679b684ed3538c276743ffc101faa57643c81d866d0bd9d1b83c154a8f5a47bbf609bd2eed37d9e c64735b1eb1319bf5c5d5e3a9a12830cf328c986124af0eb40c06372afe38545e6bbd80f532ba500 2bafb1299af4bb6bb4f6b00ec83698dc9166f994850feb52154621126b7a812b3409f66537fc0937 69d0e22d2d4e0e7e030a973d130ac7500c3d4ffdfd3f9812d4091bb503e9fbdb0c6dff83f443c369 bacbdcb16ab9cd4cae0bc6fbabb263b9f9cf76ccadab8e42dd6198c48b621642ebda9940cc7b4d84 8f6d5687dd92e1362f5a6fd8ece39bb871f7f7abc690c91f1bf49b7c42234dcb7f955bb01d37ea62 f344d773ef96566bf3b3a0ba9ca1e914dcbe97abd5f241ad76d0deb8ba744bdbeab293d9fc905ed3 5732ac9535c227e01ae1094f1f75d6fb5ce3e739d6f37ae6d976137925e491fd37899c3915f40e99 a9556914620ef5c603ab54a17101c5a1d642e7eb53d457ebe225b16a73f2d0af2985daa4ba529165 55ab2887caa63b7856a0c7a158de87085a46802d967ae2d52d5e0f9d6571e0c2d922552b7ee5eee2 803ff8c5ab27ad8a579d5dfe30ceaf1fcc6f5bc801fd66995efb70a0ecfd9cc60cb605573ab5e6e6 29054322e136bbb14f05135ac356c70c0ba71d72b33e7d2c0b952d8315cb4e966c9691adfb15884b 5e732894f0e34e2bde7ac029d2d9665078c6ea3cff599abb7c76e3df7372e358c82edc269aad647c 3993f8353f537b4df7e9f1b66b99dab9a366f40af03389ca4d7e08f5469ef7fbc246e867c6f6f7b7 71b55196b3f03341e885f9a9dcf628e92e14369715e3f18a4fc86c45479cfeb50545c718a9eac9ac 5c22361b906bc3d757b6727b16335da60065ea6f8a00162fb5009c0d3a29de3d00e7c4498af5eea7 dce689778a3504e0822c00b898f5526c76002e4daba0f9e9aba07929fa2984690a7d3e3c86587bc0 e6338a57e8ad14a789354403348f986aa6874402ddec8d6b4e3a0b72d2ca0e508b9e7d3be9c6c8b3 b95a9532d152ffddade4e662eb036cd9b801a49964010237a01406996225000429e90061ad418acb 0c209c704ab14b3fd7129014cf3640f8200088d03a024484eb00a18ada4f20a6161140e8f23afd7f 6bdbdf5e8bfdec9e31dc75edaa9b43246a75d621d694196a9b6d757dfa48bdf9c70ceb7e1caff15e c06aad562bb0a5c13e87e494cba6081ca100004a94330015d8628a41e3e720ee6698148c9c626802 740086009deacb14e70b40b77afad15d964c31d000ba2723801ec02dc50e06e831ea02f4642c00ba da9f52bc6f3f31da2bce5dd7ba5c25556f8f73df33ad3d280d6abc567a3ee9c0b636788d6a8c9b61 6667d77478dc2ef97d87ce553a2404d0442b020c995701a69cea00b31f10c0c2120cb0459b4eb156 0076acdb007b3921c0e16c0270c6be035cf9ad8d02b83d6701eeb42c80bb600ef051f206f8d8c301 9e6836c0d7d012a453d52bc523dbcf5d816f9f9a57b32bde3de12f31fab228307ea679c1f56a306f 86636250ab5b6fbdf89a6cf96cd775d13f7ee84c03604f0c01b864117f798f376be6dfc22f8eca80 e03d13105e6d0c8824d800b282bd00c99d2040762c01903db40fc8f12dfd8b645100e4a5c7012a2d ad0155abff4e68aa360180227215178aba9e3176908eba7f2ee93f6234518c5e448581b7cd97158f 6b5db1e314df2d5cc9ec473beaaf817dc7f467383f49759596fb7ffcbd7f874dfc74e89fbefacb4f e8aec680da740e80aee53380d66608a0cf5e0730801a03a69ab90006dbd40183bb3260046194e273 044cd7cba55894cd603a363b5b9c16fe11a389a530f92e2581877d645eeb5ed683e2d835f40ce2be d87f8fede787fe47eefd49d05fcff1bffcbdffb1571bbdde006628bf009b4761c01a59e58fb537ed dcd8dbf002b87a17021c49b4533c43c051f6ee2f432fddc8fd6d90d5eb7f6ba8bf95fb3ff9f4b71b 1b3cfb7b3736f049d1aea698a0ffda3a0c959fec1ced3c3e0a6d3ca2b2e03cf844ed3fb2ba13dee3 ba3fb9172c7f7e5bc08bcd4d393d2f7fb63aab0de5dac578c7f49f2dce9cdc38fa97b8fbdbe7eca6 29f12f0af79b70f957f2ed4f3efd194f7ff2e957f86da5b5fe9f90013e33fe3b67e2f87886b96afe 1195aad57b5ccb37ee05b384dd946389ba955db875d5ae9078ad0d30e5b2a518fdd20819ebb7df1a d7f6cec8a43d3c79f9eee4785346eb23951caf07769fcfed23a44fee62e2a8fec4dd4dc2cfec7f65 dffe4f00eeffb29efef01bf6cf7d2c7def694f761af41ffc623eb9c795477a58f6a7d3553b9f1e17 e37e007f872224e5131e2f6a47bf18370ecf4e841cd84d88efa34648efb34ed8da499750da96035d 596f05d9480e6d73b03a6bd16c85efca5f056a499dccca22c4b7e4fce36fd97944c7cc3fc9083ffc 9fee5376e2a0772911985bd992db972d2e9be703ad0f4e5e461e1fa9053ddb7fbaf5d52e865fdf44 e5ed023fee37b5917b589b19eb9c3805f1ba3aabadc70a5f33efa50fb1d9c5d3668b0bf62cd5e7fc 5d426731d3e3e2053fedc4e5596938d54bc3fd64abbdc1bf72101c38538990e3b5fcc31f1dfabfe4 d35bd9acbccf07f29d1eaae80a1fc2d286da67cd31bf49284a4a0ec2475d79a54377e9d7127b1136 a3defce386837976e00433e9698ee305a74f6315388b69223aeb5f28c3dcd94fccb277890e7af48e 90ddb134f6902232f2094e089fc3ad17b21f6a157ca2e93d8884f9eb2fbfeccc7efcf05f433496c7 d399d9aff79f8e76da96bde7b7294c9cacf35ade3a9daf5196acccb37db8392b04103e4df83735ad c56f6e6216df6274e83cda11b2b976c65ee3d41de387873df2d187173efbef20649f99491071d555 909d90c7a19cefbefd85b2a9f9693dfb7d583cd0a1756f503ba0b3be890adb7ee3426c7ef00efdfa fa7fe9d0a3c3091f1bb37db6abfac9a125f696d4ba349af3a7eb3c5ed09bed54cfac2e93c662fc1c 9fbb5e6674b3cdc2883a9bdff9260c09130a3e431309f8b78e07d9b1430fe5acc9f90bd9147db518 760649273207b54dd2ef9b8d5be41ddcdada43aedaa3e751e79a7b1b1992ebb7de9ef38ce18913ca a5e80fb8c27bfcb70efd3b96c71b2fc5db05027aabb4496d2fd8edad152f285f996c45d58c904418 8c6e161785214e2c828826b6c3b8859efc8554bff9e545fd35d02b5530a8ade17cdf84e0b27708b0 bae7286db4774e3adfe5d83d82e80bee3d77d45d5a013d273c735307088b932dec89a235639fbc55 bcf43c73b97ec766a77b9a9a556835f9e1c946dbf7d9be5f57bb19384f562466f5e6f99cd699ea4f 828e1c8681473713fad602c1679a9786f182b17d75dd0d07ddfc70deb790d1c67385d1b177d9cd6e 3d9259bfdd0792c9bb4c02559d711d6a38ad7b0bb7a7ed0e6be74f7dd99af7d68655cee57c53b384 85b1792737a32110d5ee012a7f27822e329f8f740f79af74fc7c5afea0ddbcd5e2cfd1a43f995d72 3a2fe6f3585a8fa6263c77fe28bce35e9f1cceba1ae4579179ab6f4379d3c3116cd8a3f4f6cc6587 9db5c3afba473bc6ddef096d177cf765a9c5206b9597d3a2a963ab9a59cf3c10c3943354f7708484 ae33eae8fa2533f474c2b8c7da9dd62f1abd0295cea816491dee2e05eaa43d4ffe402c05ab1f2e66 383befe6bbd277f65c7256b48abbe8721291b5553f9c88893e54e3656bd098f79a1e3eb33937d4c6 9a23c81bcf9647efc852b79985a9bff25b63d7a99e8ce6a67aebba14fceea24f36a7f755a9ac9315 03d286fd00d71872f3bd6e3aa3f95be96450cc51a7603451f3123828f3c6a8a894465cabbde20b7e 5bab0dd6edead64e116b89bc91f9d5695005aff521bdfdcd67ab78333975d7b3d1d83c054169ffb4 07d67127f5ee498039935af6eb87b68b47aa6dae378a6dec9ef6b07b42c7b17e25074b9d9c4f775a 509e9e3a6f6f75ef8c89d3a793993df3ea542957d5fc198195b927514a393790daabddc36ad71038 94379fd94e6e74c99c7480eeb4643f93af45514246d44a3cb7a8f53f387cc8d767755f0497592dbb d8477efebe4a2fb84ce49be54fcfbb6177d5cdec02d2ea1c62d8d88b07bedb633fba4e41d99ec6b2 e5b0334e90b89325d8449586ecb74e53e66ff6a4282be3d14e6ac6a7addde2a2bca593ba6c96b2b8 74e8e1bce4e0b62e9ee3d350f42c712d126c2623f8519f10685176f957fe9c4e5085c2fa0fc2f933 d9cbca23b36c5d06b7e9818ebe2b56c69c725f07eaaa1a0f9c4bc3ef8531d06db534668c03701afa 2091698d9d39ed0e7f4abf7c39bb18286a3319b793d179deae67ce89bc9d7ef672b3f0394b8763e3 29a1189a112f69fb267a9b052adca9e7f71410fc17dee15f9d5e9f1fe1a5150fe2e1bb356933584b 2c57ec562e0967290eeb562e4a921fb66b7d9f5d48dde03139bf92f348f8bcb6c3ae862dfafd3e15 ba62111856bd376f7509486d743ebbdcb78656e3152d2aaacfe96d7dc439b2b9117ce948e9918486 e65cbc707e7a94d6d141b87793ab403fce6ffe15160bfc48151a3cb878546b32792a2d51627bdcec 309f73ed91f8e44a7c1e6157d9a3c97666f277abcaf4355ab39da19ffcb03e957a997979de7bfe4c dce1ec5239f876965c7901c48e9df628679930be17b5576022aa7c5c37db6be9c2c93b37a74a2e79 37c4cba2e88964a51208c36e7dc2bf1ee8e2ebb369b1e89acf94d5536b92788f96e8ed72dc3c0f6a 5c7bcf115cc90e25b6f349ab81eada88998d91bf31d073daa0f723b74bef7b9528859afc054d5825 83b48e9fd511fb1531adf33d5875eb5fbfc0e0746b6f7ae3393fb1b556dd35b0ed49ee4cce2ada5e 6facba0ca316295ef3ae203c9091c6bf89a1c5b78623af352d2dc3565e5b4eb839fe5a71ed7b71c7 9566dc8d5d59fd0c5b3d4dcacc266dec184397bf0d3bbddf45266d078d884694cd85eaada51a851b 5887c2a5dd88c2e5e68ac2e9caf287156bf2d9b899f43ee3d6e6f0086a247a19e07177d7cb06c6cc 32ae4d4f7f16376d75ee39846c17988a388030ecf78ce043723c9f63da2d096e75b979afed72a55b da916bb235666bc5d19cd91c771bc6e881337dc8d65fb4bded1469c49c34a93353e4287ca576c99b 761e91d45d3a10cfa05c2142f6a11021370c8910bd7f9f78fc5e89b0725c2cb34b2a37c508078ca6 d3cf7b68e6e95b9fa6ad832bdbc1d23c6c385ffb8cf6aa92d43d4ac239b8c27f4a9b7a6b7a7bd3dc fc9d11d9a496efb035b36a31c611f2e8438b0c6927d3fe0ac434b2e9279497d61314febc3cc8dba8 9a23e96aab4e3caf339a08fd4a870045678847abf20ecfe18b229e23a6329e835a433c979f2cfec1 a2d8620b1372e765ff12cd03eee18d8afdef29e074cae3c44437ce48cb6127adbddd45ac485744a8 25678d329bb83d8cd90e062c7d588c651aadcc75ea7c79b81441be7df25e802724ad620be25569ef 08aeef5e0920ee33f8a450aae2394724b019bafc268e61c569b38f2ea5c51aad2cb92cb2d6101ead 2499015a89da73b4e2b5663fccd603bc18b16d3f1f568a95ece062eb6f2f535cdfecf567b33548c9 8b7e9e63f9383c0802777111565fd6f2b473c87e577a517d5023c87b9717487aafa8c488b54d82fb a41dfb449806b8587fccf0dcbeb4c6da2c76c28a9fee1bede8d3125a853228d20d4411de7d660e6c 4de8050c1b39d03c356e5cf3541cf49ac7d77bda3cbe33dfef0686e17c1c5b4fbc3ce6df6131a88f eaf9fea36c815e7e94ce21bfa8e3c038cc552d4bba3236af29bc3899118c35624be430f28b44ab1d c1f8e474a0f1bc9095b0f6acd04157a59a85764efc00adf68cafa30be9de7a73048af77bd82e3e6e 307c62f3cd1e6e371ad7f8c43506ed96093d56cb0914e8fa0362218e80d84236ad20a52082022a98 fc30c555f66fd1bc4594fa6c7996779571ee632193ea774da11ed1d944d9d65c5fa26c566f95a135 4de39856c325b80e508d066564b34411a481c90cbcbf7a128ca4fd78f35c38384d5c3d0f1a37ac36 6950037c053d29e304b18bf9a71e559e953abfe1c95a4cfd92c86b8511165617adcbb95a8e878daa 7277f4ea022aa7ef418d71ad2022e328ec535058ad6deb3e5e556b1e7fbe941d5dc37226d1651e7a 4e66f7ed63763d16a28832d8edfc9e5e2f86d140935e3dd3f444e1ebb96df88a55819ee709921e8f 2505813c10eb8252ecd47365c2aec99eecd78a843fadaac3c5ba5a61b3d78a9e70b9f2ae1ec2e5a6 f5114acec7f64ae804de162f52a652bc50c7760935f541c989fde087efec29e679f867e21e5057bf d99327c5ba6de1ed9231ec0f4067b9648f32d95c4ef8e20eb7e88bb7e6f199aac048f34614a09152 bc57976ef751ad6636a54a573a4115a85925ca960b7365186b2b2577d0fd8a29254c5afbc5fee23e 2d3caa954d8131847bbe158e4a397172492f6e49d7b2edc527ca965ad347b6c48fc9ecec2cead959 d2777ff8cbc46dd5f03e301698dbe9738875d2965097cf8f732a74a5cebfa8e3cc70caeeb5824d8e e1a1806e441e6d3c46f5524da9bd9e65a490391682e1e092cfd67a9fdc749b947285e601cace4f05 225bc61badccea26a9991add7380315a8d4043833729262fd0d04928c5e51b430d1aa2e78386a45d 4083e691148f366830a4051ab0d80b1a498f1e0c619ef1f26681718cae479a83db0ed6ca3a9ffdcb 73ac32139a0a630b2f9b2d01c6931a5697f3f772f930dabd0ba1b7ffded332c9f3bc054d7171064d a9f0492194524430689a053685a4a458b9a069c19314a33d68da7026c512014d474effb65318a538 dd4053dbe3a02974b414bb3e689260e45fbcfd775aebb7de81d82bcbac60238f90e966af2d4871dc 1b10e215b76351663f26e4803311749011a09c1ba315ab3e2c175e87fe275bc5b513809de70ec02e 7e4b11e6539c6b001ee3788a1e9fe2f4536ee188eca798cc003ca9a7ff683249dfdb70448aa70ee0 38980278d679013820d3f7c2a29dc289d2ff3c9a0f02b1aa7ad2fbd171b62fad6d8c7228d5a9479b aad40269796671cb353919e1df131a85e95d179adc15aeb2e768a4f08ea15266b3ba3e01620eee00 89e7e99fe2d50b20bb722185d64c312501f2c88b29c42e40f3bb21401bf43245720368932ba7b831 692110d800655bcb9f408c56b329eee9dbd8c10728cea69fc70787be00b5cd5ec52b19e63d38085a 47b25079b82d145addad7aa6c6b5eb0cdd7bbad7e0b9aa5a391412baf0e9cd9a199395727fe9d091 54f8198f7f3fe63328ff0156de3700864224c0b8611b60eacb02982b87005bded6003b192f809d0b 10c03e5301e040ec033c53dca6b894005e4b4480d77b01c021ec90c27d7a6d68e6d958abf7fd6ef4 790965db03785ce75776ed4db36c778335eccfa8c13f5db3728811a120f4ee18382ae3ef48c695bf 74e80304ff653cfe854dfcc4d59fd3773aa301becd4900bf390e206a9f9fb597107a3b404c100088 f51e01c4d5520099254680ac672f8024ce0d408af30e20a9760448397702a439cca71359c6ea4e89 48544e6b06fb2346539bfa05db0e8c5923da02af72e403b590fb70cc4f87fec9e33f1dfa37acef60 fe653bfe1fcdf78f97f617e2fb5358e7e5afc2ba99026ad03d033a8b9500cdbf1840ebb103e8beb6 02f414cf007a9ba77e1113b7a303989cbf024cbef6000ce497fe11a3a5ac29d5594f4ede78f22e6d 9adcdb1b556116b2f2733614fe6b603f2bf43fd6debf33857f72f7ff152bfc2fb9f77504cc6c5d02 2ce4b380351417b03ebc06ec2c9307ec6e97bef7f07b802bb0498ad51d704da4f0b7819328fef047 4ffbc9a7bf98debf43069ebcb5fb3ba6972cfec9befdb97ac3dcabf5f8b45fd2232a173b0f3ea91b 8fac8efed9efec5eb088e16d01cb939b723217d704f7b6d79a1f7f45c88bf17ae5ce075e40cf487c ee9cce6d29f8a3effa3576facf5667bf5084df76627f8bbbffa868bf91ff47cec42fa37776797ce4 e5fbcfd661abfcbd60cc2b37e530876e65e7845db5cb9ebc6cc90d73311e27fe7c60f7d2d9fe5cd5 d3597818277cf6708e7e39e31f9e7a75726077adcd9e3ff59f3be99e69a423c15aebad2c76d78d25 acffb26f7fb25de5ddf965e0fe9211fe5b93fc1ddf8478869946fb111558fb1e5794e0b6808cd955 3b2beb8b71578e67fb2ddd4e5e967d1d6f32f74d4468e60eeca65edc478dca574fdb679d627d57f0 eee938863b64531bcff075239e307fef1f7631967eb3385c849890cc3f837b266d9b39f84fceed2f 14612115b17ff0dfe32c667e07adfdaade94ed05bd6cb11d7bb69fbbf6099fac8d2335df79fba83e 0a76d2a93bde68af52b436c13b4e9cfc69b1f2caf3d5f2a64f374b6a37de2f423838cd3fbdc175ce df82d72ca6a3ecac304a2a71797af8c6b44df562b93531abbc111d8cc73c428ee2ed4f1ec2370061 741b18f93ff02929f71f43bd4babe6f5b24587d7131ef1e0c0ae6ac59d74b8d5d75b566f264e4e25 57f88ae37e0e62b329ce3f6e45996707409b15826337563e3b6b9a086b775a9b2dfb936d7b174c1a ab5d1439b5dd6c7c368fc9183fbe8e231faf3cc3a72f9642f635c18388afa841368ea3a15c2aec87 851572f5175af9f273a97e2db35ffc33caf440caf7f381cc2487b0989d6c95b3b94890e9255952eb c921fd8ddddb4cba773fb1f2b68bd35a2c55278d25db889c2a8b8ebd46031fe3078a19f928de0a9f 7d44fc49aa24a5049f00d702fe4359c35860bd61612685be5ab2e783449b1f06b55d01f44d586b7a 072faf7ac8bd3bec9d87bb65cf63668b1efe1ece7ff8d710bfc2e4fea32ec2756304e9bf670489d6 99677b941b974785e1c4ccdf2611929c57637c77de8fa8d3e112b2b7ed3388e81d08b2a35d7e2867 d6257f212dab7e7931830689ba4606b5f59ae89bd09af10ef651f41c34a7f5cefda6ebde02fa1b63 e0521f6fed84c2ed697fe60e62476a59b1b36bad6fc55dcdff0319e2077fbcbcc605dbfc469938e0 d1997fcc848d95c71d9d98b9193df66a4379441d874610511d771873ddc05f88ddafca314894ce72 504b3a9bbe59ef1cbc83d53e7bc8a97def79b8f8eae1372de3fab456709e23a3ea7019bb6947d280 b085c249b4e24ebe6b15366260aa8de5c6485c3263d46b39a6bbbd39ddae19474eb7297b5fdbf5ef f5a66c8ea79f297a6dcaaebd00baa24cd70d9a8ed052a936a20e52318848be392c441435d04b35be 6fd61aaae7341b66cf6b219eeb1bc877234c6ae2702abf70006a6c6c61d23b5ab3bcfd3dd32c793f 7d994b6491352bd1b96274a10262d41f12d7b594a9ae1f2fa540c7c4fe4ebb16989246be08b91374 22afc356a3c10fea7be3f57f477337ab3ca7cba0b51ec6da706a45bdc5a21dbe4f93efb4369c5db4 ca609d5533dee1da2cf7bca78cb90cf078a7452e343bbda47ae9ac5d08cc55b73135abfbc6c2d804 ecc66894a563d7d6a56b17217a6fbd37f473daadb4a86a837316eb3c09a4d5616776578de0ebd76a a5f21feba0c462bea8141e17a9bd589883b6aa9ac37f7022e07db2b6d306692e3796a38973da7ba3 1138ebc3af297160922bc4c3653fefbced63ce9ef651d4520295376b9eae1a662efc7d370e1cf6f5 f37831d289ec62aaf9e676a9d1cdf3a6138e9ec70ec797ef6ab445819aa38592322b8670da626d99 b67aab75da953818ca5db8ba95eba74d41b284be20c150daa11df774f00fbe0f245b5efeb0a273f1 325e3bc7e9f8813f8661de28d83e24bf64af6f5f491774e28aa5d6d3fe65bb5a37ba4e7ca174ef94 93b461adae775e3eed76b817e7ab938e3c56731b3956da94ba548a4f67dbeea8e1b7bf69572bc953 eef6ef3919920848b20e2625c1ce41117b19bc2f5c8d79220c682327906fa6c53f56f71eff7d1cf5 0f768af93e2d26cc623d8527bbd9683ccb8c864901f1faa777a5e3be2f99ef535cbbbdd94286d576 81dea7c58ac66016d6691993962a864b4569afd7467b455e7aed6a70f1e50d07c632b42ece241b2a ae24f841ecc553a0de44bcdc07c2f57ca90a9458c7f967de9478d63ebbad08485f75b0c54ff39f56 d678b35cdc74ed149790938eeb809396b3e1a699999fe6edf1623bc1c5fb328cdbd589bf5db0bee7 f708c389c5126f6e0b0758bf8edd6c675c79e754a980c14ae98251ed5a9e1164c3ee7c1b7669ff71 2d0911dc9ed89bb9be7083876361705acef927bf5bf3610e9c5a1fabf96a454cb7d8124a47948bf5 0acfc9846fb38b7979c6aacaeac5249701c52457d24c310919ddb0863f24fd42f0fd6e66d55bb48b e8426e1d2c4bd86c705495b0f7eae98ebdccc392e138374c0b6bc382327f4c415b8b3635b9313c61 1232beb222bebd8a82cf643afc739c37f990877aadcf161bb6842619b5b2afee928b9793efa225ae 585ddfd8c5ad9a63d5d86a32ebe68e63f43767d23b7e31a1adba76a79b7b9ea08ef6a34b1d9d7640 1dd5d6f08715c3f52e71c38c0f632e2c6e83ca5b5cf6af401bf73235b367ad03acdded63d9ef4aaf 8ee04fca6d9d75df92d3d64a22811b4d81a60d820fd3fb462bdaf4a4568e1a6b9c1c8e4daec8ad7a ecb2f20ed9caa014336ba9b561ba05f742ef9cb466b65a509d86ab7d86728d8b4661b43926fbe3c2 f79e4692da07254961ac91a4080dff0259f2ff48fc5fbff968a2d6f7c3eeaa93f4c9cb60ea4ad970 60eed956471f6e0ab4daa6d735e948900fe1ce1732fca8d9a8b5261882723309a7b9e282fa6642b2 953522335d08e930f58762d3bbf960405bf67e42c3686645b913f848615deb4d5ea17d8524438820 1eea4825984d7188bf07db133ea6a3263e6614151fc3a7418aab8f8f4bfbc1a200d9f78977dc7e7f 9b3fbaf97edcdffe9581e147431341ba5d6d2c9559a573dc41e2e52ebef916357f71b3495c6497fb 6d83ad22679c81841b4b5bb39b409d4a4f854a7b6883c222de23af823d22c9dd784e3c868fef2940 042cfcc0df8951c4c7de15c3330f52c6a6f3a38f49aab947e757ba86ceb759192bd4c67dac901d0f 7e989703e3110d3b875b585a13e7c1b9e6ee7a9ffe666927e6766478c8c4eec405846f1beefdbbac 5478cc7a596e8e2a37b61aa839064a9c1a0def4c98ea3121455ec7518ba4b2b14c3cb6679d088665 177fbf1b437cbc52629cd7820d36bdedaf9834c3f3e8a2643451e59c119144b03c44cfd737486d7e ffb6b8486db19590748674919acb7a3ffc24fef11bbb3d02adcf5d07447672ec09ddc7daaecbcf49 d74f666e5a0ee0a2ece0394c6077fb12abf5a90b8de0b72785b72b4592ea352122b81118fe91856f 5388474553c4b31d47c5626c616172e6394017722142d5269f20896b9c10bd75cec0dbb801c1a639 6e350fa792dd74c6bba471c975734d9454f9144fbb8936b2bda663d4bce9d1b33fe3cc2dfb1e9a90 f05de3d1f76fbb8b3b934abb3fb6e357721fa8955053a40bcfd1bc10646a8cd9b59f245df4aff867 33cae002b5ab6071786962c56a8e4455a3c8a195062a237ac8e9489d335d785749abea66f7306b1e 29e4fbd0ab8986caabd1af2c2a0d6283d0d0909a1a10fd12e7f5110f3ef5579463eb2fcf49df4b16 ee0f7f7cf105b308fc23e4bdbdd7f675775605f8689eda52a2b7d6f550e92ee65d89d47cbe359bff 5616ffd692e2134c3ea1ea257747d6229943eafb7615b6902e0c373f13aae90ab35613cb5d9446df ce990d1241fbd0305227102344ebfa68f7bcd5330851a84dce315ecb8b48a7da9ecfc7d5926a3daa a556fbbbd8afda1e17b5eaecc4d9d5d98ab0a2209d3bc34ab3991b5cf6938c9719d6df4eadd0befe 6cc7daf4a24edbfb63c611593c91b9ea994429727fc863ed8d7a8661ab7f685cf9c9ab41c6ab1cf4 b03235883995be8d477d8c57c93a9f1585da54eea835a938b2ab737737ac2a78655e59c5f2b1522b 4e40d9d0728d72030fa5923d4486c5d3fb742e21c80929d9aad52eed1747b3b40fd7c6cf6c3edc8d 8862ff61aff3bd0243667e51c78dc9cba0e1d5492b569295fcdd7c8c8fe585c238390c27c69f6d11 d916f45b83eab536b5189cf6b582947f5495039eab242855ae6817ad59de8a03b26ce6577ce9a09c d49253a93ac5735f1c15d3d2e2bbfea6e02baf7b9e83aac55c64bb642e7b291ad9b8bf5d66636351 cc09b5019b13b2272517e157e54f62f8a9a356bd8f9aabb8aa352c596ee191ed8ed8fcbd53599f37 12bd817c6e8df92ae59f4a04b6cc6dbfbd67f352d19ff5746eda55ea8ab42c5e98c2a6488c914b61 58933ef997a917f2c055abb90916a2b9dc60c164e5e759ce1647889de9548d11581bab35a81fabcf 14ab06a89fba6d503f7313505f239f14bfa610d4a70d2e8524ff3171fbd5b0e9e538bae174dbc7ba 796d62255d4caa2f05b6a38d208699e1cf734c88c69544ac715885467be15dad76c863f14a208b5c de21a699eabcb8025092dba5f85d9ee9eb289be25503d09ac653442280360533853d02d036b34d31 0000dac1488abb06a0fd6206a085fd01d0922701349aca291edd21326be0fd51e68cf74aa883d936 5b418c60c47c9df7ffd88e5bb5fa6a48b164454537eb806c3c5e42b55626ea9f52ef904f8bbfcd62 0e4c2837018d3dba4ae19d531cdea071a81753a88d14090d1ac75a3b45cf4df14e3f7cb28e3f1dfa 5cfa624280c6453640e38a2c41639bcda5b8b6406346ea29dcdee0de96184f4c671ab7861e18cb2b d3fff21cf30b63bda46f31d2c7557ca6346f094ed416f37ba58c76ae5f9f4d6e56320ec0aa784b00 037f9522d9013893bda690b329c24a8a1b02e01acba51877005c2ff553b8730043c5f473d0a80ce0 06cda6f8b8006e6eb6002e0d4a002e5b32802bb99fd005037adc1f3373a9a76c0dc96956cb42375a 4e10b5d9a6f2a2305b9c59e701c584fc701cf832a84af5767a2f2fbbc37e259f27a1278047d93d80 5ff809204de9f237ec0740e8430e200c02fd1cc48c8603a4bd17006293468ad1102021ba4e113f01 32e2a0144f1120e3600090a87d00489fac0364505452242380f88dc493cb19cdd5e379c7a41393ed 74cbefba3472ec0f673997ef9a425274a910718fcb6e7dceb05cb92716e1dc7c1de4fed2a1b79b17 40eb590050a1f10d27c67300f5d8fc5f88823a40d7790aa0978e0c30b0b30056a5bf8913db2dc078 3d033021fbbb6e307dd50158b71b016c40df01e657108099370360d6780e30bb7aeea91fd2b2c842 56d2969b092e3f70bcd4dae0fe95e2b38ff96f574048f21e4ad9ab78544ec53295bfc6f4d3a13f83 f2efc7fc633bfe864dfc84df3f0aeb1ff5d7fd5a6b2f12c07ddd05f8f63901443d380182c40b8010 1f34208c9105085f5d0262466701312fa4efcdf72e2016ddf4bd65fd2bdbd9ee68ac77f36d8e6b5f ad15c4af02e443b3f670871ed8e7088a9dae59f6161f3e5719b59b7fe9d0bf31fd74e8ff9154ff33 67e227f8fe9d2efcb3f6feccbe4ac105e47c3e0714e53e00b56d370075afc880ce1d024093d333a0 25bb0e685354003dac8f532c8e800ee89c91890eb28a54dab858e06e25c6af12576c3bedcd1bc2e0 e5952fbef6fd31b92abd23fe736cff8fae336f3855fbdbf86b59669a231a94a4a86488888888c894 f1fd3fe9defbec73ceef3cff7cec6decceb2866b5d5ddf9fc34a0ee68ffafc8f58e1bf670aff2bbf 972d3f00bb2e1180cb460ae0b0f522b1f632c61d70468b04dcb4ac026efd5c022eec9d637cd2bff3 05928bf6137d2a71f526f2e9af9081df31bd4989b324bca10d3fedcca4fce4562bfae114565f3ded 216c0fcd9f0cdc667052efb9eedd883c020ca3e2107c037033f31b6c95bdabfe6a1c2ff182f87921 97d7d2d9cc9bcd53d4fe983fe2eeb7ba5962df3d341fd55162904daa86fd2d5fe05ff2e99f9c8924 a697583d39573c3c844deb766fee5bef480e9b999b5f968b3778242357fdd1c4afd8b453b9f43e72 ed7c6934d933b512f853d4ea48277aab7f95dbd0867bc6d1c187e3637ab05e061e7d3cecd5149cda 619e5ef1c9dd56faa968f657ec6d128590f865ff573e457e7cd38f8f180a77b718b623f910f6934b e5cfd1f4aa474ff742cec2ed995a068713ed79e7d02e79d1d141adc731dd33df879cd9067b9f2552 bb40c86676989bcafbfd7c16fe094588f442cd7bf6d186c75ddbddb5f0d838abb801dd5c4f5c957e e7dcfe8422a8c519fe2719e1df87fab523de731d2d7d8307227c216da27c8a44940ded22d43c0a87 6c27906f6b7dd7054dc3ef67b9e1d62c92e38d8514a73fa5c372738f3ba7976ba7f25cadd3e3e776 d57c467bd7ab47a15b5c7ca2a59acb7d1681522e38a1d6201df2e835e76699b666d1e8789cd14ff2 570402f7797c124c3fcef1fd8f7c818793b19e8905f5424ede4168e797b7c02b638f1d3677df5b33 3fc96de8bd01795c6810ebf448a456b929cbb84587a82fd50c212e8216da5a605ba8ed841d4277c8 00edcd4d1c1acea20164cde81be124922a5df3a61f5b3c4c1b60f29cb8e2a334c979226bc5b31973 eceb8a375631271cc3a1791c057df590e06f2ed92407e1eb3f3d348fe5914f2e2b43efa965e7ebf4 70bf72e5d73c7110aba9e5798179e3bb43ee869fb9895ad99945f68b33fa6221b65d35f1e967d22f 4f8577b73a4dcf4d7622a54dc1f2be5bfcc58dd519ab90d31b05ba6f8db0f0b332fb15ee3cbc8cdd ec907a71ecc0e2f75faffa8049d716ff72cbfef1f226c264501ceefa1babc889ebf4c0e6977e5d51 16d8ba6ecc4d041fcde873696673516935155e797fe2f2a5a3e5353317abe88168ec2bf96ffd9b31 bccb8251174d6546d8b15030fb64011e5ecc023ea4ee1035b05882eb3fe77cab5f4f0f7a3d47dacd 7ae92d7e30247896ee7a469ded2a84a7768be768a0fbc3a0afab94db8b5b64fe7a1ebec7ebbd4f2d 7a494b538b82ebd199caa29b6191b909a708db2e9f99697aba152dafb16c8ffdd6d218c5cb819119 76a753933ccd1743b3325d0da2f1743ba09fe37dff69cfc37e1d4c2f3d479cde7be9f5f46db8b29f 3572fee55b95a4ab209fb2eef7485e872f1d55eb568fb61a4ed9a34a7e1ec5ce6561701d53dcc89d 6a76d64a700dca7c701476fdc92692e5e62a9f5218275cb1e88c0ea9f454b8cfdf962758f931bce9 63661f93bebecea15996b801fd60c4becd35da3da7c1e8bdf48ae91b52a16a1ab96d7dd255e0fa5c f7bbcc52577169a3050325d0b09b7a56fb4dfbd5b984a77ca7ca9788b6b594f836a33ffb8afdea6d 14d0e1b2f242787f955b5944f8969c3954a404897f7bd7b51683b5e86cf5458ff6a5d9b47560a659 65898c7714f734fb35e634884ce4d1b75934d36bc02864e4f304d92df12ca3abfbb6a81d6843d170 6fa4aba7fe6af0dd2cae809dd519891bbb5343ef6e7b62677ce55dc24f0a3f6a3c95d4e854929b39 846aadfbfd56ab708d46d276393d4830de819bfb77a1d1d4b7a2dac43ab54e82e3c77b3a9b477b63 b99dfb6d308f4ee9ef56c4d49dbf040bbebe2be6300a0b03f66145bdccac1d74db98f1d0d1f232a7 11cb14a1569e28d3192bb546fb7115953627b675657ed4fa8ad01b8de5e5673693738dd9b2e52127 bf5534eea1d4f9204f09f6b5efbe67b35bdd95c5708d8862bfe78d1a972bbd6f549bb9a270c7b682 70c7514db04e593541b01945ae971e1f668b13971acff80a6c4cda6a551e0d9a2833781945b4972f fa1f3d804647f502c47b67fce87d2fc46c739731a2388d2d25bbc6969573efb3d8f2fc6cbb5542b2 baa44ea041337843e36677cbcec5132caec57e343934aed22e6a54cfd98c701fb670814d6f78fe75 680c789e7dfaf585e3e4ea8b9d5eaf8bdaf95b2e24b94db023c9d05b6d72d78533f20b533bbb2407 16969154b3f664f9be7826c9aea65e33eab5b9b8b4ed75eaac64aad98f9c6f91c55669cee1920677 6acd4357af37f1d7b0290ef8a1d2b8eea75f3b4f63a4af07c2fd799c0813fbbde0df10eaf3b35bfb c2a79a4b50174f29a49e1db4394e4e5dbb5c21b03cb643d4d36c87c35816fa4cb55fb80cd5ad55f5 36aea65cddf95425e6d3a2de1c8fc983f9edd386dcd7825814684a27b3994287f5767779355c1e5a 507efb6c1e6e7e463ce74248acf43f64639c2ad2c2a309f3021b92123f2f936d9ebf72ddfa5294cd 7a139dceb8756fbde6e47a3664b73bf6c57646ebef6a8d0972059ad1bd89468795b24bf76ecf374d ba7e8d265742872627829a60539f78feb207016fe6c41dd3446df6a6a36acbea0f1afd996220e926 ad46bd12d44e974faf16148c8326b1e42e8d9bad7e8735e1b1eb17f8f7b18bf0026795eb4bc7aad5 738853e7d607bfc915d94b87dd7ed23d1656318b09aaf292e966d6011df6520fba4f0af9dac55953 b52a5a6957a3c77d5165eafd7b95e155aaca0869a5ca50b9efcc26b9f5326f6fbf182e335bdb5df1 2babfb1ccecc492b18f55ba545a78bbb3ad779916554c95ddfa01932d3638396a303ffa9a51f7c5a 2ea5eb39355fe0e4b8f9b33e5b2eb3f096a1992eccc42bf2a8fb7542d2a1656b7439ef0f6b97736e 5e33e74dbf7a87e7b7aa754867a9172b97a959e12d534035eccaa2c6dc2a0bba5caec423612bc6a5 93605de8b8816361c0b7f346cb1b9fc2e562f8669f93deb674fd0602ea036b2a74785221e5d2b890 11cdd9f4c43baab3abe7cef6952ba5dd378be41719a6db5e15e9f076c06a97e78aac99de9da955db f946f55e61dad549aadfaf32fbc0a666d8734dd55fc2b9b2683ba98a48615fa1abbcb28662b9d5aa 4dc8cdf67a2637fe0127373ba5592e00bb9d20f1c5cf5f392498b65b037f5cf18eaba1504cdb3d38 95e96b6366dd6c8bbe5a69a134916f4cf8f5b59e6f353c560d9823d37d29dff80f7a906a81da2827 e6aaf77e1baa32570da75e8b59959a89bb3ac56319b9b2b0f16e45ac4be34ab6e4bae5d6e8732c17 9af2876c7b1e424203a14168b7a349a0ee2c24d0d50423d029d288d15008ed2829dfe92066f9a739 e8970e933d31d98fc6fd9b37587e30e797ed183fcb8adce8562522ac14857727f5e4da86e9d327ef eed746e3ccb53ab1c1937a83429ae235a258591c2b68459cb62ae575d160cb2d7fd1fc9eb48279d7 c8f6233324a175c32174d2de1318c83df19e28957012fdd4b161bf37c0a84a2dc028aa02c738f0d8 502dc90996653cb8cc44a57a9a6085cdd17cb6b3fe2077e1dcc473acd689c2772090fd86cb36874f 0315d230f46175d63b541fda684dcdc9c9b1b26c2ca24a76b5f99465e29c23b7c31b44761a1049c2 991a4de8862810183150f0de7cd3c7c906b0b1e181d962d4c48dd0f1275f409e6a527511b151cc80 3fbdfd16fef4b70584dbb459c4ce1f5ac8f4b59116d54d78b3e375e0c522e7bb9359e789a0aff81d 2ff11cabc068f564a41109e23db72bf312256799b057bc52f387b12acb93ac4776b695af66430430 7b2730ab93c243665c885bd912c52eed6305a3f637168d2c5442993caf23cfcdc442ea95eb1a762c e40437a4590672b7f90a94a71cb5e4dde575c93b0a59288f1568289fe345486ab69b4999cdefe8e9 83ee7d6c0eefd7e1826f9d7a3b7fbed3477b6fd9118d89d93234b225b2789eae6f3a5b88363fd8bd e21e171b0287a6f1eff2b9de63d5fafe8cdea1ec0b65f4621ab143b880d46d01831740a6e0c662fa 2d4c03add0bd02e5edeca0b4a98b4ea9b45b1d8a1a967917917084170c1e97f3a765e0e44fd6fa53 c0efbd4ac1406e42e1a0a51af3d4fef99a040beb359a5433f7c13a655c8da3793c6a8ffdd36baf27 d7a9748ea71ddff1867f55eb9c166c911aab52af725b3eecb1913e74101e2d2e6191a3f6d0cae1ce 50abd1799436072b5d6a1b5eb1b83b075811153eb5c231438905c2d0f5fce0bcb472b7467693abad 5a5196dd2e0b993952f9ee7b6652c6619849f5824b86f7faa5cc3c9fab65de0cc6cc721692b206e8 0298ef46fd332842bba7418eb2178d8ba87dbbb8c617cd489c1bbc9742786638fb5eb817aff589a3 733a226fafb7286d87f6b4709c295f5b4281844c3f7fde7ba73c8505f7dcb81f7eb2d3c53d9ff9e4 32684650f06a7ab96b34d2b99aada7d6cfb49d2ad6851de82cb66f00370502c012aa02b895df0298 dda500cc9551d0e975a9a97ac2b3e3d1e3f49d7298e9ea28db876f6fd03577ada7ba80c76705e935 d662bd37edd711acd4a8d9e3215e5685520ab35a9b139cbb8ed785d0ed4f329f8b314c4b6ccb4e79 dbc12aa574567be007bb3340f0eb1320442517638427ca2df1e200422a6a8ccb0420e5ce1e203c9c 0248655d010835e802a42af90041f1f8c9a8530648366426c6a90b25c1d1c33c7228f50c98cfeb0f b04d7756c1f2d6aa5491b5d06ae80366d4d87cb7f0a85689c389f2f39e46447876291ea6fa26c756 243bd536f8214029c28a315ac7380400ad966e315a20c6060268ad44c5e88a312203a0b4ecc4789f 00ca8c7300656b0c404930480462323c00b4e4c62f82c8f86e289e8f44150b339d67971840d93c66 9c8505a2cdb5634ed12bcdab38e757ee8fe738356cf1e4514ee3e8bc364b97f683f6354f23829f2e 963236c058631c63e324ca2d57dac5689c6358cf18d7f8be7afc356275938ef16c018c9787316e2b 8009da0d608d4229c6860758ad1bbf112dc4af65380260f8b6013002d2462ffe531dae9e41adaf8b ddefc6ca8fed58f63b3f51c7bc5f221cfa59b7ba157890aa6376be8f426aa692ca5bf5d425ad34fd 0dc0457c0970adeac6a8af63f4f7312e11c07504c4508a315c02e0165c8fd14bacbdb8f5b6003ed1 b6009f669e311618c0ed8604f0193c03f8e0710778675b06b8aa75621c4c7309d3f5c1f605f37ff3 1c97a1f53ba9bbc75685de945286f70e6e5d64062ef5c177b596bf8fb720dda6841010656ff7cb76 9c68be0936c5f8818d708f61a50071fac08038d31540bc970d407c0a7a8c810dc8127288b14a0132 c594637c3a804caf97804426ef4420ce6a342073cc2086e30c3d6adeea776782a4d72799aa72a84e 8be2a2fa89b8414df4aa2dd41912351a92e0f6d8aae41f42ad9086b6fb3b20f3dc0590522ffae539 fe0abe64747e8372faf149acbdf12d2823f0d7f6db8141b971aefd4a9c18ad7aa0bcac2e62f8a7df 6113450694efe73e283f263e28c7432528c79d22289fd3e318637fb0a3de5a1280d1293d79a2f97a badfdda8fae1953fd6e235ec9cb8b5171a523853f502dd3ae169b8c7a57e1d51623b4e84e71fa7f1 5757e5f6f03fc226bed10e3fd22f5f0554ee2a038a9186809ae4bc24626235bf03ea2820807a169a 313616a8e6ad33a8122a02aaf15220c6db06d5fae0608c74b2a5162aa75a12c8ccef3bde9d16aa85 0d79c92823443e6c5b0526cb5453dde938ffa3437f8fe98f0efdc7da9b388e13cd37c974488ac9fd 4bf84d1214c8e208d47ac71da0b16d1ad052fc327ad23000bd8637803e3e53807eee19c0e427839f ea71f56d8ce0ae650d89ffa90e58cc4aa978a2bc3a9643253d47f380d70bd6d5aba70e6308fdbb1f fa8fd69bc409ff296bf737a771ce48acbd3fe91249a2f05f990e89bf77b338009618e5013b5538c0 86b409d857e10038fc5d041c7b1001279b16e0fa541063f9fa5f4d32a9cc75617e4a87353bbf637a 932a67dffc869fec5bb6f0f8b46ac8c32988e423dd11ab7717d6b8df01b8861415079a72f32b03ed 6f01b898bd995f421e6ccf9766fe74a6bcd6b7a59dacd2ab1a3e75b3fb53e0ecafda6689c8fb4743 fd7fe4d33f31bdd3fe935b1e670fc1bb79f7e6ee7188e4e3e37aeb5c6edf34b86b507da6aed8e499 bb90f3a070be0817f84cb92176b2f20179a237976a684317f6f8d12fc251089ff2a1797dea7b9f0b 86bb6ee6e2f8fd22196e4d2484e3638a274049ec6d22ee7e631112bf6c529e4ade10ff1532f0b441 037ea465a916c90746b875ce52e7aa47eae07ce119eb64e5f079689790d5d141339b43f37cf00339 5a057b78a61d76dd54fbe487cdd6d5273df1bb21b9354bfc6b1369cdbf421164742ddc3bd4ca6597 a2eb355203b7b85a044bd82fa49288db9f5084bff210feab6ad84358751f371fbb3f2ebdc73a937c 37995ee9f8e9c8c4a179a2aafbcee3c9fa61c313b6d4b6dbdcd081a27836ae686ba7c274d7e971b5 bf72e9fa6895b39989ab80eacc2d2e1aee52cd36368b6e91d93ba1da383be4a1fb9a9b71bf3bb3aa a09ae4dc4ebaaacd7db0f5d4698ca33f510813b725df13fcdbcbfbb5239e6897720fb99ebbf54321 ff9325ebd9e8e1b1162e6bb0ca4da639b7381f434bd8ed25f969dd7cafec846dbdea90fb0e333731 b93ea7425d9c59e54ecb7e8e3a1ddbae0d8de9673a36a7c2676e4fdcc67e6d79adf3c12afac47bac 22737c14f4f8ce08bb1ce649fa81d9af657c93bcdfb78969f6f756c4dd2d30d18f51d629d9533fe4 b31defa936db2b9722ff72c95e1ca75f8c367313890e338bb85c6c9b0aeed3b841bda7c2eb909eb8 fc2e37915287a2e53577b015afbb89b1af9c2a3f92ea891e75d12b6f86bd57cb24af797d68d6aae3 413435bd01fd79467d5bec22bd8f87c9bd46a937eaa577c3c91fab6cf2752776e34498dc5ec4b5bc 164e26b3f4b95d7c9e5a8beffecd2cea6a926d575aea5478f2fd890478cb52b2b5d9d897f9d518f6 f9ed286e5f81191a6c6892e7da657819f2f72115b1af4164355303fad5ccf56d5e867a9fa54ef41a b9296348a5b9d8f5b487d12d86fc77374a57cbbb8b168c3554eb321951c35e8ca686335c4d9048e6 21e785b3e428576ea5515b048d163aa7f6c39c6d97e9c2c4e560dc2aba9fea2868477533d4236978 e95f3a8368147407f423fcc6b4f56d2eb47a1f27b07b4ee3b2e8a557e1ca900ac1c6c86da3a0abc0 d149f7bbcf4887cfe98fd6a5f0821a5a0d42255f56bd63f24fbd1db9f2a2cd140a4fe5b99d638aad 1678a58ea47e70beb0cbefb0b6872dadbb7670875dc28e81cd22bd939d0af7f1d352529feb285082 a7491e77b981559d407dee6de0bd46daa81aae34e0ba5e5b6b748b7bada5ab58bbadc3e1a0ab75cb 465fc3aea391daaf8da69dcb74f49d0e76a8cfcc6b5b8d55a03c57a79b522f6653b2a3b288dc4036 f596db2bf75ab98bbf91bc513f2d29e213974a58914a909ccd4db49eca6e3b65f3f31b72a5e3c1fa 8c5aed8203ccf02a1d0716657c1b74ef336b87869463a36e7157f9e8f0319eea6017aca4f6ab24d6 b94cc872877a93b576b415ea6d86929b8aed7614a52e0d75d939cd877286df4e5bd2feb192367821 909497f66ceedac7521389585634a4c37758138982bd6f0c9c7ab65131e3ceb5627ee846458d6a41 717e1a7829f2d65df4c61fc5fef44ac2a4388ba7edbd0b060d1ebbe7ab2732dab65be23a8ed66d91 9e5aee0861a76a751e6de664a6153e6516e4856e2389dc4d07e5d6cabbd0ad42f9c94b5b88942568 4c6b4d3dd71c34d1706a8bbdfa7edd38efb36183a2f9b710e5f688400f6a3c3fbd6d06f58fa40675 87a897625cd8baf0be7075e11cb0fe005f99ab62e3fd9d0bcca357519baec87a6b7c68b7b9e1bd21 e0fd0c8667ba6afe7551cffed46d4ff6de5ce14fe9406e4264d4929b75206d43b520c175136dea0b 931443d4ae89bd47c0372e5cd06c50fe251e61b5b421d00fec1ba1c7db7273c9d7c9657c448b54dc f364b92227f5b60c97afb77bacb2cc6cd9927ecfb325cb6262b8756687f7d9cdb47d9a2e83637634 7b9d84f8c75f34d5d130356a0ce69961fcad8752513b0d914787699cbfae21d9c57ca755d49c6d33 90bd93189ac1432c675ee986d9cb1484e883a002d34048fe85d469be6ed4eb75e72d35eb8dadad71 2bd81d7052f499b19b16151fce797863764e3acfa0885ea30de3add104b7f9fe6e6a6730890131b5 da60dfe06a8325c37ac2ed3e5f0c3868324b0d3aa6b5f79dae39de6f5afde561cd74f5ba0aab6398 f9288bd5fbd02ad9be239e8ad565c3bc3702615250aefc6b68be793e3dcbd417d2e26b4ba8674e1b 946b5536245748dd69b6dd7cf1ccee5492196dd630e823644e68e31679b533839d6bc3bc9da9de86 c54a954ecf3bd46325bad4b4c7a663dc296aaae4d904eb3c7d5f38a30b6adbb9caf05b5d611caa7e 7ff8dcbf3a3dcf38d6f57ec9c63bd3a09596d73df4dc3476de52b847af393f9b836d7d11e4436ef5 2a455c8147de6c7b5fc9307bac5260509b47e963bd5da689dd80a99d47bb668dcabeb4eacdc3bf06 982a5d5697d41384478ad3aba0f2a103b222cc38b992ee14166517be7fca2e3229c738b16537b363 56a5f0e5ce1f3eeb4c1568698fcb8dc81c020968bd522f2b6a57e097db8e35cab73a9fca4d1c8ea2 c476cdbf56536e4d8f5c76abcc76cc7eb43ad3c7e9e44693c5d5bb3654d7e91a55bd15abe3c903ad d2324a514fb2c5539c33522a1ff53aa83868d9a9a4a7fda02c152faf72eeaa62a4527b36c9526e3b 27fccde4bb5348f87302238bef0993c045ebf7d59c73daeee45bdd7034aae5ac81c82086a1c9444b bd2f2e5565854f8a52f0693f1b4cf6e3d773fcc862e2b5bc4d87d99a57bb906c508daa9553959ed4 226afaae7d3f86e2b67ab6e25063a8d248efc94afa908dcf8d5193cab97839447adb934d96aab84f ec32ce9340e43c8c1fe2b9178e8f84098e8ff9478c378ae3164fe3dd40a497bd4a7e336b54b5f504 d9df96e60325bf972bf4d73833e81ee5564765c7694629e007a47972061fa1ee1503ae046d663479 3f5bd5687e8ebfc3e0b6ad7c4ee743a551399f2b9974e15996a42228e771ae407aaf214e96dacb1a b1836ef198145534fc2019df96861bf87d839de6f508aba45745f47a9078b4467c46688d7cdfd05a 790ea3a376a1966031dc977c7b95196c2c63fc5e99ec839ef765a93fee92dc40efccc7142f77f22f 5cbc55c7297e1136cfac4665be17fe57adf7d4a400d3b12b99766f5596f6e31db9a19727b2642f6f c44eb97d0864f7cce2873106e346b34361a770ca6383feb98dde52a8898ee256833cb0eb19616762 0e7e77322ccc07ce104e19c333ccafe9efcf139e67f75578f658538ed544f676febed88dcf5d6833 9c1fe565cf679c69623bee08b7a1d4d2b7f9b24837ce793e1b8e23e628d4b7d49b388ecb8554da24 218c5c105a9ff2f023681e7042942ff11837fd3601acd273d2e88dbb94d09af3c4918746b308fbd0 5bf09c5bf660a10439d072641ca1ece3962aadbd49ad5484ca46711b140f255978e64b7243ae94d6 66a732b79ff8610a19ab6014e5517f985e0fd73d040fbe57af6a77f26ab6dddbb62d855ba6d678ee 6a48dd9372205e4098072af549d9047af4fad8d9f4a61895b9b8e8b8156d91c7257d44a6267683df 77ea0dcf576a0e4e174c046a0e3d0aca35728d92bca2b5785e3afd9a2c8af0f0e1178286f12e60eb 2c990fe58d9a8f7b1c3f1fb6bb9902364f9309660b9b394d74e37e1a3179f13090d65bdf3088b4ab b1e5d2e4275d624038fc4fbac40ea7b2b528724e65cf44bff3349c5c077dc42e5407b0434933386d 292e24e5cd5d299e88842505f2a2a23fbe82a2ca948a856056c60b38a4b2f9fed8957397e76798ab b6e57596d197d78c1db79b4c7d706e6400b5887b67d3ba67ec1df26d0299574942edb5a2deac3e99 b926711dfdcdfa7de89e596eabbefb8aa36c05719814b51396e050638d0195aff2b87b25b54c6585 4e8a171392f6f76e51bd2e8685436d6717f0e961993f29193f5fde95be7ee8dc08c5a3ec84c43f99 573cbc65f8cc80482f8c433d9db994d454abaa4cc06612ec405b5653002a613480c06300a0d4ee00 da54fd0336c34d69dab9f61f63f3537e0c17a74dd487c2daa54bc5e3be2a78feb7eb54e0ce6cd2a4 65aac317b32b9abe2dc96265cd3b113e78a31e926a4456510b375aae368f7f11f3fcb19749b56fe3 b4b87fcf536b3cbb4eb55eec016cf9d60d40cb2188f18001e48af1e104d736800ecab7620c808ee9 20869b0150a8b300dad44c006df13380e693748c2b34e9ee766034b96860b0de82770ff38c876e19 e74bc73de5f6324e1de60dc724350ebb8c987899f42c92fab37b47ed34ea43c556f4edd3f2c3ebc6 48bbf8ba0d3a4fba0360bb378cb19f0178965dc7108f31667700cf53d9182d3cc6a10e60a7aec708 e7005ec8218097c57c8c330f606b660178624500eee50b31a4efde9a5511ef5973eeaf7283e25bcc 1afde739a5bdd695677b33a342a9fa9acc79259e49d1934285ae2807bf845747f5072c6db2fb423f f2e79986df3200b2d49518db1e40dcc23406efc698ec639c2e890ebdc23f3106a518770a2053468c e1f701e2355700b16f3780cc861040e67c1320263207c8a8f806886663319ef4e88995a1a1bbcbc0 7de43c29259e632dd593816c3cc243639923ec24ea18eb77aa59fa5c23066ebd8088c3fbbd681cd7 fb6caa59b581f6467b003d13fd18e238c6681ee3ee03d42f85890bf9970579079018121d235400ba 17e227efcf5b8006da2bd1a10f2811236e14e8da5dc583a09902e88cfb1a90e7cd11a02d72b0d935 c85e8fcc10fae3d582db9deeedd59caeeabb24eab8365fbd64f278e42aa8201e72257d36b9673f2c e983a07ab5017638d8890e7dbfce7e80679115c01e8c1fffcbbdc5787e009eab166298448c671de0 79598b71b3015ed00e002f16d231365580034507788a8b5f9b26f3008bde42fca62dddcc363f5f05 2a29bdf7cb763cdd175a26c64582929bac19fa190d2af08b15b13aee12a5e0c06672f6357b038772 df03f84ef5005172fc5f82efdf100480e0e15b8c6e3a8697a8834407aec4e83562bc0d40a8c60210 5afe1c63910784de6001d18587311e074018671810f24a0184828f86c56e53ec85bb8f90788e93ba 7b0df7ad86ac79decea99299d170e6d2fb6e1341c8e50ae766f6e0030ceff35b823e612740329ddb 2fcd3731f926b0e68f5f70a314200f3404c8fbba06ca5954026574310465acbc06653abc8332d345 4099259b89404cdca6a0cced6ea0ac2c08506eab1d5016e0c5afba7b8d3af393810176d4933bed26 7eb580fa23fc81a12d684f5b546e2e65f3a0670da3fff11cff988c9bb9e463bed9c2bf0addfda5fe fe891aeed76aa0e29ddaa0f2522c4011c57d0c17002a9e97024a21b4df99c2a7f8beb94303cae9f4 63bcdd3f0118df3a1e6cf392e1d16efd5c135fc305316eefba304cc1f5dc4718a1bf74e87fd88eff 66edfdb7e09b040bffafd337899898f73b492939505352a79f1272b5cd800774a13b0134c99d01ad 5028a0075919d0b3c31cd05b254c02997fd298a173f4a4e74ecd272f48778c14577b395fe74ad47f e9d07fe4de3fd5e3922487ff4a97f863f24d04dfc4dfcbc76792d9e69f80e5000958d9ef00361e61 01bbd192ad08f6cc5701fb410cc015c335e04adf2c86ffc868fd15d34bb57e57392b2c7e67df1eee 8f8f74040f6173cedd5de806dd73fa0d8be4f04c46c5fea39ab854af37e6068fdefc35a00bd215b3 b3ca25ac13dd0bb9a80ccf66569c9d2279e29f68ff1a85dcb1563a0a97839814364b6a9a25d9b789 553649954d34d4ffad1bf5fb3f4995b39cf0e4969cf67072fae8dedc75e6917cd4bd1b6c36f7576c 229c2ee49cba9ecd34f5384512f93ed11b3c15da10923d7ef452e1e86065e4e096f3d82137ca97f7 3e7bafed02e1fedd93f6c356a6bdbd74f0e126ea76d71bfa5c4e795ce4503f89b7df9cdbe4001383 6c922efb5f32efc3c9dc7391079ff01b3cd8d42e21bd12ce97faae7da2d793af33e5f8518de1a179 ae8cf63efdb0765db09efaa1b89c6f2ff270b1a5fcde6a13a9e3ed863e0cf79e4d0cc3f56738bcae 8568fc5cb98c9b713dde87dda29bae2de10d2d2fbad071e6f4f1f6af3004b3b2fcfad37e4211e827 9d4edcb2ffce43f83a27affa550c4f51237b3f7eda2108bc723fb7d33f48717b915ee8c682e2491f 17ae6a6be13a61576e6d22b81e67345df93352967ec3e82439b72b4d5f04ad417f816d0723a70f0f a6f34b77b49853a7d96666550e27fb6965de36f712e16923b5e127aed49d4e729bcf318942f857e9 b09f439c7ea2cf4fe2edd7dc992459688ab5c36683f1c62a559cf5a757f056cde8b6778bb3e37909 2f8f8f05e605c021776e666ea26e6116f55c78465f1ccc7e9a5ed9e6ee0e35fd4c3c662abc3d7e9a 9e87d2444a5f3a96273d7b56715b9c24926a80b9a32ede09cd7010654d329af2439325cc41343756 034b68b90326cd2e13fcc9e54d5cb2fb4e3432fed725cb74e7268298338b406cdba64adf8dfca9c3 e437139707fb496e09424bc981ab55f4f28fb15acabcc7f00e4a8fba682137c28e68c9ec93283abc 98447948dd6bccc06245b1ff9c0fb47e3d7d98f41a056467b89d1930720789ed7ac6d3e82a64faab 75266ed9049107bd2e89e3781730a2eed9f087778bd303e984ad02348bf4236e73d73d3371b9a960 798db932560b3d6d1474c60333ecf6c62679ead943b3d25b0ca2b11e9f097ab8ed3fedde37b8b95f 07fab1f7590c2fbd467678375c79fc3172be93eb2a888fe8bef1aceaf0856c6addea78a886d3c256 253ffb6ce7b2106b7f0cb2ed683d9012bd3c90cfd74172884b9f5b11f34b7bfbbdded3e62eb7cfc4 65871fabe8760ba32ed4404d32ac9607d108a1fbcf6989ef7d9c4cb3d7c86414c36d653423b70546 d7ebe487dd629019eb7eb764ebf0a9b4d0baf1aa4d0dc7c84e259fe553c7acd71fed686124864b26 bbc1155b2909f267670e6447277d391d46b9f8abe7ca3174ba25559ab5d0ce3de7497470623776c8 cdb9981ca2e509b9eba85baa07f109299d06f43df3ec39fc396548b96dbeab94965fefa0ee6b5b42 0b7af38a865de6b4daafceeb6a3cbc8b1d935db63ad47bdb695bc2b6ab3cdded40a9e7f796ecb46f 0b391da4b62d0927ceaddc69989294ca196bfa162f36e1d7692c0673fb2c761b484ec433f5afd6d9 08576538f16f27f1178baebfabcc98db0399e427416ad4f5bccb201a6aabdec7be39466e3d5de9be 3a3f685d627851c948be77a857f3d3b67829a33c975241a9e72458fe782a2e374a6aa5e56acaf7aa ef9684f6eb92d7339b52f162b59b6ad531c460125822f649af1a7d553835cad5302398eb6695bf17 6f1a3f01d73dcfec954ffda52f8a097c727154dc4d3b68cc6f8b2a3f6d76596aac4f0578783b25b5 befb2935137695c770a6e17973d831e9faa4cd80ea42f9e6e2cb8d22be6fb94ee3227987c6435226 8354d37f1bd926d21995c403b5c145dc3d508d41fec008d7c14b14464255e3ef813ae259c673ebb3 6df16bb5aaa72833cb89779a65d7addb8095c9c3892d02311b638624d830eba3bac460b83dab671b cd49891971a3b23c2507f5b6553014a471d7c21cea776aa7cb4401e6a2db92ce8391d486174e5363 56df45a168b43741e39cba5e1a15fdf5106e52252dd44e6c819ff222c273d0a8529feb13a69e7a6e 44aea9dc552e5761c6acbc34578c8f4567069e35b2743715d6682c18f76ae1c438d6fa5c211b4344 12c41fb35ed60bfd051971badd78586d0bcd6f1ac347317e6636e5c15de4607ed46a9f3f2a4e075e b48ad5534f3c5a955ee3bc69cf84db4d5bf1cf9cb1e3b9fee45477c022aaa7c5c58b73d17386cb4d 1f4556febcbf97c7b1c51d41316a8dafd3c17aa0d0ddde75580baf25a75616d543d53cde53d5aa6d 52d42485e9141314f614131d3314738090042b795118ceef52b3376d4d5d6d4c9ceead2157ca72bd c23ec2756275f8fe3c3bac32baca2d58f09addc9cb14a241aacb738fc7b8de28bee79c3b823cd65b 437bb6548442c6bfb0118334c437dd5dc9191a37a650adff5e13b5f2fcc1544708d4a4ee13b14b4d 5abb59e57541f615be617d4d1695541627cbf1bc5e2d67cbcb6d39cbeba91867a49cadec61571589 f1ec9dd78793ed34ea8d8667a43310ae9578488c484aa35af7425b286f1e2dc8927db14296267c7d 77d638c9d10db664f42c467dcfbf620a6dc073af76ea2ef7b5f26b7baa5e67e7a85a831e6fea1e91 798a6528b8f2cef7a80a3f580b959450d0ca62d09a906b66b325e542fd456c3b3b8ce854cd36013d 648f809ed03bc6184eb034b2ec571a9af1afe97802f7734333da327adfdd8952b76b71b4ca143390 d2bc9ede12c6f402e1e1e0717bae9d754633488d2628d2ac55186c5a1d6de82535d9636b8a1d0bfb caecd93c55786f782f2fcb6eaadc04cfefe4b69cdd536552c604962c3cd76da2d3ce8ef0fdadb5c6 f5e5eb8e85f9368cf5cee5167aa13e2e469afb27d6bbcaf17d3b015a0c7a35db5e6e96130b2f1547 2633577afdfcc250f453b7c37566c52a262ba3f7b71768f6a5fd59e01c69c96ed78f5eeddcbe28d5 5ae81b147bf5cc8a90f5edf252f617e5acefafc9b5f909485924afc4f6c8bd88ced4cc1130e7a3b8 ee7f6a38366ab6b07e7639c0c89058a1667d7245a225f4fd6b104b3f89f0f3643b0843e877c49273 a504ce4d6e39766e769cfd0447cf86e6b0d71ecd547de02e1b1df091c8d64e87b2a2f93a5ef9866a 7a2c1c5646d5f12d542a82c628e5e654e993b2238d13b93b23d944e760adf080b0b6b8fe0e421cf3 730fac5f2da731f2ae40a8d9b22b68954c8b88e50806c2a8c7056c1f2b271870db3cb4001a0f6574 c6861a6ef10a2dd25e010654509c3f3eede55419bcbf2d6d5c29c9b3614ab0473d687735b49b7594 da4bceadb5509d2f36eeefeca3be32c33dd37d1a36f582618df47af916a156333a1e4c4a431c2f94 a758bf5376d04b54f750d3ed1fd0aae45c11eb147e10669ec876f02ccd1030e89a7568c17c3428b3 6ddaa516bc0d4af9bb9c29b6b914532c7d0ea362bbee9f8b9b99922b6e064a7ef631b5d5448353ee 680c868b4193f22786becc0c347a926db7735cc4493d7d8034b882f05d11d40b0bf8449fb1934bf1 94dc27d40f90b0fe6ba6a023686520777d374226d87d06bf66209e05f2880f833d7382445a794099 e72c536a2957b4b8b9e14cb13dee2b857d2e1a17d041d3cf1f2b8f4f9eb0865f2f476ef0ae187962 5a3fe48d2748e78f18929b35ccc5d63aa651cf7c5c97abbe477de6dd70591dabcfbbd84dd2259a17 7d43081f639de7fc52ef4e530cb2ad34d3e3116e5c4a2d64b2e79b30df675568095adf8bfde2a6d2 1e97d6d9d1bc54e8adbce2963c05456891b915f42cf2c91f7b6231dfe33654eebcc834b3b763bd9f 795cac658613e15bfa930dd0b4a074e4f47c26ba198e45a2cc7462a4ec5675b7b7e236b34bb23abe a367098197ddf252b593a2763fe9126684d404e701211c52de7faacf4d795f2e76e7138c52ab1d68 e91584e2f6b06d1661e2a615f4e1a39f0f338549beb7aa2c729702bbc9511df1907932e22dc36dc6 df9f67da81f6705a882a4cca6506ed546e769f00252d05a07828e5407175e14171bdb781926b1f80 471f1ed3f6fd7afcc9b86e8c53fb9e569537fa2de52f3bcbccc992f7b9a3fe275d22b9aaf5bb7fd3 d89702a2bb59ce107bc5e82545411bf932ccd5b34c252d66ec71b19d0139aa976e28f4289d29e94e 4ad2861ef08ecb232871b70728d585022829e72a28b55b4a8cd704943a76fca82ac50f685412d95a 6a16e63166618cd773a2aff39711d39d9d07ab257b328e8d65a0d3706ed3696a5547c61862f82b5d c227c8da4376d2e56d0e09b1d172eac0d982d42f181a2a6566b3349b2a145516404837d16c20c4d7 0084e6cc18c23cc67c1be37d0610d60431f60880f05afc7ce6103f8f551c00119ff851d28300541e b70004692e80e0fc0d40e956da2a73e5bbc94bd77b7fab6b378354dfdf6458cd16e4432777b5bd56 5fd2e2f51ce3b492a276d4f2aea4899e790d91b93d5c16f76e7990a3c9ac9c92b5880330e937005c 4a7762d0fd18c3698cbd0b60080a6268f718e71c806136b9b81c86570280916a2f46b80230aac74f c8611880f3a90e805ed70d80dee63bc6ab34ae7103305c4e6aa00fefaf9fc473acd5cfe75bbbb408 764d665918d7d56543aad9cb2951563b781a7d9e275ff11e2a6505375fade4cd743ebf940142c38d 18cd768cb10110ec6801042f2c62f0db1893538cd71b2084580248614501a4586ec5704d80949b3e 404a8ff85168590148da88df2023ed93bfe681c47fdaa34f98efcf2637f026a37ccf48d772dd5afd 9c514527fb6c0d9cfaa6912d6e47ccd5bf8b54fe4ae3f848d9a5e016ce87850a9276d32b67300028 7fd400dabee9f1bf6e5d800a9019a395849ca2c2cc8b713d00b48147318c3440ab2714a035868de1 ab00a5393bc6397e0a338c5fc1b2f103643e7e8fe225046829889f9cc21833534290fe6ef54213cf b1ce41d982b29f0dbf0536c4f7e6bae68e43b85f5d0a224f9c873e8a348bcca7483c6ec74c93961c 80112d1360adc11860c6c4fa8dd924c66901b01ee2c750ce319c27c0fa20174322628402c0064262 51c406b725c086c61560265a8a716d004c73a600d3c77780493a15e3dd4a3230fa5856a2ba117527 ffe639eeec0a739a5b485a39fef530687a9c2f15fbd0e89191a8b40f703493f8a17f6cc70b69fd0b 5fe1f7efb0f731c233c0f75500f09b558af1ac003c929b311edf9277030f1059fc19e38403226729 007f090b80bf2b6f805fd0241916bf6cba49ddbdc476acbae30af2633bcea7333bc6b2a8f80ec668 6180b9544af844cc6556ecf4fc2bf622119e954bf0a3f5fee44c2426df3fc09bd7c4419c244f301e 00648b4400694c69404e2125c6ccfa1d36714d0172635701b96d7601e9135b40ee0ab918571e907b 67dcef0ee8e69fba7b62e3105cd96bafe052a54bd5f85d72af64382f34b34689d72f09fa8fedf88f dcfb53deee9f391389ccfa37e9778d804a99e540453969a032ebce41650e9f13ad731b1641e53215 40e52a8e41658f9c40250008a0d217195099def4570646398bfe646010e5ddbeda5aa627f83447c8 10fab4aad95c3c32fed2c1ffe939fea7a49af88cff9d2ef14fe1f74ff5b68ed505d58bee811a409f a056ba11a0565975403ccaac40cdec7c406dc1d5406d0ff540edb6fb2a50bf3230602ccf6b51f75a 5b4a6b97180f4e0614c0189f752511f95b0cf3bfa38e936ce33f4ee3ff274e388998f853b32dc974 48f27b796d0f986d3b0f980fc383786d33062c9f3a03b61d7f023b5cc98075d4196077b9e37f65b4 fe23a3b7d7fdb1f626f90dfcf6f191c4f0216cb4fbef5004217df3cb42fed6b9cad035a835d1abfe e890979093a94befa333e74b43e7cfd44a6f9ea2d65c0d9f9d793fe482f3ec289c0bbb43336abd02 f99da2121b9ccbb77f926fbf89b789513649924d9206fed325fb2ba637621fdf7ef5ee96d2dd483e 7c46377818cd2e2113b86733657b272b67eec2a7621e93b26ebbfef9e8a0dddbc1251bf743ceacbf 82e204ffece17921bdc396ef9cdfcfa7a09f50040b2b30deb32f2aebcf68365e0bcffa69258143f1 27e7364944f81e5de2924d0265ff3362227178aaef5b275eb05f429ac0cee6b7b7a6d7107f148263 33906f7a6bd705b0e293aba7ba350b517763c141df7b767dd3e34eee78fd19f8f65ab879cecaa5bd d52a677bdfead8ae02fcc3d217c3eb12f6326081ed70c8e9a37d666e92913e8b46cbdd8faefbb4b1 54128a90f8652752818bfea143ff0a43380fdf532fe436dc3e902f97b31f0aca3751794b6daa2fef a9a3e9b57079e457cdfb0d76e5f7115ffac231fe82dc7d6dd1cd6f59276caf79a70fed9bf38bbe95 e754b8eecca2fed198d1d7b369dbb5873dfdd885f5b401d87022658d8fe5c96fc22aee36dda47418 266d7ea21092d2615f2496d93f8ee3c4cb9be869896d7213c94b6ded109da1eb31e464a9a6e0c502 f3721b87dca5833975f89c67f1c4f66edb54f49a7eac2bf891545fd9697a762f4cdc78ea31c92d5f 98e54999b255dc646a6315cad747818e48232cac6a66bfac8e879791b71e524ff836b0ea3ed67f2e 875a5226acf7f1ceb39ea36ced04c989bc6223e81bcaf037a3ecd7dce916a7d78a13b666dc9c0a8c a6fd1c68ead4a1c5dec4adf323cb1359db2aaef9e5582df2de2850597f841d6a5fab2c7f32c9337b 1d5e86e2634845fc7b603142baff9c350b89a49aeaa03da7398ce7afde5a30a4d25beb7aba38eb16 c3fb45572b2b420bacaaac751949ffc1d72ffb1332f0b51b6fa9754f5a356fd5ea025b1170e23fb5 b92b5998343fb91f97ec37742e685f3833d4437178e9fbf2201ab9ea807e78bdbecdb9c3dec771c6 3da7e1d9bdf4ca750cb7e5af8cdcd6db7415d8dbeb7ed73fe9f029bc6bddca2ba5861602a9e44baa 764cde6db523979ab699fced3bacfd36ca42c21fa3ec8976b14522f8254799e8d0f6b30fa7262edb bc8ffd667019613be73934c9516640df8d52ef3317d15eda654943caf3b5ae02d538ddd72b82ae62 acf49ddc06fd9aa261d78aaa8623d650c9073bec981c6b75a84fdb695b0dc3539eabc941a9177677 d9515359397d10cb2d8938c892674a3349a9910fa9f8f090a63f0dc9a6ca79445221cee3824b3db1 1b7fc5140b133293e69b8ec6b0d73f0e2fc66ed37f5ae2a6d748b1a191f3889bee6b859716f4d2f1 69193e739d8b15163bd42b44dad1ec49b69954442976f3422bf5dcb32e3bca5394d3bba7dc72f5a2 deca85d857bc979432376bfaa38ed7841fcbb3d8ada7d38d70d12937cad9775bb878074f30dbf5bb 508516793eda8f723ff2e9578c9e5f366f68ea5ce1cc581d7f6ec3cbeaeef7b9e7636ee4d6e44857 e1cf540d07d1ea3bac99b4bf6d5b75efa0d8a273961dd98ae4b46fbd5a123202addc619193146256 6cfac309dc54a9152106d68a12b1d78e6bf4f95353b8b84013aa797cc45b6dc5ad3ff7dea96e7799 1cf739f934d7a0f4ef5fc3a5ef059f7527ad5b0ce391c4742c917ebe3663e7edcaa4709da3e6d9b5 3203f6d8bf19ad4a61a5059137e898b54347793a93aeecb426e396046bb6a4e0ad45d31fb4e31373 6befc42edd3e3642bbf59d7234caa015099785f612aad95e9ab7e45ebefebcccd17abdb1a738e798 12b87889acb1923f99309b5a3c776b6767f120abe0151ab97efab5c372baa91d02348a517bae33cc b3e10ccb9260a73b2b761c8ccedf3de9a12505c59e2bfb6f3da84c8ef1dc48709459efa4b5a4d9b9 d9f4ed4e4bec8a82dae84f3b03c1f43b167faf0e1d9e594dd7f51931f7ebf5f3f6c82d84e395cb04 b727db62d36966b3c54a4c7bd829d3fbf4f2bb2149a393ac56eb119a5d3dcfaf419542da696a1ccf 48289a8b469567691c549e50f615a3f076b704d79e5b25bb35cd0bafc66880a56a83590bc68c8d52 ca6aa7cafdd67ede971b39a7d4474d9dcacaf14ae3f2b525f0934e20d56756a8728bd373c06585b4 c5ca697ac96c25da63a093b0a7f7b3de99c6a0f1bd76dcfba91a4947c5ea705324a8a822709495da aa9567179a54b8d7605776f8d7bb9c0e2615d235d43ee94ee96fd719df1edf0996fa905367f635e8 4ca04c5e326f345bef2f09aedcdd9799a23a72c1435918c7bdb42bf56db19ce6759e9be6782e9757 79b608996d06a6cd2ead7b8e590bcbbe5d2347fea27a119f49792a2a4c079455ce9d290670af8aad a9d94abde66365c74b33e54c99934969f1bdfa24975a134a387be23b9e257164f9d2700dfef8b896 e93c62f45e8bbea01bf66214e916bac295e1e3d96af47344efbbe3a11faf12dc79042c90d75c2a6c 1e81bf14ee563c8264484c64e01bced6421b97aa9745a95dada2d51e654debf197f6a94f2b75555a c46bccfea6dc98d88772a6758948e99289e7e04e0322da8845e1bb49f4d505704d6e0eb083bf7631 a2568bd0c1f488a21565d6462bedfe1a1d6cc93b3a98534fc78ce24efb1b1c3dee4deaea4f008642 ac399dbc5a78fbd36d65e422174fc3cf8bcb9a7f4bb311bb71a8566d9003df6bd8a97b75c1555ead 5dab023aabb88f897606b9620e2699df5ca7c466587289b650dfe13b573ae15ad7796187d7358f19 db2a899e2953402b6ed445c6447381d4ceb70b3c6d0c61e8bd262498cb955d78dabc7ca71cf0948e 3ba17b3037a7320d0f461743ed0e416ed7367685b3a099d7a0d26e48837c4be5da0f915a167d3e9d df4c98bd2a77aaa330cf54c09baf912b9715c802de9209a8c276706dcc7671b4698cb023b6faaea4 31c23eacd1339f39a043a87e476ee341061937af18fc38953998e34d159a07e91994c6f5b0b47c53 a5524e388ba5a673724a2e302fa5e5d5b9cf596b3d9ef8183dfa95d5f1bc6b06f24935b51a94fb9e 3465256d20a97bd15e0d6bc91dea12917118ccd9f6a8674e6990328cfe1f5fe7de7028dbb7edcf72 88d649a54848892245642d22abacb2fcfe6f3a67ae99e7baeffbfd679b732d4d38daed6d3f3eb72b 9fc55c7697aad1ccf156cf9ec7443bcb836c97ba6d2b43aa58682dc86766b92525eb7a213e1cf521 aaf326817bd97501579ec257438df965d7c5b01db9470ddacba0e4cbaaa0e4bb3946032b17c650a3 4935b719fe38aeef9dc0b19727a4631ed942c318a558a9515f2239e57459a4aaafdde022ad4be555 d1de66fa7c99f63426e02bdf039a665b233ecb5b8b0a35b8ac54f229ef74525a1e3bc4c7ca0c88ea 059de09e5cf07195ea86983f5c3fb18684a26810c4cfd694b055117344f5e01336dbc05c4b0019c1 0a848c131efb30674e76f089c2bf87c0d8cbbda76e6eac8f9d523d726d74cd754d16ab1b8654b16a 0d2c3ebb577a5c0aa95676bbbbd45cc6abd0ebb532cecf55b89d3b909d1225b2124b7cee7c91904b 5215f7364a1d4748cdc0fc68dac37071f17d231fdd220f0fa57ad93d6246a51b7c520769983bdd72 192122cae9a1da35a1d7fa35872abc7b87c0bdcc40e048b7a149ebed4193763d1cf99bd47cd0b507 b3de542327d6e6650cda176d69e9b3ca4ad38cb4fbeda7d5222e4f54219e05a5dde1732a0cca4b8f 5be5789b3e45f32a59c1340e5b370316ddb6f722da12a11a7298c00d8449616df8b4abf6337d9b1f a71ff19348bad41a1da1c9f1f88452651a4bd5e66d01acb2b7af9703d4c3e608a05de414e34a02b4 17ea0035aa23500fcec108dfe457fd9bbd5bf620a938b7c8d76cdc16b4bba3cbd34cebc72ee1fa73 b6e24d0748c92c287741da6e7d96cc93ddecc03ca84426b0be676bc8517172994136c7a51f16574c 973eb20c4d655583524ba39d5ad467fd54e61cce80264001c0d2ca2d4608032cab0b00a353cd1893 09c072ea15600c47038c255b00c3163380e199ed30dbab7f2f25719edc6ddd55b59edf315ff16bf5 b0410d9bcb4dc5ae1fae4a23b14b5490f3832a5ea5e223b14be4ec36dd273feb530333d2bd62c6c5 cbd994c74f28e08bf113293659970036054a0c31bec1a9fb9d561aff1bcd01162887189b27c0ae12 11632702ec566f032c4a2f62f877801d6c166047bd03b0905801cc6f9d5cb6da3ffcb83a9aec6a6b 9e197edd2a3ddd455363966edd2a6f4c792acadf17e9c42e21bc29f2c96e67dd4d7658860604b2d8 ea4817ed4990fc10720077973186693186588b61eb31f65d808fb0710c7d1d637706f8984fc558d0 009f3095186b0be0537595e4d033f21de32900bcbfb1013ee8ef00debedf07027f884f22f2cdb3b5 2ba742932f597b63065fd6895d4275f047f7c72ef19d68975f75f78f5caf540bc885f01ca2c7c6bc 95ae9cc81220028a4972e859b910c3a9c6089b8098631620fac5418cde3cc66d078841e91e638500 c265f231e62a2086423fc6650b88d1380d085b9300d1ccc7bfa6236740545ba9441cdd5d35b75fcb 88458f9bf77674785f0dd9ad1e7fec12e3d5d4964ced5016660d8c60e9d07e501f33b5c1c9a93fca 8cd9820182daa70cc86328c5f8c880f4b17a0cad1363d4ff5d4146fd18cdc4734b4eb66f404e0b64 8ca508c819a7c7d8c73fd55742400e480c90ed73fc87cced1490f5f113901a4538a97b2d65073a91 4a3ac7ed52b6fed6ebaa7551affb97974cb48b4f48ea256e5b1a7fdff4a2c5151211787fb181efcd f230d5446e06a06ebd06c8a646cd9f8fa8dba215e3d60554949f006a5f5fc698ec62bc6e803ad4d2 31b63940adb84a0cbf03285f5ac47847805a2f12012d356d6a8072caf16ff6cbf1a7ad80eb21e935 96d48e3b79b382b52a8f01a41d1528aca5dab569e95c740d7e051f0bb9fe09814954ed5f919b5c5c a736f2a4ff5337a6d14aef0f92e4f6e793e620c6641ae3b50134269c62d8cf187714d0b89c073408 1440a7aa4e8cdb1ad050ff0de874250fb20fa41de319806cf82440f6d42b779b2d81493ac76651ed 7fef4d7373473f3fb5e3f670e416aaf5539d397f609682d91684f2d6e3046176790ee8e57af4ab69 5cc0e77f23897eff4223f87be85ddb48d2c15c3b2a809ca93763bc8720d7191e404ecba741ce4e97 62ec7b20d79c8620a75b24c8c915eddf9d633e646f55ccef2c8be3b26771b9780992856e22855e6e 9317d4587cb6c9cd24e9f3d55effaa1d2771efbf3c137fc926be3815af317a1fc0bc610ab068af04 580c6dc598cf008b8b67c0e61122c6be06587e304a0262a11501365b62014be7cc9fdab15ce9e434 7bf249c9ea543e8a77be37ce53eeb2999d8b1f01e3de05186aeaeddfd3f6929b4e22e83f63edfe53 31f127fdfd1301ffa4bfd32ce0aea52ac8e3771be4097b05f27cf115e3c382bcb03140be368cbfa6 b45220af964b205f87ec64ee5ee3202ff0dabc06dfa57ca6f22dc0f04dcdecd293605ac5cec2350b 6d2cf4f5ab0afdafb8f71b37ffbb5ffcc72ef127f5a56bf95fa96f12f84648f2de5a7c4ae002c118 1c80d0afc2405864cb40d8bdfa4088c21310ee33120847bd0e849018fd7160943bebf45158e58b93 dc48a9ebf8311c17d2586b94f977e7f8ffe4d03f9b90c4bdc926fc37a3f09fccb7141889d3e177d6 7b9b01b1727f02515fb240746c03885bdd07a594960625b22025d55e21dd0325d9dbfcffe2d3a7f4 97bc415abc170fa871dbdce1d6ed1861d6f172d32fe7477c4a797c5fe961085dacf71e3e5faa0176 76a0fdd799728ad47dee545c075cf86c46e2f1d30e2b3f5204b81f99bb40fa0cb76686df06367641 3697962e25a286448af0574bf69ba1fe8fed4ca6c73d3e35337f5702558ab48351bf91bd46fb62bd 84ee995f506e7c73aff1f1635ce747a8b3f60e706fe2ef02b1bedeee2bf9cd965e30bbc086b3c7cd a591bd6cf86df6be2e1ed1b73f665978358bcf9757d0b0fd5daa2fe1e94ef71ae9da7c6160e0956c 5b2245f80979bf69eabf74b27f32d448db5d2f57babffc9cf99905876354218f90c9323bfde17181 9d56850dbf114ac914ac3d55f1a553bab69ae5417d05f55fcda5f2b8b73c5fba763cedf3ea2d02f9 3e5890cbfb786e62c09b850612ccd843ee149f91363e13979f53e3e730278fa5f773fa2343f8c783 9054657f22d559f86f1fc237a00c9f2a3c3bc0566316d8a9ed72ede2dd60f5b16ac7a52714ae9ef6 ce3e170644a6e6260265e29777089bb13b889a3ad94f6e1a9f37731397fd0893e2152efd8c0e832a a3cf0852463389d647d08cb786f0b2e8ba0d54f70681be390dc8030ff74de65c766ca13ffa634148 4687257dd99f96ecb77f9a747913e3ed4f80fa31a36ff3def38bb3e67c5f6b98331b939da943c9e3 89cbc8de78cc9737a399c8ed46d0840b876a8abbbabe928bf703ccbd0641830103032f64fafb561e ed9bd90ae584568d75d84bb3d87384aedc8d864bc37e4eaf8324524d8b1b6b563f010b0afc6a47cd 4afd0e7ce4fba66f914e821f9d6c623f4d5ab27f5cb2bf32d41dfddb25bba80ee1d9b4ee62cb696b 406e06569fde5bdf6b711d3b670d7b97ae35ed39f981d78d06d6aa5b7cb6bf6dd9dede2e032bb43e f3d1cd9233d3577c48f8e90e1c8484891d22ae6db074b5b577ac4e8b7ec44b6e5bbac17abc402efd b864ff11cac6379378067e2403df5ee742ff0c733f2a84a425abbcf5f7205055a44fef38b277b171 a61bf511c17e8e40c9963e69d992a1bbdaf1d47bb3a3a2a06dfacd7bc76c90a0d70edaaf419b0c5f e32452ed228b167d437cc32e527bfd3216aef15abf099a62a68b37c6dab5a87db6724b9bb5c9b506 85974cddebea85a4209b20e96dffa47dff6ce28f07015b84e73ebde57689e79605a1fd74ef0f4b4e 2d3f1d155e644c6c6ba16d23dba35a7babc7b4e84b8f376cc12aea97615bd29d524f6e46534b6d8a 50bbd1787a8376a38c8cbadaacb9186ad06eb7a8abf47ba7fa5d2652b19b95285b8de2a350db4fba 9d9a598d5f8e42ef0a250559d9aee34432246e090f283ee9f28e3ece2ede878a7f72d8c335feef73 b3734b06aba9e96bbd659b3ce83b23ec49a17e71f96b339ad08fa698a2bfaf9e8db1924b6b1f9f42 3419c7710ddae6b275354b31aa6f51bcda6085921238a2ac9077b951334b46470ea7e3a1cc41e1aa ead4e94b25dab84845a4c85279dc390fca65b673903e97d32bc6fd93eccda479ff154b7cedc143f8 2cddfbf455dc778b5161d1f16a9adb36c89965845db5abf34f61d878cea99936abbfbcbaa75fd775 787fdda9d8697b5482eef5aa90b7d3bd66160f2f391cef806c976f70f532bf7d5fd6aa42e69dabb8 8d8c507e6eb39572395b694a33abef48d0e5e495d4827815fdd116171b65b326e2297e5c0c16d35d d15036a744d331e34af9ec68de686383d639ccf406e0f2b0e66e3f686fd9e2d0e052404fde2398e5 6b9a8ca6b43abc7b9b4a60efecdabebfeacbe1683a92d9cf685a75e491578996a375454407dbf258 1f1ccb6572719566e6e22141a7f88552cd87a8e80f3e39117bd162d1a8685a61bf5839851c427c97 1c82dd1c3ef9cb9e6178678898bc503727bcd089020fa77385c9a33411869afd66fbec8c24bad22e 97ea6807eadab2ac9bdf1ca56c474b0f0945097cb850a3efb362f5321dcb155735d4f2736dead267 a77e3db7924cab76c9b3d57e09beca43d19fdb7311cfba7ed11879fbc2010baff1921cfa08dd8c80 f157cde078e112c8f9618db6b8d771bae42ae5c2939dcfcf2c5b6bcfdb6ccd2dcf63b8c1fc10aae5 f1a7f4faaa595ca3992f3882a3c46bd9b68a98545d7c180247ec1ad5db65a26e0e73533ee374a932 e429469a5dbbb992f7e817c435d62f8bb833578badf45a2f1c3a874e21f7bec6cf99d5eb908f4f72 667ca184f8f9072a7f7b36f952cf7e7053fe8e72a925c7b10ae3d498d5f4693318a5afe28573ea45 ef3e8b3cbdc72736bd27b8658e6c3afbd9d91f2b63e883c8830e5129f586b3296fd50733b2cd94ba 402fe1b593a646ec52313bd7ef8947d5c5076a1982545e2424142f1ca2774e38a32941c86f0a557e c057eaf9875737f21252ef70d36ea7cf552bcb29bbd81d56ace2e24766f5e222066bb6d239fdb6a6 e9bd8a9769fa649bd9704a7a590e5a7ccf3d29a72e319463953a31ae1e750985dd34b2f78d1132cb a9fd33d1ad76a7f2a6d8d90c2eb956afb4ce342bd7eeaddef4944dadab51c3ca041cf592efda6221 644b143f582fc8fc68b7c973d3e746e4a0f2e69b0bb0caf65c67e1d6db60b467c66230bfe0e60cae 3ecf5160bea5cddae1920d431464b9728da49ced5224ef45ca2087b03f235e4de946947751962847 b7163111260b62429d82c90b0ff561d32937fa3cb1febee9d585849bd4215d2c6f44a517d6585cd7 4f75e70df6f2001326e5f92e658aaddcac2238418de6666d0563554ea2994681e772c1aa2ae628ac 5aa1f717ab4ee7849e913da537dd2cd7b98da8abc4ac2821a87fdf612787c2e4499632284e4cac66 8100976d03afd5ca636c798cce18cad90486e6db4dacdecb4fb1e579124c2ae0d3767786613883db 41b3bd5656364d932f18a294a31af0f6fa51696f77aa3e59db935603a69bf4a15fa1c20f67c33c5b 87152c67dc2f186d9e433adb85f61c7555c322d567a02a25bcf90639d4950e59227b0362e2aee644 a59ed9e1f37329c233d5118cd50fa93cba2935eb68f3fd72919de11c91ec91fb9eb023d990ac236d fa3c42766a693d9edf48cba565d74c5c1d36522ba9bf54c75683d1d6443ea3d8cbd4ad5a66fdb584 0dea83225be7f5fc3bb88acc86ee11d9d37b86507dd726c9e1c4c8116fc2fee669c4643793885431 50f005fc31f04c97ec62ab8a3ec5d0dd74836eda8f0b4abc8a10d26e4e19f818a1b5ccf955efa623 e2ba4917cd4e2a5dec34cae928c3f7622cbc716657ec0d6c6a66f5261067c6ff0fcdc63faae3c96a c66b04d1c46ad761f65199b3cf5d699bda4e0b4ec7eae4e3530f9921951b430d741726a6151cc617 0a45e1999063b1952d17310c1855546fc58b3b321b7490f6e8e9c2214e7b30dbd7bf8740865f4d5f 69177be2d0b3af8890f4d8b552b346dd4b41dbd7232593e9626a56f5daa9cfac3819690b6dd0bf52 17a70722d9b688d6bcd5ce478fba9e5a5dc4faaeb9ced60ac3daa7521b57cfa5969e5b150ae9f777 9d964fdb5e33679a8c408eab3e8af9f11189ea972d8eec856d1631338087432d5d82d94b4ec9f077 aa9976eb72177a9e9d0954ae86416ab684afa93463c040ededf30091b526406ac42cc6f50e10e5f0 fdbf0188d8d0638483a151d487fd420672bbb5bde974daadabd51a1cd0e6afa1761b84939fe90d5c 81e1d5a3947d8ff7c2a3559b7228b76fd1dcab201273f242a094b6863257340fa5ef7c094f8b6eed 2b3985c6af3a0f8166b79c9277f10a363db89a407d212e40d68d15404ecf2b40cef1a622174800c8 67d50228687a00b9e55e0089001f63df01c8811cb987e274ea8c32fcc45eedbda119b671a72552f5 6fc9a2a92a56bd9e630c31b14b54e03d0f89a1065df8e9b13667dab5c0a446f9521947d5280b9fb1 5306aab46f2095e18e08a8f7cf24408b293646a914635807a80aac18f218a0e63e0068a7984cc142 87210ed09116ffd438d389b1f601daed0080f66411a00edd03a8d15b0ccef0d1eb7d32fac2c6f6d1 d4b4778d913111e6bdc6babd33d4d33094e54979992f37220b2d0a7a744dec12f4c5f2bf431c4848 2954d1f60662d3526798019a7d8600964789181536465f8c71a8018ca75a318c7e8cfd0260027b00 98bcfa00acc66763ecaa00539a3d80894810e39a0158c9af008ced25b6048c4b6dfab7536add838e 23df6aa58babf6f5e6cd8dca1c193574a8d0557b59a929a79a402c7526129ad82598433db5a05eb2 6b635b80cab0a0845c4aedf511808b751ce0b92e1de3540438835493e41615b4188e15e332023856 5cc7985e00ceb1f177b90507f07ca11ee33a0438691f014ec9f11fcad02ac09ea9798c4ee83cd785 7db79e79ef3b47aab76d0f747a63409ced35f4cdfa1b0d29516160fed825064afc5c8c0ed40bddbf 6d16447d5ab691eef2ad409982c901a27aa200c1bf1840089410a3510504e56a31f66d4064b13e20 32ca3cc66a070838fb8831c200811085242046e63a20d0f21410187c03f8671fdfddeb4807f8adbf 01f83e7d77c0ba7fb1374afd9ad48e5bcfa975d43dffb3d5ccde7d96d8254aa1512f0890318319d6 b89f28886b2fd0e32b6da7a19c9e5c8e4d4aa60048cd29c4708bf1a75e39c645012447b66228bd18 a3718c870f48a2708ab1008024b3d918f3caeff6f16505c88cf504245ccd03e295b70171a642406c 26d0f7c423b3be3eadbdb27c9957bcf3fa196ab7eed2475538752715bceb350baffc39cf1e201cca 96f56e88532c36cf94b56f3d58616b806a1715400daaeaaf8fdad2f7a3b61e7f77f4fd914772a451 2ab30094580f626c6f802a15e0184b0e509210ffbcb4ef03aaac6f019527214011671150e47600a8 747007e44326bb0da69eb1725e3663161a8f8cb1c0f2ef9f897619f638106f00f9ae05f2ebadc4d0 c3befd21082273808713670a5a9347fb57e3781a3f64b31bc2faf5d15fc83b20dbab8d622c7d9075 c03186fc8cb1c540b62f1662843ac8b695c4fd9075d113c89a9bf87b9dae0cb29a3a01d946f505b2 15924fe6ee75cefa3adb1e75f378523baebd4f2f5fb2aa982d2864a592bb7936496cdcc3037e1cf8 0dd8dde7eedf75e3ff504cfc075e33403fb8750ce71c23fa00fa2993312e12a05fba09e877661163 1901fad3a2410e080d40df600fd01104017a7f2e279de3ef45b2ffaa1d93fdccb430cb0a06cbaef4 02e94b1338b11c83fd5658fc0a9e13cbf11fc5c47ff54c7c51deef12248a890a74064cd38600d3fd d080e95955c0cc985e8ced1a3073f3039801c703c6833b80d93fe25f5f1e31c0acd6f53f9de3fa65 377a55fdc53d283e5f90c3e52a4c8d5217328ddc951f07f1c1f67f6f5172ebdfccf9af6aef1fc5c4 8f58e29bfefe67040c38217e66e2e4050bb87e5d03dc000c63cc8e807355240988976c0570c78c1b e374065cb8a601b7b69b3fb5e35ca70ad7d2f5f1b9246c76b37c8b7cb5b2f3212ba2d7770d4db522 fdf2ab739cd49dff28269292f1ef7e717233ff124b246ee1ff1bfcfe347d9392afc7e980bf430b20 a437772064070c10e89a0e845c6e09841202625c8a4090a676930acea452c1ab5f674ae2c0e0d70f bf4f4fb1b382f50a2097da9ddfaf9fbdf4b34792b8f74f0efde7a6ffc4cdff7384dca651ff333dae 13b57ff97b77b503284659048854ba0a44211a01b1758e8038583140f4fa061087dcfc7fc6a7bf47 9c15b544def0a8fad6f7c9e60eb7f4f92d60abfe752f14b7577a288517764a9fcf0e4447a748259f 2717a13fe1b34941a1b4c3e1e38cc6f183173f1bed7de1ceecf4f741d8d2deb612b0ebbbbe714875 b276739fc81ff3369dc810be97957e279a2545d9443b30718b4afa7f686f41eaeea17732d2f6f7fc 8dec5ea58bf55c2ba7a8d66f865250691da10e63edfdfcbbbb23c7d3de969e5bfd805d296ee2b9c5 aae3756448b375f150f4fc31c3fbab4f8fdfaeaa91745a7aa272f7fc4a23e5618b19b520fda834a7 778a35b373cc31deb00d92f8107e12dea4250b6fd8db7f2a26920cf546dacdc3b9f77e9fc231727c 1e94d004dbbd04a0cd455d22bf478735e955f55ae396caa32078be94ff677e18519defeba83237d1 6c6316ead9d6cffcb08b591a4cf953eddb4c99146fb5d5785cec87a35979fb19418b0a375411dc72 fd66b04df2ddc482f0af96ec5f3e846f3a99846a4158c977d6c5e0365855cfd3c9127607cb455069 0789e716568e331b972e5327cbdf2791c5bf27c50b971a3f1d111e4b77011b7d862239aabe057a04 4de5fc5085e4a2ebab72d9c5d60d7560104ebbbf6ffb0327b4a3a5c3de94a8e7143f74379a1ccdc4 7ef08d54ff991af65394fdc8de2bd987499777db7e155a3fa601bf88f0f3bd7c28cdd8cd5a9db8b9 be3e1ee7fbd66856ecf6875eb9331ec2f3eec2c556cdd520d03ac1800c5afbbe4935bff1439f3eb4 2e8ecd3423873d77df3d87b752ddc8ed21dde26b44d9e3ca2a6f7d16b78a05ad337a47255a63d337 c1c9c44e27a66df04eab4d466ea7b5774d33a9ca269bf8574bf61b527d77da1f9dac74a591a12761 b4db4843fc805c5fc43ebd0bab0e1beeeb3d870b8d6ed40f3bdde263dfb5c752d0b73e337f68cde4 6062414b7fdef1eaa1df8137fbc06c90e1b11d98d12d11ca7e235593e73023741b9c7e99cc6aba00 d1fda6ab1ec3a6889eb9c633c8cb7faab23f9281642b9360d246def878cc41e0774b36f866a8e347 cf61bb50b7786fa2d6675af8fa052cc863e88e8ae4f3a6dfcc174d6c972fb70d9a915b7b3babb64c 2edf34c23ed332ec42c1d22fa382a3f39fc2b0e9ca9579e3b96c6e1a657478d666fae1a341872c55 57996155f51dc155b107f95082914f2791aae43309b6f4246baca0ae51fce9f2fe51217c43de9ec3 cc7d7b2c167c0b5a640fa6dfc85cda06758e5a26737c1b36efa7f48bebc33afff2f1a65bf1a8a698 0a98c658f1bfaa63ede37b056dd608240dda06725dcdee35d5b72e6d15bb7c1cc52890d3da7ea46c 6bf4677a97ed1a46542fab45ad2ae09d7925dae5ee15b7ddca24881f32ac9c84d17f8abc89ade11b a6b87ccdb364301a99be46f6dbe43e1a19763e58e8fc73ec379e7323d03e2b79afc9987caa7b867c abc387f243f53bea47c5ce354831f81aa2905193a899629396c3492b2f73a96ea9ea2813a5e23696 c9b0ade7f63e2897696e25cd6cf726c979315bf206915652c5955782dfcc254629fa894f93307a26 2c5e03031e87c98eb466e5d5d8c43697ae11762943e71f6bbd3196bba6066d6abd3abc2f7d9f3a95 a04b8c15f246cc6b66115dcae118ddc81cc8ecaa4e2d75ac0a19e25a7135f45e7e06c4a75ca6e88c 34ebf084049dab6c49e52d49f4dd55a3184c6ebd2205955685bd777f1572688715c240ee08b6b19f 27916a8bdc26a68e89abb4e121d2f181731a5f22fb353ded3a487f336be57246a7e94a7545939169 49f5db4a59313849add10fba59bd4c9f4625f242b3222261b73c6e8603e9b3db8f24990ea625cf0e beeac9127c0dd6a2dfbfec8bc1687f2a929fe851306b694808571c29c48fa902ef187e3d1f1dc97e 5e64475bee7995d2dc784054b8727168b19fe7753c37e54e6e5ca137a44b3caa44eff66a40d6e2d2 88dabb5de5fb46be7eeb60e346059beb6a63a18835fa5ece569d2ac2c4db060ad2677b2849d07159 29c197614d6c0843ad180cfb7a917c77db05b3dab585d0ebf6055b9d4ff8cb7abde405e2b0cdbbed d7851b77910f0752f5efdbabacaccc8b8c77a20c06a978f3dc1a52ee39bc8e71f49619d5e9ad8476 67dc7d921f296a3e3fa08f835cf7f9f2b18e1fccdead703739359fc3f65283cb82a3646f8f7ab55f 37d83228d7d0f8c85964442c9a7e9f050afb894e1572a9362bd88acef397d02af1427920e7dded54 e35ed9a9c195471b9b9d63b7219beea35e7c9e5139e4d6d6f099c33f0f8c6eb52a85ece1b8d6b34c b93ca3baf328226ff48ca5f2aede8871febee9352d66d2d2d0373b629f1b6df25db08ea80e01d219 e33a38dc9aa0b30aea38678e6a6c546855a4ce532a69a04f160e9e02f86b6df1cadf353f93172f7b 9caba4832c3baf072c9b3e8705663983ab0c9a86bfe737b966876dd13b5473e86c6f34cb5a426a4f 9d97a507c533538c1ccc3205e2996ee98464a567f8f4328ef0eadccee3b39464e09fcace9d482baa ea6e73c3b2d31fbd8b766d4332661b08f1b1e3e1af466d94fa065d6a16f7e7b2b0efd86559636b22 39df3242cf1da4b9a9643dd84c1d7e30f5b390ce6daa329e23f60d9a6e8b663e7b5cbba5acd5dd2b d425f534a89e4776c928579d90c5c960438c89db0dffb845188f1f02dfe734ccd3510d83a3c9186d 94840b8a7d70066d68cf26eafbbde1783612955fae0e3bbe43ab4d956f87c0a0f4915749699a449c 150b7faeaa2577de97eaaca2157383b4c08bb915ca2a17eb91232be1f77a4f9a8ed7d859d63e65a8 def58451fc02c9912e8cf364312c978971d9a81365dcefe0b3fe7388a7617685a976fb84aec10542 71856710235c28f0219f77e1dce27aca74912105e79676fcd457e307e3b4d5f8ce2c1e745e57b527 350a554b3b5bc5a476ac97a109a261562daa5d083aa84cddeba8d4fcf48c42775196f2933a9a4dea 28d97017dcc8682b47c4f3287f88325b4be39f4f9fc06563cee069e15ac49619ea7b39368668828e 362e460fc567e1026965c9037c18599f4cd7f0a974df8224e8fe19d8d0705e08a09292ca40c3c5b5 0c3d20d31cd53547ef9fd72fadfbb19b4a27d84ca5d6150ff3cd59b4c5eb86d57fd59c66fb7b0854 aa776e5e22b5875de0f3819a4f0d9b7926de2e84ba2ea23b513ee2173ceda49f987acf000c591650 b4899628143f77f248ab3a2fc387fd498399126265f2ba304ef7237b033dd44b049536029a9af2fe b73e9a4a0dd936a8bd374b50fbf43f60516e1552a92e680c3792d7eef7de9cd1951b7dadd342dfb5 a476dcac09385da78e3748be5746e7b2776facc47d58f9fa836983f3d287526e174eb3542162dfd8 f2fdfa5e158112e2e48ab426eb377c248e6998699fb14c7e7ca3d3032223408f41418624b865a4de e16190aae69125582c1a6700d3a70c8073860060866ec778f8203e3e0080b9be0860fcae0d4936b4 9ce82c7f43487bd9d81ae6e143d48d47452a373386cca9473a8fcaa5ca312ad7e5fd4eec14973361 88f66ccebbaa2a1d8e8f2c29792a846e2ef83573de15c2f4c06622e8094a1f48aa5533a9e9b14da6 aaa3551e2c3ec7a40003b7d00680fb5d27c6cb03f0c2b902d8c31100eff725001fda16808f8500c0 7e2a1de35a01f0446cb9f4e5d973c4b3d3b5eb9374c7b4d89a6e8c2623a5b10a66c55f7609464b95 b578d9fc1dee6841dc5a183e8811dbac198d6c1fbff1b8f76ac08869503768dc481d5332f5b80195 393f01fcc1e2dbfa28648c691e2078a61aa36e0084bb0e01922faf0152b9dc0152350880c87039c6 2669db21b5ee1e204a1d038898abc5f0ba037b8b0d7a937330b03613a1d7beb07d53ff4c8e8d4683 83ab2a7bbb73d5cf708796b1f1e625b2bdd9319108e78e9adc24df857301230815cd149057944a6f d1e45a5ce4883e6254a118033cc6910148982dc568d501f2bcda007989d318de0ea068f11d23a401 8ab51580e25917a0e07a02686a4d0124729b00b9636ebfdf2f8ebb8bde65649113e3bbe4683ba397 a3cfd1bcf96397b8ea6a4906b5122d6d113a5d1073ea81c3bcdd381b099c4ec0d8aa88584a8380aa c4eb09d06ded1e6300003adfa1005d64e2db5f948418fd6a8cbb01504f4caabda837f3007a60cf31 bc34408f3c17e3dc0068d89f02745d8e003ac51980cee00e401d6be63cb623af0bcfe879c73ced67 adbb2f8c9289763f76891b7351aab556bc6e38cd2f102fdf7287af9a25d71c8c29802206ba5ff6c4 f4b88513000b4f6f809d5210c0d60412a39903d8a4cfc7d84a009be25a8c8605b09e3702d88c0d62 8c2280cd4934c6a208b045a50db0417ce4626e90643658c72d004cb3fa006ba09bde677eded88d8b b5364f6f6cd51a153d4f8733c8446bbb2547294a74b3acb3b57c618439298e904efbec70cc8ff0a6 b235e002592da590f3950478d4c7624c0880ef97497d148f4fbf01bea4a418aa0af0a163c4b83800 1f151700b74607808f89778c11f5ab80dc5ddb00ef191b801bf149075e8b8fdb1f1f467172eaa517 ccd1dab18fa3d91b8e0e2d694f7f17504dbf6fafeaa1d71fc9901b68e22577667898213f399ea96f c9ccfd3942cef38e0165ae9108888fc8fd8d8b988f61c4df08c6728c48ff55419e6ba3181b1f100b 26b1f61283511a102ec1fe2e20975d408ca02320ac00034463a000a2325c0182e71e5d74d1b959b4 5d8bdad123736b017d71f9975d42f83cab593607cf5e949c4b0568e8260734e4991b0d908fa80ca8 cca702281aaefefa28010ccb807cd2f518ba09c870da8ff19801f254d9c5583f00b9c9a231e60540 06821e238abf3bef5d01b9a86601e932c955df64870a0059bf676c834c819fdaf1b03f0046863fdf 13bb4432d4ae785f76cbdcb61310d9890422ec60697e7aae2d7b7fd58db3f98df1cb33917cf41b49 729b7c42dc2c902599618cce32c6e908b254f913e39005d98c588e71897f04b656208be45f807abd f3807ac7cf11d42538026a3ba3ac43af8375f26e1d6fbde767b891ddb1e75ae5d69d27aa6365d6e2 ebe1ae403f9af1ebc4ae523a672a3773f64b70fc2e747f2926feb24bfc4fac2780b6d27e0ce312e3 0a25b2892420b69f0aa0bbbd3ea07bf95d8c471ad0c6bc04e8963300b4a25f13cf846549386b16cd 09fdd3397e52e63ee91c0b8bf352cd45e43e47e84bf0cecc503af8bb6efc5b3191c4bdff5b31f1c5 b7f8fb0bce0630e079010c61c38021613ec6a80118aa308e713f0126bb2400439b0a6072d5296098 641e016058a6d87158b960c0ab3356e79fc5a812d8ad65e1b3753a2c979996c88676c0e071f0be82 5cbfbcfc1541278a8924ebfd53edfded94f82d94f8566a63240de2ff1601c77f16b0f74d11b08f5a 0b7030ee012e7ebd045c6ecc028ed1e3af499535e0ca4c1a7015a4d24aa7eaf4afce714f3c886ea4 8f38f3d5d1a87acaf91a6090a112af13fe9ab6f77fe3debf22d5ff2596f8afa96f92bad243291921 974c8fabd3bb1851fc91b529037e3c7101bfb62f803f3768c05fd846c3bc5b683277af741d6597f9 ed8ceb64e787aa84f6df2d1c5862f5f257e7f867c3926d4a36423b5349b5f767805d72ebdf7e6f12 372725df7f8d90fb93f926fddeafcba1981d4d4051e93f40d16c72a0b8944d507c485b507cd2488c 7715145fcbc17ffa057e03876278f9e73805c98f198ce977d8785ab780b90caeed28985cac97b738 3b297775726173133e1bc62e94b68de3719655cf078f15ae07d8e1eebb407c7d0b30db7de5f40e42 e598de5c1a3b6cc36fdfecba1812655fbad65aabeae3ba5caa6092fee3434864b73f19ea4c5ebdfe abf6f67b217fe493b5d4759f4f12a80b3b26985354438bc78fbe2b1fe0ae5edd9163ac1684f245dd 38e84c5b47fa585f17f76edb1fe71c6bf5e99add55f566f4975ed1182de1b131f31ac05c2d829ab3 9bef35f732a7830398b1c7343575585749c653f5dbcb78db88f78f0ae1bb813f41eaff6dc926e964 92a12692d1f0a94aeb036c85fb2d3d158f1b07c1affeb3757ffe1e1dd64b7bd8b48d2e0c48279274 50d5e939bd5698994d54f33376a715a70ead48533ed4e5dff3c3dac6587af4ecd14c9a0e879ebc59 0ee155e6e436b01e32085a05b9bfb7c1ecc783405f85ddbf5ab2df3e345457ef3f5dde2443fdf650 d72e565556d533ac7bda33b416e47cefce426d36993a94e34d5cc6de8ce3a5ed612c45f6693413ed db089a988fa157b1df4378d149b9bed2875dccef620303ef52c985ffad3edba78fc38263b3f129e6 c5b91b3dfe911b748b1f73638f6b14647d7ca866c9f868f8676ad84f4bf61b9527fdd378f7e1ce9f 0cf52740bde899dca418bebfb5ebd1a77fab0e3d6957777d79630c82faca1a909b4dbf6f92aba113 9aabf87ff1b4885790ddd5aac7df169b6e34d8ecbbc5e72ab4c7e5d5d52e83e3d39ad522a8e36911 d681b7186b36b2d54a3bb016666bdfcf7eaf616fd1cf54c608c787aac181573341d296bdb0c3ddfa af96ec3f3ad9df196afbe5629e9cee9b04873b36cde57a0e97e3bb6e0117edb194fe4b281bdf4cc7 535f7a4745d3a6e9ebc0361b24d26f07667ad8264fe969cbcca7bd167de3fe71caaaaf66341fa24d 3173e71be38661689f1db6d4e41c8468d06920feb864bff831de7e2503fff43abfd9e4081ab61f7f 8ab23dfe7c3ad863717db3e4d4f0d951e1412a5e675b70dbc8ea786b6fe9d9167dd118c31694bcc1 def5a2ee943449e7df6db9e95675b5f1f4f46663ac0e3a49a4ba1e391ab41b8deb2abdf355bf9b3a a9d8ad9a528ce28eabed2775a39683ca7b395ca691a4209b601d699a92a88313a7c44f0e9d781092 843769c942f3d1c46c60da226910778a1b23ecb17bdd11a1b0194da06be3397f3e1ae54cf4d63e3e 8034197bc175cfb86375350b28d5b700a362f153b8123878a9b61f22728d7ee79ab25d2ddbd58be7 4cab0212ee2aae5efc949ffbfdf78afc729959f4a4cfa5799066cee19de0278cfe46e67fbabc89ad c1fa8c8f13d3d730a74dee9d8ec1dea84e331ae38346397d1a6932ba9dd55572eea98d5cdf57826e 37508cfce49b0bd4f683e1b9463ffb37391c4f9e3207869faa539b662ad16a8a55446c4597c7c651 90669d5b55822e84515285ba2bfac36d2062f182b118cc71b148652e8322055bbbc2deef1d7f65a7 bfc3e86f893c6ce1db645f26c286969993db3aff18d61b63999035687d56d506bdd015f26ab56bf4 43b765f623f5aa8e2cb99568591c5744949f95c73abf2c97496923cd4c692f4127e95452f3d548f4 07daf7ad08117bda4891022e5d306b7b51087dac217078dbe59dd67bcf0bf402c9bbdd9692777bf8 202ff2aa9714e193687f104458d4731eb9833547f2cbb6d17b3afaa57f6834cad0bb528777bca090 9724bc97c3a153a80a905aae8888284b9f1da248328d344a9e9d364af0159862a300bac560f8ea17 c9f76b5430abaf99101f78bec0a1f89e7774ee9a8ff67590177313921b77ef45f613894d56168309 e34dabdf33024695e91c83a4377506c930dde4713e4af7c94cdf825ca82b6566f78e266e8f2d8b5d 78cde140b0eacbdc435628cc67ab4e658d969f7e139366ad025952d92c2362d1832b92af1d5f302b bb78ef2c36df87a7c0c19b1aef3456f57cb45de97931bbe97063ebd8673f97db245ebe657cc61be5 4306feb4deb986bc21e860952dd1143634b3668b5a6673d9cb870a4387a76c3b522643e24dfd0830 7a5188da3305ff5a144d83495f0d874b058d592a1cabf14b495b3e034cac88bb035152ebdd573170 8957c12c7f520297d96778019b21799172706edc71b2f15a6dccb2b3de5860a1685c62547126e7fc 89dfc8e1a9b0431bcae77b7e93dd9f8aab6cae3238535cfb0291ceabca11f7ca4e2344af32c65f75 7089cf04d7145efee44ae3295fcbbb647fc3f4ee6992b296b408b73ba8f8d4878039685e842d94fd f0d4ad46e5915a4e45e5bc4830bb6fcf46e8e2d36bdedd3c43ae2c7a1756cedb374655cc67ce0ffb a91ccef5617a0bd6244db50e4cd67cbc8ad4a981cb14771575b25fb31de27edc2ff037808f78a565 00ac66f934ba02928aa2b5f30869fac9f59ec82657a750b403ca6388ef170766ffcdf74ae92a63a1 b489b799fa30d57ce3ad8b8642caba76b270b7f2e2ceba04ef9d6f97374ff04522f5626be66c9fc3 8fa735ddc2ae876c47389da9d3f0145179f4fe3d6127fb4e264316e402490c0f35167f97ba25bc72 3e68d8c2a0ba58e6de98a15a3d38209b0b07e0dddda4615a83d5f485520669bef338a42f7486cab0 ed49bc9fc8a5d43f0d18b13bb9b7f31d9df6b2ad5efdf01db0d1ace2f37b5d8706bb5af7d898562a 956ca7847bfb6a81933ab97c395584186d099db3473f1d90b74edd271ebdfa1e7f7bf5333e553b77 3c751a016c3139a2184cbce3fd15158a28a6b614443f2d4d785f4d7f7b36193b97dba42f13f70145 8b2b0e89b9463935ee3ebba9323fdaa6c64e0f493daff9e2b0e1ad2bfdbc5290baa9c3a8d0219527 9b748e9b50137cd43db40be5db71ec9517955a4f6c2d2155e85b27819be747dfce6d2eb82b0fea32 a6b6f8a79a5d62ca78b7427d70dea1da0ebd204191792206aca5e1bd3d2061b37ce6335cf65c4e3b 63a209ddf1661f12fbdb556a02e7ae608eb760901e80628cf8d522ed5a014877aac954927467c70d f13b509cdba023db5efa2c99a688f1866b71d4f72e645453c85ce5e2315cff4c8aa329cd100a4fa1 ccd536682ed7324288ea1767175c9e36d74830ef2de0fdaab78173ecf498e1e4e9576994760ebb37 742f3d616888f174eae5b4c55425bdaa83b995b241fa539bc7b89f41061fa220438812c890502fc6 f10032e9f8d42693210a6eeb40694eb13d516df891aa9a39b32a1ae2d1661beaabffdd696acea93d ab2f9ade4bfefa352b86cecee69fca58e560a625e46820e1e443793eb0baded9644e23650edd87d0 122aa1e436f53a57cfa90aaf3dc0dc732090c9f904c894680164ead33ac868681764acc977010532 63318a718d7f60e25441662a0d4066469c40c6795020d3aa4b83e352d47b6365dbb0d6d982da3e79 838afece0482b67e6da91fbb4485564ed2ba63ac8ba77269c8bf78b6cd21997495b6f1c1f7cd6262 7a82de28094df6e9c245f1529553e481cc60bf01198f8e7755bcbc05997d0083cc01a341e66e9500 9c7e37019c690e004c834d0cfb016051a063442a8099d118c06cf30660bcc0c4d87edf23e85fea6a fb679a21916d35db796fa7e8d5cca7a4e9259c557a6514ad566ee787d4bc878762375c2ff837bae9 335b2d5da3eed9760e475200c0766e784ccd28cd07b00e7d211d63f4a218612ac9a11d968861e501 3c7bc8009e57da00de9e26f143bebc8b71f900786fc69b75601b31de7300afd64f00fbf3028027b9 e68f56a4a67256a79d9db75b519a68ea50fcd2a8e977bba8f07eeb1bdb5521bd0a49fabc7a2bf6bc f2f6c723cce87785049f2a83b6b22f282dee9a21403aed0020f5c10120a563041029f38921213106 748c5b11208d4a3dc6ba0b90666e0e90ae7f0248af9c4902e25e2400c419b400d2aaac00a2100020 2a520688e8769c61e732b0576ac3e9d06dd04d6ac74d6fb7682476094568bff86a9a39e23f12e171 251db0fa5ae9678be256c5d103cbc2f960fb9d279daa85cd0b405be819a035260268a1f689318001 9add9000a533f918b54a8ca50e509173638c7d8096c85b0c0f03a82495005a8e1f6428b70c004a74 e3df854c15a0696cd09b4a703275117383b1692fe4a1f172b64eb35ecd767eec120fa851ae2c47f9 9cc81dad77de7bac37398e861d12da755524d4210eaa9e5b6980d99307c01a9327c0cacb4f8c5b06 6079820418111f6918d14fb4a01871ad038c14ac18e309c0286c1763f0025896cec6d8c800a35b03 8021b92340dfe9f81e9e3f064083c1ac07599a671907d2fbe91c4f7161f2639738fa0fab36d2f7aa d48a8edf43e0c72e914d577c6a227b3dac8517d4f404b71980bb35f817cc5206e06a0589d121012e 4e98df2d645c8ed130008efa7d8063a417637002388e4131a61cc089829604c4a9eb1860d1f80ab0 7bfcc7b163af0b303f1f74116b15748ee976f0533bae8ebd556297a8b3a77cbfaaac1bb56294d2c8 3c7a73237a907faf0874dae8c103f4ac800dcb318070000d8819924b92dbe413fd9e058481b08090 73420ca30a0861acc5883a80c8f2a35f0e649a88628c5040e498222090750b10a811df870ffd04f8 151200be7dc7f76bb13dd9cd307beab0f2edbb16688be8e2f46397308e865f1b1f57dd1fbb04145c 3086dd525752d96a0be452bedb293fcf570139f24440ae82d2df48be96c0f2a5184f05900db61543 4f1228b2bc9901b2c2ec63ccdf80e4b3548c65059082dc0564010e00990d2040c2a332205edd3120 cec6df9de3ef44bb5f7609efbaa8349167ab286e88027be0850c35875adf0e147a22f753483da0ad 5f65e38556ff2d96f807bbb6f6377ee6de2d4c407999618cfa0a50eefa04a861018ab165016595e3 3f653d5d40d9d3635254eeb67140e9a5f8cb32e701aa26c2566e9f87cdfe89407eec12c950bb9a8f 8ec41bc769f966bfc864df59e3837556d36d3a239d0620db7d9bbfaac57f141349e0fb7f1513ff89 ecbb33fa6d9c506f31ae487cb0a8428c8f0ee8d4740668a81e013acde4009d415abf3d131fb273ee 1de8f66852229a24b27a24aae3777a2d593adde5572e53a61f2711c7dbcdd62dadb4edc5afc6f170 3efcc97cbf71ef7f3a25fe411202ff0b7d334802e205f400b98189839c0b97622c3a2037acfb2037 a23e31ee0288d7b95d909b2c8e20d71b65cd48069cae5dac8cea62d1f9a7735c3d523ac307649e30 a8129481cefcee4fddf85f8a89c42ef12fa744e216fe836f0efcef3058ec3d005be7b380ed9c65c0 5add3e60c7e50360270802d86e985caec04e3763c006f33b60b75dce508b04f9533b6e28874d713c 8e7aac3d7f57c9869f26e1f2168d7e6dd37fcb7a9370f9af56f12f974452edfd93fefeb7e037c97c c728f36b84dcda9981fcb91a81fc8765008fc78f031ebfaf015f39a5015ff5cada09a5d3f212b54f 3f9d636a7ffabee3412dbb7b1e292947e83fa6edfd897b7fcb84ff164b2481ef1f93f09fd4f7df26 612eb1c4274e8742fdd90105ebb805057f0583c269580585b73d06454cbf836259e140b1996fff8f 983781413d6619a77857025bbe058cdab8d22ed73e5f2ac87750c0c9cddcfbe1183b0c8f336a3939 784c7fb6f7f9e662a7bf186fbbaf10ab20acbdd701bb7aef360ef60ed72e75bbfa4f2bf55e7d1c18 5e41439a5ec253adec61deb9bb30d0c3ed4786401ff26432322c71a9caabd77f2bca263dd4df55d9 f9edc28e06a9939bd6d1e38ca4c9bd76dde7825016721b0745b8b54bc6fb614c5f4bab19b72eafa0 fe525e2a8f89b28447cb6f9ee6699fb9b108e489b9209793eedc44e7ee2cd4d7b3e9c5dcaea7f1a1 7b9e14a37c6a3c1697f9d167ea3a2339ad9c131542b2758955f6af96ec2f29af182542d96428577c 33072fdb18056165385e478dbae74b4766bdf484d4ded35eb7efe8b04d34a7fde97366e33330bdb4 c6e9297f74914964cd8849f1e252e3a73363c6d27d9c1fcd4ae3e2089ace2a4315f2ebaeaf866d17 dbc0c9ac6f83acfa7d33378d9cb057651cf65eea25f96e8f7f0f5749553611cbfef810b4dde9fc97 0fe13b46ea4728abc5e721733383d4bfef1cb7269179b5c7cfeed51d7d06a76f8378048dd78ba10a d6beebd79681dbc86c0f83405b9f0606b6bdf6f7c6fadea70fcb9713768e90c39ecf68cfe11fd96e 34ccf0dde23b5fb5cb50cdb066ea7adcf174eed4810f1c6ffa16642783c2da416f6f27b19d6b9a37 3dec1e13a7c44fb9f39f0c35e9244e8aa1828c6605891ec2338c73b125260ec80d5cedd37ba8eeb0 e153ef39dcd3ec46fdc8eeba854fdf7e8e9e437b2c7d26d667f6fcbe5368cd6ad0ca825650d05131 f868fa067933b103fb6e1b8c8ab64cde668dd0bdd40cf6adf47547ce5e9ad10ae59baed6549a2256 a9257dd93fa3b89296ec8fade17b617c12a07eaf93fe95a13a51ef627541d7159ab03d2ed5706b16 9fb4743c35cf76e0755e301b44be6462bbd2d7295b50da6458d25a2657d05bf4b5621a76a1d2d52f a3da57285b9ffe16cace43edb3d93ebf91aa4c1178ddebf4a53a7c1107bf75b24fee4727fbc58ff1 f6ff4a06dc46ea79f91918f6ab25cb2c2d6881ee4c5f7b846d83da5d5b26b3b81b616ff58ded0c36 f2d3ba23fa68339a7844534ccdb28de7c2631a657896d73e7e206a32ee97eb5e2b50eaf031d4d506 fbb495a08f8d6afb11bbaad11feb22db7204572fab5eb92ae0cd6925da95df7f5ab2df82fff778fc 13f825fbd0b1b320f81df25a43b3817e7aadbde90f0cf6e64e9bd1b8b1683ce7b2af7d5642a0c998 70a87b8670aec707d94d6d30b978779c858f62f0794821a3d2f71a8f9a299608399c54723297aaf1 554769952b6ea3ab959fdbb55d2ed3e99934b31ba1047d4f00d4a2572bc1afd54af4a7cc4d6c54d1 eb5f6174d2e5fd46e53f1e0435230d9284f7bbd3ec7c5a6fba5247d33ecb825ef774c6521bb9544f 31b873bfb6ef9f4735fa719ec9b674f4aa97d9ceaf0ae94d508996c74345448fe7f2583f46d2677f 7e4b72eeffb5f79d5daee338a2dffb9cfe0f45cb59b2ac1c9c73ce39e79ce3ccae3fbcdffe48c915 6ccb55b7777b7a66675cf71c565d8824481000411024b77a473b87e172af646165cbd1e99662ee62 5c09b86cedab22658a2f844c94b609accd96e057d35e1b26dbc157f72972467fc4f22686d1614675 9f56bd664f08ebe7e440d8ce3b7c93fcd9edcd38e67e8f8055836ed994417742baaa9164dc791e27 d34eaf3d9673b433e1a223c026ab3254414dd9b24f76a598333312278dd24298774a3b81358ff47c 1e85026ca7b28393e96a8cade6890673de0d764ca3d21219afcb918049bf5acfe37534368a77bf90 74c56659d993eda54c64a212b71f5d89887476f882ed4854f2c798360d1b68b3b9b7ad29e9aa863b acd34ba6784780094a7258101dd2b0ccb925e2c4f9c5a4870e09f3361915581399e4f3111a9983dc 76cc9539d9ee68b2d54c70c0340af10583ed9b273ae0d21154afe9735056c334691ff6923d7b2c22 63761b8139c8c9241a53e4bcd437e74e79cedadb282fc425c6e2a91be3e47931ec3a97238170d129 a97e688a70cb46b7dee9b5cd74722f97c6a498ec378949b76415582386f3f9d0191268786638d976 e0d96aea2033e7e5c1cd78f9839f6e97b1286d3c59d254d8c397edc376a863b799ea733219d99f6d f38913add66c2c35f012f99ca340080298e1dbdd08c74b154aaa0297d3588c9fb6c69c0485366d8c 79b7097b3138891c1c5233e40fb3397f42b70928b1bccec6da8cc996cd7e255220b1e4575d69cd95 62c49e7551ab831241ccf54e74bbd8c768e3a16fa2c2ae8ed53e6c76487bccb7e0c84977e72029ab de6fcba06b6df369218f6f57c9162e0b93b9b55aa17496f3b9ca5abc3e7bc4dcee761be64038be37 07223abce2ab766d85b98541fb9e59c869c654b8d13ec733e3fe2ae20ae67ac1b02b56f665302ee6 ae74ce4e47d7d5b489d312b9e785d461c49c17b62edd2eecfa54afb29ed863deda949c74ca0b9232 9737b64cb47c205693ba8ec827c7667cbb5822a2e1b2c720585d49d263691c23718b3e32aa980313 ebd0143e86f7c6917b6d334c032eaf81eacf0bfa2c9198e9d9ed019443e10d5d602d217b1644dad6 e428bfc062ebf17177bd3f38dc6b78d77c21e36a5494d77ce4a11b5aa1cb6dcbc09e9cfc820e7226 388279ae665b98963562b56935f19db1dec5e54c7764ad81f1dc0a7cb38da569379c2dfa2a6f3607 ad21ca141173b271d45d860da9fc29abcfce7c1d6ce31cad31b12922b79dae424e5de09409e4401d 374ec029d2d395866e0397cf9fa1f5ec8b6cc9647c4d9963e2843a857d337211801abce315f951c1 a5efa6c3b22d224bfc76b2b7b258ba75a0a291e4d8b68c459ab843f2142c4d3815a06058fdf45035 079d58cbd46f18fba6484c9a1947bbd0d6905ac6807e999b107ade468898e470fa7595413d03ce82 a1053c9dd40a6021b305262337c0c2f922c0dcfe254c56fa928da3c5dc6ed5e2d35d8f9156ae3a1e b9f0e88148e943dd996fe39ff5a5a1a722808ab36b9825a45436efe6cb829b623a46bbde3ee96f57 367e38ea593d897ac53448b5b386d4e198d42f6b72599fcbe6dbd856d71e61c5f161a53bc8c449e7 ec0710d14083abb2006b6d3d009b24d200db99db30696e00b6f711003b987c3059550176ec6e015c 1e598ad348c499ab58f6723a9473f2890cc8dba3554fdf1c0a93cda39fe927a61e67ded5745a8c86 8c441f0fe8141eef188e442698c859edd372e244ecc2ae99554f989bc6496a5bc04ad6635a774898 4a3ae7816d8246d8d5075e363f077a303f003d2e9b819e1bf240ef9102401f9de5813ee6eb017d41 0fbf163a14d027028a33459fb437803e6538027db048166977c1933d03b32b6d19e4e4f86add66a2 805fe321ab6faff3ad4de395bb91cf741dc344a424ae829e387766293713f2e08c7d16379a88f261 b8b598b66257bf8676a6ce550459058d735d02fa30db02fa4c660493c90ae86b2c00fa7e95803c60 9180fe548900fd192f0183a5330206abeb0c0c386061d28b010396e800833e8401fd6ec314725cc7 af3e6518abb75cf10287a1b35191669ea582a39668f2ad3bdcc1dd746113c788db35c5b56d96e381 b11766c274d341ae441ec731d0d8991226ae8f394ac33230d8ad0598041a30a9f661b29a0183ccef 81215035c2e44c01432aea52fcd0a94d1c182a912a3054c114187a4d3d30d4640118eac61430e4c6 0360c8774dc0107748f92db70b67daeb4830655bee7d7131223923be55860f267405c257d4e7746e 5f2fb4728ccdc9a1b801c19af2561cda29b4e59a2e9bec24084bd890dfebb7e2b2075a8b611518f1 760318751348912d360646ccbf814903c0e46c05465b0876d536f601a3e848c364d40446c9bb84c9 d1048c72d5018c8e500e18197aaa3888ad6602261d4f5ee66df14c90edc792a933178eed17757fc4 c8cc1dcaed123e29b637bbfd89cd51b94458e229535fbd44385b2ab8711fb6254cc955f880b9d29b 2130998936309e981e302e9dcad593c66571058c83c1011887162330aea276988c65605cd3216002 9d0230e9d81e4c7a5b60c2bc3830e98d5e60dc0d4ac038cbc2e6f7e23430f6a970ce0d71a4076e7d 3ab13c47e231e76219565eb4434fbca2db257cb22b45b9039e84d191f445f7fcd957eed0e30d96b1 3975a2cb12590c6dfaf252bf07c1fc6602ccfad6089876e319304d974b609ae10760ea3a31981461 232a4b0e987aa20726b53830f5adca0d97a67e09161a1047980c29601a4683c0d4246bc054047b60 4aec44604a3652d916df28a68988ab90c8a7d7b91808ba53e1bebf1b0dd206cc7fbd4478b024a4fc cab2e54c9660c79e77b6d3b831a3439c665cd993369d3fb138000b1bd9008bd5b7061610dc02f33a 7e788f429e5980b965a680b9e89361d2090273c99a01e676a909cc1dcb1c260d3d3057781e269b18 3067ca6d608ec461594fd8ad042a0bf662d618c3eaa964ad514b88466f35dadc8d8ae1c896ce04e9 7531ee3d95621ec7cc94b00ae0d858d329ead4b281a09c34273b7527d698011c5829b3ee9aa0ad09 25b11ccd7a605990ca39694b3f687f8f425e7b80a52ec460d228014b83e85f03900b16334cba0e60 294632c092b40d81059216581cdb00b030e356c61af374bfc41cb746de6638ba9c560379b04472e3 0ec5d70e49329c4d6ce4402dc8f2215ab7e2fe75427f32192580f323e221213a38c0b19a1558b71d 1b4c762cb04ea9f710e4ce300dac5da60eac95e21858ab662512d25aad93c05a73f860722c026bae 3d07d6588600565f340aac5264948e0f071335ecb8ca7b26eaed1293b3b5e53daf9c09e57609de3f 8de9293e559ee2a1d4aa622c761d51104d6704403061e50698ebed1228512e9b80e06b62f1723049 c22e9dab6e989ca3005f3bf330e9b6013e261730a963009f702c4c1661804ff3758077a50dc0ab04 03f0ac3903f0a81ef9a1537356dc24051fb7531eb58b9a1670ddbccb46aaae11570a8bae629da1e7 87f68908846703533e4b157490a6fef7c7ec50a2387cbf5c318112e5ee0925815914cfadc4c30c52 220e6cf66519d828471f26a32db09945334c1632b059924960b3325d60039b13b0e9c61220b6fd12 20a6c3831a73bc07034cb95d22b8b424879eceaa5b904561e465e341e55a50122c775b7336467474 fd0e97fa1a6cacf87c95db25ee92fbcb26509233a00f4174e3c462f47ed9c4ce06c8424671429209 22079319fc566a1901994a7900998ed501e98ae9525c3b8ec74f1b9d399ce0fc6b9f076fb61d39a9 9de006e1ae489e0a7dbd859ecda69879b52e7ff53e2b0edfe9b40a28fd5179720ffdfea544f10d93 8111a0ec7a0093120d288a0fc2645301145d5b028a891080625d6140e1f60ea008429f944a1db4e3 1119eaed400d3bb6078b25c15f6f0628a9d4b05b138bfe01ebc16c9f11c75777338aeffd728dc367 a2b87e3f13c5e10a132582f8ce197cac02409f3c3c602c86184cfa2dc080d81e303a99050cc1a400 63338f01231aacd15e6860fe1273dc88c792743e9d96f1582c65d407eacea9e27f5688a6e5ee55a2 8a3f038aefae9350ae13febc53f833e657f1fa2a973c584527e0389085494f99d6384fc908b858c2 03b8b8bb02b804b5015cc9ca84586e7cf69a786a22cb254f99a5b2ee00d1f77b292318d1fb2fd75e a8de6fc5d78bdaf47983c3c7ad129fafc77dde29f1e9f8bdf3f92a173bf026cff5fede1553032234 1360b2a481888f1240f40c47408c354c406ce63c17f8035cd518a9db0dd2e8f8a23ebb2e548c094f 6f69b6c427b8a55d5a7a7157775724764b6c46e6d02ea19d209df4d027e7d840d233e2412584097b 534192724c35edb0bb5b7de730d63fbb03c505ef059d6ddc579e9fda01de623d04e71c85ccf570cc cb87a38171b01107bbd836513166e0729c2e06d27357b39a8d457bcbbca930b1169a93bdb7e43880 62796b364eabf30c6faac7ea0e57d334f2e65acd6d68d4711ad2faee962a48fd8cb39a1adab283fe a8df98222fd4c4375e7333a7688ccfb701bcb3cca4a9c3da5613e84d7fe889ec7c9b60637fd6c736 47a96a037f287911ee45b8bf9670d0a4b29974f2f88cd63a5821358bea976cb36a24b799a9295a09 e8cc3d37cf58f57a730091af409423c3816d63ab1cecf43c61a31239b79b1e8a549a351eb136e7ad 2fd77ccdd735ab84e486e1989c4e4835c784c6672ecbea8436c02041a7aca7e96804bd27902efac5 b66f18c885d863708e9bc83031d97a22e1f42013ed70e50ea2eb26e1a8ba2cc9a2c72ea756065d3c 63ef2dea90b8ed79ae4fe6e1027411e28aeebc182a55246ba9bc391e901aad328df1a996f4d7edf5 9139e54594ceb67c49a6dbae33866d67bfde587b7ca9ef80e42e2506535db431b2761c8b71304c1a 262d02f0d3d3741e9e4b99567991e7b3e3e57c1f3823aa539b88d7e2db760d7be48ad8ebfaa3dec1 19abee42647315be80a32917512c55c4935f7f7fd05ac0447325a8cf8ee245c32ce91a1ac783c316 f1a9d512a0ea0e6b63958ae335bbad42ec16e709c9c135383ace9c965a34353e657d7f88e088da8c c7bb8c79bdb58233ebdbcb6437c09dc136986e2eaca149a0ed88582db94434b049a1cdd6ff21c1ab 192b86d526e325dbb0a63ac16690cd17115f0fdba78a78ec4a6e9cecad62546040d931749071185b 2c07a37ebe739818a4826dea3e853db38335915ff013771f71f87e35e5f4c4c6ba5db9b6c1ee2cfb 49684848ab01540f199b4a4de194f662ebe93e06491ac91be8eaba61242761b4430d49bb5d234606 e6d0688b5b8cfd1067c5b627ef3b91ad25a2ec6e746da283592894b6a7ed4512f1b58b8e4e8f71a6 9799d4393d5f9ff3ae7dce88c82c4305d1c83812fdfdc4150e3b90afd0d30c4ee27e71929a87ecb5 b01c3515e3fd44c5de16d2f3ccba933735b764b16e29672a7c4a5a419e6cd18df0ee186f19284b07 b2a379af12288f1d3dfd79779219dadcb92662c7f9d874de03854468434f976325c89693e0ec00fc 9979f1b0a941de4c0d97acf9b85a51620a40a201eb7a14c8739b087f766fcd4c22825835bb6b71f1 dade635af7a1aa082c0e95d5e4a04548a4073468f967131205f768d0f2cf2624e2340d5afed98484 68feb8eefde3c90bcd0bcd0bcd0bcd0bcd0bcd0bcd0bcd0bcd0bcd0bcd0bcd0b8d92ec6bb8bbfb8b 68f8d99cfda57a2bc3d26b6c5e685e685e68fe2dd010a74d342c0e7fce57ed16c8f4e857d010b3d6 31c19751547fe7fb7ae7e98acd5bf6f0e3e4cfbd11ada371a21e3c623f37b5d391c659dbaff45f5c 369d2f4e7ba179a179a1f953d01087f92612e57ece874263cbf9b4f9f473567f7a8c0dacb58db3fe ed8e2e312e0abec9c93575a6fd3ff6a66fa5c225609e9b7fea927d9a09d39679db87eeb8ca7d4fb4 f2b094583b7a9e82cd5d4c7f5fefca7a3ed9e2816a2bf363ff8df97eba91225e9cf642f342f39f87 463005dbbf647596ea3d6b9dfe599d0a29fbf6dc499812d8f7ea144547476b969edbc4cee2f2b759 cf797950ca03e06997d1b1dbe7f54e38c23601f0a79d2937ce896f8946e1a0706193e12ef30dee72 b463396f8f6e60f2e6623f0e4eb3749ce600c0a8e64f76b1e578604cc796d40eed77ccb77d1fb677 b5843523fa36b65fe10162bb895a0dbd97e0bcd0bcd0fcc568446237f8153444b1617405abe89283 efccd34a7a113d25e9862b27f9d9ef7b53d92c9417a381c1d53b7c8bda1388f96c05a821233e745f 69e269e64a305d45175b5c9c9b83ff1b83dbb4d2db2fc05c8b1d9fe761d3f6e205ea45d7eafbe129 6c7717a4bd3be8d924fbb32e34fcf144ce7d6173f5f2f61b9c78bf7b0146a76ff1032310a1c0c4e6 c700d095e73f0c1dd371073cba59a43a58f9bac16fe98c5eb19d17bcd866901d0c8597f4bcd0bcd0 3c5574f3cdafa021fa1de128b6d1edcfdf391f2b9e41d45d1d97cdcbd96cfcc3827aeb741a4d2606 00bd2bfb5d2b0d0371524dcf906eeaa13b0b8ccf9a18518efd5f6c41aecd7ed79d8ad184b317a05b 2cbf31d58c6d518ff4a1ce876eeb7ea2ebc236fde51230639de7942bcc8b970bb016538327b8ac2b 14338d759e3b15061ebbd5a5e86614e6fb8dafa27caa99c27e9cbff074b2fa7cda589a26f3884e96 c20660c6acc20ffc46b005b7b4aeb87ae150ede7d975bd264642ff25a42f34ff6968e6d965175d84 5d8cb2a6ef968095ca299909cc9dc02adabddfad7ec3062961de1ba10a6a46beef4d3977b00febcb 2c5211647c22bb9ead96f11665399d7217804716a367229c948843658e745281fb8e6c856992d82b 6aa9f87c5ad0af0db3cb054f628c5bf33b27f299cb051dd1d3d5837b6d5c79fdf9a2a8e2e2138aba 7a97cb25c6ac77610df56a44675288d9dca16d80da95232bfa8ea6ba143281f44ae04b2da3829fd7 c48faf769623bf5df685f5a11abf840f0ddaa345b70aa85cad63bf3e96b3e8caae78d3a1175ec2f3 42f3ef8046ac5bab0ddab7df7d6b1f24ca596cb53725568df1f768ca31ab33e099b8a0dc71896f1c 8fa5760cdfce4ac18be33811034f54a9a76a1c7637d0e6339c12a5e7fdc1f38abf11c9fad3d52de1 67ab495bf37241177985b432290f3940a582e78eb62764db50beb3a0987dd3b6261e6224b7159d07 bad336e92a3c64caf438a8b8287dc4b1d39c11c400d26bfac44143675aeb8ace035da716ee40d88e 712a6e12dd6276d21822b294d8c03a0cc63e697dd473de3eaa1fb36425cd6398c6622216f52d861e 1ad7e9aaa668a8853f63056620a0299218cd0b0de7aebb598876f4ae4fff25a62f347f359a9d316a 5a7d9f65e20aca3907217689efd1548cb9e070eea6918c9dc743be997aa2dbc26e221f9f842fb69e 4f17d6ce533c6f80cb65d5418135249f6fba9486b46a4399d1eb581abe369cc704ae67be5cf8d476 a261941084f5b0927db01203e6a93fc1a35cbea7e0c9699a8387136687edb8a03b111fa257c98067 851407b9a4d35aa4cb912d456f19f41aa6248eab3a4d476ae155ae5f84195c871ea7bbcf40278248 97a2cbe0779a76db64735197e89ac36fb21a75577da9edfd355b28d467cfaab6306bce74c188ad42 36dac13469edb8dbad1ae169ef2c9a26fcac1e67b32f497da1f947a07154fb25313d5d7c1faf5370 46434ea471ca778bbe3b34a576910594ada529cf578b91898415c1dad59fe8363daf0a56aae3df08 bc269a5007ca96de5c31682c1e71b3aa309209ad9d8c4fc1d5d14fe8a65c820beb700468df834188 4d3d08373005aa8f219e798749c1ad4b9834da9554951570287b198f03546908ad23ea57d41b7e50 76ec1e53cb9b248dbaade0aa088f4ffa6463712c0af3a06b5f1ffa64ee9644549e998c1f955969c0 a97809cd38553219882615dc98f919cfd9f3e3836bd0988bd9d84ab096a5ba6cfe66fe7b09ea0b0d 8a9231b42d3fa2991c6d99bc9cd0773b397e5978129b5d2ef19e0ab62e23877c67a68dcc33f6aa96 10f2fd579ef54738d125280c19e761ffe8152f9c158b015fb18f8aa5520baad644b0aca11846c815 a6ea86a8d61631eba2f40db4f23237c2ae872d0be3f4a8d46d1c3fae5e0b42e0aa185a9ac3834fd0 c542f14932768f13e95ee1c6897613c114e9aa1eb4a0c62ace6a55baf3dc38620f13015975c8e5f8 301e96b1d3af28d85df171c5576a312a1de9ce137653dd02ea9eb6b6e3cee1c8c23ad07abea1d1f6 342aca4a96c0ae36e175d1d033e37a789c5557454f6f64eb670346eefb20a9972ef8f74453a14065 b6a5bbbef1ce647e52d222673bbbd21aaea03cf52767f7fc533d5e4a27038aa0569ef54774c4910b c75df247f48f821ece21c130ac668f1ba565aeaa4aa3446af64931982ee85efdfb203aae148d29d6 8e4d223c0f5683d3c5aa4a807e6c70d17875378191b6075ec149f54286fb95cd38ba2da4143756b0 f8b09c14cfedbd5a6f5a63b7c51a5415cf053d5cecbffbeec4a685032acbdb7dec13a500ad1cfe91 7ea5317f55a2fb27ec70553a17f490c2e29942a3ad8dc7dde072a7a35aab73cd4398ca4bb6ef9383 4e7cb253d2e780ce549c53c94adfe0f544c6de6c89f7b53483d4bff5a3bdf4c2bf1c1a51170fe6ec 247efa0ecdb8c1456491b307fc9091c24fb601cb19b31c1df529452857daf35591f2a81264d9ce76 1a164e465524c6c4e45148a2ef42e2d2104c64355de4306f583d124e89f3509457b3fca0642c1bff d582493fced6c52e721d5d1e237a09b7725f22d3eeee1fc74879395ced07d379e26247fdd038c761 45cbc20b7ac7e37e7de6275625ea6af6e09ec5832bd006c2aac584ad1f4da232e9bd2a4bcd538bb6 4c8488c10c0cb461cb1a3c67110f2e857e643ffa58b7cea9d6ddd3dcc7f44df249b4198a1e7d7f98 9db8098d5ffbc41b1d1a41d384f7da36a0e33c556dcbba265b227880934b877ad53d22706bfba513 fe596884c96efc5d9082cb54b57b8203725a0817be09df8ce57c7a53e172a97876c547074c691bb5 60c79a03990de3c463cc6631a94a1836dd69f5a794b8322c883dce56d6a822d8f9a8f55e00d5f005 45aa070f8acddadf27ae529dd5f427f18ad98036c16e4d0322a3e0b3840bf76bab0300280c0cf543 f7e8c8ca56d2d74e443507c97a42f59a4d3af7bdd31d05344494b2a1c6c3da88dcb6aee60f56d48c aeaba59f993f6a08082ccb1db887f5a9855ca94437ee1f1d48a53a7da55dfb09d72147d90ee641ef d83f6cc65296a5b266b3b8521a0b243ca5d6bd753c63706ba48b06fdc9060661a18d7b47d190111a 69feecd859a2b1d436f2520c7f319a4a86adc6375eea69d6035fe7a20ebcb12f5f9c5e5b53c32d40 987c6cae9945867f2a61d1e4ee74f4cafc8f0e96b26f8d38ffd10b644d5e9e7831f87c70a8da0158 53c32c395a97ba778feda3a7b294b6a17a0df163e27cdb0dc54400c05cbe777f1fcef4de75ed80a0 354cb9c80895ad1e32c49d90084a9db46b721fbe850cfdb6ba80983cecf9d376fa5d72fd9aab5741 319f900970bf30e4b6837cf77d7bec415bb4789f4a374343538347afdb7220a81d5ee5b3a08eeab6 9beac362cbd6db4e94c296e4a38d59d5e9aef4939f70f8e070c6d1c020177e43e36899f38936bfc6 5f18e627d46c8b298e714717e18bad5e7ae12f4573d844cee9887f101c31bb5959d3ff3b08251d71 65aef20fb5e37976fcaab31aa88cafa19170b44773413ed5db79dbaa481fd0b54a1afd51222dafcc f730c55975d1a0a6e45eb5047afce11617a1e2baddd5fee88d4e67bb9a0fba474f4f9e4666c74560 f5e75b7702aeccfdc030a6ee358ff22ca15ae1c1a931426c7912bb4aec23c92b47bf7af9bccb7a4f 17473b1557ed114bf821b0143793e90f3f309679a6413b6dce2bdc1de4f9d03e4c20fd6033e0bb4e 59a55d26a93915e97c57dae9b4d8a76c015db4e0b1cdfbfce34ab9b01d5e2f33d70e7e5db444c7bb 3b069b3e59686fb61ebfdd4e67c94c7a3e311dba9e9752f867a0090d27db9051d48ecaa646fcd883 91d9041d59d4c6465b0f8fef3d9de4ed5c47c8922d7152767504de74bbe2c0cb575fa3a288eeaa37 4c8645eaca850fe7accd8c800ce20bf27bdcb050c9a33845817e5f0edf778898a9b800fb306562d4 b6863e6e884ce466cecc8f78a59064906ec50f1fa8959936f223e190408a57a97d1831c16c453ed4 4b3166acdeae4eac2da54efb745ab9778da06744558915821aac40acbd9d2bad6c1aee0b45b34ac3 e26c71772dc987fd66d43d1e25c2692ef3a17d328f071bcb41a51f9a3e57f4f22d2ae88c651eb48f 90d52d55fb8ed6be9284c8a270234ced8ff60e94c0551c68241db8ae5d4af9009962273d066886f8 4c8f7536f3796cf5a512fe05d110a7f5e03c456ff7f667cb3ed6b02cf6faaa98d6ddc7874cf60b92 90ed1b9fd718977c0e6440c7ef8ddc24951c47ae73f183bbdf00f0f9e5cad50eb97fa3758ab3ed55 a84fb701bec4f4aa3974eb07cdd191d0e2e192b40c56371aa2c0fadfb51b7e966f1d7ff851ad0feb df0bc73886d5dedd1e8fcee36c112df52f28b431743f36d6b36a7a4d98fb955d6c9eb55d6d13fd63 5c09313b6a4a71b930beb5596e5840797a57355b02c3a73b4100d370a6e2c83cb8a0f7bdefb7fbc2 2eece4be8ecd83b9870e32373d6a6c903e6bd7b25949cae449a8b42b68f219d9754302028f77891e a3f632c7a386794c38bc355f6e97664f427e98e4adf84b3cff33d010f371cbefece4dbde493ada8e 57386f759761efbdfac26a43392d01875ecb9d500ce0aa64ebfbfc83f9d1ba2a11d07b667ea04314 375b3f8540e4a2694c13573c3712fddea1a5d89cee15736668afdd9818b9e05035677c89fcadf048 57978be1de299c5ba546fdab34e31a74a38bc84d7db9947a1ee34d986fc521ab0b37fdf15e93a188 5e8b6ace50fe070f820f5cf7bd75162d4e28e323edc59b0d047a0942d942d3a7dd0ff55259b6f444 0b5ef7f0d3583e3bb73c316956a7d558898a3c1c5c8f517c959c60ea13d6859df5c6c9501a8f4cb4 4e1b7f748799479c2bfae46fbcc4f43f120db1df06065530ab086dc7acca75effdaa335126f8a861 c9b7a4453dbf25b3c42aa267d3e6d0ddad49f1cd2ca8d39abe41739778373fe49afb66bfb02007af 66ceecd63c22d8773367797f84c3720cc99a664ede0cdef1104cf8f62c289ebd3cd9afad6d13d1f7 f0b687bd4c4e722b8e6814169b783073e64a9d982777bfeb83b6943dcfcc9ceb11090d3327817d63 e6048d635e78e6cdb0377da1ab627c3cdf5019a3c5eb6564af91f736502fd8c1e9ab99a31d2e9cd8 2a94a1f173c68827ab3ac9a5c54abd486fc9a43973a0329c4481e925a72f34ff3b34eba840dadb35 6ee8281e7385532a6766a47d435ffaba3fcda70829681fa5cbb67cb86f45d3bbefebbd71ea854899 937249866a09a5c8bb9bf6decda087ebe6407c1453cc20ec58bb71f9e40bf65b33e84b7fd4e58496 d0f76ae94d424be845a331a9ed8fc549b532cbe1f848b7f7b35048e01fe23ca86a41f1f13ec41e57 cce1cb9390b94f8d651c6f3586c83e8a5fd7b1bac7f54c25a2785ffcc3f4e47ec1573b0b625389ff 9d3e84f829688a5693526da32b56c86845ebeec27447f6ef39bf7d7748d4d87873c9d713ed27be5a 613277088bf84b805e68fe6c3496ed3670c644ad13a4f395c4eeb28304895e890cd20e5f44a6e99d 8126e8fc6dbc0bd14ee4074cd738322a7bc00f4ac7d7896a2a83fcf4aaa9de3766be2a9dab030760 f7874ba7e15859094c69da3bf48deb20b73ba85bd07dcbaddb178f5d6eb69f6fc856d1dbe6ef0793 1e36415817a734e4c1935159ab0d3437cdb367ae59a06be5344688ea876b5705fd7898b5923a3fdb c8aa98438df8135349b9a99f46d612aa16b39a0c9c51ebfe14bfcdb9e280ae6cc785a3bbb2a8ae16 5bedcb98d8b33b9099b98adb49c73ff6bce4e785e65ff4e87b635a1f0f63a0c7367a29332839d2cd 8798722e9b91631e7da22a94f2a72c5ea5a7870be02c75cb1d9e421947c16eeabe9261757b088ab0 bd2fddee77eb37747fa3acf7c4a42b74bbb7d564d4ddeef831f170a8a97fadaf746f5c747ad57255 cbaa12584be98955e5f9b0de1ec95624a30decd9b28d693a8d9acbb6ca927ba688aa8622f1b16f65 993f84def01b1ef97174aea9452b1c975122f1909e32980745d734e13e2eb44fe1da5b852cc4e348 25a87dc1c49596df6e7064a7fad6c6fb929d179a7f0334f3d556ef288b06124bec2aa54a80e3f1b4 473fd804eee2f8925c61e6b74f9d16bfae54cc18bf0af207a2f71db40759fef0533d38bf75d86a71 b03e511a9389ace73e94c65d7ff2355aa9d3795edd46e9e20d50adb2aa22b250f74b9d66b6e2be5a 45d843c40b6f1bbe0732b42d95297d7762fd7a3b8866244e43da1f87cf0e2f09ba9072c71ab6314c b5f7c9cdeab90fa0dbcd0a01295cd774131d6d049fc43d31be17e42ab3ee24e4c9c69fc4ae0f3af9 30d98f2ff9a8a7f9929e179aff2c34873d9175ce1c29e3a9b79cf80c9dfde6f1fa0d8e4b46d2d376 a5e13deaf9a320fb74659bd04f66d1d953ff1d9a62b8684f7aae3ac53cba75e1104e4ba3b4788f65 be3744b6b35c4e3956fe701ab29049a841428e78fe61031358ab794a3d07a5dfddef298cd68bc015 9fee7103cbd6979e84448e209aeb0d46aaad78ef6633a7ccd718e906fb78e0b69647df7c6693c66b 6e84558d230020e24507debd86c4eab43f7bdbf73ef11def88e8816e1a15642eec76b75b87d9935b cfcb2b9d5f3e0a6433dc5c0a3fb2c11eb4c4ee4b765e685e689edc783d03a7754bcf2cd021c6628d 089ed3e9021ecf0df2dcdd998d5d365c777a1c24496176eaecccea17424eb9a7ed2164cacc1a9530 6bbb0dabdf9c0a2946d7efa159ba92d17ef7f00e739eefea4f6cbef56e153c6b2e148bc695f64291 20202a4e26bced274eab19694b0e9f058c4a451b01bf992893b7a0f59a901ac08882cf917bd2a73b 4d34af74dcf1a93e0e11e44b6ce8d82fd667c669e1895e4b0f045702cbe78695f5e847361899c5c6 e4e06c4c8e2fa67ea179a1f933d018575b53ae619c6f8d83f26a66e61fce6a0ba2df5c2ef8381d5c 13375ac1b4c134f0554c1ee58059ebfe9a592c2c4f1db1017ea8e04675e5a9b1ceed381bb1d1e999 730c37bb0b1a3a4f3d3f66c9ab6acf72b8bd869688a3cb4faece31f63eb87a9f0ab8157da8abe81f 63934a5847f5e09becf9668ed07e702d190ca6c410ccd69ff84259bb55fb4e175b43978615c9613a 91ae87ea3319949fe8bddc141d1de2ac815ca296d91d1f4f18dc382785fac635d56f0dfe175bbfd0 bcd0fc2ba2d9bb9af86e1c68d0c5ae384a6ddaf574c53d77ace5fbb31badea7e7f8a84ea0de0f172 a53d4423655782df91326c1eac1abbd1201225d17155a597c2241cbed9eb283bd5eb4cc89398b90b fa8d16b2797543543fd23a534c8668142d6228e8ca8fcbe6b2e57a515ac8c119371aa7d9885132ae 2f9d2f001323e7e43190109e51cebe655145ae18338cf4bb1d6ceb7ba2e7ca079ccc07712666f2e4 badf5f9ad62e9c336893b7bb1fe8463f3e68941fbfb8fa85e685e6df004d05187b5c35dbab4d246e 5b2e662733f4d06b28c5471e0d287c1e0979a4992bd470d705b91999d243d1d5755b755edffdd9dd e519db63f9da1605fe6186d2831e660ce38fb3bd0063c8b965fdd834c96ef1ca7d27ca33328467e5 b1a6e9579afabb8d0b563ee4b6e5639b0f3c7de96002d00b67a586505bb662a309e5883d7b3ea8c0 61ed7930aedfa775dfd3af379a1f7c6d832d9cfb85112aefa417bfbdd0bcd0fc27a221cefb3a55cb 6fcdcee2f367088ff469ef2a8b85853f1f04675bf17408efbb3bfde2f12e4ac31addc7e1eaaedc35 a1ccf6377494a6459fde5fd7781402dff0a2985e4335eab12cf3c344ffac7d4820e9a95c7003b5e7 7ae5bec9feac3ffc165fc2357b09778d6873769d58734f9f393338ade27093da4a9ecef7141b5726 7ab449d5f60d0f3f9e742a8f0cbff050c88ba55f685e685e687e46436cd6c3de64b76406b5f13ade e18bcfd4d9bea95f4ba15cdfd7b614eb9908bb9a8b3acd2767f7593a5496b8551eddc9e3ee7574f1 6fae022eb98c40af3398a2fd5594fe3630301cea93ed76d9d9a0363f126da3e365be9feff52d3f2e 0028623d61dde317a7bdd0fc4fd01c0e2e3d3a77f0dd8f56f4edbf5e77949efc3f742973d7f966f6 c769e62db41d9ed7e3cde92d77d84ee6abf15ba87fea5bde08358f3db33d15c6c3ed61341e5d61bf ff867ed93fca39d4ffce87a7f976d33ffcf715504ba732dbd1f8c96788fdbfd6ab0dcc60eb9f4e87 f9e07c1a1faf58edfec3a1ff9756f32761f9926f389baf4687f1e68390ca97e3ed7fffe59bfd0fa8 d2fcb7fea1383e65faebb1e5cd0ecdfff966fa9919251be5d33b9dbe6627fe099cf30788f30fc7a8 f290e36fbf44b88fbcc45fd8b65f6fd91f1ccdef18ca71ecaf77ab31d25b90557ea90dc4cf4c3aef 0f566358df9f5cddaf37ef9f2dfcffe011fdc7b7ffb89a0fc7ff97e8fd6bb4fecb24faeff3d169f6 4b6dbae6fc8bda351bcfa7b35f92cb8facc4ffd5e9127e196c4fa7ed3a359e9cb287f974bef9d334 e7cf7aeb38f9fb9fa1fed6e3537fa45895fffbbab0d1d5eefca5f1ff929b50ed56eacd1e8746ab39 78d8eefc8771df0f47ec6f632dfd743f9866ffe134d8f60fa3371ae22e6fe64388eebd095f0c14fa cd5e18f7576fe69cbff0090a6cb710143fbe57121a4ffae7d557c34662698e14058a92df249a1748 9e823f6a5db9ed7c835e6ce9be990be7d5f870c308ef3d0acd8fbb55ffbfd3fdc3f2fd0b492975d8 defff8acac305e95b605b51eb5e2dcf63847fd553ed3d71a188ebf36c92652a444a1bf7eb912e661 50df3baf90f9a91975cbbcc5f12ad63fc1d506ece4f8100f1dbf16fefabd84665854d9e7e2e576e5 7283496365f3d377882c178ae40ee3e3f814dcae566325f74f4b25f8873f2e77c39bd1fb8a09192a 106a802068109c774a0ebe1b184332a6faff3d3efcfedb1b7dfd47c17f36fa8da7df38ee8d63df52 83df7f332b991017a620d9ade7df7fa3df6a85df7fa3deb2305186faedf617adfe5afefe1b1c4fc8 5b0cac931464967e5bfffe9bc0903c2db21024902c25c86fa9df7f13655212250863295266280526 c1cf3c434318fc4390d57c2229c92273031344926219411bc673a4204064a82c075b254a1086f042 6412450a222f4280a4228518258987ad804d6445567a0bc2522ccc2c534a264e82a8e16f56663908 803850298e21650e2e6c510e9ee1c4b7e1efbf3122cc2cc14c02a408038bd32c49b30897c0912cc7 ca9052bc0031203617459e43a860b3680ad623f0a839b00c438a1c839a43c35ed370786452a439b5 c192c8f1a81084f1127ded04cb5028130771ab0049961080a50548475e26294ee4aea554d8179ac1 fedd935b116456bc252dc989147d33520c4b426542df8c284793341af0f78147204803d821453d30 3ca380eed8431b34815869921165447596e43948d9b50253698a608c20bc4162cb884e0a80824486 0089137805c0492ca550f91d069bc3f3a2d28c0f18eaa74849b7308aa42556612101d196e66e6068 d4790e226520d7d33273d35389a444c82b29852360fbd99b6c905c32255237d5313c6c3a2d3f817d 36ef03f6a51b1f30c8e10227bee7e324345e08c6f30a0c562d09bc2a1d120b390cd60b0978a5a40c f902f23447099402a01896473ccd41a663117ff2503a6438043c1a2945121852a018e6ed6190821a 03a78e272b42a19110756928089488c6f31306474b16b8371672b02c23ad003149b0c11ca5664684 a02459400838859b5029288fb428281d84822c421d6643fd1061f3dfc70475fa2b0c8ea7324e5f61 90c60c27dcd487662549663ef1229808d513fdb5715080284580aecdff045cfb185488a8c218092a 1fa463618d020d29cd40bcf0e7eda39def00b58f8c84d4e33bec8354efd53cd033a8416395f40c14 4d9e452a8de148a8af28447b75e255f8f30a4c7d05c256f2fc95913f8a6b026f8a6b005103602f38 857450710892a80c3ec3c8908910dddf810c0bf98c9618a53864531a4238a8ec14da88244d43ad89 542d1a6c5e824c016b6065888e411d9719242f0c1c1a4efe0a81e20009a616fc004aa4c840222a55 894a7f69c80f5089228c02640745cc44d81ab52012008595a0a696a09e511a2f0a481a51c3180481 7d9418ea13820a326816401afd331b85b88857abe2e1c832b444d21c62088811ce108c5290465302 220e6c186c0eec23b4dd245a9094c6f3229c14be4078d83c387f28053f802cc9080c2a08bfb21ca7 7005a3900b696d81931402f26886540a8aa88f8c426a11fe4f6998442371474c4831a2d27881460a 182a198617558c0c142586e1d541a7294855f807ab4c191f23fbc000432dae587d700b2b212d005b 22f1b4caaea84e96153e81082fe41f4611095e5420b083142f29aa94a2f82b41a0a541b194d27d38 0f2382cb244da17e7d92e80302b945842ae05af00a4462c5a805a11a44f20deb94a1ba4118450e4e b9ea30c9f4c7a0330a273068866514820832a54e41140d994ae9bb20089f10a5a084a6daafd9d09c 20caaabe62244522a0884912aa1cea7446b8f227cc4fd182d2305684a60982c09a5425c943b3fa2b 04f691bb8edd2790456c462b3228cbc255873082ac621419750ea4a00da316a4d05420aac4972050 693cc3b15f07e87e1c875a83ab0e3a2da3294abad551b4cc2182f3b74a86423491aea34009820a44 2220de69330ab1b7c2335f80d0306338c4ae5fb519cc49730277071490326414ad023f5e1131d0a6 ba2b4ecb12942c96bb03a2a995966e8ad33234610464257dcd2921c392be033e10e41910514f8436 0614865bea495079224bf3a606115a0534cd7c32674acd0935b1f80cf8a555d07ae528e9aea722b4 9059654a833cc0a926e297266902bf364903883a05cd0d4a16e9db4e41439f5167cb2f357c02212e 64522320b47e5986e19e00d1a040d1ba2dae0d843d8584bf2bae05e4d03441d1b7c5b5815fa8f7d1 4d4de0d76e6a0111a158a48da93bd981d2c10b927c5bc30710f614aa1f95261c623fee19f0a60150 cbd10cfb04f8b5fd1fc535812c922c7591f6a5495ac0afc53fbaa909bce9a6065021145465aae500 e75c28060aa1140d46ab76bf2c49aa9c320885a0328f04ff826b3358159afa95f951826da391f2e7 d1e2e01d02f51b0de7088196be006938b14acad20b21820a0042d042576095f9571655639b4613b7 3247a0798a51b271b09114abce530cfce30b04aa3b44816bc12b101a332cb455118493aea602fc04 9788d006a059655d27a3d52da31644462e6a3f9c4a1405851a26c848b5a2e94c540a8a689dae6a06 8a9198f73e52022205040a12b4196864d5a8f3d475e642c4911899bb99e0209720db97fd9a0d365e a654d911117969645ad0d45570e03ca3161448b82851e7594980cc4633c81a573884460b1604f918 47382fc0862bcb5e444c8157d59d84560dcae0a20c12420109c52a830b15b8802c166895c1c51dff 8630c2558c2aaad03cbfb69f47b3bc0a846b2cf10df50856ffc5c053b842992ebfd881b0ab90ec8a da7fcf4623bb142daed07a0059b6b4323129b6045c5dd31cab1684bd6391490fbb2928cc03a72a59 2105326265a89fbf407864d7a9ecf4094426a88830c251664595c168b85a7c4395434b8057f810d2 e1ca87680a4064440c8c6c03d430495620c86121a9ec24223343b10305f1ca15225c83f2ccd50e54 0a425910f82f76a0c215ac32a17fda810a0d4546fa9a0d9928d7899b12a1eca0e1a6f9abe691f877 ae506453569a212066a3a108b38ca02eae44169aac8f221f545481b5fcfb6fa9c0d53116de8c148f 97cd86bc66b9fe745c3af4e72be4279b1efb7f1bbff5379beda97f1aefe0a7b7e9617c3c6d0fe3b7 e36cfb770441853e0a180ce16c0439e5fe3f294be295> endstream endobj 5 0 obj << /Filter [/ASCII85Decode /FlateDecode ] /Length 47 >> stream GhOt'1B7FZ"#S^CKb'rAF*Y=gh1H.G("/"c\H#CT$X*~> endstream endobj 2 0 obj << /Type /Pages /Kids [4 0 R] /Count 1 >> endobj 1 0 obj << /Type /Catalog /Pages 2 0 R /OCProperties<< /D<< /Order[]/ON[]/OFF[]/RBGroups[]>>/OCGs[]>> >> endobj 3 0 obj << /CreationDate (D:20190614115416+07'00') /ModDate (D:20190614115416+07'00') /Producer /Creator /Title <> >> endobj 11 0 obj << /Type /ExtGState /RI /RelativeColorimetric >> endobj xref 0 12 0000000000 65535 f 0000955101 00000 n 0000955037 00000 n 0000955220 00000 n 0000000010 00000 n 0000954892 00000 n 0000000319 00000 n 0000000359 00000 n 0000004936 00000 n 0000000538 00000 n 0000001073 00000 n 0000955556 00000 n trailer << /Size 12 /Root 1 0 R /Info 3 0 R /ID [] >> startxref 955627 %%EOF ================================================ FILE: assets/images/logotype_blue.ai ================================================ %PDF-1.5 4 0 obj << /Type /Page /Parent 2 0 R /Contents 5 0 R /PieceInfo << /Illustrator 6 0 R>> /MediaBox [-0.0000 -0.0000 245.7601 70.8001] /TrimBox [0.0000 0.0000 245.7601 70.8001] /CropBox [-0.0000 -0.0000 245.7601 70.8001] /Resources << /ProcSet [/PDF] /ExtGState << /GS11 11 0 R >> >> >> endobj 6 0 obj << /Private 7 0 R>> endobj 7 0 obj << /AIMetaData 9 0 R /AIPrivateData1 10 0 R /AIPrivateData2 8 0 R /CreatorVersion 15 /ContainerVersion 9 /RoundtripVersion 15 /NumBlock 2 >> endobj 9 0 obj << /Length 475 >> stream %!PS-Adobe-3.0 %%Creator:Adobe Illustrator (TM) 15.000000 Exported from CorelDRAW X7 %%Title:logotype_blue.ai %%CreationDate: %%Canvassize:16383 %%BoundingBox:0 0 246 71 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI3_ColorUsage: Color %AI3_Cropmarks:0 0 246 -71 %AI3_TileBox:0 0 246 -71 %AI3_TemplateBox:123 -35 123 -35 %AI3_DocumentPreview: None %AI5_ArtSize: 246 71 %AI5_NumLayers: 1 %AI5_FileFormat 2.0 %AI9_ColorModel: 2 %AI12_CMSettings: 00.MS endstream endobj 10 0 obj << /Length 4497 >> stream %AI7_Thumbnail: 128 36 8 %%BeginData: 4218 Hex Bytes %FEFEFE00A2E900A2E800A1E800A1E700A0E700A0E6009FE6009EE5009DE4 %009CE3009CE2009BE2009AE10099E00098DF0097DE0097DD0096DD0095DC %0094DB0093DA0093D90092D90092D80091D80090D7008FD6008ED5008DD4 %008CD3008CD2008BD2008AD10089D00089CF0088CF0087CE0087CD0086CD %0085CC0085CB0084CB0084CA0083CA0082C90082C80081C80080C7007FC6 %007FC5007EC5007DC4007CC3007BC2007AC10079C02767B02267B03162AC %3660AA3360AB3161AB2C64AE2966AF2965AF2E63AD2D63AD0278BF0D70B8 %0872BA0B71B90673BA1D69B21F68B1186AB3126CB5116DB5166BB4176BB3 %0F6FB70F6EB60F6DB6375FA9000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000000000000000000000000000 %000000000000000000000000000000000000 %524C45FD1500FD0343FD7C00FD0543FD7A00FD0743FD7800FD084341FD75 %00FD0A43413A3AFD7200FD0643FD0300434341FD033AFD7000FD0643FD05 %004341FD043AFD6E00FD0643FD070041FD043A4AFD6C00FD0643FD0900FD %033A4A4B4BFD6900FD0743FD0B003A3A4AFD044BFD6600FD0643FD0F004A %FD054BFD6400FD0643FD1100FD064BFD6200FD0643FD1300FD054B4CFD16 %00FD033837363535FD0300333131FD03002D2C2C00282827252524242200 %2120201E1E1D1C1C00001A1917FD040014131312FD05000E0D0D0C0A0AFD %0400070705FD0500FD0743FD0900FD0343FD0900FD034B4CFD0352FD1300 %FD043837363535340000313130FD0300FD032C0028282725252424220021 %20201E1E1D1CFD03001A1917FD040013131210FD0300FD030E0D0D0C0A0A %09FD0300070705FD0400FD0643FD0A00FD044341FD0A004B4CFD0452FD11 %0044FD043837363535343300313130FD03002C2C2A0028272525FD032422 %002120201E1E1D1CFD03001A191717FD030013131210FD03000E0E0D0D0C %0A0A0909080000070705FD0300FD0B43FD0500FD054341FD0600FD054B4C %FD0552FD0F0044443838FD050035340000313130FD03002C2C2A00282725 %FD09001E1D1DFD03001A1A1917170000141313121000000F0E0E0DFD0400 %09090808000705030000FD0C43FD0500FD0443413A3AFD0500FD054B4CFD %055246FD0E00443838FD0A003131302F2F2D2C2C2A002827252524242221 %FD03001E1E1D1CFD03001A19FD03170000141313121000000F0E0EFD0600 %0908080007050300FD0D43FD0600FD0343413AFD0600FD054B4CFD055246 %46FD0D00443838FD0A003130FD032F2D2C2C2A002827252524242221FD03 %001E1E1DFD04001A1917171514001313121010FD030F0E0EFD0600090808 %00070503FD0E43FD0700434341FD0700FD044B4CFD0552FD0446FD0C0044 %3838FD0A0031302F2F2D2C2C2A28002727252524242221FD03001E1E1DFD %04001A19171715141413131210100F0F0E0E0DFD060009080800070503FD %0A00FD0443FD0600FD0343413AFD0600FD044BFD1600443838FD0A003130 %2FFD03002C2A2800272525FD0700201E1EFD05001A191717151414131300 %10100F000E0E0DFD060008080700050303FD0A00FD0443FD0500FD034341 %413A3AFD0500FD044BFD1600FD0438FD05003433000031302FFD03002C2A %2800272525FD07001E1E1DFD04001A1A19170015141413130010100F000E %0E0D0DFD04000908080700050303FD0A00FD0443FD0500FD034341FD033A %FD0500FD044BFD1700FD033837363535343333310031302FFD03002C2A28 %00272525242422212100201E1E1D1C1C1B001A1917000014141313000010 %100F00000E0D0D0C0A0A090908080000050303FD0A00FD0443FD1100FD03 %4B4CFD18003838373635353433330000302F2FFD03002A28280027252524 %2422212100201E1E1D1C1C1B001A19170000141413130000100F0F0000FD %030D0C0A0A090908080000050303FD0A00FD0443FD1100FD034B4CFD1900 %38373635353433FD0300302F2FFD03002A2828002525242422FD03212020 %1E1E1D1C1C1B001A1917FD03001413130000100F0F0EFD03000C0A0A0909 %08FD0400050303FD0A00FD0443FD1100FD034B4CFD6700FD0443FD1100FD %034B4CFD6700FD0443FD0800FD043A4A4B4B00004B4B4C52FD6700FD0443 %0000FD054341FD043A4AFD064B4C5252FD6600FD0B4341FD043A4AFD064B %4CFD0552FD6300FD0A4341FD053A4AFD064B4CFD05524646FD5F00FD0C43 %41FD0F00FD0352FD044648FD5A00FD0943FD1B0046FD0348FD5700FD0643 %FD7800FD0543FD7A00FD0343FD7B00FD0343FD7D00 %%EndData endstream endobj 8 0 obj << /Filter [/ASCIIHexDecode /FlateDecode ] /Length 951593 >> stream 78daecbc77b3e34a722ff8ff46ec77687a0fc2839e044078ef017a9004bd3792663efd023ce7f6ed 3b337abada18e9eddb10199da75006f9cbacacccac029ab9946ed5c8f57519d51000fcbfffaf5c8e 7e44e1ebfa687d2a7f08a7d3fbf97a24353f8ab652fa016100f8f9fc60feed767dbca2f58fcde37a fe415f1fd1696892de0f9f88ef62ef5fa7a875ba6eafafbfdca2f9f2f48e8070fff3fefbeb6518be a2d6a722bcfc4bf87ceeff1ab5201c6920491d757d5fd6fbcb96bafe5b0bfc01fe8051fc0701252d c3ebea7d8e2e2ffd715d45cf277d3d5d1fcfd60ffa2fe1e587126ee396f047109d4ed77ffd419dc2 d5311e430ac8fcd3cf79c61de2be49f9b7fac7f5760e1fc7e74f36b50f9fa4cdde9fa25f01fcd212 9d6fa7187fd20ac1c88f1a82fdf8fefbdde37798d1bfeca37f6dfd50af97e8d386cdc9c7cb4aa4fd 96eac777b5fa3ecbe15fa2441ae8bb8a8d11b0d7c7397cfd803fb3430acd2f5194eb3a3ac577f8d4 41f09c56ace8f58a35168f064140b13e0dc4dcdebdcfcb4bb88ffb4270e30782ff687cf41b6df7c9 0c84ad1f280c357ef0d1bffda0fef28a9e7123cb245f102461a6f9a18d9842df948829f84d71106c b21fca30584c870c1a0f8f0b34837c281c53ea4349068a6933be2bd86c0cd9981243e6438731c53f 141bd25fc3d121155364487e688ca1097fd3184313fa5070186368b0c3987b8319c6dc1bc3e117f7 063d443e34e6dba03e941cc6dc1bcd98494ce9987ba3f1a104cd7c68dcd0c0e9e1d7708ca63f34c6 d040bf698ca4817c284c373f34c6d0803e14a46324044be39fe171210643301f3aa4d198d2748c87 a0e8180941d23112a249833081131408c30945201c2613d9111c07491241624ac5951049c1348e92 0cdcc471928d2916530647c8213c4c687c9706c526aaa092598d45812912a408886a823881502434 c49b140cb1788382a0069edc15c6690a83207c18531ca7281422629a2c3a90c5598a882943e1311d 523842602cd904ffc4e723fbfffbcfff0cff9fe1ff33fcff7fc3e36e188cd228c60ea1d821b24310 41117648d09f32f629939f32f129373ee5068aa0507c857db8c41564528190081957c29f1a3ce90e c67e356ef97406914f2bf84bebd7f0ef663469c6995f9ae3bebfb5a0491bfd4b5b332927c391a411 a5502aeed0fc1d28188785044f3210443fadf82fc3e3d8829249244d644c1ad1df1b21e8bb9cd4ff 220c847ceb8442e9f80aff4d7624ce880804473004fb921841100881becaf010a6e11838dc88bf04 8cc55f34fec21fcdc3100cc220c4c4df2144437432211009352122011d97639dc65f38e11a3731e0 30fed2716027bf648fbbc44a2240ecabc34fd99bbf4fe4cff207348860f097205fb2a3bf4347929b 81093b04fc829ed07f0c1d4eb29f5fa17f0df803f40f70e8975bfd0a3f01dffc6afa29c02f33007e 591cfa6d19f1d5377cf4031ffa321bf46f0540903f0a90681e263f027ce07f048a05f80cffa3ee7f 15e02302f2b722c4f0bf054886c732c49a6dc6b905f88b085f92525fb6fddb02fa8880ff6e3adf56 9748f231c3f8130bd1f81af28fe6e18f627c0d8f67f5037cf813f837f48f09a1bf41ff7cd82fe85f 2afe68fe03bcf1018e7d644b38d1bfc0467f5fce3f05f9cc40323c868ee271159380ff864efe0619 66e3eff06f35ff65381fb3f906fc777a4f4aff3efc2f157e81ff03f4b861f8018eff66f309f03f6a fc4bdd68b2603fc0877f07fccbd6ff17c093f68fd97c01ffb6f16f9d4318f48bc57f1bcc2fb0bf30 7f39e40fec44793f3ddc6fee91f8d6fc4fc09fab042f9de04d92cf3f40fe55d370ac9118f0df41fe 1defb7bbf815f2ef7a4e1cde2f66f213f2ef78bf6cfedb34fe5d6d7ffc2c84ffbd7a1377c1feb4e6 dfe07e2dc958eeaf75f8656b7f8ff033efdf20c13f804c2026269358c21fdcc64fb3fd204ce6fd8f d6fc13e8970bf95e7b1f07f29b727f479b80ff1bc07f63051fc871e917c8bfe34d54f7f11c5fddbf 35fb27607f85cc6f77f10b6ce26fa34e023c89397f0f1dfeb2ba3f1a46b2b07e8d3a10f59bd6bf57 dfb70889101fcd7fe4f843f8f9c585fc4331be4368b264bea365dc3531893f42fe68fb63c8df90c9 24d6fc8e37d1fc7f1af2c794c19fe9c17f16f237dee6c7eafe08f83bbaff23c03fd5fb71249fd092 a8ee1f00fe5e967f0b98f98e323fb17e768bdf26f31fc2c589bf6df9c8fef78d60e3dbe6bf93a604 41f217a5137ff37bcfefecf19708f69d84256951d2f9d31dff2ddefd6d8f64de7f7a311cf93d454c ea7ebdd16fae39f17318fb5b1cfa2cd8aff0f85bce92b84234d607f6a583e647d218fec741229f16 e2b704e2b364be33d55f33d8afcc96faa53cfccc728eb9ac9393969fc72efae37aba6ebf5b7ebff8 b459d1eb7dfb3ee7f93aa3f93e47fabec3bcfe5bc58fd6d7e57e951ca6858fbf7c57f88aac5ed7d1 bfd3dcfe51fcb7f3e91277a885afd763bf7cbfa267e947f5d3997c3cc2ffd6dbfc93b8fcd26fb5db 9fd68fe8f2ddebbbe5f9c7cbffcfc3fe2fb865f15fc2476c5c6a788e4a3fea567c8fcbf6f7ce09b9 7c9a7ed3d3afddabff1b2ce73fa19cff728e5f36d4fa973fa5b89f7dabff8dd8fe3cb2ffe46cfeaf 0caaf50ccfb7539478a6d854fe1486ea7f6ca4fb70798ae2fbfd936ff7e7e1fdef5efcffc533fa5f 8fff79daafa2ff93f4fde774fddfb6a2ff75bf7eedfe14a6ef9eff4db876d17ebbfb53ebf267d7ea ffa9e1326e595e5fafeb598e362fedb18f33b17f9ae7fc8ffdd673f3afff0cf7778e5ee13af6ceff 8c7b65d6df79e79f9aff5f7ac7554925f4a32ec4496bf1480a7af8883bbfa2c793392fa32403deec 4f9114fde57b3cfc8fba7abbfd6af7775d8978ef0635befb27cf417ffc4c8fbffbfe489eb4fe363d d4fecb467ed4498b168406368c56896126ada9d4a57cc55ee91c900a7a3d84a9def203dc75a66e53 732923237b56904a950be954fc490f5adae085bd5b94f6d7cf90bfc69f54aab4187e77298ae99ff5 df9f645c25c9ec89e9f6149733cfe446bdc1c310a0b894eb261d648e1a445c72b94f2e31ec515c4e e242be18ff9bbf92cbd91f2fa77129fabac41bf42eee5cd58a1f71b05a94d4c41d06e3d3cfcb78b8 bd1b7f2e29148b454aedde623cbc427f807d60361be7f75818524a7b49efbbbd16542dd2adca802e 382c1dbde63f458ad9fc22204b6fa6ddbfe223fcb663963698663b4cf70c8afeb2453696b76d852a 1bd5b84f07aa83829a1b1c85c5b44b591b25665938c58a2b56628d14d2af21303eb17f4d37ad07f4 d1e6179bafe2f7df54a753ce26eab3520360564a1df6f94b9a7965ac4cba3e913276376565e1ca7c 938d32ea2d47a270947bec81655e5b775685eadddd1466237f57ec0854be786d54f09252bf8a319b 72a579589567bb1250e95abc55b98f72c5aafe583a35b06f80b5f5535a03c3b5c9d773d93b521fc9 5c1a6c43c52d7843db32a4132a0a83cee30d6f20758bb02f7c8a168ba08fce68dcc4fae093c3dee0 363971c15d6adb27885b96222e2b5e6e18f9faa489a0d1be796891a9963c06b0765d428cf676aebf 3b120dc9ddba54ae747793e2b427af53ed3e045753fd63cd8e06ba32989078673826efda624979a7 da96eea5e853cc6698d51e99e1a2eda10c27d9325b4fad0eecd1cde29ca54b3edf7ad84f21dd9fb5 84d9f31988eca4959380a1c349473c7590ad02dd505a63df52534b6da74e17645e1bcee0a65e9964 647d1bec6786aadac956d5c4641630ef22d1b7bc4d30b4bbdb3de3641e65c19983acea32f2cef12a 8ff6dcdb4afb83af024236c032281edc53397ee43d8fe3713fb7bc4ff2d8029d2cd5953a15b38fe3 0cd26bc8ec52a7ccb9b3f2ee319b45973ab7c2eca560870b7d3c5f722960bbaa558687d5ae6aefd6 ca4c5e44f0a9aa452747c037fa86db6e51b1d8da9e66cc68a7a9e27d0f4dab85fd965cc65ae0fbc8 313b1e08470f38e92764b5634f6b0e28c76ccefd6c689d6fba37bda8fdc1f65a5e82fe75b42ed46f a80772b7e57caddd5b580fbdaf9b44ff8107f4e131da319367e19405128bcca7f0a3e6a482d76197 06a626901ef5f57d06a6f87d6641a5d3d966b44b4e8ab3db4606ccf58beb62ee842ef3f9e1f891cd dfe5e9ab20ca0458ccf60742d13472e352b5787f96c6b96ab78c3156548eaa8d5e6500008fca8d82 ccaae4a258add0545f359fac6e0174755f001b371bd6e9d37013b3a9bfe9d61434aea00f0187de04 0abb873ddcef2d2af063758e2d2ca42f687dd904d1555b36318a2a15f1742a33c5bd5a872788cdb3 4b9caf67b8a1363bef66bd903d37231bbfb7383d2cb5cbc0886c2fc1e33266d361d456a55bb816fa ddf9bc3ae9d105bddacfeff1517f7eafb70643834d9325f93223970501a6d87c7b4d9707038e5e97 a6f05020ba390658004f661ff6deac562daf38e4a918dc054358deba41b4d00214216623bca7794f 9cac1e1769d8aae172053853f2c61e288a2254162a7ac48aea3db66d2d58d4533a05bf4ca3522ee2 c63e05ae4c1d02f216611194f582e5a53dc95c206758607db78ae6aaee7ee0bb9ee1766b311bbff9 2e78416a131bea8c99d12336fb5c8e6b015a1d1f1a8234314eb3c3b429bd90592a0d18b3a9d2bdcc 99aad55854bda5bcd876177ca860077689642fc3e5255f6256d61361d6ada2c0afdff05c8ec65ad5 8ed96c2868b1dc96f2d26b1bd5686c277707ca1ed627bdfd21ca2e0eca162f1e41af3d38ee3245f3 24776bc333ccd1ecf98c73d4c5dcf59bd7463957bb3e844deae67510ffdeaa3636f7cba38f3e445a 993cd39a647c02412a9d4ec586eaa5c6c281484397c92d3d73a95d8680fc6b266a358ad91eb8a864 0f67e69da3b6c425777d62c73c8f3437f977361716d48c752d16b066a5e804e541a90ea5bcd22c5b 7e951ba50159de0adb5dcca6423e07edd861667755d93c30b5c2e65c488c720da0354507d6d18ea8 0f04205dbff7ec0da8f8b131961bad009a20fc046e9a97357cdc374f88c09cf268a13b6ea3237de2 624d2495c1cea02ce332517cc76c888ab6518905b1051b8376f6dc7807c2b8e97290d1220248699d 9715bead4184d001b79adad9aeb3a3ae985f1c7b55ddaef4d68445f7d9831c0c4a23e3350827ab1e 39bc579654511a6354586f87340300ddc47596aaefd37059d9f10cdbd916d9f23ebd62d7665fe104 65d7e181b50af27b922d0beab3971651ae7e13affde64d7204ed2d7732a58a925ead3acaec303255 16989eb55a7086b5bd3763757d3b9e190d34cac66c8cd7fc459913beb5b286a00bd9d54ad1b5f739 3def18a5a2ec36aec2c57d7b51db9b0e2a139f297285a03a9930c11e3e2e47bafd2e8e1be912357e b5abe3c9582a3da694990267a58ad69cadb9fb602eae7b5ce23a2174a32f4e06e287566a1c2e9b3a 7a5abee17d76353ef0d09a726b54549a66fd6853aebd37ca56a0b6183bf3b737ee32deb9ebf672df ad4cf7874c3f7b3acc80e2f6482bfbc9a9c277e8d3613f24cf4e95edc46c2e7db8835c4bf97ce1ba abdc5e3743a79c7b83a939f7ebe52d3fe494957e669a6f3f49315e294c74eb2987da68e9ec3917a4 c5def599be05033643b5e26efbea46cfb6e933925d9ecbb71c660993dc547a255e205f7fc358ded7 aa48a1aaf41a0567134f6099a58da2dd00a352a9532e95ac31c6954b84bc2fdbb95bab52ce8b51c5 a18841b572abbfaadebae5d78050ead746b75d1980bbed2b30bbdd9675fcb01cd757e7919b1c5077 f0b507eea7af114449e40aba91fb0b2c585a19495f5a5dc4d00817ad345b7774d4d3bb18eadc57d8 1ad19b783fd3dae35780e009511ad41a7960746a7835380ef0ecdd6aae1f5bb5459edf46eb551f04 319bb6b1786c3b757f59e82c8f2baa4baa85a8fbb68376cf292be73e7ab38dfea1706e0d448d2e93 158dc8906148a4295a724a74ce00097a727f48c3be765c3269215d62467653627bb9e5934b6d0591 1b6d3a85980ddf4fb72642469528619a9b144472f45849f9614b9116f8b42933c36e59291bf78712 e55607555c8491066ad14a3beaef4837d69d636cb2ebb7f13c0debe6e8800d2d8ae82decd26b54b3 b719d87334bb943c3b761b3671f3d2d5f5c20b4bb6e74b0ddd0ef0e26d324ab1c16db464b6adb156 17b6930e2b0ad332f76e4e2fa93d329bb6e1d65cc9ecc445b7b95d87008463e16b07ac979b386cad 7c2c9f5b8bf7d139ea417ef20ee9060a77876d4eab5fb69772ffb80b6bd06c6f312a7f90ce6ff028 e25df5c4d7a9f59909eaf70b351be5ae838e42dc3a08babec3e9f3eefed468f561bc74f8997b84f7 c76360c862f532c2c452f07c2f53c56e92aa8b9942934c6a0c3d6a2b8698e7dd9e88d1e1ab772521 5c6ce4908538e1c7f7526dbe1624a1a68f046e9652f7d3fb7da28c5a0b4150063ee84fa9b3201f38 6c19abfe39e3d843ab28779b645fccdde6c95611bd684d578627d98e44c258f33ef4d69249bcd532 ff3086fa42eae94d5d72d269a12e55f69a31af343580e67841be2bb95e4d15ab2a10f6d322906b37 4bfdc0deaa4a2dc7f0429e07778daea93905d1ce272610488596c733a66486025413d280dc642da7 47e9f75a7a2b28620e458d7b33a79d43d817f75710bb65c79036d24b5996c7d4757ade3a202b8fc8 395d5eeb1b8aea6f19ce9653c156485feadbaeb728b4937470701abd84d077ca25790c55b5332cb9 b11f79b1db2a0a4c27416dc1f03575ecba065777fddb0cd9f0c6b4b9629e63e4e0ecc1785f50b7e7 63b42fa65336be933c51f405fbf29880da2a1b11196edb6dd5633633e6021c67c86a4af3f5b75055 33e3fa70044757999f06bd7ab7b5394a1e780d45411a498392a3655ada13ec479c0367c24d6f9117 d7624b2e71bb599f776e18fc9a339dc5846fcfc91c63ec97f731899f938737fc83009628b26f6cbc aa3aa6449c789e2e4829353a3efdca9c734ac5f1742f51c3c88985e39ef6a4ab9880432f6cb75de3 e369ba7773c2743ca1c6e053e81dcb97d2b838aee8a9cbe8a814c3f63a82b270620282e8181b59ce f45107b94ff7823eda6a7271f2580ff7cdae25ace5ca59ba9d651a7e4c0d4f6ca7fd8622f9eee3d4 384c9e0a8d6f47323c0cdd8935ce576543048fd2da756d5943a75739cf9d9692edbc92b0d63e2d2d 46da0ab3403a808d66a1a130983c5ded79e96916e9d5fabca6745e21af921aca6d3b15a27dad5410 40a9d3aa19f4ad2eba6af9aa6fa41ed21dc266ee705596157b26afe8eef3705bbd970e76969307d2 b1df6b55c6ed96b031afab9a2e15aee9ac64c0cecca8d0e15aca20b343bbbd2e54b4283c97a4c19b dc1474064ea9c68c722542ae4e96967e7a78ce9aa6c5eb7ad530df7044db675714c5d5cecad1385b 9f27963657a613f1cc866db857b2b6c689df9a324e4899fdb1f9d84fc1e78614f7370e190d08520c 2495a445d769c4a61d69399737a7a618674140eb0e97201b5f503529a375768510c4a7eab47a40c5 a38f250ff7c365b56ecc1ea9fe55d4d62dda6c0bd27cf47ab51b22b7b22e54c43e6c2feac2926803 2a0123aa9172146751974e1e1cec4bd5c22dea1097ae68346fad200477f945f7e2b5c47e5f7b8ad4 c21d4c1ad83e9146a482bede9ac1c22360d7e3bd1869f77ce1946711754d596771fc0ccd10e58efe c67e2c4a62cf9920c6a15e0c421fd60a22f1deb294e46cebd3c70b8345d6c917e07216e79dc9f1b0 96025587b674b39b3cf51552fb03a669a25df78d6a7f23e491f24355739d9b204e5e84d0bcc92565 edbc334d479ec541370a1565512bc23960a379e20b1205cda689e29caa842fb9b93e0c54652789ba 712e0ad2b2ee2d95c56a948435b235756949e9556a4abb3c3140552b80d242f11575597e8ad17d43 613af34cb1aad939763cfec0e5d4d7405829b3ee20c76beffc524ddbee4d017651af99dee6270ad3 91a60a546ccc62ff44810ac2f3492ea0cea84169068baf81953b9e00650dfb9aa63ee67d0329f555 a512e6b581f9ee0cb423d3abcb2bd91882f17617d2ccc53950cbc5477a7dbb90a4775b3b6be5b1dd ea2e9b05ab0e04420d050ca67d6eb905bc988d953e0c0179f442b20d9b5225439f935379fe169eb9 5d57719561aab355915bf63aed0caaa3e0414a0fa5d7c5daea1ae749f746593579caa2b941b1f5ba da46675c91a9c35003eb16cf59f4850915af6901319b75edfd36e73255ca297619559cf0de52c7e6 4930e4ddb39be31abde7cda7a6b5addc5fce568dbe39c55c9158666586a90ff2d97893a068edc54a 792c4064b2b056cb30635f71a5206dab6aeb91ef4fde4e3a096b320f2fa3fe7237dc8c0ac2ed2a03 8d960796b3a16cf7ee524ee9e2bdc6ca8806d6b673e69ecab0a36f9c5e515e2f69ddd9ca7ed88cd8 5b98cacdfcd32e2543442836ea42ea1c44c3b02be35617cc03bebe8ad9282e4fa81696eee3a37b56 5bf1c114bd1a96f776e2a875e0f9ed1b1074bcdfb07a6e1be005b8d7f2b441789a025443ea09070c 3ee932fa6c86e5b09f97ab2b69649c7699b2653b684b524c6aa80bd55762698cb6431f12d60d218d 3e54dbf844563009f3605443caf97d66bc0855e932687166cfac04c1995f6cd567bb0ce92bd0d8ca 20e892cae53908348b9de5ba8f4ec755885125a56eabe409804ab9b4c2288412b3d1e60562b5283d e8bc857772a6519fc063d381e1a5d1a92f1b5a6c3be270b5c860daad5715d5033a0ef156d6d1b4f2 a24da8c1786e670b5b7b2b23b7cdc108ca403348bfda67472480a28e4c422cf68283641b653554d4 d4f2f790ebb23c5c3782bc5e507b4498018a01cd1a6d60f9d000e82cce49520d4624a6abfaf4896f 8cf305ce7a9da822698c40d586582a85d9571525548eab3ef1f409ad5bcea23a5771409b24c70ffd 76f92ad3734434dac1f0ee0f0f7479523c775cedb438a3e27e937bf8a74a2ea3860dbfde2d31d7bb 6be60d444d9d7801c8daa3d07c9073550dcf7b7cb6c19d7c686c76c33874ac0a06d730e8a9769a27 27501a583d35e9a3b971466ce7dc5791698060fbf11af32e64642acf738dcfaa236224ab50f16e00 cf4dcd4f5f4fabe56d721a68d3a937109572ba342fef4bbe6a5c6b4ce74068e549c65dcd945d238d d49ef74b2f0904170de65439586d2624fac278c60738a78b7269cded0e245e6a8a05abdb3dec29de 5b74f8d5143c194bc0dba29de65c17085de70d19ac99a9676571159570d7765388597646b98b28d1 ec13b376fa5a4f7281d5fdbc93d25c4098350a31db4dbd298ab7a97f33527621a8dc9bbbac34cc0c 9f064a2ad8b82f9027ad303132b66435fbea91be9694d3ae7d36dd817f27a399032a201c1c0dd027 04e45acbeaf2b909241b0ffdc036d8342f2e40a9f29a804e532e20f6ea96230c8905871616044b3e fd58127a2ad5ce1816ca2d5a51767ad1b2a7acae0bb5c1bc72916f590d93c18bae9ed7ea689e3ade 9ddd69e25ab7731f54ab3a9cbc4163e915c4312be6012771bd55353677ccd6c705798b4c4ea7c8c8 4cf2aade7a80487a1bd28044963dcb5ea63ba18db28aeecdeb306faed75099936a69c2c13754c900 4a83458b3fbc2796deae55f5cc249749ce05aea619188a7384f4382fb88cd08ef69ee8d73262d943 a0ab8cae462e68cb4a60ace7efeee06c90577717554a7abfa2c8881ac92f878ec692768d5777a658 929692e6c20f5bad5fa6d60e97c6b3d47c9dec084c7daea538a0bcd88d223bdbd5af78746c61e883 f43769a8a42d2927aa5c89eeda50e8305ecbcd3e1444e7dc66455af8dbe268f4a09025663c679842 d770b9fa6ce0576af749e8f56d1dbc7977842a172ec9fec670c1943625022083f9dc59f2d7af7210 4e1f33a3955b987caf9e31bde2ceda33cd103ff0c2a86ddbbb5ba9df306094e00f30ed5a8367e756 9227d059e06905344b87b5ef2e5a4743e20dbdee5dafa9e46848aaa44d4dbcd865da515e51d85bcf d09b382f7333cb8bbc081243792b1e5a78d562aadde4ec7f3815ab27eeec3f37edbc3ef26f7d354d 9bb8dba5d8d3707a5c67955ac8d03662c270a35a77027966a492471ee66a23d2a5783671057639ce d83c9e925b3d4cd9d8fb9b65afbdc8d744e6991deb104a56ed079ac67a2d3c93d64a82865ac5fdb3 07b5ca43535d8c51c19cbca16c8a1bcc6722bbb17a3ef97a15f5b2f24ace3aad03b0d09d0329af87 90d876cc9ce989d674dda5887dcec4f50b77204cbc36ba96c6ed795b4f6724d1402d477096e221f6 3e0a78740f28981553ebf4d871d02361ab8388e8be6aa98315077ad774c57eb23c219473782b5b5e 9ecc5ac16053ab5042c451a639f4de133bd2d67b7c13cc014673247f21d2a7ee9d8cb7e5dd8dd5bf e65d6254f66aceb2d2af1afb6769505a31b9ab5e10aabebe450f8cc37680d38c530a89d25c073e29 c282ac9ec695661ca81bb73ad20d8d91e43f6bbdb24995c90e540d9894930301c058bbb97823c3ec 3071e35853ef201d479aeaaba979200669a7a78e2c7a34c76f935df63db0a039912564dd2bc56c82 beb79d19c143a14acbd3c8d00bb53ba61b3c5731ebcfeb9527076d6c6cd40398239cc7836f93e58b 6f3fc1563b1c2b4f5e7bb037b72104f37aba0ccef9a7c99e5d41cfb6635369ddc5eaf1599b2ce54d f2169fecad0bb2f8a88c3a815dc76892298c68d146adc84be5461ac6b7b49468771a88d3b9884221 45bc6e52454ccf0df8c4368c994e36d406d5998d56b9659ddd43d453818367e4c51addb6fbcf322c 07e930899e8eafa724601fc045d9c727434713d35a0ca623c560aef6a470f3733290ef9774ee312e 0429ab511c3cd3335983d99dee0e53cf398656cba01a4fceda1e31b57581a85139159883b0d1273c cf600f4eb2c5b589f9a137c236549165b7866c76164dcaa39162ae5d0a88a9fe76320ba764f71060 7f6c50fae610e6ec452b758e9174fd18c9991edb92d4914ee90ce7bc0a06e69bca2d37989536a8f5 6aac75e7411ee4e4bfb45422756aa530c6b06bf6412cc875ecadaa7b7e6170978165e08b933a8210 b5121c26539825f85edf13266addcbdccf60eba5c8256766739a3d9ece34e092790596967aba3658 afa663243d364672746236e3b617c8d27ce8b7c78c3a4bfb4dee180c4cb356f55fc870e0f8a3c10a cbd7c28e572dbfbad6c667478500dcf654cf26de8640f7ef466a76bbcc5fd16b194c7665872dbf72 bd4988acfaeeee7e245bfb452f51da48aac02d5bbaca3760bb3b642c718f18d6a9282ba932a72322 d298de1661d81bf26236b3e2bbdb9b3d2d6ead662fbf7d55f8dea8ed06af42f50a87ac9ce1fdcba0 e6d7651ac95a0d662fa03de4a9b7cdc3214907877ba627c1b5b03da333468f6e396746b4ea0038ce 7959afc15d275d917ded6efed0b36ad51e960b446f3614fc56c18d235d1ea563b5708ff9f9356bf0 ed36482bcc725f9eb436e3b01bc1e7853c2aae93d72883c6be7381ad7ef12133e699f5283eeb65ef 40a523b7cbd3adaed0a3add258f7217d8f5fede97c6081d4134b3734a622ce46f14ebadac0e113ae 9a521479f34b78ad320d0f54672c0e7bed7b388e91e0c963a2044c65de28b9185f7166a039ced98d f1a2e2105d074615031a8f713f18c12bd8ca4477dd6ea7ae6e309297b9b6b2b1949cbddeea23956c 2870e33576879ebf9f62cf19461d517f6f1374be175cbc45b2616f207e356fde0ff7aa072e5ea7aa e9d05be3ec0df2aea9d0a7180cd18e91b0edd929d3baf1f9fcfbe9ad45653aeee79049d7dcbb9a13 a120e913f0b10bbb2f12b56787fec1ad977831679e3147c168227946a0afa40ea15402fd3edef950 7772d54b0bea40e62641e9bd0f833882bf1b58ad0878d6665a71f7829dad4e0e40d3ccb147d72d17 9c0449a31c2361f2b371decf706f77d098ac4e113e2eaf3a78d7589d7389a5056ae9ecbd2f6a1af6 6eafa39bbf3dd7ce31686473b3922b289d5793d6f740fec4df4e548b5f390566d997daa7be702eca 3c6daee8999a369f586f729bf0f23e2c8e75b50197745618f133744b8f663c36f83cf1c857b762f5 f0aeaece87717f48a7fa03f180d8d4fc8cee2b2d05e8e8224fbcde138e0ec97ab4744d51d8c71edc 91b84a7a9feb4ec4e96071d01646d6106af95141b9f5613964f307a4cfdffa15f9b0af8e6236d3b1 3b07b1a619679e2cbb7b8e66a763a634bfb8983c156685c07c6bf71849bd16ab25555e95d6356e38 4099adb625c9f7bc3a32062db66cc439747a9e1f8f6a4da07ecef426aa4c6f2e81377ee732ed859a 9caa4b93d5a4ae3da6794340f7a16a21ceb3b50839a4d91778776328e92c3379e9148991e385a05b d8d80aeec306572e0ae58d761f3cc67ea0db708c04dc2548264b6d672d87ec5080ed518b1ecc34f7 acc56c5afac9cf5ae505278c33b4d0052b419332556f74f677728acacc16de597a09d44a2f3bce5b c0f55de8e3cf23ba68f37ab76f49a8e0bc8fc26122ba051fd3db64cb7e3da828a06a6daf4cfa7345 177344f2b0d8cf9620210603a71230d212b3e6fba1b8eeca418376c5782145a3d64469cf3cb37c1e 8fbcf26e0e361fb3aa2dcc2b90df3bb168e62df84b19d899828e008d9b5077d1ea349b7ee716053a 1cf447657396581a373546139816bbd808bd067ee996a90545956a95e3c561e82631b7bd00cad712 240bb17aad94f86919786f246dd4e42bb3739fbf4c8fa4db79044f5e9ab55f020c9477ccdc79adf9 9b7329095e7395581ae20a8fa320a2fc547c4293d979b14dbfe434ba50f938e73b4d17488b90e4f9 e6c1f3f7baad28717e20c1a7d395bf938f7427dedd54a482ce9842d0e9e50be76aba2ccd2b3acf35 4bfb4794c705274e79d545cc869b2bf538c0d934a062b725c6e3c05b18ae5cf8a9901be1c29f592d 44602537976fc29e1591bc363ec58ea367032275e3b2506732111007308b1b74c3194fbe2cabe33b ad0f9127c2a3ab66a793db5b091baddc3128a1b834806239ea645505d34f2c79c59faba83c78b92b a880b18f413e67e7bb9bb48de0970a67978b856169b4a89b45bec5f3e4245d85ef8fe9453f378a2d 2148e995631eeb38932e3405580a9b25679d63a10ed6fccda51572e9e87094d63b74e2ecc2b5c82d 7729aaededfb594bda160d7e8af2eb22b139035ace924bccb1502a2f0fb6abce1a625f66c57e53b5 5aeff279443537350edeb82bda1ac105afca99c9bb1cb137443078799992f633180542e3d4181cc0 676ebd762058675393d772b484a8ca3cdcc26b768c382509df4dd763cfe81f38698c77dabde3f2e6 4f73c099e7bd9a541cf41542abf4b12b636decca12abd5139fb6499584218b2c8a05337cdb5298c9 d37bf6e598131a4147d6b4e67461ee58478fb0d39f026e0e1d8842d6deb9bbdde8f1e6e8f1b22849 15040f76dd11c3ed7b795542a9787baff5f2033ef68727a956655fc9f2e480c69b3f1d6b0b498c40 38b73cad64617f9634f18aa3fdc5da9335a9a41c4fe294190446b06b73e23aebfba2bf6bb729e0b2 d3c489763f8a9b8d718330c7cb8a27b57b95a1553db5e5d0765703dfd7e4ac53c4adccdcf7eb92ae 662bf45d84fadc4190b7cfb482b47946ec5415b3b9caf78ff2a8f08ec48d1d47a7727e5093e79745 5ed84da8d35c10f34fb3d47b95843533a8e936173b08662680c2719c0649163d25075d1a403e6511 ca0e4ae0a3d08a54eb38a024e938786ff0e102751f19ac2008ccc3f4cc11bbb4d9411a10a411d4e2 6757cd357b3d7221ccef4cbed92ff1373d6acc7db17d8d8c7cef58bc2a61ef321360b09c1ca9ce64 37440378447302f27a52dab649436eab30be0b8335b0195c07e1dd3a894c2084ac55062362a99a8b 531f91d2d96014f5724368b66815e6428a41ebeefe2daf46cf4da1211457338c87b2a5b4b75c017e 72a44a96d572b308a088630c604c781d6c36afe4b9a972784026bf2f65d6d3e3fcfd58d0a307c59f 40bbaf3542b534a12860211471f930d01f663940262b291673fa06a7048bdaa1abb6c4b12349ebf1 72911c0d6dbc7de9ce87d003730711940a57f7fd9a3facb7176e7562dbd388394302949284c67ede ed8f0a83cc40582f1ec7bc9b19b4957b361bef3bc0e83c190ce43c875dda6f65d339332a81041137 1d5c30054c3d9203e2bed394f77c935df6e5c373d9a883f6a1cb3fc7d04ccdf70bf3e504bee7c560 b32b2ae60459dbc726e08a14e233f2fda2a75983c4d6620b9a9f6423bb1408f9d5588a345f26e425 dbc5b2f075d9930ab7359ba483141196c7adcaa5a8ecd677529e07eb4069ca0b437e3f566db9ddb0 5bbd6d3468cadc3d8e402008f3c0b96e3565660d440afec80c43235f5a9879a0b691bdde24ce4c16 3359679c0d24572cadc42cc127f8b1344ae94947bce111b9fa9651dbe8f322d766d13babedf64f59 bd1d44d9a2cdf9a861f2be6d228826e7ee7028735dc936555e1d4963037ef59a97d34ebfaa505d72 5f991530bf85912ec599aa1c0cd4e4b584853fd08c80c91c9e72fed21d5bb97409776d7fea48ce64 5e60c051a96263e3d752a27541c74385d5cc71cf7c48d33ce164d75dd29427d1d3966b9d9a1dd8a2 898e07de7e2079a52a2e9ddaaee3e3e96672a42ab58fab71f71a422f672dc863a961d04320d88f43 8bec39bc8caf3d66fe2217f0625b5ee0d2dceb39a6a65ef4c92dde2cc589145119aee6c62c981ef0 9a54cb55b7b8d10a508f6ec4e9fff04e73d9fb8a48259bc2e87805a56b18d801b0312fcbd61411a5 610aca4b5277f19c51245f97cae2a5dbf5c9b13366075a2815f9d20930c959daea74db05e97dad45 d38b7b22d869a67a36d23aa1e8cf59c872f5ba8f686bbac9c76c683b45a4b9c996eea92783196178 d673f8de0b3da877736ba5d9153b118c97a019f9c2a3e6957b022ce68a7ea06d522e2b3cb693b170 094727f590bab73b186b8c84f7695f53db13e25a9de9425a244b501208d4b33ab84e2e6b35ab782d 4bd56efce5a65708b722afb6d59a7a1abd796a4bd8a09cbc54a002c66a86c6532648ef4ce6a5628e 904d9fb80e260ddbaf8d5eaad263b7ca0caf3a3dd907eafbc084426db44a520ead7479e7d42aeb77 dbe34b40a8b0b80194d13597afbae8f1aa36ba4b478538af3829e2e3ad1dac254d43d00ca57536fb bee94bbaa062ad6385c23c6f6b6428a3a9185e3d83729363bcf7cf67e68a6fcc122f90415ad3716c 19bdada6e4bcb1c3cef98ebb7d6ef7aa60ce6bbcbe9bc47b58e39a51fcda266a77ca6adb9c05745b 412a40a16a3cde7bc3d6654a191fd9ca7828a6fdb1db16043542d5bd1a21d8dc376fdb6447a01c5a f886dc3e1cdb2d155f470526ce3cdae8fa037b86dc720abe90a50cdbc26fd23ab53b6a882e83f61b 3e43d3e6c56654b0287a3c7001d1517970ea2983d535dbcee5d1969fce2203f9569eecaadacd49ce a18d78c3d154fa976b7d74318ddad2bb1fa6aae55a5595d3747516866e530950ab4baa7a781fdff6 505f4903eb355ab20f672f27753b4a66714d654cbcb6954ee5bb611f4a9e6bce9716c02ed42666a5 8fb5e48124abded034076759c6906da8d97486e91d1719a550b7d1d7a43c7c94e2fdc5b252d1d9ec 2cf0bbd4de17d629e46ab587d34066e08d2dd8ceb8672c30b6d23f4eb639c19d8f0c5d9f6edbf020 d8bbc2017b4ac936ca6f9e9ea99eb31c8b83dd9eb316cb9d63e64fabaa3c2a3f75338fb91e731c78 9e9c6b98577d165187665e3f3da4f9da986a573a972d77765e4f2e98bb82e6f0f394a7456e55276b 8397a9ecb091e49fb9441aada8d27d2353e276fdf639df53ab6d70ac5d5049824b8e9c57c62dbaaf 5d5b6826b51c5b6f31683c8f56ca9e3c8c410f6b99abfc4e3298d3a9ca343da76f54e45e552f179f f5c67c3ae86acf45f9a275713c79a9af4c6d9448d3b4fe534b57e7aefbe215dc4b67b275e369984b a956a346f68465545d1e616cef86f5dfe6419eb11aad4d5ad0133a15cdcac290b4fee4a1a60b2da5 241e8cc1deb4fb334dbfb593cc44a213698c5c6656199e39b3eef6d2d8595b8c7be3866876247b7a 69e7d59bc70dcbc3f962179b98d454ed8cd876e557a735551dfd648c79e2214695ca6144969b818e 2962bee711abd0cf78f3a956c30509dab9fbe47d01479997642d9d6d0dd28d9cb913dfc76dcd6429 10d71df7be9bdd8fd64d3ff099d7d049f717939443ab9a3eeecf1a0db43a0b06d7b0adcefb35ac4c ee37902e1298aa7667d39de5b2d099e582c7dbbd9ca5e42d555ec3a526bbc57cc85eee4eabce2152 365c977d64acca0e08eb8fcc0ee12ef25eb554e865271bd581589451d953d09daea6323745a0355b 741ae5da36f6012352e8eabda9c5a733039c665b5b4152a06b72724bf65a5a610bbc01e1b84b1535 bb7941ac8aeab764e741b1ce952a57f87cbd9e932e874ed5ba5ee78dcee0e05d2575bb944c7ab03b d40372184952733f379d5d251323a9c53bb253de76c7f97e4d19f03df5b32338c17d3b0e212f2a97 c2b66a113f60e692199978a979f614096add8cb1bf2d159e8736a7b00399d58236ba34ed9559320f 6165e7b453698253037b64c04bceb0babd45b193159655ed717a5d8d483092d7e0ea7676d3d37c68 c918bbd3fc9d80116230e9895bbed7baf27338dbd927bdbfb5b361ad464e50ee64a5ccc2d1c4cf1d 06bbb96bc4d8b5ef9c510ba2451138ecd3ea6bd83f6993ad0198f8d89c04b9149c0469fb84df5f1c 26c429919a0166e60df60feda8dd87ed4949ddc75b9f53bf6e00fb9b45ae8899d1f696d754b9d72c 880808c0ce6e4c8de469d8dd8e34e824584bb0c493eca87af64bd08a315e1e9fc6c2f52dd911b86a 190bf4ed7b302fa2f7ed45cb98355c23b763c1b8ceb3c8bc9056eb761c43452e9d7b91e397d81a99 e165956a5b794c096e6ed434e0f3ab5167afd9b5757e689e91814d3cd5f0e690d839a1de582a0e13 4b13167996618df304f0270754ef836203636ff742dd1d0ce67964fa041b1cbf1779a7cc5e36b9e6 c66a731bd09ca9b37cf6aa2e3c8b165a970c1088c1f238e4d602259447a581971acc534d368e5e02 f8482541da2959cab6065ccea63034b843ec60868d788272c9fedd6246fca5b110c4e6f92c1dabf9 94b752f78dde6e05e4a5c1e36239c6ac1b206cb4d024641ab5ecf65a20720ad633a54173935177c5 49462507abe4c5310de6a9be3ff76ede3023f714b5947203978833dc2630daec15526c423689350f b5da220b283dc06cdb6a76de4a903c63243a15acfcb6235430dc303a137ce98108b9e9e9e991a2a7 1e69c8beed8ac9e920d2af9881d66ed641cb9e95c9dc464d3715cacb66d4abeeedd53232f09cec20 63fa68ec41e855bd665a657f6039abeebadc4ce979c35829f5b3b5dda6d01a8a379b46bdf4f4ad68 5cbdc448f2c919b29af8b4a07bbc18fc5d66dfeec42f4aee9239957be40c00ec901d6ab6b2a30b48 272356ac6dd56c5a65433ae47311f952741ad0d497f1a495cb8add8d26c894f22ecf5b89f610a8e7 572abd5c9c20315ae3a87f4e075dffb4de5ab2331cd5f03c97378753ef66f5861527065328c560e4 460041a5905f4feef9f1a951725d3b733af690a1bf0f4e0098b73b4b3b8580d367ca654eeacb3cca 70bca12b801dc56ae60f5a7a550c3f9bc2a6ebb31c7bec4ebbfb6e753081b263d66f3e8ea34e090a 70d6af14b8bc0da6fc7c25b52cabd5eb92a39b13d57bf7713795ca3df63192ca78c6e46633c69169 987fa7e0f3183dbd666daf1281fc95a588e468c812f03918a2862c94b2cbbbd72174353378467741 a7b779b5b4a40011f38b5be95ad8c0931ebade0cacb01749043e0b82a170aae0cd52569652eb4ec6 837ba05a3621b92b552ee6dedd816a2f46f2ea7d9ddc96d1a9d9db800c19008e5a9bdc3323ceb5b2 6d8eed64945650c0bca85650c1c5e0b294df14f576297a6e679683ed564edd32391593eb9c58eeca 1b43ea94b6e360863d064309b8eb79adcdfb73e69cec6f70ac2f0a5a9d9daaae90e95ccb6bcde4d4 f7653475ae570e8cc1bcb91849a9354d019b0cd3f4d7b6857987537079ece4f66073448c15dccc78 68bd0883d135cefe5a8f41cb1987ba928dd3968b8c55c324aca9dd5b488a99827f76b7fb71778cbd 4a8b415f58cbf612aabffc4eea32c1e1f306b002609a71dec225577eccaf2bfd88b50e4ec7e292b5 9c826324c5f9c42fb00e43b843c8afd52e6a608e1da6dd95bd696269435ac3dd13af5fc0ed5050ec e959931cfc0e8fb25d4a89f3c981e2aa34d97f09d703f81a1f46687d9c99abc4a00ddf5301ef9c07 7e9cc29771a064a39e4f6bb033f3ee93f27b76ace96722e8d8771333bf4e6e5363b17a70c5e5b83a 3c91143cf5596b5369cdf3ebb9daa8193ece4690194e5273fa528bf488e6aad8991ee773f7596ab8 b9ea7c8619cd95213599b1e91e3d15ca9907b8c0fa4fa9f3f2608d8f947472d63939dbe72e82e2e0 8d3fb8c47bc49bf126cd8b1c99bf11a39e7faca7b3f1045dc01889bf0cedf160475a762590ba41e5 3dcba407c7060a5e05f14c0fa6e3746ae4d7ae17ed24ee35030cb6b9d5337516e6091b71c147b062 df028d2d8c7637955cf6e7f33e77c03aafe1b3aef479411dbf365113e9e05445defb6f2810abdb41 31270a15792b13b4b75a5ccb3192eb2a46127416dbd2664eba567aada34a589fe2532b79b6d66811 075fabc73bf11148a04fa0eed44f5aaada82fcf9b3d24ef73dab2ae56ebd8a12c2eb330bb400c712 7bf3de6cf6bebfbb05245f3176a90937ee41f93e228845c0c86a273f1e4b778b5cc9ef69f53d93bc 35e4a551651883b95d1230cf45c743b2e42c1a02f6aede4527cbe134dfa0accecde2b0ca20f05ffd 19d09fad1193bb66753f7d5d82e9317919488de91a52f61eb965e18c65f9e04a28cf06933ed1adcd bac96ecdadadd6c6e869db6b441f7a1d2707066dbfe363bb62f02e335a473b1c5dc57e2448eefe17 92e2581893cbceac1f9455603a51f14dbec19cc5abe7b756e580bcb11830546e2f0bbe8b8ee707b9 e437b2d2bbdbbe2591a723ac5c0e7bfa50dcef45662177084eac172ea3b39c45597d6fbbdc6d2052 12c32b47ae03ca2b5e54b75ebbaa4cd37c3e5ccc050caf0ef3fdad75e77dcc22d97e00cec3ddf0ec 092b6196c41bf6e93a4bd3ed3f67c2120419ce567a55eabe39358483f9aef1fd342b42119337c482 3535045b29903b43be4f9523990b98d7419a07de198de4d704bbb3d32e408ae47bb996fb0b6ecac9 a5d4a1c5b6fac93bb7d235e7567896542b79cf6d8ca48777339994129a0b092bc63916d811985dbe 5b34b4a87ed5f4d7a4c68eb0fe9482d53daf0e91fa84b387b337a4140fb432cf8805fe584aafb668 17a83a67f2463029af9abc6ae5ab3dfe68e93d8a638e397d26186d3965581e3666b74237db7c179f 033d775aadb8836cd0f97d255aaac5fb931d42f5209c0fabcf9adf613d9ea996dab43e7a0013479d 5709b6c0356ba47339df2cb0c8276f3f70e5cbf80675500f341eecb4cdf72edb68038ffcc574bf28 a1c3617ea878e392321e21abd98419e466367f5db5475edeead5589a7e0e9a9e474b76c0105d4edc c661e95d189754288f77e959ee3e9ef96be2f3532a7e0517869366bcf7cee0d0663c89e80533cb2e 46647de34cfde16c756437c39e04d5e1e7cbc91f5f0a9f3ad99d48f4807d340726c8b060f630afb8 036b6111ea2c98fc320878a6f866a61912acb180bd497e0baed96d5cf8c0c0258f235ad0ae50f047 b48a834a5dac08fe6aea1cbc06b3dc696fb168ed38adad4a79d6b814f8d8b44372c0f1549923a9d5 4ca4986aabbedb1d5b7c0d37cb92b3a6b075d6c624019e4e93a321a1036e42e755388702c6bf8a02 bb3f0b9cf0688f84fea5b116a61768dbc08b002bd817fa2db627672a3b6f5feb62ddf03d5e574866 c244d25c1e1da526bf82462795582825396fe06f210fabc7be07939f577b03a7741794f386adcb46 56910c42ec8b2b889096b38aded60fd86cc6d72be6d6deddf2794d14a23ecf659a06ebe9e786da57 5c983f45884dccfda6ad68d89313ace31bce65283e2faff6bb1d67a03df9f3e8bbf0302c37256db9 2bfcbe2ba4e321c682435a7c7b9626fb3973e8e89087d9fcf958bed4bbc519a0998cdb110180a042 ff3e857cd60c4e1cab6bb0f5daaa53679b1fd9dce63471d8781fb3b2d45626f90168be37052b04b5 ab2e8c7d5da085726eface61e55a4d49b55f7df6d52aac46ca35fb1a990adfe728703f91e75cdaf6 f472d7e20e3b71df735e65d7c962a90e2f038d491d9e6f3a567d9fb784d9ab252ecef559f21adcfc d5c22c2eb5b80c2dbaa4d213bc1bcc39f6541519b50203fe9b326d3e5bab3a446d53b7dcc5caecc6 199b58ce0de61b54292135900d14a03d4ad7e33d8661d7ba5ca9fca8ca8d6666360d44f9cd39d425 795fa0d704deadd138f6787c532457f582c75c2dffd6ae0a7cad1eccfbcbb7327cddf886bcad1cab 2684de6166071c01b9871f73435b3010d67bde4f72a1e49078eef0ca72624f5ec84c6b7a4adfb6b9 8057ef8de4e7a1e50685adfcfece14f8f1b0ac492745e88a6f14bdf39b0d484a6e985f760b370f13 8af7db5ef26be9578d79204341588f38d9308bc48c9801b8dcd8c184c46da4998161155a9a9a4b56 0297758e9e05c7e4e456ead4cbb654b5c9012691f44102eab59d64a5f740a65bc93012263647527e 5fd87a04ce6cb4369a86c4dda219ab4d71862afcff30751d680b6ac9722da8802298c10448ce390b e69cb3fb7ff2dfb9f366017e9687a64f557530348edaac4f88a47a9fef4cce69b5b4f3a15ec27af0 636d76a4eb4a3f109fdc819ab64ebaef9b3ff5a3799ffed221abdd9abbea6ece1a0fbb225f5ec33d a713cdbf9a4c13231cd32dd36e2d0c5b7bd61f9762b81a0cf59bf2ea6af494b622d9dcaec3672551 b416b27054791785fe7c02787fac737c3b9050d05db80ff2d8d6464e5fc21a33a0e096c464a993f0 6c34e1bff23bcd7ad65a0385ddd29ec3c54b7c0cc3adfa60cf032ed2bf8770e4846bad08cd8e9d07 46c6fe45adac35b32d05c59d39cae9a0515ab335f589b59df0319ea013bbc21fd4dd684a28dff7e1 3de6de8f8bba0daef87031e52f71e9b41f68e0901b63d5ed94f5a8d596d73ea05ecbf620795d6eb4 a7a0ae84e86153a811cfc0925a573390c90f8deb3d59367d63f258ddf6cfc3ceec754b63d51df85a 6fe02e8acf26b733d0e20776e043b315a0e2d2119bc28fa4fc6890214ffd8b24befa4a3ec7877303 1f31d81fa90ba7e665b04faaddb3f29475ee9277425ac4c8e98d7f64bfa2340e55ceda9f69cb8c28 70adfc04e0d402fb9d3973ba122fc53313cc34d3c9beddbc94ebcaf3bc1e98e771780016e7ce59dd 36166f2b3d95fbfe90c4563afbcbe3e69579af7f5180fd75de7f2bf7b9d99f4dc5fe4d6bcfb55d49 199ad01c9957a1eee4a67d16346ff2496f921ca657d3860ef399b9a3d2ad3177e68c05d65f808964 a52ed3de8f1766a7861e8c596fcfb78e8bec68acfa6bc684b36faea40be83071b5eb675f3157ba4b 7aca2e05dc6929aa9a95a6a04bf80b1e3a381bbf0d7b2c9efb86e4e976152ed2060f03dd6af10ec3 d6311e4d8c7588dd12f0662fc34bd9ed99358b4d8cea7674f00fd567dece6358ec531dad040cf340 b57431ea9deab615ebd6ca2d4ffbbea1077bbdc013db8e5e5600d6ec72f3bb7b0fbeedf86deb4d23 4c1a6d7103b4f6a1db5b1146957dd4fbed6b77e3afcbaaacbf37f2be5a84e4bf2ed5dbc92c1b23a9 49c66a3fdc4d02a9d934d6e6d1d267a7fd741c63c9de68763baf9138855ff18f2192faa93cb25a1a b31d846def8019e898ad15bcc13cd35b93df91166ed791eb9c7d71d6b440da60cc5dde7029ca0f4f cfead2f0ad5f96a6defb344d31d966c78fbea8cdbac8bb8b7bce792c80463980b210fb1853c19f4b 1b8f575ba27af01b80c85c0a07b794d92ef5d60956aad133d12989d54be352b97fa567b19f77db39 ce2e98fd28e57baabcc9c1dde306f3c8a6ad6120df8730e882547bc5672a7553aaa5e3d97eaf5ca2 4b0dda7b45b9757af64fd26fcae355155036c7e2d32a4fc65c60d0b5aa0eb5ecb15baf95666adbbe e683985a1650ae031406678abac7aea619e3bd5d67d44e43c180aea6cbd3c40e89609c23e134f4d2 e25da6335f5a16cbb6cd23599c38d8a3d1e041cfdc9aa5d2a16463b24c128f9238314ccdbd5b2992 e521509e2c01c888006b634104b8f2d71f8e723d755f7558ac1f2aee2da09d16bc6ad9c4e173261f f2f462978adb8da562e9a5416da0b165aabc68ed51a30f206799d7dabac93a59e3035a806cd5fcc3 d8c92b85b685ac3e5c68df61aff2e28696fd31864476064acec94063ab7c77f8f226a9cd6d7e6b74 ccd0439a3e5e6ca7f1b385adedeb14d595c6cc61c27454295a3bd860c910751efe565269ab44ad9f 8df649ca5d0e6f39bc3ad6c0825d6034faa41acb8c458766dde34f1467e178c27288fd8b8a2637b0 bf50dc3d778b162e485742590dc170dd7886e6b27d27cafb45bd638b8f67dfec25af8eb75d60b359 9d3b0eed74d6c82d55f91ad871d67abd09cbe7670e39c2ded2182b23237337beb51bb0df9b05143d c72ca0580f0183f53c4deb7496114d9713bd7c295685cbe65b0dc49dac32b4a1ce44effaf4bd7dd2 973bd35a844ba3792b9fc8f7a0d56806550065208d01e36ae278e038d813becad6329904a9291162 2c648a3c6b4f7fd7dd195f0cc44b3b520af5f7c7dd62565a039e8340194d3f53cfb6dcd32f54ce3f d9789e07213c6aacb52c6ae68b2d35913fdc7cf247d647a797966a8d9a8db9977012770660b7aa61 aae0baade2790e35bad8491bdd3ba1d9ed013ddb29e15533add7487f75ebd922f456efc6a5a8f15e 6d3d5c0fc0028d1b4317499def22ce172162e7e77d62d486dbbb6b4364330773fa81497b415f36f6 1a8ac6a853669cb6170ab8375234b761bda21be1d6af8f5b07b9a257abb38f0247c58655889bf593 3f0fd9ec2dbf925d96ee89bfca56b9f0f06b4265254cd89f6a42eabba1eb3e776e7fa75186b3562e a2c359350ebb4a47d0a15bd386b36d9dfa3f2497dca28c127fbdbab0ea41c34fe178f7be7a5819de 8c9a9d0af18bb49dedcceaab4dfb8b3cf380f6b6e06364bfd7310f39bee0985792fcdd0bfb8963cd e9676f1c0d6cc2db2bf593a020801f13b71bee122262f77d4864c2205d000ebcb891d83efd16bc5d fbd672a830327324b917e73bbeff054739550f5ebd6306cea5aefb9442977e758f8de43eefab8e31 32ceedad681a51e27c587b7aeb60906724a6790bea6773a056233d1047034146512e51b2a4cc9ef5 5255ac34cc71d4030b7e57c5a698b809a6f9c86f30fedc6b55d975ea1279a875c21ac7e766c266f6 03d39d256f7f4448bb21799099c2a51c4d4e71408ecd5e43ce1b038343b0fe36a346cb901f1d661b c08f9754bceb5447a18fc7a561277b5fe7bf546e776b0dbd3a8887cde7808d01ca522f3cbb0b718a 2976eba4d651b7e319e52f0adf5a9522c7807a61b283ff34c3ee0fc956f821697f93828eaf25d6bf 8ac6d484ec1f550f6472d45d178cf2fa33f63f8364937777ebdc9bd077f057f46b03e25e6aafdf47 dd6231cf082b52532f2e2f88fd6946a5c8a5cf09dbff145ceb889eae813ea13fdd4239da59c5e554 f2be64eb54951a52d7fc7407b05f9015274772ff2169e6a588987337828424d1c82bf8df57483497 3889deecc8c9b292e6b3b3206daa35a1ee142dafe8a918aa97f41a8b1b7a922646f2f884da862591 d037df4054aed79a6cb50459feb24ff68292758388cbfcaf07ca339ac7a6a7914dabeab48b65175d 765f1e37b7f3b8ddf57e60ea485c347765f13b98dd62e484f2c175b1127fa2c4dc84ccfe86fa854b 46363964f2092afdb9e1815c702e8dc7cfb611aa8db991e9e9357f362e3b1da6fb8abf0caf1369cd bcb63d2af1eaafa57f281db6c40c785831183c248f50476255f583a9dbbe15280f38adc01c89f653 a1abd3b43601075c0af69fc2d8e35729073da33eb9f2cb22f19866b92f0037cc6bed38560ae27156 1ec59a1c3480a7857fa570425afa8dda0732b11aafe492bf6b65d084add158f575931baff72fd0b5 f2ac835f1b839fdc883fd1d6e05a651f1470397cbde408784d981f9279fac7a14feb4ef62a052937 69784d3512bec098806b933e3e304baab238d6e262e3edd4b69cb1510daf1a46e431710b6407f971 81f9736114c16e2a43f6f6693841b44eb1daa5433db337af5f82bd1d87e850f8db0d3b2cbe7423b3 1be1016da3e53de2037a40b4f4e05214de3f300b3f17e77a567d2c784e5f049035f3c046a23ac4ae 8f35c1d43cce4aeda8f530e9da3538ad4db3755b87d48abe1426e704d6fba998878081b400457ad0 43d5bdb393d2d898020dea58c3e78ed39d6ad177011d3a0d4759db5360da0bb14e8c5760549c5a4f 84b90415892cff902c891f92859b86b394e46450027d7f48604901f6cd3ea4d8b999e21164fc0d67 af43567b8ef5961bce1f9520bbe351e1837ed7baaa5c4b461de902d269e397a3e7b7fc19a33e5ea2 b6e6e6112216e345d1adaa7750583e048d15d40996dddaa3d27e9c14fbf76655fd80536e7fce2db8 cc9ddbf93a65eefc8e13e9de32a92ea7a578c34f93deeb7e62a2e309e542d9dfb0b5dba807fa76a7 6f05e4d2578a8863b675b70b5e8d566b4873735d3105360d9d39b145de836e6fbd150ecd462e7127 e4bc42360b8bf35764eb0e9f2a1e79008d46bf2cf2faa316cddca5fc4392767f4886b7457d07dde8 cd9cee4817b7cb4fa19ff8c22fdb512a974d759c9aeca45fdd7efb4b79d8f0a769a535eb000074c9 bf4649b0d0d6d556dbe081f765a1aa8fcd7a566abcc2816a2e48b5f1cbd819b2ec5f9bf5c20552eb 67f93b6e49b31178feb47a6a672c0151fbb2807e48b2dfd57c22ebf37ae76ed2bb2a76d117d46134 296cc3bccf06ff7ec850ef16e5d158a4e42b5a69b725bd6ef75ac9ca5406005cdad6b5c69978e941 53cf9be68fa695b688785a0bc06c6076abba39e951e5b405778fcd51917d9a8d01324c1ad7ed01a2 d7f2c2c81c22cfd0a153a82c7f6026f90322573321260dfaf2f4df8e542c1733d23bac89320eae6d c5e49b495aa13a28edf48a76fb66bfe26d6aa280bc15254d7a5d393dfbfc5200d2ac6f7c45ac2993 30844e03dfc1f3dbd3fdaedcc73855eb58530a68db159fbb636c6fe91634be50b6f90e9d6f70a7ec dd0fc9f4476e4f143bfd12d312fd118c6ab000d36bfacc6a21816ca6aa3fb912dda4fbc2aaa8b82b 26eee9345ec49d5b3ba7ea40761a3fb4207c5ef52502613cfafae8f1b4da5e4e9849721c848c3088 aaed833a16e56cd214e3f4124cbbbd718c8fd136b408169e55acd8fbc0b0f54b8ee4a0a137d412b7 6c2d9b59eff74468c9781e0292bc020ca756349e2243547919c32f248b6d044a924afe527ed4e85e 43745d43a6ac72aa36c370b1b99ddeb61c038d83d8bad55fd1ae64caf227e90152716b230a516b5c 14b6f8a33677773bea1d9cbf1513cae63455e5e3893b80c4bd8da80d0db8f0e74dc44e71d3a3742e a8bd85ebaa9dd9d0830cf4626bdd13cfd8066786b3c656db7962205d9707a90195fdb7b6c8445e19 2ca6e05a4e949bed2b6c895f35a3bc9d278c19b26c09b81008fb4cf36486d61453d53f55f149a36c 8f9cd43ec66a40b6e44297ba81862ab48de6244db8106f919382aa173dbb103afc01da0d2c64c689 ce7eb96d0a1f0eeb8ed287ccd83cbfc86d3ba93eae3eebbf34cc59a1fa7bcfe2d9e1b0ea37a54dcc 36f72fee7840b74152175ec185be1704088adb525cac3cbc59a9b91507faa0d17df45f6bb7303ff6 2489e35d303b580b73a83033f64466628688743eec378ea116ccd7f9f7c9a4684a8c6623a3298894 5d19d596c824e8849e224e924a5a4f9725c9f31383929b82622fa51beace3e2ed6e606ebd9cc7fac 283ee3ce931def55d040020d9c4f1af233106e9559be42af1b0e8a7478c7cf73099a2c47e0f68c29 26d7dfed59b95a6fa43abde9ffe2e979e2d6d44a33961b643cd9d14b5528bf463d7a5e5afca8cf9d b88a82e82a751d0a9efee3d0dc48b71f94853117db3c7e57733f4d992eb09adf58d88ca051674479 54c4a72875aa25d13f7d40b5473d4462d62eb5a404a2b6eaad9ace8bb232bbcb428227b2eff08b31 bb5d9665aab6bfc95ff62ce9570dacc973be585278aeca5107aef8d799d2268c92f26e9e86355883 ae4ada20148de8b6a1f94f2d35b56bb1fd91c18bb5f5508ba0347ffac564bbfffc087651dd6bc229 2a29e89874896aa574d15844bc2ad777d8296ea6c051b378979390d7379fc24b30d09858358630a5 39a68adab35721cc6e1675656a134ea846c98b8c00f8dd3b70fb2163878a221b647fa7a8eaefea9b 1525f0e08d4e47597c6b25d3ad1e68c6d95cfa98e4377732bf3b1f8fb6a07ef2a2974c65cb073ebb bebb96509e634abda38c4a2033a81aad8e5c159b8b77236ede06cbe055006ae2032e15b446ef59f5 aefc019662f79e91e95a7fb8b58950915571ce6113c2d61c863ab1ca4394c6d3eaae99339b719a5d 96623f913547a0ea8be87b502a5201d87ff96e9f0802f7a4aea5fdf5cee1d2fa817977e340c959c9 e9963a2dd835c86e011392eaf118b9c9f3958db9b7274ae8dd50a353759e04225e903a2a9e7b9d64 5fe3e61146545819e9050ae6f79d9b97c85659f10a83e644e3e0f762b33c4e841b12a2f643299da6 ed860788a9e90fb943ec6f52f032384af63540f146ed86c4cd22fc953918d64a94df7d199c53cab5 a72e5ef056484e84121f0fc5a1def9c91c654a263761f19cbff53290d943ce30b7e27158b6f4d6fa e954df423c9432ac3434e04599ce0010184a7e14199a7af9e856c47c6e7279555f6962f93860e742 94e734d95319443305b1ddbebefca552adbeae7a6586bd0a4d2090940fcccfd44905e6031a7f14b4 aa3fe1d4e5f1acc93b14b8aaefe2b0a01e97156d905e3b53f5f92cdfb4c67d5dae2e2a335d2baf8a 37bd85bdf22d8ae3e30022cde5a9afab0dc755ccc47993263015866a7737a0d8020b8a067dfe72aa dd983dda3aeb71fa73b795b5fa9b29167e6a40d3ebd36f53f1f9a5eed3e1bce0445ba2a06c985345 06d3266ad38f591e02cab7739d0c9a6c945843da7654d173a56af4cbb8165ca31e9afdd185e4861d 9960657d6d65e45c78e33e3e0dbdc75ede28f6c520197532f9b844fd28295707f0db1d9bad39b5d6 b8aa5af65429ac223aff1a9d9ad49bf2bd1d36bd095d38c5e0bb36531a516b2f19697c0856c60351 d43a6ef50fdc24f1eea634539efa41aa3a4f6be236cf6a594386b69174a214cfa283f9964f42221a 7207509203bbbb28c83bcea7be19f88c1b11b5b12b8a05678d76512dec82a86da82adaac0c0a0fb8 7dd0a515b2969d006a7bbd3a604f47f805948f04084828dda052d2983e94c6ef28faf64b1512f8fa 8695d42d5955b5cb7fdd7696e55ea7ba37d65964bf8e0d6e03cdb71634f6373ab037cefcab0c3ba6 54699de867f8598a85f3a56a96bab77e737239d352f1331c99f10e5372f57191972b353015a75872 0c99f524650888c67b001342fa79e62d23d223e279c355a674afbee9dab26e4e2dc3020dac22cbdf 817c9538d3186f04271ccfb6866a1b8c68387bbba0b5862f52d5bac1c7c01ebc49b73a87a5aa06bb adfe2e9e8566eb70eda941ad831bce4ccb8507502d8b070db6d71703dbcffab699ce41630c16fafa 762bf4f8a723ed8da2d2227593a9f1dd49117de86195a5f4c0382d2a52bf72d027ae7736c0e0b30d 9604f570866cefa4bbd6085695e28db11bc7697e7beaeda58e5393d37865a166fdada3bb2dd5f8b2 eed6749e92ae4fe6d6029006dc5693a0fb52278dc3de3a498ce8edb6b0ae435645e2f9f10b77db8f c5533b58b4d06551fc60bf14acacbde7195711943dfad73e5aef5474e3315702481e38d1491446da f35123958f5f92821d362e6be9395951a4d510bc275ed4b468b7b9350ec42e73bfe21bd03145de00 c97b36d7bc71be2a6a8f7faddc4c4ff640a3a685a745eed9f0a501728944e6f993836e71d1458cd6 35d855a3bae6cf6e878a80b9057b8f9732bdf63cf77ca7a01ea62d2934b5687f487f311b5a291b4b 84c675ae3e556a15d10466215a635a94da4859df09e653373753b45d7f2d013fce2868e9b80fbb1c 3adb1b82db6ef3447cc59d21f12ab3eae95110baa259b2d97db984a7cb42260ec6a16543fbfe12fa 140e73718efd04de595ec4ae567318f13019f24ea1e11bd2ba32f8fbc78fd15b8a6c143c8e877c57 64a44b91de5a96caf1b5d3be3b95f990646ca1f7cd29e54c54a3ea19b2f78dc153dfe1c5b75a62e7 33eb95b813e6b07c8cd4c24a5c5878ffc6e2d5ebd15331118d2cb04fe6e416bac73b48ed2ba1a56f d557e44437bb69b46f5edbbad5ef37a938ad37f419592f5af08be20797bdd5d507858364fad2a65a 0bb79db98e35b74f8b1ac8f91b34cd3bafd699b5bbeb920e6bf4c07a3ce17c0acf02f74489f9bd02 35f3e545b2c93856a773eaad78b33b26ea26aea0210c413dd538be965f7df1fda2f6b75eb73c8bf8 92164a3d4cd17e422b173917fddf678f83815b8652fbd810aac6ed81ba35ab83e61cdae616d2d9b4 75fd9e83c9e79a6782d5c13d471bb3c03b48856acdd4e9d164f47a3577de0d234fc6e9b3073a09be 8cdd195c7c18d3c52b83d152bd6bf5cb255a9f5e1f1bdb68bcde89bbea2fcc1fcfc87b0745bc4bbd 224ea71ac6076a2283fed4f202ff5bf00c7bfc106b22d3be7b1701464c70751ae648eab9452999cf 71d0d198fdd44f9bc26360fef84f7f946d76b3f8452faf4600d6f98e0cd17854ee00f9122043f82e 21181d516bab3f68307f52c80466a722e708cb83bf582e081ede73453e686a758f824ea59e113c61 c18b9cb54b8d0b6b94832e4f71e44c565e9d7fe6ad71f151edd1e9d84f96534c29907c9e6cc47d73 50f67a5aa94c4d3aaf8b34bcae5217557f3acad8bff7d22912cece4e7de925b788067275b2efe9d5 d7a16fb8ccb3acb820467b23f3a372c9925a287efd775b55a6cd520f944e9092dd00c7e90e2f7909 0fad76625e39641fdb393487560e263706a9cc33ce35581eed7f0289492653b7a38505aaf3daddb4 d74298395883dbb708ab3cd25606c3dbfbe9b15f5ae24b465b6c0ab88e5cd1bafe1a90bee5aebd9c d9b81ad86138ccf23fe66c283d1d82c85addb5ee8426504a785b1281098ad40e3de3a0b6caf641d1 8d1c49f843d22fb8f3e9cb909ef07cec5ce76dd0315b6d8abc2da69c3d6109c4965e11d5aa9993dc 7eb0eec3666623a3182fbdefa599b16fdc331dc1e2b26e873d3860cfc0cdb926f09c3df7aa98b77a d68ef6c4ec74ba2e4679aeda8d87d625330e687579001cb702f9763d7bae7324f60f091eb9a481e7 ff75254d6e641ad512aee6546f93069932352d100e83a10d09cebbf9d95c973ef3a8dead0cbeec40 f87c9f19d7ee6ba457c27ef197ddd7c97837d84e9da42576d9c49faee3ec59dffcaead7db93b6a8b f508ff9a797392151e3727b448b551d70fa0d85a3cbbb9731b1b3fd57c27e375c599f1b7cd41e35e 8e9085977e191d08d379ca5fca2c1ff491c7b6be9b29a0b0d01650506aa15141d74aba38208f9866 56c4bba288957c5c4124c4321a15cfe3faa8b577d7e2d2f4bd80a6bd0f5e7fd7b6521ff95cfd4e37 222a0a499c247771c1bd8ff3ce8b535ee787e43a88985a16f233627657d0771f0dbcf28d1c607243 55da69ccf9883ccc0b2b757fb6a0156ec71bdee1c10f0acbcb3c56ce8a90feb81373510aede4a17d d2b7197ce4f782dec54a5f0bf916e3f7efdb5de7f5a83435965c0ebc5693322ba61d9434a66804ee 6275cead754fff2139e63d50a13404717e38d24fa6a0197d7f7a7914fad736f032666b8bf4702e5b d79df9786b8ca693ab7b1e9358110ac667ddd2dabee60133441e9faba963bfc262d0838c05ed962b ba2dff08b4170a8d55e7809dffa689c23afe7055e3d6ab8cefd38bd55e2cface5de6f37ceb4d7e60 76a7608fa31a5f75debf77491469bf366f46fd195bedbab87954ddc920547e4882be93d6fd85db26 c96771381c7ef589a2a19aaf4ff34893e5c14a0e3790fff68fb17ba345bac707e8a0077acd0b8afe 88f192f2113319b9203e7855e6b5eace29f91ce070661ce648763f249b55e02d2b29f7fd3ea5b815 7f1fde6381e87dbb03f6c2bd4be517812b84dcbb2eac93a17feae045e70c7fb122bbf3027df19423 2d18f518015efb3a376863cd140eea9da171be1df8e6f4acc577efdd6ed5a1872180dde3280a1bad 0964362e89d0c3202b7034e7f5436219ff38b7b2904a67141a7d5f262036a4562369b10f9ff8c43c 28a6c4a51cf184aea05b6e7b962a87d3255293c50a00eecd8bdc86a6e5bc69b7205426d34c4e2e58 964c1bb63934701a9167c2818e427d9bfb69ad4af900c8d7a24086b481c75036a17f3cd831eabed4 5cd6ffc0fc90888d3125a1f3d1c268685a9b98b9315270cec465b84cd4570dfc84fddeb78f5ede93 ab7aadea70582fb510805f68a406772fd7ff741017f8736f6e9ae0896cc59bbef21952e5e6c3604f 1e1e6648b26f218c87eb8f25ce0623abd087011fece8ebbd2878d268a6fd8b841f27b63850461158 e76ce47d55236eb047894de4e7f683d523466260a9d81a03b00967157f9c26e8900b199843fa528b 960caad5bac53abf240a9957b18473dc818a9521e1ee51e7dca5a5105bc18516b8566da7ae8d7d7f 7ef7631897aabc3562870577f3489bff38b7561e2ddc3d81e4b43fca57bff8d9dcde87eff3de2466 859aea05158c0990d92343bff0b3e29ae5efca4fd06e1978b1444d5bafbeaed69826123f5e97a028 1e5688e8dd04dac3867fa887756994d3c1e0dcb827cdf77cb50e2a8d3de873dd860f0f3e8b8bc523 9bb9cb2431fc43629734f4f8c2661f4bf486eb3256e69c3d3e9ad036c4b6a5802cf223b562a43dd7 b8551c69c4f1fb4d584f0b60986fb631f21976e90254be7f0f68f44a4e86109fc1fee4a04193ee95 6268114ecd422a98215fbb5deb9068f195c5b80d2f92e263c49fc4b47a4f7eeaab64fee7011d3fdb a927836fb234f90c65fc0c0be98dd0e4bfc1ffdd8d9199230b24fb85e9574e606d2d2b2da898f004 fd0b7b484e5489adbffe83a43074d57d80edb371c06dba2f545055bb566a8e6b1c44d7d1542daa62 9c8ee3adf3ea967e0262a95a9afdb7d58ac63a97ff82092776056afcc09cd64661fa532097c09eb4 9790e7e9b622d6e2b98671486bb73cead2d68ce2fa5acf070144534377f976db1cc9b74d4dadc5b4 6ba587c0a07a05e594d708cc8b7f0d12fadb88eb83425b326567f88e8e98d428ad77b5a7a1b640d6 ab3ff7e4bf48beadec18ee3412652f2b479cf8c278404fe2f606d4793b56e0668ce04311e1786069 97fdb317b18595f64f77b7d0fb7f300cd809341f554821e5f8a4df032f65d07d9bfb5bfc286ecbf5 c169ceb8b23d8623dab54ea54ff15836f6fdefc4037b4becbf48accca44d866c364bd3803ef139e5 188355886e2f8eb4eb67b3ce2ada3e6f3b84b579d9519f64337c7c3bbf9b1de2f4ff47b2c4f63b80 65a0e02e2c4c45f1ef905cfcc917be2f7ab7e74a42e6d7b3a089b22a19a3f2a83b30c4bc6b485cc7 8bb2ccac4e61f1730f2c6994654dfe9789efe3af6c95c4ddc8ba0a2d91160caff5a8484ef8ae8ac4 7152a7c5373e95fb3b8f93487a31af0565ba2e5fb4ca4d7e6caeec7c651f109595a03eb72025f96f 4757af62a8a30612f3aff05610eb9ad352c54b91101ba86912c9b93c50ddfa96901a7baa5beac7b6 afae27dc9eedb4faeb2453d637e37ce03a5cf2ba09ba3b38c84607f922fc27b1646a3e3f31fa82e2 f24d16629773e21afdfa2943adbd2acb559b3ace648ef2ddb2534558465d116e5a32087b8a5d086e c32e7d811d3dcf9671ea02427b065e894e77f635834c26c4218a1f4bc6026d1b42364b1810986671 042f7372ebefe19f1a4a7abb9a964167d97d0aad135f3d9f0caa0e9b8943d5a613c12e8d805a9dd9 d9b6f7baf7255b7e6153f9d39f2416d8e67f8013c5d9939745f855ee470eb4e64d7eea133d7fe20f 16bcdfc5fe0a2b9762bfe77e0bcdb530a50b6069bc9c768c30394f47d463328a2ec88b4dfd9a7560 819338d18ad86216a7f816fe5196ca98cc4cdf0fdbdad3106067f2aa15523af4b6191d4af54ec04c 161bba39776ccccf47e5bf8bb3e2e0da763ea9c38ec00685fd966fa9913f8654eecdb742e88c3bd0 7415394e7615c82e31282d3f55d2c85aaa2e175fad7954a3d61b0e604447d6bc2fa6aaa65ee475b4 70925fd3f99304db68be3f4d384f859ae2f06a8afae3e82e5e3f78aade07ec33dbf44041f8ec4b2f a97376105b2c726b7153b9ada4432b59724a056b4936b09dc9e635563b8e0c2de5fe64d455faa576 bb30a62bb25299343ec275f9cc3374081e435e590b95509c39edbe62b1ada2727ed04349039e9b1f 41596c54780d34e5e1e991a064d89eab7d2c0895ef7b4fa74a8687061fbc4f42c41d208b3c698e7e c6edafa89482023b06ce575df08d9ca749cca1daed40460dd231c122e4a6d03e15debde656bbe2da 8ed7f6c4db7fd0f5b1adacd2ad60a8ce5b4e21ad6fb19d4f43146fe277b01c141d5365dd95242873 0dad6b426436a097a590e8b4311688e3dfd239ff0329bcc77b94a97c60d9bdeb0355705eeb3a5bac c21587059675d14f979bf63c096b76cf682d2571b16d161b73c6d043f6dee4823bfbf5ebe8691201 ef8ececf631f900b23cdf28f7bbd2dac17c236dfcb41aacedc03477449dc2f88370a09d4d199c547 493e26f633199f96a7d41a8509f7a0378171b9a84c7c169d9a0030870e63dda7db508ce58d8856c0 665b5d439a7f4a5607f171a7c522b9217f8a3718e4398d43f003e98d1ddf9af417cf0bdf5f554f92 c347f4988aeba9c05768a3ff663d24460ee15bfcf167ad7a4f8d976bce3e5fd9711f973874b832f3 1dc503f530ea37f427e0e29c4def54f5bc9adc470b2dc89bc8f907a62f34082b8d5a1c883862139c d5b499ef00005a7afe92583b4494ec51805d2e5d5e84ef399a2bcf7a8316f5f18c12d7786fa5363e e0a77709f1a6e409055c0d3dbe8a60575091ab90c46acb5b31fb9b905c530fa5928127f90bcd8f7a e9f63c288318e0950159a3e9975a5c2b46b5432973d43b37cf0fbea6acaf7c5915b1990e98d2a7ab ca4da32f1781d9efa6ead27d5d30b0b3acd38220a42bfca0179babdceb94ef0717ef510517d0d66a 78538cd4c391a20d49dadab9dfd5f77ab70e65e2dcb38b47919716c7c94bc378c4b0aa59e12037e7 5397a64e206d12bb3921af09b9dcb4ab2dd99855af2f4546d126b06b0da77f4d7d51bf2cbe6b77d1 d6b21fc99f895d5872fde34d28bc6aa13398be773252bb31ddbbe4bcedea8eadc9ab87b4aebcbe83 bef537772a340750904c814f389f9650f125645bd5911f907f344249323bf5fcd750a7a70579e5a2 db90ebf7fba0497e90bb8bb46a89bc391e86850224f5b47d698d8a2482bdac777088e3bdd63e8bdf 79d9e1bd3eaa8456dfab48e11956bbbe0f8efddd74de934981c32a7762053a7555c93b88d5d2dd8b 82f2b5379c5457774834a4baa0d699973e16ad454b42b1dd82b2e4c08cc1c71e930e1df1d2ac82cd 4d30bd0a89ccd3895940e7ea4cbb0dc49d117ce8aa69efcc151b47cdbbd13fdb154e9aaef282240f 70a96214c21e48a8b3795520018234aa1e5d86d78b9227bc33aba2452c59f09cfb01161a4fb4a16b 5db229cf3aafaa58dab526fad08a2f24fe7665f1509407fa1091d97a8075ce92733dcf0dace14fff 21b71ea1c497ed539b7ca76363a6cd2c39655fb036f6d4379b3ed2b67c6f68072d9cbf31fc243e70 05d7da13ede2a10e3c8676bac26dd5a6a6942b6dd75ce36bed0875465a69b95948c700c0347f72c9 ef1bf5f3f2d4617c743a1aedf82d0d0b43b73eac3b89d6fb32a176c9c4d60f8973ce37fb5555217c aafaa9010ccce57c09aa2633b65950ad168c6fab3257bdfa8ac0b5c32536783c49d467a1de84b383 95530ee3a7fbaa1a254007676a7626ee1029296a79b5fa4844f7023bd5e2d9536b480319b6b8d8b1 decc5553d9e1b656af5c0aa9c5f970432b44d3dcd6b0af3f249bb1120d0abc4ec24cd54fec70aeec ca83bc20c9041360ee157eba47797d2718debe868ab3baff5e3beababcc0d3f799b3d22d076944ad 5b72506b12c67af4d215439dfbe2c940e85093294389d3273a38bf05c44fabbbb2f2e8407ced35bd 15bc1104e71392aab25ee5fd4e36f103b3809456219a696f3ef98ebbb8d953c46a71cb0cec588881 11262921e5163a5f6fdd0ed911fb54bee36917cecc56db1a57baa486cc27656b58370166da99ceed 4e772e0acdae99570a39b410ffc86232b9f5d5a259e7f9858258f0d3b031426bb982bc7ff4ed6239 75fe28a5d2498b8165032cfe3b57fd240c8e1965be57fa9d4e4ed156843dc832b56c346b8fde4245 3c3140d774d0524e6ec1eed79c4a28f7fcfc7e4d8135bd6dd2943e1a1498e6dabff35e885ce5a165 5ecdc2a579e95d0c7b2a4702f231d6c1e6855e5e87be7cbfea1553bbae89ff90dbb35437116c102a 9dd5a7af41c55d7e7b1ae909c7697011d2eac95b2e8cfe576ab6be107a51b7e356c310c6c6086484 37aede89aef2476e8df5c31a98c441ed1b5c6c8cb9fb16508d588c0bfa53aa767a568bd60de0f9dd ebeb4b9945778e96f772e85b64f231b836fcaf563d338051597327391b218cbd9650555fa7e4808a 2bd7a135dd3d47ba0c1397d6f606d8e6637119e941d4b240a33b6b1ab74fd9fe0749c729ddfcd679 37d4537898db0f9c73142277fb639efaa057467bedf9fcee1887454bafef4a0f745e567cfb726d43 faa4020bff2219daba75eead65fa8d57c38630a8e93dacf0a228fff1f2d5da55d0bed567b9e52727 da532ed5bcf4ada38db505fea4f9c6ecbc3fe61f187dd7f1b5641555d6fab038a4b8feb6728896cb 15a2bdd244e87e856b12d6c903ac1d8e9f3e1a759c8efb7d5dab7a0f1f66ff2281142fe40250ac3a 626edbb1c3d3ebea9a1f5d1a6aad1ec4d9527de07842e7da581388cf4f208f71427e582d62ed8f24 b4a557e127bbdf5335ec31386f379eaa8b7ade87211a312d30bb6cedb4e04f9fa83ce0b188763b8a 9dc8b5bc451161dbba21c6df65ddde21c4ee0f8c7a2b1c6de7da1a49c2a48e82d27451b2ec370ebe 87a5d6e3475bce5dd46ebceb9b06ffc12732134d67760f3865c5e1f0c7ad0f22f4fe3b13e56abf5f aa5e4072726b9f26d96094f89f82ca7d978ef5b2ad009faf060575b83f752dcd2a34909ee2765552 3ea99679e89cfeb5351e137b395cbf0578dcee1af5833bb7ee17d018442fc4d7c7b08e58f5575c6c 1024fcfdab7bb6d6530bdf1bbba2497df67a91890fff80e1aae5b2dd7b236beb4503d4a8d795598b 1fc5bc55f50b386ed5acbe1930f2c24cd62e8b904f6966f2da656aa69ab5fe17c9656b7d3bddbc58 ccaf5569ed8a0582b4881a850c9813f77570462b99d68998356af36fd176af8783e960c56e315b28 25fd2583ce3f488a6c1205c9f674b7641a3c8c4a029efa75a776341362ddc6c982fc74bf1f305fd6 68b640c643fa2268386c384e4d74d753fe05735c59f17a88f2ceb6388e087b68988f2d331f6049f4 0a96a7be6db6edf85eff9e969c3f78be61b376ac3c8bcb4139318086f9cf03126947cc872f18e95e 84c34a8d46c8f82756d969a6f8fea41ec2ed066cf4b9e36535f26ed72d011bb738e699165271eff5 85fa4fa898585c0ace3775c3c203bdc6c7f71de5ebd141ed3e274355e06bd4d6633f8dfcd96013ef 7214c11798788bfbec0828ef252301fbe7ee1f30d0c90ea492dda17d5d78d648fe359e4a7c7641bd 56e38eb7ee116a4aabe677e81abbee1936fcae24eda31eed48ee15fef74cccbc51d9f7b0cc645c78 1a2b47e2417844719a76c7d8f5ad6c9253d9ad0d08120b91adacece7e3b7cb95a812b006ce964ae2 f23fa122041365a187dfc8f0242c5d932535d3f476d35fb93523865a9b96fc378e7d3a80baa3e2df 181edfdf6f6ddf1c59b6a06bff8d1635f3e2e5d867baee3436ef3db7ef0a7dbad91567cf96d90e0b 8c437e2a0a16e991611c9167db11cb52bf501aba3deda022ff5894b95aa3c4d7d7f9259eaf3bb341 6678f460c93e6154e818e355bd356e5748bbfc00717b754715f854727b56594287d6f29a26ff2291 6d0fb68801836e8cc44bc043c7b95fb85bb7b7a310d7fe627fabf577cbfb1e73862ae4ec28b969ef 1b8fa4d0c6a8aff6d297cd7f8e05e93437a17bc797ae446aca7042f7d8a0c51633a765dec62dc388 7f9a3946145bc4163778372cedad85d2a32caa39aaff8f735bd98fbb4e754d957bc48ad99f3f9f58 baaf2e9d3122acd9972fa95101aa52c8c27fdeb9d5b37689d8a47c0600a8bf165baf37f5b75a80ad d1e181dbaf413e1691d5a95fb2098c0f5d3609d7642be702f58f363704b2b56743282824a5e5775f 1634e869382efe57b3fb3b968a1bc75e7740c1432910676dc20e8fbc0c7782399d48e5acba0fd431 f20b94c66e25d1366f0569e56fb7736baef49cbfc1987f7ca713733f296d950123ac8ee8bdbd813d 95f665760de4e16a527fbf3a6f8538c58a9f32d002841af44419d416b8539891fffa4e27188e34e3 d82677f1ebae8de90c0856ad63a16382683e24ab31b562cd1fd50803795e90bad6e1c185cf7d37f9 caeb3afc3f0e58bad16193a29c34c49347a93779824b63366a55fd001e693f24e8d8e8bf4a9637ab 653648de0696be491f257bde6f8aff716e7330e0219c2de433b990d773bb3794850009d553470855 dbb27965ee4d9ce3a55a84ab4fab5b2712cfabaca57cc74efd7f90789baae77e74f9164c2b6daa17 eb9ce6bc378bfcef107d6454bbd7afc98d747e0a7fef35cfb33da87d5df427ba679fbfd543ff2229 5d43da7457e4cc52039f3bad07fe1e1eab9d6101e6bcc93c2e78c3ad3bac025e79ed5c6fed960715 bd3d00f4bacadfb3b9c7ff5ccd64268f90d1f34d0fb2073254f1163058b0a5ecd11c7f7ac60bf537 26c43ec3833c8e2f4bacd0492485b3cb01694fc242fcaf319828934a84c1fd1e772d709b1475c7ef caedfeb725be84c8bcfec2a044255f13583ad41ffc0a9d0de2e036eafe8e0549e4e3f65bfd07c9f3 548485ef319ca73d8c9be27cb0be880cc0e9496fd3b4d003b42a88d961d38fe7d70d5e48c5de50c2 88bf9c66d706e17f5dca0448e7d15ee8779c662cbb33224bacd5b7d664bbb3821c7e6a50f4b8bfc6 f0655f876477d2a623e01840f90a975f4e8161f11f2457f50b68252199fe544939c6c5e89b1bc4ea 5aec94e312166558b919fdf476023da2c10c9f1411dabfa84bf078b5ae48c1fb2f92ea386b97a7fd 9e3eb91a48006471509f584d69da5eeb538abd870f3ca0ca6da24de901d15783fba734f8c7b9053b ff1f2de413198496f7389412d28acab8aa2a4bf336583da351c5df62d8f0f13113934a42a979e78a 1cd613f52568c6d67c8c54fe8bc41843d3e5a44f7ba9ebd4753fe76931e23850533c06657bbc655f a1a0978c72775ee5acc0d0f0a01bad981f92e2e57f90bc02fceba9a7da23fe5e6734ae34bdbb3b01 817304d60f1e5636662db7c06fef61899920454900f3fe347d53d2df965d80bbff0173ad0db8399d bea2acf4e9303db1f2e5d75f92d3cadbd58d6b3cb8ba7084da25322ce85bfe3cae5cc5cbf0ac638d 7e3b11b1c32691573b6a3f4143a0c2b78901ce1559e96f9dee3d687604ae505af103f051e18172f7 252a0e210a2e18eef15265da926413ef8bfc87ed161b26fa941b5d1c6710eb9c8572a93a96879f8c 61d37d17534bb7a42dcf2890e0111f7087bb52cf526a1f3d7f3d05b1f23aa34784f514b33bae495b e000a6737e59d6eedd5e97299007d05a5e5c4c8b3ffa9b8d707bcfa10e936abf48d5f8463aac74cc cf97d3a82d220965a33328ca29e768a38bbca7fbbb5a2bc0c12cdf066735e002ced41a85a652a9be 3093aabe55f60045fcb089405b23848b1b9e9ad48a68b2963063a0ba0b093e05f1d8dde2a9d76b50 049d12ef99b9b69b9cb3a1c301a3b9659e159a3e6ccb25a5c5b543212f7a75309b1b59e266dee0d1 c1b1554c006ca6df5f364661f7d9d6a7ded820d856606b84ed2a814ca16bd63b4ce517f37537ce20 6bd953b7def119ce37172f543f6dfa4e3d5b1ba25fbc7bc97cb094c784a1e5cf86da17b694597ef5 c468b16bd7460b6e1731c77a26060a71d9b07e7684db87fefde47d2a0b95d38aceabb85a13ae5120 699cd498f7d79b0f0f7e0630ea80b68e0350da8c9867d2a64b5b46e9f4b5414f90721332fc76fa01 877c46214a8d90ccdd56cca9c87c3f9da45d1c4923e8230ec4c57a7e350227065850d9c41227d640 8689d494db8c8458ae226dbdf546a99b40f59cbabc2ef22610f19b8368a1afa550ec3e7289eb1502 a6c7abcb6a24bc70be23d532b924b8b5a22c6e2736de374e8d9b183d977d2995cc2d72a8910b8983 f882b2f421282e008a2f59d32fcaab1647e8a192f6e42200bd058d1a7c47763330657d31cd2b85a2 7118312d51beaee4cffda75f9aeeb80e7c8239a66c5108641ff549c35999475f2b7338ce37f0cf44 ec70d3bbfa14015618c25aa77762888d7a1b9a7371b47b377eb7d4c651df2d4056f0f2a5146ee561 fe3566467b1e7b85bf0b2d40fb5fe3f3fd6e7e616fa6f46109570c321ed6057ad77cb6c0426fa35f 975f4ff8cadcb180df1449fb689b0963abf2d43e140b3fd5022221fbe5370b41fee0a23df89ce67c efdac9b5678fddf518abbb073d41765b16a2af88b6799b72ba7c0aab787027392730560d893975b5 ae7acd3692b7be85bf9f26be60baf71cf4dddaede0f2de231b36d77a8f77d07e7724b412432f8875 bbaf570e6e9ed346e94bdedaeda7c0471973f9b078fdf0e4efcfe13db07082e0e2cdadd5fd9c6788 b78f40803ff70a16228452e618b082cae18d3f05037d98a5a7054032c4012ca8e2575827831d15b2 d3d985a02ec022a7eae1876de83c6a2e464db329ad02b46ec4fcfc356c15acf3cbd11bf466a394a1 62c3a24be16af4be8d3c8533574bbed992f7ecb496bd9549da1f766bb831e1c99b4fa98d24f0ca8e 4aa842cfa5bd7cd14ede35e4774bda2f598cbd9d5cae365c451c92269fea4d51b639aa41de27725f 588e569452deb59946177c35c4792682ca1274f0bf642bfb6e068929f4689a2cdfeb48fd92af4acc ccfc72a3cff427d10247ff5b9b537e538429673399029eaa9cb8eba02c446d4b8ed110ca5533e4c1 b34d55e59dca50c489dbe817420b42e597255bbc39972e898be35465ef7628a5e3e2a37e7e186755 ed5e9bf2900ab37fc9adfa3c0fef42899ef306d32f3a86f1998d841731acb1fe57dbea6f694c8b33 9a6388e213ecebd6f43397cce36a50261fdfad6e9a7e432bb47efc1f93ca5fa778651602c0408434 0b2a63bbf8b842c271fab75a7f386db8030b54c9449c0ff16d3d62ab1753dd78a4c466370068ccaf 43ad2b1d2ceed3c8107dd6169b9ec93d51a184612c0b553e57b744a082f01c547778769bc3f6a9c8 33e206f0a0f260ab2facb85fcedbe0d434a8088ece186844b6a53ddf19de271291b946209dd35468 f64ad870b81e3dbcc9a25b156bef095397baecdc4d4f1d59dc5d842940f78f7d8d094f22c720fb8d 3e589f1f095a3e40bc819c1d6602d2e73cd29ef088127eec10c2050a540239c07571d474e172876f d7ed22f102554529b76d9243cff4b518eeb45daf701211f5766132c34ab5c5001f0ffaf57793eb3b f25e3bcc8172ada03cba7cb40f237dbaa9e9ff925bb9f2cc68d59e7c2eeac3f3236e2b51257506f8 ea4861028dbf345f817a7c459b4e6537a18487be53b55e036e809f297710e99e92c7edcdb218e89b 8a52a7862b33ff761628e12e49e0fdf5377ff3a878403f789f2b52b857cb6a77f253a3fe0cbac9d5 7ef9aa557086fa4735ebe7d9491ebff6baf2882b0f65cd5d1e0a34fa40f472f72e283b664a2ba3d3 856b270ff1aa3c389055cee155828a9dbf3638159c59a33fc6620a3fb1ac5f22d392bb01fbe2ddf8 d0d74dd4e6e460c8d4fb55b7bfd0115b982b6598de60ec06bc6bcf64d1555eb4cbfeeb3b89b8c49d ce23050f4fb41550c7a9f44ad57cd28b6e7a975f0e90f891cc1e474a9bda33a809db222b3f3bb32e 0466086c28b4d6ffc78e9b9fabc88f1966b134dcab240f4f9e9cb3ee1e24693311c1debc877eed10 38f3325d288818e1d9918d16ba79038c823ff7eabf60465f31589644797b93d900b93353a9270873 6aff32369e09b5a0ff23eb2a175ce596e5b3901023420221b8bb6bdc3dc4f5fd6f9839f31db9bff7 30537bd17477d56ad1b6b97d0e2d7e6031b4a0a1a6f333b6522c7f30dc8dbc70fd8b04f718b23f78 2982ba166e3b69590c33f1bebb509386c6a2ab90304ba296408582a6dd84935faf0c1f95a845ae2c fdd058857f485a9cdb734d49434b98c24f9437ed12805662b4965b1709b63b741e38cab4924f4f97 66c6bde462a71f1132bf297e74999b96be79e5f1485bc9f9b4154fe0217638decb8b8d81adc8f9b4 e33a501999771e0bbca094e415e454dacb7cb505ce9b4a9a5be9ce18befc966a9845fa6b8a26357d abbee3e12aedd7909fa5a8c362954e3be849bdd42f171bb7c0b8d5083fb866b61b82dd9feab5fce9 55f0b53457cd1afa2ba439ae5927235f10416bfa540682fca88c0d14bc0b564cf2dd8e2ee68e06c7 e18935459e0af4da9819bf314caf13d83cac74ff8e25c79a6f20472aabf713b05ed2e26e81acabd1 8ca71dac01dc2f5ac06ad36a5e57c585a5b1e1ca52ce05012c14f0b5a5866fece70599397eb6773f 66776806fc281b43cd5fc3cbd6e59a0fc9f467cd56a79ed32c67ccefbae66c5581a0db7c5c715cf0 beb58cfb39f8a387cfaaf119ca7785136333f8b28dc86cd12c413dafc5bc4f6eebb649e4c75073ba 999fbda492cfd6ec987d8a8fc17a94a20e3ed1f55f39ce5fc96aec513dd2486fe99e8ff9cd3c6a3d ad8af128e928766a62dd306f69b809dbd8004adbd4d47f589595793b48ad3f2457c1301628a514e6 4896727409a4461bf30d7da7a6d8208c97f181330ec7d6375ef60774345c5408b389cdab600baacd 1caa519afd20d15cbf0671fdf32c0aa86eb1c88e27dfb7842f2b1d3f1a1dd476e5ce37c4e948cbf9 e4cec976e195cd6ae12e856c73ee17b44af5f7c6a19f7bc2be65e896041a7222d50697977751e484 489fdf84a0099981a7b0a4d7088c75a254d42ae1ddfc8f0a7ceab3a2725d1399445990d50747398a 5146b2162c8ffb9c12b61324df9fee8df7eeb67e81d0d9643b52a7a7d3deed4fc17999a7f8aefac9 1d1fae0b16d9bf634970f7502fcf45a6774374a7c44e5da13d0c08194586fad8fb06c8e7ebed3698 999f8929fa39ee93aeaceed01ccad315c34f9ec9af3048568e17abedb7d66e2e240966fac1735649 7665c767e3376a8ee673f3d4673c473d90fbb2985ed7e6a1326a380679effd21f10527e9b4321310 4eaac93a6b82d31daab29589d2f97d72d0d35db06fddd3a7412f37357b7f3ea90e5d149c9c8e08b8 5d2dccc7bf0ad8d1a84dfd21d5da38554bf11975082ade015e4eec59ce4c51125b6a5e09ee660a94 9d1c38b66cbe5adf97ca76fbf65a9fe7fec0d81ffbac6ab1d0db2f736112e38a3d1ea1017ed8f7be 8455a88136b778a48ddaed8206c0bd4966fb769d5c583dcb766bddfae5ef8ab75b657d0489ddf11e f6a4b2c599d66465c5a86d4736d3ddbed0f2647188283c8eed16dd1a95d5ad48f98df326b6f92998 fc2169bfe38d3579716dfcfde63b0b448916d6d9c226eb694f1879f823bc89612677575761231277 951c1f0ddf6bf7cb9acbb83cbb6b590d3ea049cdf94d109bd58716b580e442e556a78b44c0c36b48 a8d4f2eb0a7613199dbd811010a1b8d01d8645d9db2d875eb735a8fe0b897acd2ef2c3fdf6d0611f 9da3abd0574209cedf8401d3598a566ed5f6346804934a75581c01aad8384f83d938c9eaccdfbe5e 9007d1cfb188c9916f6b8b32d70c4695f99a0c4786a5e7aa93d05facd26673c62eb3220b1d6d3e76 be056ef1c2c28f6d1d59d805f7741aa77fc752af07aa4c09ac614d46a69cc2929fdcb137d64cca27 936a1f7cef52b9c1d5fe28b99be84eaefaccae938d60bdc9e65cdfccfe945b11b354cfc9e7d3b91f 965f1b920c84aadd3785a1b7e89ed5667c9ab56cd234244f79fb70e1eace45bba4b263575e12b73f 2490eb6b6a6bc9e20ac9792d48a6bc7569adb53f30d4734342cbf2340f3ae86c7534b4499786fbb0 27c35608d48fdcd1c20ceaf7ce4e0490ab12ec9f47d7bbe5df2859ddc04fff5dec6fbddaf235695a b1cdfa420bbbbb9bc7ee52f89c3ddd75cb9f86731e976bffa1dc9629efe347145b05412b522a74db 53697bd8de8e774cd865b4b33bae05c76a303f0181b11a74dd037f65818e934f2d9a6d413f48b8fc c1bd70f5fd67d0b39244ed1823a8c8c79a0877db9d24a3b8f57da18a0bbb8b5fedd61762982f819c 21ea67bbe15e0ac5c56f409c1a43b157baedb694f6c40391ec4a8d64b94a788483624ec2e7ee233e 8e8956b98f1c65e9102f9e593345766f72b968c67a05fd29b7ec03276fb2b409a0242e14731de95a 9a2b45bdedc55cb753abef85ea504998e819c3212de7c5a63652ee96bc7061c1ddfde94e8b62e200 fd3d55ba38230db927d718c9571d040b973f8b024c2db1a28ebe189577f5f0a98550b0081f8f9f52 f3c3d044168dcd2f9241d3591ac90a6ac7ed2b71efc0570e35121f422360d969d74f159d3382c898 86f3c9f3911f764dd3d855cf6567f120ed7f2bb7e90c89c9a60893b7c01f5a57bcf3cd3d8bef2752 2b71036b50e8f5c32610eecaef552db01ce75c0c9651293bd2edecdf0a186b6f11d2e5bae543c4f7 fd59a7d4d047ce51be67378561db5a57ea1bf3dd7282bafb090ee5ee397f6a83a13d7d9a94e32a1c f78764da88dc433b4f9e4af6d56fc05a1c7a954f17c90304e80df00a1d84977aad7cbfb91daf142d f741be7e9e64c364889f77f30f189143e3d0b4fae5f0a5bf1fd8ab65f2c162bc23823d66bfeab37d b31650a65308a827a0e55fb95adfa9d774d2e12267fa878402c600b7cb7758a15ce174e07019d276 37ab7e68accad1961f1f4be9c08414b550a5899780538d674f301bd9d8877da081c926536e2b57f2 b9d2f742bb6d59c35c9fe190941ded45d4756efd44988e201d214ee2e37ee0fb236e3a04e04b7c92 737d331b34e3d8b7e25fad78ca948739cfd43a3656c465a826037d96e94d1a0fce0ce589b82df578 6ab0290439a121a7b66525d4ac9085b09569cc677df1e74cc8a3bfb754dafdc07d015931485ae5b2 ac531de40ac7ee41db4ca1053121d5fd25bc7667eed780fa84f5d2d6e779c5a90cbbf61f125aeadb f3c1a6a331fd8fbe1aa35f9754529b7069b03de8bb434548362bec5c783676237d967cb9fbae60bf 7e2d6d3efdf70b228fac4f5860a536edc1e5f08c9c815a689ea0c227990ccb47e8b6d825e6c22b76 1379f3fd4c5e41e56e5e9383641f6f61fb1f2449cfc4ea8b8eddde3ced4f6df3b3df136c2055b8e4 0f207b7e3e38f12d71dd6203cb576d139cd7c3e3c4c92a3da6afff40729a891dafb1f4f75d472a8f 91338a02ee74604e12a85584a08b83076e37651bf18c69de7255e367a7977511f92f436c68ff580b 6d75dfe59ed671ba23c5efbecc5a324e804de3d3058ebe89c3fbd8566fc52234ed57dc786a9a2149 aeb21036a9fe1bc9403be54336df61dee21082328aeb421f00e39d7e9d912ec079215cf78baed8f3 de0ba52ef750d4191c1dd994988232cf8fa45c33bc1f9561f385f27cc99222728955c4dd793b121e 8955d59211ba94698d0724ada80be4cbd23eca68fececaaee5691146abe268db55c7f2cdd453752f f51b139655d2d22ee18e8630b55fb9cd511d151f65a10b5c1dfe35fefe2e9db154099b3f0ac8d9d1 767ab39ceea41dd53ce4ecf37b6dd4ea54ca8cc0663d780f7ff67b1a03d71e71f342aba8c65aca19 bb6fb229248d6185c46783d8cc0d5453aa6c8856b53e932b669b93095d445cb17b1cc1a48354ee4d 665f6c6a561a7e4ef6aa58a7b87362def9d621e6ec6f0a120a5bac9da91c88be791e6d08a89d44cb 3e28b94591c6acf376b2a58e44a1e8dfab3bdd4746c302dbdca49e32b346670f8b3f439e3caa3d62 f668c2ee37821784fb1bbd40efb41cbb48afa7eae0749f2429bba9c6d567255bb343eb8b8d62810b 8c0f1329576167637bc705084e058878b0f8cda96622b56b7eeb634a9e15fa514ce72e7bb965abf8 7c464ed6f6c56f1160a72bbc8a21d3707aa0522d5854ac35139a53b6a73641204a76e3111e4fd79a 50f701063a266d2ce063aca9ed41434820fc7c19ea118f535db3859b96b19bf58188029877afb3e4 200f1d27c7bddfe3eb37c46a1d66442ed6db0d4ef8feb2661e889aae6d63c39edcc5cb994ff352e3 a170edfade511c6efda595f7dc401037c45395f081874fef9b86c4de3e9ad6aeee0a95ce956cc85d 1f95b39bf3301e40e79130dfbc5e528fb8978dd77e72958899b79267edf182758bda421e15069978 af6c5fd4ba796918ae7297305f652fcd2a503e8c6edff477010a7177cbbb2bdc9b29db817791f245 f12a1d3162acf6505997497722e0f90fec699a7eaf283cfbd22b45fc4bc59fb31836276ef514b1db 83f5c308bc5e432027b9977e24b6a40133684b9c57e039333d15aa863cd67232640142538614cd98 309d939cb263059095f6c534f83dc9198cbc73facc7c6d23fc9a1090e33e96006cbab3ae5721fafe 2a3d6b60ee582176b4c6515b921ed24528dfc7d78bb508ef75b3c39472d94a2bc91b1d1b456e7897 753d57db4beeb9abf6053e74d64c6523496e75063fc5ab71049b9566a5eb0c4c302f9dc5cd1de851 53c30a0ee32c87660bb0cdd9a7d7830ba92f63e43ec635168530e706b9788166828fd4292bf6db7b 03735cfc94f575f9383d55bd4ea536301ef0920cc25519ec8285ca95ed156a0b4d3f4a9598760e23 de98ac21ba7ff869250967774511eb62ab0f1fa119125cb53b2b51ecbd0eac4667d71a1248c8b4f6 c8c096c022d33bd2ca9ac3f14e4b784c9b48b25bb7e742e9130fb0e5075762c1e9d444f17897cb4b 2629fa6258e68d456f9b515cff6ed427cc9b3c94344f186cd5cdb37de35b340de9208cf5a9e766cd 8bf50013f59eba5c37a6428f9476a313656842f0ab24a8e701f154869bedd67282c015dbfbb4a4da edc141a874ce4b6940d9d99e42ad3d6c0edbf32968ca9f4bf5ad3dac5eb7387bbf0eca7040645d83 dbdcd7e68f947c7462573e3f1358b99d4f1f65759c9f95d55b21296c90bba94909f4d421a3490d72 b661354ef3c6da1c5b94fe925b6368e9a984a81bc29422feaa2b4eed2a8ba998f254d1ece9737848 2a621495dac8b90e1ba57bf9a2a2cb4dae38ba57af069e8059634eb1ef2e1ff4cdb2d4e02d01e12b 5600ae975a3c5379c9d5d54f511fd9fb747756135c9f14d4b67af5677b31b4ea9fd74a8d6e43f54f 60310a829bd30e46383afaae58da6cc4c7b93ee07ae4e4eeccfb312957dfca0bddbf8e96d32e1bb6 924fe0b0381c34ebf67bec6dff925be7f32c5f82b96f1bc23cce3f654596057f9cf66e52511c0964 e378ab7be3f0519171073fd5572ff7e96167bfa11cf3efbfe4f62435783539024631bfa7a327f21e 8af99a2b70ad31974943a1d3a079899b7e08d4de427a6089c14ac6f7e3a4385c36cfeecb51955f24 2626703d05e5d6fcfb2c29725e3589a4bbc3d7a24a320e91cedc2fc0494049935d0dac47aadd0f27 1fa3a2e86b4ef877727b223ec6e9b60ef5fcab6473e02a35cdaa122b2c46a01aff3adb1b93a3e927 2296a5b5b87873919922d739e8405022c59ff4980d36abda0cc9170498cb097a3f513cf1bc6c76c4 c1fa67d6900142eb3a3e9adf45198085c3d7b59169f539dcdf95d23488cd649f6c7e0596468144b4 ea967e69f04b99caa77ae9a0ad8ed71be30eb63565517a1b3a496d66adc3e11b25a2daf6a69fe9d1 1c746fb59f914640779be5fcb9b68551b59a168e476bd5ce175831270c81af773130ad6cfb3c0e4d 2f075d58c4476d49a058754656cffa04972903a137ef7f2a06cf4a0deb712aa9d43be6588b3f2a78 873213a0cf83fdcaec06bd913a5b695acbeae73a663c6f2e34a1acadc164e105e6e62233bf729ca6 bc286704b558f9bd1cb60551987e1cacd89d28db6ac47486140dd9abb501abe112ac563d429cd85d a69e1559e8f4aab2fae3aa9fa1b40de2d6378b493c1f0ecd817c831a3b5a15c75b8fda797b65955f a0adf6807db863cacba922d0e881a399123821ad12ffaa18841a52c4c26f5cce6d6f01ff660b3f6d a5b5cfb523bffd97d4610797775064f22be5f0f2fb55f9a611be7c7d46da7257ddfe217908527e9f dc14c1a1bca41e6d1b327e003dba1a38543449e591d29ac04c2bd70297612a36c7cada03334b03c7 f5c1d6e979e7f4078ceef991cc1c3f7bd84e6ca1c9f537e3804b45a56bdbe9416f7f2c602174f79f 8f3d183a64458598ad98daf78fd38666bfa6a28f8a73de6ae49765e9b2d2196170e86637ec16a554 8be4b4a51f2420d7662da5bde26071fd1ac86899546de06640b9b881dc15b07960339973af4d5fcd 8a74dd53a4d9685d4f5cbe1922f249977993cd1f566d2be226ca9c8956e630cd1b9556fd84feb460 9d64d5ea6be96fc5a079dcc905fdedd55009a86b67ad85915b831f981109958489d6e34f35a3670d f3707e730574705d1d98b0051bb9796aeef5e576fcabdcaa6715b58c73383be8d2d6c90201cb8a4a d1784ded9a3eab76b9766d226c8cd744e68c06de3e566a7a343673b4d9363d68d6ff47186434fee4 49a282346d3b0648577b4f1c931868bdb68d794b451778e8d33892f5ad0d74aa5958d3d3d7a496bb 94eb1f6b7398fcca712acb9fdb9e7a2fc55a3bff1833b7095d7637e26bae4d975e0edde8e785dbec c6571db16ae34ae9a85aceb3a922c6c1abfeb18f63905379d9e8890550cd863506de075f6ab51109 12ecf176f3135c1d6abde4fd6a7cf3f59ddf18f35bfd1b2dbcdc67b63bda9697347ee5b87b1a4f63 b9d762d530c7224cbffe9a44c8753fd66ae7248f46c8dc0f89d231d0067d37d36ccaeffef5ed7be3 ebc9509fc8fc0f0c8f7a365e4579fc817aecf49a705e7dbb8d3b83612af2c689adb997cee4501b2e db91704f162d6fdbda644d94e79e64f63fb39fcb29b9b2000e7cc808595da79b9fbd0bd4650c20c2 3baa81ceb602d55b2bcde5a536531d3b4fadb8296af9f25eda1cf33367d55ffc7ecba65a0763fbb3 b9e3dc3cbaf0d2232640fbfc80b90e1670b0bcdfd5f6f656c0f89aa621192350027c00386e2bfa00 a5350aaa673c5ba5f23d168901a3b24aca3a62970a659022d275a41e824dcb7a5c15ada56034a691 fdd7d8868549bda82cf2a026e75eb81d9cc13f61f0889cad66c265db15b872f9ae1a3082c616997f 86d856092c8322bb558bd8edf51a49b3aa21f6a986757c7f033bb51a63e6376ad47f15b0b55043ad 958a97cdee35af91e97a0e58c9d7d39a43953db550e16358d294c94a46cc53fec314fd437b697169 a85a53a8c2ff81699c8db3702ab00b712db9e59ebd3511733dc57c68c838c9c21898c4ea4ad63a0c 263b7cf1619bbb21b102ac0fdfb74c44ecff2251dc9f95e2fe34189d8ce5ae9c92b1b0297a1f8711 8cf396eab780a062794aabfb346ba52951f45b33d9e5047c6cc1afd6fe0f497567202fe6c8da60a8 87aa7aad1b83056e6274438482eed6528c0554cacaae6be5373bf5ef74947c49f63002a27a21b43c 20847f1b781bef3ec698d74223242d658c3b3584e470800683119d731a9bd4fdf05bb09c0bce6ff7 99dfec8e7721e65705a791dfffdebf1baf2e946936014eed6d5a801d9f3f0537db9f29218e7622a5 2fd6ab9d9b2f8de86a253aa515297f224efe18ed675727d3bc5aaf94b26fb974e5962d7a279e7b13 c77bd7f66b1c71de86949ac5b5670ed75ca32f8b5937913c6001c01b866200021ec728f96540da14 b83dfdf1f75de2e1f47245addb4851b9c601eb1eb01b833c96155c55b509ebfa56b0ad04c8f1a4ce f979dd9d50a54c0cef4906bddc2a7fca2d871d655c6f4fa2bb4b5d2a8bce7a08eabac5976de7b2b0 2e0dfb14927a9f9f706e91b2ca20dc7500fdf490796b59e8617f48563767dafffa7aad3c4fcc4535 99386e4f1391f190fe98236cfbfed97c61ab4c65d0a741339e892d47c3834c888856ffa1803de90f e1b4804ddba93652a6f3cdb0aaf6d9ce9fece36e843584331cd87decf5b26f8f420f54c5ddde46e6 7bcfb26f63eddfca6dbae8da337fbffb7e805f4251cd97007bde429a883fa50bee6865976df5223d 2bfdf3f4eeca49a1660f8c28cbbb82e57f2099e4eac540dd5e74bb5755b10ebfad3cfd35d29adbdc e29c952836c81ed5f2d5c36a6633e53c0faa9317ed1879a26311e488f83b162d4cde6db484f75754 ccbc0b7023c95ded6d337f34d69c3b73eb718f12ce4576277abc34d4ec281994b2c1a78399e2b7b8 d26f729b1fd255f05ae286056e1737e776bf0dcc48937f614821d2f4f1be3a781e0ce133af77a2db a058006e36ad89cf4eb8b7d2e8f2f943a236a264d91171f3734bc4d5d548c325c522f0f3c3fcaca4 e83a793024fa06554ca0332fbdf0491c6053a2fa33ae44eff6bb979f63a126a574219fe7b96e48a4 0b1e9d74bc9e620b9a1e98ba31a88e0fee4b390fac7db0f22add1c762328b50b78658bec34c1ff50 6e2538588b0704afdf6bb21662321794a3cb003e720aa4ed3ac6db8f22052f6e1dd4d156b3cada9b acaeb3df67f37dcbfe4562f1f7b3b1eabdc7fe5e520ea807176463bf083305ca870ba563759a6be8 c6f626793eb947bf695ffb5136dbb5d2e3e7f13f24c2d0b75f96861748626dddd4f9cb2f54670b78 96b88eb501e88e17f57da178adefe6d6d4fca8eed22b2cfe6d697fd642710f5c722bd121f09ea77a 155574f7edacf94fcf93617b5f8d5b61cb19eb1bc833d787d737dbd1241b85a1e8bf90f0b0775c7e 269d17d743bc71bdbbf7a2d7cfa1c151b5857be602a3dc97c1f78ab7f4c4b8860c56ddfafb35f9d7 e37f483a0bbcb8a023d17c0e6ae4556c62979463ebabf2a44f38fbb07423d23787ecd6741fab0cd3 ac913f11c1fc3cbb61b75839fc0dcde6e9a8d6fbeaa287a307087c7263bbf1ead17906a9d9a35587 5f7fd95eaf8e93d5bc2f361782a9359f61ab288dfe55736270f95f24d780e80a61d893baeb27f568 d243390b6ba2498e7bc96147e865623c4b2440e9134970b5b26594d851a9edbfaef91bd9ffda0a0e ef7a57dc5e507430ef13b2b4d6ada433c4a45aff3134e5cbba7c890fab3a9a7fdff794d2ea2d7601 e494a53f4b337d0cbcff82d9539d956abc62277eb4aed72639bfa7ea7ed2dac64061bf2a774b8cab de97f82c1a246cfd8ba491d3a31c6affe70b3abc4bf1ac6255d0e10402f50df1c92ef2a35dc37ed5 26aa99e8171dc323069df22035938746a5acf2be36df18fffb82f65d463777f3611cad167aaf493e 9e1b7353ac76c3e7f5902bf7ceb261aedeb61aaaabdb31bb79cc18c1bfcdfe0fcceb162d9aaf053a a64aa2dd936b6e54b00bcb5abf757bd8b14cb3a147af25905c8eda7647c000efc6d9a7ff45b22266 39d7eee57711212e8fdf3fd344f0dddaed58d82bb41ac8a8ec7d20c1852ab54ab0b8a75720b782c1 2f7fe7faff89e4dc9e7e63ff7ee8632c8e312a9b304228c45385be5512bef08966525e2c1004d12f 16447ef3ccc64dc9cda1f2a87c58ea2103c6b26932b67e8f172f34e6d2b2c57185f2d630e9a71f08 7b7e5fe20fc27dc43e0f65538a8bf38b684dab8f9688b11ba53e81ca5265f48d2183a3dc51673cd2 a1496fa0ba4f61946dbe90f19280b0b6c1aee5b664528a86fb75de22f91c6e562553f50b5c47b8ca f35765c2fb7d8d1ddb96b162232e92a6c655fb7e2d1e0dd25f820add7bbe7636218ce5aa47882ded a05457263b8b172234db15d13c16f696fe70f2aee04d5f24f06899b299336715c29c3514676123a9 b9f36708fd65cb9e44dcae9079e06621eb40cab093ca1dc94c4b67872f1b66abe2accb800516cfaa c17f195e88da9b878b8c5bd93438e2940b6d1dee3c0acef856e8d36e73e133120b0f9c963a14d8cd f2336e5a1780b5cff819e0dde2cbcdd5cd4bc5bacecc52c7b70b793ba59cc87fad6487346bb39918 ce53dc7bc1f69a81f280d99174321b63e055dff91dbbc44e6a45f210c2ddc133d1280d8a6a40bfaf cbb89796535c26fc9ce696db585451bd274500b70e7d667c2858564380994891de4491261e94f01d c99d9b719a2336026603b2196374d2cc4cc06ee3ed57d7ca4fca843ca01111aadba37800542f744b 2fee3be06784466c6733600e587756c1d141df9fc522ae5fecbc1a108da14bbdd8a82721883ed3b8 cd816447cd0d2b538229d0be420a421eaf67db4a15d158ea30f3ae5725b0d794d57649f82d625186 12aaf2bdaa7db21ee3c1881fe0b38a58ef906f61f84db74535dcc39275d42618ff78187239adbd65 4bec21a5302eee95ca217a7e7dd468e557623cbb269290250771c78fd150d97328ca06777a080eff ec52f38d3b5492b750125f02be6b1c3eb3b6da73da5dd9184d7f650d7df95ae18cb6afeecde1a125 6939a553e30cfa08f1332ecf68fda3a70ba834cf6e71db939d53d73b89cf88cc434f4b225eeeea3d bbf9f98279cfddcba74f9bf9e7526584d497149ae8d4cd26303438e149e1543196ae261e5a43011a b4ba0df37e874d357529892c68fffa96a1672ba351141c7e5de59cc5ef4ee504e18c5fc009eeb906 07f6bc7075b9703ab8b609a61ed82a644cf945d1624bb454816c2bddc95665f8debaf93d11faf2f3 52a2faa2de917bfd53ea85a719c5dc6bea9d74e355d624fba5ae56c0ed63216ae0057cee2ab5594b f4b8b80754aee2c142526e4a0c3ea86254c31d14ce1e9794ee8cd825670a6621b0f6b5026bf5a01c 7a3e79a81fb7a029376c81b312d1bc321e3ea9adcdd4fb646da58e0a5f9f5d28008e64085a8adcf4 5d39d6a405cce45bf719d95c47449413448ecb9795b8919f0b931086a040ac0e1717009d9d4a1619 2e19a561d81d7d5087970c9708a9f2c6f34bf65e5c2eb849ddc96e3cd44587bba2258513847db5f1 d694c01b16f0d37428be3ce5f57d41dd8abd6e273aa798f2493a3d0a98646e8e2d8170caaa7c0d68 94601a91203ee305a69c0e39b536c6305c162ac65c8b0665e52fe5d0e12f9714226d016b43bc0d49 9ae5cc2480cf716ccd19737277b47bc90da69822ce2d8194b92a54e45bcfa87c91b89eca22cb6c31 e2ad67c530c9a8c567d1170aae108bd6f68aa93b60bf13357f98c51b7cb07ddb9aeeee7c6968f5b4 1ab7a84cf42fa89b5adb97fe25b0f45ec3947d70f786da038c892117eb337ed97e180ccf2592912c f7451181b5110236c48ab14d564df18527f702a6ac02b3d1a8927fcaad39daacf23687483e078c37 7361bec68776ed1b44f8591d76f17a616b5a972e7f13a12275aad588f1d04a8f765141937ef04f91 5ec040277fa4bc287ee17e4c38e290934ed2bb10ca3cb4cb951480bfb404a535dbac48e7d89557c2 ac97770a44d9001c427b78bf488ab3b2134201b36006c37a59c059260a4af66bc245e7d3acb3ecb6 20bf41e44642d995a2ea4b96086ff78d1cf2c07cb3ffd150f6dc529f6b2b517c3f68456faed06309 b856a44dc8ba87a3d5fcc6570605aa65eade28b8cdab909097eb9b02f60400175875e45f61909026 033a1fb4513d3ff7405e3bb12756918759f3856e997a01db22b517efa555cf00d1fe0d622f262ac2 988c9a3ac1413f48b46e3a0a9513d558ca518275f9dabed957a7a667523ad8ef0bbbc99ed2e4a20f 37ebefe55ae22b80ae03bb3797efecf4acf25ea64ba36c81c931d1093d8f8becd800e58b3561b8e1 0e33a52e225d95d5e0cba75b5a40cacb47d15543fa417e4f113828895743f4ed04897f4da55e0b6f e2cded3f25af9810ca39cac1720302b3162c72557e896a9c3ede4ae948efe0e5c6396b9d1c5d5406 6e37cd8b2275d0ee5bf2b7b54d63aa6951b797135cf456a4c51ecb3aa41f3ed05d1ad59adbf66c91 6f1b158f7ec8bd61a75d39a4906230949a8d98d08656eb9f3ebbfe915f4cb61d717b4d4f96e49622 91e9051cd9d0fcb34542692499b15985f5579bb5d061b12ba34342ceebf9276c894869f9abc50dd3 1ce2229fb6cc9fdfe88c65a34d3680d659d63c4da476b1dda67b5dda61947a53e26e5da3b21441d8 21e7a582fa784b7fc5a5c7e8c4d18fc944e4bf5993af51c85da8cebc1ab15ea8456f98a30c5177d1 120c2b29e461751193c43832b39a5b576b34edc361fc9b13aae873fd8cf072d4e6ae9e9a30ef7827 87f04a7f0a66e2ccda45521c07e461298a111564f3890aa84f4c654c3d7b3be89f9ed9872992fd93 d0bae671a6b1ad9b3fe5a39385864788c6729d4b5331cfddbd5befa2d55428d70f8035ea8b6b405d be36a2df113217b74de52bb61db18ff6a3aa9f7b55945e4840891febedbb41d16407e917d3be880d 560763b7bd4b25c5b86522a404279bb785ab5cf357631950e65babd48801ff4d0353317c16086df3 8d01786e37df4bcb8e0feb347337eb380b2ee5152f178d529f9480dea3b9523e649c49eb858774f7 c6b29c32c948a51a71369d877a7477ae328e1f55f5de307648a3e56c5489b3a2afcf639312e12c2f ea1cec3c8d5b48feb5b61d89829c2006c8f547db81f60e6358e9d97caf634e9f84ce0ea2bb4a598d 75bd306b54f5ee80c9c4148d5a8c75603f416a46986abfd2ba840f1657d3f6b627996d58092597d7 9219404f4c416fd342eb4039bce97f8e35e5a27d391a41196f3301998dd1afa99d7f84c18b98d6b0 3b076bafccd9d8efbbfe96e16439ec94f2bb933d76c7a8924fa056ed70697d6c4f3c702ad1a9b680 6701e4ac7590fe0e9110b7f134f4e28272958af10ca340b054f580defa20d72f07ace56deca2eb4d 99a27c3c2099aa5e42a55ad3f9c451dfd0b9c2df64bf238c083b7762b2078bf503b903cb1223b027 6c13c03b7f38d9a8324e751ab521bd3efbf4363d2bb372ddcb0129f2b67627f0fadb338b3a62914a 4bf5ac00c6692d0d8570f50bc8bc0b88eec073760f6fd5758b4b411774baddee191c47fd357feabb bc3596cabfced6802bdba6a519931e43a8559a73d6246f0debb8dd6e0ea895507ac41bebf8050b45 9240fd8c98a082bbdd5d77b37a272f902fd534db720644bcb75aa18276d54d135b8d9e44ded9d4c5 c9ddb99a663979c336d9e8496bab219a0f551b80ab3904feb9c764f1c7dfcf1f7dc95432a18b3657 404b1eb78d9e8195d1189d9cf31dc588bdc008c71d0612e98da0e69eebbc197c1abf8c5be7e022fd 5b3178002e0df581963d3d8f12395c5976969af64972ba2e514db8ad6d3d6db71e9e7f063439b71a b8ef37eb3a386c64935c9ffff487ee4f6adf719bd44b2bb1c6705dfd687a404928932bcc8d8d4637 b5cbb2a741626fd730d25c1b36da4373f2af5118d244f845229dca599d8d0dd6df2b55ef620fbc44 8577ebba2161ad7474c3c647e4cfd606a5069abbe8b1e023e8dbd6749bbffe97acb1e115d2dcd6a8 61b2efb8d5bb9453b9651ca205f1c13b9bf462aba7f14f9d0d0417a586b354bdad6ea2e5d5ff7055 ae5838777cf5d8e7941801bdce26599fbcf42d2f557a38cf37366ee5e005604751278bae085e47c3 93b31ec6d47f2109b3ab08bfd90b35622e55aa74afddd4bc4787845aa5feda63edc587f33e3dc12d c9f20de7e650def24e93dbe1e77159346fad9f13660adecd6751e895ba367261da0f2fa1b883bc31 5c99dedbb55e7595a51c4273135c3d881c9d72de670288cd07d9fb3eeefdba38730e7b39a75e5caf 08ea1c8382df6e224e433f44cddde0aa8aa3a84e3b9d0eb42a595875283134bb708c528efd575827 40eb4fb9a565a1949382d2ea6403b72fbdd12afe49d67a6060e7d685474dd32054a9a4d4d80eddd2 20375f4484b2be14f96cb647f9ef58f48db98af701be52514655e2a5671592adddb48f7752bd1e2e 1923b0d06d6754b2720b432b5917cd3a83c9bf0416e11efe36e6501f63bad1c3994d9955545eb74b 17aea6a7adced3e43cac5a234e8fbc91bb168ae662fdc07357f05636e21af9f9f70bfa556e95bd91 a31508b7851560de0344349270766de27011339fe1f8641c87825a8a74d8369fefe7c33c752aafff 31156a46a3aa537ced42fdbe6518f4c85caff699e8b50d7cf0433c6a8860bfeced80248dfef05dce e7ed1568770ab9cb7f598b84e815a47cc6c9c1fdeeae9f30a55fa646bd59c235d38d1d0d340af332 54b2deb5865bded51993124ea57f23f94b6e09e8b378529511ad449de3aad1729f2ecde41c0d0a9f e06158c147953a6b0c72dd28b00adfbcba58c5f999bcb864e3347efbdfcd0e0566f330825efbb9db 81acf6e44a81730436f5716b9d55de73e9fa940f26d5efcf992b9ce7c7cf6acf4b72ca6f6daa7a4f 3fd99d5de5835f27cf2ddfbaab7b5fc276a316335cd605856dba7ef5d1902a225c0c4502b1315fee ddb2fce4a148c76e9a99caf6f21fca6d89f448ed88b61db9a44868e7967845ac1dd4fd730e908102 d7f1ea527e0e4e1df02e4b39c276db45e2b7d2c358f3e06fbf121e82604b69b123d66db484b805e6 9a94b2bdafb35cc005e06dbda2a2ce406d6c7b8a0b0bed14a82f515b8b12229bcb51fa1b1f948217 a7f55e716db25cd4b52955593be4ea35aa8b11c7e9b9c5f6e40824dd042ffb11a82328ac39b9d9e1 f8ff955b9cef3365633298427657a8559aa7fcd2358ee5af631ae4afab0a733ecc8c2725c2f61a13 2a5f1251eb996183a9ff97a9e44d6bdbdf96db70d4db5ad35b5ab01b8894a583f58e795c59ab665a b43bf4ea011e5dfe612d3645ca2686a4fa3f768bc3f351e084afa76e1dabcf7c733cb425471ece6a 562a1a7245becb8163593cf4fdd70b03f8634fb51c4a7dff59da9f30d83ff4e493efb5f2bbdc97dc bb9f6777f49eb950bed63ad337b222f6f0dda90bac97d732ab6a9eecc12efcebf9556a59cb4eb8d0 6f17b8cb87bee6dc24112e9d4dfd70c4b2a1732cf740af49a39dfb52b27aedc661c5ea397a8c9a99 87bc8ca4a6413db215767f93a60e836d2c3ceb61f3fd6ae479e056d846dbbbf6816a6e79c03f0bf4 22faccde931c55c62bc2679fa4ce91d91efe2ccd8070e4d7cba1fb8f8e0a2749f5c38d4452f58930 fa88cb1621879a6cb9c586ff16a406745d06edbe097c9f5d3baa133e329bcf15ff399357b0879e9f e6a9a3a37254e733312530e26d17a28adbb552daedc781cf90616ec82d1acacaa923f607069bfff3 82d0bd9e62aa2eb4057f11f8f32f1233af9e54d9f0fdabff290a42b8d5720d7ee7594f20ebaa9bfc 34fb0d3ae3e0bf5fd0d62fb455b5795af72cbdd712163e70c359a8dd5a01faa1bf39f885e3f998db 1f51c8700e55ca56c85bef7f91acbe24c3947be5b697ac763f96d66b767cd32e5392e760f0bc4844 a46c8ebbdac675f678f639f737ff7efc0f492ff4904a8e686e2a0662e78b3bd813d77200152e68ce 46982ae3a18dd934b70c9e96f5d00a3fb751972504ff0f98400ea74be288df237665474f6d161b39 a64c6916af4ff71e93eb072c978a0022e6c05d08df0f7551c4fc7a572a0ae86f17a4ec05138e92aa bc68e70220cbd3d8cb5df59978f64485c7ae34126ad5619bebfa851bb60c766fa90ecf3cfe5038ce caf80d3ccafec757ac3cde627dc0ec1c057112d548038f5baa393bbe2430676de8fb4d84e9023f82 646ffec5f9b97b59f484d9924328a7425c15f077b403da8572517b487db7d328b91b733058218a59 6845643147adf85b6b08a8c6b0fea04787d20b03e708a209c21c615efd67a15cc8f30b1dfcd274d3 53f777778ed7245d8ac5ccd9748ac95b50040950f4e3ad30232bcca146512f6165108d08a5dd12a6 37cece975f8f8da8cd17047a0c98c597616e1635107957d8b2910cf4d41a385cda119d9ac397077d cc4afcde9554a023d7f62f18f5b3ad3457ac53cfc68c2a5d5e38665d575cd16caea481d33f444337 4de9211ab49a8a9cdcf307974ce8369ea3c033392d2a576773b423b28f2e468da409d69cf8743a72 defd7e04faa6255a6134649b23b24dfe54a638b3424056ad75db385dd75cb09fbf7c7a7feae1ebdd 68d3ae8154c3ebcf764772ead6d1d2cea6150fb8ad38e3c97982bd8191771c434db6955a69452ea7 8338cae75e5dac3b7f4c48ee88cbc171365e6672373bbcb61bdc7e7d0fcc4ab1c2cd1bd713b0f0cd b1d55f6d5c9e3def36fa67cb9a94cf1f1bc21c8b254ef54c81e56fee5c2a74f931dac3813e7f62c7 77e9e03e2bc55c83b988d21beb7e7d94d5b06f763debbf61c252b5c91e1a63573a9ef03db70f8704 ef3b1d9748a9424b38d9ab44042827a88b1f79fcb58c85247bc0e3f7ae59ab9caf7d6a844d1d6dcf 3e6ec2691e84cc399e05ac997635492bc07d6ead98195b435eda08fbbae56a9def9dfa7ce10e19ae b2888e9972bb97ad1e6805b2067c48aa484c0271da3054c5d1e3841190e18600b1e74d35ed43c035 8840ac5d702dd5981e964895d1ed5f5d903790c8eed6f062d06caae376bba0bd2f5e99dc2fab38d3 db8590eeae2e14d39c4fcf887ef12efa67547358a54f6e0b27baf9314c649a253fe58d9914ed8379 848f73bc39c13ec2f178cf99b72642922fa93ac54df4bcfe99d749da55265725e25a5f5cca564dda 83e2ed9ba4ffc97100d1564319576e2bace20c76e90b4f561acc80417ee590e805a068772420d0a9 d275f2c730622a223d2a1c5787bcfd80917fca128c93a577fde97978c2604fd205568e0f5ebaee60 04ede14bbcc8c496d75c4b654adfe6989a36ef16bdf2eb52116f8bc53f152cf708998a955451a577 142656b7d3812779931ed9743b70fac57da64055eb37b29562af9d7f16920dcd23877561376bb0ae 94367f63abd16b9c276461924724e19e6b0ab94507a3af673496c539ec740c68f5e634939e2a4cad e0576bec02124c3f72b4b13de2ff520ed55c700e2fb7e7befcb4f7956feef33909b7ba4252ef5bb0 e16376d6915a9fd5b3b966477dd19c6e1ad266d30df3775f81a5e17191fd6f9603dd43d6177e7c3e 2cb90edc927808b768911afb2e7f8a2ad9a1616ba2349656a94c8bd0e6bd80363db1243f4135514b 31b5fcb516bedd316961639da54d98fab281f58bac65cff254f3d160146770c1f86648234dd2eea1 aa81567a023e5457f9b778c6348c3fbf7e530eb0a699f9de5acb374c8c7adfcb3c572f8f9f5adfc7 11b624ad62ac8576077aeb1a2b9cd73cd521cbdc9ff5efd7a5ab45648bfcbda0c191a0dda1223e4b 8d8a59ee7713ea12ba28699f956c6990898fba55e6317be5e157120c4d6d6763dcb2d47d81f9c1e4 62de2bfbdfd8aace6fcd817d62af5b22065c99f596ead2eee691195dbec4c7f6ac755edaeab2a032 a3d17509097a21b5dd4511539e3ce1fc87721b939de6d2e044e59e1b7a2d284dc98adf0dc932d695 dd7e7d15d2ca4bbec17da6ff4de47203973dc5a3142cd786a1833edfe02f920ea50c82173c37f1c2 edfb9a6bd3e9d57fdf06596731b91fbcb8b6cef7089f1c0b02ede4080f42f881e91de1aba93c43e7 fccf64bfa642be6f23e1a0f65b9483977a6ac5ee3104cb9a1d16b097a0ba5b5a6a43eb0b388fba75 58339a8b245749bdace052384cae598fc384524423ed3068c9b849f1ee5064786ccf704c0d15e4fe 1e7451aede7c0bed7eaa29368957cacc7e7614a33cd7311ae15f49e7e6119ef8ebf010f1b70af112 a0de3416bf29427668782fb450b1b7a00f92bbe0fbf57754a5a497e56ce4d9a951ce61858bab6078 03cd2ea70499461f534970708f075ca8456bfe0b932d93d1057d789823bb3e2f29564b7d8bf26945 949b3a3e506beb5cfda72b02e93dff55fb71114a8cec5c50ee1d637975d8d3169c342c619d7749f2 34e699f6f8bd7210eb437e3dd3be8118122fd3999a130461a07fdac16f4399e44d5a80d1a8e7d60c e2bf0bd482f9990969a8256ec695de231ce1ee9b9d3101fc095f2f48413977a999c6550f687dbedf 3efe5e1056210fe4f1ca49908b58ab27a631e8a1f2fdaec473c79aee6e23f69e4c3e75b55c17adf9 03dd087bdccbcae072a6afbeacf5f0f66bb752ae65c82e1f21650a17f2398ad2aa0be706684d466f 547104b020d0e9cf737b76ce7ca9f78d97aa4ee8ce753ddaf1ff4c3b6cbe883611f9ec87b8eb3e5a ae67e5a3d4e10cca9d46b1af7a8386d564161c90af23dd4deab978e32a104f98cb390cbeb41b51a9 f42bc7bde3bb40964768a43764292005fc85d04979bdd1fbe2aed69c01d3177bc0961ba3b35e0685 3ebfc80afc79f4fb47b2b6dd5f9144c7afc24509ba18c4bcd41ec8e69f6f4f3538da6f7f1a30c73d 8431a4c10b1fa9dad3e55af09561554f5af5dfba11693a25efd9e55420548993cc5daa705f6e4b49 4671894971d0130c322a28f0b18235891c2c8ac7f178a61c2a765a88cbbd99346874998c35ffb20f 332cf70c61fb1ab7e9cf3de4241eed855293bc6cdac2200fc99a7359cb9069ddaa2dec545328bb9a 9980aadfe697dfc88e5d3abf452c3c15d3be927c2a845078241dfc6e7ba1ca178a13912d302bf8c9 3643f5d3011652d9a96c0a3da9676a0c65cdb379a5f11f3d3cf5d915951234775c65a9ba6e546b67 3e014a2ff4c8803dfdc0763fc2d296cbd56287db18553f582ae5596d01e4a91c6bb639f4777829b7 98bf4d73fbf9a0ec8c77125c9f130f339df5473cd7be7461ef62c5e6e35edd0866bccefa6f0ac9c5 5d59f50fef996fb408fd230c12f4982804d4ed326b3a80d79ab0875253466d9c48ed7477f578b85c ca43aba23bb4ef7dbe24333d8104ea4c3fb2b07af9b7199f630fa3854702db419640e1c6b1805754 f0ed6e879d2357aed0d56faadd4add645c84f9789390057731bb3aab1d38324f7ceb9fed30c395e9 b5de6d72700b12aa496e9766fae859482140ea8c34df7816fe2c0ccafe239f1d1a57f47ba98d7e19 e9afb520d600ce2ea7f6cc5e386de851123cf55121dc619b603e637b006f19f01319d423e83ce069 575f1be96599e4e996ad094601df66eb83805f53299d5f9c2abf8c6cc4040930a4cbb7e1cf51c3ca 15b8d5c3579230fc381f6dbbb02e65235c18e2e8cc71c6f459f99d27a36ac55e3616a0023339c0be 8a70a1eb289d068b61655cb1256600efd4dce8d4afa313f1219396fab32e643dca8b797112e7957c b59679c875edef589c955438143f04912289828e095ba6e24eab55835c56799ffa3725e74eeb6576 8c23aa0ab410bd344186bf613d0ea2df18441b266bfeec8ab0360dd104b8513bdc739196d69e1d29 b478b69e2beba8de29da4f79cd8760dee7ed9c9ec66925ebb1f8d32552c3e3ad520fc58f08b330f6 de232fd6a8486e6ec7d5aa59507ab6f85ef6b262d83265be2213f1a8930656afd0ffd043baa8fa92 f58247373e59dc8536962bb6ad4bf3c109b72d92afcd4b3c64bd701092168032cc7bfbaf3f3a7d34 f3bf0416259bd1c5de5eee0bf7f2b6e52cb6eb39bff7a256d3c5d6436730fe32b819b5d9962b65de 72baa6a1a9478af95f61906cc6d53ed1e7f323c7badf4024992d2ad419ecde9dd161dc82bee7946d c464a62d4575bd039202f541ebc4f98a79fabe20ee572f35225a6c5884581960e791143048afef59 7d66e737a6560e63efdf74deae2a68ab608aab1c9f0396913d0589df0e2365368c33050ccc020131 ee9f15f668b35b6376e50a083cde3679a1ddc64de8cc0d20b15f9404fff1799aafe18806ac351688 6f3805b3b00efe1d4b3dd6c62516c05aed5e532c76c991ce5f374c832fda23a986d85947be7e3096 8fc2d7dd35a489ea60e622fffe1db6a0df19ea77fc2f51f79e1fe96d04829acca1a03598b191bcbf ee37df4f20d843edd2b2afe8cea2ab0fe947171892635b9d2ed92c0304a8ff506e2bb13c1e75aeed e501e1d4c1727756b6d0b557ff0c4ab8d662908dca9ab9b0d0651b034dbf934d232d82b7ff55c0b6 8e16e943aad5913b50cf6b71a052d55fde61abd40f6e36a00902ba146db4f7d3b7d6b4df14702e95 b766a9bf0dffcb5a8a23315171aa6d7ba869cea3414f2ea6f34d7d33eaebe66dec0de4794f3c175c 75bbb3eaa3f6d818bd01e7ff2bb7b8618198ddad7e7ff87e63d7ad2266bf6c7bb6ba4a175d1c554e 8f8a62cfa48eaaaac2ce004ea87bb466af1df85f02cbe6e08fe91844f81daa92ede9c0f4935820aa 72ab963562d255a4aa06d65ea273a3c3bacbf453b9685f2238ff6b2d8dc521bbd3f9e430e461eca8 ae57aebb3dea0dfc1f59dfd5ad20b36cfb5b8c280822a288820124e71c44cc8839bfec5f7f757d77 9f73f7be2fcbb1c630d0ddd55535ab66cf6e96aeea80627cf1a19de81a085496d088769dcd5e3b1d edfedfad8b93c2b6f3632879f8ff3021dfccb1accc3f76012b907695beccd281f28aea458878f77b 233400eeaa6e3d5ba572996b8dbb46749299faf09fbc4bc03b72fb6f5cf82293b25130b9c332e6ed af889b77cae3eddbb47f93a616374da07e9185891c59b25227d17f389cd3d11d677e50ecdf025cf9 a226d2aadd6d39d94462147b0589b74f03806ab70fc3f606ea42e29f9451c2deb512fb9e843f5f1d e3ff233a373f2cfe6965e2226d9fa683cb682c4cfbc53b320c3af5e963048c8427aab4815aadf0e2 c8edd297e82ec2fc3390bda59cfe6381928c5bbb81de9a58a5800f9ec48d5f4db0df68c093e5b842 b921f282f51cbaa53e143d844dcbfd0de4c3fe97dde2dd3edb108585a6731a123a083225133182e8 118f71fda07ac9162ff1dae04291c6d0eaff6f69ff7e987838edf857b785ee36b68c188d94d376c4 169cefa8bdcc16ef475eec7fc625fa584964536cf1ffef67fffd24e8b94084446f6d6f9d51a39741 c4faf9abd990a4f2419c4b446625cbe3d1fea68a98a631907e154ee73cb6d1f7ed271a74fabf5a6d f5cac39a5eec1932394c17fdc008098b27670340c3a3f7e0a14d184b2536bb42a15a3787e40afab5 41dbd57f5b1aef7412e29f877914e1f4eb219b8161975e030872a50f6db54a2fe30515cea5ebb2f7 1abac79aa4a388fa8fc42eeba381f4c739f9f79c50b096fafb0ca945b43dc213ec47edd56ecd5b11 f0c2833206bcfa5edb7d6e49a17628c99375dc057fac8a7fd0a584d82cfdcf93e4525d1f3f83db56 c5f6eb0d540c507db29fd4f66a387f36cae816af3132c9dfd49650ff5faa554d496effb140034fb6 f57e8014adb7cdcac23351909af1061c1f1bb3af422d50a6249516685ede7146c9e27ed9eceebf17 68d52e0c3974714da47c14ff38b7e01d79a79c77cc5899abc56619392eb73ca2b3b67ca4f57f22bb 14e6ece73f9ea43f16f55e0784aff78a2820b7d24b9aaad00610eb635de0965c5bae7625afd03fcd 86e24acfedffb2b4ff7918fdb90cc4d59a0944bb7021c045fab4c41cb96d24e4592a942b5b5f97a6 9dad27378a4afa5f9ffddf7f5f3d1d28fcfdf9feccdfeb14999df97f1549e7d92a1440a0f8af7ffd eb3f5e0b14057e8358a1e814c6485a2f1c0fd56b917b979c52b1992825972e7c7d26b4c8cabb927e af4c706c57791e9075d5d8521ba0f1f0b31f31218dc3bc46494cb576eb41ddbad6bcc920441e3760 9ad711887644077ac4955ac37cae3d181d5928bc7d295b64bab5c566a5fc6837635528a283566d8f def181da32091dc750eff9c1b296be6ff3efee4f440fafd5d0104fd9aedd19a12fa1f341f793aecf ec4704712f33c47523aa3dabda4cc836be3b90c7feaf5838433a83a6d2b606fb85f9a114b6a5d24d 0584e83ca9cd87eab63018b5b046617482ddddd8d4c6bfe317932e359d4d1ec6f2bb166778cf0e0b ec795a369ea5e97210e09ca0b82adf2c6c8efcc92f7705c75442b1ff745f3f61b9be94be5e91cc27 fd8a824c3d4139750b47d501d89ed69f858e5e581bb93e5f4e7e1bd498a6186942494935f7d121b5 74ddbddb1d9547ec874c8c9c208ba62ebd3f705ee9094ade02e5759f53732f809e8345b0570ec750 47a472d429e1dde851a88871f03acd66a3cafa91543b4b3c59eb1bfdfb3373b9fc3ca52d136ea7d7 26632fbc4df058d2cca5bf2a5f0177b534678bb55040f61b189a1e3779c3cdb75afa4deab073c3d8 9d3da99b9999b0dfe372adbf3fa75c9c1bbafc38b4e60de0b09facbfb3208e7edc94537936964e01 7236cfed4dce9fb702025e46e59573b99bc1fc0b65c6fb1bb846c35bbc059a773c4085fb7ab1351e fdce107f6c4962f4ec46ecf119e75cf202ce65e46791d542f7647cb39cf7312f22731bf941f67864 1e4a18231e4a4ba6582c93bbbc5bdef74a686554dbd62a677c5dad4e67cf72f5a1cedf80ac1268ad 3c1a4b35dbaaccea8ddae3559f551a34d8e19c1db86bf486d018419ed09d69d90dc5c73b3040eabf 261e1c4e1a7b04df3c9648e69757cdaf0164cd0fdb9fa3d60d0d5bc87198b456f4f1808d864b087b 6e2e5f0b5bb157bcb926517c3350ed0ec3d46bdd62a134ef0630251244f6a289cbed82f57492fa90 4da0fceb47913bb7fbe80be6aa3e0091783258a3a735c5e97d88066ec0885e2c1ac99005ccc6a87a e8c6a3c5a3d91f4f2dbe38a9abd774b206248ce1ab832d0b8ec702bbadcfb1a944d0150e59222fee b01a7e78a3016efe34c75e9a255c3b6d5e74ee2d56ea239a247de6d5404e36cfab32edc35d15422e 8c9ab9634dd32468a9e3a74e4d7f7c6ddb8896cd82c9606fdb82c05ad73a14d08d6db690aa433804 e3bc3175ed26a5eb9ff2e014e043bf81571afe611cfa81e5d370487e80202a645f434db9948df9f2 6b3d8323bc313bf62425b1cee9714e2aef765a2822563ad7e8eb826b38bd652358abcb3dbd14575a e7c8afdbe5eb747dadd67f59f4c679b5b96dbf2689db0fb6507733a3e1664c6bb9ded7abca7bbf83 d94eaed263ed8099c9f070dc9597476ddfad9dd060303ee5a59a7d5669787ac10496bf5cba0273b5 f31179eb8115f8f694b2c23da0da3f2cfde87f63f6e3fa1ce14f99d59257d150ac9f51160b5f430d 0a33e948145bd7e45e4c7d262f11adf056daf57bb5f2105d42e5e385fb54983d71addc5e9d53556c 9359f553aeac00bde4dc6a4087846a5e04fed0e717d716827a5a06df60af3e9e807b699f4393d778 f07598e5bca1da470e06b20bf0b3c82d82c39a896c7739d11c4b48b1f918ba19aa855f63047bfda8 95b4c50423edeb163b1dc8735be2ce551ca067bfbb96f0d84cfc0ed92e943a175455bb2a51fb1090 91e9c492d8a3bdf1a07ce97d226946fa42cbea13514beb5fd69038305a8444a17b43a7f6db724ccb d5e569d8305d68b8251c76c41fd5685c8fadf778956c7e14ffc9f401ad999a32eb30abe660c57208 424feb8dcf79ba867291e3a97d8d070fc50dbfb5479a20693925225bfd8b3f263c28e9af6151c685 e65dbe8dc8bbe249c647a54a75482b6e3694961e635be791f96fdf187074c18c4390f2a6b99fa556 0fdf95adf7e2cdd889d8df3853d46fb90da8e6bb878a59f5ac7a4df57b37e9ea7f82dd20988fa124 e46a02103592848b0ed8691d9beea736eb15ebccec3d68cc929952ff7570e68c5d40d33a6490e956 788c17f276282c5b78662ecf563b5c3985d96a4d9af879fdc10ee5cdec28b6b68c0f33bbfabc1cee 3210fe64da5e62f61d3e0df777e13acbfded607da0a1f9e1581a95cfc714a9fd7a1e27563b246748 a4d8f3f1309d5cbc064f5d4718d5bed5ab55e09643f7f7dd3219efd1e360ef71bb7ed4a75a708aaf 12f909bf165978173ab2df2c784c6614cb974a549487b757f11e8df912d3ffbeedd0c87e05eff280 bdb4cbeb0b78af741c29a9cc95b7546d7eb04e35341a6da0a10d7b80977d570fe459abe6f6d05dbd 4e81f5ba33eb08609d500fa05bb9f721b02aef208f21c60de8de7c37826d3f8491953282e37bfe6b e523183db821e9fdbe6e768feb597373897d94ea6e03f4307fc72d46996c5af7c9e18a498e01b68b d73eddb60cc2c721b2ffc0e3a1497770efb1e96cdb26d91d95fa87ee0d21444256c670af8ac4e75e 0063bf204db6f987436e9f7bbd3fb97cacfebb398e06d6f2b9a79ae11aa0d6a70d434f7460477fdc 6830f440ed32c2efae353a0297fe5836587002194469b25a11458655bc3a5bb150824d1e4f653a32 4e6bae28157fa52e2e7649851f56d62fa1b0976421ce28401c15fb8954d215469a5712409ec4cf8d 529df63565d99d932a37a5410db41e4f6d57d91c7579b9da19a8b1db1827f3b333ad2d75fadaebf6 63bdced3a61d1f3b3fa68dc310c3a55b7fc7b0bb2f618167b8f58edf73897b506c6c97c1aaee06a1 d233dda85bbb2771818feef19adbf7674653da27142f4b7350f890f36be1d04ee703acbfd04ab9bc a4c9fd7685b4ba9dd53b477e7a70ebec1bb93661a75ad9ca8ff8b21bb6c243d65ae5c77dc5685ef7 577074ca57702b3d389c2e1e95cb073dc95d5a3f8b4d667be1a2e6e3caa471e536a634e24eb5f1ed 032b5ef2c7cb60f5a7f536b157e5b9fab1d59fcff12f31bdce62b9bea914d743d0a2e512299fbfff 963d2be5fa4db91adf74193fb1b709d86364b9bb5e2f64071011d460c6a49cbd3e73498cfde7e10c 8f18cdd94a0d495f99464421d8485d17573fc129295dd756e25932b76a73305ac9d51956e9b92eb7 54165b6629efc5fafac19f7b0f1be42d5a7c3bcbe6f264dd407380b62009231e45d308819d7e227d fffb1dc5c7783a53f69a9b58990ce755108db09cf8d16125f6ea8b8a5679e5526b95ba1bbf591293 6bdf0cc1dbb86a8f01642f559f012ed647c59a49ae2c4a72e24ab5479fa8542794f950d677f7e45e cb9f748c549448ec3d0bedc57c35aaf9cbf4b5175d62f50b04466cdf636729544a1258d93547af2b 0798d7f3752fe5d21140972363a95d93bd273c4174ba1f81333c29b2ea58c4f0140dba6e1a85e543 a529867a5211b4b1da7747fb4b412275c0ea3547f1da7436d0f1ef60a9e6bd6e25b1e0acf195e608 a7791d4a8ff7b93e47ee9db2d82595935103052874410514b307018c62567ebb9532799582fe3c44 77c42635aaa54e5188604dca9ca28a6fb04e0f17aee347d50f38f057b89bbb3b2110d987351490fa ea1012b7172bd5fde3bc5745ed9eed34c62f190391cbb55f72cf47ba1a044274014a69ff7aec6f44 aed6132b21c0e8a1dae4e678e33111a3ec658e04bf14fa9939ce254d667f59279a8ff68071ca6857 2b377bcc4e885a1d49b94843951fbb6bef39013e92ad2065e553ed00bc4c4bae941d9e9632bfdc14 a25649cb32e10d4ce5f0fc2867e6328c347a8e262a32aeeaf35a4dc154f9f5fc9980b268159b5adc 1b2d95c773c3297232d90d79b56d280eb654157153839bf68a1615f54409cadda9d9db7acbc84d72 3bab284a07a9bbdb6fecd58fbc7d52ba4bbecb05ec62ada58f8fa8a0274eef6e6bd0efd0af3a9f77 0b8a7cb55727e05939b8afe97ba3d0b6f999e54590b76d6e7350caf33c57978fc9d874f98c901f37 6946df6f0f4ddff2f048a9e11ba0992ac056fd54105ce9d899bebe0d7adb003e176bf225eaff925b 276bb70e6ed78216f2329854b8a21daface0e28de4edf6c074dd7b59368a4f6aa9e0687439967b38 9ed8053693737ae07d77ed5c0b9393bf93dd6b59506bd5a0ef7519f525db274aa3bdf949b58e22f4 0bd272c69cebcd2cb37cbd75ebd4e4e3987cac417b369bbf6b524dd653d17590bd60476e73749579 74d899ba9708f7b857d990f5b96e76d5dbdcb4aa08647f47ad83879e42a8dbfa4f7acbe4db644c57 f19fa5a5f33bd994876ede53c22ef68916ddcb4b1e2f9203cdf59eb91bb4462d39d830cbe699408e 463d2c1af2ac7047564ead79df06d3754da6dfc5b6bdf8e6eee9f2caaf6562f8694e69aed60a2f0a de939977f9b7365d6efdf4ec59c326e4db0504f65e202352e9dc850df6704122942b80521d6b6df4 21bcb5e5ed251c48c381c76b52ff6c5372173b4bebeeb9a6eecd2843e4ebad24bbe41836647b292c b6bb85a7e296f8ab44eb136ba45aca91359599c9dada8fb5cc76aec05699305a533d20c766a7d7dc 9b0a61aa86aa546f56d67a4c2cb3d7ffdc74b18fd921d46d5cf4cdde3b6b664c8ea4dba0116ba98e 2fd4d3fdfa1cbc87e92fdea8d9f9be5219aaa32389efb5d59a76a575ef108b6958394af6b28ea95a a4eb6f53ab65a0b961505bbdeb5fa8ed228b8dd1b08b943ab19ee34e7dddc135a3b1b754d833a91d f60c4b817c354fdae175f9a9740590f7915caf95b7b5b262a952cbc7081bee611595afb6fd816f3d 46c68ebef06af9f88c9083d1386bc3f5f9ac371abbe77c5be5c1a8fdc4220df7e66d632739ae7729 a31755bf8955065e766af666a6fef80f6ae5774b4be1f88a0cfbf8be2897ddd9def24657486dff91 69ca395a7f3dbc88c6c3d3c753e7c7dd4c5c94ee655fbc6484da787fe001070c0eb6e129b6b2db9d eac813cddf3a5d785fb553afd698e3b3c2eff0e2c2405a35f54519ae31ac8be738fb64bada5f99c0 e4545145bf551f8c949c38f6f0e78d96ac1e0bc9ca725c3d6c4e1aebed44ddac6914552afa402b0a 17c711bf53f57ee1284ec5be184b8795a7bc0dee571c1a103e5370cf60f9a4c48a1e358b0b756a14 27c2c629be597726cdb78e38e7b7a145f5544347cab7b9780981c0d8df76c2387f379e5f93a3b4df 2d96e7f61a02bbd223ed7535c36d122b29c90db5d6d27ea9ba15696dc9656574a088313135912e52 151a2dfca3c0a78aae1f69b2d0afe0d05329b9f9535b286b068ea35ae90b74c7aa0dbbc76e2c15af 757d4be098a9692d4ba3ee939e1627a06e60b0a78fbb8cf9534d510fddc2592f17f672db97872b15 090e63adbe27c9a5028faff64329e0e653000c4790b9be79f7a9c4d0b978cc736d0e30861eb7d787 b3a54ca688a469571f6f6814b3f0e10b2fabaa66e4bc25d5959f2c43e49d4bbafbb8b640e3f57921 aa65c77d5b4b21444fac6379b40df78e39da9f48cd43c6fbb6f9dd867a6be1b4d40ddba117dba6b5 8a62b91899ea48e1ecf50abcfb4c7a800db85ccdb91b760a1d49beff5ca7f612576f52197f1aa6f3 b1765a614f0b48c54204cde267a4d500defba82c753ef1b38061463813a62af0aa6c037c8b2b3a8b 8aaf91029f5f4e18f4bff3e54df66dbd327b1bb7d14657cd477db5e850ca8fa1b644d6c38a490cdb 159bde7bc6ccf72249bfd08711e77429261856e3bbb6021f3d7274a076f63d21447577ecde11f893 aebe763bdd9b6f86f0c370ae66cb6020e386f4ea12caea052333df7d223a2e82bf046a44f20dcd37 6745556bceab585b6e37da56e7c1ce546e200e935373b113a5dd47759f870832dd72c311add989b0 33223a4efb6b782c5ea657dc3c296ade6beca2abc4a764cf001bf745231c0f7179e2ec7fec412fbe 88be3ff974736584764b0e57ea20325c4736f293375a96c26d76f4ad48dee47d7b0b1973f8e061cd 4488e40baa0df4fa103bcecec035d72faf33e9c20ad537ced822d166c5f0604317e9ca2647faf367 69e7c3dea4c2bc4beca17b4fc59a91adfb0e796e3cfb92a2a48dcfc27dddb98ea72f0705ab71ab26 f6fe1d54258ed86c0c65a9b2e6f35dbbd20a61907a33a53a46a7933818fce94dd46be3e069b16cff d81cb31e65a01ee57217c73172c94005606a9f18d0b502a0fb64b1e6c737d7fe90308ed5a747847b d6d5b3c606d13b35f202a3c0b9abee2b28e58a2d68ee15d08ae31fdb03dc660ef64ebc1bb58e5b08 f01f28340dfe55a6db653eb386f3ce4c3f16bd0186f8515f0fc760a44d7c2c8b37b72a3b135702e1 10930da72ff3cd30c88baa600d5eeb0d73ae7e0eaeec2e2e86098320a1e535db8226f9527b955b65 988ab13f8a7f546c965d5c4878d7274ee7a473f578bb5978daa267346b217a0e65931ace5e7411ed 3c5cf40c90ba9f810f0c997ee148890a62ada5ba763c317bc315f6fad41c50da52bac85a4832d5fc 9b059f4f7fa70ac3158a045b62f14def13b9484c8b56c929087744cb8ab3089ec6f245c7adb21091 133cb75b307811a716abf862e599f1e76c1889ce6d58738ebbef2c38d7332155826dd96297da1e5d 8e5b756945b67edc14a3512580607a2faacaf40b760366536ba9d4670dc8c7306ebaeb0041c79f59 5991a3e1eb60d3bf7aeb2bddc67274422ac633c39846a9583acaf790cf43157cdd2db77507b48c17 6fdefc9314f8d186fb55a0d453266e1c82203e03fcd8f1d54e0f3a9967511ca26bc5d0944579dad5 1db272f7f329145bfc5333fd6d43509559771a1a8b49a6b8d8fe511b9bc5624f67c8cf37868770bb b317dc48c38c3a6dc852fc03ec0d2aaf4fd46fa491833bdce4adea40ec39eda93bf1baeca7c49d8b 23d96acc08dcde0063afffd95e4a0659f11d93af7ce73997c48e2655f4a78e0da8a22fd7df4940f5 d8b33ffcfa0985c4764f578a2abf9f71b6393618f70a95c8baaf3692c5cc7cadb3411792215592ee 774a935a435f3dc65a813d9402832c76ccd5d5a984c3f735762f94a1726e81b53d880c259bb78fbd fe6160cb76c9181f4d708ae2e8a52afdb890fa99a373ed41edf73ef2f908c96a7d65fcbab9749432 791b85bb7db9e018b7153106ac86e7d52f8d83d5dc7dc3fa5c7b86d630516bfae558171b31e37f13 d04a0908d8778d36d558a19367ee9cdce560fc2b7471ec261884c1a276b57bfb2ad44fc1c6c6a554 db341e7cbd859e979da9713bbf1d6d23244da77444125110c449bc439dab98f6f3a6c8f9e03b50af 9a38a476f79bb8547665776296c7788b9d9da5418bfe5507adfe675d8440507564b44f8509d8d317 da9a1d17e52bdaf6c3f97d3c674703762c9b0a1b7b9eb9ab90e517519655ce436d93eccf9b42a34d cbc1a3f736b4ee20b5dd723cd4abe7831897cca82ed2dceb47b9549f2b6aefe7eefb45bf2df9acf6 247bebec60bf8ad720c9535699179bcb16d98454a4902bccc40d665cf86a6bfd1b74338bccb31842 9f0dc0dcdff7826e3f68d5abf0a4da3b2c0550136291b28be9e8fc276a541cc72a935c5da368c143 9bd66663c71fdcda91f20c19e1e9e7ba2597de0d9fbe37387ad5f55f46e4ea9cd33fdff6780d5700 ed532a6d4d52d8dca1ad8338eab8afc7f1f3d515b44a3755dccbe26f7b06f3cdadcca4185fb3a75c e9e2fa15edd9db74becb0856f2b1e5aa3f42b0447c5d15850df5184c2b368208eff00057d4a8e18c 3fc296a65c2f3c4217effd88103a54b64b7bf158eced07b23771081cff488ac6a177ef7dd380c915 fa948a6bad2694e771acbb907aedafe568d01aa90127b633c6594ab8b7c138d51d966f512f0d8b63 7bf48589d6503b0e9beb9314ebb787fbd105a8a55ad70f20cc39ea75085ffbe4e7d3847017a551db 5dc3de6e1c3768839abdbd5e6bb2b5d7c6c8c5abf9efc618ebfd34b61abe6a80c496d0be9934b738 548fbe74d8909928e05f187f5076d5f130674851bf305958eebfaadddba2c4896f73f43be5e17614 c04750abba9642aa3e33a5d2faa42b8f7145818769341f0fb80e5715aa8a6c7c034964525167a036 7a7799f1d8d4cb82fa1b9b3e6a8a2ca8f3835d3028079c93902727f075bc28b6a5a59414815f8f40 43c3323fab2de86c0c9e6bb23a2ce94a30411b95eeae721e29596d6d383e167fb1f1395b292c9a11 c6fe5edee9632d854d26f7b864be6f3ea72b75b7d4f33b0c86b750990fe8d640d3a26c33f7dabefb ab0e62d2e026a9e1f6c85afc4037a19aa7d494a554e752664f9192c6509975dc5ee4d8a0cab5d163 e6cdcce2f582fa59c586badb8587eaec195c3bb5e73546ac63e1a59eac67d598b05e5befd366e68d a4ca8f3a9674e6803e8de1936de7cdd72914e53b361820d7dcf40573e52ef7720fb30e3aaeab0b53 b04a18fb8486b74e55358640232d2aec5112916cedd73ecf286e2ed478743391b7d3abdd5d5f28f4 85eed789fdb24ed37d825fe818158e48da413b7a710ca506b09dcf751ce65ad1765eedcfeea3b23d 8dd8a1eb0f1a221722c33b35a0e80877a0e69977a52ed0c5c25136325e188b99ebc2ce80b4f68cd1 6aaf576d9e2d573fd729b1b08446c1e6358f1e3a148dae89157bb78b23f858de9c7573bb5eb6a9d6 58b0b5fe9b43b660f9a567c396a09f2f785926abf7b1780c6fcfe5dd69cd276e34b745bb9e048950 1b3abd2799ab6286547f17bb05f762fe42d76ca3280dd876e40c16b77efd52a2e67261a88cd78976 18f240997ec93b4c4dd34ea507d2dd19f696a7d49a8db247d8c2979799204fa8f3d523c94a1146f7 ef812c9375c39c653d5f9650f5173db5309ad9cb6aa1634e9cac12aa0658a16711a3abbd6b076828 37879a06788581d15379a32ae2e3c2db3e31c4c109f7dd2b10252c5777baa9f365c95c1a1dc391e6 2ab15ad2e8bca96b37476846505ff8ad0d9e950ab17ac33eb0ebcfa60f58c4d88af2f9cebb09f62d 4b36f96edd9e4410bc9856c87ce276882fdede9cd359b188557aefa0b3d2371747f45d3c105bd5d9 72a7512235b06b95e30a4ccf88a1168aaf5f7b75c5f6b388073bd4d0e9772adf58b4a42634f92178 0b58b75ea125cc96f8bbd06e1bd55da9e136286d0327e3b4ac35576edf984bf84db6615af4ed1657 4b3fb55369321fe08e93f6e5426c45ab3509c995bfeb4fb082a1f968afa8b73af56ea0afd3d2c24a dbfa1e7c4eab33d54f8cdeaabea35f3c825e367e722c7e713d9c60347bdd0d9d60c4016167ae879d faa82c9aab97d371f68a11c007b2b3d38bdbd1d31076f75ff49415ff738bcbad9e99aeced237ab44 6b4f7ff85a5831c5dd79b23de9fa4e399ab8de63893f5a54e31bfef7961358ca511843f519dad44a 17f82e2e86a19b7db3edb5d81c0df7e2c3d92241f138cf45d39ffdb0a7d41d9c9e427f4714a4e2a7 c24989d6fd10ce4ea725bfd57364930da7975c85dacabb919a22dc3fbfd2567e2d291215f445bd97 ac7427abdf15f89df15299e922a3da3941e567bb084b6b57cc9ae7adf07740eea1f375819e9dfb3b ea6d2406a13caec2ce6d41dece6a5cf4ea26bf8ba3623ee1b97a6468b503a64955cc2709e259d4d5 d6f4769451e01bf399ee00706e418f114059fc24cf47cfb2b68fbc2dc47bf2f09743e7b396f14e9f b9c8e00d6968471f5a270f6f48a2b7c3295a20e745d598502e2fb6196c5b6761cb1ff70bb050d93a 969bbb4bde098561282c65ecc46d0f83ad85b88354f48ef88500a6c589ee178f3f5906e9d24c8f27 acd54d669bf5ebc1cb8d333ffb985e3504ef812bc0d706a2bed84ecbdd38ed4878e31378887f5da6 25434b45aac4de0405ae55433b29a8c43db9b3b741b3589ab3854a9f777babaa0b66cb9f7a7784b8 e6429818ed6fbe3606390f301e8e38749162f77e1ebecdf5fc4948c9a15c3e0ed97763a3ce498a07 a36d7d36209fe3b47f47737e7fdd5f55e135dd446c5fa0854500b3f42b9127ee7863c8e249dbfcd0 1adae83d1cfd749c61dc2c5b8cd656506d6ff169f4e2877e35709ca5ba4a056f8d08cd4779357d27 9c1446abd6536c494baf9b05866b2b27f928d90ab13d94248e17c45efe50c4ce4089d9cf8d136eed caaf27ada0464428db3a2c7ef3baf659a9aced09ddd21f94544521f90bc6cb109245c849fa704822 5f8efde7aa89f347056074549edd27846d0467595e39cd91ec57c5685aea6d35390448574e276fb4 b32dcd7f4206f2ec476841df9de63eb225caa8ae9aa9dc25765244341e3dedbc1c54e416dc6fcbd9 cdc1d4cf53c5e4fed24a296f6d56d4ce4ec8e540d0bac8a7b70b14b768e8d2014fd3c5b9f23e5a39 425bd24e29ffddafa00fec810968eef06b0ccf263b1e4513ddbcee6f32cae4f78e5ddf405a0d781d e4fd66f2c9e42be5fbd638984af2ed9286f8e251751e203896747c5f906b0c9058ced3a94b9baeda a0886e4f30c618fdab0bc8c4311f35a1caada0bebd712eb53f5d237d0888169aeedb91baab3430b7 8dd0f526b7f950e22a478bb94483aa3d26ea272913a24b479c6b5d83c0938e6cf31372e78e5a97f9 0d701a52b9f59c077a5ff91d58884e218f48302211923dd143bff6cdd12441d59654693ff6ed2e77 f8c8a589f46c6283e4ac030daf209e42b79ed215465b001b6c24de1b95ba491c886b2cd19da784f0 c398b13bf5ccbba6674a72b1d38f32d26172f76001052f97a716c96f5fddc162fb36d550dc2af76b 507591e742f790eaf7dbaaac84d9df2f69af5bbe44b6a7ad41aee8ac9b686b483a2f1f7cb37b59a0 7abeba5d74837a38c9713af2046a71fb95edb4f83aac187dbb5d12b2375f535f1e9f4cd20dff16c5 acff05498f2e8f0f388f93faef12f2cd745ffaa6b844bbf217192f351931979e77ee9bf244ea51ea eee9bb22c0d7db32d9203eea20f1eefd54fb1b8d3cd2aa13b5207402f8e5cbbe3c03d9b336304872 963ddcb696424f560d3aada93e81a150dd9f3c4445cabbf3a46482b9da7cd443657fdff470d0fac2 507f8f16959dd34c56d7fb85b132d30c54f3f1a745eeba27ac6e42637da916177e24d00860ebb3ab 7352661db7dca7a312af31f44d528c79364790a0b255413a38aa3a0a30715ebd775d689b9f943797 439a7d5558ebf23518c50a0c616cc42fc13896eddedf45a21b38c60b0c81683b5a3a28d2e393ad4a bd99196693c4530b9b63fc05fdc3beb7eb2a27453fd5517e2badba4e6116980aa1f4e7fdfac4484c fc51e615dc035608f9d0548dd8e78a5acb81538c6ebb3f44108f1d6bab58473dd02a0275f2777bd9 50f0969c8c7178463b9438fb2840cf28b79fb8441a91331928446f812dc79b13b7b0ee7c459911e4 c661e0d16dd659be174a971e9579ae5da0830ad63b2825baff0b6be4035e41762ab4c74a497f4c91 093688748839e1ca6977d0a2150b96963d09119591fa38a83326d567e385f17df31b49476fb328fa e35802e4ebfe64b72f607564d5e33eab4046c9484739eaf2db79ff176fccade031d619037481f4d3 862179c695a393ba2e9c17ed864edf8d5def449f7231ce8788c64894dc78e0f14bdab16cc93ca1d3 49b0bedc65b9d2bf4c0cab7fed281d6b71956ef7d0d3a573781f2e69efef087351199c34bd50f5b0 687afdc8e45244d56c0e7fe68c201c356d373e1b0b377d5840a571524376ded0dd4f6f34f5bdd652 2d7d3c458bab1dab27d29ea658bdcf37554fad098c1dbcbbd25d576ec6edfefa010f7f7fdca666a3 4820fa864552599a85a99e8e3d5fdbe641302436715d332e1352fde06501736b7449e5694554d9c9 394decfbabec763cb763949ec7b63948a6a15d87fa57edfd0c85697d0c774cb47cf9f105b44616b4 7a9d75f7a38f80fb521553c78559941554a967cbc6e87682fde69452fc3a3ea2f49e4a21d2bd92e0 8e50de2cb421654ee9477752f9356401d5b9133e664d355da7d22c50c12fb499dd4fdc8f46fe45bd 7d55f76f8fa691d17d26182c0857f3f97b914dcf1ddbad6be7a7ba0b29ac579fe34b739f7b7bb525 8e2bb0de5b081a1bc325a37a78b4bcc564e9ce567dd8d5de473d954c71810407e658d56a43e6c74c a1e76ccb771ee8b4a78edee40a331be1c4d87ff2a572f38174268ccedbe5b21a5e746e85b40c1971 b9041e4fee1a5f544456d83ffa81b3bfae54ff76748917a05ced63da52d532142d60af3dd8eb959c ffe98fbabac5068eb5b035fea4cd5eb6337e1dc54a1dec0acc58e6cd437931a06458c4456844d58c 4ec4975b44e1964980f154b593e915c32775b1a51d507cda4ffd6a7c01f0869502407a5a1d13de4c 9e4ffca76521cdc0cbc898414cd4f50fabb674bb4ab28e1bfaa041a0c454e60af1c9112a9ba57dc7 e8a26ad684be150da058e01232522e083430eba73e46154d3856c4e755d5c33468b7b0e9beab34a3 32a00d0f8d1f280c5b6f4434c1c76a63b704265373ecbcd757e5a4641aabd89f88c876a379a73437 be43197439b189abfe50df6a8b7bd36fe8d074ae2414b3b6d3a6f0b189b6ecda0476395a156170e3 cfc9b26432fbe47772cdd046d7ea6009e8353ded160e7a9392dead4e5e76b4aa3dbf7fe3c2a51584 43f4e40f08bd628523b1a70eafa2e2acbce46c56615b9c40251eb4d8fea2ae7fed9fed5237716ad4 8033a84d6a37fd8b4f6f7f2a5dc9b5cadb54741d5af9a50c04313cbb9a71b89ef13a2af5dc404b5d 035d0f2f03bd1bbfac38f25d2d9f4d5bdf05da7c741fb2deeafce9df02ac7305935a7975b6c6313d 554b0ef50c1925958ced73f07336e3c33ea1dcacb1ebea2c003dbb9d12d6b31aa785a4c19b1edac8 89d952e3ec2f464392ae60190aea266e30b34c8585019e887430e48bdccb2878e17e303c4b91dbbc 7d7d745cbd05ad4e3fe8189bbcf0eb11a8663f2cb81c56d9f24e2b05fc91dfea48574b7d0bc57871 775eafbb39228fefb710c836632d6e150b3f046920cae807355ce5e641833d44489e738c824a011d 1915ab3d94f860b77183363f9e32a682fd21694e9dd86273772477f9f323791d746a0ec545d63c64 c5865c588c973a15de56cea310b8aa6a919cc78cba5549244a43655b9bf49d66799e0e8f440828c3 54fcfa3ebbf2c043b85a54aa576f6e94b4f8a70a09c543b227dff54feee3a0dad685b5b8d377d36c eddc6138676fedd9500b4fc5959581a94c9acaf2a92e9bd3b9b1115a78f30e4277e5826f682d870d f98b68f3a53dce04d5dd36ce2df175d078d3544abfd1d88efc590f2d162ceb4f6d0b99064e7771b7 2f5c356902c3ba75212ed09d3b9dd4623d687971e5f1d401a3c2b945b2df76f8003eb16e7b71b596 47ff69d1169e929c3a650d0f6f2ac6a8279268e9eb087fae5336e4aea6d026ec805ddf086eb7c1d3 9d74073d3119df60afc8c39a8de59b6038ba8881dd8b378089921080db3d403474b400e84458ea36 40c4bda9995798796cb1bbd0f20aef858bc374e03477d2cf43b3e36ddef5d8fe88b200e90e91a4c0 6cec9a6921463d9d34d1ead41ce97399296abd35c5d85bebde4a4e48d670db728c8a32fd08c2fbbc 40dbe555960fd1fabbefd5f644cd2cac971d5c29765e56e31dffaa1c7aadf7c41b6d7e496be386e2 c45bee72fd46e0d6875f1b57235801f3eb6458da9a4227e2ceee69775e1191cd74854f996dd995e4 d046cccb20121febd030a8deed64ac5e0b551a525c1cce8110e021b54ffd2cada51c15ef28691445 0f454fa2767dc8014bc81e7b84d25a8a50c83169f4b203f3c9a829a377468eb62e3996b78e7d5356 8345e21f973d7d529e2e2005df355db741b5444208b94c7ee60fcc620fab1f8c4266ddfa4bce3ae9 4e4fcef4d598b6ae33fd124c2fc1cb58d1dca20d7fb4f908df79c89e03a93ad36ca85bde1cdbe2cd ef61f7cbada216af286cac7bc30a84ee3349a16d601c413e349605117b98f31cf895edfcde75f1cd 86e852c380f578e11849cc13938df4d056599499a7b49623b97d3ea9dce593e804e7d306d22629b7 75dc760329357d4eb16b13eb53ee96dca4a117072f72251a9f33f8b10b902e639faaffe340e9d583 e41b63e87885b8efc7d5f6f5bd0b67195994c9a4ec78fdb5ec7867ad32187f710d6c27c732e834ed 1425465456335b5a0c9aeafbb6465ee5cd5b9b1b45457b8d6abefe5a7c66e13b9abc83e6e5fa1335 e2c6b37ce26db015e132daf539388241642f13a065a574416b1786fcc3482ec1ca00bbf237a1a39f 967aa8a25ea85fb1508697f54d84150b1b2fe5ebc058faa2062f3ecd17f69b684a44d7fa3076c303 7ea7224c4a8050e44376bf101ed75ada669d858aaabb033e1ce0a504db25199330c9857f5cdb7918 b36146e6d31e2298f4b0eba1d8bad4a2be204064e10a6385c56b06161d6c24098a5d9a8b8f372db4 f8f22f484b00d2e4a2db6e6c0edb01484a80c8c4bec6d8ed0eb7981fa4d1396a3855c06cc14a8bf0 a55d78430cdb5eab8a72a91e94ddd8f8cc88526c33ee5cb51498e62ec1de910ae47c7a31e59c2187 2e3f9afe0cba354ce49a9c80e4d17ca1eb0728da83a11cd9e752124ef809ff468f8ab6796ddd08c5 66ed61b556dea9bb34dc7a3bd13874388c1cabb5cdc8b159a798c029732f2ac27a8f1a45abaa2a13 ea039a17cdfb31ba6297a75346a830b041f6e2f09bb39d0032a6367dedc11299933796a79664fb77 75f1d00193b7276570336bdf14ffc8e2496d36caf8f3fcb0b05bdc0b0ca30ddda13f549337bbefc2 c723b387dc91bab71f22d065146d58772b49e1ab6aaed4cd6655d7fd64bc54faca74ec2903771f63 ad5ac230d6f86d3ffd99e7ef06e32919b2a2684645d070846a75d6b2aee0506776d7aa591f4201f8 491b2f75888c86b365e4fcbab8fcf1e2c97e897e6ec3115882e8a7d1c79dd6d6a6dd4fbb33ebc86d 4d370560475861c97190b2924db575f17ed427167751b0ac6f4663ff98479fd9f1c60cd39de77dcc 2fe8b3b9464eba4296db59c7f8614fa7bd14d896f375e086aff449e35c873a50a3e4316a569d3457 43e2adb354a837f9f3e6be9d635c660cba95e25ae8e9eb4154191c8aedb17796858fc560ee4616dd c6a0c16dc473d8404d953e9b8299313f6723a1349da7a9e90d478cb66b884ffd7d8bc3abde21c891 73961a97bee52b17b7d15c37a4ada412d4d3c652ef5a5f1075517a3f89dd925ac021abf2e74069fb 4b29e93885fa60e0381b793dd5a5109d4d7ee2fa6dc1642cd95d7eedf6b355c60d774959b2c14a84 b1235a17c14b4d5d2f02566b7e963ff18819587df5e2038d68dfabdd8991e1d55418911fde72588b 9a2fedf6568cc6fd6019bd4905846d7ea19465ef17081676435db0463bfc22a86c3b9b6927b43918 bbb9a383bd781f48f347d0f638f5a0be2afcc0191becab713e76aecaadfb9c18fd197014663b8db7 054c53e6fd0ee48f24b8e49b22110da2eff6cd096946fcbc80be9951a8d7527a11dacce88ad646eb 1b0b98111f90db098692c36277514f9025ebcf8cb9d32c318519f0e18281108a5df3f13e4f82f276 596f2f07b3a27e21fb2ffbb952cf70cd1b10ea936509fda5cabf02b1b0a032d347478b46f27c2ee7 23fd92b51c76bf50c35b78f70903ca19f31af7be112ddb4428e9efdabab49c56cc4d76e640af176f d5c9663c494ff2d666a38d5df4eb7e768e8fba741bc85c8f77d0fdfc77a5939f21f74e7be31d3ae6 f0d806ed45d158c2681afdaef2a98df575e18e1dbbead5e0f6fcb32e18593e99f12453e02385c3c4 12b160542f5f4082b8a9d8a2bf7bafe9cdaee488d4727b94c4b8d26d420ef62b7449934febc1f393 405fa3ebd15cdae2242c00b27c74c4105f4a1bab531156fa6b355d6297ab74f6fc54f40a45a48b59 4059ae7d1c583aba4994af16f3a9b6c5473dbede9ddf62bc76e0d433b1aaf34721ff15ba94c01568 b53d16246179d98cbef1b80d29816d36c5b3bd3d3489104e1530256d0e6ad4b3e5491471730ff375 eebd4adb365fae5c8dde6a23f217fc2a4dabb66f6b0fadc9081f743cea7cf4ea54759fecafbd2ac9 4ec4ed858231f6ac76ffc0c13bc488e000c69cc6247cf1007ebec8a756d1365f0aae0af0653aa476 0d7ea79f21f825ca507bde1c2befb9ea99e7ed94da51bd4596bdb860f971cbdcc0921f96ab747efc 3477e96e497e3877645637164debb40cab8233ea573af93dfe7c8da70d49edd7375e0b3d6a3c07cf 323eb5cb4e27a41e1723ead68a0ce716b2bddc03ea45cf59ec547ea397534aad6c41ebe467b98877 9e3fd7d9e4a51bad65e35e8b3dbe98eea2464a6c2a04556e7ab19e84f91ebe81b8500f4dbe84e602 db000e6bcfd46ea030aa35e1ce82ee97cdac04385205ae87bba5b75c6ebbc3113fed2eb052b06266 a345f57afd7107394acfaed20ef1e8e8dc1b63bc95cd1a5467f97ebb425ad88800ae6a4d9912443d 169db50c49d1220532c1e6cefc47966bc92d36fb1a32e417a57bed27e9d064a4c4280a3e5799caa3 c215c437c53fdd5e31323e25f951c5b22d2da69c846bafa9346c37717f86a4a1d4ebdcce92947f13 ef9ba89ad2946467d22a888f03ba790ca5a49ac83241170e08d1f40a32c25c19d1491520b905f84a 7511682ceee3e077a3b431006b9cf2ccbcbdd4c8aecc64e99080a2d4f733c90f9d0a6e54271d05d4 0fdfaf03addba66c2c7233ba3ee7bf39673c2720d74667839545dded25225ce3c67aa1e0dfa412b5 63fb97f332570fefebafc32ead56150f99eebdad9284eb5cf0d7efc76cbf755b4ec7aa096261f30a 74f1b6e7acb6e6fe44a44365827fc39141493c2b41741bc3a72804692cf9e065d83b94d785544083 6bf8f808da6dc4baeb81f73b58ea410b89151ed1f22e18f3f7da96c263fc4de7a447dfafdda7c67e 2bd01257217c44a7ce6df5b5a204a14a47bd191095cb5142ce5441b9a015ed10409cdf27e7b2583e 93d1380f51d3be3d8d82b8c3ff4eb1e3d4ad971913699a4b37ac4eaf882f9a488f87392780ca3740 6069a9112f6fc381e06ce5b500daf4da8f5bd041ec3d6ca73f4d261d5b00d6b0d4a6d31ee2fae596 76e352815f4ddb5c6c933773b11ec5bf369180b781b636758de7ac4889b09059b7786cc0dd8b0f4a bbb3e8abd506dee93435f3a6f5f6d2fa5e61176bab1c70a0a243eae2eb5d9d0ad421f9024c1cd4f6 f6b9e40968dde5cf1700530e143620b574f43bfa2fdc1bcd449999980877a6b82375e4ada2b6a58f f7cd4ded99b8102f9892e10354c5167b45bc14c49ba27a237d241d37a0d41eb440655caae46d68b6 2a4ac904c415ad7ba9a4f3eae5a3b69545acb0afc72f1db41bc16aa404cc6eaa407a0270020b3a4a 7b6a2b4ab1abdc48803db3f263f8e295fadddac0e2036aca3b617b54caaeb50d6e137f61d45b482e 67800229130d7d6b375475e518129ce113a912eaf512fe82b4bc10ce48bbe8cd10e515b419a5ce92 bb79bc7e1cbe704e5bc856c25c2d02e9e7d6a05d89e5c9cca94f4ff5d1ca3090ee47666f67a6b7f5 2a47eda483801ced5a6f3868c88cdab0a2b33c6cde7b015cb07e41da1b50a022b7b6822daf71f162 e7f39f2e4ab41d0c9d693034f961cd9307992561cf3933d03ec39227efda0b24b9ec51273a4e484f aef88797990cad92bf6406b9f410b8cd5418fa8013ea9b482e0fa95f58eb79670231d5c174243305 df87978b3daa3595d3547a5863c30f0ef671d66a0e69691f4c46329d0f9601779ccca4bc49958783 47a5ebf8645896ab97d711bb7983bbe193d0467637df049396c1f5126ac4bfd6b7b47130d0a48a60 6db67cd194347757f4b4216cc080bc6c05e9005dea3d116f15ec483f14657cb8e8c2599673da8599 5d8dcb0c79b8d7097fe3c6e518d6735569498db451e3b18137d22e63e9400f2ec0af472060e7f24b ab67fb596b653f7971eadfeeaa5bf2eef1e4787b8b9da73bd243afa3e989c81d456eb146b42dbb1c 31871414c434291a5a954dba04a1d76c095b172aaaccf5a986fa304ed21d2825ba9e147f8a362e04 2f7385baeb6b6d4e90923899d9bc7c3bcdeaea873adea88533a0e4398bded5e93b435b41f1bc917d 816fab804ebe23017ebdf553de5e68d172c06987e5aeabed6cf1a85e5136647af586a81e97fd9f2a a43a2a5644a2003569b50a7a23e539bfe88d15783c2a83e8b4d0c2d16ee70c826e649177e9a6de9f 8e21acefadb311b1d3a23a985c8f146315381dde3e19e51c54ab2dcfbf4dd4882ec34a3200907096 a03f4697d717d7b9563a765a5a6fdd239d0a95b7d4d11a7e4c8e3ebe320fc3f8a41cf7e0b59b776e 2b3d560b73c5e974c2c6ad5e17540e64ee1aa2af57f656b875fd1709ce551962736178ecbcddefde d1951b36fca9a650f0b1f37dd4b1e72a468a842dafae9bfa88147b0acfc5ab108a07afd9509d7a6a 5c621eea6555d4825bb5f07f38baaa4555a120fa2db6202022a888747783822d76c7f9ffabf7f13c 1ca9d96bd69a34ad2aa32c79ef8620911aa7b61901dcb937eff6fadee578104c76a669706580b836 51abfcbca77537c059a096b8e914cedfa4d5abc71bb5faa896c7e0f62b216783fd887af6dc6bf824 69d2e4e0dab01374a3c44d04f0630ed6533bbec47fb9b4a1ce93a0fa2a55cdd63b01bfb25e6e7bf6 2348b9be26fc3af215d7addf5c684a0f087b318ad54c33ebf6e5400d5a9e3dbde84de25d0a0097ce 3d7a58ccb5f262dbf1c60fa0274bf632d406fc0b739983450d7dcb81b4c5f2fc7220a5da44491b7e ea82b26cd84d05fa95c1c583566d62564acec23bf6bba2e1122e6f8c2d7df6bdbc41b033f44d18ca dfb4edb0ceb1497483fbc810767bc426bf7fb4ce87656a58d527e403d1dc743dbff5b65393e3ddc2 faf3a543f7a358cb07f8536b4ef07a2c87043e73adeaa289db7635d250aaf5d565522f0dad88ad09 5180dd591f832dca6b0e563bfdd90a9eee147504e7605b2396396f4367a0f3177b55aa3a0404de54 eb7dab9cad1d590f21509ffd649499f7c82f3daa5d2f6eedbce642a8e6786ee7592e4b8ae98b7ed3 e612a77a2a8c61b55897dd16cab276fde0fea1ece074b0e1833eb1fa2bec13d5d04a392d85e6db4d 70f5a00768458cc4de78e44817e6c7d3d8fafbcef83b7147d93cd06d11d537c4ba983c2e59e62ce9 40fd997ab1027ab2f480cbe1e5ccc33f600c6fe995734e84a7041f3bfbe8af831ced95fc9a91a7ae b8f10bee9e5a5bf94ea3dce7bd72cc4968fc6fbe18d63e61ca3ffbd341ad92b8e27af1d6a949e331 0eab32e474c906ca1cd66d2a3a20afb7fd15946bfc09125fae5bdd8ad688303188abbc30bbb3004b f1d112206bf8265119de2cd070f5285a023f437f8915f9ed5d227f43c39d81722cafd4b232bfba67 fc50433ecbcb4a1bb049c5814acbad0fc4a6a88efb4d24828ea8a176090a572fb6c40744fd8fa751 fd7ed6c8f1abee8961a9d9b588cd40bb769a1f27e9ff2f4b6895814faecf8c7914c5f36064de93f9 edebae2ad360fac9d5afc15e1a467be1c3de8d77b4410bf8da21cc7df5603b1fbbed8a31448cde6a 50b1edec7cf69c7a17b48d2fb0847d5d2b94dc0e242b5d0dfe2f15972b537c749488b1f90a26923b 839f49573aef4d53826f92f3a5f2626bb4f96af1977aed86c72fdb33c54a93769323360c2af0b9c9 87bac23b23875d787d90dcf73fb0a7d8d0c6ef3949bdb46f431fe7e7a4ad729f7fdbcd9543784d01 2d87c0aecb07939c2b298c809d7d6c2d7ebc73a7f9dbb0d458ba8287ccdd4e2c325df9695076aa09 237b0c37bc964b6c55f3c16d6f217b2ffd7da1602544d6b819f97eb2fb653c78c6b33f416bed07ee a120d8fe79765fba1b8afb383d7dc8b5718818d9b25c3d5bdb52977697057a1aa3fae01030c175af b466c52d1ed86ed9f36b487da4e1081af0a9ab38f71e2a7405e03674d54afb7f0a4fe8cfbc56de99 46561255cc102520d7b8c5be375eb619d2e76dafc143873511f3fa9376c755f9d45f286bdfbfc397 a1531b369136d13c068e2a4cbfc4ca29bfadcd57f84873d4bfa4f107698b6b8053ffcf867d5acdb8 aaadbc614e878a2295ee7060f683b8736e2e5baa5fff5cdccfa17f019171a3a63d1cdf1ef71f87ba 363cb730d525313b5e6c071316dc77deeab935cec2e1a655eb35fa1ea7294458f78ef9e017e882b7 a7d54b4787bee8a8a7816345bab535501266136bdc7e88aefd8cf49b52ea440d716b0ee53f66ab9f 026de3e7273feb1457cfd23f3bf6e80ed9d91fe88f3e3b83183ebb29d36c67ea752e3096798b7e2f 2dba6254c01cced7a9b94bfece816df94fe28300139369a08a8786f907fed481c2787d9fd6bede48 f3eb2b067dd7c35f725c3cb497480cf49e336cbcd0907fe8e49002eb5f5af88c5cbffe28ce9d1351 c97f9686b5bbb8b34163093c7524dc1c221a932cc6634f1df368ea630c95464cf63830e33572736d 77eafa7fc829205eee27b31fef5071675c6f8c408d9cb0a4436f64dbdac5b5ca2fcf892cba328c69 66feabb6131b732508c8ee0cf9f29ac9638865b2e321a5c9d7ad5881d2798cca4ba78a26b9a3d4d4 5eab7690da5663e99f129e6db8aa733eca31b3bb515179bea319bbc218c17c03bafe849ace88ebc8 e87ac0417dfc1fcd822e2b08de78f9b6dd451af660d266cd5d657c1a4f5784122314f2273cd6a61f c78a700d56c1ae31ac5b0c1ab81d2af4945babdbb9f4a9812b0345c9c1cbe1a1850e47b6156d6a8b 69abc58f25e37b727e9656a74ac7f19399242382d1675f4689b7e3cd485be1cd56ce2b8f86de0b7c 22f2a07478e7b50e904d5d510e156de6851f9580deafc9554d544ee2af906a70ad65b24f77e0a051 3c86ea67cf6dc3451dfdb935b4571ae95a5edd3d3d4f703960cfb1a06e73309cbd6bcf54b28cb9a6 67c86c979e4fb03b6ad73a63fdcba5c0e8a879065e83e2afdc98a64b7fad3f35e8b4df78fa2bb34d 67f67caf35c7b2438b9b0d7ffe66526e616b8e38a32333ab1e3ef16b3b8806d5b682989ded100f5e e0fa8d723e7131ce45bc73afc1620762b713664cfa4c376b0fc3ae442b93c2c1bb3890228d7179d4 04ed9a8deca4650493a5390e16e4ffd6c501ac747df8c39d60c0dfdaa6aedfa70e4e9bb126d56cc5 47c8557d2c759f4b0e1902a0eb3c76412c5caef8a09296770e4065af406a791dd43ce7b2b5ec67b0 abd6590cd44793abe97cfdf0242bfc9f414b64bba807833ea126f39ab91c95d5b9ea51932fbd9ccb ce198733b87014da76bd150265705f7965d6b8fac8ecebb46f69cca349c4d4a17e1ad77618cbb59a 8f20383577685c8f75b8ff078e05cf6e833f66130013dc44835d8f723a18f2769105bd03d345a76e c1e69f3351929896fad9fa1a8754bb9c584cd3a0bedcd60cce48830e75f1c0e3ad914f78c093e73d 4b754f30d316fab676c0627b2a9e7a921bcabf20a454e4f5c1cc65d6215d2d159eacb6cbcc6493bc e6444296870a7faefec5e7771f42b218aca94edc92fd33eed781fa7632d6b6dd0e325fad9c0af7b7 85791583d166de588353529019590d534f4ce9d2ed67d0587362015aafe52f43d9a34ead55a4adf5 ca97fcbb7e6733948a876ae94e0a5fa6d2e149d1dde0efac53cfc7663c87b916b1200e675dd28b34 7a3ccc10397360aa67def28b33e85f0bf0ba15de285be35fc9c86ccb0025bef4de45a6876bfdace5 cd1952bf7658b3f22077898c8e40acbf5377c6eadcf78274d50fa166c5478ce0be499da7fe5a493b 99993a1dd75de4e1b1dca27b4525b70164ff496f476d429c3ece6f2daad5c3b97bd40d9ab736f8ca c7a60758574f8be20cb87ad2c6f8681f6e06f45702df448e4dd76a83f78991090ae98e9f6d0712ef f7787b79953119c1b7d61d98cfbe4c783f82e89a7237777bfaec7cb1e517e5902e620f0bba7f3695 037c5fa1477870f046cfd73c5583e59a78a404eea44f510957cfe9b63dd81f60bbca6099d7b0b77f 20d4111666deb2cd697ee3731ed974e4609d945e13f8145ba45f8c086f82ffaf1a8a7571b8c2f425 d17592d671eaaf438580f4e355b766dbd9dbe9b10f55bad7ffaab114d8f3ecdc3e22343df7cac1f9 baf4537cebcac4fd327b7946fd3c0fed4ceeb447bd46d769b5aaa97bd0c2293860955f30c582c7a3 a57cbded4ff3774eed2466ae824a484e463e302b71b257e0779571384d04fb1b58590e474d8d64c5 84e84c80a756ae13733d015fe016bdf74935ffca769925f95b6aa821afd50efa4e21a4eef9ff363c 54a13443c60ab55f9f1ea8bd3393b4bf831168cec0a8222ea9a27aeadcaf52e53e3267b3d1873559 b105cb602dc3bdfa794c1a1f5d6b2ba8d54b051642a6c632da72aafa68e6f8a35a650c5fa17f98a6 43bb4d65b30a32d9553e082ebe91e32361c5ed17d6d7035306b72aa1ed4c98b3bb00db54c479c7a0 8439beb00605e3abefbc4b21d9ae6a9ac3bd3c14b61b489bdadcf310e0e5ad29018844bbc85cfa55 a97a622df9c87c897df19b2b7877a6a7a4a29c9f1f0adfd07e62bd8bcd4adb8845651d07836b72bb 9f4c11e886682c4a952c22d5a12489cd62aa416e56f6f783ae2a1f3fa94ed55763ee0bf728ae4658 f4d39ec8face87562ef741a139c26a799a71f8f898937fa276dbc08eb44a2a71429530e982421e3f 12af4e206fc73dc5a64b1c3e692e176e9d336a9a1341edd516449bb3bf3e5d127c318ca3b9e64fb3 fae9fe3f91ff4a1d400dd7249f206e0f921d66b61d6e993519009dbf8dcaf985891ce03dfd558c80 cce75128e4b5e1b3ba2805052502eff3d83e9fdbe74c5f519814ca28ce3d56781c2f2eb75ca16267 82db7fdccf04bcd9007b68bc6e3f9797e31c124584da68ab7c834725d076a59c22c6dadf69b0572b a3c94cbed6ee659d5b7b9f617d16a52a7a1ce4fae7b7f7cbb9fd415abe7cf4d5894c0193775a50aa 646f5dad49b67eb31fec7abc86d5e31b533417ff6b7370dac235e9e45ef44e536a74dd7714eae05b 900d0c0bea8bb13d7b1ae76e535791971b077fc9cc3782d21fac4ea531a16060a96308cfaf67e503 342483e66364d003f317e5d04716d285171b9030b82e7652f08d418fd7d66bef949839ab5c789cb0 90bf936ed78764470d1a158ff5cbd0d5ea270d4833f0c6a92b50bf8022d250f4d5f16f36d747683f 68c8f32fc316bc7b50c9c85f1cda933b12a5ec9a24273bef59e09c9683aa3a956b36d9c7aa0f9be7 b1b5760c5f307ca961a0b97dc82b9961fa607a824337925af942817a6cd9545ef42880f990555eb0 5cb0adb33675efdcdf49abd495dfec87eed026793bed81b6ae9a93f72cdc2fc2f14be146b28ee7a6 cfec2f7e52f96341657038447245de3221b83207ea408b14b2b28104af757a6e340fe8c75f4f9f8f ad672cbbd2ce21b3b4b7b3b14cbbed7e9529f2e473d78d07c546c965bccb95905286cc66d97243c2 fe54d5e59602bbb83b745cbed670f5de4840a61f70852f46dc9e9541720779974b15cc3bdc9193df 77f824a5170c4faea8b157abfb7b31d8a5cd1feb0c44a6ddd7782e03916a042dec157d504d21a807 496debebe21162c6663b8879a35ea055b97a3711e3eff88731a5e2d05268322b199f9c2d30f47d51 d4cd92989ad4e878ce9d18c395e940d10c6ddefd090f774c788c8a5b60c3108ded4c4c3b87a95aec f788a13d0ca1bf435603cd46bb86b1f81c1d68f397f7753561787dd63f5a31382139a315ee3afa46 7dcc757cf690f5f71f32d42fc0f84e0b5683d63f9df22f0e6da08d2e89d55aafa701f2bda5b17ebf 0f9974ec95ece4834cf5ee69a93bf370b8b30e077ea4d3d5654bf8dc81d002777f6f3d71fd43dfec 1fc7a67918c50652983cf4170d2fc63316222ddd24648430e40f3abde1f3866a27ef8e6bf6b25938 57494275b0c6d8a3f75badd93edae2f4d09b3d3befc77665f549d333f8a2f59858dc3689067733d3 940674722ad7911f90e524d626bb524930f75bdaeb37654b6f32f4cfdff449af4e3b3d05baebeb59 a1c0d047ef5ac8769aab7ffbdb3974a8ba9d74140fd6c85b6fa8959f593f74e6d3b9965ef6e12863 e5a177f4fa818e8b8cd7797c8fafd3306613a3c50bfcf8787edf32c7dffef62eaaaf2360d9c1b6d2 4b193487b5fe5f7d2ec0529447e51b876a1b8ca9f72199a6bcf04f087587d6ba30fe59b5ec3a889d d5b15053c3d60274732f3dbdd40f01406a563f90692e49734d28cf8a91ec46bf7313a617b7ab575c 11eb5ce090765502c0f557a7befd92001517cb75d9b0af48c9b770a2224a3da369db1d527df31055 eecbfafc75b4c2a8b4e9416d94542e6bfa66a1bb56b7b567a79ee6dff9b5cdd8c26f2883af24c78a 1277a7aab5b5dfaa42039ba38a3c55d1a2069598ea3f5340bd087facf9287a4b74a61789b66df047 73772af4845ff718dd1f65bea52aa39ea96d90a76efb7f80f967985fc77e346ababbedff64943939 767c625d52147d512c96a6dd3b4f2130c81f06a0c325ab216b86676e7782653fb78839eb2f5ef2b8 c8e7e6a9761b981cc45586e7722d3019349b9bb819d9e8d7d1978d27d3ed99b466b563ab3dfd45d5 bdf2a2a69be6441e196b657177b6581f3121546e7115c1ffde5ab49e1b6fa9bd202255185a5a2e73 66393bff41a366af6cf2727f6b0e322275cf042e04f8bba39a256baacac87255f784474c182bc3fb 4d19194633eced84b0373356f52681e66b0fb27684cb197fae368e0ef3ae9d34a8aa6ddcac423446 9e1f858b29111ac9fdda62f36bdaf487cac2335c6fa912c6e1b3728ef864664cf3f61a521ff5b9c5 75e6bf4a4863cf192757b7f6766af8f0c8083b5b4bda57f351643cba174362536a2854b6355f96b5 81a1f7dd039a4fd4ad43526ac758af412e628108cc3db7b437a269776e9413b6316e41d58bc14942 97553cedf7d222ed3a548cd12640086e472dbc85756d1b56369d402e79bfd935bfe3f900cf44b607 a353d1cba99b7bebca4b51cc145d9a499fa1b3827d89ec164aa6d442776bcfb417da965f958a1a2b e19729a7f6ff6c1417398452fabb2ddc999f5a5a7f8ded9508aea04ecedf49a6acc49adad77b88bd 104639de3c491f0deeaf77d6d5d1db2d3c43fa7a3f5a76dce07a46ac4ff1c7ea8035349d645a6384 7d7eece8adc5e497bfb16757733bd8e0ca47a7c7dad1ba4349ad2dee9c2f38aeb67b6b989c73dff3 d2c26af53f9a33160d55f5dfad9d19aef8953dcbc331ed9fe397d98aa79cf5b0a7a7ee5d2d8ec676 f7a87ffff765b7acbcfcbfa12cba8a7727964eeb2fb74e1527f214ddde40e64e603714689bc79e63 3ddbcc63e07724dfb252e16c8d18426eebc19030e3d2746f9e76d1db7b1ad52c28e183a37d630633 151a36614fec3731bbf607ff4aade86163e33aeb047a59dc9c79744ff820b7b171a5619e2feabe35 a34bae997d3fa053f5808a7935c64138db749a76cf51364255836ddf69561796294c91c157fb965d a6be587ccff28d6b5b3ea1d80892fe649419d48747cf90412d65f6cedb369918520ecb7511cd1e8d ba35455a185d2fab98ffcad65dab9ef35cb7e89123b74d9b73332e5e8fd6016c7d55277e67edf5d7 7ecd44618c31d67eacad3f9558f2c578f4cbb0479ff7b66df56ef26d40d017c54f355c35e75e896a 5b191938830b303005a3d674c025b817ce2fe413a28bd79762f53249aa919fbb6fa6844a798d962b 93e654738f87c5065349b0a32c67b5a743e9c7ff2b6b99557cd054cfe5438ce8dc746961dc948693 f6fc716b56e7caa713af4cd51cf25ad99fd35b562ec3afce3d854eea9a3cd2e6b79a16a033d62663 d5b4a794fe75669fabee1f0fd399a4f32b4c9b23efffcd175c3d9f536d0ca7b47b19faca4924ec62 838f5bd58774aadae178f902e78cc1e8c7d227ff2abe85a06d3f8dd0241b97b637de332b36260fa9 71fc7b632ea13a78cf6dc777237ad717f67dae41c880887e25f186bedc23d6b2b3e7ec16b89e3b64 85c6fcb2fabc49e5924fdaf467707583fe291f3e31af6b29ded771f5d322c1064c8d32332f3bdbf5 45db05ff1a5fb7faa758159f49deb9261525caeb3fd773f7afdefd958cb034ff283b5b8b729c8417 e09ede09efb6f041405b66131811f8416c357615dc62df01612db47a125a8345e269228988abb355 f60316d0dc0677580c574f01746337b0edddf83ac3487136b1b3b5f99bde6dcdeb13bbd5fefe8c79 746e17ef193e330d2b596994898faa1be7c72ddbf21b794020c1db213517ee3100b67157edfdc96e a5eb07a2a2b26f9357f66eb5905366c958aa8c4b4eede875778db9e8ddf05f5744dcfea4a47358b6 8b61083ac3a08bef633bf4b4178637e0978bc94ac5327779b935d81f234b09369bb44252a29cf460 4a941eab4e7410708f6e4b0623f959340de26155c3578e92c8cf03fbf68404f93d0df4010f6dd589 6e67873c9f03fd30d0a6b29de7b31853cb0acfd5ae9982c0683704bd4e73b0539a0fe5febec47e89 77e6e8ece610eabdb6099deb4bec014fe17cd49be3bb1167ec829295cfc5d78618f97f4c5bec5dd1 d1c3c72c4d5775c4f71fc7391e3565463b2dc667d7ddee2118f226ae2eb107d68ed96ba14bc97c63 dc270726eaac5647be6c6288e1ed74348067dc783086ac9b41d7f5c243ebc41b5d16acf7bfd94fb8 f71d623eda804363b532a8e9018f36830b2f371ae38e0dd48372b04df2fa682e03158b7c3b176f37 4d153ca8972233697c44e74e782f98dacdcb661db11d1b1a14ba0e506ec9fdc4eb6168f7e05f6888 2b48b1ece4c103f41de0561ab8c69cb255962abb512ccdd1fdfc6a5be46bb8b10baac2836e47199a 7d8339461da87d95ae5955f555bdc606a4775d8cfcfdb4eacee8f79fc7e40a83fbcf0b69ff55dabf 69708e796721583d4f9e168b6ab6b53dceaadae5f6ae450ab3ae84ad9472b9c9c3a483017bd37d78 b29c0cd47af3ea35a27ecbed87928c9ea2f5c1be968da56d8e772570eaf4be342827bae10b15ba52 511fffa6dec735202c8246caed46d21d54022372771ec0b829ae270ceceed94ddfe9cbf71436e79d aeddff547b5694f1a4bca9033561f3acb0e34d40d90cb881c7e215495e49893af23d3d89be7a172c 6321d4effdbc671b937d5ac9f677d92348d10250f1166af4a1cb4cf89bab0a0d183eca5107e59379 4fb08764b33e5546ce5f1e9564f385cdb6d24d25db14e0637fbd08aac618a849b64339e6401fcac5 19beaa87ecf03b37a979580a0c08056fad8cc6cd68ff16773d19b69e9a306c90015c5fb5da64c14c b5e7f9da7065f59300c91c6fe9c90994c72319aaf3873d0118581c96e2a9c96c86584f76f4cf833b 86f546f8c6d6b3e0bfc4fd08c4cc9394f9021a1a5fd003286c6c5f243c9483679bb436334c48d2d2 7044bfc05362d590fb57c997d64c4ffefbf34db5c7cd7c319de06d95406f6689f55ce730bce9c0ab 3ae08c6c342f525fc47efe865fb825dbd106fd775ce5fcd1b0f542f736b33bb2816c897fd821d719 8b59cd71f7ecd928646e2a8ca9d9bda7ad9d37b0ac9f9b3b1f3ea14502ac96327dd1ea233704d859 68c3e55b4f08b28d0348d1ef32de933b37da3e322f591317ce1df57b39b04dd25d53b11f540a32eb 3e3fb3622568b2aa158de7647358b79c8587f63a5fe978e1ded8fef1ac3b030d6fbaae508aa174ff 3958dae5f8b2b1eacc978587f633e868eeeb507c1ad81d7a8ffb51a0d52aabb0af3fce3dba07461e f27a95bdac3b3db5c387bdb5cf0bf7ec74919b090ea767d37c0deccb8ccf3591a1ff585b1c72954f d66436415f753f84a4af0f401281e1ff419a6bb1da906fd2ea2f78d71f23f0c15f01d5ec38dbafea 5621a18a64910cece1615e5ddf2b54956eeaf24aeaced2d9d982f1d6b8bb5526eb9514e1325c8577 a2acaaebcab3e44d58d16a3eff6a73edf1cc7e8563d326b151183fc123d57f964fe3ed1c9bf6a36a 0b506f29e9c5ca7ce6a38fa9906886701ff99ff884b448c44175745f60cea480f742c58648fd34e8 1c27afea83a29afc38d553fd0f4e92cbbd810fd6dbec7f74b0df6a87640399218d1978d2b7e70ee5 de942e0e70d8974737304dcd815b8dfbde496b6dae3a3a9d5ea223de9f5647a6d96db66f51b01897 3b9067348c2351dbfbfd206eb782b5021971bdbd732005fb15270975e17975dacdf16cd299ce0a0a 3ab99a0d697b297e62ac810bb50b6bf5e7c43048df6e1921938768da2571ec8a60da07c6328d1a0f bb5ecfb4c160c48c191b714779d24bd9c2c4fb737e5f72cac8fb079d51b3164f3b44a2e85631323c 6f2bedeeadcd2245cda9ae22f64615a702783e3dfd99a7a5e3a2d27851ed94e9bb7f176418fb8f9b 8e0bd341c5e1bb981290e7cd0c118c75d3dade07a8db28921d702825ff879c3ac9b394c1775865a6 566f10b45a7f5a0a8555af9f3fdab6d791969b701f455c8700ea2587d041c5735bc8b875c6bcc9af a0cfb7bdacdc595959ffc27b8dc15eba6fa266343d2d3971a05d76f2a1d0b7ffc7e6680125edb306 a13ca8b73d74fbbdb122166e4323e19e091f5ec04443c8932f0e79acc8bcdc56e467659a4a5ad2e8 da33d81d2b0bf56d7f8522f6e432d54dd5a056aaa9b50bc175bfaa31d3ecc1fe47a0b4d3596a2cdb 065bd1c52afa114b8b2112c6950aa6f3af4621d1d2e7a66c4466ae1b5c0f928b975b1e9669eea94f f62d4ef560c545ca6cd8d19f3d99e08d39264e867447b136e295164b87e9c45a55ff6f91b400a642 49b67caa72b567ba325d84a795de56a7ba8bd5ae63968f0b5e13deb035bf5007c13b5588133f1d8b afc03d42882b91ce49eccfd5ad52b370c869e62e209724ac4e4ebcaf7ad82fe9a55a39b13f728b40 773731cda0d4e3d8423a8e07551c0839fa8a090d22ec98f7867df4476de029ae9f038af5906ec51d d5ca4b7901b48b6e3c3c34edbe5a67b57a9104b37b8f45d2abbe9972976689f69774eac5c89ef835 c60851dbfe9315397d041a926f2453028624bf5a3edd7cc159cab49f7411bcd13b5b8beb7cc0e651 ef2ff5a89b3c710eca82d73f17cf1cfa59234173d011a9cbb1c44296ab07cb9b6ccb22db70bbd61a feb93517c4de03f5882be60c3fc7e0bc2fe1474e3c7e381f5dd6da9930af3e84ce9dee489f9a4526 4c8b4e24ac9597c916b16d078d41982b6653e290c164b0b693f5a3acb65e4e961c371d4ee891c249 8d03ea0736c6663fd888fb0371d610ed326702281ac93231f91282ab5ac602fdd651e1a3171ba589 7a990eb7775d6e4dde1f85bf62654fd4e799fcce858a5a073a0f69d81d5795dded61ab97658b198c a65fdbdbfe71bf4ca1f657ae4de086bc186b7f59c4c88d813f898b1dd7d6f679d5935f1da2a25f6f 9aadbd26a544398c6a00fd84f295de9520467d352e0a269ea0b11e9c0b49574922cdcb299f5b34b0 a4a453e5a0ba5ce9f1fb36e686d109b9485d54f447e6c624ef0b507980f2b3ff98cb9e71bbf6658d a59775b8777dec8df059f0e29c452bd1bc2a1a6e771470d2be06a33a203cfb4e15b820f21dc8235a 7d0ef7d67d0590ea7080ff8afa302a4df6e691fcf2c4aac90459a97903c34eb30289a7679139eef2 51f8bc702b499f9d5e17fe3c2070fd59d157864acfe967cf27616fecb9a33e06f513cce7d5bd79ef 0213e1fc0e95f0639e7b71ffeafe72d212d86269cd48fd24d855cf90cceeb77f7453e3971e1db631 e552476718f13956edf57a5ad356b7c36e3238108389bfcf24b14f619c53a24135093ae38f64a8d7 9af0e5154aa80173437e0c5e8dbefe99fc92c51e110c4aeae4240ab036cf2debd64c3a8298da5c68 2eb67e36f7b70b714a1f70adce1aedb43eb50db93a7c79a34d4a3021bea178652c4b150c59940597 3884aa167c2ad7713fbef4f91e815ff4a35cf97516dbc0031a0a977a19d5afdcf6ce1fb6452229ba 73351ac072d54b6f4f548116cfb1a16f1e32d4c2ab23ad39bc66da522aa15f4a5b2be44e729ae965 b8dd519b6aed4fa96bbea393f32b4d9d8ecdefd16d28ff7b0a97d97ed601d693a7863f6dc870c211 95e2d537aab5272d506b46a26ff5bb32ab89ed83a92937ebc1438ff74bcba8774f7b9da750afafd3 0f1db8d5657d118f3a1097757afa2c527815ee5935bf37147fcb2a4db6bd7ca8936e3452467f1866 3cafdf5f43cddb9e121ef6c1c8a88aac3debd9ae53b9ce7a8609dd59a35aee9f93aff0579d5da448 cabdd6244c09f507f6e1223f55e5bddc72e1f02d594716f7b44e55f94d89ef952b35c57cf99744c7 821909793dbd6402927456208d853ced4ef5fc5e733255b6f4f92acf2740df4df5e747d56517a4ea 2df6e8d46a354c9332af83fe6da38ba58f444acf2b4e3b169fd12eaeefd55fac53694fa6a171a295 69f0cc2ea072509a28d73ecbba3787c1863a6bcf58e2180080a3b1b78376f22627289b71ba8576d0 b26ca3aceebe3f2a966489252b03ca97e451132a8517b2aaa9f56ed41a6e5eef5f74d02fcbc3b106 829d107d676dc4a9f5d092ae46ab383ad7e1209b8e575d7955498786c95fa6a96e5c68c504ea5bf6 5cac8f51f9866c55e5dc3d105302b73c4d1b43da649ac6d06ab430adc7f1f27507cafc67695f99de 59f17f8d33652195452c0d9ec3b668ba9796590c9d15797acd77324c8b5dd35f487fed03a05d95bf c7ee688e33220845b92b496287514c89e40f3adc832732bd797d4ccc5f92cc721504cab039fb3d8d d93cc92d3c12ab6d55d94b27b3ad3ff396d1c2efda61d5b81ad9e9ebf30ed976a916c7eac9480cb5 2a865520d3c0a1aa1b9ea89e48ced040cd5fdb8431b9ee4aedf5ea48ebd8b84f9935a8a705521984 fe8f0827adc2a8751afa57d564b0e13d7a27a344c83483f7deae31eacab0015bee00178ac1dc20fa 45ddb0fec4b8b5c834c12003a1acabc2ea6c57a48d6fdb7f87b2ee77515d2ce7af8a952d8e437d9c 577f2840369aced3bc6e1a65a304e9ef7601cf5813af9c3b4694f9a19f1ce1bd77c9f2890e5f5b55 7503bd01d7c3c5b7de93e3257dd8098603d9b4ae3b3831c07b0bdcb0d696b2351acbfba9759b0b89 a9928bdfec6e6dcabd63cbfadc5bc171735e68b7f8040af1785478e79bf365f18d333f38263dc539 17cb971e56b44b7bdbe5bbd6a5346a18147f27fc6617a5126de0005a34ad11aaf4b8d6a3ba2ba35a 31e5855f8776eb676906513aeba8ba9de1c8304a9c536c58fa76babd421579ab5943c7a734f2364d ad4e859152b7f7386b9e3c3c0b4c7b8144a6c690daa114960619fc1ef9d4ecbcd459737e686f1e16 ec807d59325a86f68b75ba8cf4a1f906a938ce7d7576e57311bd856470419dfa67ab8ed8bc7f94d0 3a6ad8fdebc9ef963c8c949f8b1a612561830743e86fa795aad0d0199df8a52163938a148303c2ce b859c89dbb7243761cec3733c52e0d3e595f9086ac329e0d8696fc6cec1077df2a6b25002b5b5f7e 2f3a13a41da8109aac6deab808e591543da8f6e65cb266f6cafe22a478503fa2625998d42b63ebe9 d3d592ac659ba708bf80f7c5f97feba2c16f68eb852f313dbef512a3ae912f4b7bb002479f0856bf c2c5c07cdd0f7e1f383daafaa501e0661a2d2124929a23a3526ee7a6b3bcc2f6752b0476fdd95c5b 3da43b94e6622bb78403219acbaaf74b7d533e233a667639eaa6d4a9ecb0f918dd98487b583687d7 3ddb6ad3fad398472a68feddf5b70ee656e80e59e06ea6717fcabe7932fe62da786b12acdce9bd09 78661db68fc4043a048c64336c612e8fd66fbe808948c1cee69bd92130f52764fa5db62f092d2af5 0e820299c8917f53149cf6dd5a72ce8d873cea6393c36b6453c222329ee14d6ad158ea9b9354999b 1a7b7869dbeeed1c3e3c7a61b6230c61a7a7d16ff9899ff9c0cc380e0e726fcbde6e2ebb5e4c8c8d 384590d916d7ed2fa1138c9bb0b85a9fdc2c523c372a26de099e120c306c94fc752de3c41c4b54ab f27db15bfc0b20d90432b1a8836ace6ba861c6022ad6bf9a5bf9f8551cd476fd0a60a353288b62c4 f3fbe17be99d8eec9b694a45210ee135e3da847f2354bc26491f750f3844439ac2ec04ef2bd73db9 b2e189b0346efb5017cffea3f0d274ff270c5bc7977448d85f0d94abe1fc9ef42bd7aa7c19a27b87 fc2c2e1dfc2c736a9b092f366edc9b600b0f0a2d7f64816795f95c61dae7aef26a6d0857ba0b093d 1d3f5255d8148a43710542a0c6e9a45513c2b387059cc1ea39f895c46b97b7fcb4747fb63388e70a d11d27c95d454ab7fc6a9d55755d1c5a0e535deec9feb119ebdaa99cd9dc045975daa732a98f8fc5 d072e9c00395d55af942218fb99210f5e5f5a350cc3f3d293b5a63fc13ec348b514b5356ceb8add6 838ca86783d878dfd4b795e2f7311cd0aa63fc260e997f463cd0d7c2a6e29cd2cbc30956659e978e 92641f87cbb31d4eda2d1258655f6949e3436bc1687e0793c69a7914e4ff43b51bafc9009c9cf8ad 59398a84935bd042362f4ae205076a611faacf3f1ac2266db706ac1ceb268c5744098f47b65dca4b 1686452d381bdfdfd6973f36ccc94bb374399fd64297d3960e68ef52be4b4c7fccc64f0bcdb1dbdb 757ff0809e1d370a368ec56bb76d0715066d3b8cb3a75900d419dc41c6d25c1b97a1c3aaa5441eee 9aa7481ea467db9d63eae8b15eac828a3bfbbe04c872f00b55dab9167bef9bcf53fc3b9ef06cf982 ed66740a4c6d030f94e79052b9bf7211469dd72d6101ba410b7a55bd04ac3422fb3d386f49b5dbdf dcd306b3b83d2dc48d7c5eee3f4efca41c60e41fb7ea4dc59e51db321b6255420a093afd079befa1 d472aa599b41323c3ef7bc152badbbc3142e14fe7dfcb86546af4174318bd4c30a076d69b674955c 4a2565542905c1acc9ec98cb436828bb7ac9f62b9d59b95f8b82b3aa74f881cbd1b2d29ee6f87f7f 33e82d3a76d1f4446009d2804e363531b8affe4642c0c282f68eeb379ffe4cb2e1c12f717ae9267e dcd915f1bbc4bc9075e278c01dd458439083db533dd574d2daf1a754e933316f9add19ef4f1ff2af 3186f1c56ad5accf32d103e863da7bbe565b23cf93c809ebdba2bde9681f437f2391ddb1ba1c58b5 c0b621d6b955501d3998405977ccd64efcc50b4050198e3dea60858536732bd2b2d4ed05a46b16a1 f1eb58b193eef80e4d81c9dc842abc6b092fe3ac94ae23cc836f4fceaf861ec088c961e39c0ebbd8 b5af60da3b3ae0c5f64680e734d2c1b97deeccc7d6b0cd7d7e51df0f4870c4c9c47744e89be3c548 e86ce59ff6f4197256f1205a3d0ced4dcb7693981e3a91dfaf7731db73ecf3e17db20713c286b683 d9c9227a47c982be00256fc97814bedd66d35b9f4e4766d0ed41fe73cec16ecfbec3bdc56edc7037 cff5c4de86d86f6046fbc81b2f3bad1c6b96f778f540f681d7cc62bfdc8d014a6eb306d0e1f8842e 77e36230190d4497084418cfd8907a2a4a4725ea0be95623446fd34e1bad1e1f579553522b3bdcdd 904570b63a889c1afca033813a7f39f5b7f4bb9200743fe17e1b13f8996d2872c0fa6f5f195edac8 20ebd695bf79aeb83577f2016a76f8d03c6b8027fe19ddb13ae3884a9bda07118b61dd01d7daab4a 51feeb05b5dbd1e9e8d7d96f1f819a224dd45dd9eaa6e5a0a4a04d680ab093e3fe261c3bd3b52650 1a1b3dd00b4aed4241d24c7d5d0419b5d0f1d33deb6bcba8c278aa1c361185fe3ac08e6eb8ce5062 7680225089beae77bfc055edfce820db2356b9114efb56c8753fcc6058cdd706f5b7b57ca443fb1d ffb755aa33aa4cdcca62d16fadd7a7c2e8d2fbcc06abc49f9083fd89f53605300268dea0668b5f71 df8422fc87a64cbf7712866684143fede91656514702b3dd351be55ecd2ea4520df89eb2ba31ad38 e7680d0e3016bd215f4550bb54830ddaf97e6d9938da5c729e79496d067762e00fb4686e7a72fcbc e2b79ea4929a52967dac15e20042f8d27e2523de31eb76c3c8aa1ea809cce8ae544351df6a2409be 5302d27e469fdc95cc8d84640d786ed9b5edc7d6293802cef6f1abbc131e8ec4a95962dbcd7bd777 af0d2eb0d6e963d03a76066e610cef1e55affe1aff3b514f40ecc7a93d7206bb1c82aae4bc69f596 db86e584648585dc46ceedd04e637296d2c5e0503dda5f44cdc6c969c1a2581091a9f8da624498e6 f727b4231e4ff9f890561eaf0afb26436fdfea9e4c7f1c3a53562760d4893b2d31e992601a4db111 b1fe10a2b431e6cbc83f6bf7f664bb2b2968d1e67c99e8d1a0f51a68eab2341a3906b5e158ae7e4c 64eb2b6ac7fc0ad9902502a09516ebd663356fd7b145bcfe818df2ae8b7820479729dc6cbc08ad84 f5395749f26a73bf6cf7753083ea93b6371147c8f07855ef333b4f18d5dd1097dc79685883a64369 5bbcda9710c6b4e9aa1478869d1ec19338ebeaf45144ed3d3a6bfe2c6d84a3925133f25e4af28b21 593e547dfd2866d748469d01761dcc61bd98de72df9b40054cbf3841bf1d9ab0b3117c15403b096b 40afcf76dc5911b751675e7e9b9b6e88c4f67ae9118f255c31fb17ec47a0824916d868cda731e31c d80ff714aeba2d309d3b4682df6f36b766509695bb817d5bf6aa491c1b4fb2f6a8e05fe76944e131 19bfb14770242db487fcf9e54bb90c9b7b6b682af555df614bfe0110f7d1ef32c6e5c2a26922059f 51efc0359c5bde99c700ff78110f206bd88babd00dfa3228a28dedfa6005f7d3d535f2e5b3d557db 1bd3b34dce6ee230c9723ba0e7dd97f72819bdd535593d917357dd88701896aebf8963d8d5580fed f771987b8b73ef023b42fb6139658e769ad3b30fb87fe9c814e6a54878774e4eca5e1890f3949620 01179433d99ba60a3ef26ccab4bffe63ebc7cf4532a42dad6c47590d7b198f8d62c976f25f46c1c6 7b9a767aa6280c195d60e9bfa97781be72237621485ca31e2015b51a256ff3435b664deeeb9d2b3e a2362ec44bedc9ed2bccfbf7bae68d2e554ed56a79d2deb74055a2284780b2b26df07ee597f4524f 26351777defacc704938d0c43e33949745ae604b6205eb35698a68c08692f2412ced8d6beb59e7d4 bfe2ed6ec0b56bfce65f087da8ef482522c90db1f8c412f1829683e17a753686a49c2aeefaf63368 d82daf6746ff8f2498b924cce2524e44f6dff449710b2ecc75b3b43bd94dc72909f3f9b244ef978d c8c28f40592e35b719166f56b6298fec4c9d385b2043ba55d37fe48ec362e6bded1cab44dffb5a72 f87f67f19a588a8c13f79ded7e4f8b666f341a94fa056f0f28d054be0ea10c8fed0a6d4ebbb6478f d1ac1fc1f9aa13b593d596dd5d4f556d632613ffada88850315f5f1eb473eeeef24db99215443dcc f607ff035deae7eda91c0c36274cbd551d4bae9b30d26c58723a2ecfc529925a5cb4a351e100acc9 a0c06b47b1da9cdffbab0c70dc833c59c8ebd882e0f90c585beb0bd6a62bdce811fa5f9a3d1997e1 323be0bd5f6848232b32930cb43f950f6f0045571fd341b048c48e845783272609c8c9ad0ca84245 2b76313e0c26b72fba401ff9e99cf7f6820496bc3c1d914ab61abd05316e43225bff752aa89543bf c93999cc95a45f7d9ab699f37348199dba6a2c58b4d40b566970815a3b710eadc672dfd82fd569e7 f391ae6fb6a9907ef6a511e7f4a074c6fb99ea69b4da31c36b5f9d085cace3b3a29d669b96a18c97 8b482c8e966d157ff48fd9a8e8648849af4adae5274aeba92e2fd59a321ca9db5ea693274d4db440 7dc5e80a9a74bf0e9171afe3af69e17d3f5f2e964633b2523161f0b7b2bf10a0fe9a355772a93379 53c78aceeb4fa9c628e3c3e5a76f3adc2c327e9e74a2cd40d14cac97cad8ce49aa0aa3deab631e7c 9cfdde91cf8b0b3bd0f82a59d4ac7a6b5e9585dfa862a15a464db9344f54eea273d0eec3bf8dc368 87712f4454bce23c6a7b080cfcbe8de0e68d8bd20adb8a5394df6b09b6df4d4af9ecd7b662de8f4a bde6041daab695add6fb636bb41d07f164a64ec2e7d1a179f6397d98e8a9db0c2a7ff259789f709b 73ee29ea559f4453da89eb56afd3defcaabb9deab09a2a7f6b21861e7d0bb1cad823e4605a6b7a5d 56cae36964ddf9d38babca29b92042b8044562e213100586efd0b3dba3d7d70264bbd3bb96317beb 1a35adf968cce246b99027696d8172dbecf133016319ebbd24d90890604f980b87eec7dd507e036d c938ef2ec49f3df73c684ec6cafa0fac7cafc297aca350be686dea8cba7ce3c3736cd63d681643d7 a4d7855e0bbdbed5d3ce739a1acaad99207573fa97bfd155e3fc40f19937578486fe6708692f8c1a cdfe4b58ba18a4127fe251df720f422a1f0e91ba99ec5896c1a72b99b46a8ea607259f6860685559 38e65657ba24de5a4fd885a6dce767f9f468598e36d37f08ad68c313a3a45e6e4bd87176516bad55 55e59f6b9dbc3e494fddc8cfa1261c8b18456c6ea04d8f8fdfbc7492089e55d1d615f05e95edfe9e d5be7070d7b54a63ac8cfeb82db3ecae9ababe47c62ae7377f088d6f7ba7a73e7ec30b6d4ec269eb ef5c29f43f320ba4d1e5f3b0ad4697b6b8fb652b33279f15ad83ce9a9bb12e28cc408548ab7bb0cc 5ee406ea64348151d0cf59e350afde746a3545821af231dd59aefdba5725be5e196add903a38d1a4 3e9045eff26206cfe4cf96156ff23d67d0018ffff285453136a7f56ad30842a8ebfc0b7d0e2fd2c6 0cb2ae4056f14fd34e5bb25d732d56badbb7d7628aae3c33bb23125f767f82dd61458e513b8b7c86 02c229b7226996eb254b727d790228b132bccfc409c3f6d58d379d853d2105a4431999d2cf8ad4f5 ae5673aa10cbde0e378266dbb16bf7a7fafcf438a8f78a580b355f84b0aea13f3a68995b574d269e b6916a8e2a0bd33efe0e0f4a4f9619afd6234bc7c9cabbbcbb2be5f1d639b4de9993f63da8edb4ed 497b7914571cd8212a08c6fab8dd28f3ddc3e7f1213832b2d706a56bc187164116fa45078d2971b8 772d4f7bc89d30b81acf41a5049ee14d59f557685b0f9071d3ecca8bab881c276b7d7971d77cc87a 3b4934e29b7e35aa7cff4d0a5b7927373e064e4fc6c8fdd955550d58cac6a96531ae5822ffb763a7 f3aba6edc8ca5329fdc96d85417a2dbd2e81fe48df99b6da6095a6ae3153b08baf82b1fa77b19e06 f81c3f5bedf7aed01110b4d56dbdc31b835d71d34eca72a911e26bc6bd4170adbdc0ed454bd7cb5f 316c3f9ee78c8e1e2e477dd4585790e78a3ee9a1f83819c3b47376cc505f9885846baac6ea6b9972 2f5b53c8be707e8dc0c9a8be43cfc60b78ae351ff1e46ef336a68d49598074ebd9805ac218560ca3 fbf7834e25cac7aa9eacd0c8e9f5809bda03e31ba749f3a5ddefc933f530adb6fbdc3433aca180ed b44546be907715724df935dbeb37951edaa73a7ef8dacbd65082cf722a9d2006f14a6998a9184f75 a9c5d2fe51754743b4a9565e825ab7342e3ad623cce73a14edde2d17e20fa625ce35f98c14ba8e9b a21136b2c64e49ab41c2c15892fa558d0c7fbd338f7ebbf95a3bcfb57fd2f862b6463e4bfc699d2c 92d6c757f877195b7cddc709737eda0aa0cf33c97b46fdf0c53550657bd62794dd00663e535f03ea 5c8d4cec86b74fce348530ed1673582bad847b0bc7cf8c2d7ffcbaba44649febb5dba1f5f90b6de6 7604ea427d9325fff7e2664dbe571f0e0aa97ed650ab798b3c78c1d63085ba7443f37de9decc6337 43855e18e4167a9b6462abbc78889edf2e9927de7386bdb9f294ae8b336c463179eacc1ca8abe8ab edda74eb5b1264dece2f15a18d36e0dd140272a9b6de695b8eafbb8dd94b549b11254851d854c9cc ba588c88f98d46d4c14b0eccda5a2fe0cdfebed6247b6c9bc3405c1b1fce9dfde3e8aab6150782e0 b76021428c244489bb072710dc5dff7f2ffbb6e7dcdd0b3bd3d35dd552eda2a1dff673f8d2d1edc0 a55dbdc9b6fd18fbfef269fcb12a5ddc117e19fbe91f44263bf122f72096bbf93b1d35e111868bde 6cb4db799fd770679bae6efbd721d6fb152f4eed67c6f6fd4eb3e6f8951bb966921182fa8a317dfa 22de23d1f3017bf9c2ed13fdaf78d4ab655fa1c95d54a079ecf54496d0be217e0e5fc7edd81b8c66 2bbedb2defc3c633c3bc83aa39644a229b3f109846be94f61ef03119d9fe410c54af0e74db7679fe bc27f946443cfe70abb487cdd92f9f16ab9365e8d9edca9d31d8e32c026ad79377d8cc27e863281c 837df6d07c1feeddbd3b18e29dfede4dddd777f1d40afcb34e9fdf64e8c19d6bce6bc83c4a10e227 3690d003f2eff9039130c252bf3c1ffedae0e0cfb24605c3fbc3fe8330c1c55ac237ae3b673b3bf7 32cdd1b6190bdb2cbc1e640f1a082ec371f122216643daebe8008bbec6fd6bf8c0deb9cff541caad 1495e6dfb9f4bc64c39b1db56c1b630d18183f55ab78c77675f6ad3f2efaebed62d181d9b004b16a f3168ca89fa8843f3650f46625273c545e09320062e3b67ea1fac440c15863c8bb78e88327e33b2d efa2947e89d479a61dade82f5887fbf2f5d418c7f7df12074738ed37a1d8950e4ec04c1ae676549a 469f39f9510679c8587d77b18a5a516dc6e61478b5439b49c25ec36d10c8e2b27106fde41d229b93 00ddfa7bc97d0b1b30ca22a462b07dda72e67cdb0dbf56ff9781125ddca9bad52348842aa18ad44c b362374eef64700fdd5e63ab4d328fd23b9ba0c0b4897dc1cb5b9fba7ed170f1e4970ab6bc7f7cb0 c65b214e8953d6bb6085f71833eb60764a0102ad8c3ede1f74fbad72f973276d1c26be2ceae37073 139aceb4a49f7e1a55fe5c3e84955bed20926daa111cda5b2f88ad43952ae6f73060a80612883649 347ec226feee02bc03beffecdb81d14c13781c2ac1c39b1fe59b39edfc97d3bde240e0fe911fb64d e472f8d6aaf700cda90f410ad63578d8957100b78e2358cdfb930098bd9060719deef46176e5d3d4 f7de4120b380f0edd4b2a4ba96bf417dea2a54b7a0c7915fee96fc5785fae134b41cf51f612541c6 41395ba236796df5bbd87da4050959d7e5a2d25032652d5e02e4e4ccfe9e14fa4c94f14af18fd30b 47e0b53b10699bcdc23fc95001fb01b40e267470ea565bcd8eeaafbdbd0af2acfd7f296a5819f24c 7086b5ab37ce5214708c26105991f146243c1eb62b0ba4535eb66c4e59dca336930366f53bb7b555 6f4465da3dbd487bed0e19300ad552a07c01ff5cc11432d3bbbc8da7b22e60f3fefad7446e731d32 8e1c8e3f432446ba2ef55b7fd66c885d95b151d03caab34a5aa24e1a5fa5988e3507ec525ccc0d8b fc2eddaf7da897d2285eb03b64db76b62ebedbe661b7277246f447e99cf2451d269f41d9fd690dd9 4f76e158159a8f17a19e334637f39debda9b45831276c0b65ab0767bafa91ce6f1bd0b256bf3e9f9 9b13995c8cdd4dd979f8f28f9ed24a7c9ce46f6e5f8734cf2df9f5a8d8b822f9b96e475eba487f9d 29e13a78b8c8173976bd631d2142e0b2ad1a248c82c13ec0a0f8dd0f70a9399e9d035d5e8da22bb6 df3252d9a5fde7974bc2ebeb73c22e0bf1ee0fa3fa25c461e1026d6e5ad3f7870011bf8d7ea20c2d f8ffdea81e903231820dd7dcf8d01a85f318d845d0c3a8364bc53308cee2d10c0568726934f46723 c0b6a41614b772a1bf8fef49f23cb43bb176794e24e07ee9c74b86fe445a440f18867f3e2227c3b2 d0db30bfd410f6e8ecf621651ff3e0306989d0cb6dd7037277dfc629999f9470f800d2cbe9338db6 532be1a20ca41351a895c2159200e463707e447de9390a4b4e1035c867570cebdd552308e8a2ae4a 5cebae547be1f97f07b1399ff127044cd5c341b976ab97bb4c69d987d3f7cbfe2ebd61058b4a5540 b7007f29c43b0ee8809ef45939ce7497f59e5f496c93f6e9ace3c701dec508b2ddea3d0eb6d1d3dd 2c23775040704cf7bf9809bf3dc909cdb58ef0561714879c0cd9c857edbdca243dfdcf2576a75de9 86ecf9adb9f1ccbb5c2a32772477286dfdead9a557d14eba49c54303a12638ede1368a8e47a10b3e 80ece86e8f90d8f551e40739a4f319caed6983ef652b351db5e2d8c11d5aed74939796b984da985e 9cf7a982c7adf5de424003b1dc9bcb7fc2fd6ade523ef22270d7c015ef406590faf335c6c63d73a3 7a2ae7eb0f65321eed55f3e8a796104f80d7151d6b82e3198fbc159161a301b5a6fbb577dde4f78e 02bd4c69e198177f437faee9a1bf1ab41c2e61fd0878b9092a231ee1dcd707bfddad0fa381d46b21 7cc769fbe2d91d859a6b3f95a36bfd1ffcbf611d32132501e3e3ebd80c390c829369988294720a81 60a86cac181e8019ba0e5f8f0014aa61b81a057dc8abbd74bf18f0dd6c0cfddddeb4b73dc5cd7abd 998a1a62b6d4b4728ae8aada8a2784f903b784b59166216de48d88f9b639c45a784920fbc92d04ef ad585910f424a5b2fa317d259703efc48b71fc32553d51a6c59592902f17f558631bad7a151cddaf 9776c88be34918e8f80bea794114b41e835f581b43ebe38b5f1c145cd9909ff1a057aa047479c3be b562bd16bba7abc5611efc87ec6f4792ccca1f1980566b8db5f9529d8bcdf6742cade977476b6e18 75705f2459ebbb2b9df4414dacf6f43d87939c210ffe0b6620e43d2b98f7aa816e64ddc689b59740 7c7d080af8f3eec2463b1a8aa1fde22fe898326b777dd1bb97f22b8d2ae0d7c2882edc519a6e1bfb 43a2473b587f956457925e30bedb676eb9d605a2be7bfac94d4953b231b42e87aad3b7fbe6b6f59d ae293bf0fe6cf62bda0dd2295f2b0ebb96a134ee9051c33e1d33b7552e0b71abaa5ec1a2a7b89e90 69f5fe7dfb28f307f29ab8d83967baf9848769a6456e5c47647f7d9d19476d8fd86dfcd9b95762df 8c3f684782bdc5e3e6f9e73211d564b02a4ddea7d4af69cab0a7a568c4960fa0ee3d64f0da01d5b5 f007db2e80773d5fb4640594768d5127e2bcef675d8d26f6fb097ef7caaf7ee353d5d9b6f79d3f36 7fc762b941df61369dd749ecd20c5aeb06cde568940e97c3165e3667947f5cb5eab143bd3770aedf 537f80ec5f612e3de752d11ad2910d5617dd5d4978b1951a3e09a7a3ea30eb05af5f12928cb7b741 f0b9d3954486946b63d1591b81a4b0fba8ed361388103a13ffd81ae5bd247e36f9ab84ab31b76999 9db1923769a27dc022e134b9a412dd5961df9e0f867cf45cc6287f63e055fdb30b0c7df59fe2dade 511f03ea296b2b3176d26ba1508a29e571511edcfd63b8f2a7d0afb4f5d419a1dbb2e0d66bcc8634 70333b10023b60d97836d639ce3bb8c04d95d5839b775149ebeb30fc7968516d90fcd9d0ebd7126f 4c0f45614820dd94d60bd4b0c4233fb7f46dac937b07071c08b375e736f6a1e1be3aa7ad3240188a 5dce7611725001eb12cb9966ddd85caf81566caf09d630062b0a622bccbae5ac29a76d13fc81f93f e62335ea5e498cbd36b30dabd9b53259b8c59bf96533566507ef1c5feead576f6a1b6b634a64cdd8 79440c0aa66abc3ae444074daf53a168477e03e0a053679f2182db643bd53c2faccf1127189bdd5f c3a5f2accca79a52c7bf011509753d7dd9466bca63987f824a9815dd741645fcc6d3ef61f6434c0b 934d2fa3c723be8f2f0db9512b49b6aac760743c447d75413736e2dc2ca470d3d61d23afbf5cb28b 77a2ffeda350f36dbf1a9b4bbffbace09db1578d25f14824810434b6e937dad7e4d7b49ca9ebf0ba 4a84959e6ac7d57dd572ae9d5ed439f79e16d4e44b28354db6c159ad834240236e4a6cb07a2fa1c4 4b9b5e7c7f3db7d6ae5cb53a9c67a52a70ef20a27caea7c96bfd020dda647b640486c3283543d59e 3c9b79ef222d1f23ed631662fad0f6fe663c74fbdd45ed20cbf3f250d5e102ef2c80fa46b3f81ad3 62ca15255937c15f20300ff0f68172bbbb1d6ed8bc625c6bd74772526f3779243db7e6154e782b90 614a7d0fb2b2f5c5dd97700962d1e06755d691176d80e8199a696df075ea21c3c1a467af56895615 be8e512d1ccc176fea2fcba1e7f4e3694adfeaedcff1175f93b5f4d43ab0cc9489ddd7c4ae54ec93 337106790302f5a90bdc1242abfe61a1787f1a0656b5772feb835e0737c7ec3ab25e455a33e94b09 17a4c9a4617f074560a3fdcf4f5f8008ecf6d1a59cc877bdeaa7d425b1a3e27ca803af5ebaabcce3 926ed3e52f664fef619caa60f156717be31a6576906197c19ffd8d57af80aa7debb77a0d819d71de acc29d945aa73846cb2791072893fc1afcb5328745c6f93218f987be87e91f7c01f0e7ae04fb9dd9 a56709db0224acdc417d673fab3b5fa236ea90c7cd2576cac84031e3e8edfae62b8c3a1d2dd7ec55 279107f5f41ce6cff066ccd6404ebf38fcb7c42138c097adadc3df46c3a6bac7a0ee17cbf6e251eb 473578be4f57b157529ef978678834374cf8a2d4d6c95abee593c371196d2ba5bab9dc051f42f7b8 53d8c6a4c2194bb37916cc83798fffd855d9eb37363fb6f67666cb8e441173b568443d996ed5a5b4 45fbaa4139f9985ebf87d718dd30b80dd4b2b01197e6ad10dc9d36d2fbf358869d817feaf757434f 91e0a1a3dfd7c5b90b4c8ba936ab8606cf8fa0696ace3bbf5e0ed3597b55a29d952ad1fcbbec39ce 9e5aa6976f33689b8bcdc85eb274ea2845aba6acc286ef28b7826a0f218fd5cbe1e8e062eb67407d b2a567dacc27f6fe58ec1a21f38be7e0ba8b9acfe964132ccde56f958b36341e3bebbce6ae3ae6d7 7d83d6db2f7b2f9f00ce3d494bf3652d71b772ab24b8a84537fbbe98895e9f15a53f9b3f50261208 4f53ecb5ce766acc1f16120fce96ce21b576b9ebb376e375aed9d3eae80739a82913f41cc5f53357 ee3c4f887bc056eedfb90bfa48bccdfc42f44a4e76c42bc6b757889a6d5b4be775af3dadb68a6eb8 9a357db9de99b8392592aae0bc02f11efef1bb5e157f76e37d622e7c773ffa8d95eadd03f7b20a6c 62f8ed19cf1bdfe0e6497f78f8ede3e1a965e584cb505e252f7cb433349ce9f3bd44c65daaea7302 1a6810f412bcc7e595468da9b7d2e713dd520fafe33634e1b16b2a72b262972ef0e33741b1526676 7b586670aee8b70364671aeea0609631f4f688944f3d56739ec3ab856bf43e018f75d9806f8f4482 3a19116de5f06655da7640c9976b1e26c2e8e2f0f33d8f6c4b0b32d00e8bb60add96bf78e3b9c861 d8819232a33d76d74475cde63415d1f2c558f55f091b74c228f69660643da6cb11ceed466238da64 735725ad3fc32cb46e5f8c8ca9bae1bba0b9e3a96fb7510d68bdc7e953f1e09f47e9bafafed12873 bcf38e1475ed2c62234d40a7a6b82472a9fd59121e1f2beeab74aebb215aabc98319a67b94785d28 fda0b555b7c7f34fa2bc0eb486b80e1828ad13de2be07dacd1fb8b74f34df1f5d77d000d23f0f0fb 18b53b177467af3fcec6bad529e9f4a7df7785e797178efb0f675cb1e6da7d4493539348ccb2b521 cabfc41112c0838fa43b9f5399b03534509c423d2f4de620980e9c7d19f92bef318be75f47677bb3 7f79e816838c3f7fc0cc74dc4d6b9763688ebf9ce74c18fa1093cc83602481f6aa9c0bd61955defa e7b6983adcfbdcb24710f2f7fd980ee16c5b9cede477436c569ecfaf1b57e4b757abd81ff8bad315 cf1b3c7e00ca8c7b86615f3c35f366d616b1ba39a4cb36ef57bc09b5d0ed2e093bccbb37387a73b5 3b749ea3918b914687f25ed566e47557c9cc5f3ceeef30e0e2871995066ddd6c29dfb0645d1656a7 9860427914ff86fd0247593bf6f9d077c97b43ddf9df9014dd18ef2808d8db517e3ef209433a5ceb b6ba9782381fdc0473503f56e42ab337a3fef8cf124eb34597596b3b224c2727dfb126cb0e4617b3 69d059e18cc702c3ffeb7799b0ca67dd61f130c6ed774fafcd6b518ad6ae897957e74f7e59f1d138 55e723db54e62479386076f839d9bcdb8ccd0c69be86fb2075cb863e8840c4ae2e4749e7f4aaaccc 3ad3bcb5578faf959e3be7dfa1598e317499fe7ee3c49be365e0a0caee85d1f78313ae5bb6f477c8 ab86fb9e27cfb65f8fd3c0af8603f5d4caaecaa4d1df0562f30e7339d61c6bd7cdf21150474124a6 e4f96baa37621da8e5110d6d7ce0a700e3a0eb04f7d1fbfc694e3bf3bad61a466b9fbb3733293df1 927e7a8f4c5fb18a26edf8c6d19c8e3e15bf7f58bdd1ca54afdbdb7aa00778b704b914605e0d29f4 258fb900a82afe70bfad7a07cf997e7f391b8e83ddc8ea6765d8db95a90f9189f1dea9e8c3c0b7d9 3f588fbac78d3be19fbc8b86166b9c371c610705d8753ba50922d52c69e74040cf705f637d493733 93758efb69ee0d4afb0f8a687ae8ee2ec66f8ec04f67eba9630fcfa2fb415f1d673c1c4d95e9e2c6 797045525c042717ec77b09a78cdb3ccb91b15b912f162917bc10129bcd5ce9bc1fabd2c7b6fb41c d8e3e9b066c836b009ec81dc7374bc3d13c7e7e14f9d27a82d664db794830f1a8c73df9fb9b8eabe 18b78d5278a0f9f199a0fc46f210ed47f395c5d5186bda0322261499ee50d11fe9e41db1bc1ab0b3 89df0ff5ddf5e012c4fe418402d00bbac2e2ea293afe5f2d2193a289ffc4beb9b5b49f63a3cc8a40 5238eb9d9d57e89368e4c7736cbeb4c271d7df1375ff0c6711d44124373d441794e38fa7e0f0dacd bd53dd35ec8cd5ca1db1aa75ecea57de2a60ce00e9943affeec63eea81c3a641558cef31a93a07f5 f5219c1da047d814bb7830cb7dfeb84cf8479edd4d1aa3d3de4d3363622667dba6100db1af249491 f7522d8ed5755401676973605d49037c514418094316b981c48f14fe79b9ec19b292ba34cfaf26a5 3687bd7b38b705b2dd994337ed540860d80e3eef16617d56463fa75ec197dd79383671426bb07f1a c1212f78486fbbb273d5182138b44a03f54952a491ec222a1839e39febe4f368179bb3cee119e82a 4e35fd5a9cda70a15941bbb2993640876d390b2ad3836852ce4d9c5e8cad91cb4dfd37c9d6db35f5 a4db8ac5f4fccdf751616eb79be1c09f96e56fd6170ec7aaf79a0b41a3ffb239556d5440b35933f1 5af301e9133c96a9ca451dbaea9d3df8ac745cf192356fb9fd87d5f399d91e6d6afb8fe57ee8b5e5 87f2e9daa06792ee45d262ec7f47226ce47bb4ff17db11d89b347c5d9ac4c20f72f8b11b01de52ca 406665c5a42f1d21c77bbb598653d3aee6b74a57cf0f747902bd5a50d79746878e57eaf7df2ae05b ad708af40e9e1ccfa63c1c2c83104000db0bc33bd4546e4334b0169faff729557ea9a186423403ff a1b547fe509654435e3a7252792c0cf7513201c9b5d26d0cd038efa12ddd62fac6611a7eec5ed30b 0fcd334e69ed7ef024dc868f6da2030c3d7d39004f95926b10afa17298de8594a4fcfeffb66b0d2e b87b006de3cd929e7bc8bb8735d92e588f84772879b9deec378c319a040f6b86f8a6732635296efe d1a4ee9e486d79960bc57a3354a2095c242aec9fe8eaf63ad5b6d955885b5f8d45e7ebffcd49a622 5cf5881bd97f3c646e7e1df87b6192b14168edcfeaadabdbb8afc747a597b022964b7a445a787427 a407b15ea30bd3df1fbb517dc3e4487d94a876510444e8c7c440fdb9133d159271ac72c90f72086e 1242c67a051ca3d40132ea116582854aaf38dc4d8763f47887597b713b2b21bfae5da07a59dbb9f1 f5d589f63d5f6f7b2b2eb102ebb98ea8974cb07551ded922ba71c3f8fa0688155f611d096b7cfe6b 7717eb3b22bc5b7337a69f8360fff0a66a585ba04e94a94c787e88550121af6fe7266956c8cb7b98 3a0c9a73b7bdbdd8c17ec62dd1e7ae25bb2f113c07fdeaa705a9073bf1a67db9f9f7acd0569b9844 3f2ce06df6e02484c56ab3756e7f7defcf4c9960501a8f89dda7d6f0f6933cfd035c6207f1cb38eb 57f53b1c6803f2abb6f4d62c7854a23c386ca417ff9825e320de1fca817b69f4a82546400152f71b 015e7bff8234069effce735996d8bfc021b850a7bc29f99942f241c28fdc768d3d9d23738ab402fb 5d1bb656ec2d0a4753f51c5462ef4bec9242090ed21af05fe71185f43a0d3cc0d85dfdcfdcced5bf a8061b8975bafda6be0353f05d7e7514e358ed68ed008886e53f5c7d1e47e4a687f9db3f24814120 9f85b53cc7fd2b75f3a0e9e6560bb03e0376e7d79812f67a50c8a256801dc697eaf4cdfeb6d5e036 61d20e78327138467e1dc44689dd8bf1558c1a30f86d55ad79f5ed44ce6d3a68af62baabe44051ce ce6fca63afa5d257d3ab5b30b57aa394fc8eb2d810e4851cdfa676de189457aee5753ffb281d3b21 6801c09fff95b671d6b5905f1b9c30adaf7d7d7dbfd55271d284e8135f99990d09cce28bfc5471e6 1971d69ceda3516fd67061fbf41838e9a2790bc71d1a6e1b9dc83533253353628b75d891009b964b d37e7c7a378ecdb2dbd76d673efce505a2d1f90935769fabe44c39400fa74a6d02ee945ae4010fab 489ecda223987776e940dd6b37be01f288de90c7953330bed368fa9815b83c6de6aec023f3707100 2af024ebc81ee6371f219446ad362db57ee96ef745e5587c771c8b8d8cd5c88391de2e3a5cd6fb66 057a141edfe592703f1ec78dd7acd7f27aac03878dd37c07812090fb550b0ce3ed109d08edb1bcf4 d778ad11574fe49a9eac88bedf55de97a8542c7fe555dca85749dffac3f421d56dede123dd99f97a f79a06b391ebb6e1608485bd53b28b09eee9b3d63e5f8410747f44adf901245fc81d08fe829d1dfe 5dfaee0fed4450507bdae560032122d4da6e027fd6e9ffd2763163c49ec095924f34ae2b40346e1e 3cba5b6e9fc2d381bc845dc25471e39e8e83cff6710dce6efd003f3ff237600e9bbf5f32157c01b4 895b1b18744bfdb957861840b9a10a8d8f2fddc4fd3cf0ebb9fa2bac6863415f67e4f44c202daebb 30e5943ac5f7f276565f0679e1d4d7df6d3f037a08bbecec21954e12a1db5f31d10f40f774a42a9f b33fbcd246bb617e308560354a84b5ef41e1753eb047de63184579f7971d1468604c68c53583bac5 678f3170f487fba05e19caa604522540197a98d3a4b34e46e50b82f44bbbcc8e99b6161d5c09005b 08de73cd4423baf32e10b0fd68c898815fbf64f782ac35fb6ca76d65fcec5758493ec632470fafc5 d33e7e9e694c966f6be802f04357d87ecbe15e362c01233bb18deed44aa7219543066aa889bddd3f 8b543ae94d8205e6809311f53ceec4f816397df9ae1b64ec38825d5d06bbf9d4f83f5096155887c3 6b243b60bf98db3aafa8745542a1e6a82a4cdd7e91fb49b9119958a534e87ac05443234f382d6124 d99aded095a9509ea4230133ba4bef9cf0cd0c1d3d11066264c7bbf0b49224dbdbafbc4a7015def1 9e06fc88ee02526f54e0fbdc07a8663d1c9eb90db817c74f9feb22666616bac10e93e52ce0a00e9b a2f7fbbed99bd74a41856f2d62df824f5859c5507fed7ebf51adfce66076776dfbbdf9f5f73cc36a 225c04b8326c846bf5dc4c675bb7c3fc611a2ba49c344808bac913d4c9b782fc73bf45d9a0df68d4 5ac239c0cb1a1f2af0cb03df5e51f5b7c9195332f1c5a7f490bc4997dd6daa31df63d9fa7697bf46 656582d612032c27aaa808435857e25e61d1c1a646964142b29ac828738ea5ceb39708b7bd424a1f 545e6adbc21fd2d5b5c6de6f8a7a6232426d6d1dc4609c4d6850ef3dc25cc6bfa140f599582fa1fa f3020de73e7938c53d1f4a9522d924e532f107e4c2a22f07e18233bf4b96370f4dfea25512a3214c c4fcfcf74c9a9c71b16286387ce796336759da412fbd5d9739aa4fdb8285865421fa2f2f2cbcd801 cbdc0fd9c88bb71829dec22b3b7fd8e1a6cd0fa3321322d5baeb25e3ab057e664963581f3f3cf7ca c5bc338427d1a9236fbcfd63b391ec7db76c366c4ff54e5d6faa8430b812f03f4aed5d5d11d06fe1 6d482c9cef6fccc7072bf9d81e32a74607290fdca876de3f0568fab0dd412710c30000bd3687396b f91c695970836e945a20d78061fc573d5098036406fd8ed3582ea6a07fbfca07b698affb5179f477 e719391a8a55f1f14b7419feb6518e0d0f4765df85387e5bef56236a6f613a5bb16222df929d1088 55d2e6ff424ed6ba575addb21334b93dd1ddb915c52a677cd1ec487ce0217fccc69d276989ada8d5 cbebce54cd8e12add6b51fbf31917a53681c262c1556ba2ad11ad040105a3d9aedd90ad817b0f020 18c093bb759ac0c36d1fabda828f807e27b9cde1543b0da211d1eb03f8af1df96203dedacf287219 884e732be9454cc7ceb931f87d8ccc4fb6b1594d2c5a6e5cc2487dcc2fb035d4971a6de2ebb531dd 8dda4ed7410e487a30187b0300864aaeb7a510373d468526f255bb37014ccfcbf147af3dbcbd318d b236f7a661d184fbcd99b52baf7e841d9f372aa96d5c262b5741583d85aceca647a5f356c9f5fdd7 7e0c868c71ae42552d2742a46d8f2b7b8be62cd178f8f08aaee78b87bd2868c2ee9c7204d98ee08f 9bce375a9b53aeb6ffae2bb6cd44e00fdc2a9d1952d7769f6c67bf9663567b3a0cc0d9a1b8745657 6f678ebbd1151f96d389bb7abc6ecea2c3b7e3d74b853c705c36da6e6d8c599fe7f3e4b54c94531e 01ba904eb991787ae520e933b29a50fb76f003b7de48fab3dbb3b25391cfb4b2f7c1de3212e95205 f3c1c9f111bc996ea9fd1ae2a0f61744b8206197b6baa0ee0d0e9ac252d068756ce34a780cde6b74 debf96ceae239112113706d930a9a3cfbdf89ce0d7ffeb77cbb32446b26c2b6f1b4b5b32fa663b6a a0c3bef6088894cacdb31362d281b04cf06b35a07d310ef867a8f2e74795f02279c267550cd949d7 d6b2ab16d6689c449fa6a87cc913c56e85e629faa6bb5f2030ccc3e78a7762ff8fc3bbeb8553e5bb 78949e1ae31ed1d9eb6225bb71e68bee609de62e5fcb702a791242607c8aef6b2fcd3b1dd7541472 cb18f65e9085a28f4b83a01f4288ee5781197ed63baf3e59ff5ae2a5f7f194591e78c0555a693d94 0c3ae676c8240c8b4738aeb75aa0e41c26950b96ec660b8b64ef88d7e1483f7c5e67907c29e88fe1 f068d7140b76aa5e4f056fce578faaa8f3cbb77ec37ba4cd2ec51f1c6c0eb5dbdc426623d94543fb 09bfd113ece484fad0283b1bb958be78e967f023eb27468394681b0f4c3a895f66504fd2d6827e68 d6a2bcd8d801e1fa7fdfa42f38614d75dd33e82f827d21e196b49fffc29afa800f1dc3349c8a8d65 93975e8c948d70a035c3feacb9a5b91dd960d3629647b7d49cf00ef7795d91e6311c79ad6344c907 9a8e1cf22197dd281204f5959f1af2e1f88cdc0d63970ca054e75a1acfff5ae23df87df3acacc48f b1389ef4bc017f6cb94ad9b6fc4fe59105ac0f1f14accc0ff479faac06953902696af57c144c4cce fce5ea3b368ef6ebd6e47befd4ef10bbd02e468d25a2b1e5a69f8ca9459b6a19bf5e0ebb1b80b368 a839b432513bacacd59c4a7874494bc7b8dead858ec859082c61ca7c2f18024bee6918289fc5ce2d 359613df60bd7a1a85ed61fb3bcf655de4d659c2fb87480d9e87930079dd52f4e199e067693cc54f 9bc49b54c37c86b66d656c0d103f697483e0f46524c6a90eede6a0f9ce1e1510911f7206c875dba0 5272d189b5814b7598d7a25f89f582ff98c3cba786c58bc5231ccef67d67dd6ad7bcb178fe19b498 f76fa223ac2f8196bdf583cccdd29fd25deaf212a57aeae1c985ee086c1e49aa567c8dd172faf5e6 fcf002c33758b6b727bb67b178696c4946aba4acddc1d3a6eda5d1266357d0e6bda3e19086f03301 fabbd9ee8c8b50bbb83636d4504bf4095bfc0a33bf744e2c77dea81db5eddfa999416f70572f40fd 6430f2a36c0dfbf53bb79f0e27e6cc1b264e5919acc98a8abf6dbbf24798e6e3ad04db0890bad6bc 60fed7080c4d3595d136b538a756326bb37347ea972dc3c61a9399e50de1909e41cfdc21a255e170 ab79150d7473eada1f9ef5b4433a730ef9e6e0e4d3ada1db77fba2e2c81fb801bba06c7cdeed07e7 c2c92fd7e9faa01d58fb39df271ed594f26a909fb895da5187c7cbf2c6dbd666943a0e948151d456 9aaffa5f5e4f2ea39e046194edb3e4776b2a9f559f0e8ecb8a4f0ec3b29d420884c6d364ef737940 b9cf022cff729d8c93bbe173327a68d5279c2bd363bb1c1ae8e7adbf5a831ad750815eb065ccb705 3d872be27c9e4b41abbb9c3ad9b1d0e1dd6771f38fdd92a798567767086c9788b760a2685df4b617 d79ebd8f9661f9c7a48dc904716821176be1b4c41c6d78187268e7cfbb07137094b8631f99d8fbd4 be652a6cfff92356e514bd7bf493174b1c74a55a8ed94770a8c5f6dff998d32941fdd136a61bde07 57d711c78d327c6fe23fc811746943f4ec013bd65b5a5e6f5767f5c02b5af15a586decbb12a133d8 7b0edb258a99b1a94e65efab6ff8dabe212ebf6d8b22abb300c7baaed59fd26bb9ffb6de6e035eaf 65b13f5ea8bd757071075df417085afb5d7baa8fb3d8f67810efe1a341696e7e8ddbd7a7ba8d1134 e692cce9b35261bb06e06a5d571ce9f4c7fd3a9cfdb90be2a08519456b5a76ab3438a0ca609db3f4 46ffe241f583d6b04710ecb44baddfc7f8e6790f98f3caf66bf2ae4b58a7fbfbd61e547a4f8bef23 757bd9a5dd96fb2edeb634cc73e7da1d7ef1cee2db733ae941f094a2d685dee2dcf66a29ff355b3e d2574f8d8de0f0332ab6fc3b80f1a717f46b547636f5e8640f0264dc3c8fb58aab3fb08a8bb1cd61 23a3cab14711f6c7bb4003dc44703af05299714cf2aaca6d0cfe2ebc61ed8159eee5396fd1538cf2 a678d6b65f5f7289778687817786d63377bd3ff2bfcc2de114ac4f7e76357dd6eff2aa1d940fc1d2 d85d4de9f24755e2b594066accedac2db0019b8bc618f7bf8f01e5742beea431ec53357f2e56f75e a4aabf468a8912839e333150d6b84987846723494d7fe96e7332776eadd234a8877ef1f97bc4dc2e c35312e7fe2ced28bbd611216009d40e4165d0f5b5c3ce8555741e3d13b35bc98df1198a7831ef35 62c67bfec516f2ad3707d05b0bbff599e9b0bb1edcc87bd8ffc2ca6e26563ca1366cebecea688a6b 746f074fd386c57407a672abd6048365847bf422a73feabedc5905437ff7c418549d1a130a4a8379 2f06c10bbc1bdadbe3f0ee1b37089557974753f1c431e867dfd14fd288abcc81b1e685b3c41f5793 3b295f176363ae1147ff610a27c4bcfc2131b6cfd503d7b13d6d5c66280d3b22b0a7dcfc83884ed3 ae3ee9e913af3f01733a6e6e285392fa23bfa2727fd70f1e0abb19a0ff53aa4573df86b879197385 e7b6fee767e8830c8233c1943b58dfcd266c831ded66a065ad52ceab7d960e497fca3b7bfaee1ebd ddfbe323ddbee9ba243466fd6d1c37b5cab2e8da6d4178395e54f8c27072fa0502fb7371ba2eb8b3 0d5a6e56ce4e5e1559f7fa56284c5c86be1b3b8aef7da777154ae7a7a1172f9a804df4c14d7b3828 deeea9c4080e9a87355611f3c0ab5feab0f3124a5b922101c053dc9ae5d5a7dc6fa610592c6aaeb7 d9f2912f6f3ea03accfb0bff41ca551b6f7b81a0b893d05f0dc66f07fc74029aa072d5eff475d1c5 df711dd3c04fe63bfbd1c48b13448416c7e0e9bbf7616e55da26d2d6ac2b1f0e87daaf01c6e637d9 9345ef4012827967fd071a6d9084c3472508b146ea0ed3238d1ca0cb3d28b972cbafedc3a12ae303 221ef5cab9e54e4441401e791eadb27ecb4e073b9cae7a8722dc659ceeacf3718429fe45f859dae9 423b1e0be41074ba20970066ec63dcc598be385fb2a9f4bc73545cbb451d66bca1f78a1d1df5c888 e12a3e822a1d9dfa3e4fe161bfabc1da6bf0b4d8f1e512860de425775c49975bd8e8147ec5ee6fb8 9c1bbc5a4f957d9da0b0f3aa29cd1e28f8ba33e4c090a61e27b4d4d8dbe66dc20221f0758660f7b2 be3b69052a828b11dd450d99ecb51e883682a528f7182f971883e6c046101697049f84706e41b5d9 4f3627f03b93333c71cdbd43aad63b2836e39a5c4ec4cc104b3ee59f96b6c0d13e5998bed8d6fd75 6f3f6bfee13edb5a8268c53f95e91045fe88b0d3e26e4c409c4e01f8aea965f74c68536f779ad1c2 43767e41da06c679ead7a1f39569b5b8ab7d1ac28a4f3293233ed1ef94b30142dfefc09d2f7c596c 3eeef9da1d07e8f8b26e8f1d3c712efd1cf704d6bcb1cf8d4cbafc69f1f6b2c14b6e6afd55e2ce2e 53da7b26d8ef7f8352bb52d563537be5f75a280d51415cf2ae1b73eb4ace82130a66f8f5de7642b8 cf1d1c32759123fcd2915c790ab0ebe173275afb35affaf03e314b21b5fe13f19b1cf8f257c93468 5bfe2e0c5613f397e570db83e9915d73836de02e7a5d77a74fd266db796c83060af43d9dab2e51a1 5473fc5d5e0e7dfa93f07fb07352f8436a653b6975f914e270378cca4bbdee9a7940d28fcb080e4d 289dfd01e90b814ff6f5df8e956066c07d6f48bc10a451c11a0152805b3fd6f4ab485da38398ecdb 7e7a08f04eab546fdde5b2d0692705333a12bd83a5abb3da9f1d467b3347c6d3a362140dc28fdc73 6351bfabab85bd599340725588dff27a6e546123f9bdbfe909d08b548a5fdbbe861cfaab3f8f9ee7 18d2067123db3dbc8882c62eb4ca4b8a4d0fc87ad87f162bb1e64f6255dd57f7b1b1198bcc918d44 bd165446514f8f20220726b0718ba99f4a7c785a5277e4d5b87ced328e8e43adb23d80dab0b05cd2 de6ca2c18b7439be77b60dadcffdfddbbb3fa19a4d6a6ace5478147a0a0a622d0a956d763fdf85b5 fb218441265c3a1fa08f04f77d4e08cb03f27f0bd60755d5307947293344c4d0ba49fc23ac1fab24 3115cb07fb2e2d1fc13c28984683dfdf5d68b01904c37aeb0616015cf3a46e6006c7630473b0dcc5 9d7639f9fbcbbb874b81db93e43c527c111836f923ec9802dc666e7f27f081786a29b000b0b867f4 9bd5c0019677a11bfbb2fb08f9bf9f52fb15137e3cc92306f3518036a23931eced7a9e2bdef5a05c 6d0a0dae9214de5ad44a015e3bc3e0fd25383e769eff88873fd9732fae7c8e01bf8b51a97f61f132 554ade9e9f80dfdc5faacf16d66e8d5cdf1bde66fed11733d8adfaa8ef18801334ee612ad8c2a01b 821b10f60b98b5196d7ce782c28172bf970f7bc424557ff59b40d62b553f8a4eb5860e6090ff6685 b67f9c064d08e87c3c7f0eeb448fedbd506658a9e052e611c19f831608b232f56b0a596fc6e951c1 0e8d7885be758c3e7793da889f83c7bf3f5802623151fac97f1392ecfd022ddb03f81876b0e57a4e e9c715a02c456b9bf6f92b87ed0f16ac632bd78bbf8b3202ef210b3677d96c1cf59570565f1f1b4f 272801bd6c027d04a6c7dc03754f3fe154edb2190955d6133d8ffe8b32c4e73f2cdc38dac3aeb919 8da9a8e3653924b532d4a9d383209c696ac42e3de1add336314bcbf7794c0971453546f6ad122ff8 eb6f435e0159e4d4b6a3f8d52a10acb63bd92fa84e86e3ab9a824d2ef9b525b8b3891d24b1a9717f f77b15cdb35e99c5dde9784412bb55687d2fa75be4d70018addbd0c0612c3d0afb834481fa4b6de7 f6e20a1222936b835d567791cd4de561ac1f06282544e78a53ad869fc867855f851d47b6e39973fc c30361177f4d1079ba8add25dde54200a53aa0dfebd3def83168c6c01aff3291599dbbd477748ba2 f5022451c3d0dde2fe95c2517fd847096237f1a807e885653075a16d7b5cf2d650f5bf6aefec3db8 b30554bf7ae94965a390d826544bade85e517edae1c6ed1cf0fa1fbdf06e66b51e02f9e80fed64bd 8b0f1d322ac8bbcb269813b792dfe65e6ea4ca509b714e22e0afe6fc2902c86585ac11ff8b5efeac b1c942f282a96843a6137ff019dd8391223ad0d52995ff38380107de7339ce642255786d9546726f 7d65feb8ed5c9216d27aa2e665ad2927172d54b67ec336ea5b3ba00fabed527fccd29a75e5c29f6a 2ff23aef71db2a79be28810b31a45fa3a16cf42f79fb5876dbfafd32f4d41d617ed4442e4e3c2fad 31839e763b46cf54aa44fd9a0daccbbd9b38c0f3aca5a5f9e5a62e9682222c7737da014215d64354 fc1d5a7b7df566eda27b3a9b7422ec34e810fbb4896bbe0d95e4b6b9a5915283706aac2bc0eb331b a8c2de7f95808db92adf87c249791ff552033b58a71d48fe7dbb25c115299b3aa5ad1ae9dca1fec0 df02f45f1956e9774ddb828a5bbc799b57279a5c18f6c09f14bb3a0c03b74aad18b173a84aed3ade 5db91171779461945469b8fc203ce8fa28cc5216bb8df6e5daf7ee911ed1336b887bef512afafdc1 20e6a6c5fef4abdfb00036f053a2ba95ce23a0c9496ac3f73dda1b69caac7fc6afcdefc98fa67f74 e78d0146b47271311a487f7e76b13fa6961c219ff07309cb02508f2a523ae811a1ecb8903c549d3b 75537742509cab3f516dc3a326fb86fd3a4301b554c8e6ea9e851ebb9e7492c579cab394da5daa9b 6a10c41d63b7168f97679bfd82c231326b154f5dee51163f7a9e1bb6a4cdcd1a2754297c1d22b0b3 ebdc325a1bb48fe699607f8120ab4946cad79fbb96c422bd7b623cebc11fb299d7a9f9b2ae478b45 bf69a09db8d30867e33f84889544b5a20e8f9e210d41916ae4572de766fa9fb91d71991390c2081f e707db9d9c48ad3d2ebad66c59fca03af6a4f2b33902dab9abb4d96d049adb411b74a7997c5aed72 737cddc34ad884c13f085e2e8b07c0de6ae7b4b336224c972808695f4ddbad1f6d6317bc91f673f3 75d475b21537ef26e28a9beb5d5315f9d7762d1b64b9a6ec07d4c480f3e948839ede87053bc0d37c f3d4d624b3258d5d7d1071eac863f54776c783e0a4da7b639918b9d85b9c43a38f5578f33138c072 4a950011939e6f1bae6f27dafeb0f59bab1bf65f54db7ee86dcb7b05269246d5b2575b37096ef0ec 0d9dd6fedd779ad5952c3ef484554007de39bbe16caac8e5f4d49a6a77c075b98a6b20880b62e78a 9a7902cdcb0e70045efe755ecdbd735eb9f3149afc173c3f4eeb5beff1cdcfd2367d47c2c46c7ffd d230f2d409a4f59bbdfb16f11be1c5308f013f438ab84ffab68f442dddfd9eec94e795d06ead3602 d5c44939eb5c06c187f80e65424c4a2ddb689582a88cfd98b46ec1fd01762aa03f6096de16f69465 8f3e0d8d8884bde618dbe5a686ae162b36660f562e8e7aae27c83d148b8415ba525e4770ddf45be3 4bd866476fd3592300b27de352e00dbc3bfd5e27900dd1ec8f49a7abc179c6a3bad1925b9dab9d28 28fa6a7b67376931d6b31edd0e0ca3fff9d12176c846db3070e62ddb151b2bef8b1d387ec1572503 b49b0d1d2cde89b413d0d034d3a0c51f73bca6acb60fc20614ffe76cc87c3873f527da21ff281b9a c13e3e5dda3e8d55d496daa7adf110c165aace913ad03a25edd10dbda8a9e12f8d77555e334ef31d 1a7f5119b169e419a0bb40a8598724cedcdd2e1eb96ffd92a9cd77eba7d7a9fc91cf54e3e411ab7d 8138d0461ea9f142b15e18db6ac19888df0d492f63346b7f733bf6d70e3ff06af7f25c721d1ca4e9 2d44cd5e635318f7f0c6281ccaada5774cf62c4c086edab5e8920c545afd467eed105e6ecd7b6b0d a087c11f1a0233ecede227beeb7c069581cda3cc475a7daf37d5544a17fb5b826a4aaf5443b8bdf7 109c22abadf465009648412c3ddc8e80aa36fdadcce0cf622d79a37577234038fecb0b1887aabef7 880446dbdc268d241d614d4f77b1ab1ab2fe9e3e4d0ee7bf8ba937ccfa7b07a26710fbf8e51b5438 337a9f382234be0573bda90947f2022be7efca082c069065267eb19c93cfdd008d935f90d6ced7d7 9ea4ed1ae05ff9c2b0166c2620c8748cfb1bb7cf73ed55313714bcb188e6b8b0129f5782952a77e4 8fec91cbbd32342f1b7a30f09490a9b985918da40e7a8d17a320a2fcad633ef19d7d9c00bf398294 6d36517eec6665252eadf3a4562af5dae609f98320f1508f4e4af0d4b4a441920d695084e3d2e366 b9ad4d8850dc681774470bdcdc429d8fbe77e79c68d56e88b5fefc3144a3e59f64de3518fb4c3c7e 1497dafef1953f2fa7492e2fdfb5c63c0c49736a8375bf9a9035bbd6472bd263994d0ce7ae1ee5c3 1269283c781e9be73fdccd6ae59ea0fd21a3929d92ba4dc06ee89af2e73272b36bf0803ed3eed791 17f62fa5aadeb3c353fbde7bb4babf74697d0940656167e2b63e7cdf3666774f3e297f30b899c3d7 b66cef7a49bfb1b33fc73f0c2fbc3d1e1c0916f0941afa392af1aaf3c7b6e55ab3d63371a98ceb61 6b3868bd97f92ffd6085403f35af652222aa00d273002e7a3b83e5a006b33dd577cf0205b41fdc63 af612f6e61d7c559a05af1742970839e6b5f4dcbd4aff66d4771be0b3bdbd5ab64adea01d8b86a81 eedec5efc39d3726bf7763aa8495bb52d9adcb14098eda0e59b9b9c33743a9d74a036af5d3b0f0ca a7c1c4d837250d7fd121ebf9e52bea5486cf3fd0de52aa3e34961ae24e6dedd4614590fce95835e5 03edddf9d38055fd5ef2ff796a077f445348d5c9fde88e52167cc3cdc6eb81b8bf96ce8eabd89bcc b8cc6d3a2aaf14422a2e84d60686921ebab5ef4b39363ad796a2145ef0a053c020320dc3afaf0116 186475688fc41c843b77fd27d31694f0cf4e889b2aa0f255c18ebdf2286f5f187cc0f72cb71f65f7 a0a15117806cbe3fc639ec77af98b90c48bdf189b17250d884ef22fe48d65f2e5d15aa1761e7c4d1 b62d71cd3c952e35fde0e250ff77378ca16ef7ca4608fb5e9959d1d822bbcff46fc45c7d5c6a4c20 62bf88ed48b46716ae0ede8a666515b932757d9b2d716f6ebc0fb6aaf246768eb29d41e4355660fd 59ed5ddd6567de47b6ed7a6aeda2daffdd44facb4675c2a83c95f1f05c35a969a72c4edf5aae05a3 e466652f1c65ea4fed693858843bf82de862b33677b086d721e78174db86920b3375c1dbd5d0ded5 654d8ef5b06d40c454338a3f1fc711e2e2473c4c3fb25a965d4aab6411659e5d9b96664e383be1c8 6391e0ce9188426f2e5423cd7ecba005dac4439b5489a108bce5995d823f1fa3af3503fa784828fb b530606b4f6e9ed8a22a345da859745c51bbfed276d0ae7eb9787f4e06579c677dda7e4f461d676d 77ff4239e4c8ec9a2bc92e3f4a1726843a3d72586e8cdce3e789d993305b346ae9c7f332801e78ec ae54555f8c8b79872fab2b97f75915f2c672e65dd7abdf9a1d5d199f343a5d6e3f7e69f036add2bc ca61737e4ef9a84765ce7e8ef4a16f7959f20df53b6c6f0d136cf706b769308b86bedadb6a4d3640 6fedc07a171543f06f3dd2ade36480ec16a4dd84a567a3516ffd8ac5fec9d020f72d92ff683bcf76 4595a50dff1673c49c7396202082201904459020a05fde5fff827bce4e6766ce5a13be3c2cd06b71 5355dd74154d5b9c9135a58a8a76839b18f3c66b30af16093415d9677ef40eedd64283ccdd74d01f aef069255be1b7c00661b3dbd2163f768bc5c2e07e410a3da7095148ef3c424b6a72b8cbfb17c85d bf1704ec2ca64c6132b8dd02b8d53413b5d662d59f2f88110dabfc7858544ba5d64a731c0d41a787 cab4139ac99152b0b26076ef86fd219f32a759ba9801796af46836f94b7a8ec3751682bb601ed8de bbec6ac8c1dc7b32eca5d4cbdfaa456f3b64d8cc264bac4ac330dbcccc46cffd759bbd95814eaeb3 582e3a9d520b4ce95cb956c35971b5d4812b34e802c3528268f636d7691b87252f6d4ff0e9189b3f 95f27e3d1c37723d652cbd5f5ddcd0477c835070adf19467ddd5cbe72bdb07b4990008451d3667bf b1878eb38952a8d57b32289cedf572200cc4e174a7e1ab70e1ce571e28b16d318fc19b4caf5edf6c 93e2ba56b4b5f4b68d8957b0af2971e251aa869c12e5aae5045ce93c9a93acdb6d6d2e85c56259b3 f3dbded8e2a0eda6ce64573eb56835cce149044baf84bcb1da9d1740228a00ba0455045f389a2aac 3a280a5d00394a33160f7c9890cd3b94aea4e25e60b9bed4ea6d2855d843e384db5faf33f353add0 a71310d3919f5b6650434ac35a6612f5978f2c7416c1ed58dad32fd804e687b9b10a1ebd5c196ec2 92fdbc2d9f21756cb0b7a1029fdc4d7ad371a11240f6f771c21e659720091e1339ad4054333998ac 16e7b33c5a4e0ce459e7b58b8634cd05e963fd76b7d1cc21d6f4c8aeccfe80a8be46b48aac134378 dbdb3ab5d2ac3ea922a9ec9c80b071aa372a3e67fe20eb84c75df65c8ae7aa77ae457c3a32266a1f 799425bcde07bbc8543f4e42c4ae6f0fe572dfda2f73ac26ec5269ee912fdd7b950dc6d312dc21d2 667f316094b15c0bf7305a1fcc9a4f3e2dcd00640bc35246052a6cb1442dac821cf70248a94023c5 7c797e585f6f71a7abef8b43e144f6a7b9ca99829265fcd281d3cc729edc5f45689b6d9feaed7ce9 b82c1f881c9ce96fc0f224ccf1eb687c83c3ce1aa5f27bfe406c5f07bebeedd747fb9e01b371a42d fa4fbf031658cc68caf76467693f6002b43627b6c2a75a9335afb415c81eaefde2a4e9b5b72c9557 912438cf0d3b499e5f9a58a6b0399a03bc53a8d2e9756a774e6f8f9976bf0ef4f5f6a61f6e24507b 1ce2952cca2090996ec553d881cbb8b8c85b0ff705d5825b6ae53ed6db1e4c749dcdf240059b49c5 9a3791792fb1eda6e3fe0d2d221579972f82b5ce06807264b15744dd4e05aaa7dd158cc1e3c920d0 571d30f5a2e237f2578eea94dad668d8042136b5db2cfbe0b59e34141e7cb2c9f5d6df6c5365f2ae 6e20443a93106d11c5426e0375e1dc2d1f2ec7905ceef5ccc8f157dac8af5bcb85de9c65d92bf4e2 7ac0e66261d50a3fe9bc5fc706bae53248f066b678e2270c3c3ccf10381a782f079c36af23634bd6 5789e9a1db3e89fb1a523c81f7b54c57a7355f3991707829ae23875fcb65723275607d0d2fa03170 7c161a23b606f3703183f1b3f75cf50eddec3586c364b6889552eaaa7e5b91daa4569da028ca8a0b 20bf50c3f952be4dd1e47e57289427af6065dd16ce4ed0ca83decd06ecd160547bed0c7525b61a9c 359c087627b1db95e6f92a34abb9f329d4897b815d071c174af5b4d85cf5d7b7d4ae81a6839c7fac 467e698a0d64b92e939d69857d4c85e5ae83ac53ab7b5d3d5c92f3e8fed24726c36601188c36f8aa 5ec43004d9d4e4025ef6c28d7c33ce8885a4f91e681de39929f354610dc3f572d268bad75279210e 4c1c9e32065b459085b44217080b0b29d32bad99a6b4c1f63880b45f0d243f69cdd6e05e6ce3a024 086127b399eacbfb4607a0114566eaa7e3c95c771eb52ba40f33b16f8065ae406ed4e4b30bc31814 142e2db8084e97e914d2bd10d75e679029ad3b925a072b877ca529e3d7eaa6dd3bcdc1bba11fab30 7634b7c325c943ec3a332c1d4a4b14dc9cb34ff8680295fcb147cea03dbe8daf66a3497aad6ddb75 73bbca26eceda3b249d651ba1df59013c402afc8b6066c5be71568d59516749b15d8c233db4d4314 8fe8b0f5ec647af906e6819a2be4376695149a140bc5f3903bf56d90bb1bd585b9896b36d0ce314d 28c3fa6689855b24645fe827dc9cafda793d314ac15db4aaad392f70da0ae2f4e04a09ac6ceb683d 5f5feff403dc5724006ccecf1e00cd4a4f786ef27568cd77d2c5fc992dc050b1568dae6b18cf1768 a3e86ede67472b666f9f94565d857be00883f43dae58de16a0f4c56dca5bad3546b58857e131ae27 9640b175420f10d8cf8d80db6c83278031ae8cc76253bd64c6e3fab155c43c1581aa8233bd4eafc5 763c9d074b60815f327460b250b7da186d2aa9545ec028677d4bdccc9d7cae8fda448736275d46bd a32fbe76af73adea65b6ceb95374385410e0b2a9038bcb51e67652d73c14671d78b75eb0edd66e71 0b0bb9cb7914bf18b375d7fdebce682dace65eda2ce7cb446eb1db25894c55cddcdd657f1e2677c5 8769950bf5eb633d637249e441b8ad427ed01c6ed170c0ec12fb42b98d645b8705591d83c87e3f81 ea94bd2caff2faae866c502baeaa03c19651a31eb2794456de6b56a4b7f9ee76f1041c847c56b2f9 cac884a06469aec2e042ee37772caaad73d33c042bb78c5f550a3979334ce70af08d9ef2e5ce1a22 b7a4bc1290627a3a298c5ed61e7472fdd8370862b9cbf65a82339bb608b4a18703cdebfb654fdde6 7dca807b57a509f8736309a6a533089357a554d4d2d315f86c0108928f72c3fc226bdf20a3d780c0 a76e969a1b87e0c164f63084a464f955e536adb834049e7a451c2e9411a9dc32261c341877e73049 49b50214b55cc8ce8405a4b6b9f0ed1e4c7a50d10a0a50916fceea1bfe5c86c65639019da6a805d8 dd1903c9ce280397c79257d44751c69d6715013e5b4efc1e411e6b795778c64a42d8c37289bf6455 e1edf5ff25c2c5194a248ab9e4fffddffffd631b09904bf62faf6eeab0bb6dd3f726cf642b1e66e4 b6f4229957c6ed46319dce2f4a23f911d707cbc78d76065c800eaa7513066af03e8a4aad5b439bd9 3025b6a6ecdd699f6672bee3e70ebd5e4b5b837d14ee9d06d77ae9362ad8cf54fcca4f73c20fb8e5 f49940c979579c698bfdaa192ecd52aeb22e5fbdf891e4668d9eb1add43a4a50e201baf080191510 7252edefec4c12c2aa8ac5e2e05634f76a85481fd2d6aa458e89ee8aa27b45eae88681ce34b8cbf3 84ccd92aabe777533ea74f7161863464916d643cc977dcb8bb51da943a50b121059f8de496d38bd2 c0ba2cd795cc552827dac6d330d7660f138e16d1c62f77d35fbc1ce0d4a9b99b6961e6c9197fef27 555d098620f3880c59cc24980003626b76529d273a4d39861fbf299dc6f50d91a9330e97ad5cd75a f672f69cd8be89fc4af74a85acba6a1553de735ae4ec1d543a558b54f938e664a03b685895166126 aa68bccc5064eb517d6b845043c1ae6c2bdd66cdf6c8df67635bc715b5debec1610358f5afa3f57a 509af0cb2b34ef5e77e6aa7a5af7b739125261ba2a765013732422c77b15922d1c31babdebd991a9 843ab77e849090a915a4c84a795fee8d4b516e990a27aa295f310d18efe31b5b6c25f3927bf9896b a677008ce4bed98b4c755dde82c41c33c9c03d45f6da69f7663eb4ed5a7797b08d56a2e8e80ba2e5 6edaafb1976fc09bd87cf8436841277f927354ff955e58016d5f83b721a3d37ccd96bfda90f1ccce afd8f2571b323acdd76cf9ab0d199de65fb60c6baf5e37c09da418ec8bdbaaeff202e1b12fc175c5 0e5775942536b7b5ed687f4716c0c96a6e72aad93938c6adbf3adc8dd174ec5f53cf545257602eaf 6df54e4dbdedf938d2dedb5f285fe38c4ef3d3a81f90cf5ecd0f9a343acd4fa37ef66a7e8bf77f43 087c93333acd6ff6fe67afe6674c1a9de6377bff1f57f3fbbcff6b43e0fb9cd1697eb3f73f71353f 6dd2e834bfd9fbffb99adfecfd5f16021fe08c4ef39bbdffb1abf935268d0750bfd7fb6fcee834bf d9fbbf22043e6ad2e834bfd9fbfff36a7e61838a4ef39bbdffe68c43e0f77aff2743e053268d4ef3 9bbdffbdabf903759fc84b112a5cf34988253cae203bee224d01ceba0e4e6c70d2d9dd11b1c35878 aeaf98dd56c7344ea8e15ea441ddd7b460e59fab0feda9ecbd4adc0b4841422efc6a09eb85492f68 714f3168e9f3f8eee9f7baf4de9bd8ecdd5d0054d129e6d7231b6837a17b4dec50e641ed4b065b05 6e97e5b26b695016b6552badd94aa75c7324a6797005e1950cbe2fd1693ef0ad7f49843aef072d3e 29052d675b7b04998833c591a69b7d60395bbb8fba77c40356e6e194c08d57b2193f28bae48f5d55 83e6fb8b6a81de55e9ecfb5789614e17312567ae7c81426f3f2dd169fe75e40baa9e97ff40a57d81 7045706f38ebe5347537dab5a6d5ca8653e3a95bd065351d501ae40a926ae92955e9dca78a14f60c 459c425d99575859e1ca562f2eaabeb7bf50c27a633bf05df029456d695bf75e2b3a4ea3dc4507bd d8dab3f7b22c38046e74d3ea5d64adbed6e0314d9ceb589b570e3b429042d2e1c4a93e6685dce0ce 7220b5e2d85a29257c40e2956e3ff8d5b77c413de4953f50b906bd771469a244d6cc7ae64131b257 41deb4f5f2e2b23ed7832aaeb81d335ec05d0abdc451e4d73b4ac81d7d92d3db7b8a45d4f2f1d49c 5d995f23d169feda092c7bd2f7c9ce538e9afdb611c5288c3ac57a43b02cd2330d16c2025d4bf9cd 337a192e1497d030f9f85c4788ca891096ddca9e2fde659c35163dfc8427bc3d43f8dce12dd169fe daf96909ac576452729657a336bf6d46cd7d08dac0e6153ff330bb18275fb385a2afc121db52dba8 3793e9fa74278d85101796501de14bf933c21ae2043e59d80b661c4841e8c70a413f2af10f227ef0 ab01aead067eaf92511ef46ddb881cdf5edd6b171335269511ad97113e506d72d894830e3595d849 0d11e4e206e24b834bfcac9035bcd5e66459850de3e8d70dd3cd3020dd7f2da05f28f1d5c4dba045 cc070fdfcfa851273aa947cd28358aac49cc2e85b64a9fb1d62c5028a6da96b8fc742c660275c56f fccc8abdb5d1c5e99e2bcf1937719dd30f1f9bd383e17a793c119dd55f12af45fecf233f28be3b9d 0f1fc3e5f31c39be51bad70406b8ce074e474372efd290f2c8d65f72a2e3b504656e75b88b9fecb2 e804ed9f5a5661c890457b44fb5d664c0fd0f5f878d23a93631200669f92f86afef7b77c12980f3d f6f9d46ccdba04b7815eb6239f6ffa2a21740579a43d5f628e452a1c02b58a27a2bc2d323d2c28d3 4348ac1e591aacbd4f6377ea9400020d2a63a4dba4bc08bbbf5aa2d33c7c693272c5c953b3f0de44 8a7cbe76cecd5265249f1a4f415c0e589fdb61da8b796c8f019d285e1ec7948f3ea9acd64c90cab4 98220bd423fe154b237b28e3529eb8968e857f493cebf62b873f25de2bdf1bdbe08964ae997b7377 364f402887e87d224a127be2d0ea2bbedf3074237d39ce36c933b969f4d403947a5d08a37f3508c4 52acbd55c2ac3d2680f6be395fbbb81d8cbccf4bbc06cc77bfe08ae5c2f8f6b0d7330d0e9c8342f9 fa53ca00f49043e71ec83099e9e198ed4347b2e4b31451170372df3a890cee960816efce171ce667 a7f1fd063b321d111b346b321a3a80faabe57e753af5385bdb9287beda999d686936ddf8dced722d 314ca5d6a594fe78744032c26c4f008d25de53cd0d46bb0c84beaac80e6595058a4e467d0c4d253a fb9d700088ddbc5b24bf26f1e3e26f7df601096ba94e22ac692b30acd9b77b586f4e7ad1bd95a51f 347d7e78aca6d55cc160e24cda594d9abb3bdcc4090bcd940ea6f5bc31374784b95b476d4b067549 69d7e0669a9717c1789789a167cf67e5e07e4aa2d37cf3b38fa27e80332ea6fc2cea0738dfc5949f 44fd00e7f78cf651d40f70c699f4eff5fef7aee6177affcd196704bfd7fb6fce38047eaff77f3c04 3e6fd23893febddeffc6d5fc6aefbf39e310f8bdde7f73c699f4eff5fe0f85c00f9a34ce087eaff7 fffb6a7e8bf7df9c3fde757ecaa4f170f0f77affb321f033268d1f48fe5eefffe36ac25a7994fc82 fa72edb03e7ae7aa11ea70257b5e6a2a565d31adcc1df909eeee97c0a1ac9bbea1cc3b9a3fdddcf6 59321ed64435fa6ef97a4d0c35fb329d4a09ad403e938a5929c5eb8cbcb73f2dffc5b946e34495a5 fff340d24b8122e0ce0d66ec28750cb1cb48fa60994d8931edca92bfb95a52317c44b85e4feef07e 496b9ca7ad59e0a922233221b7efe7fb07249e2ff0bfbff505d586a1b05e4cbac1bd890e22c7ef8f de44631d57ec3045372b5103679d47401b2c77887b354cb356b3684837aa318833822b6b91177df9 e2ae67fd9e36d59ab2b165e2567f8a0cb5b27e8dbc0750ed452a4245e13f50f7097810393e424d11 b41d512279675d5f776ced0cae6da0bdd85b989a38de1e902e1809202feb05f9259fe15d5351eebd 9d263d8ccb450ccf7e284cd5defd2d7111f2cf9d1f947f73b67478e0bd1afb63644dd47294dd3af3 7e1a759eb6ee46aa33b3ac450d360f78f1603ce7695157c77bf68c8014afd8e38724f94e4516c72c 7c16327dd8e5736ec1faa8c445c8ef7ee10bea8b44a2669ff7a2661fa172059a761769f8e6ac9760 da06269d5a84581c9a07abb3bd0d3708795975368c56e94d4e8a33e8c7bd80dcaf1e397162b8222f 8b1395dbb6da77f6d22bd8bf4622ce75fa8f0792ff412567dbbe37b149cacd1aab8baddd7bcffb2e d52c9b4e58ec1aaf646771cdae425cbb3c4b94ba2f961979d0ce1d45beb83cf1ca59e0b8b2da904f 66af609e50a6ecfe2571d9ee9f473e25616d82fcc9597a44cd3ee24c11f0c1590f7ae7bb512e7871 99370e81dbb094ab5fb34375aa978f0358bd07dbbdec2f29524a041229e4e424c581e2fc7832758f 638895a9339d62e3f1298913f66f7df6076abd20a0514f5af7a3663fe9458e9fe09135abd2bd26bc ac9bbff69e57e1a657f4ed63303937047aabf4006d27b1e51a212ca116c6e97b1a7b576e4fd93de3 5494234d71187f0cd3e2e5d7c85f9cef31748c3a44b783c8f1c39d0d8c5f27d331af9a319969eea5 008e2b6713bf8f156f5d5d49cff30812336b1ce64bf50bc41ae21c3935750da61f2b0c3f869de9f1 383aaef97f495c1afacae10f4858db12d9b0de38a3514fda0da2b6b4ed47cda80146d63489dbc0e1 d88bfc9a7a1a92bbc76f15aa9dec7c28bd96c84c9c578d0d7fae9436ec2db55d9f70ecb5660e33f1 5d93848f23a386517cbd497d5ea2d37cedf07f502f58d443b502579c0e3b36787acdcc0346acaff3 55ffa85dfbbaa3da977d4566f8d4501453b9097fe6c733aec2b2f3d3bdd69833075e99d3471a59fe 5184ec6f287e5784a8f439b1fb3512d6f64c2eb05e67ccef654a7140478eafd622832a9db8d2dbd7 b757833e374b3b4f0e9bd58a94ce133d5e6b425df676977ba77dbf3df8a33079f4b1d1f1a435c7ef 022295c93d96e4726c6cbe26d169bef9d93725acb14a21fea5bf7dd4dfe73d5bb3bc9cd95daf3371 997770364f38ab5066cb97a6860908c5aa153f5b63b1bc5c3eb5915e8d0e1266e3f8bc61ade3f8d5 6f5342a7d8a53248d82797aa313a9c93fce407253acdbf8e84b5bb5d0a5adc9d885a7cd2b6acf9d6 ba669ee243abaab3b1f2a8d67989efdc7da1941ae558ec964f33fd5a39797c1db8f431b59ae6a839 d72c92f2235d260b35ab7238ab7a5c523d9493a72671210fad5f2351b34f013ed9b91d226bca8ac1 024afc28228accd14bedccf37339c1b982b0d9afee2cae631e1d8a4bfb989e91772ad7187a6431f5 f20f60df7c1eca969a2090d2214954855d7a6ff636d93d1a6c0adf94f8a5dfef7ee15f12e0ecabe2 4d581ab59ab717a26f4580516d379f9147afdb42d89ce74776bfd664fac95a2a253977f9a073e578 ad3ec240cc335153c5cb1e4f62c6be498226ee54a7779c30a72ede59f71fd82351f37f46e25ae79f 3b3e99f2aa36b8f6e697d5d8189d9b0943904f0d392b9ceb9321bb67a26e876b14096a75ea1e0ed5 c28ad8df118fc4dd3143e35d123c6147b3cf62837593479fc59a889ef89c848ebb09255e277edccd 69bf46bc67c8948db1584a9ccdf561a1f4655e11f3bb563cc59fdd9b14407375ab43154ea52161ae d029eeed520b2cd85cb7e8ab8780286bae1134556cee76020f60bbf9acb8df65fc280ce4634822f2 2ea4be2ff114fffffdadb758d8f5249fcdd53ea9f4e9fa42cc2f5c9add07739d9edcd777521b4901 d1d0b229bc671139f4458eca688a01eabb452217afd28528d3b087acd38f0152909c11ac2dad090c e68d295c3eebf348acd54f4bfc7654bc0dabd5be16563da211d68072742300e96734549d4f035ca7 4fbedb955dbf8b3245ef99ee74dcf4051fda6a3139bf833b16b290726f6755912a61a25870341b09 8fbbdd214fb9e17e60dc5a58358e347d065b86269927fd67e4fb9cef62ca4fa27e8033ee6c7e16f5 039c713ded67513f20f100eaf77affcdf931a3fdb449e310f8bddeff5808fc1a93c6f5b4dfebfd37 e73b047eabf73fd16e7edaa47131e5f77aff7f5ecd2f6c507108fc5eefbf39df99f46ff5fe4f8640 843ad2bf98b4d3388635424944e9343c0b5a8788b37b639d87bfe28a8f0147c4b3eddcd9c69adaea babebc8392005ac876859ae6a27a301beb80bedd9d40bced678166b8515e76a5d38aafe5471bfdbc e1bbe78f4a5c84fccae13f506b29a6f90555b393c1bdb89ffb64853c3de815ed3c8605aee03d39a6 e70af5e6dc56036875873a7e64d23b8999e67542ddecf5eb746b1fbc780065b8be77337a8557a067 48c756213f79566e839bf23312567b8bcb17ce698ff9e381e42b15e00e3a7bf8f2fee43d5fa4ed4d c0c8fba934d17585dd7ae1e405637bbf245ab0b51bc89889792bead669beb86b8012d265722c4683 936dce3e979cb129dff7a422b7bc95f897c4a5a17f1ef980fc0b35ac27b2693f5e8de8316c6c696f 42ec6d574c5305772e515d477eaee3458dec72943f5b68b70299ad3dbd3306451bbf4c39e0a029a7 c3f1accdc7bc7a2dd6cf4a637037a45ead2a8a34f6e23f25d169fe75e40b6a596947a8c353d496aa 99c8f1aba9f72aa08c2b02e8dd9d1b58d6510648cbd6dacdf1bdcaa84b737fab6c0c1ad9c2974ca3 0f69457587a84636832b2627c7a34e99a8a38244a5d08bc03aa6204c208efd1909ab73f0fa85139c b3515b6a67de774f7f3ef1583b429d1bab9ba32cc1b453b82f6af72b95ee59cd223fbb51abcaea22 38de4adb78ce5addc1a5add2e448447ae41bb81878054678596d9d9ffb14cf675190fd97c445c8af 1cfea67c0d350ad08833355d1dddec717e75d6f5f9cb06b7fdf2bdb6c875cc43421fbf5f8cd9bb33 bd401373f576c2174a0bbdada4e3b2b11547972b224c31eac02f0e57995b0f419e2bd8a3d3e7259e 70f9e7ce17d4f6b5f3057534c93e68743e891cbf3a3aca6eaedbda7918de8d54b360e135a0793bf2 6affb20857c3f395eb8f95bdd09d483e412ec431fe5a096913033975d68f97a0e58a3426b0c0b4c3 b140a7c9fe8cfc9d93d87251f734cc3c86c3795c1770e78f495c9b9c9c6da05d762c6b914b995d1c a81a93ccb1ab9792d59e8a49e9a1dc33f363913d4ea6428614e7dc39315ab3109adb9d7660933dd5 8ca6c058cb1cfb35898b90dffaec5f125661ccf81b2aee0c33deabb19dba5969b88fac5915a34cb4 609a4e987d1a4f375dbcc85cab79be555ef1c2394a47d57be2cbbe0d84f9b93ce2ce0a3665e10ab0 3ad55907665ac7044dbb7940a49d7b9afb418927f5c5db2fa813bb1ba1c27cd44335d2de849b8c23 c7f7301b9814580bef05da6d9808eceb1ce5737a79f16ca8fba1d99142f7d8112440e8719ae00dd8 aabe9830763d9cd3aea782473f7cc46381233dc98947ba14f23f237fe7e4f6bc4f767bf11b711167 315f8da728de6ba47db8f92353305295c0d43560963963a573430e3a625b9c670f5dbe94873bec2e 6b0f18a7321dd3d4fe313b8e72fc86e2c60e494dbc8e424da89cf44d898b90dffdc25bc22a49ddc2 daf6d58f6e4eb21835fb6a2a727cb56703e360613a0a071a93d9f57029808b9b56559f69b553251b 12b7643a42617d8867dbb1b7b3d063dc91dda707c3fef098b886332a7d32b6644e4c5007b5bfd6c8 5cada3fc8cbcc7d07fa2d60b77f1315cb55351332a34236b2a7de37940469715d7396848ceb9a904 bccfc8a343a12e28d75a9bab1efdfaa9dd6fb7687ac6758e63add9a3327d6b482e37fae2b099eb08 a1abc5b8b389b623e96724ac0a9c19056836e21ce5e528404b51d789db25f3c010c0355b00fa7fd4 d37452b52fb8259f12d5acb87872750e81baa553fb3a2fd1b4ae558e93e1a24ecd815c8b5c8646f7 706ea923e2923dcc09883176446564edbf2ff1d57cf70b61f5a258518c5687be3b7505376be4c37b 4da04323792b84f184de615ce665e47054b98bd252cb72c65aca9e3a937d8a66465ee63891c1f8c5 186a4ed44ae46a610187f349ad1317126f111513eaefd1e26e16c971f9331256ddf7af1345313a99 3ce8dd9db73555bede8e4e59d1b5d42979c6c6cb99d2672aac94362e77beb2283f4f874118d0a353 f2418974343854c0c2eba0f58dd4a18cf35902bee005a23adf95f728b3ace17767d68e64134f1979 6f7f50c25ae9e546018acebdd4f64c5b78b6415f5693297b36f16e46a1cccc429a0197f74d1af2da 97139919dde8717f605039797b3de8e5d7ed00f4af3681e0dc636f5eb060dfcc4e9fb8cd8c9338d1 1c64310f8a97138942e503121721bff559d443951f418b802791355798913c76e65113cfc9caa39a 2c493c785ef170aabc3f913cc1d3c9eb8127cf1596230c2fe0f696ca49fba60baaf8211a8ec746eb 289d2bf618d56ed831d174d0b09b7651e6960e22c93d7f46c21a3b09bde733157736e661381a6ad7 68e8a97666d6454e68678087cba9fe894a109ba3d85677e4f612590a2586fb7d2bbc1278f7825118 9d9dd2d8801971e8c92972e8184a0b68b2929677bc14aabbd9e071d9a59fd61ff2fe6dbfffec7c4a c2da9348d8e0d089e79f56009580703842c40dfeb20912a79ede2f478878932cf39738d288863699 e03dcb9c63c30bb346b92c02a1a96607de097a6db79b4300b6cb66727b4496d30764350829f8bcd7 8f9118cc47252e427ee5b0c76294a19f399a513b1da11cb93bb9e52fb8cc4594d9eb71ee01f6e13a db047ba26ca730a6b5c8a2930028efc451aebecbf28936b24e3bbdb8763a8c6ba7f170f0aff22974 ddea2b0829eb5ba87a957e4a2e3972965428b127489954219e35c4c3adc6f0d4d314e8982555ec50 955c12f790098bbe0eb6bc9bbf303d625b9811d6d48b703a4184d37945ffad94004d249f02b15a26 1349290f36ac7c616be3f9f296682581bf245e38e79f473e2061a53126c2a8330fc3ea623109ab62 2045598a980b6b7c751ed6f42d13fdb5b702ecb64e3d284a8d8b29ee0bce54dc54e3d6b3e563d414 f3ec707ddf5c1bc8bdd4af1296ae96690b1a37040bb8345473579ddf2f213bd2f459d6903f2aef02 f13f8e7c1ef5039c7149f567513fc0f9be11fc24ea0738ffdb689f47fd00671c02bfd7fbff7d35bf c5fb6fce38047eaff7df9cef10f8addeff6c08fc8c49e362caeff5fe3faee6f779ffcd19dfa47f91 f787cbc3174e04998555232b47e36a331fd69c493cfb21b078988984bd077885ca3c1ee5b0e48d5a cb862316ce1d5b36d0c9fdbcefadef25aa005b975c81b020bc70b22ac9b2641a64ef6a385c60e9b3 aaa56a3967237e4de2a1fab73efb4bbea056b5e71fa8b5744389c6d54121faf7f365d09aa127dfcd 90967f4091d4e338ad01dee8746cba2933d9b515808eeb02f7f37dbeb997aed59d058505d2bc255e 9cb923cb67b39e1d3a9771bb7639abfc5854a1a57bfa4189db4d6505915f5029227237d08b509d42 31a29c2cfc830f330f7f859a8fe30b4979272957726725bceee456cfce5d3bc223ebdaae2ead6af5 85987547216e2d1a638c8386c9d7a344595abeb889b335c5b8cbac6c795bfa07e50b67cf7c7de19c 6eb5b05eec957db7bb5a3ee8c6244ed81f83177cf39e1c967267565874f2bd65f5ae475016729f0c cc66d69c1aa4df595e5e291dd4e31f39d133e486d6960a24a990f494e4163264a42e9e22bf29f1db abdffdc2df51456611d640540b2c2742eddee68bc7b0b03a7a93e9caf0526930e1ac5a56fe7e590f 2ad6ce901bb72856bb46ffb08e7f68e7921277236dc5fbd3f37625ac55b8b3df29b71d4bcbfbd496 17fd7540094f48247e50c20a8a53ef4c7afe487c4125482dc0f976e9e1d3f385c772f3a32b3c5686 9b1d0c9fb6964a14eed5e60c30f7cb73cd389a764b9726524b2b169f1df5ba42870a6a5467f29e4d 6d25f79add8bbe5a600576f65e8b9c178653fc07e58dfa774e4eb8042d28e21c167a332fd599506e 7637bfbedf8dbab7fdfbb5a7a7ad063f2edec83b0b5c84eea2a26dfab39a8adcc886d24c673b92b7 e18662d0c316e2704d2002abdb2c3f1f4e094e2600ecfb125707bff5d9df51afca2a46d5a3186d14 bde76b3873c5f49c7294c1f0ec14ce6df75e0d94974940b39c31bc0e0b7afefe289c2bf122634dad 174f4e921e39ad2932f0a227704d60cc8b15601b57698f5ca193dbb3e74702fd41f90f67987c87c0 1b553beb3e998950592d42cd1e878788b22cda402a655a66700d6e8fee207d5934b5d4f9e2f139e5 de3d17a4c7f555164fc9755598aed32d3efb50fbdcba6ead580d8c10cbd4637fbad68c78eee07bfb 79092bf4f118f5a4e9d4174efb7e8dda52b51039be317194656f6f6b6c95bf2362eaf25e62c20c3c e3b47c047ab1b27daa661fcfc8e410cd89274b290ab36a13e073a05a67b527d46181363e3b219485 31a6aaef191397b00f485c52fdcae1bfa37ad775587bf9d7a82d55f3ae080ca7cebaded8d9403b1b 17026cf9e61715f39a691f1c0d4087a1428c072969084e32c23c89e5f87c238c1376161a6c80d3ae dc6d3178ae35a69df106a289cb01a76df780fea0fc9373f59ed759cc188f61a39c8f1c5f1d46064d 6dee35d2db9bdd91c618930aa8ebdbdccc562d22fb90fbf3c4534c1dcb29be509d665818500ba7fa 7e0c448961a941775bc9c1f1480eb6c74176b73f1eb31bf42d7149f5cf9d8f4a5891793aeaf48be9 b0560e36c13d9137a2b654ce458e2ff7ee46d29e59adac09192cc4119715988e871c5ab554b194c3 23f59012a811f08a1a24d8ebba993a3596c72ced5e5ba5233d726b14dbbdf5a824965951c9f5ee40 8dd71bfca31217bafe79e4efa89dec366881cf8b3721ca59a7f04c352dabc7f56e74539b5eb38d35 ae9791c759dd07195b7a5ab6272c09fec1019773786a42b9244dedf1cc7118960aefcaadee56c9b9 7cef1e569cbf38acf8c8ba191d227e50c2ca4d65a29eb41671820dd027d3befec7a38842da065ad7 9279c030e02ac85847d780267ac65a92aaf48b1957144b9ecf5d0cc93fedb3e7300aa2d7eb9810a1 3429b54af9839a7101427bdd3b04302b2df6464667083073a0fe92b8caf1cf23df972fa8d77e36ea 49e7d0835edecfcebae63f2d6b4ebea2b01c3e752d9de86a55cbda2b5eb03c4bdce316bf502614b3 5ac036ee078f0e4697f0982a945fe4aacf670e257753242a4aabbaaff3f52ebecf406bbca5d478dc 422de6531217bafedc092bbe718a9ad3341a39bf68c463cf3739b2266b45edbc7db9acf6f64babaa c781dae9cd0ef2884f6a82ba9dbb2c9e016c3aecd76d4abcc11eb9ee649f87f2e99424aae42ebf47 95593c5119dff3dd06deee6e4618899e40acd74c503f2861b51272612db5ce0738c7c2eedcbb8891 35e771b676cd3c394183c34d4a25e0ee443e15f34731f7dc6b5c1dbb9acc602bdd280912ae64112c 9807a0a639442d89f87bacb749e03636cee06dbd5bc4a84cb78e06c3711fa55ff8fc5f124f51fcca e1af49348a4af361ad4d95fcaeb1dfdae0063e1acf2903468d67aaaaf6a59e974f057f2e2e077b92 ab9bb6c0d068f37ccc6cf2717e73d0b58644ec6a8abab7c694813bd5918977d64de78f9998fd6eee 85326826838e0a1560f7d2ba8dcf4b5c1d8cb76115ae89616d7f2d7b936d636a356565ad9faf09f0 dc4c262e7238bc954449a2171cdaf31086c92ca2ceadb6a1c9d28da6893ae5d1fbd698e2f06e6f21 61c7754d41433eada1a76ec240474618bf4ab2e3eba1b79b6a89d72e3d4da67f50c2ea65a104d6d9 2b3b05136b5de4cdae79c69a3aa6f469353e4d44b92d71e8dcec314ca5bf3866297c7bb8ce5c646f a7cb084ee20a86d17390409f588742c77a914193b334bb9b1d1d7e9719383222dd1d0d596e1d03c9 971ff7af493ce1f25b9ffd2561ad44698f61ae9f3492f3f1f96cae075d85ba1d0e1162cbe0505849 30c35ba24829fd56ed8064c8f69e00ca032cc09509fa22a9f8b6864e94c56627263ad02edbcd2188 7c4ba048a1eee0f0f96c10f076a21fe172cae2a00ba50b3f28eff2837ad52cec9ac7cf8df33972f7 e0dc1497c8058e28b73c333434835a05d69d30579507ee215c2202aca5d114932bed1689440d51a6 8f1652908c2e0ce6a53e5c3e4b63e8ba95661052e61771e9339e120f9a880481584d427e50dcf9f6 393d37d4ce40e2c72b4338e70a65ae312fcfe9d76e0a510566f77e4cd438b314de8b06d0e8c417e5 5d9603f5886d64466c7d2fc2aa051151ee19974f9371f9340d36ac64766be3c9425cfa2c6fdb6eb2 baf1c864e39b12af0ffddd2fa8a67ad6458908177cc53635b64d8f923457b300521b651a7b7bdbee 6243071beee6afe21cd616ea06ba6e8e3ba87a39102056dd1db736b63cbd2bb7cd25bff10e5d6143 75bb7224e3f3a6e70ff4754077af6b66d8bdad87af96f5831202ed79fc6b1121f0e2a3cc1f3b16c2 8ad5dc4683ab400bab0f241fa5ffda24facbdb87d5a32d87d5faf5eedf9151f038e8cfac77a46f35 f734253b6e62078f1cee89ae9ce916869c748a246c91124ef6a2e66b1638dfbe2727d90f45b830d7 22f37979a37e80f39d11fc24ea0738e314f767513fc01927853f8bfa01ce7731e5b77affcdf93da3 fd1aefbf39df21f05bbdffbd10f885de7f73c621f0fbbc5f29a8d29bf31d0256731361a5f4889228 c4a8e32fa852a084d58119a1528bf071709331eaa5ea9e40fa8d3a8e50e1a5936e6fe1ff70ca9c9d 0d7dddbc2a6dd9e80c30ee92a83af18395f7f653120293edec0b2ac317c36a6d0d8555a37a895215 31e2840af3b0366b9061add23e078dbb1dff84a0df5e44a8e4b892f30631ea78baef44777974eaa4 f7f3b52da5563b7b4191b49dabddce1648ee2f8693d6649d0b0c46cb4133ea03f22ea9fee3c817d4 465cf37da3f6112832e4c4086b44500e6b7c7b1e58b321e93bc382fa78a444cb0b4efdc07d556e29 37a9a12567264d5ab6746e8fec9cd758dfd761e1bd664a71d3385aa030576e98f63a5f12489a3fab ddc151059f37e2f312025b74fe8553d1cb518072c87b2cd0816f614d2f03013eebcd835666423e1e ec4bf24e39c470a770c275e6afddd35e1d1bf9fbb665572dc8a43aa6e120d39b457756b7a6d6c78c 437ac15c4ee59aa4e5f825a302f71b295bd40a7f4b3c45f1cf9d0fc817d4a1a37c41a514249e7d6c 86f544afe23b7e7be67757bd83f74c79a2cbe3e0d559f88f3804ec62671658d78d9d36516651bced bbb9bad165a5eee5995b8e751e2e2df5b45247b46511e722ca0b25e38b092e1d36a9dd47252e74fd b9f37754d302a2dccf4623c7cb568043d5b2df45ab93c7f1d5c0bdb1e8098ee44e35bb54d02dcb58 b61e26468a09c38372b9cb2b352eeaf3c4a5a22d65b27dde10dbf846a0ead276a556f23221e3c89c 90bacd0015075d0efebc84c09e5c849555a87ee1f4828893bbc4ebd904ad59a3fcf057eda9f7e4da b83bcf999c7d9e4fb57b85506fa675af38c6633d0f2edc1e4a684a824d9db7ab5e4185534145d959 d7b67c4f5e27726b9182443f6163c249466021d958807f499ce2fef3c837e5efa8c1a312b5a55484 6a3feffee156283e06afc6d89b741a9823ab366b97b30bd542a7c6f546269f7146704d18b6a329a6 f33897e556a898292d25ef37eb8244569b55d15ff7fbe2805f2c84934fa0fc8cab6df9ccb4b8f994 c405e278fb5fa840198f623473f7bb72b5e4b14475e40a4603750a1bef74874d4431f786a11bc3ac 70d5975bd03c83ced151ccb3ff90dbeafc250699302d3c6dbd244cd27aeb5d871e38633ec335112e 4f391bf6dc37569f971060d965d4e9a7cf5f383b0d3cb0f4a4f5a01ba9fc7b8909a01cc5703e0bdb 60d23e5a2606f237aa8e4b1741efa95a09a95dd49a3fb3e40e423a6220a47c812b9c12bcf080f29c 7c9e55b9420aeab3672901b3a550dd9c2ef869f52f894baa5f39fc2ff91b6a3597a8c473baf1003f 44a8c34221efce8fa9be5338bd36f7ab60eeade608a10d861d737a61690aea0d7bc46facc8ce2b6f 88213eb4044e565c7e014c43aec0e6d3ecf69a074ed75cbd73aa6ef0357373a15876abcfcb7b0cfd 176a2d578d7aa8091eb4a08ce5b1fb57d651eaa99e0db6ecf9bd36bf226687ef1caee9de8dd4ca67 9157eebaa44874ea7611783265f00b9bb059cd0abdd355b8241893910b4cc3b935e3e9a3363958d0 4476b0a2edea78f9790981b3bc8a6e4ec56840d207aaf154d9bd7f78f8376f0266b3ce3a9f8ddf29 bc5f5d7364354d65751bcc723b7d938071d5bc822799f4779238c9ed557e299b1756973ae6699754 6d06330f214de868f6f8c8d0b5637fe54f8efd66657da44695e5d7242ea97eebb3b744a842645206 d0c3eaa2538f7a2874ef7725df70e76290b6b55350b3ac9ed8bef945627c1596fa56ab5486a84258 cda3347c757921db9ac7c341ae6473da6977cf194cab77ba1f1ffad8a7c228daa991dd2b937c87ec 538959724d317a69f5790981fbf91d0215a57989ee4f8b46d443b1c4831eb85747e97b89c89a62ce 3cac91a291bc011d7d8b2cd7ea7dd546e541327110c447f2c469705b3c9900a632ed76e27aa447d1 8d95cdd45d927f951307e951281c9647347ec27e582efd0d993ede979f9737ea174e116c84f5c2ed e0b1ec4db7355c7e5878160e0c763679c5e3b442596f69557cbc5548a28a8a42e541f09b9a4bb335 acc0339dfa423e323d4ba726fce646661b25e75000924fa29c7be5f697f3aab3bf704990d89cefab 6f4a9cb07febb31078ded6d1fda97f8dee4f5433684d59dc153c4189acb9bfddfc44e37291cfaf40 bbd6f8961a350b481e1a3d4258a1e181bd6d5c9a39702ff6789af4e3c4834aaf55f5a0f81b8300c1 d27d8f2c93016e9eab39bc5986db78335d877133f5dc7c5ec24ade8d24981a7f2c34736d47cd8946 1d652108914187c255d879bc5e46c4e08cb5f096423175504ae7e7145f768ac7539b748fc757a6c0 52190f930edb64e24c5456872b7e4fcf6ccc797643acdb9ee7d123c5b6d1e371173f8ac03abb2efc 79092bf567948c64d7d1188f48751ec33c0cd9406975bc0d086da79f3d448c17944d285e508f234d e2810b2c1485177972ea27821ead658aca3e5fcc01ac61c2de840115270a8e815140fc7ad4f296d8 bdb6f7c28e03badd5d6a67ec764fc9c1bf2f7175f02b87a35154060cab3dc28ada52a3eda5dac395 85f93678594d6ae0d9dc004a4499ccc809f0321436db1ec4b666cd1d7da2ba3895f3577bc2a01472 df74a727ecc100f154ab282e127a5c91bceff8fcc3dfa5277e0a111fa53292abcffa482eff803f2f 6105adc3611516ed2840b37169c829285cff2af4a596069b0ea276ba8fab3cd2ce0561b36ff4d8fd 885ad0cfb3baa556cd274cdc88068abb27718ff9f305853e9b008b8ea19cb4e38f8ff32e7db70c64 59d66d24770d9ff0269fcfc345b61b2f0bfade7e4ac28a3cde85b564e03e062156340ff54d4abb66 373d95802422423c5882fa6ce7d83d4635e8175a8c479d54e1d49a11e68a5ce1de32b5c1e80b01a1 2c3342d1a40e10bb8cf4a01079e09c90fc5e17e073d950e1d242bd427ae8b810c4a65f1f90f80711 ff7924ac667658583b571d1b6c346fdac52f9911656b208fec25256c545f67f70c1cd25cddc84588 993281157a559caa694d6ce8627d7412ac67bb45a2b342942e0022053411e737f0f66ca170796210 d0c5338e50b5269dc09baacb20ba712e9f97b00aa984c78e1227bd5ce6a60a25fadebbdd4ce88e50 c2c91ddb5e37f88892bac4aff15b44e3fc74f19eb50d22c04462377f593944991a005290d4060ce6 f90e74dd1efb50f5ba1b812682cf40ac7658810deb086e89d611dbb65d9e7c4b5c1dfc73e70312d6 1803d48b85932b87550c169730eaf270f1553b79437b41a7f64598041333626f6fe923c6b452ec4e 1c6271690859a7fb67585b764cb87c6eba10126587115bfa05364c3fb9b5313fae53fa856ddb7902 71fdb3bee9f97e6b1d30c9fe4725fec1f73f77f43cb517e56315d68512dac4583b1d584c001633c7 b9bb030ed7e9adb56f5fcb7df475c026bb2c975ec0608edf4048e9808226bc3b44601b3a629ab17f ab9df6fcf78b98eb806ee96b66d832de25d01537adbbab69baee7f5e64aac0317c69bbe64f3df500 d329ecc852057a178f3a097399b170aa8afa68ea68be9082b84c4355ddcf6f6d5400b66d9b6d6ca8 0edd5b07c7fd70cd0cf6d3d58bddce57dc64b55c4d53abcd2a25ceb74b713187978bdc1c5d66d5f9 7ea16ce6e45f12ffceef3f8f7c53c27276540ecb57120a81e9e11a02ceac12564e3524ac845c3412 cc275261e509b4c30a0b44e383766e1f02d7b4100251da1c2785080ebc7cacebe71ef6ea5e79ec5f cfeea30d25279e97a96cbcde7040b8bebd619d54eec9dc0bbd2d6556c18030daec62f751890bc4f1 f667503fc0193f2cfe59d40f70be8b903f89fa01ceff18ed67503fc019d7d37eaff7ff7335bfd9fb 6fcef826fdf3deaf2e80b0ecb17008802723ac2436d5b0a2f67751f6778df2950190f9a30eddef84 957b3f1ac8ee3b87b09202c4000913b7a0364e3f7d0c4ee61f36fdac3dda9adff3bcce73ea91b712 e8068501e90ea763ce9ee3b793555e37c85be3c8e197004923ff9278a8fe95c37fc9df5109e91656 46a77a944e235858dd658db07a9c662321fa113416df98fa071f370deee1e827fdd1b9e19ef76890 f1d578fd0e0cb881b4efb84c9d9eb8c3f30574b8d493b285d0e7acad39616e0d493c5c8681b5d3c4 d978fb2909cb3db0f2cea45332f20515d39b519acae061556a44a80f221789d20fb03c05fa6d0420 1ebda2c47a477fa9ba27a264b98963f2e94c4f5ece49975e757bd10bfa7656cd2eeeca78885a5a6f cedc30c07cd7a1994502d716dd31ac6a51bef02909cb73ac1a02e5cb2e0438c38a7254a71556116d 1fd6d22b33ac75b442603eec41dc6e3ae5e9ead1b31298fbeae28c9bd4f29293595c0c7b39a61ff7 4d7691b2f462bf64818769cbbccad391597d819b5be3411d2ec1b548688bd96ca782db33a82014bc fe9ac4b354bff5d9df5135e71e56ac743bcafd3c22a2dc47a86050f49d81d67b50de7ae52512d6ce 4dd14bca963d57b89f71f2625db20ddb34b4208cd71db4766ad670da3860741678e712b8b391fe0a 0e9026f21b4cd5ee7748a92d561bc929be169f928813a98440fbfe5ea9ef0fd4b0d809abd757843a 65adc09abe0a7ee72e76bc537fba70d3900edb8ad622ee604e66ad6aafa1dcee84661887fbd4bd84 a611ea7c174debe9155cd67253b87d561e9785aa3d5d44a921dbb80829118eb7148f196ef62909cb 30517da386801dda518bef7423c7570f515675b18256fa957f0cda4ccbe54f9df8f1aa93bbf1db3b 94aee0566d4cd337a2e00957ffd939eb427373d19674de3e6f26b740d5916b4631c622a0206ed093 adfe702b1d4aa98dd8bf517361f49a4ebf297149f52b87bfa04e7c2ceaa1d24ed49646112ad08e50 35dff20fc720eb9d3ca6e9c84960649786fcdadaedf2c8cd81a30c94d92d687daee0fcf97c741515 f2b6f1542b65776adc651cce8692c3e4b252c769d6c563979809a14c2d8584d69ff1332037fe9484 6592a97de12c169d77d7b95844a8d33919e053d77af8752fe32511aae6e485dae00e17e2a510f225 c8183453a82eba0e71d6a00aa35627a820df4f595572679a21fa0dc6139e693c218c07a79290b013 7d7e36adcdb9e5e2fda61797cfe9a34fc9df519b35376af6602f6a465b3268757cf331389b49775e 63aaf616eef72d9450e35f25b9919bdbf2c23f18482b3238a622bb3b25db8b362bf5668c200e0b0b 4d48eed2377e762e069ce455b25cbebf6cb06aa84ed8cd891fb3a5f161f87d89275cfef3c8175430 19a5d3a38e1756292c4225d0837f906e86c74eae0947b638e07ead8e3aa63dd547c61046277a7edf 5ca906df8595e69e26a47ed13c0aafd594e5856bf8aea7c9b87461cf30efb225464e9ee0840f9c2a c2667caa90b32173eb0d069f92b02c88f52f9ceb51c42952bd77314513a8873f385fbca4777e3a85 0d53bc57cd69f3e649bbce452c66fb5ac94b2f947ba6044589e80413131582e0453047732b8be7d9 726fac9e606c663128340b99466420faaed447f49da90c22a9c73df47bfb51f9824ae4f7d1cd69f1 08ab5e7437b208967cd0f59beecea94b608325b16061ccb46c84f5564d5f03d7cefb67dd027d2a93 f9fb463c9d0a082f354638b7460c8a2dbbcb1363ae4b326d779e57ba8de6fd23690f73c71e71ef1f 49ce1f1c5d27f911894baa7fee84e58bd6f882ca807e584b5d07010e0a07ef793e9f1da576f6eed7 0b93340f895ce6ca8fef790da8134dc51e9dc6d2b04c2e84197f0539956ba22c703a120ca6e48f34 41b8c2bb72bb3ceb1433916d6a942e64c897c476a9a1711950f4e3d2ff947ce1e42a4458318988b3 6cf77de7b18f4b43de64222bb6860bb66555b7eeed485c435d555629d56a8faa32355b0ec4547f35 e3f33766c3569007cc58ee6e4f775e4f8a0a772c472526b0424e29f27e102df97558ec7bf583c8ee 7be434cffe21f17a9d7fee7c5fc2b26b36bfa0064c10754fa981dfdd45a9f49cdaf3913561ddec16 cbe76be6dcbf6b706292521cb1094889463eaeaaf30ade1bb3d702b264ee9605d15db38b52cfa94b 92fc19650e92b85508f5b4ba1145771fee752a05104511ee136a0aee7e54e2dc33de7e41d55aefb7 79c2a8d95787518cc288b30a2323614183359247e2a46f11c051efb7765a1ea8c5b2206d9f4d0ebc 5447a726309dd33d57da525ca2be23ff9fb633ed56d6e816ed6fb15740b1ef7b5141040451414040 1101e9a4f4ff5f3039c9496ef2dcfd8e77dc2f9307b247f6dc05146b2daa8ad9e2bae38ac1fac8c2 7a37b9047695fbc064a8fc3a646a5a00335b67d3df550a58f73f0228a78366fc1cedef013a7363cf 5bb71f88bb19ee96e141f2a100eb705636f7123217af7633e56b7c098314594ec112acbc1b82637d bac743bf3de6a5fc01db172a4582bd8957664756d73c43c3639966f303830a965448f15054a2f6fe 6e48ef4654ff4f2425d5bf1ef92780321cc5aa9fc501a0fb0f88efa556373ef1f9a943bfccd55ddd 15e6b78dd3107576960dd5e1842acbda6c581329e75d3f758768effbdd287132d91797d28225a8ca 7a57cb5814ed09fb3d15ee2989ead9fbfb56685c836d6a8f94b643333ddd9e36f2f03fc237806a65 e21ca9441ce31e0a79bf3aa45b776f4bb6fb1002a37133f3ce5277b9505241c9785db2db0d2a9597 bdbad01ed7da87338176f7cb0c3e62cd5116dbd5298ea083f268471dafc5e3563473ca36536d2675 0172b61a45641ebec2643eb39d9153b236fd8f00ca6bb4032a942480ea78f009de02093b8dc3216f ac37fdfab5eee9497ea3f5b08b7e9984544a22363758e01642e598d2ae8dfdf26db7d8aad0e8315e 279cd1bdf361450d0fabed76224ef7e45c19491bbd40dc37d0eae01306fc2e10b8721812784f9aff 0d4911f21f0eff095016073d50793922a83e31e0958e6c782f7a35f35a378aad5891602ed94be726 11f7fb4bf009287f4cf315687fedac51b6a62875a62397930701f5c6c21195c61f0b52ad1d08b2b8 13761b98e74e04012415b7468685d79414583fe7abf29a719cf17f04507e62fddf56192988615fc3 6f666e6b8f74974d7b2aa859ed4bd67c5312e18b17612f21cf63664cbef76b43cdb2f5330a315dfd 54a6cee8b0b1551ad53e898560b259bf1f0b82cc5c08dcee09344e39177eedc2976455abf54e766e ebd6bc1cac7c30cdfc470095be380255a6c19b6781cbe86cf34ca829e6fe5216f3594baadc6784b0 f7f249c27e505a437d8fa784c7ce5dfb4f3a62b090ca1c03b0cd9f1f39b2749160c25c2fca046a4e eb385d1d75d62e331aadd9e662b10af62b62c577b9dd323a49620c70fd27fc5684fce7ff06d0f6b3 6f8a1dc6d29962e579c91a2c27dd1b83e05caf98b553849f6787991f129c39add13b16d9f3f4c0fb 88dbd9879463bbf675835ca10781debf2388711af5c2b54b3fdeeb96774daf02ce2aaebaaf3bb28c 8ecfda72f07976b08f941e63e76965f91fc13c3d3727bdbe297d571ccb953ffa999a347861ffbe3d 8f1fd2c9f15a775ae1d08bdc6202b2d4a7ce1d72426a93ec628317743c16dc6e633b825bb38dfe21 166b8ab15845599e06452d16830dec73ae58d8245b7c62994b2559b275a12cbae9c5bc382bfe47d0 ad0acf2993ede428b6ba56e3f8d92ca8439e23a43d721eddd9bafef6e988c6c056e93b99a4787f9b 2f0ab1600ec6eb965f5bb7dc6b7bc5b7e5fef2d4a746d847c426d824339e631965bc5acc0b2d7c91 d7bbdbb9b61aece64b78cccf4b774c9ce1e84cfe57c4bfe69f0e2b9f4576276e97cceea0d44b55ce 1cdd7a3b77a9614cd752286a1cce797299498904097197b54b756eb158fab13cf59e2ef611ee2136 49ab60a1cccee9455e6393ef11cc97109e9fdd884971865726f00cb126e529596fa353d46dd72736 3bec4ce8f664f01f01409f6504601a6f01a4407d07c3220fc20565695a8f13406f032a70e3022ae9 38722dcb8b122877a7f18fda8b38fda63916208d8b0290946745f74d2a8a36f94a2122ec51f56541 d3ceab3665e6a1f392360177db529ef04e936e6eb34f32690796f2985d7998f39fe26faa3ff0fcfe 35ffa5ea0f3c9359dfffadea0f3c93d2d07fabfa03cf6f2cf0fff5ec7f3dffef46fbb96a75cf8272 96f340d9583540a59922416534d1e27f91af58f68080f29ee980729e587c2f014de0a28dc1c911da 11cc97a55ec21755bf675eb5db0b099fd3622b6c1efbb3807347b87f083b942bc3eae6593c8d71bb 12dad8e329e2f35f23a9aaffb103e01209007ca2db00e9891c28b72f3ea840421c5e318d2da80874 ac2ac41176656494635fa31f55e9e9fc45731dea557ff5c570f769dec216def7023f9ca582fd7653 0a3aef7df2b2d83fb6ae63ef4d3ed75e9ab1b6cfa236266c34ebae4cdfacce8d4fea30fa29005c67 df00d6f838f49fabfb389d76c238ed37dabf0ded5dd2a002b46b8ce81d59abb0fc7aa2876ee84995 69d83ebcc980179f073f52aeaa7faa5ab6f719dd5f9e787773de789ea97869a7de7573fe78f12cda 838dbd21321bd30faa0b634cacc6b77cfd9b7b7eb73fc0ffa52aa5e2334d871d80165806a0bd64b4 741ffdbc9e77030e7d9a6807af4a71e283eb355974ce4fcd379c3759cd6457d9b46e6efe54779f1a d4fd3c316e5e72ae165e7790fa6d6aa36d73fd688d872b63cc11d32b265a037d6d1efb3f4092b027 5b000ff858d53ec7b91f79e741d92bc7675a2f74634b25563da04644bdd17718747528384afd862f f69f436f4ace3077d144e8a71e678a4fa86e6a0e12f20f9b2cb2818deea4ac45997ab23acf63a787 7db3bb71a6c658daceafd8f335d6ccdebaafdaab56efa7f8dd33d2e2745af10ff105da8e3d41a307 503e60bfe1e06360bcd8713e0aa2d321ef9f2bcd9a375bcb3db7a8b7668ef1d208fb712639cbe957 cf0f2f0dae66a0e9d6fdd4d9fac6873a668cf1ed59bbcd1eefc1157bbfa79a39df0cd5ba9fe95dd8 8ed9fee25b52fd9f9d7f0580b1f3e77755f31dab36866f805627fd645d0c36a2b7835bc8cb5110a4 202eebcd07b9f2f3d6c39ace868b4676cd2557df756e0b8032fbf9d5de90ca6fe1a63cafeaad9051 adebb2aa86fa6de5e675c46c353514ccc617578afa0a4ff11d3972e7cd9f2269b4af2a9237e3dc2f 0b9f4065897f003a2707c9ba18eccbab0dae0198459e9f2588940badfd92633a58cdae5b66ffd1ed a766f791dd59dd2e0f797b5d215d56c74149d41e705e57ed5764ab348d26df23b8b8065ebeb43bb7 9ed21b901d3955aeb6a4f3a3d0f829004ce92980a0ee0094cb95d893a693c526f83ea8b6c7ecab7d 6d2565bb20a5d98e57782dc1b31c3cf33603779107cf2e5bf7cc6cdabf616d75aadf5be84a7bd836 a532537a7fe14ac3b3124ee686d2db2e3df9f4d4f3f2902d37a4090f9ae74bc1699ce7ba5eff1349 75f0af47fe86ffadda6e0aa0a24969802ae75815dfb0e161dd52fce9d27eb830163f6eb6e63b6371 9b7cf17e061e7a5b42e9b67e7f8f931457ab45b7f9c5ef0c7125fc64691998de514e09ba224d7ce7 71be54b3d179ee30957341d0e32036cdd444485e557f0a001f1fdff505906e348c3bd3a9082ab69e 4ee69c0f22268b31c13b834adec2d06ecf4d69eedabbaa1199effa3963146406be96cb6a43abcb9f de652fcdc74affed2de491226ca4294370e7c56c248afa69751321effb913ac1d8c39040744735a1 1c35d1d3e354f931625523f5bb27bb3a834a64a44135e30e5ede614c05231e11dd2b714b26973b96 4bdad65ee0bcfb34ecbeaf86d62f6a4e6d8d5e024d692b03151ac899cc617a5657cda578ede628e1 5ef279a1324daba7edb1e29c6a03397faa0d8bd5e33395468f8efb2aff0d4949f51f0e7ff1bbea3c 3302e533710668de492773e307af4eb1bef1e56afee82295dbc5a99f69f331906ba65174a250b752 b9accab5ea65e5642ceb72e6fa48ca7667ad301d8b6be235173683ebe644958fdc7157dc9f8f4df1 f938ecb3dd748c2b7ae042b592a0fc5300588dc512cfdfde463152dcdfa732a03ac90fc28388e07e cec9ef9f1beb76b6dbd9867a57b0ab76bdfb8aa7edb2f7d4e5c0e76159ea4c6a520191da22deac0c 04f4a84e8f6e76bc3cf8ef2e7de8b666227f24a91b3fa8be93776bfbf793aaf0fdf706e18ffd63f9 a7f85d952cc599bfcbc59ef36cecc9d607c1bb95597a25c8a61dfb681eac1ef99d7f632c276b59b7 bbaca3eeb7978f32d2eca2b41840a868f488a6509dbe7a47f7b69b1cf81a8af127244feecf85fc61 9f19d5354e49ef7d4e0e5ac83e838c91fd788cfd2392bfe61f0e03d80a33bfab7e0419a064211d39 13a4178ceec1dc5d1be6c6763f3c634aabc5f6565ed4458d1d0e1f97c18e88e49c77ce9de1b5070b 5b665a3f7ad4b5f35d94218b8df8e13a33df4fba36c1cd1b26c76afbcc852dd95d27465464b55eab cce5c92ef253fceec9a31350a9ebdfe971a8524dbf3c3e68f933541b3dc9ce19b30eed3266e8aff7 5a777699a3da23a09b92513afef996233382a518d0a9796f560f07576ef1a937dae32e1b7bc2eac3 f36a67744eccae4cddce0ce9e51f316ec98280bb72338fecf0511efe2900fc02b1aad29a820ae65c e2eea9970943a0d43c8c263b4e036bf4cc29e03ab78afb5c6bdc3d48262d5d526a4a979642ee2992 83f1e7d4d20f85c3d12e56f874b86b70eabade6357913bd955087ec9d4309aa299f359a0fc6c74a7 fc9c92a39bb908a19d4dbefcaf484aaa7f3d123f47b3d9f8913f9881ca31bc7c3f1f19bc9b73e469 c6ed6cf55b8b8281a0d3beeee672f4e56dbb0779717554f1213d1f276f528f0e00e6727ce6f386b9 65715d65111e492e01c66eb8237aa7f20b6a7fa6c96d94d5e2ae4a45eedbc140c96c8f4b04ddbe06 11f253c40f2728198480cde39eb4a0460ed2fd7c9798c83919db2b9623e32add33573a359d68714c cc2a0a9c17cff7c05385ddac681ffb9756c8e7f2cb0fb76abf8bbbc79aaa3034a8b7e836170da81e 652fb643dcdc92e7c7e74466eb833b992d4519329bda2403fcc9b1d1427f8ab8d32fc7aaeb2306d0 6e557db5a39bf734db29fb8e952ce94a7dc4a216a89d999a2a577879b9e95e44fbd24b169a3946a7 9acd2bc559c85de3c7c5ce7af54b4c0bb150eaa5ef5a5b71361e92f2b085913969466f162f4a24ae 03eb415c1b9b1c714d3deb844e1cff1f484aaa7fec0064d3ceff36fc1cbd0f2fbe5c06aad58706f3 2bf53cea5aafbf862f52e6b990f595b83f6f4b81720ad759e33091609b3332037fe7a4b437d31e4c f3541416cbdbb3e03749657f4dde7b6eb493bad840296b4be0b39c809b5ae716e395c649b85fc3cd e6b6f35300449e1440854a2f5f5ec5e31d7b331e5fed0299bcc5554161675db27cbd2caf56ebc5b9 b6ccb0a7c3b32d1ea6f456e38cf7d5dc3142dea10f2a1f52237c91d94e17cd125944d2e86645e63a 0441bfc7f8c32faef15ab7c9ae99d3425e37970f67e5f9d3e2cabb4bdfc9e5c9f60700e5340b01b4 529cf93912ebdcca38e3ab437a492a8568e04a772842c5e7319a9d8edbe5f690cfeb07ee6ec1f26e c75349ad933eca990795299d5d527d2ca3cdf5bdc81166a603e176a156c5ebab5a67bdbb7726ab7d 1cc3aeba278e5b9e5286b21cecab2ef641b7d91f20fe35df2d283b36e46a173eb8de1d4abd006d7d 97616cb23f539d922f04251c397eb6c301af75f52547e62adb5dcba4587ae0678edbd98795c86576 a96f906bd5245033fbc4e938bc5bb30d3fb50a38bff0dbb84eab9c544e1bd879f21e62935c73b550 30828bf1507e0aff7d23ed1b0cd9fc25ba29a284bce49710d40ac7d3c024930965c70cc365f6b761 1166e9e2aec1f0d57c524365931a2a94d4501d8220e10b83d328b74fa6ef9f62c1a99c8c3a4dcaa7 3523b6abd858e6520b16f3622b35d7d683e2bc64528dd98d94075f2445c83f76fe1546012229b5db aeeea455267b38450dab77501a30c5978e1d8923b33573c7c2964f9f1ad867abf4415240dd42b15d 07c5eb76baf15d5fa0feeeadf8f67db48c0eea14fb88bb64f4e9261978ba2617f3c286996babcd61 5ebaaf93cae9469d9af4fe31251baaff5324f78d8f78a4a4ed39eac4bd6a345fe2b52a6bcfce4326 20f8157daa9f182a73580a6449c92a04095dae6b979a9aab806d3e63b162b81cbc731fec3cfa64b1 8cece617f3bc09cdb5a582cc4bc6099de195ef8769a62645b7a7649ded4f6c961e4fea218b8dddc3 89f829e4d97e4a9cbc85badeeb6ea8efd81291a6c6be9525b5e113dd20eab5fd5dc90257476bb6b6 c79611df5d631f01ddc65a99dd629efb1ce61a1688f3d2cd96677859bb4ccded599fa24ff636b177 ec7d520f486becf2e473dc024b7f1408f86bd44b13a9612413d93f11ff9abf1df91b4029901ea014 563f00221a5d000b9b0340b68b0020af740d942bab65fc2fee0810553200523f840076af25004be9 f88789e612c09d05f34d71739c08a0d7e31619147023022abc5f8f69077a91af552374eaab5ed860 89b17f2816875e4a56fbcf4563d0757409edfc005fcfe4d7fcb7aa3ff04c5651fc6f557fe0f94d0a ff4bd51f7826d9daaf55a16c270520aed703b0733c01e4c284b165257e2e4cb9352877f4f8d8cbb8 0384322280949e71b0e815e31fe69acbe89eeb331171fc0e868d2aa5adfe7a9c0ff66bdbd6a357d5 ca9542a604d7020f6ff682f6a333f684913f72b3d16ee0c0d9a06d13dbdb4f00a08c6e01a83cfa66 d29034e903a4a00aa09cd5012813a306284b3a0eca5c28c6bee0019028f506089d43a20dfbeebeac 833f7f51c537153e5b3e1f328fdc256c2e6a8fb0d95f8501b7dbe7fdd054ab7ebfa0b6bd11ac2675 81d8723374561cd2b7b622d97e3066bbf90300083112d57edca4c6720090de33766a3bb1a7c1b440 251512a0ec43f131b9f19d274d1ec1fb65b3d7d2ab7e105be14edc4f02dfdc11c1beb7e7828e2e4a fe312ddffcfede72bd77359ff646ab56d99d6ebacde7c51ef79d95848eaced95eede5f43bd6ebca7 2bf46f48b2b57f38fcbb6a7b9a06708a1ec609d5fb1cb766dc729594d406956681883194a2edac6a bd98529c40b3172a1f04e2acee477374e01f9dd6ca7bfb25da1ba348b29285973ed535576e8c6d77 6a50d153cddd206775536af6bd6c762dab761edc5f4ba379930795ea2d27be2a3f00805a8e0da0c9 2a03e0c679fcdbdba89a1237682d052a43a7032a4c7b1359cdf9f9c5d8901976da5218f49e838c3f 243315ef4c073d57e92873373f6089a77a3bed9fc590979c75ed7473e0abe9d99b6c216751c572e5 b143cacdfb8b7e24f7cd4dc62a35fd4aeecafaba47fd04001a870e80f038528197b7497c81f695b8 3547e95831157b3a38f97aca4321ecb8ced57f2fb6c910453f9d4152ee65a8404fedb56c3e4b9fde c8412ed9a5bd79a668cb4e8747ab8e8697c76e95b5ccfdacfc36de46ae608c2b35f436ab171bfa7a beab6a288c96552a4ea8ff09c9d3f3af477e5765b9b8eb3e05b3f81a5d5c40a5b18a559d6a37b21c 9a08837ce3100c2686eac9bb91ed2eecfbeb0957e639a77c2ba09653103a0ff7be98987c23b7bc0f 2ef764e4bd213eaffc4d3655e5369bbfad2bb66ea5f4ab89c13a52ddd4544acea39716a5204a503f c13f0080d620f63c0bf9b87bcacc4179427c173caf2cb90c400bc3eecb6d2ed7414497393fb33d48 2ea6c0c6138144d7a64ad5b4d5ec09b0d97bbd6bc639c4da7130868eaedae6835d4ba788d2d77ee6 a499f992a6a1cb75a852c63977712b7645d97fe8a4d1e453b907c98347b5f403c4ad997dfea68a14 900528b3dc77fd8e4c52ebedbe5afe72e97fbc2ce3e5df9cf0c47bb066dbd39df5ad43c759c65dcc f999dbe5d22a5d57e95b55c735baa391dc64a2da6a6f7d71d92a73693d7aa2122c7853e95e83b77c 6a3720e973802029a33bc5b3b2d20bff8aa4d0f5c7ceefaab74b0120d53a06cab74bb2d8849189ec 1edd097bdddedccfb6bd8d0badb77ba7caa425ab5d426ff773bafabca92cf1bec22b3ba73db439a2 329554fdc2be8d9ec29b42b2b69d1c8103210f9b175e3a6f9c8b34a9cec3b3c21c72e7f9e8521435 992988cbd92af703c4ad09b900728d2240e6832528bb7aec197d5fe4477567d90a4e7675ec2db221 fe2c07ab3887f7ae07f314981723af3e1e57a45f0c340ac5536a130705257c72151928d3969c626a 43293bcb61e7395da4ce85c64a129784ee8850b799116ed1a820948770124025dbcc0ff0bf55c971 acfa313480a220f36257c37a90a2cb4377b53016ce5621368f576eb3bb67998970bddd98649a8f66 8feeaeda6aa04039f685ac3ce26b90345dbe6be745e7de1357a5e35430da222194b777fef4a88fcc 13e95ec0a97a7ee78ecec4cf26c8fc1adf926abc8d4f7cd503d0c7290184c756a052f7f4a430990e c374a3ea676f61e7792fea71a859ed2f4db18492377d0f1d75f2d1d75476b6b395681285f2783c4d 49b9935f381709b122189f5953a894c749adf3547db79647a635db1d1bc15d3d7887511843ccc490 bf48ff00b167d9fdddd3a4d6a03208634f124a87bd4d36b93dbdc5e4d174ac35d7b3f64c7a72cf29 daea4a1c1d4e636ab072e92dbb77f9bc507c293f87807835e4ac70a766d089ca966a47c609ba071f fe2c0e9d39caf0076126f10731edc55867626cd2fc6187a592d9abc9f6d7884f7cc3037029840012 ecf1dfd6ef407928150839afe45eb37ad5a92344f331908e1da354e017faf673d9a99de6f3ac0c0d 24b90424e54a3fcf252d7c091b0b4b9d2816291e5b05b372e8f8a736dfb7f9c95e84b4cd7e744784 7d7a233bfb34594fef474a2bb58faf97cf0ff00da0beaa75008172e618abd2293da2d1cf27481d2f b9e7bdbf2ad92dbe59b92b03a47a35e1c954db4d38fad2dfab829c0d23fdbc0c26b6607a7a70a207 a3cfb1a504793e6af2c8fe935f36f699d43cb904b85987c659f5681e586c387bb0aa904971393dfd e172a7f7fb07884f7ccf07f0300b83724b8d6311ada0bfd8a5f2f2b3ee2ae558229c5cd08f4893d3 c672ec96751a85466a77d12794b4b63b4ac585ad8a9542d93c3139c63db67138e22370cbeecf1b1c e2e6a96a95c55e506f77abb7b11d82e3ec0e21d07b0c19ecd6dcfdbdbb1ad66f484aaa7fecfc137e 57a5cb0828afad4ddc4395f5304cd1beab87a9c0dedd0fbe999d16dd5b79ce1735960a7bca6707af 25d51b73e27d77964fcf5136991571ec60db272f90efd77ee2b329aeb06ce459680aa1bb720db499 6abb30a7edc79ca1e96da4d334b60e19d4523f0caa8ae007882fd0a1ffcdd6b4761994457f13f750 8df83cbe0a8fe7a6c03d1ea7635270beee4d9d690eb31730115ab2727f2fce78a5b513ea3c291ef7 9ca7f3eff3d4da4f1ddfe78ab3dd676778ed3cb35d8430cd8cbd16c5f5bc244ea3daef1eb50d45f3 b20dd9b14fb5cfc70fc5def6ef1f20be40e771ca945fa2714f9ade44365a55fdec9553ed9d713fdd 0bca87b9d67a48f2364a7da1859432ed2faa12a45da78253cf6f8f01c08ffc477d5cf6b3706ab2b7 b4e5ee36f42662ea957696f2c510dabe78afb1edbf0b13526cf5487214dc6472f49879a4106a69f2 9d55527f2229a9fef5c89f00f0000f7f9b205519c09b57eb239d5c78f3644da9386c5e2d9825b56e 2ad02f69374c49d7ddaa2a52a9ebf8d4197f88c3089e70fbf9c79058a3f89d59bcabce9e36ed7abb 90e2a9617a7bc2ab25f2bc283736caba31dae495254168f348dae40ff1f9cb6399dc265f73b33f00 8029f60590bb52fd06506a0d0f86fc7b63b7ce7ceb462cfb79addb18d2974c5d3365cccae6cf9b40 689e82ce7372101f28b157657ecfdeb9acc43cd7c495e69cdc83ea13577f9b2e511f32975d163785 f7bc46acc6fc0837143719738b1b97dd0537a8b48713a5411637eae3fc0f0060f51c81f2c8ad46d6 ab3877e1a956bfddce84ab75eba3baa2d4cbbc5c0a95e7d9740bc947830417489de3492bcef8f902 db70e5bcc6eeeaafb240f3e387460d00636d33fb8e4fe64ff5f4a6c4954ac4fad56ae29bcf608ca3 67995adbd38abcb62b177f6d45b9dc9a3a154b7f4352e8fa87c3007edde2ecee9da90491ff29dfae bcaa6bedfdfeaee4af71f6841f0a67d119d3bec0f524e838423b1dbe108a53f6b1ffac194f22690a 8860bf95e6ba4c2ee06f2cb059e5a7364140fd17fe206639bc6aadcb6ba67eecaebcb3bf5c71d3fe 7ed57e3dcc55fbd478afdaf434fb03c4271ef9bc76233a6514b44ff2ad08ad75b445f97230b53309 6a98e0dd1de574707ac131bde940fbebe4de64adf560c8ec1bda8212b121b19d9521962c3ace6903 fbfa85a8448281d7538cb3de4db968b5c776d9e5cb55d1e5914bf7b1f7ab990cb2c0c4e381c7c68d aa858da189ff0380b23e78dd331a4f694f0ce7946cb171101928533cf58fcff5316d6c155eb51ede 1ec1aff99d3b1e24f33de9880e3ad4f8450fc965b630dbe00590d4501f490d55e2d76c8349a6f133 ea323aaee2ccfc433cb1f3840158e622270349adda227fab8ce71a9e7cb81507c23fe13b55fe2f47 cc97cc6ff4ea136c94b1fea0c5ed90618fa36750e5b1cb1ee7ccf15360e9827f63828dec511fae0b b6b37798d450b9a4863a4263bb663bb62b0e62bbf464191d9ec9f8b4e5e06de1d8797ca7b08c72df 2ff2ba2ace97f05d9b97ccb43dc3ab153035190c8ab1477f806bb968ac1471b4c2c547aab239882d 35b93d39738457772c3419305d8bc6a90f4bef62c1a1489694b24690d0e3be7629c25eb7dc55b0ea 86fdf7f2d4ef6663b1522116cb238b79fe83ceb565d49c978ca03bc32bee706a52c1624a36f29b89 cd21c9a8a149fd3590c6ee71a6fd0097fea8899d919bbf3a00a5b5e0d05cf641475b38bd9d456a81 2c49569d3031ab8fdbf873be666b2abeea0614f56d348071d824dd3d2e94594b5ae435f4325f42a5 ebec4664ee33e4115953b2e63ea7e833082674cb05639777d2e316700aa34088fb9b5e26db1c464a a1f3aff89654ff67472a95ee8b236f96a61c4ccd47f4c08155b274164d9c46eaf68a6f04de72f00a 3e58e6ece417f9cb039e97ae46758623466b6a927a6f8a3a97d1c466f4d9a4ee4b8b31dbe596e356 7448c6a78df821430e2369450d4fd32d37f8a82b7e70c6086190b9ade4febc4ce83118e30700c5d3 fd0c8a4ccd8c71f900a8d21b0178b4fe7eff067e56e2d8e5bd43e27f5d8700dec5010cdc7a0b00ce c63f0f3d160040872304a0c5bd05a0416e02a07a9f005069cb45b7b3708ef009305fe631ebbf50b7 f009e9369c0beb61070ed83e0c7b91af26abc1b99365f727f857d51f787e83dbff52f5078afff3d7 80a2f69462cbca1d14857b9c024ce76300af2831b66cc6b14b4dae00a4e8c7c7dcf416c0782e566d f72d0085ab3862bc249e87671b40586e12dd3e353cc2d9111b95db5bf1653ef4e4f5eaab5a7c7821 3d7640e02aaf5cd02ebc219f5f3d616f38db979e732e93fb0140d17ec9b162336e52c78fb32a899d 0258972580f417af6f3d6dfb4401127b00a45ea3017cab9d63df9115fb329f8858ece0a8fc3cb65e 8fb53a7a91c17df5aaf26f26746af029a4af7d2df032c43368cf39e0f74654c63bc9bba237dc88c8 f392aa976ca3764a86257cb7bf46aceac74daaf71fa05483e3b3e9297300034b06c8818b0062e56a 00519b5380505326965dc8d1a6dc79bcac7e07bcb6d771f2900e9d60500f99f9b81f36c0060b3c66 bf0d38583d046dd9bff83c801d4f48c3a1979ab532ee441b949e17a88b38d0c184acaad9ceff0049 a315a377dcaaced402a56dbf00e0868bc50d9abe00e421c5b14bae528bb198c5d25b2642d9cef945 1de07bb81bbdc2b02947b980b303d47f4141c73f709fa9df474bb807b416eb0943ececa5b8a3f9ad 431fe5e05938cb1f47c7a3a2035ddcb2550d86a5fb614aa4ef0782c9fc1aa054c829a00816b1e765 5104f0f2b38c4f387c01e57252962616f5efe024425844d5c18a0edd4bee140423558fbb73d1f38f f429e3bd3912f646eab2e14a1f7ce84e71127373d913f58ca387e3b3f029dd1c28445cbb52ac036b 3b6ee71f8ed243eefdce367f9bae5fe92f9292ea1f3bff0450aac65aa502610328cb41f1358aaee3 d6ec6bb15df60dca06d788b695c32cf4f00e19f08ec5fba70d75f1ce8d81edcaaf1270f3a56c52e8 7a6288873ad7dea3e3c08c33b5ef488ab02bf302673dc0e4f260e4a56d72da32b8f7a7ed8f21a8a3 c2f5125c333abc1a7d7e0050ea55be45c85295710034d1e32e09747150ce2e6255a3f10115486fbc 9a83d528381c726b7f943eeebcecb22b3db58b7d736eeb93ef20e4366da3cd0e6451b35afdb16be4 faa64f14e7f757ffbd31de93dcde48f79bdab7d1ae2be7aa221cb816c96356bb37bb69b5a6dec10f f0558d71780288f5e2fea83afb2e89a2834a6af88e6a0487869ddb2819a8ec8f746bee5ea238035d b5a383531679cdb241d57eb89cf332bbe759daf8d44a2523930eaab719ea76af713035bb16e53ca9 5f6794a4dd97c7bb5631eebe4a559e9fcb6e207f946e7610fd896436d15f8ffc09509a37d558958b 9bd448c5fdd17c932c352268a032243fafa6b582fdf7a3d8f272be387aae1bddb56dd5949dc5903d d93ce498ab71365fd66ddedc87f1afb962d130addfa036a4239d465d43ebe5be6add264bb59ea5d9 cb6ee16a8a7f2b394ab73d00f2f1507f4ba2fe897e005022e32bb23417be5f608653501520fc9e04 e59bae4756837c87dd7abfe8677aefaa5bda1c7b0e3acd4caddd1827efef0eb337b2c153baea4dec a6e3a7fc532327b748a56b5cf6e28e99b212f48896d26598b11cc10f521e908d640a96243ab8298d 192d3c2b692d3ae7676cf80380123fd563df8b1b774f8db8eb34c5d8f3f3d0a39a310983010b67bd bc60218e79c29217f996bbbdf5cda3935a1ab91bcc5c57c2e6a43d8ec14565b2b47961af6d4fe1f9 ec5b8eb47741fa483e2a8d2dbf7b56a022769e19dc41d42a8e2e6251db134ba76128e0a9b2ff3724 d9da3f1c8e554771abf28617f750c33a400299049592a3bf5a39c8f5c7a6f376a1b458b01d68507e f03bbc65281d667a5d93ea46b336e85e6de2bca484574897c1e69154d5e5d4f0104ad9ca3a7d9e0f 3058d4d84d4b2c3d8e5301afbd680171579793c91ace899c80e054cd7a7e02efd70025651137a9f2 883d4fcbc6b7eb4c5645496a93e1fe001c4f218dd7b3bca1d29647640af7388f456fcb4167a8a3cc 7aa5ee2877a7bcb6434116ee8f8b346dac8cf322d774c5a59b068291f91484723f5f3b91bbfaf068 071be2481fd2c925706cf4a7660cc13bb86fcefd017e570dc2386cd2f00628b7acd8130b9375198d 87abe9c4d3a9da9df071d81e3fc62275494aaad7f224ddd3eafe00bb74d913258bbb2c2f4d6d4e3a 17a7cdab08177d4b28076a78aaea6cfac8a44fe583d7bd750f6dfa83f1617377e2c371d188b1f2f8 4366fafc277c4baa7f39024a26710550b9100238629ba0bcf6b7d1f6635cfc3849369e46ba62dacd fadb31d3ad7a785b4d99925e559e9d4b1065a6ca20c43792ec7bdcb9949a8ac27df1524f1672fc8e 4f7300e61e3c7ffce143ad51e4fba949730f246dbe17ba2d7e3fcaabd7fda8507cee855ec5f9017e f76c432140f27cecb97b252fbd5e4f5690fd4966a43dcbd2fb6a75d8fdd55051d7bd56b07e5ef504 aaae80813b9633efc6fa8c45075a44a6cdd3c9ba5ee5e3aeb7360e7bb8eef08728f3da0b5e2abf4f 57ca756e8a4dc65cae14b05c0e5a7fbfe693839e0e371dbeec1f203ef1cc0d4078e30590aed28abb a7f4f6d5ca2e046fde4b4bb6a35267535c94ceb77583b7f4dab998b9bce075b226a492ba9923a9b0 2eaf447cc950279b4af3c75db0970edd554be707dd8cb51f97cc809b658d2cbb781b15b6c84283dd 2a64a9ddea85c8bb6b47b6d9e254fd7724eb0bfcb103a00c17ab9e877184b7b9c5aa9fe236ec2e2a bc0b0df7bcc5b9999da1ca347fdd70b8a9faa6f751c4b83f972e0fb6271afb702e54b9297964458b 3bf4aa6b71ffd9e7d57d063393008ad5b2bcb7bb3ef1cf0e413898d9904e8f416b1382418bc19941 4bd883b9537beb07f8ddd35d0280c8760b54ea30190c847bf22d3ce74120a429f2c3f96dadb559bd 7eacea97a37308e5d9e0533aaf8bcbb6b0d5f4e9d1b39af8a117c80c9f5a8d4f9cf2fa28ec123fdd 76781f7f32646bfea6e9eaa244b92bae49b1486119431328368b260562ba8e63d60f00204430005c e73f00f1835664d573b8a76c062bbbb9580e0d4c12fa3a5dbe902abf761465925eb85289750b223a 1e2633564eadd4617838d66b6b3edd3b52dce5dee5d9d520907671dfabd3ce7a62515eaf116ec351 3fbfeded893a09ba6041822a7f2441ad74dbf2dbb1f56b244bb6265b00b52ef7dfe673949b9fe6ab 19505317c2ce3d5394a1f795acd75a6af09eae2fa9de5690b4a86289e643cb9cd85e013d44713acc 67dae93987ad761b168eea1c63e1b640ef8abc4671c13cb904b67da6e19382bfc8916994ab6da674 38dd4c573cbf998e33fa465a8fed1f0040eb8709e057980665b1d4088e76d4b21d73096eeb079c5c d05a7bbeaf28127a9dca051fe6cee5cfdb3879bb3538802580f9c991e970cb6d7aba33e5dd9aa11b 75866e3d82e3365a9f64f233a7ef646684fb9b7c97c810d849af12a551634a949a2e4f9490814e14 8363325ee0bbfd3500b4f31e0099411950a921a8ab1dced1ed5add5c3476d65515691ae4a595341b 8a1676678466f8bc1efb01f5e2a7f76c895b9db7c9636df778e6864c33bf5f50a134de6e85417e4f 4aad40dacc1de54ae84dd3c30df491c6cb0c545d3f9afc6cfd40ba07bc3cbeea6bd37cb93fc03780 aa371c504e0f33c1eb18bd8cb993396a4feb7652cee1493adf713c2dd60783e92968dff9e3a9f8be f34af5f8e2d61154d8d96dbdceb476d32175b4df8bed9890497286edf69b52792611ebc5f8866fd6 b3a4d1d696c966d6f5aa5d5b3d9fe3f9ea29267934dbd7564c38b57f00005bf81394f519b88fed13 ad6f8f4b56f96ccbdc99e8a504a12592c8773cf4a3bb3a9cafb2c0cff9abc919f01eec9c46b54887 3501a586fca8b3cd8ee0e9467b592be2f65169fc91158f7875a1aa6bfa7ab7561ef206abf6b6052f c3be3a58f26e875ef267495ef244d6fa222974fdb1f34f008874b61ffb344f5ccb588ab80c0a17f2 7c8bb8dd692ff7ccc319904d7e91c913fbd59c12d987bf3677cd65eb45f7b78fec560a08982c8ee0 efe2c0f0061e1015ea3dc36bec73bd66427fb7e2ded669f91afbeab2ff801d4cacf53fd8e82956b0 349b1b2ee4b6b05b48af82fa03b8056a9d749db7d256c72eafc2762d954e15fcc4f59f247f591a69 ee9e1f76590ad288dd6e5612986e237fa3c48bed6ef32dfebd59eba31c816e6018a7a25a73cda672 bd159f7d4d96d1c25f2e076e9ac2ceed1cbf50fab96446fe227fed3ce6da9a01f325f284e6a547a7 3bbb2d1edb180ef56b18b90b3755b9427f21a95a7a79f28a1416479cf9254bd5b73966ff70daf4e0 565d7ebfb716ce77dbbcd81337b779ac8e1a465243e59c75cb655eab6eb84e2d4f7dac807dc41982 65945675312fd45b736dd51ace4bf7ee7c86a3e3cdd4a437fc946c5cd589cdbdec09dd9d65fe4452 52fdeb913fa1350ac5893cb59e73a1fe1ecff8ac771eef76e3ed8dfa307e9ad486626983e7ae4d82 2c3d47385df697ebd653ddacbac171b71c00ea809d47948465e44d927b2ee679ec36d796336b5e32 c6ee0caf0c5e5393ea7ea6a8db8b25da4364ec1ee68d71eb8df747fc985f8c7a996817e3c3fe1ab2 743c8e05bbd64d169de3a5143dd8d5944777ab74d4cb06b96c1e6b9744dd55d7cf83e5202ae7b149 aa5a5ecc73e5fa7c59cab6e6a55b7534c3cbe5d9d4dc42cb29faccae27f6ae404eea419619b35dc0 8d8253781cf5525e1c58c8a924801a0ef32963705e669dc1046a447d8598e67f0071d3478687e190 eeefb6f36d639bfea82881ea4b66d5f5b2476cfc36a4f9b2b055a7e6e6fb0a6f42376073cc7672ce 88efbffd6124bea3e169924f0d87592437382ff285c1a45882faca3a83f4e7481eed6964a6de5b56 4b9dee8dc9f7bb78131977cc7d69de21bbf575db3e75e9bf21fe35ff741814261f0614727501e4ad cd15145771fb95acde14409bd21140f23e7e8ec941164064a90ea0de7806e27e940625e77a06a56b fa0e4abb669c37aed7d918ea7721cdd2f8dd01a5766d125d436213ad0f17fe757f1ad20b6d3df490 e2bd47f0048615ec04e911b4469ce97737a3fbaf010a447e070a95ae18cbeebfebdc16c55c11945e c358556e0900f2650740412607201e8955c9f53cf61599d8f72983d23367829298a8ee36b1e2ee8a c6beef4e742b5726d1fa32c25f6691d9bfd0f1410a2945d203b770496281a0b5122c3f809987bfbf af1ede31aa99bf46ac9a899bb4338855a7d73b28be9b10803af422561c8b006e3e9f006ed50bb16c b701206533ffd6a1497517fbfa32808ab015ddc44e14e1ad592e42cc4df965cef7cd18fee4b579a7 57a13dee73619d9f9e839630527d3ebdb87bd16c6a79a77cc7f2065ac57233ecddfc1b92eae05f8f 8002578a5b753a3b8382f47a80527f81008853e2f0a3494900dee5e2ac753d28c4beeb168033c7f8 6f302d36327a2f252aaf7cf365faefe04592a5ccab5a6821a12d8c935820a447d430acfb2216b0e8 73e7074c5af0a27d5af50651f1ee9e9715db1d7fdef653995c2d475baccd5f23b6435990ccba04c5 341ca72adb6df9db6886b9023023c6aacf669cb55ed745008b7237f6951751a5b0675edbe6fe1c3a b3dd2da423d10b1b4b2d15b092560cda1dbbea0751bee7f3343af57b8d3de30d2bbba33ba9efe4a7 72568de77cf24c72cf673e949f4ee93db32d538ecc5f03148c7aac4a6067501cf5e34435d4ab0086 a235801d4b01486dfaadf443d15d543b2f6bbe9985cfe638490ac3e66c2c04dcb2a5fba1d472fc43 65003c800d73dee9b32c7b43966e79a9f66de29ead3cf9545cf4e068615d76a0fee066e3bbae6d97 5b75e7614737cb6c1343e39f907c9be88f1d50f05ab12ab78e5505220e02db5e0dc02b84880593b2 f49689b3d6830fbf28936a863b61300ebab90fee1f4bc1de7bb7dd8b972e2a7757da69be9b35f5d4 f332f7a06741f06adf41e4ad42df5906f3958d3b246b6dd3bcf8a0235a33bd21ffb8879ddec3108a a1719b985bfdd700c5d4808bdb752f832238f8f1059a4c3dd7bb9bdf5e455462d5500f5ff6552e84 edc2ace647c4a7ef89ee1973e5034e3fd5f5507816c9aaeeac04c8b60d1f7ad9957c2e633d4e35c4 aa7a9dd683a94c2726573f5006b86cf7c6a878906ed30c73bd651526a90b5c0b7af3aa43de5bff35 40111ac7aa9ea88052cf0801e4b59355513032294b5f22f220f9216b62691f34d1b237c1b4b69b7f cc27cead55485ee1d9f7c8e12d4b3ac8564d5ddd1e4db6e3dc5fb56264bce14fc618cdf3652305ba 9ddbb4492daf8b99bad375ed29ea30f4d23482f00df561c9d78be3ced47f45321133d9fea65a84ae 1750e241145fa3c358b5476d41b9bcbabc1ae2c20e7aa01679d39c997feaed39eae062a16fd9da61 f16073b5ed3d42d4bd210a6be926cf50fd963ba1ce7561945efa35574d5e7ae9f0a05dd18c0f3f54 adacbd516b589ebf30b7ba7269e283abc285a82e1f807df93540b181ed4171f854e37ba9fc06f092 ec00848fd3a04776914c920d3b99e6dd4f436fdf2dd6f994436c1b258be94a4d937fe546c699c796 b7793a4f5f31f576d0d72c2b6bf70b69a8d62efe7fd6ccc9fbb22bac8a97a6aeb7947da680c92f72 be93fb4e1c45bf999b2e8d7c366934292dd3eaaf115b6ef8f85ecae9002a3762d5131bab9af2f6e5 2c6ae7a03f4febaeda512de78e63a1f51c7919f32055ca465619b7af7a2a4826fbe93846e31afa28 336a5d088f97ddfd7151f627e12ebf3cc99707956b467a6b69541a43d3f1592624f23c4353c7732e 9ae8a27a6aab622995557e8defd37349c5aa4ee50a207c9102b07dea8072e6ba0d3dc13ffae9a52c c749dee66a5307c47e74511a18d3e7b1705d3199ba5eb6d8a14a8785c585150c52e1675b4e3e35fa a2f4213aba9419c0f679c6d692d7aba2fa1897446cab7684ab5bc18535bbe5637817019984eac9cc 3e2ebf0628d2fb032815fa0680cedb3440f25af75b980c42ffc879396996cc2370366744b25a8276 bd67dadef37ae55b69bdc269a8cab0d5cea51dcbcb60b658cb29a8484b99f07938e79faa2c2e33f2 5db8f5545f40183777da98a5da092545ec68f78b6c8cad7ca4decce55817574946f0ddfe2b7e57ed 2dee00720f198074cd6ef440ae9ba0bf59336ef195616d3a3ee3e66936946f5a5fb0f43838f8a8cf 230e5f3abcd790dfe96d321252929630769e8757525c8a1b56b805ebf3c9d4bbfaa99a1e3847ba8b a70e2e7d2a1fd8666b7668f947ead0cec0628cd1850f94a6fc6b80e249fcce262a915b13401f2156 9d999da8caefd6fe4843364fa4af6dad64e8609cde87a72bde1a191a9dbabf2efb73b520bf05a196 7ce3aa775ee0d64c84e2c44a282b2875aa0a99c39136a2cb815583071fa632c9fb1b9ee726f03e7a 99a37d148db731aec7fd494f2b09e45fe3ab0a4acae111a7fd7a16207bb7f36a38dd8537fd984b07 15972b13c0a96429b09b9e3df03ac95774959d8a8172ca6673720666d033567ab74578bb1b0995f1 607974ecb81bf6248d3bb4adb3c4f76ebcb117b24ab04ff5c30277fee03d6e728e086e2261b1b971 57382965c95f2425d53f76fe0650d4e45835b85800c69cf8b9748f3a21b72a4d5d6c424f2d5706e3 7b165bcdaf4471b4d36890562f3d2c7e409de75efaaca17d445cb37e4ba8bcbf9fdf3d3ee7f9f9c1 6f981bfea0e2cc1e7c70719f7a0c756e5a5cbaec652d65d9c225d588715eb10515e1d882b597d945 ee28fd1aa0685f4f00421ef6b798720c73a09cfeb48383a4f59feb4bb5f7e0ed55e3a6e39589be1d c394da495d6565786bd9d29c39bfc51b5d2909e8e1583bba48b77fd8d3f6943f4ef9f55eaccf194e 2eb78fece50d5d767a00d93bb8374d46423286a3571962375930c6336219c39d493bb8849d7f8ddf 55937927b09dfa6d71515fcce12d07d505f43e09aff09598df075a7366254fcf4b7fba17e5691b7d 9c973e1d098f54ae706a1477d503df2b75f853ea36d98fa9e5929be16d8a2d8270bf837d47663628 78d0d6aa0ce8da9d28d335a53c8b71a2692a5b3fc768fd2f2425d5bf1ef90214234bf86d92048296 f31179bf55bd591120162bbd53375d1ce7b5273c6b5f5ecbdd4a4953b383549cc7417e055d8627a6 fdca1dc23156e64f4ebeb59f4c95ef78e8bc3b59ec6e8b14c198cd074bdbe845a45ce862502d02bc b6417e046df71a88ef2861b9ddfae02350ad3e2afd1aa0947fc69e6ed58deff9ef43fad58870f879 cb2c52666a77d5740b75536a27cad49571a73e93b0499e15efcc4d3b3d973df7d8d9de53bca0cce0 7dd60b1aac8ed1bd1d1e56a70ca98335dd985d68aa3d3c9cb63c77b99227caf0c861a3930cb2d87c 8cc7901cc238490ed3f6294624fe1aa034cf9de3db7ee8c5f7d230171c6e5eca3c8dc7c26d35ca08 aa2fcd42658cb3e53896b74767840f921457a8ef79f9d89d661fbcb83b44fb9c5e2fb0d78e8dee08 97ed32b545634cf9e970b90d6d7d4b0243389029eba26da625c7252e44334f5ca6e180b8ccf00d71 e9da47e28206c2df909454ff7a0494ccbe12df4bb807ca233c6b5bec55bc691ecf6acffa8455c4a7 6649cb62b9289acab22b349a347ee44ff3137fde86b77d5e6102164fa3991d4ad965da2db32d6a2f 3692e9d8db41379c91e3c663b399552e1c81d50d05bf72d927beaeac72386c43037c9d3f13f80ae4 e3870893117e8df85e62548054ce7ed8fecc2d43f6da490cad57af8054de8e444a2527ab0a4e7ef9 3e7110681cfbf91ec64b4475bf2f5c3d8d2d0b0b8771ba950fdd666f852da891e836854cba64b650 9d6c0a701927a07c81c5cb50595993d3a1bdaee68ceccad6c8e4af59a3204fac517db05fd9a3c1e9 d700d0fba103c4506cf310359657e4adaf2e077db192302d8e231ac70a77ecb71def90c6b695bd8a 87132ece5393a527779612dfd3716f6f51af533fda8a9d7c9e9c3e6fe5cdc23cb588954e8e70e28e afd65b6dceac9e107b5e3509db5efae824b7f40bf9fed203ebd5d2d3e5dd921ba9c77f425252fd63 07c06a4ab51fd5d3ea56aa3133b5add7e672ce7a6302bd155787d17c43eed5ab1e72ebc7b4c69245 63b6a3db29860e74e74c9d6682b19d40844fe6ad6e7a5372518840c2520347df996fa3d1e3fc62c5 cef3d432d05bc2925fedcd650fc9a7b1c894db5874a82cb1684b30d8a9c81c7f8d284e1845430ab7 e3df4a7ed2cb9a09f6024ba6601d86f667cedd5ab0c6560b6a9af1905a93692bc282028c446fa5d9 41200bc648238c63cdc21f67f885d7b24876dd2c66a015b7ce379661800e97fd5e0bc30461c660e9 f45e5a4866eeb998929bdc2257839379d28bec9b592fb2579df9351eeda331d6d1d97ba88c2ad789 b8d976c78793a60c3978301933deaa62d3fdee2e47a51bcfe6767aefccc845b4d86e60a39f2c3483 5bc79ab47e9e8bd795ffc8d9ab6e2d172e8f2292c13e1904c232857c753163d0ee5cf5bbb379a98b 91b35bfe2eced67ac59cfd1fc2ce735b556509d4cfa228c19c73564c2892b344230202adef7f61ee 73f7b9678f35f7fdf3f504d758964d87aaa2aaba749233b35bf2fb6737e830f9153f89ff496b17d9 6bef0cbac24829ea8d91d087cf43a660fb1d32dc61f4416b5cddbdad7fb3bbeb7752db3e3bec70db b8bf521fea95c0634ea0f12110c4953216ce2b48139ce502661f0b1367830576a1e29fcab0152e37 bb1d95d2acea99cd29d9794fa68da8b19b7889e637a1872f7bd2c98ce37f87ce75573dc50a4b03e1 dd13fb8c85fa6db2cbe66bc46a0ba53af4ae6a0f1f1bbad60ef1982d64565fb186ada699517db9c8 77da0b73d51f2c700c5fcc9d2dbe9997ee8bb492f6f434abbe706e4ab617d2c4e316e7491b2cec31 37de3e47b14686a311ace6865f33ac0c9575278d1a1a4e8bdbe5bf435d96e19e48b1628f45d04b93 6466d7d27e3528611b6f5f3ee1623723af20d9351638a25ee6a52bf39c3ec9b53ff198593469473f 2958e3b77084c7dc902d8c62852a8fc4a9501f7e75b6355496726f08d9c26870de18f3017c33d67d bc66133de7f4607adb7656eedeb8a2d525fa83c7bf23dd6fea81d8e520dd6f9e9aeab5b85fbacffc 9a7e3ac92093b4c90233d9cdacfa981c2674a74a8f7be0cb0dbfda531a42a6a90ee00b77eee315da ec3947d2e995bcedb54b7416f7ce33998e9dc6a7f36ad3935afa36aadd813a518b5b74becdd8eae6 9ae27a863547c543b5a1ec4fadc6b42af5ff1d00463b6b9097a747907f39228023fb09d062540018 9699022c51757f2c0212b200864f42808d440c6085671b60d9d202a0c1fc00d0ab2000547b2476ee b9f80028370e1308b90451255ebfd05e746dd7a751851b6fc203981ddfaff18c7ab7b40af96e5d5b a72098e7d3da0f3fedaf0070bd9f886ae36422f4530448377001ba299612296b7380190e0730134b e434f77122f139f90df8b70bb0566309b0e2864c24d67e0295d120b000faaebbb14d0ca27853dbe7 e2a22b97a27df1d10d1ffbcf34acbbe8fa4db52a8780eda154d083f354d08d3e941f092fda8f8e57 ea57007830dda45f937f6d13519311091016f5006a0f2a0013fa0b50c8c73c28c03d0760319588fa f492df20617d809d2638c0c6c7536c07b21497aab2115d9f8f47b4c7a377b45fb7a1a85a5c15c3e3 9c6dbf5fa63dfdf143175eeba0577910be700c687fd8b8d2dec7d3684fde1eff3ff81115e43f64d2 b946430548d00d0086ee6a008bb62b5098a79efef9e6fae352ed692011fa9d880a1a8344376caca2 dbbc7d8cf6f14488aae4460f9f05ea161e2f461036668fefdb8361ec4d89c356c0aaabb11fe78f2b ef8b1df7de8496690f2a1f9997765f31aff9b2fd175297eadf17ff0080978b4df2e0e913402a9bc4 fc1f1f22801dd4062874391c14b45ea2c55ce57bf257f08d2ff0bb18dd33ef6e54ab78f3905cbd89 b7a7bc9937dd85d49f4e7b949ce0bd6abb0187ade2a0a7d0793f9e3e6bde77058d3c68f25dbce6f7 eade35eb6dda5dd925d6c53610fbdc68269d82f915003e6cb7c950157faa5d238c6c02549292877c bf374141bd6d40b1b8ff71f63fe2dd3ef844359f43c3e6836ebde92f3909c232b109fa4be2e4c71f 4ef2476dcef0beb7f3dd5308efed415698799d37cdd20b3e6f7b2ee6d2e926fddcb6cebbc76d7fa6 1ed527c7dec9c288be79bbe469fc2b00cc1389a8cb330510ff61030c713f496fe6da89e51c6f53f7 b912efb3af7bf81a516924e49b798c7201af346bbe7440fa7e46ce2dbde915102f1d06ec0b1901d5 3503c471f17dcd7d3acf11786ef103f2b85ddcce9d3c5ec6379abee3d7fe40252e22c59c9cef7b46 3a0ad73dfe17a9c1febf777e00609d4c44bdbf99e4c1e72fc98347d35223b50e28aeab7b5074af4a d4a80ace9b6556beff91e38f9f25dce2cbc8f2ad175a65a66e31b35c3f779531f9b8af0669d2d2a3 a60cce77321c5f6f7e7de65fc3d23c73ed135af59269693d67aa05737b213eb636328a480b578da3 55980907f3923f12bf02c0a1b24f0628c4fcc49eff2c9df72e040afea8fbe3998e8eda867b7399de d9ffaee0bb37e794f70b2beef2cfabd8aa3d8eb957ef7eb285e98de1369beba0d548ba0d8658471d 41aa93534b175bef4ede3672dc4216ee3d9be665564a550eb36cf436c6011b1e8d9ad338eaa7d297 f87724a28a3b80ec1adc7f02a5cbcb1c288e881e2821f37dd8364b5430aa40b2b7682ba6eb9c16a9 5fe0593591ef9dd2a964c9df7fab9749e6dc75660f726a2f95d1dab2c2116915d10e6fee3623ddac 40c4c338f4c55877e55c456f41fdfe995910ab7317391f347e4decb5416bfb4fa40197ff7b0720b9 7382cb5c486c3f3ead8b71ca832249f7a26711c283d87108effca269f7525b280fb79a716eef70e7 5f949efd75e0d908b5b146b96115df6edfac58d2cc3866f99ff3d65e3d9ed25ba4249f998b6a6951 feee69bcd982d54f816fabe39d3b57b3d532a1a84f64afccc4dcff0700299b7b8016a8c4f0e3f4b4 2c82fa13e09f966b0ebd9939f7953bbe7eada10ef1ac6700730be99d76996af6cd362f93c82abdfc 9c59e5f8b2d1582fdb7abb571f9d7bd867a909d368af7ef80fab8ebdaca666b7adbb32d3d71fd940 5f3579e98dd32120a334bf95ec8eb49736397ef7ef0048e7460014371480bd5d0f14b13b024af0b9 fbe6568b91a7d7cbf3e7ddb45677c6381049df04bc6d919bf49db4551620df20b9ed47ef142be8b9 f778d5b4a1227755f94e4d15cd5ead15185a9172b2290b32eaca8eb429be42a9785b95c47dcd1888 954f114f30de0b0fb9b5fd135247d7df1700997a898e213e545028011f14873e12ef45b215880ad2 7dad8beae071ba0c46d7f12d5839e8fe4859952e7c364e13f1a177ac4eac81ca2baf2af4bea49c97 ed860253a5818cedbfe9eb5569637e09f1eac7ac58ed950ce1f15c7942a3e961fc2b1876798a9517 3c75ceee53ecfe1d899c4fe2af1499c2140940f10890884cf4e4b4be800a5d1a6ea5da6ddea2c3a3 73c9bbc2dcba923dd2acf39ea27717d38b062e2050d5069b51167a0f93f1755c95b673a72bde4865 22562fca466808e489a73c51e1dbf4f7c1b1d134cbf584779beb89f37423485a63c7c6b8b6fd7700 64fb3efc95cf51a0cb69546f0e09e96ea6e4e9fd75e9593f06a5eba434a9d9f6b537321f1768af07 32279c0756cdfef1433ff497b2ec0c805cb0beb05466cdb258c3899640328b214f1ffb2beead748e 5c2f1c89ac38a02fecf00b7d982f4d371865064d13acb68cb2dbff8ed4d195b600a1e3c35fc90705 a7f306c5b880bcdff70bfa5a8ff3f09d9d4eb28ef18c4bd6fef1ed1bd4ddd99c6368cb69d94ba82b 46e3f4482ced4a24558a374870c56d81f777cd1adf110a3dae4f67d24e63c57b66cf66d002cf4c27 7d9b3e9fb9905e0cca157ab134c7f462554accec55639362fd2b00a2424780115d0314bea3372855 6bc918407790bb53a5f48dc7f503aea1bdb970a849626647e7eadc4a539026adc2154b95379dc155 aa585120bcaad4970f3223940b1f48950589c9c86638759c6c627a22c45567280b76cf146e8d02aa b00e4b5461b31d24f0d2a8a1a4fdaeff1dc96e8f25a29e67e67f72e3e7b57c90d9d4c0b36e149f97 b9b6f22ca282c346a7bc6d9e47edd14c3daf734705135445daebf534c8426c14af1e1f889b0f2754 0a7956fab825467dab2d5ab70e231acd1e70aa101f4ea75d5352c93b035cf2ceea05f24e347be49d 3bafc8d49dfceff851d50bd5232840070b1457db775c3540d6d3f7faebce6edaa2edacbe77c3f372 d933a8d76ada94ab4d149c7109e9a62c7991ac172dfedd139e9c70e945ac2ce472ccbcad17e81572 68509bec6c70da3debe910201f85ee81acef0ef2f16458f7630b5f220940e778b2c86582f3fadf91 aca4931328b4791b140dfa1d7a0e19bed664fe7c859a5bd2aa95e974dea42f6180a6b6fcb28a9860 28eff2d656743b4b5ee88c83332792fb1b3bcd970266fef2333446f018551acfaaa76aafd5251bcd e2f4d8aea0bb03bbea0a44e430574298255b6ca4ba6d22d2fa332262b7e9103874031aff15003513 c5bf40590e28e5c5e0cd8585c7f5837776b63370b746472e9ccf196bfa5651f884c93b46ea8aaeb7 5e09dd7a85e280f6d2d8a9bf4ae3051864057b9493fca4d3cd3dc1e4531b578ef4086d1db8ce674c c4c7fc76ffdda2ec5e291fecfde451c9ee2786ded84fccf2642fc75bfcdf01b0aac1fe181e2f3711 7567fa779f1dad9d447b5b9a24315e9ec7554650d1aefb942b4c1e125fc7595360cb8319f7d9640f ecccd42406edce6deae240cfd3616146642bbb838e9d67ab70e85fd00631d23f83fdd4c9e23f9edb 5c9dd921cb83b9b530046ccdaf5edf21cde6648714f0d5bf03606cc4270334e7bc7ba02b5dd4fb61 6a550974a2f7d5eb4445a711293de8f1cfbc690f928d3c2e45654efee447ec9c0d76b4edb31cb55f f5cea706f4bd1d03e9ec1f841efe21e4e600d96b954a756760f9ee0eddd697dbe2b27bda5c51d5d8 ec9c7ebcd929616db36346a3cd6e47ae7e907a07ffbef807922db42280e223a3df3b477d6c6f4af9 a1febec84355578643e921b797020bae1af73dd703f67cdaa38ca9195d7a8bcbcbd3f3b13991f4b6 211ffbc83b2d987118651d97804422dac3ea3ab7c3844169bbf106edcd3e9ccdd6cf01735c37e4f0 bc6e2862bc6eec3ad57563268ed68dee6df9ef00057bccfbd0e09bae028e2e7d7a86d714fa9ada98 0ce41d78f7f918dff6d9f3136319dc17ee74f9f1cd52f54caf7a6a55369363e8f47607e950628959 d1d3f6cbaa73d9ad9b677fbbab909fcdfdc8636b777b6aac9b77758c33f530d53a57213d52572183 c5ab70a754f08e86f55661b53a4b31ff15a07838b0b7feb4dbb32a4fa17b1ead7a3da5d84f161ed6 5c74d8c52ad3a50ecbe6e6d492e3345e80ece528701c0ee2f241767a4342bbf7f0bdf119523b07ea 29db2bc0accd330b3d378d391cafdb780ec6d94ba1ba8a2bfde16a2870bbe57704c94b2523f84bf9 da2e2ebf59adb394d9c44cfe1ba94bf57feffce059ed323d7b3da75a7aefd1ee2816f7ee8a6d78d1 66cf07b749d5dc65eb28dc9ac783527b5f88f366f2d99b67a5b42fd4e2eeae3c81e6dbdafcbbdf90 2ec6aee9e0c792c6c31eece07d11765752a1f15965767d74397de08d654e70a68b65063e2e1095d2 178836fb2e90bd5d5e20fd623f417df82b2eb916db30dadd464bd54b71e7c79f76a53bdc240c9b14 e1562a0719d3aa04329e8abbcbdcbc6fef12fcd9b8d77e61d3ea11cd35632a239cf795e5ea337388 d5d8b8b1abecf1ab2e673e642f8c2ef458a046359a6fb07e7e76a589da6cdf79a6efd666d5fcf038 7d18f6797a24d7d1f4380a4ad363f353fd15e6c16f34b4e9c46b498f22dde6be9db041dd40b17c90 9421babba812bead933565cd76a7375c3872d14f250bc5caafa072585ec203d05c985873b85879f8 72ee74f0edbca413a7d96d4973b3ea4b51a764fb6c4d3cce794ee8fe371ebfe51a32ee438bc6283e 9bd351ac378e2371f9b047e22a7bff2fd228d5ffbda37da95b43deade9162fa899065d46d0d20128 0d787bdd6fb36b965e6e57dfa5252d17d0dd5e6096f59897ae56307d924c346df87276e2315774d2 8eaea5f15b08ea636ef869ff9431503efd91384327c3af812e87caaa4b0c21674c0f16e5b53c80ef aad3c7ebbedfc7bc4a3681544b7069ff0aa560d14da197c9d4696781168f7d054beba76daf7015e0 bdf0fd5d2eb2f27cbe2db0c4f479dcd013ba3311c6bd4f43198da0923e9c22903538afa1eb00be42 cf3e5e895f3de7f80e7a25ef197689ce0d74ab610875c84188b43d1994daf4b4dc6cbdf51f3f748b 5b6e17cdd8a6774d71f364137cc55f21b6cc5a83314bddd231c4aab95d21fa86abe835f2e7d6db82 a664bd561d737da437fcaafe78705e5d167dbccca7fb4d6f5b3fedba447b4974c8fe806c7b528b6a 77b215a6c5cd51ae199b59a139c23e7243d9066a03ba07461d7ed9560d6f5f6f55877bbcaa25f00a 2b3719ca546a1086949f670cfb0792aff9ef05c85d0f6390eb9d9620f7460e20fff96a00166c0f20 7ba30050e830046827f9009d926af2d7f301d04ae903d0fc024b0c19b199209e00244e75dde78a4c 200b09a2744d8bad53f116af5bcb28bab06c3e2a3fe55258f3e5c6fb34b37b816f08bd80591dbb29 fa41e73be9fd8a44cedd08e4e6fc0ae44be503801bd819c051f4536e0ab9062580b6f91140b7c967 28cd25a26edfcf44d1ee67003aa3934fd3237dd02a344d30dcc6963927e3357ee4e362413b4797cb eb1aedf6c520bc3fe7b9b05ed894dec986d1081881edf9e177939ee3e1f3caa4eff374afef0f3ad5 df0172c129e9d2bd9488bae91f017cec1900e915438096d00a40653711c2493e403de70cd00bec02 549d267232621aa51adb15a713af0d6f12177178135d0b0d32daa93d2eaacc365af8c80b4e78301e defb55a8e682a0d22806dde3b0e10fe85ed7fb747a034f0aab036f4c7c075eb6e6fd1169686fda82 7c964944d52c1ce45f249574e8d10688368e01ba695501facdcc00d6204e093c1d6085c62bb6d545 362e467831da37b6ada8e2d0a3f0581697615d3fefdfafa5c7bc290c56de2da76dfd38efb71b3760 73b7ac2fa04fcc934bcfaa97e5a1ee6b06e2e12b7f7286aed1521214b9df01f245699208ebe2001e 382c4066c605a0c553320e5febfa8f3f6dd55c004c38d3003b7ef5b8c4c06eb47f94bee1d141d0b7 972bd4df14dfec07c1ab390b586aba4de6fdf6e4473751f405e266f843e47bf7e4cdf0fbd28829e2 1ad2b6ea6259bafbb4597af42cc6d334cee671256bc3c7be89fc0e901f5bd3e4c1e7d600d6bf3c40 82ec1da0d2230bb0a5defa8f07bdf265a39b73534372a95cdff44409138d5bcf073dd74a43e27db1 6875bcefdc1a7b727c5f79d0c8275e5a9049f66ab6a9bee0def6eaae44337a6e5437ffd81b7ef9fe f886ed9b97bb0fafc167ddbbb2f2b87789a162f757fcbc8a585f1251ed723275bb3d09a0e3d61360 681602d8dd6f83c24cc4a3fbd0a4c2a64e4a41385958be083a891152ec66bc1c5c2fbc16cd62dd35 7789b68d3d2ad3a7b3ea6e9e25a54b3e6ed39df8a8f2a77415b83f07be776b9f5f994b7c7aa19744 c49a3379beba0e846b5d7b7ed9b753747e05c89fdc1980ebd3c4ec578f69fd8ef50b608756620474 914e5c7e10cb9f78e8f2741ff07287f33320a3bdf4dde5e65a2b3d7c5e3a2cf4b8a3abe2a3365935 efcd7eaf7fa34fbdd995db57d797d86c909751612239ca6c6339d0e315d9b0f7cd5b78b754311da1 d131b7c352d72cbde2745b336ed4b3fd2b405ef21351793a75f8196780d5241f60e1120605b5d30d 4f746e1cf09f0cee6955ebf0427727e179c596d6e3c057dc9b7fb5a32b3f22d2fc9b8b5419151c15 abd59cdcb4dcb5177c796a23f5fed6c2a90565154a9c6a6e89d83508aa9cd15d082fe9cd45a212f8 08d139d3d6a27deeacfbad5f01f21648d5410087e7d4e1f74a663771094061ca20d15df71befeed9 ea272af26ee2da97f1fa59c58aa77b0b68d275f06c5b8e96f39eb6fe52221b85f89c557097057357 9cd58dfb7cdc338878b7d0dd1143e84d464ddfb09ffd5ece3e77c42dd0f88c8ea960e6375529efb7 d5d1c36dab99a3d5fa15c9ca949903641a93c983cf1a007b833728282a129eec43c997d7ddc60b1b 65bb3f7e683e9adedefde3f63239be2907ce29aa656f468e79ed973de3d1ca7cf457fe9ad75b43bb 7c6668a3a5857763a4f1cb17ae02f97352c7b996a666742b5066583eafe49c594dd6b7bbb6bcecac db32128ed3ecd59ff64f48764f64918c512c593091baf5572277e17b41dedd5b05f1e0eeb5f82472 fbeaed7d6e772ed38a37b7f1dc96b04a5447341eb16fe8d48eb99fd9e12cddd6b468dbfa6a03bd81 aad2a75a55b3ed6a5799118d99ac5bc3bdbc5ccb9264ddd0a7b4268e9054acbd2ae2e5956f8b3b39 fb83d6af48e6527901d04cefe7c83d6c314ea37a6b615c9932b960587d655d6738cfdd3deb8b5e94 8b58b34d6f3b346faff2c6207b775a6f5f4eaa160f5b8efa2d675feaa41f00655e5420d9988b4519 15c5a6b4f6959178ad986b71f7c4d2212056f08d2d1c2ec147a8b3f35202a3cdbfe6da0f5abf02c0 87fe12a0cd3903306e99c8e9f4c3a8ae8fbe3ed4dc454f62124457f139fc3ac8b159fcf143f348d7 200377790e0b0752138f1551552691a9cc5df6269bd97d20d9cfde572a159a88b89f35abc243e8f4 85ba375af22f5aa1f9d6dd3b73c1711f71ec30c63876346f71bdccb0c9f50af3bff0e352fdbf17ff 45b2de4f1351f13d9b8c51e6923cf86918b63e99f06576d1d7dd47268f4b8e7e47d6850a51c3952e 4dbd1333534daab4f6ea947ab1ca62216b32160c6d690b37d2541271efc591f02c8739a1b1f44b7c 7b726f732ce74fb95ebd7660056aabb0c30e1624e010e69bfb36986f1e6ba568fe8a64cee3899c22 cbfd249cff9524bb78bf7b63cb731ddebedefa17cfb62d5e0ccc03c1e4f420cbd7b54f663052d563 7ea32cde1a2517d673492af7aa9658c3de0fa19933df3c5d97321c573a606cbc3835d8a1c48f1939 b0b6cc84aba56fd819a8ef78b4f6e9e4e9336ad61378ad14cd5f01901c9e0cd4a7c683425b724071 8507c1905ddf9f84dcd32e33acaf5a3baaf3345aa741e62cc0e35481d2b2b3ca40d15f012e17c0e6 2895af0d417087de99f7cbe2850bd1ed8bebb7a7801dd55a30a3ac5b5506d2067d7a011fd69439b2 380acf2e9e090044e1105ea5f092d24cd1f82f5297eaffde014887483490ea4300858deec4bb6fb2 5342c7c8783459f2e8a0e58c64d65970d3bb41e1a3c978bda8e6f54f575ebf85b954594f08c18d21 8ef71d47e5429eb459b0e9a7efd6d8cca21c31b97136472fd86c99329f68972a145acbd37677624e 6518b9265033a772ad5349b06da668fc0a80d0ca0a60f3480405dbbd44c76926350a3dd8597157a9 232dac6b4de5f440541dedf3b04275be0d50d9b68d96b4efefa762fddad8f1c1f04973fc9d9458c9 9e5ac91a5f485605320c286ba67fa9826060a7ad776d91f76e764ed68eddd3d16ddc535fe791f417 9fa39b1e05e9b63a8d14f55f0110c7c193699f4dc3fb7c27a438debc7786d6ccd1e5cad8ac07ddd3 59506786aa796b4fc10e424eba95a8541d148fd67cc407d117e784ad42b2f2921099d9bb7ca697a6 7fa1d6aafe3aed040690778f458fee9daa1f5ba8363e30dbece1d02d9ff443375f8b12a8d8819917 1a29eaff40eae8fafb02a058bc01d80d154171006c1f1ab68fd7ccb23eb48b73bbafb3c263af41cb 4851f05ccb956ed4e02b92fd494568078d01171fe315abe4c4e47fa2461c8dd5721a55ca5c9c5325 e2d3d37cc8bab78b8f54790f1f58745525a209352406e7d77eff59ced4fd67888409183441582706 62ee770094acee92b9d4954091f7cff7b637eb3bc855ebfc1c55e94fdada773a5f29789b15a49bfb ba89e4190542e7d42872a00a75d8e9d69c31f007dfd1d8bbc39c6eb82f91cf996e1ebde1e97e7877 966f22ae0fa1fdb7d42eee27c4a6b7d32c6db39b6fead26e3ef17fea0bcca76b78a729617da7d16e ed5780f4085f50a0babc2f2fe3f545b18ca65519f51ae7187dd65538d9a3a47badc708fe7e63f1fc 480e3869f746d859e3d9fcf143dfc53175d9e09b13011092247d5f38768ebc76e86f775762b41c7b 7b459e6476e7f3a4b883c1bebbc5c736be2d400371e370a1b7c5eebbfc16d3efd55f91ba1fd23699 4e9ba4573f63f64e39c596bd862775a3157eeb1ac41155e95ead3505667726b84f01d2582d3f7419 a3c165e9754896a98a8cf74e756eb43c06addcfe10557c96901c5ed9abfadadae73ecbe74f510668 166f0b60826c76e363737dd71e8bf5a13f60d78741c15d1fea0cb43ea07e654d7cdcdf01b040defb 9f8132bb4c5bfb9a599fc655eddbe6aaf2255b4b0d0f81d99e0a8960c892590db63cbdd9dc2ed45e afc6a7a3ddc7c836516b1e7ba772625b567c9c801a77720f97257e87a187f376b339dd36fbc53a58 3f2c36bf6e14af0d9c22d019deae492cde4671176f7dfcb44c1bdef29a15bce594cbbf02144e8fdd 9da23e0deb32822be7c8162b8a29f52be26978297159aa88d21b6b3d383df7ee91a4d7cdf39163c9 d701c06ef6906982f4d710b96ed8dd23437fbe2b74f4cd76cb5bf486905579fd7c4b16ee01c9c53b e3cb77c59db3e52540c6a3a568f9a7a5282bb765ecb5bfcbd8210b4b71ba28fe8a74e99ccdc39e73 667b15a3753897d5737f51910ed55c854ba66781de881a7c7c7f278583544197c40cdb71fbe5d475 76d6a3f6de15572778bb73a4dae6d1b90c3775febe5cb7646b8fb3d09d5b4544785e0d5eb9fb8fe7 b65d8b97596e5d58688b70b898a3e46931fbf6ae8b99c3868b197dcb2d665b0ffb15379693abe6a1 e39434f98697e5edb55ce1a30e5f62925d0b21e95515fae934cbcc6faf44afbfadd6f3d4a6b1591b 6bea7df1d66dbd92c5df875d09ef7da4ce2a962f93d508bae34b8500e4725a2f888b3355331608b4 70e7e659cf26eb22529b392f7a3e73bc9532db6e5feecc61ba69c1f3d9b63b877f855d6878c5b330 d9969442ab591118452d3186f641494f684044eeb4029b07deccaee935b5c239edc3ad44d035976a 71f95ce6583e5e2c613bf50e2e10375f9e5ba77a73be1e2f47f362f6b49c5dae97c3ac52bdb3d383 943b4febebe973f22aea99498b43aa09ce8b71b05c89e360f0798e03ac0b7e85cebc88740824abd1 281999c7b0c2cedb45ecd478b421622acde3cd41518215609ecd5516a6760b23637073bbfb316657 3c7f9b3e76d9d7b41e15a3c9cb98e726edc6be300e3cb63a66956b67dc9b46a3515ca9af4643619c a6fc0ebf4346184e9cbb358476b56070ee4ac8e03ceff4060bf8bc192c9089f22bd4736d54966a8f b0cc8d3f83c2e9d91d4284e2ae1211a7dbd7f2f3ceb80bd433bb3f81caf9c6767afc4e9849fb3e94 c6bdcfdc188da0a133541693c710b226de60511cbefbe67e08fa787591ed39e402ee957cbcd8bd31 74bd4b749d7ee729bce71d72d4203acd0c25b63df565b73d6d10b4e9b99ca692fcb47f825c660b7f 4d1e6a7fd9e4882f98479b4379f45cca3df536dbaaea6be25185fab8070ac3e157431683733220fa 78f9b2ef6deb3ad9bd513ad3adbef534f2be43f625a9ed49bcdaee6419bdc5cd4f5653c4d797c6f7 b27e34a6959d5f3f1f4fa00e7b1a5cc33b8f4a0d8bd06ed51106d3ea7678dafe0a3e2a9f8af48614 d25f7310179b685b56b6ee528a77cef4fab18d51a41c8221a44bdf3e5e62d15ec93d563a4f76de68 d3a346bbc5cdb05e3336e061e36bc793c6b4e4cdea8bda795e334fe2b28605345e2dc5f8a6428c70 a2fc54f13418b6e41953b6d4c170b9d82f2ff48278d85eb0ef8b7431853a47bf02402bb901b25fa3 07a0e7600672b7f209e4379e0ef21ee50378d446002cbe3ac9f2b95efdf80580cf00f8d3d101fc22 1f096e1180bd42f281b7a82410db09bc716c32d032c1f010e3dd3d1b3982a147a56f700f6bb9c07f 93cb0f083cfb9b0d682ace069d9293f5df37e52fa48eaebf2ffe0b001d8d26801a8f3ec8c1b319c8 974634c83bb009e086162452ced144ba7c0f2035064f001880d4477a02c54d100380603538369d69 25c6b7446ab0c785b2384cf05e46ce03de47db638f096fdef11cd63adaeddd1c685e40cb57e08725 0ff2b99b03f97d42813c8010bf0340c2b59508fb1980dcfcf0e34cc953249b48d9b3012c042180e3 230690457790fc737d0310bec627202d801c6eaf187f79202ed0081c5dcab572b4bd8f5a51f9b01d 84f73a370f8997b30d6bd4977abb9dc939f0079b9b1f4e366949230f2c36b127d5949c3772e9bc97 592f722fb5d8ff1d00321e89a84f341135b8ac40fee3080036d81b40e04ad25ffb6b0920b7f51020 ef7007d0dc220df08fd74bd28a2e32fd8c2a90128787be940d6be05e78bb12a8bf4f9342efdd82fa d3c04fb6fe80598864d001b1e20f20f8e28d6bd5d76bd6aa46af5c30cdbbcb2a92739107c83dada3 fbff20f5d9fcef1d003d8236c8f5bac36478221b001f61192015fb0e1076f9052884567f3cfdb15d ceeda26bbdc0858f764d7fbf6a85ebbb756a064150ec7f83ee7e86f851f550f17993498780f72948 034fbadc17de789fdb7bd9ea5478e59ba4e5a233e5f95c1b4efcb87c2fb94765cae7eff7709dbbd7 072de85700288a1239b5f528e9cde91e20f989f6f32a42cbbb002dca598052e37a6c6fac715401d2 eafdd2a4531098aae44717ddf68779fde9c9433df4b2ca157a69d3a8f0caf341d5358668d75d7ebb 1317550edbe726a7d28f3d1a1af76309dc6e543df34ea386bad7cbe72254e58cf321d75f476e8e7f 47328db21d902f3223001fc4234076c733405b030fa052908b6d7b5d0f9fc8b8f76eb3dd991f8f5a 3b4fc9d7539fcd4bfb16b5173c6d5ddd15dff29ff6ab1e3f4bed5efeb127dac5fbc36ad7efc7f5aa 7f7b5da9e5353093e53af25fb2f38922dbc97e3fae3d9fa1c032f46fc65a0dae1f0bfd681fd39659 f05fa486c7df17c9832f7641fe648d13cbff4d01347335016a9d829f72cd5195cb97de9d6cb6e58f 1a8fc1ebacdb2b179fc8c7e7b62f880fa27534ee647575bbd199b677ed19c9cc12beb9acf39d16d2 431c9c095fa839d06034b0e732b1b2e1f68db6b025a69b1b7bf8346ea5556c5489c9577fd6da5ffd e8963e7ae304813f01e4d856222700d3a4374b0c40f18f05b06418a6f3668b16736f9ad4107f74dc 975f8bfbbcf5bc4ce6d3474d2a6d6ef4e376ba8032c13b8addd79c1cd4b9d88b7ed3b5cc532db2b0 6b1d329dfdac6496cc65d3b8e1d2c4a85efcbd4e56fbcad923c9ebb9ed9be9cb628deb9e3f5a2fe2 3e6a2c10e05724337edcfdcb8b86f893ffe4f0639cfb0e9f81fef1c1ac97792dcb10f2ac649df2bd d554ba17e07526ceac06d274057b99bd9c2c3c5645737bd99d8d7b7e7d316ac3e54b279575ac373b 147ca60f6c457bbf8caec6d128aec6e18e574703cb52bedf42a04ca7b58f92cb2140c995cb1ff97c cf817f2075a9a62dc88f0f3d00dff115409b7b3ee9d0ae0d0ab9ccfbdd115bbe977b19efe74e5a81 5bd02ce72f63db2ddb062275ade27c39372bcdd6cea8578a8cdefc04f299693f0c2d24ae37ad6fd9 be0ac65e9ae6a38eb46f415100dc5412ed6c26eb598d9417e7962159c8ee25e196f5950a27fd071f f1d2e2c19f900c5026d97ecadc3a31a70521e9cddd25ba2392eb032748d37cdcf5b97dbfb70cf375 c9dad3d85ef597a879655b4de3d8cc8ff5966ee3e7ae2c93da805871aa64cd35359b9d38caacda7e ca3ad98be4650187256b96188f05e33c142f38b61377055913eebbcf533854bb69a70987def22bd4 e2c12705f81340de551351b7e6e6afec68ec767042773cbe7ab3c3dc7c3c70f17c91f99c63afb84c 60de8a68ce68ac5e95f33b67f43581dacc7f8a032fda3b451b63b492dfc5a2bc34de497f7d9f37a9 d87906e2eee06684bb1d1684c3b6d3e5dd3b89f3a76349e65b0deacef95e10734106fd728cf2f9a4 007fc28fca815889a8ce7b0730f891e69cf3ce9b997dcc177a7a4bb7e091e31d78ccebe66dc4bb46 e3217dce5c795fd086f6b0a54eb8ea44810b4f5c5ecd9ca3b46990ac78dd12aa58d1b78e70f8e02e ff6ae3697a1cdf220e08c7b87a93eb5285051bb5cf02cb87d52b3be08f213bd0ed6f8acfaf0070ff 9ec8f9fd2672f68234e19cb702de9095e795dd52976f7b9766165bce7cac1ae46c703bbfbd59a48d ea0d58856e504d81c5675f72bae242bc618b9d589d8e68e178684a3cb5c9e95c70cedcb82ec8bd59 a18542ec2058d69944eb9d3263d06799ace4384c566ebf696d324fcd285adb6cc0af0030e1f792b9 84ed01b6ff88f1a57d32fcb1d2601fc7953a77e600dd9b049e15f5ce20b235f19ef5d52905e514a4 fa2c4b8eca75c59bb04dab8c08cf6d73cd7bcbdc916f9f7c8eebed2d95154cdd61be19dd6326dd6b 86d6c46c859e8f9663ca502f47ca6071935acd3d9f5a2daa9f1431b522dae04f00b09ee9ff5565a4 41fc78d1a23bd152bcd98ddbdc7a44d4b5315fc38de654663530910d75ea1aae82b0c657ba34b9a2 78af106d81546a239e26c225c7adad3d1bb33cc57cc99dc44c2e6b933ee7d74f1a5e31e90965d44a d10a2727f7e99f36c66e7f2a8d72e7530917bdd3c6823f29c0af48f6a5da10a0628f0005a82286ee f3c5dea976d4ba4caef3ba798be6f373b89c9c7eea439ff76765d9a21e7221a662f1cee0a8706a34 eb3c93cbf4390eb88b6490f25b4631d7a7e4d64ca4e1a8a55378a3753f39db417c2ae92b942450ad 43569dfa26d9e65cedf8dc4d9f64f5f2fca448e74ddac67f02403afd1140e3d50114da15deffa00e 7e15a44dd53649a862905f78ac491bf8a0e85655968bbdde55aa8ce661a2168d609ea173158e27c2 1e3bce32691e01338d9738ad37c769207d9d3f5db0ac762a4fb35792e0d1f7f1f96ac0c7667bdf3a 78ef0f7ea00f8c7ca08f85fbc14ba6f2c18bce3f88ff0480d0f81860d5fde127ae7383d28f3a75ad 5ea03956b2ca1db1700ecf624f9dedcdadbcbe78825411615b78edaa3ecf62b92c2740a0cc8e25bd cdccf68731bdc4c72b6a3dab10a7dde0c39087f64339bac4d53e36ad977f604a70aa7512e1a25727 fa963c27faf65a22fa727823faca3422b82f11a788fe846479da8e92b9b4df85a742637deb8249d1 c6311333c84f07d6c6eb5a43b6478b955489c4344a55a0b6a6c1b37cec72c3f6edc34efaf70293bf 1eea346ace065451ad2f4e1521bb23ebd4e3746c119a78602ccd24784d78ee3ff0f5bb1f8fa1ca3e 9b3d4e77eab9c5ef33effb759f093b61825194224dc1fa69ff018036c4a457afecce5dcf5af50b54 f150f3166e9133bff7738ad1d58b52b592990a94d33f71f160a1b2df0d776321428968b3c5c09453 5d574fb7c2a0471e8deccf796baf4fb43e04efe49147aec0ef3fd7e3793f81a9fb2e999c609757e3 e276054f46db155261b74ba03adbe5a7106c8d7a27fc1500156e135068c99bfbe94dfd94085f7d45 44f73785bcaa654e59e9ead47302f5e1079cb870f7ecb48d88cc62317268fcb40da872759039d59a dd22d9ac145bc70e1a0d0f7de8ba248640d8efa7598ad99d7b47656b3637ce166384f76613bd7f0e db2a91f3c1fad642a94d69ad5b9bd2a6ec6f4a936698e2fd272463149afa23eddebec84c8c268f69 0a6b1ff89e936da19b1168848fb9d1026d327a76b9a20b229fa62e52e58bab9f6a71ee419e46b5cf b19bcf23077e98ad11d224e8edd5be3ddde92d76bd4358e1b42d904771b3f5057b4d3c040f771bef 3c4efaab2e4e06dd134e5e2f064e9e6b2f9ce43ac17f91ba54fff70ec0b4f6ec516f53451ba3b779 bd0be5f26afec2e7c4e7349fe146f356485bbb5581aa341e63f255a91c8ec1702d1fa2ad74390cee 179f18bfaed9bdd67e147746df6e6eedb196d617d8165961b1d9c532b13ef81483bfbaf2196f092f 77c566aad0329a51cd65345f1e565d22a32d23a4ff588660eafd8ae0fd29772ff2ce430c176de6b5 b1794f9d2972e9b8ccf121f2c83088f709c9577b1f1d7b68a9457ca1fd8a80fa0f663fb72bface6c 2f1f3b8c3bc5db0d10f21b224b95d7cf39db5e374c618cd305195fbd7706b9ea3d5ef2526ce6af8b 6fb2932e94ee2b3da46e3189a5dd42a94db5850cc4db6272e2dc5ff1386cd8a2e54cf1fc9967415e c1ba4c5ef0c264939e55979fd3e14305c4b7707bee4cb28f6d9d8b33dcdc622c2d34b3a9254f754d fa9ab96e6a591767aaf518ef3ce7c88a6fae2ac93c397696e33c3b59660c7bbb98610133d777457d beecf2ef9925e68b09427c669d087e8e586f6b668dbff799d57aff3f48dd767f5f389a20e6ff9a41 f3e62551389c719e1b178d2cb50b5fd141b0a0d70efb4eefebe6abe6af22c8adae06fc62b694eb37 7299553079a16d3797c5bc63f80b38f7f9ce8d471b9daf8eabb4d3e6d8501ecc368a319f9576f9e3 f45699c9d3aaa0df27cf510b9a3cd7b9fee41888e4e4592aa993e37978fd1566ad5ecb69e3a70f4b 378141f83ef8e4e8b55f4f359b636fd1f677786371c7fdb5ee2cb324f358c027bd36c7ceb9c96cdb c789e9cd52f869adf53c4f48f6ed4c9a8bea2bc1068cfdda191ed320ae8c3b72a93d0a379be9886f 5f89513f2c8a43a0f3f7a1746ca5232d69c3fe50224d6a38f26375380a30e34fd03e553f2f3b330b 153af716cca0e42a737c439bf7ce3c120fdc1f4bf642f55163b66592717da8eaedc9699d998f19b6 9c06918f2278c40c3fcda9341c338bf3303be32e032d7f7e0ef2c7dbbb6f8c128d00550b48cfde4d abbd4d57e9f78ad177d5bd9a2bbabb2703b3bb3f1da26e75922f257097ddeab6b0fb0792aff96995 956a21823f9d216c7e2e432445b1e17e91179febe649b517b3e9439b564b2b75cc6211341a56c6cd e164751a0fe6b484f757ba4af4362f95ea956457e81290ab749e4468741a6ee8b4e94926b56f5aef 73c66bf511246e8aeb7aaef1bd8e4b0d65cf771ad0d39d37207744d6cf2745a92fdaa5678261f027 882e7642b96c2bc89f8ecb47bc3f779544c4356f2d661d3b0d229f1c172569285e217500dbeb6fcf 21a86297686d6a1db2b7e9b4e9d17cd0e266bd7133367af3e6086baf1acab6b1a99f0ff0ae0ebfa0 430d6f4354d5e160be7213bf72a596c5cc72132edd4b343e0c4b9d02970e81e2fbe2558bdcbeda4f 301cff099c247df25425fc004229bc5feb27b0928e2c6bcae4d887c5818235e95ed17b1b1db20b5f daedcfd76dc6e76fdc50d66ea67ede5ba9ca51334909a9613e5fa896a275b1420c979532396d554b f412ad17df76be5588af5117fb3e1f036cda0c66e8a2fbc2117cf822e0ed3460f235f42ae6c84d64 e4c86ddefe1352052a5b7a63209b83ca20eb1c5b00225b7390ab020ae408f20c723aec82dc9dce82 7c1eab827c851a827c2fbb4930a6407eaeca204fe4ec047d37011925b0b2b1d10758bcfc94eb312a 8fd260d8c89e90b3a878be1dc23df2e5dfc7e2570d1241ada0157f6d3f10bdff801d397f04c8b6bf 05909d562b20fba59a008ad74b90e36a4c62fb9806c8977b3fe5dbf31d2707f2bb4903e499eb04e4 b5d62ec18901f96ba881fcad76890d62e4c6ab3e19c72850a1c896ee58b419836a5482ca9df07a9e 4ec32aa21cde8da2c905edaa29fbacf8b6fcdec873bc38e3a401309ea02a8e379cd1f69f90480917 93de9c5401b4325a203757d7200fe13cc86fa1a4abe87d00f217180179ffd80470313f4bb0dcc746 a07331d6bba8911d83f46ba2d238f3086fc5e63bdc5f47dfb0badfc1ef67552a27faf3bdf56e9c72 a3c06b1184ffee918c2766b6d2ebbb13ad979246764d1ee4e5051df18b7b6e8c9c3f0164b758fa36 0a40d8261135cef71229737b90974d09e4bfc32b801bd708c0a3210660dc6ec7e6b83e8f9c797717 959e6b26249a07f9fd6418e34d76f5dbbb11b95e904801027a54c9059dccb8e8bfd543c3efe7a554 17f06213d9bd14a640bb67a12cba8be1d47a9a4afff2c4a7b5cbc3390327c5e54f0059ba5404d051 a8811cbb1c813cd54f63ba2115c082f000b0994f8cc2e7b1f0e3bc2fd8edf0165dc76f178bd6efe6 2e7f0ae84751f0c3465df3396fec78a0337b7a6278787b235efabe948f81bca672547de5a6f59eab e7f8f513b779f2b17debe2fddebf9b37b7c5db57bf37b0ae745c4f2b915f3b226afe0920ebb51351 a36b1de45bd6eca78803801f131d2095970b90f93c135d0a4324ac19935ae0d3f39e1feac785076c 6aef8d4b02fd52094d7ae55c233dd32bd1055f57170902f7697168fc2c802af4b89c86c54739d834 ef77f6b5b8fa177b7f091f57c601de4375c69dab69ab3c6bd8b3c1deb0739fb9fe272463735f06b9 59f4130f9db7338bc4acf61880cc581320d78a1717e2eee7edde6328e84ebc8227b9cfc64bcb7e47 eed27caf9e76e14b3cae9598795456b1743f385ffdf6aa40976b80be9fd7ee267e5ff83b92bd0c0e d3b223bde87415b035c85bdbf9254659e80693cdf5ad64984502328c6bf56d18bbe74dff130064b0 89a8413d992d833e0e906e8903887fb5623bdbf5c2baf27afbd1661f7b590b4f576877d55b949fa5 02deb93f8cfef8466d6bf8b58780fd6558f429479ebbbc93357dcdd6d639c7ce6b39d732e6cdd84a 8c2cd8b471b56116afc8dca8c4fda37e1c11d2f9a5d2e6999a6d8d733b3f35b4c0e8a4cfe6a7fd07 402eaf95417ebdeb00f84ea701c8431ea0a3c88a2a8fc72de8d5278f9766be5f4f27ff04f767c782 af1cb42c3adf75abe940fbd6c89e9bf5a5b552d19de9e4d1d42f606e848a605cbff5b351edd42ffa 239c797a6370f89e5f9f47e9dc9ed6871aab1f766a8c5abc3adc8486f22ddd7545bee9e75f0172bb 472227b0fa89d96feefe4a92ddf03feede37a5ac4c6f727d5acf6d7ee4dc3cbdf0ba8c7a97c8c965 ae79cb34b48ae92842c7b8f1c7b1517ded573ad9de13678fd8d0e7b6b512b5f79a32b4de557ea822 ac85ead08210e5bb5ea5f1d0cae4fa58cbe77e9d95e7e06048e678f61fe0d9aefe2780dc252a03b8 ef0f00a2bcf73f3ed4e8eadb9a1f13b1ece2a3a6786f4e74f592e90d6c1b6106e946605e86dd8f41 842554273f58fd4c07df9e1656e399d6c7df1b55549f07e51bdd584511625581bcaf232fba9827c3 8f292499c7734dc2fcea42dcce284a2ce7034db81991f1178863a0ff2035d8ffbef801c817f389a8 7a610410efbbff89440c8fd15cf0a0a07f7a10fae17019f9116b235ea09b15f47ad3dddbf57d664e 664ee3f762499546dba69a61f0a1327d4ce7b2de58ec7e2288b7a39384eb23412a0c7043dccae443 2c4f9e5f81d00b25a1b654a63ce94047beb99dcb090493f33bacf12b407e5c2c0324d1bc01da2c11 89a17ae57fead97ce2938b0b2a7e0d597b692f579ba35919cc25fd745bd9e7aeb4f7d44f6ff851b3 cd0eaae42b85aabc5c215dc9523213a9f006b8b8abf984705ffbac50bb028d7717852bdfb4fa21e7 afd502d7b921692a091b121cc1f2b5afc4f283b9c5f63f3de35780bcd44a44ed4c1351c97622eafc cafa8996b27d96697470c966da63f35a86b63a5582382d92605d95e7705a8c5ed1702c928d590e96 ec815f16af25a9215616fc403848a719ef06872ddfea9d288e214f12d72da836cbef5c9f01cf0acc 4824df61c6ade68ec904a2c86421d84a61d2aa16ebff45ea52fdfb02e4dd710520343903a8d9dbc7 25f3487acad31ddd7ca95ab497faad671c6b267eee55cf942aab8eaacc578e23afb6b740da2cf58c b89ff005e1d85fd7f8d77adce55b5a77c2b17037cdf462a351ebc00e9816cf488faece641b3b9756 bd0744e7bbf526a547f70da5c7533e8163524bd33452e87f02806bbb2a401c2e91331eeea25a065b dfdd7e15fdc92330e8bc55b48996de6ee173f5dbc48fcadcd88af2cae12ca974205db1bac181d058 8c61be3dae943896459b6cf4cc0f998ff39a3393dc63c764f988a167dfbc46199dca9d42f9c4e2b5 07afda69fd3da5bfe6b4c914f85351c5f553f1421a29f43f01c08b43d2a55f6e928cd1deda1ff5f4 ce3554cf39273f2d678cc7ad56d3e25e79a2424c732fe3589f974af7912e565fd3c74f45e55b27e4 db5605e27a5ab6c80e85479d913dbbc740656d4acf97f2863264f9446190ad9c36fdd795bccac598 dc4fd83259cd759709ee0c59cdf7f504532385fe0ffcb8549316c01c5ffd8f176dde5b3d883396bb 8c57d38cb541cc586fe36a415586f7816c1edcb55486425aacd50b2aefbd910bf776333e1b5fdc2f f3350c849924c6073d0ff72dcaacaf4614466dfeeab4dbe648de90bd4856d7b2733c6a8ff7b1b118 160f1ef2991d3c94a10f1e96d31294f514e73f01c06fab06d0a33309c91c3eb8374d0fb28d521c9b d522917a07b578378395c5f4d091b632bf146bc4e5c4fbadbbc4853ddf66c1c87fb199ae05985c43 c8d3487953a2f0e5bc79da4eda03f2c655e76415b48923d9eaf1076f3f370feda7e0135cf38b125c cb9e10bdf720ddd608aeed6a04d7c9e829ce7f0240da6103a0c66dec29ff87aefb5c7215871200fc 2c04e79c130e6030396330d139828d01fbfd177a7667aaba7aff7cbaaedb55a88400e9481c904af5 94dc669f3dd9dc459e14775eb6c5bd3f6679d96a1b4d77ba583f458ed3d6e85a573784bd556cdafe f9ac9b872b91b49fe3a0781a0fcb624d6ab784260b0d78e1915f70cf0a44b1ef5c2cb323a2ea3209 803f186471cc31409e9c3040a1c83108665b0c323ab919ce5ffc2ce4cb854e52ca85d3dbe592878e 0b194a1bb42bbc9dd87a065661268606d7bdd5d7cf3d886863b44ea95362ac280b8576e4a54e5fa4 95bc788af47a02089c542df08f4bbece3d77418f1b94c3193bce1db32ec06c26178101acaf4dcfa3 c695867523bdf521bd7eca87a5bceed248593b342c7aee5fa4cfa56627622c627079222e904e4b8f 9127e747a13de736fe862e14b3465bbff264491bdbce5071da675cde2e40513af02d533ccbadbd70 5bd71f42cbafc47cf7fc85b9c13da8b07af9de613e8be39801f1dd925e583b8edac2e1862a4e4a67 7265911fb2b2c877c8cae040912b75916db522578ce864d87f91142748c747df7ae1349db4935d63 aa454e029443734fd2be2edace59fdc821a8a09361472ed5988578fee8ac70af9ed7429b8d3cbe27 be7ebeb7a6bea290d5e30060be9d4b9181ea568346796b406db377b80e578b26abc59db662c8ef7e d5a8913171bbe59bc46d7b20889bd15553389bb8092beb2f92e2cdca06b7375683a0c3320cd3c7c1 90886c787d7d6dead8d8d7f4097e52f2633c94aaeeb126dcc5ef9897b12ac1850f52e64653d36211 d43c30d66eeb3370791bd3859c0653046e54c9e3d1e89235d84056cda2b1fa89dcce5c090fdcd8c5 e52519e2fd1254c57bdff312ef3dbb528a68e1bd136dfe45d89bae1be7710efdfcd39ac6b3f63657 a617eabd4ad15750af741459d4cd32f5f1f2ab99e3463baacb224f77c12c72214ba777bc355d0466 2eb5b7f90b55c9492f92c6d5efea7a348ac4c363eb44b7280f7085b4d0e5fb72e797e356c9c2125f f2b18ddc28631b2a42b1cd6229629bc1267bc703dbd415e32feed4ac0dffd39afd06155bde16880c 1ee3426d72f37d8912e313b7ae250e631fa9885e7ad72ab50aca239281119cac5f5d69c58f83cdaa f52d65b33542a27a37a25743dfb8ca11e032f2a9f272d213da98a95908361bc534ea986d1dcd43d2 19cdd7d0029acfb5e668eea6b0686e7b5d2fb6b8f7273fadb9bd778df86709cfeec49bb3f488d75a 77f196776ff2ce3f0de1c0e4666b9b6c54967b22687c00a2b7593671ad7b9d2de3f4442f91425fc6 be0fd6c42cd1da61f36978c360a8f44631ac09a28526515d10a2dc5f94bbf76c857d4eb96d695ec3 ccddec5a1c7f66976f7d38afb53d7276393ea5d9c57ac87fb13f6861e28e0c22310f1338f9598050 e149f4125bd5d28d9dde9a7b921107d632b6a4ecf2c4e0717443b1808117fbb9df5e946ff5e9fca4 d3e49cc6f7d2ec1abc37334e2978b3d602b9cc5ab4fb9efaf5373415e37e6ddabd5b63e425e62944 9dea1632ac4ec349e415dbc8e0bd419181f76490810e640f829ff2179eccc889b5cd753e8600bdbf 1a804e63a9de6202f6a35027aa3a125d5cc3cf1bb420e5d5598bb818c8ebcc0488ea968ac890e57b 133d39cc2793438e1a7fc96e7698b1d5a38df11cf2b663c87d5e462ed70c47586b098eb0f1a33e2a 944693e12e3832434299d843c2a944c3321af586e5e662392c17d6ec5fd8f609fb6e986305d092ae f2b3c25ea99d434eef58178a74bded7fb59cfa00214e90dcdd195b773319c32ba83cc25ec3ee7095 9fa44fa61d830ee88e4e0deac04dec730b5febb71a45bbf7b80f0ebd2eb2bc775fe5fda73b50c16c 35aa130d17bd8e9e7f129df183d23b63bf7a6f7fcca0dcfe58f8e82fccea6400aedfcd1728ef4f60 c4ab8def9d2ecdde7b3cdcbecc7faa28da88309ecdaedcf0004059046a904ee493fe8d370a3ddf72 ebbd5eeddeebaaeb78d2893ef1bc33a120bc6d5e21aa0d8dab420b35ab4a73571d9acd9246661f4b d71ff5cbeaf5adb3bd59adde846e48edee0d989ab0b4373501af9d7fc8f20bfcfbe3879f6512c533 0689d0bdb47d7a1b140e443f0f5a6861f54d47e5831a3772dc35d3678739b22717ad7d770405cf0e 52083fed79e509b7b0c6b5d8dc09d76ae3a8d8f54635da64518e3a3bd9f46ac29c1b54034f44aafd a2825646558e28234d912bcdbb8a5a74d5935bcc27c03585fa14769b7da9b0339bbdbf506d86fb8a 2d937c32283f3a1181d3cdc602287628a9ffd4325f58523da1a4629db19de3da503a926aeef8c5a6 719457bb3a3b1e1c6b777370ad066efd5109f760508e4fb767e97b73c2d2bc6dc645accf7f0ac498 007317b706c3c20a2842320367af92809a50e80386529b01c880a0527632800cf3d65fc45f218c12 a0d0fb2680a6e512b03f6c27e0315a2450591412a80b5829d839e5f84ea05e339f4033ed27340491 df4902c9333c456513c87aa9096457cc043a2d77b14b8b9718abdb419cbf3d926827e47211812c9b 6f6a204e4276a2602f61ae50cfe06a0b4f79bd119efde5fa37d9d6de7f7fc45fe31b27004b810908 5d0a692da55e022183f4f8c6554ea0fdc84db16e0974c82729542981fca09b40dfe12c455ac5eef1 c0c718f552e3c2a06846bba8bb8d087d9aa59b8acab8e8bf4f252f7e53c728f7aed1ad7ad8e45793 5747a6b1677f4d53c1bab8168391af887e628aa2bf99ad84bf88bf49354980d0821390fc1613087f 8d1228fec90999c0cdb296c023619bc04b284821c094633581f9f620c682c92cdacf49222a7b3cfb 3e718efca65b37e35df32327bccac543c8cdc7f7b005d3ef97b854c157b7e8579f0ad51a05e3567d e16fcc451617f0c12a273dec90961e0b6d213e72a38178f792baf08bb4350920017b3b28814ab3b4 12f9c13481d37978025f082381c3fb21c9c193578a0bc7c5e2b11a557a41f74d6bdf2c721bde8a55 2c6c916deae55767c24b0c79edf91a49663a5976b6cfc1e6760e22b2f80cf4eaf8eb7f58beec4f1f b7c123a79e6777bc59a46e07b125ddaa604ebc9eed48bc36725be117d98bff599936a89c56f51067 bb909d66023bca22c9f5fa429223775692b37a97143d4a8fc782effa852a85ad446ebd7a1563f854 19771644f7231e4c9080f1bf2024f9a6535dfb3374683f9c3b9ead783c5041bd3ff29d5b7cc75528 7f238d61e7ca9c70e49c9e15fc14ce67dc69786d73c7982bb1c7c92360fe2201673b3081da253881 d15c37c94ddf7892f36539c997a09f1941be4d5edf9762d50fdbcc377a869d040a8c3c50f1ad4eb9 e3434a73f4c072c8e2bec3e7c4bd74c498db9162c55bf52aebd70be73ad7c627385d8439f43ccbcb d2f7a49158fd6868c2f060ed2dec00578e3f5f57c0188dd9171a2cb3dbdd317a470823ea17696b06 6955b7935c92031783b4963522c92f8f4a92df0eddf7458bf7afc0b1ce417c221efe7c82c58f426d 95bb97c762ed569b71d9dbabd7262b0e2f1d919b9dfbea123b69f18a3a2608cd1f37c05a3b020bcf 3ed8b9f7e90013b5d7deeb6c4abbfde2dddf9e0878b1ad9d6a94c7d501da6b5e9fb4fbe04ed45ffc dc6cda507aa10cd46292dbd8e37fd2201772792d3a954ad6eb39959d003915bc07f6799f6f27ec1a 5c1eea263e0fca12744c6e64f1389d48cd832df3bd03fc9626fbe55845f785cf96daad6607615781 a22cd0b5a59739775b2f2eee1e47ae3feee35d69b9dde56ae628a648da11b4a2edb53ba5ec31d6fd 9304d21b693d9f5135c937df48927f6b693de9861a3e34590d36fc23dbcbf1281657eb2bb7a93ba7 a87dde1fa7dee17e58f87eb45f8601bc5b2597d2f63cbd37b77527e87bb7618c782d23c65c7f5aa2 ddaed3969d178a5bce60a79eec8888227b7c46ab96393c4c2cd0f8ac4c679acbba8089021163e6ec 1bfd1709140dc1b47bd6eb497e5d9b2585f98e8a4eebbbf08cca09f3585e5bd4b5b533b9e3e7d853 0ee878e8ec4bd5c9697b7ef41edeedd58bbdf610c9a2eaae642015e7f51cb59ce10019da5144ccec 491bc3ad4f2073d6acefae2df01def4c74d20eccdcf758dae08bd26053ca91b841f22a6b54db0cf3 ff929e7514fc598a78ac5a6983f6d3aa1e03eadd22313200f5e9e2567fa1c8f15b3c62fb5d61c7ef aaea4ef7eec465ebcaf5ddd9d1daeed38ee5e3d79e5cf779cb6a9d6a16447a6dd3ad3ec6661ebb2e 36bb42426d4a64214b63601ca1916d90ae71d32f4500d419dfe8afefbd105bf3ef06bb6eaff3ccba ed55ff24811512484fbcd24e5b939827c552b87aa98debec5182d4ee7928cbed43de9d4f7fd27fa8 7dd2eb9447b2134688692725ea6003f0fc6ec1452432b1d918dcecdc7171438c164de3288e7b46f5 3545f44b3a4dd11b77914f4fd4d55cb75ff0490b54f6a3c9a372470d37c4420df721af6aab0b9b45 07d39251471597fe45da47e94f9267bdcecfb6b97875dc61cf49b9d5be353a3278840e407557eb3c 875e077f2e9d51da5b6de490ac2d987d3b26e6c3970df14a9ec6a9f1cef60e1a35c28775f6f4aaea cd45d45e0bfa6ba805df0faac9768b55c368a0a99abe3e28f1f7fd560c5b692a0853982940836052 583683f98b24d731d326bdec7f861c858018bf6fcbf2f0c608647c1e1e76af3dc188794f88e88eb3 ee8b731b3828b4e9691b65b3dfd8a67172ec83518bed87de7c72afb538908075e7a61635a5c4d7d5 3725f5d491274d9564792015e4f0527e22b7b5fa569e7bdc5386f96e5df2da3622799d262d61cf26 2b2d81fe9f2439e10c2605e89abdc54d8d0273ae172ed2fbfb3ace47c8635713baa03b28759bf674 dccd9e9ea67719109bd5662a18b4b3d4756eb7dcae7d6071d65eb971a00d26dd585dcb6d5849deed 8ab2e1861d05f06788bca8ce9692c7f2a2b46cfaaeb8975abeb8eadd2ae2aabf42c4f2fb44a624ac 58c9c15917f8297f91e4762f2829cc9276bcff6283db79377b9d902a75dfef81f7c513fba7d8fe54 5f552b378d869bd5fd83197527c7eaad5d455d774f155b53ebf5a3aa97c0bbf29945d9eb0aca54f3 bfb29d844539d70d1a92174243a9d8cca3e24aecf1c2e9459a02adbeee427dc417f9eb27374a1157 297b3683f98b245f28662134b81dfadd45b6267d6dd35070849b9deb8eced9277730a65f96d3d44b 9b03aa778dfad399ebed9c4faf7ba597a40d6b91a94e5afe5631b32fab3b98e9cbb98d1a493868e4 c4036ad5c455bceb0967e43e17ea4a48f35cdcc8a6b87ccb502f9c3f6dc09c049e879c048d891491 c9a0ff22c98fea69558f8556307d4f80f3d0d7d2d6ec0217ef719ceeedcfac7833f1ee246730c4ac a5b7073fdf5bd3c292ba52e39ace2bdff156576692e3ca282b1da5ed43f5a552858f44921120e1bc 632b4203a0da3cbf9011cedfba24d7fd061afbb22747565d04003bccf33d7658c82d59155bb119cc 0f5948f5df1f3f24796d5a480adf72f35e89d1d70980a8fb8e1e64df9c7bb7f696836e8f1b522d81 3aff6c37b4101f8e55a336c4146b8cb30a644a9a9c77795bc213e62052207a132ec349f813ee167a 5f9e7f8e8a5c501fb5b81e3f1fb3ea912298b866c98c7e2bed99096f7ee86f67d64df13166228ed9 0ce62f927c4817e395556e5defe5deebe015fa59f07edb9a2827679c7c76264ee05bfd3e31e375ef 1d5454838cfbca7c9c5fc8d8ac4a49bbcb44128f8f8929d62ac39dd06c342ebc40549f5c70aa7eb8 7eae9063b549a1cec4562d3d1d30b2a4ad3125d29079cc82f7943b9fbd2977516da738280da94526 83cea07e91147aebd24bb9dd72e7701d04fb55b77c735fd4ea644395dbceb80c206b2d53e84b350e 424181475a5b2eac2ed39f8cca4c408835e3cb0b1c985ff30fb49ade7826f181eb5b5f9fd5de61c4 24e3046610e95da5ad37d0a3e17113a53009e1c85d68d824913e054842879a24c1f3e9dc817fd119 14b9bb83f40f5948f5df1f4941ba561fc5b3fe3e0155e1b1bde51e57475f8f4f261eab5bbd53dc6d 5423096e0aacd54069d51dd5451a15c6024768182f2a2ec33da38bc20d2677935d5b417a91beef37 669ace4ab2fc028b9efba53c6d5fa40ac9a343aebaefe9eaa4d5e8153d92cd55ed530d88ab74ac12 57199dae6ac7339972a332c85ffc74d01bb323df87c2be7ff7bac1fa624352259bad199727bed5d6 90bc913da6ef49958e1509dced51e225a0dce3d4fa68ceea6d64c57c2c5660ccb96530a0e77a345a 74cfd47666f954d17513b2b251e01503da0de2865e47446b0faf707f45e9b804c6594fc3bbae9bce 0bdfb371ca96c0c5eb76f5172f657b80cfa3d625d8d14ee1eee86fee6a124c74d23bb7de56594c9b ba786e5f37bc34293cb8617f01b193e5bac1cca453f6ba02ed840946e7973586c20f1d892a499445 9221bb5f5d9ac465d510a9577a3e45100f869b0ade334efda5fa7ca3cba13a97b178189e303d710b 58dc9a0db1b8686258f471f05f6461bbac7c145f4e74f046d8c393f0c7dd863ecb9b71156e676d1d 5477d281a965dff36b49cc97a50fb4bb3a45d44e2b96c8e37edc256b5d6eba62d50db16aa24f9e78 140a1ad159952dfcd9458e785f9d67594696da7d1e2d478292c78ce7be8d01836486ce8d1187c244 b843e1e90e42e1dea48bc2557381c29083fdc52550dae18e3ed71ecee4bdbe9b65b775d3bbcaf6a2 38ee374b0828dc11c064e68dad409517fa7a7525ea77e2918ebff0e7d6ace283763458464077b11c 2fa624f6a92b22b6b9796b6cca3f1d0c446a677461779e686eb10216cbad525914f1f768be7a22d4 bca204d6bca29eb3b0ddbc82cd9af3cac841e695e661f11747d89503372c3d1ee9ec8b7e18dca3fe d026b87a95a88ab5e334cfde502b4015f11774a497637fe26133480f5087fda633c4cf308bd92cb6 0775b42855cdf9fc10bec839b92e0af3ea72aacf2e45ce9d31fefe3c6bc8c06b7a9f2f725301f6da 4880c30b446e5b2a2277781f91c14619915eea1091cecef42fb25be74ded04b675dd071bda5d066b f55c0ce4ddba77e19f08b2a53dadab137ebb2760b32e41cdc967819db1036b3bbddfd3c167072160 44b6e52ad2a783fee4ddc84f275a3c21262383e1c609b1d5c69bf22bdb7039465eddf3187094f7c8 46a3423a82a50723f85366869e79d90d3dd98247f0a9da1cc10a3afa0b273e264f73256d5eba94ce 8954e8bbf58507ea1e99743669aff89c9a8dd3b06fe1c8cda952b242fad26239b2c53a35ca75afce 70e9d66ec3a2267f0607ec591a90ad667b50f910e3fed9d4d03e43064cbfd1af2abd3b4c3a3ddedb 5e7a6dbe9e74838959ebcae5eeac2b5700addb7bc5d9ba67b7e752956e4fbbf45382e12face5f81a 1abcb689d46f9c7b8957b27465a75ce291f567a42f276c2ccc8f428d42fae67239ca01ceac9f3e04 d19ed05999bd4ef979e9ca6a2d3b4c278cbb4047db71e5ce48f05aed241d7db7110798b501b449b4 ec062bb660c3dd34bd55e9d45c56c877b3f0fa561a7b579d3656dc406e545aef4ba3d2de17539ed5 5f6c1ad5d3cf7771e3d722924e4bfcc18d95f9813c8bf5cd527f57a5f9d1e8d093f45e870dbd9339 ebf15403e9ac6175d5fee416567bcaaf4fad8565fbcd74da1e358beb17d8587da242fd4c81d57afd 5d69d7f8717d50f50fd3ecbb51d55e8f232baa664be5b890d865dd27eee58999c0a5ef9cea96acda 172fcda27404398bb6d62fd69aa824f27ebe7df203c13a53e468652fe3f944991f772366f22617cb 6171c0671dbaeb5fe271ebab1283e65662a8c6b117adeb9765d1abddd32745ad03751fd57ea11854 b455f15d36d2294fe97bfb82252808f345ac1f570ac4386ce62eeeae070b2b178164e6be82facdb6 02860f750b6a62e4ff84542526432dfe42f190dc5be832df1b8df7f61eae7e796d51ce63ece42dad f0c121c74cbb3d2c18b7a05db357a707f0a0264cf364b59f7389caa864d065a46649a5794bce5ef3 29623d625d204623235f03e10d7cdf9ecc74b46b3ba0c6715bc090564700e96d7c00e903df946525 e53a0090417f996208ff17d7ad3abf883f8bda25fe96d96c1537fe22861f7f9311980058d84c8035 394db95109a00fb494cd3601f8f72d0184519c00c6269ff2ac25c0a6d94b803d85a458586c8b772a 5e74633e86d3616ce4693d33bdefe3bba85870b2c3bc5724048434962fbdb855a9f914d5c6e0d945 8793e0d5e84d02256eff49fc3921d7f84b188ff8bbbb0409a05985048487dd04ec9fd1041cb4b814 c548c0fa73ffb351b9310a52b46fcabb14db49ad192f36a37eb4059149b43cb3685464b4d5fbd070 d9f72a0e95776593df84e7696f1b3220737bddd0f3f7e913612950ebf7861f19f9a1af130dc49f74 aac8e30b94b2a1fa4ff98bf8db616ff1370ed25a62e94c189c97aa0968e9c3047c56f0144e4c795a 09f81a9d13f0ac8531daf080682b5c8b5109481a6f1205baef6aa3390ecff7d1cff7d61801c35f77 80655ebcad89aff6e2a83f835cec3ea55dfb1284653ef1e3ba557c985fb7f980a870747707d0f49e f702e4b6e3eec88d68ed26bf481b72f748ab987f2620c47f7e1a2d401b0944804802191c95f25413 6833f262772adf223c269e6f0aa03e219ba373afec6bec2fa1bd6f3f03eb3a78caf3641a845e8206 1a5e5805a3f688f7e380d27ca42f588feffb727a587a23bba7ddb1f9287f23d66cf34a1db4e1850d 85c9592c8c46e7cebe393c3dc5da6f1260043d13e0d2484fe921cc92631c3a09dc582e12187db009 8cf5f5b8b042bc37756f9ec2260d673dedd5e10bef67dfaa83c11aee16fd845b347cc45f741e766f 397ccc436efa80b5cdf2be2cece97b61ff966eab2a6c5c4f6c6777a59b7270169935781a2076e5b8 3e3dbb874d741d1ca6dbd3e000e2767fef94946c8be24ff91f09e056d35ace664102f193425ac14a 3f81cf3696e4a01a1f179bfd75788d9ee6abdbb7bd606d8a277f8ad18fc74214a3bbf732a1db216f 166f15c2a95de9d3b67db9d5b759feb4b33fd94ece5debb038a9f06d751aa453fba3de1e6f0e1f4b 391e667538d9a393416987538bd6f6705df4b6e47adadf56c7dd9e77fe547e9380d5615acf8f1926 f0c0a924397035fa276b6f82c738d4e4c29bb2179f83774bf267e047bf6f85977b2307fee9da28dd 1e6789d35f27d5d493630c59d071c21e4b07f371ac1d669b6f77ef42d0688fbaedc53ecf32f40e7f e8f2ae247edc6dd59d661b2e3db6e9e45ca17b6eb81d081c38327aee39fdbcf3270928e1696b6e8b 9f047e81f524b7f12649bed821deccee443e87726ef5705e0c79239f33f1d236bfea69f8b898d90e e2af73d91de68def6d8f75c260b773a07847a04d687bdc350b5b4ac0eadee549743d56d126ee3d72 97aeb08378e759ee6d1cf9b4bdd95a9c07ac0499d4ac0dc0f72d6031e9fd60dab976f7872c66f3ef 8fb441793f6dd01990e4a6ad46922f5cc771494c16af5e549c3edc557b7cbd58bbd9497311fc60bd 9aec1edb75d41df144ec2d15217bef8a5337af79649fae586363b773ddc08ed2da64bbed9cfee7d1 b4d7b3f7c01ec3e5999578186d4d8b9a660147e8602efafc67e3ad2f954de10bf58dbd19f6fe6135 bb747f914063f591c0271dfedf00ea1a18bf9bf965b6332500aa93d6ad36416aa7d1f9d439c0276f ba5b4dbc95774d272beee3b35f3b2f686f3b03f679b0d78ff7cd1e4bf0cbda84858f35d55a69733c ea6573214edb66aecb8d374b674f6c8a5841360e05ca332aab384b31a1d3e1bcacd735abbfbe8d8c c19a4b84fe5f2490a75f935c670b25f99192c57ae1e14b0efcc67ddf21a0d37bb3040fb9ceb4be3d b70743d75fb65047add6295b1f0db2cf215a9fcd54b76615da351d863b99b91d7ddf6c0925dc144f 3a601ce86dc1a8dcce75fd2c80039de90cb0f54da1f9353f783a9a6f602f4d229e45ad57e977d5d7 79345055a6d9ff451652cdca04aeddee49ce78c1495e136b7179b46e04c8c3ff5c9b7eff7cb0a347 b43d73e7a22b614edb8e5ecec4fae67dc2749e3e67e60791b2c10dd0348e406e6b5417c58b7ec9b5 02bd31a944ebdb7798e54f5bb7e7b38a16c07c47eb2dcda91a964046558fcb8d12d79ebea2df9679 65a25f3bf277f21efcbf24300dfa497aade7927cb8a93ddff3d6e7ca2d91e3692c88de4fce147915 78ed1909dbf114ad5bb33b3e34f30abed894b63c65502555d02f94b9d61b57c7590be9fd460b48f3 aac9d573a886ecf9a36acd6f518957df8662547b63f9cb92846c357d4d9e7de8ab0c59b96c8d4072 e7ab9684c1ebc1bff47f91f6d17af013458b098d2eddcb47ed788a86b67b4073156bdb187d2e8ed6 cc7dac79032a9b05a0d1338edbc654bf1cdbf8fafe1e66b1ce75673c5334d9444d350c27aeaa69e4 598913d257902e1ec9df970ccbf3a15593a1381c4ad8b48a4b059057c49d0b1e45023341b15c2c36 c5728b1e09477f31c8e8ffe2e7d6599ebe923cdb824361bb4aaedc7a7e38ceca4b7b77f0cf861bac b647ebfbd443b3d076f306e5ed9a3aeb87e3b5903eff35398949f53d830475e4e635655328d90a30 8576f2dc812e328c365e126634b32c235201999745c266bbc229e72e046a5715851aa1eff86b65f0 e5af5dbfc1b3617d9831c8e8ff22c9117498e4dd4196be69723fabe07bbbdf46f5b4355d7eed8c1a 3de7270e9dc39fc6a94f403a5794ebeb4e680cb47e62cdd43168e1ca06dd310ab03bcaf2a27cd948 1e7df1a4c2767714f778e08be563fc11e83a5c106adb4e9be74ac48c6f1e2f1cf7a0671e2736c00f d789f92c9d6e5a46c38c4146ff17e9b524864901a20bf755fb783e7e766b77779ce42c37d84f341b 6807c6668fb71ffad51b01eb6e0fa9a8ef04e9281f109b286083c2e48520642b8592776205a95867 3571c50b96700a84bd4053da8dbf5ead886fb5f639ee11004daedbef20ecf3adb1aca2575d763039 c7ec0041ab29e741caed4f925cbccfa651d1f151816ff5be7c3a421dc5dbb2b39be968af8666c1b2 a51934195cd6e23e8ed56804171513849bb2d3a8a50f3061b4908a4a9f1057d18813ce134411ea72 dfe06ffdf9966fadb10b277ea8907df5e86cd18b5542afc644a3f788597f108a199b778bfecca588 fe2caa95147590a2ffd0ff45921f26dfd7b3d48d2e521139ee778385eb7558c7b491285637fbf55c d25beccfa7c3b4c1d9782926e2c1325a3fd5a4b4a17be221ed96623557c305066f32fcedd891f936 5cd33969d974d9d7a173668750d3672277fe652605ae429b2b6f40cfaa35927258c1a01c6ef6a29c 6550a21c7c960da0d212ef67f47e915e4b15f0e1d5bf9793ce3c773b6a5277dcfe4335ad051e6a46 9d0438ed25765ce51363be8cd2342095264a45acce371d81f1f6639ef7af2817f41e59ae214e7a03 229b4e6cd6ec504c6c460f8103fd1dc16997140b090d75c7450a55b92eb98daf4b12df201a899bd5 80c425b740e272a39332ecfd4592bf4c73f715fef39ef47166ec77db26053af607244db3281fb5b5 af3e3875b2b86e64f4045fc5e3a41c0b6cad5be085d6b8c1052637e4fa903263356c8333f1de6619 637555e9eff96ad2f3c66347b9c2f346e54fef3749347e6e9d64e9be68afa88e8daeaaafa6425cd4 e0465c5836b7aa5ef3ed55d5abf632babf88cb853a7cbdf5adcb7e47735bf7e946aee53426a6c12c 05551ba68372d96d4f25f1b8de645d4068f64e2f2e486e20fbae825576c4d6bb8c918c270c305da0 f4dc5992348c8a2285ed9435b92faf5d9238edcfab53fd11ae6afc0722d853a58e3feaab292edcde 022edc77675cd8cfc1947b13173671964ef7a7fc456024927f1ef69ac75d0d9f6e9d75e27b264ef4 2cbd7d10d6ca3c3765c4e3c56778212cda5cffb1b8338943270c70378bf4a2736e529efd1952c57c 6e46ee77ad9f2f9455ca0376753a2d9455bdbe34098ea7f744ab6dfbb8289f3ecb6704569683c978 84bdbf57168b007987bd5fc32ff63ebb35eceddcdb7ff128e6abf723643c0e9e4875b3eda336f872 3ce342576d4d73e76ba93c8b285eeca2383b46248db6a3ed9eda963e4ff230684364658b57574c89 ef10374a1d117cff8612adf787c47dbd22e0dd6f6bbd7c595377398457174c5f2a6f6c52f40bd9c6 31936af5d059cd5ea1e00db15070977fa3a02194528e2d1414dce62fae7cff78db9deaeb83a3b7c0 ddff86f679c851d349fc5ab85e4196f94c628cda72779aacd61a567a9855634c5e8936a947b81406 f9653886ea4bad38ec2f87071cc18c2a836193d0a1d1afe64ba835ce6dd0d9677058b8241d2cf217 079aefb8626b4eb414745e0a3afabc74825ef3924de5e725c5adcf4bb491a5d3fd29ffe3688d0a27 4fb416071b9a7e0ec6b52cedb5b514ba72918175eeb5f1396a7b7531e28e156678bfc2cacb111aec 30442cf9a8f51a7e5018658b0b2f6f3516d83d1c2c0a6229cb3232275ed5e5bceca0ecec844aca8c ce1fac59ed51b94cafe22a9e72dd5b1979b853047978051979ac833bf2c01110794cb4ea5fec4e8b fde99f8592f2f075d2bb033edb09a9826eb0156edff4c6f565b63c592bee086c9313270b8fa2b1f9 fe06af67a7ef683fab537230bdd52edf696b9d14107fd26c20e217e921dda33c9dbc28773951fb5f 6e3288dada38d2196face3fe63f439b5b307c1c8540e9d91a98af468d61ced472616c623733c2aa6 20a55f78dda378b6b059eb62b43ab78b3629a127893c532e376aadd61461a31cae8c307ce1054b64 da7aa9fd896af8d97b0463fdd555c71367bd1d598dc77d347b14a2a13b1dc0430c5c5586f9f3a63d d831d7d1801895b141294198fe716fadfb1409edfbb51e13f62e6fa8d263bdeda2d7c4d776afd969 3e7b2cb1867a2ce2157ec852b6fefbc3b696f46d4363adfb7a583fdce4dd183af2b20dd83416bf14 a2dbbbd0a8537f63b3463137191be55e6f40905abb771d05e9110e3dbefb90d44db70b9df79da707 de3b0a37883a83319fbd2adf7e7fb6d5f6faf0e9b6c75477dafaf415aa65e680750be4b163d321a0 6f13edec7a4db4ab0b4db4baba3417e13e6a2e2e11f80bb31c4e1fffec8677e7f8551076d8965954 175986cb550b1f0a18e834895963dc9a8e0d041d0c08d36a77c569a1defa4cec7a335f7b0c1bbb1c 4537f0f484344a46c9ad1f09f452a7cad2b35e7d9ebfb58b0a156b2c3a6bd79a0d13a9a6d30baada 994eb5cab3f2c816bd2a72c88195bed71994dfcb902fbff1c3b9ac05d1eb1746bbb07ca9167ef6c5 6b2538b1d3d6de5eddea92fc13d59f31d7d5626c08fc685096dc4ee7f902ea2dd0a22b7572f3c9e6 9eb52b0e20b56630a7abe29c952acfab615406e39d5b5e9bc743e9530defa5cdbb1115ed4f0d28e6 c879b9b00c8d5efe80858bdc596af3b97a28d9f0cdab3f619e3b57e0767b8ac0edc993fd8ff4303f a566a09597746a0d6f6c12c11e493f6f1af6495c76c6c20a36360c75dc3fcdac4e6740bfeb4d34ad 528d799d0b15a51155cb51b81f97becd27569a9965b688ba8050c01f809aa780c4c8356e57ebe730 c1690bf5e1d3091cb51e3e80f4de0980f4a7a5ff8b9cce5729a10e2043f29c12a5ff3b9a957e21ef 6baacf0f3ad4815a25ddcd5277bfc2bc7a7c1063e3becda2837dbae776dbd1226934f0f5b852151a 7abe6478315074945ba3400c73dd3c35b975734d58ef43c18119831adb9b0086589d0148b7b74c21 e914474af96cd27a4e0f297690d6b30ea7acb30d306955cbf314994bf9a67f351cee7e1127adae11 27ebe5264ebcd526fed0cd4bfc795e3ef1171c5552dc41fc09213405e7e2744eb58e3f57d08b3fef e52d65f3937af2f37e7ce34f5429c4b349b11c83df562372e46e2742e7cb519487b9d97bebd9c41b 5f3ef977a954d3c36a8d383d6f07e411f82111073d6c9df3c3d126ff0f5a61fd9bec30c97a65c69f f9c9893fd4d589bf1dcd8fbf62138abf7bb7197f0fa549fc4d0702f157da8af157ae5829ab638ae7 c7b3f7fe1db9e3d337423fafdc7b377b95de040cd6df25afd80e8fcbee30a48ad36c8d20acfaebd5 eb225d8557234cf4a7d01a9dfc50ab3dfc5193fa3e8ccda6f84056ebe203e82ac5bb0d8abf496bf7 4c1be8097bf117991f13a0508d1300b34a09c095ba294c16b94d80e5994e00bca5c5d677e1441ebc 3c4685257d7f1307310c4f553109a98b09bdae4db3f0621f87743427faade763961b3c3b6e11099e d8601928058ef3df2b7bed8f2ef0f16e4f01ff0e9ffbdfdb1215b33406d7fd7d59ba569041f17206 ea855fc45fa0b68bbf02b24b80c6e59600a1062660bf5a4fc0b9344a89f0041c0c84c80bd209d4fe ddcaf60883dbd735ac5c9e7ea1e23fc5fd208bd904af748a190c9821ec470db4e4afef4cfdf141d4 eec304b6e3c7d47ea177279fa3ef3962a0de8ab4b8bf9c71db3ff37d2f3949dcab70544d33771cce 24f81057f9dfa4559cef7f962262629bd6b21d24a019671927a47602b53fd3042a0f5651a90db221 c39ea557bb62ae83d75db5fde8a9eefdc9c0bc3eccede1f9804a87e88e5287ef6d7b3dc2379c0bcb d7c3e7dbb856e7f9de85f1aad956ab337f6860a7a047f2c7103b3b875802affbef6510efe72332b7 73f7b3dc0e5bf5e05da152857e9156914a5b9331766915d3492784cf4a09b43d67a946c6f3f7d17b 612f0163b2a767a0a10dda47ca55e901ddf2fa6d27c5f6f5a885bbcb651f9ecff7f3fd7e0ae2cbf3 d4472ed1f10dbc81e388810b87a4d1ac1d1003e9ee6d8042f673fb48eebc4655dbee11e6e89d9443 e8d5f310ec5eb771f658735ba51de8b6da17e8170980cadb049c95ce09b4fc7c1238a7571298a9f7 a3b2e58f5e1db731f437396b72f76ac4e2563631eafc5801ec491924f2714d00c661a3c0ee0188e0 ecdcec1793c2659f2bf583dd92eabf77c53e0d6e576baeb0ad8cedba471f9e03af4e7530977b0b82 e3e3f99dd30df0a7adf60dd08a3c19b2f425055a93e21cf845029c2d2f013ff8ed674b7c6e0824b0 ed5763cf69755f8f4fafe96f6ca27edb5f9eadb39f5ff58e7a6e3c3d4c8331be47e125b7c38b0b79 4bf6968677d696b6c78cc4bd7bdbaf2f2ebff27cb7dd7dc78ea47e40a737aa976c75bfc8362adbc3 953db5e25e8db5269ae6985621be9b9034fc6ed0b003fd435e2b83bf48c0ae6525d0dabc27b0c280 49ae9babbee9af5409c6770cbe1d7ac4f7f4dabef207d33b670bf9bb6d68f6b7e4de9979e9936ae5 f2f1817502e2a538e9f44ab7b5613a5b1cc695bd151bdd8b850003dffc9ee8d8b4682767ce079ffa c6355aa30d86282b63772e6f8c7283b8e8c738f8e835e409ff9005baaec009fa45026e8e6602d7fc 47922b2bdf24b7010b2f09bb7defdbf5fc7eee2d56f703c4b592ed516b14bd66a5d17282b83db2df 406f6125f5e1ca42f81967016d4631e796948d6c4c786e3a1bcc73f69b02e75f8d7d3b171a44d003 f5531f2deb34bcedebb56d65b9e64a9caa3d14f8ac89032cd23ad11a569f3a07a9ca6409fe22815a 25376dcd9c9fe4042f4ba4f93e6fbbd1c3398eb6977bbeae1f2cf276d89ec0c3cb150117b2dfbc5b b5367dab63dacbe3c4840f7774b3ec7d2963bf867863356ea8fac96c183a4dce3cbd5e654e6b8ee5 1feb56739b688f475cd0babd46d605d417a42f54c52b4a4a54540eca3a80dfca589ee6e44fbf0bff 457ae227db240774832477df273ed4eedb17a151518fc67226ec88fdc47145a477b3d7fa30b1c01a 920da0cc5c0b6b1a07081b18158c9aeaf45e58ae6f5d835e73afa3a8f9c3a3aa8971606add0db453 55307f5107ce3054f406052be3fbb1297fc42a224f5f3c27836e772f39d82694d0c227f70fb9bd9f 05887fcaff486098df2739641a84b78373bb55f78a7e4cfabab8c7ce21e3718bc0b0a35a70b24032 0c37cbe40d19950b50d5eb0fb8b3e6bbe591e63bfd85d6cb8f08552510461d96892caaae4427d950 2675d9913fe98c529eb52f810c01654042174855cae7ac91b8dd35181127ce9e58ea4e5fc2e16542 29db9c40aa3af48b049e3bc79f689f3fe3e7cef999ff49117e581451617bda5994f36ab38a352bca bb0d3e927dfd7c963feb5bbc2f6901706aaa616edf5387fc1351f4e08d2a13a540ca6654e0e499de 5125e7dbb7d2a929b6177730751371cf4d8463f15914c8609e7d9242a8ca1792bf0c189767a2c8e7 1bc6044cc1e10ce817096cbd2ee989b7a33b0103d6691444ea6e1f5679afa510a4adcf6b9c99aff6 1da3ba426e6b3e4622ad775a66311b75785b5515a32d7664abcf0de5d97b3393dca5b794f2873d2d eec88728962e4f5d3872054fa05afd2b7f91b088677b7681bb6b499713c604ceb5bf258b0d8ee683 0d4e55306598cb807fc8a283fffe48fb68e59e5e46a07fab351fd611786f952d93fff2ce6b3b22ad 1976a58ce30830d3c73870d2fae572a8c4b722207f837a499ecfa72d095b0e7ae2ee301e8b04b95a 08c70bbd122856c972a9f297c75ae559716f71f7d7e3c8095ae1c906c91866e5cda6c5f6c9ce9279 570393d1aee82dc5ff32da2d9fcb807f91e43a03ff1916daa7cba3bb31f7db627a3df2429cbde663 eb418d34f32b0ed759c45e6bb2bedf2b49ede4cbd6e7f1913ce895970a4bb02696579fb6405dc001 7f6de6109e7dd431eed16d519c082e65aee32826abe4377bb6bf7b06ccba520499d1996ed109f3ce 5e92a5374d794d237ef59aa27f52ee7006f48b24472c9f3e8463eee96d74f5dd816414b7a7ec78cb 9a97d3d65407e8bab358c8eac85ab90ac02937c99b6d227155d0b3cb533889e70a7f550f4dbe199f 7bdcc388265c17f82cd8a75324d941be2230efdd5867c6e5854727a7f59d9ed64f1f1ab8b7ea942d 6c66d4a2db9753be176ad1639294b44ba740ff918554b332c9edd6ef473e987bc7cda9a86f196da6 38c3c8e04d0c7b923a6b8073f58de77805b8b62c6939eb9ec44a33bd50ebddc597e754bec8890653 635f65b5c32aa7ed8889ea8719b3bebd56f4a7fd627f1aed59d468705077283b5a5ea9dc448949ef fba9904b7b8590451416532e67b288cd12b23812a00cf01731dea1c3db09ee7887c53bd23d3ee929 36826bc286783ca89ff59ba33f573692434adec3d7c50af3d9f3b769d1e77cb49eb02fa201b383d3 b0c2e803b4c58c237e409b1309a1a75f674939f32d43a1b0af90db65649278a9755a1da845b8226b 87d2aa72eb8e88337fcd3a34c174d83d715ee5df29189401fe226cf7a4ebb5f948dc7da978d6dd7e a5a458b04c09461df42875bdd667720e1ba262e52c293c8fe82ed7ab6fafecb09dde6d27f3308bd9 d0a6f72dd233bcd1a09c63bb4ba1323e21b76f1225f1b54a91a5f1565a91e679435ca0684f305ef7 89df8b7c1ee70f9f3edea6141a6fd3d80e6f2fee2fbc8d7640bc3dac01bfc85ef6b3ead62eeb3fd6 f6d233753b4121d52cd658692dc622a580243a15c94e80f06d24cfb1e160623246093fd2568d0f28 77ac2654de747324011d2b64e9f26daf8e5c75b8aafa8d2971912638c1f6183e8bdcde3549c385d1 79bb0c369fc7529ea11016badb36a62dc704a6e13507d326868f69080c60a150f8cda3d096f70787 6e995ea7c9e896adc79a715e12b2362c2f29a954f8d967c3f3376ac00edfda8ab6a4a34a6158e092 8494bfae8e613b5cd5b0f404b17baa4834575a1d7f742f3d5c78c513bca395d0a59c34a8657f4faa d8ba22d9d8e87cbba29b66e583220fbd8902520b43010ac8e202283067ef28d0bf2429f7cf2fae57 0fd9ef4b7cd172d65dd4309741b0d67973ae2856ae4b090dfb326147fd5a9f86a7094212499f5bd5 5ed48668c6f21e1789e363f93c85d932d152519b39ec1d77cad8da9837b131210fd04f79374337a7 9040a74c5558d8f79ebe5818fa71be05fc70be74d0cabc8846d37971b45fcf8badd1795e2c6a618a 9bfce2348e376eb6c9229d6f5836381c6e36d57e60681a5653a4dd11a4b8673d9851ded4ebae6862 d0c6c59bb85caaa5a78ce983828d7ef4fe099d95a8270a1e8dcf02ed9ff28bdcfb5d9d6ff5427b8e 4f3ac379a9c4a2b3c3d1cd3691cf482a56a6e7a8ed4c195d7d4c1b483e87dc017388f00e2d23b70b bc476e2e1620378d7affe267f9c9793f1cfb9fd0beb0bc9b2a80248a58bbbc6826f9eee66485b1b3 e5d5a5ba3c3750f372182cd043859cef7ab4362fb99e37a30a8fcbf42201cf29db6b7ea68d709a1e df556a48a7b06e4f82fd693491a50236e9f7e6dc380c0d7dac79a5d378b4949251526ab446c631a2 47862266eb9ea3f80cdc46b1537bfe62dbb4ea9e0da657bd71b187aef63603472ed6af2af79a9a34 9dbb6a0b82df7923740ee9cd19458715e4d1192293e782a726fddb591dbf8d8a3b1e03dd2c6c374a 4e7430dad09b6404dc1c6868eb5165b8402aed610e988e07dec926064b3a1107c5c6dce9af8ca3df af4cb152ef0c95e6fd4a1fdcf42bb5d5b95f8137c12f1cbd106dcdd25dcf9622f44e34d9a920bb73 c5fad7d698ef5065c9aaaf2d97a385389e130fa6355128b6347420a034d88ea8f1a0b8df117db2fb 15fb5568b8e99d5d66d76b14a44bf7f638bfbabcf8f976dbb35eb1134064b3235dced94cba1db640 a2ad7e68b53d34df87564cad81965147062d23df535a7aecec5afa037cfcc2caabe7bdd17cea476d 1cf40ed211645d6ef819ab54b95ea5f1febe862e96fde21011efa52c39f0083c744a3dc69e82ed50 8aa056fc5e765ac872336d01c5e7aa69054db139b7a84d139e1bdb06e6b9e74681fbbeeafb0902d5 89af5cad978fdf518dae0f895a2dbae9d5eb8ebe57b955b7526d559b54959b1db32c2355ae5f3d56 b95afef01f9bdace3faf07917e91778560cfcbd4c3a40b8429e24f8d257e42e688cfe0ddd16c29d4 7a1764936f87fe0768c2e222aed1f82baa88ef0b507ee9936659c5f52cb15979d88ef052f4ac7325 dd5ead4b13daf38adf217c2b9a31121767bb7db908899d61c19deea8025645edfc8e8dc23c313eb4 73478b667214bc3772c7f963f31f7af719dd15b7363c0b82d0cfee020cac55344224ef34bad8ee17 d336680e46b3b5d5e85d8c47a93d923e60635919c7d55be91c96872df45984582828b8e1bd54282c 4b9d3ce1f7c6392a6ccfe12b869270f3c10b9038dd1ae0d3f1b7e0a0d9ca36c302eb0d0f026332d7 4ed92c813185e8c078fe7d00e3855504c6a884a6a8d87fa856dbbc8bac6aec59c0e4362b4e9f0818 a896f1292fbec6a3d93b68f758e1596e257211aeef5f8b6c8b62c5b76e5169220d9ff91de1dee1ab 7d7fc1add71c827a79ac040e3b5413980cb02e3059b848ca170726e8904b310d6082e50ec08455de c084ab9453ec3130e1476c4ae0001381790213b19425ccf829ff433a7eaa1736595f6d92b61d0533 a718396b2c07b3d1bc0e757b4d11a8b6a6fd62ae5e7148a01cd6ce711172ab4ff8721b5d40a5979c 01dd924300e9c0404a014ce9177e22b71daa9672ec0348179aa64c88145d00901e68a6acceff179b a6d3bf1bc0d3ff0df3feb3a197bfa648b7ff88e329cfc6f18aa1d37fa1641c6ffc6c4b7c1cfb0b2f 8e3f0f3f8ebf03208e8f5c398ead6b378eede62c854dffd43e8a71ece48d383ecfbc1869cc0ed177 373b471681dfa379997f45d0494fde2ebd83def9ad5b0889d2a5fe3a5261ef79c1a12cd6f96c583d c60f960dc9ef7d18fb11facefe117e0e878776d8a6f8e7e37fc4f1dee1e2f86132e9bf242a4eb8c5 26fee40ad7142a8e939b978f133d69c489301ac5092163e9bfbed99023465e7935b287a54d041b25 e7ed21edfd7b094c2ee17e31bc8744340cc2b28e46afd344005f354b2e3caff363e3d9f4c241207c 4ba8dff7dadc63e4a3fa7d433afb3bd87b5d6f4e35badd16e1eb76cb69e76c21ffa7fc214e9a3e1f 276cc4c59fda528d3fa7da21fe02da33fe8200147f5e482dc51dc69f0b84c5ffc3d67977290a2c7f ffb520e69c892a488e02822405540c98457dff0fceeefdcdbd7b9e7f3e3db3b3e750366d77d5b7ab ab73d1877f16e28ffa281fd3f99deb81eebd76ad073711aa6faed9a4b3bf2a58f774ed7c7e8ec75d b409f6ba0cf20c78be596ae50cbdedf66931de23272cdf628e4baeab1dc2bdb2dc6f5eee6e27de94 439c90e43e565be37ddc4d06bb5f64d61146fae6593dfdf4f27efa49a2e4a7ecc48fa4fa92cae9e7 b4efa61e0762cf62b427ee5bc2636e524517af6a43d72e17d9b42e83b3ed9dcd811d9ce17bb43dd9 c86a7fc25efb73e28df6f76452cf03c795542d1d8ba75ef3c0f4a7d07e3bf7a83889bd6fb4b6d58f 776ff3c0ebdbf5cba81fd60e04eed7a3fce75fa4efad63a6efcf6cf657eb35ebd71770b7c10c693d cd9fd2dea39ad8d0ad7dc4b1cb956f1367ab53654fcea0fa75d5131fc96b49de2959472a57720f6b b2b43a54a3d27a2ff41afb9d0ce593f8b428dde27ea5f7d91afca8b4793c8c4e16c71ef0f588efc9 1111484e18a2491c96379763c0d5bd7d50dbebbba076b7f73ff83a50df36fd60f13ceb4bc07c01bb 64fbca8db9fb2ba75df229992fd6ef62715abff643a07d46577e3f59d664e4585647e3fd0eea53bb ceb6c0c5fdfd5bdaced182b679ba0573b3e0fadefabdef076bd7a4bebec03af7629368b9f69f51b1 b60343da2c34c20a4221419d8d8555cbcc5b4bb53459fb97197ff0f51173f087397cff0fd24f78d2 5fc01c5f64d60d0e2fb0bc79be40ac55b8ef6bd36f5078991facdc096c97f387cd7357dd7565bab9 3541acbfb1db24b6f6463419adc831134d5b9a1c46aaa685556061055b3ff48246210c56fbe373b7 6a77dfa76536abbf973d822bf9d7e66de00f5594f6ac6afcd56c5ca7f90a5d3c1d1e5dc06e1c1c9f a91e1ca20aec7ff1029a27e595ebdbee0bec88c71718e6d267e5be482ff7ad744956a495ec25ac7c dd9aebe367eda5ab52348d57cd9079ee87815049be11c1eac05fc855fb91b2cb19fa9196bd4f71e6 1b7cddf4a106ec798f031d7928aa1ddcd77b7377473c9877730db2edf88f73e61c5450cd2eeb7eb0 d80c5fc705fb381d16356bbdff073ffb37c2437ee54ee9f20506f3fd2bdf785e6e6a1ced4e9399bc da9d3aa2bf7146dd38a298d62dd8157ab9957c195697b3c7b0edcfcbc8d07b7213dc43f713c27d9b 3ae78e5ebae4e61c4f73966cb270c8dae7eb75daa154dcd834323edb95d20a58706c5a5fd4eb246a 89e65b3593f2c837958d7f30bb9c75985feaf2fe1fbc7235447b81546bf5ca0f37fbfb81bc87a7c9 a9acee0f9322b771a2e87b94242a2fa27075b4bc643918ac522f2dad0beea7b6aeb9e3f9b5eb2cd3 17ec1447c0c8a697006957c436b7d8b61079515771dddae3aa6349801758adf87334d556eb697653 b332bf3820341fd6169271df17be69d70682103b3d7d11873fb01d68ff8b57ced1e72ff04187cf35 256e9240bd89bb733da5b6d60d9f443484ab2be530f47cb33e8cdd8f33bc3a2b11f9d8918a16ed2a 2034173c217fb386ac7d38832d89764666725953a6aa9f78b3377c29f36ba16dce87f4d0331e1569 6b9897e4a6bfa06a41770a665fc7a301aff93dd7d126b7dc56cbcfcb87ff1f5e6029b17fb622868b cd152a94ac835870c52d36d2c975b172c282967b127c938d4dd78b0ea153820e47bb4abf9ed6a171 cb59ed45a162aa9f5a6b7e9d34fb73230f21c6431e8d0dab23d006723604dd19fb337d045ebf8fd1 81437badf9327fd6f2e720370bbc5e77464df68cba0e0796ca2acb6d865ba2d6babbc33f788172dd ff11264f05049dc5f77c8b5b4f8b1332ac1f2c74396c43840790f2b7928553f267cb85d09dedac4e deb89afdb2f59e1bdcb2605866583350e4dcd11de73cd047a337aaf9fb32a111529fd50a6d429e05 2fcb9a9547c14add80c04165f7cc47d9b5d74d4538e384d2743fa67c9c90df6da2ac758fb292d70e ffe0e7c53f847a313872fdda6c8bbd3e5c18036d72d5d328d473aa4fdca17a357921284ddb3ce79b d1dc38f713c3ba430ffd8d8c013dc792256d59fb491fd50aa2d2995189369c95ed105737f879aa72 b91cafecc4a6a6882dca539a89bf93951190cadd1c5b97cecbd748d2a6a62e6972772d0d3af4511a 94c7877f907dd3b5f5e5f1ac7e0ffeef93c259dde46b0917c8cd3ce93f5610ea12d012b16b93b360 75dc9b399fb76f2b03cbbdf7fab894bb6a642df79a8562119c95d35645e5f06e53ad03584f118931 acb49ada443e1e978cac286755ba00e56fe02169fe68230d08ff2ede5bfd9a384fd6b808cf185d48 fb4920a483eef18bc33f78e5f94d7cce8dfdf96e705928d11a5e71abd902987acea9863a659a1e58 4779c1ce6f7bf73b040c6cb4f475b0156eb5626f739a55a0cd43ad63e14711bd6d41694d2e555909 ee2db9ab540652e6856392f61169f13ed124d1ccef2c113e36d742aac83701bb1c4bfcc72010fe33 6fcc780f3657193e5f07eaa7fd077fc7e8de31b7b68549616c9bdc725e7e51ee7254c6163bbede35 350d9b1a8bd358d14192b467518f8b54ee49ee953daddde484575e72f7a08392ded1bf3eb434387b 75f1aeedbba239011111c97749c196c7828077e673fe733e077c16329ff87c7e51e0566113caf052 b929c32cb9291eed336cff455a0ae6dbe3e6c4ce377e19feba1c811cf39c8fd5d75387169e98a548 f7ce1c9ea7237d92ab88b348ad9b2a4f74574abb52dfcabd463d9174b99bbdc8af2f8c0c90bcf002 f9aa60afccb6804b7b4800da9f313fd18a2c9f1f933a1780ee7721e0a641eec4aecbe31ccb6c6e03 96d90632cbc4889f61756019dfdbff837ba795ac0e520e9dafa97a515a6910c37b6333a06cee78c4 cc81b3ede89fc88466d13162d5c664fbddbf914fe5ad235d07f7b5f840ee0711713e57c101c15400 a406c8fb6da4c24f4e4c8bcf3bf6900b465b9c2b2d2f0cbb9e5654966d731e5b3ded0e8c3068014c e3beec318d872c328d63decdc0ef32a8dfd5f3a7fdc5055d54ed9dae3cf588354179395fe0bc3b2d f9f4a2f5596246bab33bdad418f654fec24ce593ac2a9231502dd19a7a81e0b0ab9d00ecb6679e80 2f4f2eb01f00478d4adf21c09573bd06cb8a788fadb5788489136fca08762a33cdd1c0a1e5a518d3 1df2f9a6ce25b14d9dcb239e3abd4f0e75fa20db0cd33d75bae0bb5f9c7397bcb37d4d965ad84c52 c5b72bc857e8729899c398d78686e9137bda5663ead0904f417e2441e3062fbcdb4343c8f5718f2f 90cc86a322e9c86eaafa8d65afdb0fb383e23c233c1f55a61935b23987c1615aa94a13ea2cd802a5 411f8beaa7e36fceed748e2f9fd3f968dc9cc260999942776731850ec03a4323fe0709bd3f391b02 55b4d505d82bde64d213edc65266e6164a63b3b5d068c9a73b55119f230312466940f185f8a0fc74 5a0ad82c8757036617237ba6d59c5c6845993ee96e7796a3ce17bf4c69deb5450d88d2703a0f1bf8 1456788e4cbb91412eae9590f840f495709f9f2ae1a63b92704fd49c70d77198e115ffe07b86fdff 7e394833c78936635a5f9a50a4bad3c750b6662a441bb97a0b55e4cdba213e67b5b2904b734d8ef6 4763b6ae0802d3f22c83ee06be47e9e575440d2ec0716a0e4bb729fce8bdc9d41a7d37bd489b32ea c467e374098f3b20c464d8a008f0c1289395b5f52653ac9b4c8a9f6d711cf91c3e8ee6b5d9381265 7f1c91fefa1fc4289033c3d614337c7c11694e0d1daaf3a750a67f2e772c7e50e9b6779afcf2b92b b1db8958a2934d085197fb9b9a3eca4d857c0d208bc44d624902c86c4b647efb89c8b387c724a8bd 81c9745f2b8dd708d41c332f6138ae3afe78148f5efc48a84fad51639f7c3b0d3fc80c80cbdde200 972b3b099781be8b4b5736fa079b8275b456c37e6bee069ead2f947163668cd123a58afe0a11edb6 d366f98958a47b1a084e91175827811d8c12f9f427336542314b75bcbedeec710d7aaf4671da8c47 828d9ef063957ae0723ccfe11d212c63e7e6b38dcd5208456f384ba173209ea1d00e0f91a7983c90 455b6d23d61be610ebbc349167b799fda1520e7ef095edbeed9f5d885111349ddac333cd797a52b5 681cd2f2253451213fb2ba4cfba0554827f27213ea3804463b5e6ae3c7608be3ddf287c534bda5a2 b7076ca1f34059a130e56e91b47c3c211877fe46d2f06758cfc1ee635c85c7d6bc07adcac711446e fb2254e41d671835dbc9903e9e2a433a5a4d87b4d3d587f44cf43268cb5fac863372e18678d15ea8 657b6178bea3aa8792428b2f9dc7fe94a136fad4bc3caf4f42dfcce3ca3e4a51f836cbc19fe4d481 27a32a06ad96430a9a8aaa3c8c127b3e64d4ad3bac8ec0f520ceb50e037e49defb87969eeb4bc9be d66fcf2a83de2cc74d7bbde559eb5ea7ec7716e81a9d66ae3bdcbcf0aefe3195ae7eb9db5d7dfbf1 7ee1e7c8b16bef89a2673ec69eab978e9626df721c2b142c7ecc74abd480f440a539da2fd422f2aa f1e9703df12ffd836bbc7aa7fbf37b1d62af4f7586dd5b999d748db3cf759e83b5d259e401ab8306 ed65fb4d4db66db7635fda396dff692d27bd7a8bcceb7033a4ee6c93ee2a4eb37ced5c1b9b79aed3 2cef0e5cb3bceacd9b6513b77ff10d0a2b2abdb2ae747135cf21a6af1e795617dd68ccb3fbf18498 a64f029e5459ac850d1fc3224431edb4771ea3e7f63bd676ad22269e9b157e0b36b8c7a7dea887f0 a0bea7c5515dea864cbd75b9c93595287f63cf5aaf305a552f47e7501d76af69e5018caa15d38f91 f2ab854b65fb555c9771e75228e35a382edb5e27fb378fd07eb138116464a25e25d236bb892f9b65 c8e0ab85224f3d7ad7f1643d0dbfe73d31c3b36b50d832727d858a9fed4fef98342b97c1aea63a66 547ea98db04434f37131c842d662b932030bac1ed40ab5e1ab9fdf1590715e08552edf542e06a800 dd00ecf8f373ee2cd68b390d0ee1dca0447d5d0e60cebe2260cead0100ce4f06194cfc1716148ab1 1e9ea250d187812d947a8a421b6d9c22caf9028cddb0b4051776d762bf4b5d5fed8f53bb36b697f1 a17a65bd4d79944dfb851a077ddf0dd82dbe17c0bd565801f07c7404e0659002f0aa5004e03ddb06 e0fb150790fc840790e17d0120101d0308997b0388b8ec008834a200c4aa3a19a213802c26c50c76 f317c6f251ffa6c4ab2a5ef5c53cf93698d978cbff91ccfb49b70b17ebd54abf7bece4da13b6736f 343c3aa942fd282e11d746946f0d581fb8e3371b40805bf6987ec10390111a65936b903dd0ed0000 b2995501e493ff6e130168459a02285ed4005475430075a03b80ae6f35003d1b2300bdb45500fdec 430003c6b70cf3cf2f66fba5194900472c58996ec9e4b27022f1f3521bc2e48669f42e611f6ce759 f4f9b347c08be70abadbee0b9b677e9dbb4c511f40aa6f1740d8ce12405eb3358022eb1840a5ebfe 2facf61540030b04d067b505604d130330b2ce03d83cb401ec2e24009e2fe701bcb11e023834e5fe a4f65e2c0087b1750671ff8bbffaee08a6ff034dce705ba4e978b4c9105cd374d2cc6798b5325c91 3425da64065e787ee8a9f21c6fa7fa63d5987e2b5c3e8af2c4b9d367dcbf6d074870ab9bc8e6ba47 a687abf8922e9764a4a7173577289e2fe4ab79d64b53fc74676dee04efb6b324153f76822517e707 c78fbab57ff11febeac48f0e9db5194431c36e9e59d90f3358a7ccc0029081ad67580f522c38e14f affc983e56f513fb284a27f14e9f4eea8dbb05da750ffbf36bcbf6ed8b0a38dea5ebafc3b35e88e3 f3207c7db7224e66b59426a9001513ec68f58f5ef734398206c01dc8675bdb47585edb57dc83badb 8e03e5177fadc33f789aceba44fa2a9dd5f4d5819c0cce364ddf8fac4b2f502ec5f3e8778fe0397e 43ad4771df1fdc99368addb6b31171abdf46f4558247fc25b151e9d20320f5ac139071ba87d02249 bdb293d83c1a1e3f0762779c7484cb61a52d817d643e9b3b7ef486626945305bf53553b7bdb5fc95 5437d799a06e8c3ea1fc224d5ff434c36094be564f317df3be93be8dd2267d0b9373fa26bccfd3bf cc8a8fd2bb59be09f0ad7e95f167ef32236ef0d9a08ff8e9b13d1227b471a493d7e1f9756e9351e7 261f7deda61d8961ce3a0456ceddaf9d77b08b97cfedaed9ee5ce34e1f03b2f574d9de40a7da648d f505397297d99407b6e479b84c2823246de2bf90be4c97ca4c9c13e9fbe3fc6c457cd461907e6cfb 98fd9485d7455a7addf6e5327039a7cbfc1926b4ea09a3b5760236e8feb1d82191436500e37bcec4 c85d8bad53b1b2ab73db4bab226eb514d636771cb336704cfbeb54586cd7d8f3f873a19387178108 8cb956582a9c274115efc82b3e6617cb43b36f2ea567c75cb617e5f92fd2f7e04ca5ef4b93493f49 69f102603d7a01e43d799604f3726b4faf97b369a3d7c4b3decf43b48abe4916bbfd4d2dc63d6652 dd0e8d697b6315a4e1fa454be81abfe893c81faad368f2b0b930a056724895f77ab0d6ee76c00eeb e12a0ec6c755b3aca54b594b6bfe392f8ebc1bba133d685b5bb84fbeb07017c3dc577e70d1fbcdfa 45fadedc26e9273484bf726feeb2490bc5edeed6a9afd7a757780b8fa50d1cc5a778bfdb3c2bd175 ed3ca277b42cc785301c1c6a61d93cb503aef418ac76ec03598983ea77ff6679344bd3a5321df2fe 798d28bec63273ef76337d6f3e4f62372dd5efee62ad979dcfa08e39a02466d16c7b67da4567672f a2d172f10fd24fe73dfa2ba0f675fb959b5dbe1b92f76670754f6f62ae1dd87534dbcea19eb51e4f da6118bedb8780dbf7ee2bf185024bb58616fd8b38aaf98384687b777b36f4106086ba69ec4f5c5b 8869e7733c088e67e735270f346c7b15f36b7b2a6cbf436011a5edc2a28a7b90c5df5a9c798069c3 948b73c76c47a2fd0fd2cf93a55eb95a4f7de5d6abc5a35e79e967ac8a4d0f9b691bddc27acc442b 2ed6039e75dce5a9e86f7cfd1a7c0b9b7956217eba2ffa9c73f1edade4f850aee1e417b98e1d60b5 a14d55206cb1e6a1c9821d0aeca2fa70144b4083b9d5ac940253d62789d919c6b9f9b930edcffbf4 9e368c6b4b372063ece84f6860ffe0ab0efedf2f2fc038099995b6f9acc08891acf2777c97a8a7de d6ecf6dbd154eb4f5607b62ff9d7a86f792801056eae8bee9dc210bdd854c0be169bb2082e6a9c52 b676f545c36a9acbae794476df4c48537eddf0f999cb4de75abdc9cffb775637e688e11a7019d8eb cfcdf4a563835353fbe4754a1b53c94c032b1567b6dc7cec7ff0a737c1f6dd3963c93c0b95b9d568 f35accbeca6d1420bbc6ea102d11df78ee58d71dee34a7b0d9b976f97e5b2feae55c62b56aef9ba9 48b9d7fc7c2a8273cd19548c7b6ed030e6cb71d780450dd1178936d23135a4b5cf28a768e35d7fa1 81a2b199ad90fcb7d0cc8c7ccd6a6ac4b50995a9eb8a5ad95f1c652b6ded7f908d4cc978d24bde4c a60649c5d06888ad0b3cd50f849d58f36fc2a0ebba1e37b5c337272fea1267592d671ecc2fa4f873 d86f902c4e86d95b3ef4d4083f3af68cf2da27bc57358f015b1a1817fbb395314067538826d5289c 0b2a439fe76ab50785caf6ea5c9406d92ccb87628ccbd27a28c96d565f64306de95493163ff80a5d dff6953b657ef4e08c8a07016b519b71318f85f54ea7bfd4ec71ddfd7492861da1e06851bfd77953 65ebfa7c08b75c03c1bb918e13ad9de61db18b96ef628fd9eac203b3d2902fa8ebbc5d53ab94f74d b250e2f27ea8f097e25869e8302bcb797126b70fb7a57452d0449a61e782d4070c4cbc2e014134c8 899581b1b3280b5ffce205221ff78c934f7e37bb5fa6eb62f78aad12ad33f452a3f50d3c9cd558af 2c7836424c354918e34124aaee54f696e60fefab59805e36b3929b1e55164c2f4a3cfd3c15a1d3ce c907ad5d94e5015e973ba0da934eab152acda40f255ecf2d59345ccd1121be70109e0dedbbe92558 4708115025e005546d9bfcbbd75efc83174815bd53de3e0bdbe7da26c32d7fc097faaa3074bd63a5 615711a4689ee0e9c078f812a98fba92a8e51fbc3e2b977557add5f5f0db698264c6f2e1642772a7 6f5fa5d32d4aa53e7902c55b09a888c6a9d716a1ecfd0bcf9147088b5c2a0ae87e9c3ddf3ceeb365 bfffe1479f02cce7bc19c72d27d739b724f20b6e397c59bff8fa695b92b78eeb381436ee4d2283fd 231ef9563b0739d377dab00ea37c7e6e30d58eee840d5c2b4c869cba29f7546537c017f211c19772 c7996ea4fe12d9893792398b50c4dc8527a3be85456f5d14d0ebe3abaaf3ef657bc0bb223fe6c7ad 88e7960e6872e488db70c5dcfbcd86fb70c08601ca66581b2c4d1d165f58bfb85e02473deca0a5b8 5e91fc747542fd91e7d86fd866b97bd39c093fc7150c545fd734e27c45d4cdf4412962ff25c99df4 aa4bda0870c439f15a09cf08d8085815d8f3ae009cf831d47bf259340572cbd0aa72a472ea72c55e 156323806158dadf1a6c4584d7ccb6b549996d5bfb4e360c77ae310cb791b50cc1e21f5c108d5276 e7ed988fa284a196d78d3376097f032d9ad2aa3187141fd473165b9a95efc64011673e219d538313 ef557f26a44ddf123065fd2d6fc8bb9738e2c1611c73ab0270e68a347067a34ae3c3d217aacc563c a7cd6c333f9ce19b3d8aa91f758dde2b9f909670ed414b23ac4db76e678a6edd4733bab597ad7f70 1e81c4d71d8c61abc285fcb537f59f036be2d00517b23a2fafa9bfbd1938a3b932a888eb7e47bac0 10269ad5112dd8102bf31ec61a3ce8c90e37cd1b011bc9ce96653a71c26cb5c38de127c09b69e43b 457a1f10df21404bb20fd1edce8ba492cf44a5546fbba47a047c9d5e0bf7faf45a74c9e9e555fede 444b58ffe05468f7d5cd679d72814cb429efbd3208bbde55a0f93dc737b46530caa9753178fde4aa 1bc7ba8854ce43fef378137cbe52e6b952a33663190b5a30db37ea33bccbaee90328ec6969ef5fe9 b614a7d4a9fdce53eabb5fa77aae30985eb90331351a0d693a3ce81ef958b42ea4856d6ba4d5fb39 85475ad59b4c5ab5be455ab9e6fc17c70ddb9cad57b73db7ea2faab407b667e4a2a5d3908125bdfa aca2950159c5d9a788d06481f7f3f31e373dbb38cb823b8689a74f85699ccb262d0f9adff451ea64 76426a868c76d3eb9a3a4d0d76f69842b530473e6eef2a6999fd1e8922024ebc4b57817036b84d8c b8fd91c8359822916bc2632257588a04f07ccfffc13e693e67119b7d19bf7e1a5c29314e34a24853 a75a433d0f94ea4abb157fc42c8a78f004787bb36bb7d564e2fd18a60f2f654a9d6b7381ca3c776d 6a249bc514b2df2b7291cb6d4874593b106f12bd116e4bfe1039755e9c2c7bc7efa1a50999eba093 c25266c621f9988fe9121f8fcb9b522ec31d1d973d921f6753a09e6169fc22461a9c1c1c7bb6e07d 26006b375a4d628e68e070c61c0e356918cb59b4a29def2ceb70b7ef483b925199d27a97de744e82 d98a910c1812eb8d65c2f5b339002c68ee6419bac184a477f138dc1e93316d949fe3ccb1ca8f364f a53ee2c23334aa339d292ec6f20c6f89f92843f4c692123fc48eaffb57b9c55b2899fdad43ebbfd8 e4ddaebc1a40aae8ae0280b7d4cf6da2fbe476a01c364e4d708ed487655fda8dee16c1d3741ed7df 2416320d627c31a109f9f026e3884ab871657357465bbdfe55a046fc10f6f0bd8585b8848a7bbcf5 5e5cb06473fb60bd46b18c5e0e5807d56577840eb1a68cdc3fce12317de4812030d0454cd19c2226 799711735ad47e11d5a4a2e2db182339d5d34bfcb914f5114e661b40eacbfd9550e3c92e9da395e5 f846da432f21c0b77a1933e63a3367fd68e187411bc1db264260b312cfa357565250a3e699e8f076 f1110bbe4708f2aa1ce09703df6067a4e66060b7fd2ed2902fd56188682bcc30983dad61a0f9a721 35141a43aa991f0fa9c2841f0690a9fe6275a95ed49f94f84547f40523a77163b555980ec4c59aa8 73159dce53c316fd24f2947dc60f6afc95bbb13e74baa210d32922a8316ac16e6106c339da1a43cb ee8985886b5e198650c91852cf9e332c877c34d8d0de7150dbee1ffd9d502df44548edf68e8b64d2 53f0f1acd705dedbee791916ba6789407a9d55fc1d02bd4e50927fe1dbc7ade6d4e4a966c23d41d0 2ac8602ceb1564c8afe6fd06d396c745d261b8f7b836d6afe84d08f6d0d2bc45c3caa07c1d702b12 ecefdbf37abfe56cfabd64f4c67b2a589b7e95db8b04095dbd4dcdba83936577eeb338e820207068 a72b3c6de325a3dcfa9c4b50cbd374ae053e2a5e7365ed1e2df018f65b6054235aa04df0bf7083cb 76be5036ccdc183f214195d8ee574c11ed47036263b5d9a2200e2e4f42620ae05d44bac3e32dbb1f 34726ed4cd66d565dba9b492367e115f2d7f68175b44fede6a06d467d0a42a2dbc59bad07463adcf a546f5b134ea71985bd69b95febe76b838df19bad619be6bd5d353c6abb3a8a7576ed5e450b9e5a2 7ab53f80d16abfae51bfb0f7f9c4fe53f464dd4565d9808b244fb979843e1bcd2e3936c7f591d49d e4117b893c86557974ec1a38f94d546ef91bdbabef7a57b37a5e9c96d541b57daccc05e45e812103 2c3f9feb6a7911e57ba54fb58196dc2b4395c6c64a29ae0a1fa710555bdb42c5d09e79eef9a9e7eb f67c024ad9ec0e4a60fe028a77a702ee1bd56fada19ff607d6b573f70ddf416c553ed414715c0569 56dc5747535be90cc7fc12aca3c8e9941bb293f8d6bdb58efbd612fd847571d6732bf38f302f81eb a39a6ff46025772a90764e3dba51aea79cbfa757812bde7c02431f0681611cb680e1ae8602c39bc5 01c37bc90420d08b01280f7f32a43d002ab81c0015a92500b54bb70c7ef51726aa83a1b659229e3c 7f7774be72b8b3941907df1ca849d5e13ae8e38094869b6afbd5bd99b5538b5406db7acb992e2b48 37b08b53e7ad83edb82e025067953d860655005268078016c72d0025e80d808b2108c0ed661380a7 3e06c0729b07e0606b03f091fc6ee4676d9a03e074d107e017ca6578b800fc961220fb04b95fe8e1 da0b143d6f58420920255a7fb5c9c9da3c0f3188376bc30d4301dd3b48dd5ac505bdafa93335fc16 a072f3f17cf74d4bc8f5858102c0057806c0846800b0b1300138de6576dc6a2180d49c0b800c800f 8070420d40dc170420179501d06add04507c1303e868f406502ed70150cda700541fce01d4db6f00 d41fdc7ef0231067adaaf6f29e986f1d356656b75922d88e30ecc6806da8520c0addfbd87a36b340 2da95e47e6bae47bd72578846f993910ad03b0bfb4fe5b8cfe117c9d510020272102d092b3fd790c 5aba02a8ec140134ac66163ded3180c1980a60662e04b0bdff00b007d1c8f0180178415500bc057a 19c8f817bff26986e2380327fc6aa73f92ead839ff8f7c1a0f9f5ed9411fabba3579142587bed327 8bbb5766817cdbf663edc6dd4ee6750fc78bab985efd6bcb7e85976454dd5dbacbc6e97c2127e979 10c95f15356825a958fa564b48ecd6843b7a3d5e3b8eafba75040dde3caca0c9fc17995995499ae2 29f657431d8dd90c4b2d33b1b6cc303b6678bc32134795e778536b64d6e5bab76def3cbcd58df337 25fe2a3ecfe36b6bf1a62e095ee02e5d3f279e2f44593deb85a671bad335eb6456203f4979344ab0 0375387a9dd9fd086a9bfca168e53abbedb88cedeaab3e154b2554885beb0ebf4dd832f78b9f4efb 63e576f8f7a7d180ca30ff4ac06f27b392d86688ef4f909dbdef743206330307e54b8255eae7cb04 6c9f0701d8ff6eabc3277853c49294ab4f126c5f991e3f528b3d8e4fadef227d58f5bbb343713e30 775bbceec67ba2bb8e5b2179da76b7abfc466fa4adf55da9406bf8329844a95e1f47f6101cfde23f 269a9d34553f689a9e44264dd7616667b61aa73658fb6eaf3ec878b3b96dbb8be3b56571d7f36055 7f9ce075e59360bb7cfee8b5f2e523a8e6eb87a27e6deee9c7a9b3e3de412f730efd61dc5af9e856 6dc7c4e6323b309bc1eda5ac4da46a45a9338922bc963c436f35fccadddf43b38360fabd97aac47a f86addb7ff0b9981512f4d93a89dbe560b327d0fd159fa6e08568abf21e7beeeb9ee55dab6fcb3be 4ec2e4d55aed8e79687adcaf2bf875c73f7acf587a37bea7beb7a7710ddcf6f69df2c668376beb87 d369adad11da8f5e3b02891c519e4440eab021c19c66616184f801b5b38f2bf655ca2fc53ed3f58f bbfdc4efc209e59dd368ea69b64dfe224d53aff153cc04a4e1f4bdad31e9a7e2e819aece9d838ff3 8bd6adcf9337ab9a87b0d77377cdcd61b5d5b28fbd81fb41b25e2c0ff7c84d0eef08ec1dc190f4cf a5b048bceb019de4da4145ad0d563cd04157757ff45da4975253e096ad63e638a9d8cbf32efe70ef 0d9beb9c8b30edae635fa5b183fb116bfb0595fe07e98b8c6ae9fb7cc4d38f42b1e9270c940753ae 299741706113b00fd0bb960c7d6ff9dddc5790b6c6479013ae926610ac81fe6e1537fba75563317a 2ca5cf288be8b74ac19f35c4b2df7bb84dcf40171d6ff8de40ee63f31ebb68a3403bafc7487146e8 ccb6fdcd35ce5c8fc96b4105d78eb5e90ebfc5b32cf6227156cde339733799b0bf48df1c37483f8b 3ef1029a16fb2cf17de6daebee91e38a11ba710f590ed60e1c922145b8e22afe2ce74bf9b0f4fcd9 7bb3f66edcf9e041fbdbc55db46f5fd9ce793bef8fe38e2b0527b7af57eca53468da647bdc5b848e 882ee8914b5a9bdd51b038a9699abb97bc36c54dfe394f064e7bde355f23e3424d04432f0f7963b0 6970bf483f9d27fe0220e7db698ff5a4363d01eb57731ffbb9e2f66ad681750eeeb603aed9c2968a 3e60bd7bb9abba8b7bd772bc12bc74b2f87a6b9337eeb88860eeb2608a8bd4da323660f1d555c1aa 5f2f355382ee6db35dac43f324c2be9d36eff514ceb85c8f8631249b81fe68f9371d99410dcdc939 9886af004e03c8173ff38b27ee177f1469600748a777431f1c6a69d0daa46016db2c8de52ba84f56 35ffa26d21373d6dbf07989d89b817eda97d37168c9fba167f4c03f3d00363b36d80c7f989e8dee6 bdb09b1a571a010ca32b95f5872ed5756b687775347fc73447a9d3daa84bab33dfdbf83382e89f66 85e6bba6523685aa657cfded346503c482c2fa3eff8b174004dc3d3e94a9a45896a0ed3d449bd16a 4d168346854bfdcbb90dba4e69d2b50372822f6a00c35acda6aa9a9dae6acefb13dd338cc08ef467 d98e756b137d6b776befc1fea639f7fb4b1b597970b62c372a336283b4d470c0432a755f116a3928 4a0ad7a11da576be1de59db728cbad420e928e2141490a2df0ffe005dc4af2f9a9b6f0fdde1c7ee5 eeb53f2c340381ec96fcabd54d5d878c5e76702bb5ac5daf8464c17c693aef5fdaa2feb4aa9a8e95 87b6e672c852cbedf1f58c6ce3bb59e1259d557a24ddd54ace7c2b9bdda1a8d45ba79abc7b35fab2 e84cbfaba79464df1949b9d71752d79cefc40b05e7c5c1261c0877be36cd400882d98078c1cc77b8 1fbc72b986721a1bc5516c008f61445985f6f2cc54ca1eea565e4e81911ed64e71aaf3f3c1e91b73 77fd1d693a26c5ac369e654130b93c186a54bc3a6a85b92e152e7eac95fabc7a90c5b47a965b76ef 21250c9b93bab15c162fc63afb883080085920480ba61a1902d21f6cf857eefce1ed95d2e7f1e98d e471aaf37369d0e79de37ef1a8f11497149fc5c9e6f55c0f83ddecdcf6e73e587196cbcf7bc111ed 8bd9e5474503de101ded63b2e86cc50a53956951bcb2550555defbb229b70bb22325a11348bdca4f e949f1b25d1fc4e1f07011ee99d7209861bf202034dbe05fdd35c4e37e7eca030433e3fce627e226 89f2e4f2b36e274348b041ffc9b341edccfde2dabfb9e4212edf27eb65d7875649edf62dffe16187 4bc5a693e46d269dc7d93091d35bfbdccb8dd9942d0fd52a5c1b2b0dbcc9c86da12f4b3da5a989fa a5690a8f61c715cc071af02f14ddf04e594e787ce3dc7940bbe5387fd2a971449eed73f9c3e1bb10 b081d253580adb066c19183f98f5126c332cc98d33ec78868543ee1717585c52bbd3ca1e47a1e7c0 cbcbfcdc73c17754593480d57bae8fe3b38e8fe4fb6c1a85152586c2ae2c57365f074a9a3576a468 642bbcf0706f0aff5a1d75de993e173cb07e7a9ccf95238e18a0072e7f67af6cb0f23f6cb97d2e33 eb73adc3b0ae3c626ae38744ef1abc4f0b47f09a2169d14d151ed14d466333ccbf0ed44ffb833396 ba746c9ca471c81d64d87f148f7da734b62be6a9a9bf0d646a9cb4bc085dd46a4ce66599165b527f a840c2e3cd8df837c851fc68aa089cbf5614ae50d30c9612b5055b86c36f322cb3293eb70cbbae9d 98da8c48e95d7f5ea0c5dcb34d37f7239c3a4a8140299db64375defb33d5f978cde9d900b10c2433 3d0b24fb8bd38459305bf4868c02091510efc584fd453c65bea559e64677f2d281197b5259ec7c90 e545ee23de1ac5aa60bd2b5ddead77516ed9ee125c6106b32c75c32466038f35a616d116bd63349f 167b9b0dddbcde8ed471d97c500a498254b71566e69c0ac85473e46fa74d0763c026efa09e90f7fc b446ce830425e78b169d61c09273a5c5fc20291922b3018b8dd14a7509c4f505a5671dd678d9b0f4 e16b16ccfb89d29ccc63a9ef2e9fc202d916f9712d697185f4f29dd3581aff8c198e48297a971405 5a54bb33ea78edce29c518bb5497d0a2e9a5b0dc4fb5e3f3361da8dd1c79c7953a69024788847728 43a4626c11767b7220b073b542601b13cef0a108cc03991f7cf33abfed213ed2ecba98fddfa5511f a10ea5605d73b6ab96f451507da935fa7d94b4ce782b64135fc2833526c786a95463b655bb4fefa1 1546b7165b92523e0937bdf0a932d59b45633a38761dd2ec0ebe9bc524fce16222f51667c2e66f6f 026f762b93cf733e98788bf77432c1056392cfe5e30c8ff204bc335086fd7402ee2fcc2f765a03a4 c36d9c9ff8968962367be976e7d0f355d20aa7d757e5909561781052b0b2e141e6b2639962e5c5d4 afbd32dd2e4c3a54afcbc0535d97c6e4236f3324427922f12aef35c2dedc2d02a8a7dec47b743793 894526933ce5a6e3a09c96c6d32ddc1d97f49018ada1fe37ed7ac43ce3cda86a1b855175561d8caa 2c331955472af58bed621d51819c7f11dee8d4c7ade32aede8efcfbaa872cb2015e7676ecf83f67e cd560b7c444b9e77a37a720c4e875ba0412207e0bbe34138681b230077484efcf1949be4f792320e 247b3ea6da5b775c3addc3113b2eee473510bbe3f14a0771414a5b78b3438eb0c3f92863b2ce8658 07ea0058a7b9ee629d62759401a7b0763a9cfee0eb75823b9b5e5edf31e1167365dcd44efb8eb66a 5845a5951829ffd9c37bb60aa911ddf6aaabe9f00dc4c41b9cbc885c5b2e4d0827688fc3dc011a97 c9eb68c49ef2f4a8366b8bb8706bab78738e9bd891d4bfb300a614a32d7a662e6754abd5df68ffa6 d690dbfc0e237384e611e89df3e1a71b3ce1a74eb4e027bfc6e0e7e441fe22aada12e32fc6f1d4e6 9e37dc48977a7b562db345498fa9949b9e5a5f95836e7ff0887c96e225911367c138640e97d16659 cee1bba859c55bbd5117530c0ac6ba843541b5d0a1d1011d8bc87c7bd210580016700a2121bc784a 07e883790fc8038022348ef9fe70d54ca82199d0df2dbc21994d6743d239d786a40641190ce21741 4798b01e28ad694be53554cfe3484b9141a828bcc27ecad6faad643acff7d644eee6ac469c9f7990 095658a3fa309bdaee81062248d9fd1604846dedd8853ef7330479566e044d288819ae36a8389c72 823e2cd51d67c0487134a8a2d5537ffb16807e63bc6ef40ef91ede9382fdacd7a6c5b8d7c6b152af dd5bf67aedea13ebb56bb9d10f96308df03f29f14ec0ceadce189db150b5255d5fe5224f6c6b2ffa 386f9d894fa7bd1d576c7685f53a1b07b65fb00d4d6e4a3ca48aee75b0562fc0a07afd94fbf172d0 ee378bf0a0778838bc2733f369f7740d85eeccb87dbf37dd3edc743b863dda7420dcbfb5ad5da1d8 46c5d9b0f56e57f99673da072d67e3e55a8e5769b51c9d843370e80fbc7cbe2a2c3a458ad1fd7d11 51e4f7b52562c0bbc472d2ed450da2fa75b27287dfe30a78db2042f8b59fbbc33294cc7b4a9eb7ba 5a67b5eedcdcfba90337eaaff642eee5db5867526dbddf66b7351e7b700b049349731914b966b103 6b8df0ac798dca60b7ab6ff3d0ab5ea7dc466d5fe94f6ae2f6f11d02b516b1bad5c4395ea989a2dd cbb01afcc06165e44faaf96c63df517930be7678f2782cd3c93a0548dc2fde47993f7f44a105b119 862cedf58e9fc86adbb5abda2cc6ecac512d047e9da74f5f05aadee855cf35c9e8a5b5364480d553 c1a9577b74d0ad5c2b0fb432341a54f901f16a1959acdc9253ad1c4a786ce68a3edc1a1402dc150a d31b1115827af55d9cb87e3dc3baf98385a221babe7c1fe99ffb3de30b2e7c5e4197e52fc7dad412 13705256c014d3d8c619062750dc976072d57e9519ab5952fd59ad7d3ef365cbc198a29f76e54258 251785b220aef29ba61be76b8be305dce18537d8f4bba5dc9110bf454e739dd04581b35ae680fe95 3281fe2d8b8dfa3727070c4006fade972467b86d80fe937d6698bd7f304fa387a65507674e1e2ea2 115fce0503ba6f850d82c4f7453c197f3e08502cdd060d2db7cb1ed3b188e2aa596e57adea1983d4 d2dbe1b8fc666b12b9ae561a0383559bcda0ebc0607ff780c1a1bb01068f79368e1f29000c0b641d 1836cf08306c8d5860085f2d6088f03b6088567319b60360482b5f1f1a18324d3fc3fa92e195fec0 206a7953edb4778298e35704db6ac5c8147b3f7ba3fd33aa204e4d7f0f04943a75ac78bc6e9657bc 57d514d9288d1d5702f7fc8a0274774800430d2181a1cbd03f8fd96c156078e93ac0f0a3ad01a872 3b01506ff40120e65807a0190a03d0faca01d059b201e8533f0270655f00e09e020370bf2902f028 f601788c2400ccf1d71f685ce1b590e1d159e12b41c2fc5ceee83bd838eec32d14ed940a835d3ebe 7716f872dfd8eced557510ae17c595739ee54e8d7dd62d6789062074cb03907c1701e8585700b839 d60098706d0016de21002f853300df9e0080d4d906808c5f5fe7164074950790b8eb66d8240072e3 8a00722fc3000aae45006de06e866897e192fc40d11eb22914af9848eba72a31594bbb01fa6c48d5 612d6ebf3b8be87d6af0e0655d311757b7c0dc76da8f0edd0433b3ac8ff4578cfea3436f167f35df 51cdff1fcdd7c91e582edd011475ca00ba180e01f47961000cd62c0093a0038099cf3c8059b30180 2d1a2c80859ef9536222aa44195a9b1ffcd577ff08933f9a24c66408f57fe5533bdf2f3fbd32d47a aceafdfea32841c89d3ea1e37b16204f6fdc8d606ff539255cf7b0a0fc48aae94cbfb66ccbbaa880 e15cba7e109cf5c27a7bbad387e4046f5fcfffa4ca3a83a3d77d9047d0a88a8715841b87e2a2a1ff c997a5df8fd90f32ebee68866890e182642676a77f527bbff2e938e766a0e2145b59b7c7aa26bcef 514b2cdcb63dae7aab1b4afb2a3e95fe25c114f8d2f544ec7c996893f32050a6a73ba53127b3ec8a 49ca2d9404db2ff4a3d776174770162e0fe4edfcdd29dcd3e9f3b2ab2febf9582ad2ed6dc22cd06d 37de511bbde9fec5e0a84d7ff01f2bf9c65f21f547a0fea3a09fbff269337c1485e5e6c65db4e355 7cccef97aecb7dddc1b30eaaf9d37daa574ef05aad27764d6d1f3fa2d43b8e13667058f5a6f0a168 4cb03dfdc4c7bbba57256229df66b76ab9216d2e1c32df0cf6fc726db6a363946a8d5c84dd9966e8 21fa301cbf4c24041de97bd3d24ffb83cc40a99901af64f07aff3304965a6665ddb8b64cdd3ceb39 c63b99457e9d60f1647f1c1ff1f381bcf46e7bfa517deeeacef5154bb933b04dc81db8ed469b6f45 e58d5e0d2b9b417c6caecde6b11ba5ca198ab0cb7b1c7a509909c1053a0bc8cfcc5b4544feb2aa84 686ec957d8eab2be9db5fc3dcf367f9119362c6540ca69ca98ed345553e43fd9dd4f904ee8ab782f d12738a498a3d740e54351abe9bbbabd33b709e13a9b0b3d5f6e06db79b4361bda364a65251b5e67 31093f9a760dc777ed19ac10e51d4c4b517115b1dbdaaaf2dd97e0910ae6efb305e35b10506277a6 7702a983d793d66fd728d76aee70d36c3a0fadf65fc8accc03696a4f4ae98bfc74d39744e24fef06 8eae7b271e26a9bcec1e8a270c8adbdd3bbed183845a23da5e88f0fdfd3b0b843e0a2e828003fda0 b407c3d5da6cc5ab5aa97b5cc66bf8b214665cea1f6b26e8cbb77dd53bc349cbebaf41c8bdcdc8a9 0bdd168ab3981696f6fb64dd6db0f62e2f4813ed2f8a483f5b7c5eb5de0fbe92eab7cdbab10da6af f9a591be397198ba7505b9097ab57542bc457117f7f4fce67ae19bd1db5a612139585141a5bd9596 fbdc4a5bb6c4fdc25792c4f72ef83df206fe65ebdec562e29aadfad549d5faf7b88263e378cefef8 78d1f604bd6983476fb058d920b12855c7b2c5084bcfaac2c4cd6c30cbd25cbae57af3f6bc30344e 64eebf90bec47d3d7dbb59e8b45c91c3335cab7e8e8543e1b213921f1f7afdfc543e21f96ed457fc b43df44fb9f6d8bbb610d67de013c5790913c3c18f53dbf6317e694f3efa7a11f0e66e413556a745 e9b0bd592c9abcacea07c89bf17658379b8d496f2e5be668dea141d1986dc9ef3969a36f142e3a14 7225ed49ef7adaa2fb4634f47286676f7d03fd207d6bca30fd60b9d1193e59a543e6fc7db67a229e a2cf5bdf06d1cb7c2c65785dfe6a8f1de7dd5da34e6e729ada05f9212ccaee6bf67d371cf89a5bb5 a0ea9a62a7b1329be7e166ae0c7abb79179c9e8df34a7a1883b605e8b7535cd6e131d0d15210c134 4c32b9d907ad2c6663573ba8ab46bda0928f794f2d5a1b54895017f9c54f4a7ce789dfa466d44df2 4ba3babd0dd04fb46c6297a036c537feb9583ab94f9fc83bb9cfa8b1081f0264711b616c8a7b819e 2788251997d16c66e8b5c0d2ef62e0ea66eb1068a97ddd6a36fe3acc3e4be09b0f3df3c4de6b3669 510575651b0d75ca3c6025daf5688599afe6725ceac67236ad80d26130e848d25d44ffa02d61f00f 7ef4fdb3d59874f6cd18aaaedd573717d4f5cad5d76eedd85d6cfd9f4bb85718f0b1b656ae6a4a5c a13bef2165d418b24d5247c41aabd9695ffe16b3d7663e30b2667962e4aa41530ed592226f9535e6 270af3393de458c8e564a1d9af4a8785d09764fa31154ff17026ce8ce45b5f40ecc32a205cd3b425 18ce0011a051ebbff088cc0596f8ef5e773b7fdf2a6174abe7fd6b29b9ba38fd88ed290a8516bf92 eff313bb2c1ad917aea9bde2e550038e4b7c4660474aa5269baf66a3ac839dacb0d44d97e3f37b21 0b7ad9970e8f7228c901b4933a147b116767e325f6b56349b84d3a5d013ac863feb9c8abfc02d343 1e03066feeedaf9a9c4b7c606e5c48915f5cd563f24d4b3856c06a6f0380bb6ad038a6790fad4537 3be00fbbccd52bace6b351f9a43f8b30a08d7cac3a2342b2ab96751251d8073791c532474b478ee3 25a53e57c5b3a4e9a2962d9cc2cdd92c85f9e8b611e07aedabaaf30ba9ffe03144c9739fd2adcd8d b926c681836c465d3e7a4b960c3e2fb6483375b6886d2126fc6c915f5ca06e02eda512d08f8269b6 02cfb6dbbcb32ceab7450d35e2f929dd2cf527117e878036fa3c533594d3a2c28d3f4d59948b0349 d140543cdf2b8438402ab4305f77453ead75b35eda61731e9b8b2ee7a64ac08da3cd9e0399e7955d fd3fb6ce6b615565dbd6cf8239a02892738e12540445316731ebfb6f74cc79c65afbec9b8f0bf5a7 7ec4aade5bb5de41889c569afa0d75215e49556d69a65a3dbd6265edc7df6d22c5c01ab50c3d2c43 4229e03d207f3853eb94dcf4fa2378ae79637082dfa342a474d46b70f0bbdbfea0334d7c0ee6e6ee 7cef5f1c508f72f6918c6b56ca266df326c698491c77ac415f12e12777f78f9ace3d8e969e0f9f9e 26d5f28156dac01375d15756aa2a4ccf6aadf8782beb2357578c6845c8fb5c5d93ede92892ed9d7b 935bd2ab2ab76409954e1583948e6f89f8e1942feca83579e87f178299950b1be3f75bcd87fa44ba 0dfc44d8f668c14fbc125f9d3ae0183eda2e49bc2cacce944c12e21b06d361603d9e18a45e28da9c 2629b6a42ed6b6a6aa4660ab356cd7578ce131521a4a752eef61f1eb4c91ed347cc8ad49be229d4c 1d95bce655913a4733142f23ec225ebc4d45eca3b58ed80731e22f8ea5ee9a5e011fad9338d6a039 ce734c7e08cd5bd73e7142b65e5245278ed14a22dbb58edf34cac298e7cd7841404e9f0040559b15 01482b2b054455d76552d9600d4e690c1b92bca74843ee566c4f6eada2a174f2d344ca26a99d8414 f4bb78d9ef4a62df813b224ecf65e101e081309cbc4ec2703afc16fe0b147181058acc96aeff87fd 7a1cfd71ef4ffda70f450ba9960b3ad5f2d5ff5c731b57c5aa936e0bb1430b73d599316af64e3aff 895e9adc589654adbd0415139db5e57db2c7e5ae74fffa6ca4d3f2254a9e5ed62404251df1725307 623f8927226e7fb6c2a38d5e85e13b2808d4f8d3e6df8623f2e366b1cfb387e58167174291cfa9b3 369f632ef85f6c4f244f2d2a77019d048cfe355c86eb25901b60faebea1784f7d66990696cf54bd8 d0180985b15ef87476ea729f45fb9bb79c930f79b922b76d0f92bcd31015afc8901407b9bd20e2d3 bb2a84a5972550a7568f7f7bfc881fb3f18ae772b76f791c9fdb31396e6ac74d4ea4608e2bbe573e 3b8fe50d3bef57f2ecdc34a10c4bfc2f3678bd46ce7615048d19016d0ef79d57bef7b2971777a9ac 36b6578fc626d5a90d75a1bb1caadaeef4ddbf51ac4aed22b7778597e49348491cb008283cb62c2c 849649f0ef6397e5c7d144e639666ff279e0e571a2550bb912c4ccd8f9333eb24af479b3554d0599 753da5197da73b0cd8ad2c1950de020ca8e0dfe74631206da219bad80fabb758a07e8506e382926f 041ebeccf993d1f0e298bd7063de9fd2489f164e81aa83ac2f1f96de4cf207c3bd9845a47721bc1c 72fc47ccd5786e5184f8bc8b639c78a1be55df5ca9af89ec02efeb6c753e7198b5720a18a3569e32 e045ddd3bb7ef2a0a12750a18e0b97a05cad6a65d8ce2897f3de948bbe41cac52894721bf81f2c45 e54e4f7bbb3a1e2dfdb431c095e1b739b0274fe5d43ecbe2da602f9d91ba1af707f2b1dcf4c59b98 0b8588c1d77c6cca299784dd3757fa0c8aac6acc41b6d638c38c317c134c83ae73f4ee832b748ba7 4cead4f07a94bbdfc4e4a57d5f91bd0ff3f54091583c291277a18110c362a211e4828b0972547f10 a4e757339c6182d4ce9d1fe66b3e5b97a9569d0c9b5204fe4406671b21a935f0d1b5a674ef917c24 a9be18082b5700aa039f9366abe427779faf3b66c337eff4be0b0374b72d56a8d3d8812824ef23e4 259952645f3a08c4fdf4d088a15f77080a15427c1464ab2b2b5d4e78ae42e5b0c96ad9c6846ce6c4 8a8d5b88158bf32b56784225ac7054beb74076f4e11f66adee8c1d4f6b0f32f011a9ee972ab54fd7 d95452e36397d62a78be8f258c6d0ff849d57759ad9db7e86c861950e7b2b820af887324f1e9f441 3cc4344f50c74f151f23250867afd0b7ea1bcf0d381a134bba88151763139dabe71e5ad9e6c6c86a 206f91fa73f2e86c47cd7ac762676c07caf3bd4ef30e1d3bcd7d9cef3467b77aa739afb67e98e25c 9f8b74c5c7fbe1b156738ddcfd63dd66d76f9ca6278bcf463ea1f78930122b015769e81edd9eae2d 92a8b95d7c3c8f47781ebead3071523da18b22fc4095399f43d6b05141f434823a3b2c463ad6fd48 77a0302fc16e15b660786d05ed5e633a6ba387ca77166805b05e6811971c063dfb230b7a0e7a1be8 6957df2d22b1ab2d22081a3fc4e360280edd0281f989b1af768fcfe5c764c6f354ab6dd2add40fee 095f748b21d37cd33e49b8918d89ef8b8a18d2e6db22bc63fb85083ee5b105dcb1d543fbd2ea5ddb 9817bd5b7764536c91d33b08bda45a071a95490a0234556a4eea49b759187ca286f422d68df268fe 04971c0d81b5e42c83b5d926016b0e7901d5c3a600aaf3cb770bef771c2d3c4f1e906c8ab89bf6b0 6a6397fec728e6e757a57b58ef84f7f232ff49e6147a43fb382fd85dc4b8edf5765fac88d0287e1b cdc9030f9a455a9834661f6fd5a8189323b86aec6fa076c87deb3deb5bb852ae9b1fa1553bf05da2 d69e4dc4ea592938552495e2ca005b1c2af88329961fe1872ad340e89743c4dc971fdafd557e7095 fc0fc31393137a856384386db45b33c7453fa7d54f839b345827df459a2f4df74bda1d3e6202f04a 03b45e95dcf615f17428176f4550d721a606bb1fa98a4ea16e252811830ab1d0e372d4192ccbf465 7528c5f8e15aca871fa058da54ab858529760ad543c8e50df861e7767deadba62d67e3fb13708cd8 32e0b20003b8c87a001c7d6e996170f8614075c7bc57efab883d904c509fa74a5e81b7ce4388f7c9 99d9abb335497187299a655c43d8efb63c885bc82668b4c772b517ddbeee8752fca96179b3eee1b9 d6b3cf031e33d10004383900926b8519ec3980d4d3238080e82b435c039006886708550069b68719 363b0081cc028090180920adb40b206d679661bbfbe1eb7e28e515fed727dc8c59b9a9eeee4a497c 45d69bd3c6f1951ae0931d5e22cf8b0e9ca6a31600d6faa0f1685ad5bea4282541b1997cd31ba100 f2c43b19bc2c6e2f037a06cc05d056106548bf864b00a5882380cad337803a3098212400d46d666f 8d96d9bb46fc0140c740f6be714202e85ae966b8c600ba69af7ef0d6812fdbc15a22f5a528b7e56b c5aa09c54d2f4fa764f020f29fc9b7ac146959e1b2058c8d0834fb7dbf8a2773b358497b7c0e66f7 d9df9dce325c4f3480e5ee0c808198026068ec0118f78900cce01600d69b9f006c527f03d8650c02 781da1322c4d0047f93180f3f95fd75edc585401dce4b2d7cc870de09696bd66cd963f38a791af99 93bbc1aa87c8c3c4181c40ecee10574840241f4897abec5b6cf79680cdcd3ea884c1b95bd0d3b5fc cf98b8060f60635efae9d0ef9efa8f01f98ff0fbe902784807007ed82e0002a2ce00216e73009108 1040dcee2c4062910b9002310748f3f100c841d804c800e10132d97b0039c32619dcf50ff6631b7e 0d9746b59bc8726076496e53879be448dbe49043c54c5bec035e8210521b55e87dddcfefd637ed37 b67f06261fb3cbf280dd7ffcbd3fc1f77288fe7f93efe8e78726cf9d0f40b5370d80522406a016a5 2e403d5733802e6b77806ed71b004d6d7880567837c3629ce1b3f8e15ff9f4e7eb3c123ff93443e8 feab9d6adb0ca7af66f3f848d7fc835be56a7771976bdd16ad227aab7a45f2aa5f8bec151cd4c58b f52cca1728aaebe991695a293ca93be75468f7cee8bc3d3cdd14767222d6daea1835faa7c3a7bb79 1fb8b452df279846ee16d4f0bb47b05d7393eed6c80fbb5b30e9da7f910d3041fe1526ffcaa7bfe6 0de7e1e323be678fbc56d9df4b56fd7aabbacdcf15ec83c5cbf72177a9f32935ce295f6a9f7b05f0 9be2feeb96cd53c7a75e628ff42e2f1c62e893c5b15eded88bd792b3539ef9fed6004ae38d5d0497 eba322a6abd4484a2b74ff81973787609601cc093fbfec5f64a353c19f30f9c7da1bc1ff6140a6a7 8e775b34ab83ab9e96e28b750757293cceefcf6892bf9c88c5f3718c6acfcf216e9ef287bcb32fed c57453dd29f7616dabbf4d70b3e3e4e6064a6478ed96656c956ada371c5ca1dbaeb8248e5d7d1175 66bdf9a77f9fccb9a794ce1266929f49c0ab912cc432f2076af1d5f9211b58a7f42f7eb2f4ef1680 c49bb2e7850b1414b29802b8774fc4fcf2edd779f818fbf13e694f925d3590e79b1d8bacd647b1be 5dc38bca61d5ab15cfcb9bf1be2c89fde3be783ab9f7823e7f72f318fd94e6f961be3e13dfd576b2 e05032a926a23835ca6367b2d37393890d9aa7f868afbecb5aecb6ded5183e5dff03ffc8a74c2dff af2c1dc10f6eae62291c555a276276c60edc6ecc6cd704cc6fa0495e59a5f2cd5c06e0a2bb78daeb ec9a787130e7ae51344b88e05b1e37139ff12c5930d1325181643b5d0bf1616a14d797c94ed93d27 d0fa928fdd66a13e4e9d0e32eec18138baf56eee8878288be819cecf11fd393c333c3ee1273ebc7f f8779cff0cf6cf2dc0d7aefaa95d3cd2cbc37ba75ca4c2dacd9fcacb9b366b2ce8a307cff37d0d9f 95429949948f264ed7bca64ec15936d7d96dd3994067af17bb5c7f1877f2a3f138dd6d6663acb55f 8f6eefc2694472b5affc10453bb610012daf19c66f80090bbad11d4af7fc3c5876f54ba051eb5c50 7bc7c05ffcab4867c1ebf32895aee0a27f3c3e2574be5d63d272d52b6d368bc8dc5f67497ac825b5 eee1bb913f6dacdef0a475cf1131522972e3feb8ad8c715fb34761def44654e20da271ab1f45ec69 328d72a3d32a1473a75d58dcd5ae43a5d5f904ab91560d746d8f0eb6bb863480c8a0df7756f0ae97 a28377af974fbf01540f4dd665ff26c5a51ffe8cf26b403e5f9af8e12090ce7ad32e94872b6cc0f6 e613814a12ed33ba4c1c799a8b516e5b1f3d5b5b38fab00722e2b60f2e4cc8a21296464563a8d62b dd6175dbfed678046b0b1f06e0938d07bb489b0f6c75b8ed1f379373bf839f5ebd4ba95de961aa0c fbc16523f8e494ef7923e8b3f3008f7eb91336a9b9422ea8ba85a95df9e137c4abc979c09153868f 0d7c24beb1c022cb27e359d5acf4a6569c9bc43d0a3f8ea2847a45fc482987654b690e6b91850626 e0d383bd190a83d62154faa7706bf53b9f95d3bbac1f831edeb886fefd509cfa145d5f7aaf0f75f0 58c3bc7b396cfb7da0932b84c59633af6a9c235f00afbb12a24d572f555ff656652a19a49a6dd5c8 ea0fcfd72e0f9d098dcd6ff715e4b10466b75d6232fbe9a4737e0cc6c1cb09a289b5dd8495cefb1e 6c4bf9fca07bc97f6f81be5facb67b5705c27b784ab07e8811a2f79e719a379615cbcbad14d79dfa fec02dde0623679e6ce74eb57cde7657e7e6b50b7262cedee5e74d3b0b1569eb484d1c0b8eb1a599 1abba7d96b362a267a68d5bfa5f2e8bc56fbe1ba551ad5236fb1a5d503ba3e663a98ec27c863948c c6e43c888a22e80ff59abe181ce6feb9efc7e3778fe826659f1eaf9b1e972c3a6ed23a926ec93bb2 8e723d8a4e75fad1ba46e9f35da4bbe0b2ead9bb6fe75ee84ac7d671eaafac4e697a32d353ee6d62 2c5737eef50365043bd6d25fe46bae476ffbae33e37d4907b87b3d8bb44eb51fd214036afb7aad59 5e72ccf935754ef67ef431e4e4d7fea33e180e41fae60ee0ee33ee0518b8f3993671f70a7926ef96 5b52cda9213cd435061262ef4b0669b75483b54eb59e6475cc9e665ef045d7ec3d8e3de3ae3c2363 5883e6faabcfedf591307dea6c29fffddd681357c13481cd195a31379aaab3a4735565a95f542be5 59fd2fcea4f40237e7eea1345f98874f7cbb4a8748ea52b36103b2c241a7b1f67a24d70bbcf8b558 bad2f5963ab5c5f3dd6d5c0bdf2d3cbbf504ea96c7145ae66552464dbc58268da1d3e00c0aee48fa 2be50d9de55547cf1582409bec8f53ad48e737eaec4367c3592f4aca0a0711457b8c55a5be60c7f2 567d5de4ada615652be7d57ff8aa83dfe3cf12bf7aa26169d6a007ef3173ec1cc2da0a990fbc1a1d f6c895ed7a1312f39de5804fbaa66befedb6e0de2c44713f26de1b968de17d04ea6f6ad4d2d9f104 d573fa9ed284dd9ed38af6eb1b72a873aa65ab9571c75756ba3d567474bd52c00278912d59cacb50 3b85a543aac89233a944122cccce124c34f3e2f929d4ffe230cf5da0e5271994a727437f8f04a675 0cf6b7cabc8fe3d0b761869fcbab9e5be9dcbbdda65b185b67a1b6320770e364843cf6d4c73292d3 7367a1aa89a8d050e7810cab95978c29ab519756c07a5f90b7db99265b83972b43121649c7b23197 9cd3f12ca65c03107bf9de57b613d13d240ab7ee2a106e8e771202f89113825abb9601a9ffb0d789 7d6b91bc9ccaa43fa4df51857f1d067ee931f7df4a3e74e7e58edbb58a43d33ac7938149b49733fd 93dfecf47ceb72d54ac8e9bb27ad2a49aea8accbc59a026ac5a66c6d8bb00c594d5c729e382bc10b 330b97d4912df63af740c4729d44b84ddd831058cf8f40b6a426ff7c3d793e1afb7d9ee1f103cf60 930fcfe0b92af7b9bf6b3f7cc3c163ad0fcd979a558d876ff81dd667a7437fa01ee6ded44f434747 caae0dc3a669125dcad5e3bc1c6be2495ba96ace3b2946a97b9377ee1890a1cbb424b9f8b426a6e1 b429f6942b2a62b52cb5be5df06f4620047dcd144861d6e75f5061ca336e67cb034cf4e426b9729d e3b731cbf13bb1c715e4dc2e43f7cd26ef6939c3aafac3067db0cdd9e6aad7c65ceef31a42edfdb1 176ee6735709e26117c24f5ff78319141043ff8c8bb626b59a915acbb5e7f2eed8dec92d46b84a1d 817a8abd399713ee55b12c04a9dfe05fd800e6a3fb9ce099d983e701a7ad731358f638fe331b7385 09b06667a67acf52cc4b8d2d3f7f3d2199e5a8e4324befb06534147f311ae8943284d51f562f1887 9283a8d447a298be06293a3cf8dcc1993946390a2dffe3d90653c9e99a648d356503acfab2fdb84e 25f7f3588bfd66ee24dc9dca37e410c80f0cf0231e2df1c00cad6571bbdee684f600e70ae739cbca dc59612b854a9759eee590d1bab32553a71b577af3e9976973d322e9a6b9eed24dd15fd34d32f7a0 f62fb440ed4f74f987e584af7f6fe8a9ef12f5a8aac6cffe2de81e3c89b6e6dd565d1d9a6443b6b5 e4baca46c970b2dc3ad0b678593b9170bf8f17fceb3ddef1acbebd7093ddf9c515c94f819dbdab55 b6c2814d66057228a3ed2c9ade92e1f716a0cdd7c1a29bab66401d74734e39e0fe4cb5ef6c913c0f 4f18e9d38649fa1d74412297e98d4436cf1c896cc1f20fbf828809d12fd54270d37bf6a211b5776b 3c31b32e0439d42715c4566bc7c1b762456e37ab92d85f7d74819aa1019ff3d10927e6f50dab9495 23b3f2fc3ba32309406fa7bb326d89379086ca259872b42a4ec108c793e7abaf93a8b8ed11b77265 420c96e68120bc770e7fa2dd0e1ede6bdfd3e0e1fe36c3c3b972c9b0cee16198967e98d7df45781c cb5904ef86ccc3e7b7cd7d77df0567e6506d0cd5e5e06ccadd8da289f86a27f263c915b9522e76d8 2a91460ca87ee6b4b5adeea823017fd541ca798a2f3255ed02d9ab0555e266464d2268ee5082387c 183c6220056700c5c13e9bed18e3a1e206cb1fcd279a78b9262a212309955077824a8dfc09950af4 0b15ef76e187c4cd3d3a23c98fbfc57e036cdd7a78e567eedbb4f7333372fc6b281f98b1290e1450 e5393b1059655b63e99d5a53a9e344ed93e9c18d498c4e5659b0bf3ee0af467ac5a343fe8d334eab 88c51fb28ef1b1da4667059740a5d9e2bbb786969d9285a8291120b55eb8e86cf0e2b5d308ad6aa7 11211cbc678110de23bd7da7a13d1f9d06d7caff30c5c4071a9a65a6dc1b05f7bb63ae573bf3612d 126ddedd0f25bf3fb0043a2e69ec4253bfb551740bde302436f438fce52f2d9c2d000126b4cb13ac 70c656a8ccf107b492776f8826f5de48bdb529754c6f5fef34d14207dedf301a6e9392dc3e97d75e 1bd14b93d615b48eadfe3e5f6ce1dd31093d60e1eb17801eb5c2ba8533c6b58577e2cf0f315b9ba3 81372d94bc3932bad9e7d96067706a2f51f5d53214037cea7049f2d49986c548649f5f72d8649723 d08afae691fa14b73b166406f0c1f562180626dfadeff679b2dfb77d11b8b6ae8bc2ab3570a912f4 b8082014f6fb48f3fd58b2cd7154369af99a1434a69be3ba51828817383fde6050d17c1d9c8f8504 9cfbb343a398369e3f442b98eef443655f7241c8fcb6d3b588b1b2cbfe597b261f77a34860c2a9c7 d64e37934298968c8f2b1e87aca233d94eab30da22662807456dc784e8f7aad78cb943d4e40bb959 23e9d6370da94d9dc1852f3c4015f5f3f575b202eb865cfcf6ebac7d1fe8566b815eb77abc1726d5 0e659c2be9a756abf47a0bbed233fda8924e73eb0c6cfac3d0093eb02fceb44217bee037230ed09d ba29da0b093b79236eb61ef7682797da048dd7545449846f0718d86d862414254fa451b654b86ed6 0cbab63703b9d6c5d36ef514de7a558fa98e2a970d3aaff44d6557be1f9c4b79e8ac81d21bc8d74b ac80a2c56295130ab231760a95667b96d79cc93d5faf5150be5e6f7dfdd0798d8d87f9e5185cfcf0 b39abb0db45df8b566d1e5327d944f29bf14d8a93c66c1c2342007cf9d8395ce77add362db428b4c 1dba21b36bb4d6dd14a0f2c337c1a298dd930545c4c942b5647e6fe8bcae7a7a1eaccddcdc76730f 7310544800c76d6d01b863dc0098052a196c0c80b9bc9a210c0198a78e008cbdb317f00903c004ef 0130148f7ff0a54b07ea7a64a568b20af252cd2bf41521c5c79e5c73ca429e52e96a10e2fc24f13a fb606fb486c7a2d490df305d735c172b3fdbfb564105ce15e0b8562a40071bc3191e14d0c11109e8 087d3bc3690074c4d614e858c13ec3e709746ce31b7264c71b0e74ba960e749c620474c2cd19e8b8 72f687bc1a9321ca3eeea3d31f5c735e6e590fad58d615acf296fd6335e513bfb5fd63de66497d84 6ce15eaf4dd427566351d8ca3597cc7f75e8723427f142cd63a12c9b9cd600049ed70184c9c100a2 0a74868902201ed0cd400600328a6700b22a1d00e4ecbc337c4000492d0a40818a95613201d01c99 022858a8678859006da0ce4f206e4ce31fbae74fad6372d34a4db5a96a4e1c01d09dddec981319c2 d40a5d5dc549fbd695064d49ed766b272e54cb2cbee0f2e6688800c8a2d202d012d5015042430154 ec62003a58d2001a003f49154d540740778b218015a0398035fc2380095500c0fa6308c0064d16c0 a64b07c0127e0660fbd22343d204b087286438bb00f664273ffc1e5aa9abc76653eefb6899578ac4 874635ecf25dd6244cde773a2e3d6bceec5a503b7d8a76393728c9b9c3f24202a8d5c70174bba100 0c04d87f34df9f01f9dbede1e7f4c5de0b0bc0617000e04a3807f0a872faa7e38424c000312bcbbf 1613fb78009045650790a57c0e20a1390a90a4a065d8672f50d8fc0753a01aa4ead4a88e38bee90d d656ec123129b5ee1d4f5c6da0226a8f6b3e4c7925d182b51cbcaab1ff0c2c1bd34f87febfbcc73f cdd7f4077f1a3bfc117eebedec84ec2c05c8315500c8f7bd035074200354c40d016a99db03d4695a 00a8b780017415d0011a3606198eb39fb5373bfe2b9fbad03f3ed4ff904f1fd1bf4d0696d7475c9e bcef497d5abc97ac55fd5675a7d055bf4c3b971d3ec62ed6634ea5473a9bf79ccf5c38a7fc543ef7 0a73ed8fa43a354f4165eb1e9ffa6170a4778ff810b72bab43dec7cffb52a003bbea68dbde82d337 b3d94994feeb87f0bf9a22fcab447e2d9dffad9d46b969ffd7e776311eff9a0cdc94c3f678d5d3ed ed62dd57efd47927f9732f1f944e3729bb358865af718cea3674a4b7bdce21866cf490770d622f5e 547ab7c0256eab7f3069b3e399ecfc32e3ae525d0a57e82efefa0596411b7c2d9ebe5a5b44e81a5b d0b71b33ff043bfa2ffe554e7f9ae44ff33d12d928abe25d5cafb5ebba1d3b17eb1605a9f38a2767 741a2c8f4fd5d81e3ea6743ce41df21b40ed4bbdd66dbb26eff7cd8e5d3e36d074f65ebba531b04a d5b0b0ead5a695e5cd1cd797c461da5a44f00a9b7f7a67769e1f3ed499f8417ac982f766895aacdd a76b45284d8dea10ca306afdfcb23ffc1121ffca92bf4b1b776e0bf0d5b958d711714e198fffd725 8bdbfb925ff4b66034edad8f8213ac52451f2d6f061d2f893d912ca236ba58d067623d8f517437cf 07d9dd9590c4f7e7392b8dd07ba2e688cf742df2a529b8301a13bbf66d0265a6620c1f21679cbadb e518bd16d3d16d80bf47012116fee27fc9a78f8f0094d223beca9f88995839e4edd2b75bc2160c6d 680dcf306499fdeac845d42a70f3b8f39266097ed166a5f06426ca67df4daaf1c59f1a85d3600ace eed1c4ae5ce209b47acd6217bc2fc7a9fdda8d7bedc66574f3b1f788b8699528224770f81903df70 308cb95e3fcc27e97c98c8f0692855e8db5ffcd7500bc01574bbc7635416b7bb6a6f715ca552edbc 789a97c79c4be7c0ac341c9793ea78dc988249084fec72985d13edf7e0cd18de86dcb8070dc4317a 0c95d1cd9d1a23e232b6a3677fe547f46315849ff03c0eb9cf231b8950d90f4b73fc1e2c94b01c54 d70f6460344d7d001e2ec3fece6dcdfb36a3eefb2d80d9f55b3569ff1bddbfb740364a6d7f7c4aa5 780b06436f79538b8379dc5e8d67a560bc981a397f3f8116d6659c9ae273740bd5fc88acbad528ea 05cd0828c49d309e2df090efa6cc30391ff92cbabdc9c3325f35037556fdae3741ad4b0703e36d4d fb7b6eb0ee77c1f7ad771a72e55ee73d45fdcb5ab37ddc04a65e58e40f1e75b02feedb91d21ffe0c 710d8db20b2941d38d1d4cbcc5f3a3cbb3445b08d3b5da33262de1f82d5a1af7ab9fc5882a960e11 abd0cf5058dbc5a17c77ebc18a72a140af24e860abc7c4c042776cff10ecc4be233db5de79f9b47a be0ffafef50687fe20d167deb335de7bb4777eb81f8eabb9dc6e4b39499bfdfe374e697599771706 76eeaa9874ef56efe4ed87271defdee7dea1b9dfd5a7f06a311e0c8304ace8e6a47d0c84f1e5d110 47d4271a84d3fd713e54569f63a0dfab8f815d69e4fa2edaaaf4d2a005f6b032f6ad26f26f4b3e5b 13ea0ce93daf06e731842179402932dc78b170dc42270d1c695a9a3a6591de76d5a37fb337ccb362 9b758bb0f6d6dbb2bae468669e56c0d9f4f4c6e32fae8dd2263de652e1ebb35905fc6e95ac2f5a18 f712b13b62705b8ef2fe4b182ebaa417345063d23f91d1b677d116177f48242fef156d0a599c7da9 b913f3d2740b8753db994505dca90000d55d6e5a42b7de6c29f6e621d9769356bf5e75eb504dc656 bb775b99de034c4d641e958c41a7841af825d4f5c7544874ba343a66783db5f7e2f4f82145bcf26b 67b42b591a31765713cfc1a311cbc24e58a91a5a60b666dca075bdd8bdfea8f0d3d3a8416be1b10b f2e00a17e9eec80fe6dd5d295ab15b5f6b55db6a680dbb791f762c87ea6356fb3da14d5f5f0a260a 5e3463302c38062133a11eae8c994efbe9598b0b585ee3f6eb8e967738554d98faafeabb34910f19 96cf0c9b97b2c0878f53b8ad03eb3e34bd24db16b91a877c7d142a50c90d9a59f6de47b61eef3f5c 4df1b8e6b0e796aa9ba953c52e9b2e28bfce36d4031e96732f02664a554a263a2e7e535c2300eb4d 83d8211dfd69b3844ebf6456fb8c3c59e3b3c851cb5f5f0355221b53b55c760fcae20c004a8d33da f2a690976563361fcb0da5b9cfd07d48fbaaf9fce1987f56f2cb3733be4de17aedbbac8df2e46314 ece0a7d747c735dd0f9f5dd1cbe315c15152dce9827b39b28e636d6ea6bbeecec45afdd420d9e153 8fa621a033e2bca4c58b794d2b74564d35b95c11559a56286559aaf38a7a940da5164dbe1b2bb209 9c62b9b1c576d27e90bca5d6abd6144fcb85287aba1e899e91db8b4803bf8b48817afeb0573fefc2 623a193fe29b5858870bef3c1eb846eafbcf5bc170936f9ff9ea7b4adbb67d30ac8ef6fec602c69d ccc5fa4bab2c75e68a1f35fed9bea833067966d11e975396a652566a07a52e6f1ca7259bf414931b 9f332b75cd9c22b571d615cfc5c9584494cb5ab876f8873000ee0d019fba02ff90d0900fcbd3af7d 940f2bc51b3f7cd79f3f6cbbc3697e5e7594fb38722eeba1319d8ffbd870e17b93d2c570d44353b4 ed9b439b174d978ca138f574b6338f3441d8ce545959ad95657a3d2975ec7a95cde1e325eddfcfaf db4eeac6a5b2d436d086e8ddb98e880c1d4ab8ca2b4918c0952effe8e1433ee4474b9e6e36eedcfb 30aa73e388e4388ecd0f390eb1d65ceebaba64783cb9dcf6fc585f7d273f0353fa3ee2e3e53a682d 9c6fb982ff5a05be2bd7267ab7717e0816924d2a0625829c9eebd0963a2f727d6505ebb1bcc594b9 dc9cf93bc9a9f827f1ac0f6e22b21bbc840119e705fcb5abf28f55bec5873e8af334aaf3dc27bf32 b9f1befe2d28e3f26d61ce4edffb0b2bc66c852d9900c32c9a499f5994d0558641cacc9ff3c70fbf 5632d35301ba477261b41e201d6bece740db77eab8a75be7fc5630865790d2734e4aab4ae1a929f5 4fe9ab72c850a11a4a701b9c8abe0faf844100eef8c70b3cf1340b5eb94f9dfb70e3ab5be2b841d2 e0f2e20d619316ccb2e249d7d8d268db63165c33c9c2b751ca54f7cd32bd1e6e28daa0dd1e6d744a 4bdaa8895f670a0d6eacfb0fcbb8c51526fdf2f3166ab2b7eee37d71ecce49deef36e75dc31c9407 bcce153f94aaf036a680ed48948ee0ae2bfaa7455f0872eb11ff148f339e5ea46b2eee7c8e5cbeff b9b089087f9f27cd4a25a6c0968e3d9059b88b0e5303528a5e4f4199362cc7a31bd021a6762ff648 d9a343816a711a4e9e0a2d37c37a4e1e1fd56306eefec3afe54d3cd48eb7a1b9d2d63dea418c9dd5 08fbcdd0e735a51b745de4b4127ca414b0cb63920b928c88be0c837f6e0d8ffb1c8d21c733c1944d 26d1922d17e31db3381c53a606a70f7a039473b431c1aa74c3b4db947de8e1542b4c45f2c4c0d95a 520bbf8e2eb2b3cded888b9525357db28d12d8e9641359ce9310d86ab727b075f5f6c35ccbd1e531 3b486e417b87acfcdca734ea5ad382670eee4d5d9b3e4156310a0129b95c0d156ecf3bc133e64be2 f8a866ff2edaa6d567963833666a213fa3cdaab1a11bebe044751bd18d6add771ff2347c9548a45a 6b10973583127da3c71138f6b4f061080f714a19afb177adfdc6469b551b1b6dfb06364a3a136c14 b8db0cbb6f709b1d8fd7996911955131b2af03946cafbd92911fdb2e04f83fc95cadae53463a9e79 42b8137794074c0d6667ca8863347ea9d39bd9c9a5f6e7fb906af38509e9cdca0b12913b3be2da56 2f04ee1bcf6f8afb4087793ccc9fead85b7ac1d8b84d32187b0e74748a1efba858a0176871767820 7345692273b5ad2073e63442e608b6c9609f9179dd4e7fdd79c2d586bff69ecc7be5acedddc8c2e0 8da71790f3b794446ec9012ddced32ce039711c256e0668b36f7158ceabe5989f441d924ae41bf47 10e5d9080fb56582bdb79b15360e8023c6918d1b3a1db5dea8a86965b4541f4188626d70a44ab6e4 ceeae57a1d903d7e574f7897e752d84a9e35d89acd05d80ab5216c399b056cb9e503bc3d9269fcb8 2f4a4328ac5d7cbe395f765b6c2f325efcc85397e25c172f6397c9263d80602b8ed0c902c6559344 91410b7f425b06fb30d7ef161ec66d2b5d34819ad9057a0a6364c1c8334405bc4d676d8e4e1da3b9 7f74c0632e0fdb9d7a0d6e011ad2768511d7ee9400ab952ed4510babedf6d06dab16a1db0ea6a15b f2e841b7613657a0f7d92ec3fe5bb132cefb4671d0f3ef177759f597d665608f7481323dc58cc686 10e65d8e13813b496fd71442a2de18c23e11584725bc0a23aa23721d23d6d54e830fbbb0bd5f0dda a7f676d4763fb979bb1337b7ad7ef6e57ccdb0d84c7e42f7eeb40491e733d41c6145a2c9dc65a531 a196fd865065d68d427cfc8033c145c11941771b052b4bdd0a767dd5e0137c1bae4fe57c6fd41aa5 4eb3202e4d4ac5a22cb7663de96c2ad93c4d1a5f11925917f71405efdb281e0dfc165a363e20dc45 8d5aeb72729116ce2e58e891bb29106597ece6e854f59bac4744cd1c6bcd1ac2d4df348ae2fc0cca c7c71bac7870a5ae5d15b8b625c66ccd7cd5bbd503dbfd7622af3af9dab5da4ef650b53d8cf46af7 000d3358b3eaa1182e7f96784fb6f0938d8cab0b7d2a9423659be0be3838282627be7ad24f2f27c8 71114367aad286f7ef05d8c297b57263ea4c2aa0c22fbf3134586d16a8baeed4843a48337acd8a25 a706f1eea0eaec934915eea6ab8a9fe64f15b447bfcb4121a8959e728a95e8352c17b9bdd52b24ed f7ba20ae07b942c914a842922b646bd2951914f3881bf5a958fba6518ed5299ecc70705b684a088c 64b78cf7f94f4a5bcca6ee2a245e18b198605df18e5544e1d6c0b6c1665eb857ea4683ca577ad634 570e168f46e955cf754acca04915f9a8ce170b0ca515a489d02d9485fe77f5ccabf3d934b7815fbb 5ca3dfba037bbc5f015ae105055a91ad012daff4f5fa6c53a095253340cb40840c7d2bc3c4f1e62b e06dfbf7c3e9d79a45053f402c06db629f930b44973a970c0d67dffdef2d80809d6d3633c7a54e93 47a466dd884795ca45cfe58b937ef391dbe6a72fa0bb8c8b405b3b82405baf7680b6af3319e60ad0 ee159d0c729461bf06da13e69e615301da5306cf70338076e27fad56407bd8be64b8667f23f438a0 1d15ba40dbad798e4d2cdfd96f273c694b60b7cee6c2d3849fccdf01d35c412e114e481dad26a6d8 ee8503aa3969ddd0fa266a4195fe8aad14858b05e4b6abeb1380a5f03b7566c7b400c05d1acae063 003c3c73001c3635009e7b3e001fee31001f996d8675f6a11753cf70a400f8addb009ce6a600fc59 de810ea03501f8f2ca3e7bb59d0c71dfeed7861f63a26fbf6994dac48f3b913a9d66ecea521c91b7 e09bc8c10d13f6105686720d9da96fc518ab0cba7ba8b0a81dcac0b15705800e56cd011d032d009d de57968ea710d0d9d4a90c8208742e13134072853e80d4d5efd40920487a00104efc64489b00c22b 2c80984537c364012016f30490a0dcce108900322c3919d0be35bc6c0b59fe7b78c8287d3cf252ee bea23bf235c685da67d0e9eecb5d68ec365510d4896f9bb64a204878411d514de0d7601ea98e6b00 e23c9a7f345fe4d8cccef04460002d990480421b0140c98e0da0522f0050fb3903d0997e02d0473e 9fc1690358a92e0218b4ecff6d36f1d33ae5ddb7df04a70098b3f533bc8766be7928abd0f2f01681 c62e651bcfd78e781bb939b23d7ca25684bc3c70fd281a55ec048985e5ee8503489483fe19ce4f87 fe198f7f826fee571b05606899fb47f8d53b0290ad572680adf301807dac398033b9338047e32240 b44c1c20a89a0110f23a0608c7ba004404d701627160006229d80071da47195e13bd061f6a329efb 147e9d44befb9eb75cf98c2dbaf50ddc07a0a4b17437bd2a6e0ff482de2099ff18db1feff16f243f 09faaff1f827f8ce4bddff6a313c1e02c4b5b90048647fff597b7dad0a90698d0428fad605287530 03280fbf03d4e8dd04a8719f07a875c3cd608f3244d37fe5d39f45f2dba8f54fe5feefe16c87e0f9 e4f0c5934eb42c405c9af7bbb8b5be73da4d392ac5ab7ee1aa971dce352e5048b752e743745238e6 f073af409067744633a7a042f02762c5c9c7284b170e1f9bb30fdc49e9edc5ab35de294f7fb93580 79bab14b60657d542d6cedd60ed21ade9cbfa5f2abd45c297ff0ed8cf0af7c5ace6590ea1976f8af c1f0e3238ace3da989c3dba2a927d7356c6d2ed65d3ca6f0184eff34940dcae0fbf8d41af923bdad 95fee92aebbcbefb377b31bd3776ca7dd9dceaef616b63e7bb9df551eae26b78d96757e876a87cab e79dc5d32b8f17f4d538cc63e25e98732fb2f5eb7e304b5809ff8bff4b3bfdd35fc020ff18657728 a6a7ceabe39ed16923383ed5c2385b8d2fc9bee40fe7db35292f3776aef9df56d9dce98f553668e6 ee8b67f7fdfa2fab6c099c896fe87b9a64c1714c524d06fad4285f83c94ed7f6131bdcbf27d0be59 8b8fdd7afd2ffe6ba8e5dc3da98c0a57d063ebe794c9b58ecf2ca5fbb96477ca95e33736509257a9 bcfb8af7cb9bbeb0164f3b7216f429eacd632408e6f9811bcec4a73d9e9522779aa8803d4faa93de 7a6a14dddd64a7f4ce13681ddc63b731cb8dd3eea53eeec1183ebaf55c79443cf2c3e8195a9b28a2 b7b79f4bf62ba9fedf46d90270d975bae753907fdcf609a43c37d0387aae7a65afb0785a4275fe71 056896e0482759d06d62bae62bf4149c55f8895d294a1368555163172c1a31bcab7c3382716a37bd 317aaaf747370f8946c4159944cf81bc8ae867f718c6cce811e627d7f2502ab53bc142ed4b815a6f 0441756bcc076b2b3964589f0606141fff0ef156b596a713914093ed1a2b443fdbf54288e71fa738 9f89b7eb36a98ecee9149c9e9f136891e66378b32b8fd1c3a23e0ae0a43922d25d278ab0051ad1f7 1d197e86291b72ef6316358e6fca30e1f3e6b034cb3b815aa90e82ea8a9e0c0c50ff764eeaefece9 b50f9d3ec59e8b8a480fbebd753f1d0a81dfa3fa531f7d0fe73ffc46792fe9e5cbe9c689c9768d4e 9c25b1a6a55982dcf4e99a499c09340f82185ec7d3d1ad1baca2a71f1cc24fd0fbe63721f7ea3d86 09dbfb0c4bd37e215888a34a505df46b83b53a6a0e8cda0ceeefcc19d6b79b7bba77744e42cfed14 b291f41bae8f3e98c80b9460e591b5fc3d0b97ecba0b9440c1899fa1ebf0d13972f865e5bbac3905 f53df9239fa6d474b153ce176f11811f36511ef7cec42e743be354379951d0329588be9add307ff4 8261b9364b821abe5c0f8cf9e9d8df574f977eab777ff64e780ee875e6c5a27f51ea55bf0fff0a31 bd7b0f87bd21af11ee6bdfe7dd5177a9b9c0fbe53982d1193b456377ecca8f4ece5ed171c7ae5f44 d3da4e0e9165899585054195e50f7ff472a6054e97f741bb9b68da4d8c11ec848fa87cee7b9a8871 c75058100e74b0749bc6c09cc883be531c253d1f9eacfd6bbc3ff944e372f3c2eee5e5bedf8f5c96 223c4a6ebe51a93bd37d13724a148274e76f81ea567553b0f59da7d96070f22dbbd2989847dff8d6 78982e7a078c74a6c246cf814dfdf609477a105f57191eeb1f5278ff586fb7447e312fd59a7eec3f 53358a2b7766b8282368a00b4e6360d12bba9716ae9a1f542bbe1761ed911bcbf8dccd9fe5af51d9 9138f3dc5d24e6ad5b2b9b2f7b7d0af3760309cbd63eb702ad96b86a9927e88d9b1d2fc718fd5c5b 31b06dd7d587c466ac93afc6561b69bdb7c6a21d589d043b5d15642e528b95709961bef9e1dbbefd b57a67d9cfdd9d4d6df9ec8fa2cb4e1faabb3d37b0af6da2977ef4967fdbd38817ad06925b9042db 298fb641b7b63d4f6c93ccadacfda8b8b7dab9e2d9f4acfaddb81c2b2fa3efc279fd01a0657d38e1 be8f0bd129d3ef68a3c794d2d8f02aa9d35add568b7d3954e6c27ead5416ad87bc72f790ac23ae2a 83f96a286d137191c1df4adbc0da1c8af7e279c1ebe42cbef8877e389bc7c660ffda083d5c7910de 68c67d5bb6ba13edd9722a6590b5b73942b70e90e89967c68a4c6413ce0cfce0aff4900ef73af599 a7da989fde34ae71fea8d36e5a50c5f6a7aacce32aa4280687cbabbdcec9fa706548bb6a71205969 772941bdd7371610dd62b721c28b8e2ca46a3210520d9c67e0b63f6c6d64759e69ad2c597f93617f 585ff7cc3e9a9b88ded83890ce5ccf323bedd903ede626c1cdf3fc261b83decdd2c3d9a7a77d6070 a47131f12d28539302b1564b32b15794157156aabefe90f59bfe91c1a0579476d21a94a0d3ad231e c7282b76f2a22aa4bb554fe80d91397faf8cae7cb046ea3c69dc459e347b03eed54c67dc0b02b73f ac2fd0e09be226593e3b8ba69ad51f1ceeaaf9d3a1dd923c26bba0f884ad2360344cd46160fd69f4 782da67a9a9ae8b1ab96768b4051c9632caf47e95c6ee4d2b5645bcf83d46a952ea2eb951e6287c5 72c2a56e7f630101b37a2dfe4e1c497e588615eee5591e3742ce538ecdcb290724ef2a3be9ce7856 80993e2b54e324c373fbc36ae80e2e9373ff340fe7577dd0ef5548d3e36b82d85dfb0e691da3b463 a20ff2bb21a94706d0d478aa45a9659494951a499a7243633cc9be1aa17822ec89d889ecb9d007ec b5806de2233f6cc6179e7ca46fee15158adc48659a1c70b1317632398aac60112e5b84c613461e55 4e4c851d7fab89e8559ea2e95501f03218537a798fd71992cd929de89778108e6643a3440f7a818f 59aed467257b7753b26f6112c37ab42e80598e39ab2b4be484c966e9c2495d04d0440f7fd9c26551 ebf3f70df0dd5be329bc36e15e8fce8263e9ce86cb55d5333b59f71eacd0dbe4d9a2500619f940a2 4cc5e9f1d98cf2b469bda68ca9adb9da5316c41528ab5521a9e669e650cd732ba69a2b6999c1582f 8a07fa32a6d7ddaf2d21b05578e047b786e5d4c2ba6879658234c8830b6bfced022a1a2257e46649 6b896760480bd7e248e21ff0c4e0ded8c8e1d8e1a4cf4e2bcb882deacb0933475f2b46be97f74c65 465ee955d70368bdbdfc6eac50dbf10ba62c836129a8116527785443d209073b1266a03c91022b8c 48737d8748f3859838dfb06506663d5ba5d574c437e4d9c0dd54079e5006ac6eb35a91ccbe87523a 90326db5525e827253c4bfce7bd107eaa0801f709ca73e02cf71055661c5b66031735ff698ca4d0b 685dea8e69b03c9c51dbd37e4b59deeb4c415ce7451ec16e9984ed1144a4144011fe3b5bb6d0d52e c06f06b1c603ec06e0c4c1fa557d1347cac689c524caf09ae3c4b2bc4aecfc33fdb9f7fb7dbc14b8 b27ab1ecb6fe960c6a9027d5d9bdd692cd6bb72efa9d5c997fec57156e3cbe76d8e9ee4d31f37751 a4d7f99c466773834359a7e677ff868246c28874737242c2dbfe9a48add991e811af077e1b55f278 a0c90d9cac4f70ec79ab295814d83d0c283f97185009df68bc263a683cc99919cc108dfb9324c37e 39ed9417e7b0366ece7a1475fc169439da666399577b25eab9794a2adaeb02497049a8f3cfc6a1cc 716331cf96020ba4d7da00a3768371f61d3ee732e9aa678b4837578fc088dc10bf3deb311e2cd825 f6aa093b2cba0caf18d33f00182036bf750428bf6011b4a04e046486345c44ba4633a41cc0f7ce92 3c429d2535d63a4bb83240ca8a36cdd05fc4c1d53e05fbe923f1d9f162d06d1efaa6f1388d45ad54 5d9392331836056252a971dc7b50621601fcadf4a21b48b5429e4a304c5c3a0449601345c0874d5f c75ecea08b8de0791f03e26d844ef847820acdf6169939f8199161edd959c6e35247e3dfed4ebd29 73f0e6b0b1e166a71567385edafbab0db6f73be26b4eca8eab5e7b9f85dc70b340cdc72ca49e07de 719b78c55d30b0ce1fcdd4e39c232a6b2c22859bab35b97878ad316a4017a97de796233b8728870f 8d04c45ef71b8ae52a1f06157a550999dd21039187c477ebbbb3aa88838eb672c69dba3e5dc0e6ed b0879bc3e2ad7d90c542bbbdf21badb37e255b7e83d5a1eb701942039a3f42787a2f43f865cd4383 3ae74283dc2084aeec791ac993f2b1f7d88489a38ff8be1934a86feb494d8244516eb50d928f3602 c44ae7439d6e9cd01271792439ec8dc12f5438370a8802aaad8e4e1a18bc1db90c6c696ba57dd8ee ccb6633dbdd6f9551ab6fc113c69a1acb28606dbc11922acc3bb199e7e85314d7aa4a38d38b7921a 5c82f71b7969bf01934ab7004e3f24ddc8c3a9ddc877e0a0c1a9ca78b81d5e8e3e178a53fb3868f5 0d866a9a6a8d4145f1c231242714a9165307572079ae43659c4add1c5a7c7d5e3fb9bbd47bb65d60 566ef59a7b08ba852f0c0a9806db7c4e5a52333225b3f139a86e2376826183a78f0998c49f2d2809 e8b5be38b8857a0d5e43b5f505666b0ddcb7abbb677356fd1fc2ce745b5126ddd6d712a2284adf37 028220d86187a2288a1df6bddeff41336b7f55b5f718e7cf638eb15cc9bb2008222633663483dd0b 6976c63c92acf8ef6c2dfdec758ae83b19fedcfb5edca3670ddebaf76a73f65dafb604d8a80c7d5e 56ab8b2a23efcd19268a045ce066771b62d0ede14df6facc0d0f3ed917a6ef4a79742e0b389a9fa8 6c69a9d5e55231197efd69c54d73e914b1e3ae8dec26f92142414c5438466e5260f3e13d7f39150b 7981737978e81fecdc78420f73aa763ae6c0bd5dcca938abe65ed3b90bcb996ce767896f358f49e8 8ef565cf2e11dbbac9b63e5fefa03e21a5b2b20d59e68f64ae7d6e08b35e49598a09a20f3eb6f277 b440d64ec85e64ef85936d640becc629e60778233dfd8f85008f945539f70ef7662e33dfbbd99955 e8657367750a597c670b15ee97ef02e68ca3b008d8867d0110b96cfac07e8453803fad3bc08f2899 a2218306b9353ad5dc3b49c7257ee8e4aae37e756f2f1a86884096bab8e6cbb287c9bc5036da246b efd645aa631673f81b773fa805fd0e83b41bf8097ef2dd1d148bcf7d665d431e19b724412069940b a0493409401c423ec54707c4517153cc068038951680784faf80f8b085141b1190c06e00e20a4729 560f40dc1a242092d34fe82276a56a6b8f7537eea46c86b6a3d5fb66fa306be9217ab3cb189cd5c4 618115b902d0289aea8628a1a49343d4ea090039a8c307fc5c9f4e507c8336809cfadb14e70b2067 0c00e4d64352acbfdbef02f2864a292c23c5aa09c87b6908a8ec7005a85cf19e625c0414ccc8290e 2d40e5db0b40bea9578a2d01c84fb59262ea36061365e31853736635a1fed090f085a7dacbb8265d d6f7afd58aaf443989de2f388618bd5c0c8ddb9b02e28d1f001e17a93b54829403a0ec7302a8319c fe6ba9a5875e8659401d3f28a04e340da877570174e16a011a61db80a6c7634033c52da0cbc10bd0 2a89fd0462355601ad191d40ebb9558a150074a542a73856006d48adbf39e1b8bb302fd270ac67cf a3ae420da2ba30195eaa4cc25c1552e2110e5d120c8e74aab502ac06934c66ebcc6e80268dcbef30 fee401e868f10474b24d8f7e477280c96a25c0602b1e30424e054ca55a034c7dd305cc8099026616 ed0113a5679f49120a303bc300cca3e0a79827807956b3807965d3df7d35ab29d65f95c3cdfaced1 6a29f6b2f2663be1cfbf2d3eedb0c515d1a54df1fbbd86553b370139d5b3240ce573850c11e63e80 811e9fbf3af4dac903e63340feaabeca1605acf52000ebb13c608361e5a743c77717b0c7723f9d5b 2573c0c9da0970d55b0e70ed3607b800afa5d88c01b76ca63f5891488ab50cb835e700ee3ce8d66a c7c6c51cb8cdad1613fdb97c3e4d02be9af33b74e7b8ace306bff87536ed6722c3617747679afb05 f2b72ceb84fd91a039a2c2fed5557f26dfff117effc44e4040033c673a80776e3ee0432b06fcf971 0142798600a1a6ca40e882d62f6262152f80706ebc8058e42820327b0388aaeaa5188eac033b7854 c261eff00b1311c7b7d58c2590c8273e8365bb94bc17767e5c89d4cc3e53a5ffbdb0c158fc2767e2 a743ff5f9aef2fd8e19be4fb27dda1321a017146ad819439be80e4f47120ad75034817680064b048 805c6ae480cce10290b5790dc82ed94f614f7edae91f53df4f3ed58bff5abcffdb9fed6f4cefe363 529b7b542a1d6f3151ba5fd74cee7d49f80f7421869ffcb9fd7e164f67ed869db8e84c1e6fe68d39 8acb0377084a897850b6e7f25e3bacf5dfd661ccba9ac442ec6ed74aec6d12fd10ac0f16bc5ed3eb f6fb5f5b87fd2b0fe13ff11f42df2fadf5eb435609e3a1c5907387eb50e7baa69edf89c725e13eb3 73fb755e9e7a99437214e3f070088ae1651fe2de6d17d1e66307f7f4f7762de7df5b7492019b66e6 9d5d7bf01e5e9dededffa4ca9e98f8d97ecbb172a1d2c9b550ef2ea0d16e3e3781fef849aa9579f1 bfa210226471a4ff4b3e7d2a5309bdc36e96b9a29d9d74a683be76bc553af6e169f55bfb10abf792 982b0d36497936581f2a83d1ea6c3526cb9bab4d7f0e62cc98c7cf96f2d72dfbe92ac942bb897fdc b273782c3e223ba367666bc32eccd038a0a7cde2590d0f0dba117ac469313977d8cba4c70eb22966 f909771dc03ffc53e7b7097ca5b49b9560af73fb31830ecf2a07ef22e256daa2231b5b7b599e5ef5 0abcb814134c5d7cbcbbbe80fa3b736e3c76b5285636f50809e7cdd95adf7466e87cde9b26d5cd70 4aac96e3f0e0ec66219d1c973f49b5057613ee94bf8c7d4e00c1d36fa181f2da49a350e3eb231d4a a623684eef8751d5baffc17fba646fd6f6783a75df93e52ec293e5faa065d74bbf38dd2d3eedce65 1ef1f5d7efdac88dec6cad39c834310d6c4a2c0d2af44a063739373471c21d44797cf3346dec3386 113cfb9a150482e58e3e23a3390a95766f0485ddd130d257f3213cbf26be8d94ee83b5eb1406e86e cbf79376edbbeabbdfa48fe33e71292e7b873eb6f9e157e5439b371f6995834512d3a0bbbc591377 a19d142fb25e883f4d2ab74978b03ecbc9d9fdecc6b7d6f33c4e278ecf20e0b360f419667323ed9d 4e82a049f63bf1189a10c0877094a5fcd82c723eb2cc8b83758d5407e89634fa494377fbc4c1edf4 3cc60fbae77eb2ec724fe4d2b905bd7c47caa08af79cb55d2f30b67d2f300ba15786df935f9de783 9059ef8c3df3b5c12dc535ad47b158e4a789be932767676c8c7d725c0f0276da1d85e23c1846e5e9 6c084f972bdfce6d7683b5b53c0ed0f5f2da4fdccda34fec96efdea1b5877a1e75cd77cfdd53b1db e3de44e7e6bf998e2fe35fa3b2f79c08865786aaf576688efd36b4bc2c5a9163de5a7072419b71cb 569b76dacfa5b8b59bc8e9d6b93a87cefea0c487f1d24794ca4fe10dbdfcbb38f6893632faf41d6a 182996e8c77aa3325857dbce4fee76daed3e910c063d8f1c8dbbe7ce68d6e5aea3b8e3a727af233e 273bef19cc8e5eb9b8b8b63f9bfdabad0b37a80d3d61a465da34d98c2f86d8b4fb13b3b1c93dda0d cc53a7f51db33bd5c959bde41efde277fd8ddb916cd765f3412745d83dd3c3ed72d37809f5b9a97a 72c8b42a64500eacfc304a5ba81f3b3618b87913e993198fe93162ac7679ef6c77a4e9ade98d4574 e081000dda95223d6b672fdab2551594afd6d92ae49c43b366399766891e3c1b9b4f926de0bd4fa9 dece118c7b6a6b8adb556635e7bac9f41d51a8c6b5c7e3fea829f690b63f2c63d9936baf9b221eda da20f07fe6eda5b4d8bbd33de1ebc144ab7df36c8685cb04efef27b77cef24299f1e730599cefd59 26bd091394dbb37a62b716a373ab85209f7ed3e923e3c6f681cc1a84452ceb0784dbd66997debb67 deb8badcd0783ab76a0f72c45354ac05dc9aac95b3c8b709a4830bcdb2a1f7a46b456b2ab6f258ff 568d1f3251b547f76ab568ebdd147e606e8acdd12edf4fa6f385bb6f4d0697a6318ccd9ad83f8cba 64d73713a4a35c79c8d3da3bd036a645bc858c78b191f4cabfcd84c96cd3ad3394e7b9bd6e6fe0f2 5a3c71fc289ad55ee47a590b8e876d2d7dfa1eec2904ddec6c13fd58735282adfcd8c5abcbda5aac 166f9069d625b16de2f96d64ec4ed59b417157b472ca8ecdca2957fc7a072b9d4539f8617d72dde9 2ca146ad205ce8e660c7ca52f7ee5a943755e3523befe5b3cd4db6f96a24ef7ebeeedd368cdbcf5f 1467c8beccda2b2ad66b2ac974ec69874f6faf8c34b6e63333b20ab0fedd4cb8ba3c7849b5c47847 730be28789d72f90b1278a25830aca6ce56447950a7b855afa75d69de903b370d11ecb41517b9c6a 8636eaee3c6dd4c3026de41683e5e8848553261d788df22169f659a22475d496f2d5a1db0523409b b87ecfd58f27fdedf69b78c6192eeb446d7c1d8af60c9eead68299db56a1bf6c549ddcb15b2d2d5e 43b381dcc7c6be0f22a32d145795d382dc55ba56f9a25fcfee5b1f841b4417eb45461bedcbdffb46 534671430d8bc444d536b3a30a0d0ca41c49392d45a795e2382c47e42588a1ed2b9c0c49aded6fee c56af761e7642fb7e2986683314bf5aebcccb9c28dffd4dedee56dcf8c5cd15a04025b5d4d856f4b 33b77bb962124ab566b4c35ab372c65add0ad76af9fa8d1a4e7471329d6b4f6db7d1022c7b523f43 f2a9867235a742ab31518e7a59a56ceaaeabc4f364a4d86d6baf14190896d79745595e5fd9468a9f cf465e6f87a3f912be4c7f56f3c1be9ab73a93de556eadd2496883aef025577486b9da6409de362c 74ee16622d7266c2ef09e350bdf295b3732d57b87bc6d47d39e768cf49aea995d15257fd24ec50d5 49f6eb8454a1572d2e47cbe9be9c67773725bee5328a1d553179434e45d97d6335199bf47c69e7d2 3ba985253989dcb715893c64ea1219cbfd14b551542f4ec3a0b225bc7e372dc48bb227a5d9849f4c 9ddb33df371eb58f5fcfd9c6e5f8aeaeadeadd24eeecdb600a8d5285e7bab42e9903512bd7baaa1a 6ebb869a15274ed90c26cd72dedef79465f11d28c54121923786bc91eb70eb2c6387cd476aa9b9a2 44956abc78dceebe0a94d8f1f581c84ae78d70797b90d00f3929c5ca15fafd4c3705359c514227fc 59e27b83ebce6ac7d595d2f0ec03e33ccfa0644763216b1573f3b749eaecbd72a1724f7d081179ad 7c247055bfa2df57dfe5b921cae57cace84a71439b721da7d3b3f4305ad26e311848adf67e2a51f4 7b299e8070143b9bfe5bb8e28bbcd07fe2ac20041d837fd4b25d7e54f257bc9cd01feedd7c0adcbb d5a8f1f27cdfe1e545ee3bea0caf56351c3a01d4e906e2dc6aad5713a5dedbc48c03b4176a2d3378 d66c9eebafcaa555b8eb726672d1c02106e5f934292acbfd81544af48d9771fda648adf94d97280b b6c5ce196d886ca8f584abdef8be2c1606d8622108bbc78e1f29f89d579036ccbdd77b8a9bf4659d d384a5c7416987c1cee2cf9b35d490630d8db6d85977e0a5d8a653cab51cfa4498789d30185acd1d 372abb432162edb9b92c55ebb9c7578434d8acfad265757f53a74fe75cae6eaca7521a7979195ff7 3089da79b4d8a57a82709d84aa30d0922aff48ce2e3f6a411eafc8dc909bac8c19a739830d0771d7 2b3bbbd3100b571b3813174e0a639dabbf204da4f78ae8b5e03fe8b5e8b2f49ab8990c622aad14dd c17872c7c301938d3ccfdcd4ad4647b21447559a4c3aea98a3c6f13ccbe8c353e1a9ce88c1b55cd8 d027b9213f2f12cd17332257251161b0e2bfebd6f82767f2bce2db0a17e69b154e5b06353662e316 6b5ccf7d168ed090894d6dc520c7ee895e77f61fdad538944aa2ae4435ab1997220b9d0979589317 f2b0c9921409824a8a4f8322335c2f1d5365befeb4de9dea79ed1a5eb1eafdb1a2d42aa1c9541b99 3e5a19602348cb6caf4f65b5512ee973ee7614bb376f2f8847ffce97339b1c1736ee2807bdb2346b aa45918967c5326337ca557a7dd05dda0dfc2e8d95e3ef8a15aab9b9cd29b241ecc823d178924cb0 2e101795e0887e6662137c448df0bbb93be2c3421fc5ef0aa312fc287409be7df04645f61476c79a edb57085b55c794b287661c7337ff472456e40652b5efccc49cd1c7715792838f0c1984f387dc09f d9bc5dfe30f6a59ea73795214e638b3143b5e8a54491e78b4e76f8bb45b239ac495c16fa8010e866 88dfcff11a97f9e7057be7cc1c365eac692c53442be874f35b8889561a6a82569a14824ea3a3824e 87988581bbd7f65bf068d2c99164bbd145df5527f38494eaf65d642ab7b28caa11a7400a7a193c25 46216efc4bea1dd9b9914de84d38de52ade1fc429e0aef0cc9bab9af724bf4ef388e3f648ac14785 aa8cbd9d9a8e4dd0610d536f470f9d49af215a7933f3d2c2691e4a85247e17572d0c2d3ae9c315d9 f6e016d2e06731d210da1964db1078646bf42bc5d2ec55eff7a7dab8ed94c02f9a4594f6553b46ce 8ae9955e8c1ed471b46c37b9ac4cd6476fe1de43ee5c85358f4c493926e4a9aaaef0478227d864cc de30a8640074b66de751581c61252b98d025a4bc978acee6a117d106ee208d83e07dd54122e88f0b 1ed8ac0af40cbbe67b44279fe78e80877dd675603181a739e5d8bec2e2bc4bc183fbbb0cdf04d5ea 4e2c74dc6c2fe62d276427550bcb468ac18b3b46d347e934a19e1761b1df6e7f78b579bbffc47b51 3a516d77b7c79f1cb546a3f22c2eba8ab5419a6ef78290fcea5df086875c81913fa57c6f8d50f01d 2304d8df197a2e3819760e705d2fab47db31342fe4d650dea9dc334b745fcc145b820c3685dd6f71 b9bb76976003a44ca6585ee3197b4c8b9e15c5a3fa2decb46a79a251358f544fd15fde9255ab740e 972995ceff1411d652d70fea50c4ce84f8eeefb16c3eb729ba7b6b91bff887590e3cf579b662fbdf 005a68be092e50018f5e99e5fe98cd94e87b116c2e180d708197520416c00da49b623007b85938a7 98e4015e95d39f5aa00d70255c02bc9ccecd70e680039c25845feaba0baa6acbaa1795ef63cde83b cdb29e296e7865cb3d883f92b9313721ba49044fe29ecb5f31fdd63a16eb95d526fd37bac8ce4469 9c41b397101085fb121008b90704ddbca7586600c1648b29ca748ab00c0816aafd945bb53d48f18e 01a1b56e80d00be9b7f479fa858ad9018408af53cc3e8090b4f437c9815cbf07e4b0568072cd2a55 57ec9fcf5cb584a5289df35b92d7914f91c134264bf217ef8d559ce33730a3b865d963fe1a363659 a3e1cd32e8343f01c4233b4bc1af00990bce29ce2f4012642e858d02525c738094100d9066b791e2 310464b59a7eb97a7f00d26aa55ff1183dc5b90fc85a37f909c40e974db16501a9e3fa2f9ac5da36 4b9ec9e5344787c8664521d62d5108b6339a2deece2879cd17616ca6943fc5ed717ccffbc8e99885 afb7f430157206c8a9b900e4c55b020ac409a04ad04f52a54a3a00141b1400a53e494039a2986260 00aa9769012aac8f53bcd2ef4fdd37a066f9f42bb3c80454620d01b52b1d00355fc0805a085c8a8d f537279c647b9540311ae5f5d5fcbe8a10efc3bac25583094775b910c321e1982f26ad02c8fbb17c 87969ab20714136f00e53d77803a124740e795f34ff805b4d4be03da5c0340a7d32c400f1d1ad0f3 ad02e87dda08e947c7030c51fc597b19a277000c59840023a5e79c912d073056294cb1b900c6ae15 0153be4b805175db7695e2f49713ae9977ba297702c9e2674e4da3db7b8bc7c34e932861bd29921f 0e4e10543bad6e807a71a73f7ee8f6ed8f04cd6010008c4e407f85dfd080ffaabfe75109309f2b0d d8a2aa02b6bc7400db61fa805dc60bc09e946b8a4301b09fb60cb822d54a715c008eed3d01c791f8 2ffb418b55c0e944dd22d7e4bca21ad4a8dca0b4aea8f4cb2e8be52b06f1ea597269f334e9fc6352 2f41eb9c0301fa767affade95bce9f008c9ff1f867f2fd89abbfc409a2c2fe0ef30b9df8a9bf835d 7ac08477008f9e4780d78d15e0a7b927e02f0b1c08996a0508687100042e4980a07b5920d4693ec5 a40a8446ce33851db9d4ac409dca3c6af87c7e64b7a9feb6f275a962966c6908979ef1ac5dc3d13f 67e94f6ddfb2fe54f4957bff2deff89f9c895f88ef2fd5a110d4fe3ddae1d11801d110f640dcbea1 9fb597dbf0404ae72840ea7333204ddf77206d2718906eb2066468d30432c68cfe4393fcf9657fe1 0d84f1678b3365b61a3db4783fbf1b9bd3f686b457c72bda9b5d2fc4f067893fd393f6e7d4839ad0 f166baf0515c9a85c3b3e6a207656be2fb90d0a93de449cc0eee937c828cf2d2160db3da869867ad 35bdc2daab1e6a4f96b7e6f9988eb3edc23f6108ffe09b70f9fdfc8f52b1ec2f6ce2f1a944f2dd58 f72a37a4d573af68d7ef5c083f18fdd93a4c8ca5f9fe532f2ef7501b6c7670f7986cd7d270b749d4 da7e43cc2ac7b507ab97d5d9166e3fc3e5867d2e7d9c03f1b3cda5473d2ba545c81bd4021a76e539 3c59d8919d2df91132df1d672ec266ff4941f88f5dc3fedd7afa80aaf3ec754dd64be7f613277f49 16950c7f50567b650f355746625dbbf6869822b53f99b23727692dc564dd890332ee2f3e9d68f8af 60d9de646e3c83288acb7efcc72afb4b954517d165da44927778a8834248eff9effce66fa66c1ff5 c7be304bc6e2b3f40e9e01910982320c7ef8a7ca7b54906e97c6a572388ad1f6b2330ee27d434cfc c7aa97af81a5b8ada413a543055b407d969ac3239c8bac0ffeddd069b6d64be5ffed96cd383fb7ec a447e6bc3f56599f2d8d82e7009f068158598f3e41eb34d2c10a0c23a3800f4db8aff9718d1cf876 c95ffac8f67cfae16795fd57bec04f3ebd10fdd2661f1683d1faa0567bf1d37d8c165077368dac57 fb6fc84013ee5ec2833d7a4dcef55e66c2edfdfcd8a77bc5b1780ef020e07b64a0dc037614ca3d7e a4bd03791869befa47520d8d211c258e6fe70fadc1ba76eb0fd0040efb49d34afac471f6ec791c5c ead1b795d1edc9a54e8acef4bf5cb297845193bdb6f186ab5e6e545d401dbf3c73016f8607abe8fe ae0d96ef8cc5e3cb0f022e3f1d7d7c6c39d25ecc6e18a9cc6908cf989b6fc3e4d34762e633708b14 3440372cdc4fea72b14fec05ac7768ab548f3e57f8eeb9d72a77b9fbb0daf1e579a3237ef2232fd0 cb5f55bdfd9947f77668f1681b5aaff55654471a298c7e0ac7bfc5a5d7f927f8fdaa9cc3038c0b0f d542697c6bacf040b92c8491f6586943e3b3b57d3bbb6d0edcc2b6d74f9cfda84f24a769efd07c7c 4d7d3dfaf858777bec69dbe5ae8f43e736789f3be2f371f39ee3cccb2b67a04cfb33230a6d1da6b1 36145b5ccb2cb6cbcdb8be729a36511a34d65e7dd9402f97473d1974c87a53bc94eb4d89afd58997 543fb7efd7efe2f20d31c6ddb971e5f9905e9e902060a0b4f1c8edb76f436e6e802e5dac4f6c9b6c cf238272b7c7f846e7d60f9d8ef8885a5ea044ddf6278cfc76a8c7411b9a47612baaaee7adb4c35a 356327d9366df4f66d698d75f3796ba0c7cfbb9e74997c3d9d7a92eec18f14977e7d1ca7a7694387 874e49ed36efc235bff5666a92c22a296ac64f8cfed98da7c49c4203e5f3017eeca0d77e62dbbbde a1ae9fba3d5affbe8ae8f87c35e705720d6d875a8d6e43514b6c99f9bed68c3be36ad35677f5c67a 7b6c37dcc6bd574f9eaf61bd39ce4edc63291fb9de367d6e5cc4f2c1e9c3bd47ed6e4fa19a74b963 f66b56966c95881c6b3a26beba80555117072b87b64bd545362355ab73d64851b1b7a85f6f467183 e4c7c3cc151de61f50ae4f49f4ab2be0f6c97b4d8e5bafdcec276dbd36bdb7f28d53ae590c1e68a3 ce525c7d3793953a45540db713d4bfa34e97b5874de7ca6c7a8e30dd0d6b23fc38a9c9cfecc29e94 f31b5b2b4a276bd6b79f169c1bc3d5453a5eaa220c5f369da95f37d1063d351262753188b1831bc4 2aa7550e8eeca4a8a4fde162644d5b4cfebb87e4686696e8fefe0c239d27cb673c48f11f2dab793f 36579d4ed2d88ab5737def2d20f7b4b9a2cef5f1661cd12ec9b5d185add81f81b5ec30576dd89055 4de711746760c1bdd6a81aebd3591599cf97a64b1dbf330213fd14ef46d355b395c3ae8555bce15d d62f48d9d57be7f344e7fb8db37637445c4befd4b236cc171c4d5a16ea8ba9eed893419d51fd35f3 667a7c162a79bac1645b45cc7d369ab5e3b94e3fe5c4ed81d2f7308e8f573fb532d2487bccde84b4 a2f98cb7f28585528d4f57a35ae4aeb6e946ef8689a52dc5689e4a0383ecb2e34a0752e71526e96c f53e393eebfceb0dd23629a0eaebd617d5f13cefa899823b2a4fcfe8b741972bbd43a95ce95b4a8a 959de2d19817e6dd7af0b1b2ea80144e6c679cbfa1ad7458986d9027fee9f6c5f9c59136f8ae561e 6eb7b63e79df2df34e64abb5025732eb683a20ddf90a6fb4ccb65a392edbdf37ec15b6d4b6f5cb75 d6d40571e2690ff834d464fb1aaa6f165fa9ea4c3daa99c6f85dae3caf88b2a809bc6295a69652b8 f14379e51ff6b223fb45b9f481c4145635c5a83e6b4c8cfa289abcb4dec55a7cc7695e2e13ffd1a1 d3c73df9aabd99cec59eee737babaa3556d55a737034eb9dc5db68cd12b8d2391c50fd5afe30ba30 fd08da8880cb9a7c200c75c210b69a99728df2ac6274cbb97d63a42c468bb962d999af255e5e5d84 87ec4c270529c95d19a971a89a12e1dd07a2976926a207950b221dad05919e13a6d8be3bf5f0dc44 dd9fcfbc7b3fcfb876a11161f5536b93734652f665cf2ad58b55986c77e656525646bb447cb31f2a 5dd1b8eb03bb0969a3ba5b50df8f1eae6a8acf9467eb502cc3585856ac56585110f95093d708dc96 51171d48096f4ca56676b2110fadeb55f41425279cc32e29f4ea858ac0e3fd2e7f7bf21bde1f83ef 8bfcf4b3c7f37e277d5ef85dd499dc5e48ddaf4b1bbd33c976b9e6d64b4bbf9ea6b99a665f5e9665 5167739b4c7606cda22bfd963bafb427039dd43007bfca1192cf9661b78828d69d21e4b5cc3032ba 92beb7a7d444a5b244dc9a55f1e0077591c9c71de17c82c6426fa22c05de999c793f39035e1a6a04 f7aaee356e8c543a5c79fd5a71a03e8538d090580e18819ee2608fc7c5677d40a97dcd9b551deee7 dbfede374f6d0cffd1a19d337e31da4727d16f95cf4a53cede5c85e6deb60c9fa29b5284e28f8ce5 e76961b565513c5c7784707ece69a117df2581af6535fece4a357e9869b67969bb1e71af26b4e0ca 2fee7b7b726039f9b0530742d90ad72d3373ffd164aa7267ce5415fdc354cb592685ad31732fb283 69fd50ff45b3b4172b8dab9f2b3a56fb441dd8b285e5db24b3994b85bb725bad4cc7cb72b4d3e78a 3de1bf8f35196b964f12d991ee622753cb087db802f377bb52e4874c9fe25ed331cf8d2b9b32a712 a0ca8127df64a7813b6073c570c6ccafd08ea90eb427bd82a3225d5ba2225dea2c1ad496fbeeef77 074f6a8bfd1630535b1c2bd3a58a5d1dcd1f49a3271235bdb5e614debd2d78cc8e966aae8a299d57 e592df9eb560f1d994e7586fa91427c548da358e91d8291f3742bf012efc30c8beb83728409c5aa7 1076ba6771b632aa7e6f4f36a77464a6ba1e579842efe2d22ba1d8a59d9c1d525bcb5f530dfa73a3 f08b9927f7d39b40b60dcb21e97c6e4a9c96d31b49bdeb448aa34c521fcc1c7edf3774154fd59b4d 09e19c178261d6b2c47ec3e84d72dd7aeb43767256b361b2518aef722cb506f74864ed7ec80f1ff5 989b20c30397e92def6c6ebefc308bea15660ae70c4a3b7c8ea2d1ac2052db7955a308aa6393fbd3 b24db6c3ec883867f998e8ee826f7e1ac1b5b359fca6f458dc07888d8bd3d9187b1aeaf91b438be1 62b325e283e458f109ccad77a029a537bce0c3d5a6e81dad6e06145cb9ece497369e8d2e4a6d186c a4d60d8b8501339ef14a4e9eb0b3f01702c42c767242afdeea99469dda936a248d0c45f833846cbf 1638498f4f2cd12bbd65824b7003bff9b5062ebefa7decb9bc47581925f7e8e7360668382c92a82e 07060a7d38bf1485d7030a5d66080a6d0bdfb7b8a85eaaa9fd73d56cb4e37d4eaff39b0367cffd0b 66b646ef9cfe9085b75a39342e327eea6d458ecc2f79a5d29ab1c6ba30a1d7e66a4c35d9c59cf48c d38e381f335782574b6fdc9f11595c3244047b11ed6f4bc3ca5e97c700b350517d7ab5d1ac817b25 f3501d97f2de7253b4aff947b138eb97908d89a9483d1f77116ca26d10f7c3c1c8865e3029de724f e2b07a0b9b6c35e7f58838abb65c6206bbdee7b48953fa86999411a05d245af312fe59be2f396854 8b9862361392c7526344a4d3c809f6ea1b2b4ccd35f6e8d4ea5ed1ec79f22ecd7b49ae94bf1f8ac5 e502a68a25841091cddaa820383fa8175ac355bf4059d4227f42865f6928cfba6f18bee22d0116bc 6c2337bcf87358a8f500dc9f40187c795b7c473f1cdd46c71f69b5cada65abf87588e98ffb2ca71a 83f75b4ef06f72abefeeb8b07f5c31f1a7b6a09aaf74722cc0ca08cb60c9d7ab5e2a74a349b11425 4ba4913fef0afbdaed5c6873e82b7ff24928df95f4227c1ddb242c960229a71061253bf10ef52c94 2186d06cd65d416991f7cc62e9a0194b7d6b19041af63396ee1d338b7e3693a25cfc59e2fb63d77d 741ccd46b2266774e30aaa7daa13b86cc38f8fe459e8950f8ee69e351fc70db59bb30ba2bf9d4d31 f54204c5d5c4eb17dacb620ff673f7514e39dfa36cc8bf56596894dd4391425e2138e4bfd72613eb 353883cc3b18700b731ea06b5001e8d9f3007a8166290667805e3924c5b50cd0a4df05e84eda0074 7ef8007481159a6da5e63a1041a8d5c69be12a129031359a0cf20ac66c3f221fc2df093b97bd4a07 ba1e8e3724bb2ac5d824e8ce4a48f516140e4d73907b3ec66d285fa53ac0bd158600f3d9698afa2a c5fc90e27507d850cea61862295e02c0167635c5b90bb0d898a7787c57e4036cd92d026c2568005b 8301c0c22001d8349de861531fad8bf39263db0cac19e75b96d3cb7d95282fd16e41ea0811f849e6 8c03f327b23bec27d8c7782c4bb660448523148f7363fcd283f277e1e71dc4b58c97421802dc192f 521cb700ef61e714e62bc51406f8344fa6b0e514871ac067ca20c57609f0a8f200f82e87a7589a00 5f98e9ff122307800751fa6b638472f4daf517cdd2dcadd5ca9078f15a05af500a2eb58bc2880e20 d6aa1d9e143dcd9ff13257dd95eccd625538f69028a736985166c5f77b007f0b1d4050f50120e465 98e21503c22a1f00d1f6ef2976001001f9cbb925628f49715601b114dd148b212056ec3ac5ee0d88 738b02c485b452dc2680d876cf8048d07c8a096fd7889963b2d45ad7df24299757b4408bd7ab56e2 8c9d9ba549227ee3e3c2f17b7b96964be150e87c1aeb5c66eb4d33a87e1c0162be1c0132fb9a0052 284e0159e1e7209d556e528407400ea02720677a0e90db1003e40de20105591540a1a716a0307592 e2b807145e830045647fea2045c42ea0a47604280abfa758940045e34275cf0c5c63407daa9aa162 65b97dc7593e0c388cc126769e902bcd4f69654e6f85eee87ac846ec6d099a752cada9ce477f75e8 9fe05b4eb6bfc3fc84df31941e7dc95d00756cbc00f53a17005da04940536d11d0d57715d0a35637 c52b0274d03c037a9ccfa78824404fac36a0d7f832c5e605e84d054fb153aa74f9d4d63fdad3fe6d 23fa78a9a2b42a096c0947085278f1086a85e9ace13c6b3db2e644d9fdad2d2d2bad246d46b4e45c 01dd1cdcff1a8f7fe2eacfe9fb937eff644f1805c0f466246036d4cf44ce82b1035896f401ab864b c03aca33c50d07acdbad00b62f0c003b033bc06ea759c0260c93c2d78cdbe6d1d5f2e6d3952e1760 f039bf2053dd354463d1804411ef8d67b3f3ddfbfe13c87f3af4afb66f597f8af9a9cfe776e1df6d c7ff29fcfe8978f8596b8da50a385f6a002e4902c0634e027821fd6bf8ca8a057ca35603bc4ffe52 46f8e87605fcce2f01fe41c829da699bd93f06ca5efdb484c93e6b334db5a011a005f1c53dc813b0 4666f2e0c09d5eff56d83f0118ff9677fcaf60e19f0efd137eff0918fe9333f13fc22f953480306a 874014ab172046541188b79706a46cd80392d0488064d05920354e3c90fcaa9562e9fd9ffb467df3 1b54484de1d41f1fa3dd7b4076777c8bf1c6fcea9c8df5a571d7f6e7f69b3b9d7a107a3dde4ce471 1497f0fb1094b299fda7fecaeeb5fd05de45f41149627e5eda3aef16b6694255727d307fc6b1df16 62cb5ba3632dc5c3ae1b2bd7e27cf1195cc0427b95a9ff6f1ec20f66e9a12d74f61663bc7245bbf9 efd661987ba6c798774c1f44fd3f5b874d87bb886a8d13eb26879ba4fc08d787ca76b6a6178bf9aa 87cc96cb9b3b5a2f7d6cb68b9fadd121564ea3cb22e4468f05e44799393c0ef3919d3953b3b58996 67e8b2d19836517c392592e933f4a84cf1b77558faf9bf770dfb97053546b1c7a57179648f37bd91 df7f1c0edd194798daae8533bf3ee89eb8e25692b21413468f9563d15c68e96c6b1e0977676e3c5e 8d2856eedff79e11121e3bb3b57eff06cb5e46d3a4fafa57b02c96dd4cceade271c29da4d7d8e75a 85e0e91ff92090ddfae813c2f351a8b72f233d3b7afe83ff0a193875dfa3af716c0fd50ff1faa0be d273634fb7b1b2ef9c16dab9f3981bf76e26b2deedfc0c8db4e2b499d7f0f05053a890de8aec7f58 6515f57f5b65add648fb38bd61a4b7839fa49a5dc57e6c654e3eb2563203175b310374df6af4130f 84fd26a327ffe01f2fefd187c4d94fa48a9fae62cd8debb3317341d499128b60f80b1998f4b07835 beb592ef6c2d7876e373a0dce2fb2894e2f7081acf33c3485da6c7cfec103f369625df8637b88fc4 97b4cb2e1ef901ba7929fd26012a7de2403a3d8f2977bae7be37e9728f64dbb9053ae848e0cc7acf 59c3f102e3f47574fd6394bd1b2bf2fa8b42587be0515b68c79e3443c30e39393b143b168fa572a0 5c296ba4a503faa109a8ae1f57d8a18f2c8470e022f2a29fb8c2aadfc4d4a44fecaac79e47a9e71e 7daa7edfb077cfddfabbcbdd2cd0b9f96db823beda45ef399e905e39b312daa171d2da7a9eaab7a2 5a7dd882b7c74d336e9a9fa64de5b926726a561beb6edc4a91783fbd7c934895c602eab8e21f8557 3c70d02f94e19e407eacc5a5c1da5cd3fda49648bd4363adf5e8c3d9eef698f4cebdf56f5ec717a1 81f74c2fae1794e1f4ef0748d40e2b70dcd673e8a60d2de87dcb4488533376e97bd34ebbadc6ba65 e71b2e157edf78d493ee59ae1377a2e61e86a3a1eb296957710e1739a757c199142d35c5d4fca9fa b1929ccb7fe4d39fc20b4f88eb008dfd63ef506fdeba3dba95e9f87cabe03d8763b2fd990cb9b60e 8dbf5d671b8a36959699df58cdb8b6749a7669d76c22db4ba7e112bb5e033d5c86f5c47b4deac4e5 15b9874171edd24f6ceff4cafccde1d36ebbe657a6684d82f3921dd8b663978bcfb1f5d90ece56d8 84614b278deffc26fd1c08e97d93732264b4a37fe7323d87f0252db0b8e972a7fec27bfa6adcfe8c abfb3634abdd5a705c4baf4db19b9e11bc51aa27ed763a1ca127bc7be88d24d7e3676a7ade5686d3 931796c3bdb7df7704b5dbe4daae49d0b56707e67568970b8599153ad4dad2b9ceb51a45fb6cd5ac 52a4199f86ba69f7c89eb1c92689e1ee7560b88f356ab84f88496f1e44fb99a28751abfdec937670 ea48abdda6ad2f4bdfadc35a667f326edab4326eb89816d79396b3730fddc6d565d008383d3fc9d7 eef91b56936a67d27e71306f972344b6814968d694d42c2bdb519cea5c6db4abf9edc8379783d3cc 2ce5731ba3de91af06ae2ebe0a54a5959458fd640e2dbd4b49639d3d7daeda35f4106de01e186d50 87a59fb43f968c4161b0e9b63f9d740e7f6f1b58f6d82c5de5757ddfdecedde31d1eba9dfc6decf4 5178551b9adcc91e77c4879dc9d8d04fee1ef8452bd73850d5eaf3c6570b4b58319d12ac9ba52b6d 190d43ac1b046c752a6daf3dd4cf6032d3bb9b77a2dd44f6ae8941035683e29e51956bd52a7f06af 51597bf54e65bd20212922ae0cad0eca6f9f42fac88ea6c1a8d43b457ed6d346d1b3e9bc2ea73a1d e31bc7c7468bdae84d84f6fb1a8f6c8d782e2d43850f55ab81decdd553ca98a86d168cc6c5c52a07 c1a52a5e2e10f4b315287a8f5e57b45b2ff98ed3345fff34d5e71eedab659a9b943f9ff6aaacbbdb 8b123d10483117035a5e323953b62f435f2e0ea483b431ee796963ea4c8a40093bfc92fb99b7bbe2 b2996bc5a1ffaa1f339bb3e35f4a490d50edef83c08a66cfb01a7bcd6115d9785313dd875b83f8c4 e78ae79e5ffaf901b23aaf8082e6aff398fac2f2941adc79410532af94a705db2867bb9ea3ccb9c8 53f2d17924d728642117df8d935477d640c279f1db0b88adafb396b2e4be7042ee89702a4e61e154 2ad2294c79fc686dc44163dfc03b2063c34df7eebd7f3a744dc5733bab8a6a71b5181fa6c64e9086 95239d1f55985e79a9f37373af4927fbaa8e35ef17720aa27eb65c21c78832ef8c31a5aaee187939 db0972ad095469f32a56a5fab8dc14f7a5e6406c5d93483899c85ee8a69d097f1d9f317ea0591a2f 62b91ef7d88d37dc635fcba53852dc23ce8bc16432fe6a9d7dcf6a935eb661e61b54c17c3bb2ddbf d839f9baab96243a365a7d3fac74dacc4817b8dd4093a563a8aa4e7653aef8f9a3521d83abbc2a15 df72a95182a42dc116243c9031b10d545aa4361d4938f5975fafbac0e52e0e7f3d303d7e107853ee 09e22d37da124ff623f64aece4052bacb69c792ce4a82b16724b100be9753cc5921f45e850eaf1ae 42b4961e09d759477ad7d44bda79acb45962ec6b70fa405ab8dfc1adf6d05e4375420efbe5cab53e 522c781ccb0e334da486303c8afbd1f826d2c8fc2d74dd052470fc15e107c3477a612d9ce39e7455 e5d2b666b11ff7e8b1215e0c586864ae98c84a6e8c59e40b0c7c397c7b683a1e582dda9672718a3d a06d59c6699b6a71c3d8efcb5de92e92cd2dc0f2ae90906f3bf7502f3f1dbad2eddf62edb1e627aa 46cefd329ca9f4e4d501ef4b8d29158aed05150b675addf1375ef83a53787128dcb8d15b7871caaa 9163436e90763ed09666a15d4e66a2166b30a6dc69d2f16ae9d3763a8ca08bbc77a136b96c9ec22c 9b277745a84e36379328ada7f622776806257710cbf8eeb226775499207f49e4956cde51c8fcdb42 14f6621cc8ee4e1f3c92589ddc8b9332acf67dd979523df1e0ae3ce1dc5af97cdaa747dc3379acd9 cff1b46375f67a66b350e1c9982602983ca914e8f8d525697b3ce3a94de9f48dd0a3dc1be55258d4 19903bf33827c913994e7cbae32cd1d1198e60b3898b5f16d614bfc4ec93609e8b22c1bc32f4a055 5265af3281c9fa697ecfd734eff5aea261e952e9b15aa2054a1497cdfc6b22bb84fa55a0c4c3fad1 1578b9d1e25ea8d1e580d49eb0d95a77c198dbc1865e8ab3235d0c16376a635f3f54bd04c3e46e40 a064cbb05892824765a2e3ed6d8255a92e7ecdb4a6787ffb3a600f5206d8f0b8a130b96b7eb354d1 370f8d53ec6f985cd7f2d870b3a2faac48c9edc2f646bab7c22e6f9be0fef949e6ba3fe01255777b b152647713d1dbe4fafcbd3ae970634e69b1953cd266f25778409760624ad53d7149ee2ecad73846 b6a6ed0b71ca759f44671f43f8954e107c00600a17a6a2848d70c7c0e4c7b685be037c8c6ac5d616 cd6ceeafd2ac51274a0699374bb963342ae54eed4bc940a05c696639c46f9541737b880827e0a6df cc949fcb3c6d4cd78b163cd1a49c6fe90b69674c27fc7d7eea732a6779ccfc0835e9da26ae53f828 ea90d4fa3026bafc738e5f879f352e22487a96d6dc0d5378ed83beef4d18d5aa018642850357327a f0b70994605e738bd67ce217112bb744d648fd86389b4c0941eb43ad90107cbf9090f001414d3f83 387308fdad326878a047d442bf9faf62d6f0addf6bb3b35a413ec94f2f173b5e67c2bda9fd80ad24 ce37b899768acf16d9b65c17bf65f906367a4b3df4a399e9094abc050a35834dc978c5c762acae6f 453bf302c8ba592c202e59260bc9d8930a4d35a9e60f09dac97bcdf62ccf90ef13dceb5a30ccf319 29e70f3b5fa12be78fcc0d9c0ec8de70cf30f31edc5ac8755e689276bea6c2c6b1577f6bafebe452 2e60ebadb447c95810a6d50997996c7d06c91b3d8aa89cdb04b7761be86779b44b66e3d82adac1cb 47361930413023fb4d8029340fd8a6f08dafce7b17fd9e67a64308eee7e6c5dc105952d957fa94c8 961f7c0d9a2a5e1fd23f9f25949d361e99b98110992a3c3333e6ab36cecce5fc1ecab6dc67cb75ba b2334678a2ba9909f94a7faefe9e3733b87d514ac338114f3eb6e4de0f356416b7c5904a54a54ff4 acb5870573a95132b7cb1a825d543bcfd2a53adc0fd57e4eae52a3ec78c54eb3aa535942d35b6b0f e5a4fe35337f6d41a6a03e10508b4adff537a064d654506a81568ade0c94dac425c5a1044a765b07 a51a3b0025f598809246de7e6937b5b437234ccacbc0ba7ca2de658b312e32518876c26009966c44 0b53da951601c918f4007bb9e177d15269dec59a483d183af0b5fa36b39963dbca588651078eadb4 015a74062916d314ef15404bc23145ff95e2510028aa3200e5761580f2652fc5790e50a17103a848 6029ae2640c9ce7755044029fe90c27fb9c32dfc279aa5725d3ff21a84963e8a33d5ae123d890f7c b904d68cdd1322f2a88ec6b8ccc03e9a93bd2ed2802fcd3c87579c2c640e0cb036e8f43f3f884e8a 4117a0cf53f03bccab344f9f27b524c5fc0ab04206a4a894522c7980214c3545d80518a32c535c9f 004b4f2cc050c10218062600cb066780e5f250cda063d16ccfa684aecc57f9f4f6797fe4a4dff82e f915fc2838b2e67eb3a65ad5d21c1f3e9d099adb9d8748c3e7bbf0ad326c425173650177a618001b 3836c0668b16c0a28c0fb0449e02ecd65fa5d8ec019ec51e296ad9141b1ce0395202381efeacbd38 41f829161b8093ea07e0548649b174018ed8338017e16b0abf6039774facdc8216a1e9f216291749 14122f8af8e0a69de689aeafe75b42cc3c16a831d5a64813f587f03d93ed41e6ae50ff1d2633aa01 5cddd701de7937013ea1ba292a4380afe673809fc116e01fe90c08c47f03827e150051d6e914b10a 08956ea4988d01a1b1bb147b08107a8b0744856f024278c6801007bf385d422230f3f0b625fd551e d2ea9c7aa0724b4572bf5416c621ca67e246b51234f2b64b6487a03378a8ea23a8c0d73b6939b916 200a6a1710d5ce0010ada10f88e1740888dd3d04c4beb800c4c3defd1cc4787207a488418034dc12 201b070e907e7a1d487fd106e4909da6d89c0039aa150019149514e98c9e1cb7d7804c3b1d407607 98d1d76c45579925572e69575c141e3798b5c69f37799e92df7704981e55f6c86ed15dc1f2329e65 1c7de9ff2d6bb70bfe7a8ffbe53920376efc2f03b2bffa97fafb3902aac6dd01d50972805ac104a0 4e96f8377182aef553dce39fd6c934ef8066d3b649b34905d05cdb07342f7c77bb3ba53f1564aaf2 d0025d5d54e6c21fff36b43e23d451bc6770f001f722bec58ff068a76d32e8060bff16f6ade94f25 6368ffd3a1fff11effc4d59fd3f71ff537d9bf0193a5f37f37bfab975490f6ae75c05cc920459400 e62601c0421f16b0d9890398873a05cc135c010b8f8b80cde7f95f344b12994a13194a42705d314c bd9aa0847c39e64aa87979fd224432ee3359ff53dbdf9afed17a7ffede9e92fbbfc326da0ffc4fc4 c4376af829b38023e61ae09aa607b8e43303dc7d7006dc832ea438298087871dc093fa1af0721e00 be1a52806fa3aa6a777cebb75f25573d6c05f28626241a8f8e4881df6e0154d2dae7dfb5f9d5f60d e7f82703e3df6cc7ff388eff2b67e2bf04df9fd3f78fd6fbf480d09ec54024866f209a55e6b77b5c ab5803e2fc3605e27e7203e2d3c080043fca4022abeeff964f7fe10d67e9196488ea2384c5d63d2a 2a835b8c6be115ed21d1f9203d97a7b37ade1e6fc67a7f78dac157553f281bffb2d7f6f67517d1fa 3d8979e6b95dcbd9d726512f9f0d31bb66d774bccdaf7ac513f63714c1cb95179f3eeb2cb4c73898 9b803ccfe1e9aef44f04c23ff88f7da3fe2000187c375617ecba2697cc85180ca5dfd661276e3a75 0e01526fee22aad0daaec5597b434c0d6f4d2fb8eeaa87d083e5cd45874b71878ce38082c2c5a7f3 9c2db4eb6d318fc4dbfab7755870db4536789e676b037e4d131b83a7c4c6a4420f5f5726674fec4f 7a6ce134e1aedddcf83668fc1bfecdcb1b66fde7754dcc4fa7b3425d0f41fef4dac19e0d368992cd a687597bd0abb8e29647227e36a6f422645adcdc78c842142b821c2121abcedc2cae4f936ac99c36 0bb81d1e9c82137a28defc932adb23057f7cebc8e924f35a8d8340ecec479f60f91c85aa808da0d9 f48f4b356f8f86f0f2960ce1157df56307b9fcf06f2ed9c685ddfeec889b441af7977eb1305a84d4 369c1bb7451c59afd56ee66616e769139e3d42af38fa4c7ab8078d6fed263c16cf2ef293547b4d34 08788f187d864d7a14ca1e37d2debe348cb45e79684291e9c766ecf8c832690fd60e120cd0448cfb 4d7274ed1dba50a1e77127b347dfab83ee79b85bfe414f9ec78f8f1efd3fbedeb35b5566ddd6fe2d 258a92932441328aa2981326cc628effff45e67cf6b3cf5ae7bc1f661fb33575788f2aa0aa2e3abd de69c8401aff41aa490f3a8eb9306fba3cc3a7793d0c0a0f77c2ef4ffe583a9d3a23e576180ebf7d 301f42a3c76a902c1076fdc878c4fde4d0baf436cee3def390f7abbbf7ae9fae4f7ca02e75cce53b 019b413bec25b55db73b02c3b7f987a1b6faa58613bc26d36630d2d169f3bbf0e266e81048534762 bd096d952035c8a6927a790f7013eda4607256fd0ee849079be447234e4186d09067fa914e897d64 91069ef7bc82e976f755a7dea5f6d55627a0ebfdf6a5551db5f99b13b692036ad1ea4bcd55f01a55 37c1486d1e0215f4cecdef74726feab9d9a7092de35cc342ee981fd560ce7749ad5cdf3487b53a7e f98c6afb6ef7777ad67ca98cd7a8d756a9516ffa8f51f64f14c22fad61bad76d32059303f323dd7b 1b33be76a96df8eab0c72ddce62f07a2d517ae5cf01abce540f902adf90d51bba967d16a636ee7eb 0dab80fe6e4534e035ddf1a32ad7f7913d3daa6f7c615af7683daaed5bfab6e61783b317f7fa4f8f 7d2da0ea650c886a32d1972bf759e455e43c3b49e473775feb7a323faf1e0577e415ca292f9f6d94 05f30f874ef1695220b7fb079fdaebe677dcba34a159e3d9b0e021e447ee0cf191ed92ac6f6ae762 1d3f1ec59acf1e93bfff72d6bc40389a1efbb8b9d5cbe053ab262342b3ca7fc96ea5af93c38a9c2d a59b6d8d6c7be37cd7adb313560f5f073a14307bde68956c8bcdfb367c19cdada82b3d2d57ece713 89c9e5d7df28934b655d481b3225bc2de92ece9229f770d88097cac047365258f7887ad21c4cf5b7 8f8717b79b778fbdf7bed58edccf56f9f71ca9f4cb73a22267368c3b32a3a2abc21bd1f946b1e284 eecb70a0edcbb1e775b866c331deb2a2401b59c8b5be343d717234f157ee6bec4775d2a0c14dd3e3 59edb75ad3038bd825d279ea5c7e05a5307aa45c6edf1e1e05c7968cdb8bc67c3a4c3a6dfdedd47c d9f5bdb8a536ab979e35a8f22f7f569181b77247467070becbcec5d191cec38136e1d7b6883164c3 87c56f896b458d2d66b9cc9536379347d1f4f4ac6cec0f846ef803abaac71f3fd0b96a34d6ae02be d58445fd597e3421bc2c5faaaafaeee65aead8586dd532453c12a940e90d88b435db57923b348d7c f937b3a9d79abdd06bf55ffdca6327372bf2f35173479b41cdf93ec66d275b588c6cab7d5a58ab2c d85968237f32bd73fe661213e16334f01264d07bbba09ffa2d422fe607ac765dad244d2c9eb4f213 22aae592affed637ea849e4fd5cc043a28b36afdabe4ee77aae4d855b38420b9bebcde2c62797d15 33893490f1bd5e4d4ee83dfe083e8a7cf6abe5dace6b87934565b886c62e04bb5d277779376dfbd1 f2acd5a458b330a8f2832926493742a3b15aacf573f91aebc5ddfba6ddfac44793f2c56cf9b92a15 ca4ad120d449cf6054cdec4aca2c9e6a0aac9e2aa508a3db25a4e786b207477b195fe16fc92f7608 89bae3ba1858b3ae18d8b5df3c2df9f90162e094d091ea4a7ffcdbcd59285f6bb154da555e83fac2 8576f789eda26acfc21787c0a4da46cd68f60b9e7e3e153bda3d531a6b52bdb22c0f5fad9dfa55bb 2755db8eefca9c1cbf15f379fc0d6ba5c8b9212517cd90f2a68b156522272ad2fed8b0255a5934c4 600b4622d775b742e7397f08422460fc009dabbcbc535abc7c670e894cbfbcfcc820c3ec9485dbbd 0af56d2003f4ea7582e2de0dcfceef3902db8da2897920b9be114cc6812ec8644debbb3bb73c32d6 befa8d330315ba7c42c5320a51293a32fb12ca3227d90bc5bb9c1c8f1fc93f54b3e289a92362f099 d1c2b57a1485ae8019fc6351aef183c6e2b7f62cbebfd955711cbab76286b822dcf4e8299c11c001 97e3d65bce6819ef4466f9fe5ad4d074f3649f28e46fd5df937ece4233238bb8f627464bc9f7b547 bf1694c7e8cd53a7b9a6a32c10a75a5a11f5df9156420793914cac2733a9814f22f1e4cf7622f75e 9f84ebf87c1744ecf9e11f770ae64b9684173ff97ab1586e4dd462a6fca970c65eec70b9fe74c13a 85fc852dac5b30b3f65089a9125183a992ed0d53a572bfb567f2d3ccf5ea0e8b063a0c40ed547cdc 2ae5ec676f572c6a69324627d4c5eaa35f1eafca816a886b4fc97f3547decebf967438c335f174c7 3b62d1e2478258a1a6fc60472d8b1f89df16c72ff3542c47edf491df191702ce047784cbed288675 2845660bafa1cdac5799168317cb537a775bc5747d5ece51717e2b50cdb555a39a1b7e45c5c8f141 25cbfd6cb779a1b0a6d57c67bc6bef70730dfbbcb76a27fc7f3874f9e32f7aaab1c39a4a416957e5 da01b125661c19c2ad1ebafc33889a7ce9bae916b5dc66c4cd9ccd94cb6d364b76d9f9ee59c760af ccfaa8bc99eab00333b81211f41e85459aea2a26151bb32615c0b95f600679092a07b2c33d33a490 e9f2c47d4e7bc4bd7f5f10f741f54a26fd02753a028c35dccb29537dbc1637c75eaff766b30845ba 84e86139731af494c2f3d394eb825915cfabbb2df47a1d835754d52c6a35fd67b5e2cca615b0c918 d267365977cc540ffd05bd67fa6bbafed9c734b5819f54ec0910c53e2c94bc2ce645b2d38475e27e 916a443f9c0d09b9c6ecf0d1b1fbc1d5806470c06ddd6459d39fe16a95bde0a3f5e837876ecb5632 59a809ab4ce5a30e6e76159fec8d762d5e96bf043151e162a327efbeeb867829e01541ea85563144 398d9b8ba0ccba85b7cd7842dea7f703b44dd3056e480555614a71bcbb223b7d6f4f0a56f8b35a11 0ffafe25e476368fbf359dc1c7f85cc5817faf60d392d6c3f4cf6e8d2e3cf185dac49142f3c79a8d e6236e8226e3ea095d58dab7a5aef6589db107994a66dab85975bfbdd77bc76859861ad949692395 ba2987f6964da15ffc568a61e05b5c3e97d598cd79aad00768ac53c1695a25afe56d931476d71e31 a01e63423e65e7f8b84c6cf072463d61c90ce58919d2228b2e46271cb52b9c88ac769e85547a9716 82c95a4a07b7efe3ad505bd7f102a9e7f4fc31bb1a1448c33b146addf819646f21e6ddaa3670e19c 7d4b2de6da881d2d55f8f498c8fb48ec899d46b3c1ab665ce1f2866032dee7a4d1f4bdab26938ea2 420c7ae2efa9083c39766b7886abb530a3dbe86339633145ede56289169ceb1ea95cb357049b9641 a19eab2105f27828e69b5c56cb3357d787db461cc23cac9d72123acf659f3b4dce3ee799664e82b6 abdcada35e1b95d8ff4581559f7723636f58f16674cacea1acebfeb2b4b92dc67f9079ff536f16b3 a34d95458f9c4537aa079dbce52b2afea96764cc44ce65b4f0383b48d5fed4111cc1da85ba470ef2 c78738cd370795357cfe347ee30dcc57a78f9ce42f33d9e1e986659549498442a86b43da027432f3 821565ccf5fd9981bd0e9d31b7a69b31679971669ea96d7c0ae858455b61c0f2752c99e89bec5eb5 16d61f5e2e768967972fe35a1a0e5c40238f211dc1a18af39541281951c54ce72823d54353cec755 b30c5ffa7537d72f2b5ef6b57382ac4a557bd0371e8690cecda2ccbcbb3b64e0e7f30ea248ca0214 ad93897c5480620d1fa0f89fe7a4517c730728e127af91ac05d0dc6d0492ebc5aac60ff3a89bcfe4 3226e77f6e1a08e0bd829e85a5748aba136130b8f58a465f09989d11d6a87348bbc42bbfb2b0394c 96918db72ce583362b64c7602efef205b2cd8e9ac9871d13acf47915a08ba801d025d44f449925d2 db26f2bc0134d29252a3350dd053594fe41400f4ec2d017a419e00dd6c9217b67507a03b314c64bc 4b161557d4f68ebbdf55401f9497577511def72552e322f1c6b5427e5238f7b9c2896fd1cd61bf4e 4a8b7c0587762d0b45394ccf9f4a3d25fb8e623e63375d0160ba2c03cc1b59895c6a00ab21ad44b4 21c0bab325c07a99140d61bdf23391651e607d924b646c026c40b713d9ac003674de001b51c90ba3 9b07b0566b0ab076fee42ee4296ab63a3da0957b939b5279c40799d9d32be1396d4c8b66f53c64ea 37bc4dde379d46ea556f3daa289a4cd8f3a7c559cf8e4f9094591a6152e5d715018e846580735005 e0ea0f4babcd1ec0dd5d08f04a610df056e59cc8f60bf036832532483ed1c93b89f493f775d96d22 9774df28bcdee401ee97fc44de0b80dbfecdde656aa8fe6c38a09c8b7bf752ddde1fa5765fd8f0da d29ea5c89cea2cbf1d020cdc265aa99f6af9b35874b213bfaa6510712f037c384fe4785101fec2ad d4410c9b1e20a8650710d2670408999903c26aec00e19f6e806814a144ba04209ad952227e151001 344c647800444b497e413b2327b20a00e1b96b40d4e08f798e44549bacb58cb201d59f1f5a8e99cb 491895b37f91790beb8d89b277e9a155880bf2e7ebb89ed5e6330b6c7ab8060893350031736c40ec 92a38ab80dab80cc1c1b80c40b5d40f27608c8ea6a0dc84efe04c8d07a0172b3cda780784bb389f4 3540ee90e4fdbbe11490fbe225913302c843b70cc8a59cfc8ee5770fc85100f4572d87962d488214 8f9bbd25418b2fc585f8de314c895c129f513544d7e7b09fef70ef56364b14bcb4d17eb5fd81d195 5b1d906bac09c83313fc05bf8cdd0154693004947d9a01aac9ed40d2c057401d1f00d0b08e816484 15004d27a7364dcfda8066c408d085ed03d06c8304344a98292046e301a0b1760c681ccfea2057c0 cb79ad57904fc6e42b80777865ebc8e3480ef79915861cd969be73b206d0a2530bfe1696d69472e8 7f8dc764f61fb85a13e6e9d7fca1bffe0ad0d7e800980c74034cd1cf02a672270133714a8059dfaa 80d9d8c94c73f3de0166ea7f0133a3b97f76bbeb4d01b310938f2d8e8836356c46a9f96d541c229d 0cb7de8d7ef334aa5f9ac6d80a3d6ef2d7fb770ed9df533f69a55af76f6d6959bf0c0c060d377ffd bda9e7f867f2fd053bfc17f5edcf5e803d2030e0b2430e70069906d072dd69e36fb6706f73065cdf 2d006e802889ec5a801bd6d62039e23e890cc8746b4d992f56711e1eb859263938dff862debd14da ec709785cdcefc6fa3fd9ae84f61494d29524d932f52f09c461da7b6e3d46cfc7f03bf296b4d337d 239107c5d7c106bc15f4003fc0d7892cdf801f9a34e04739fb6fa6f0d84fc92d9f4c6a7e9134bc42 85155104b043b1874980a4374150aad27d64e71376f71794a749d16953fd0afbc37affb5f6a6dfff 1f1113526a86fd5fccf74fb043ba99dc1fdcdba900916507408c0a3110e3350cc453b23e17cf7400 2416de02c989212005f52290461ff3bf31efdff08634b275fad29fdaf25b79985b38b83b47b87fdd f3f1f8c28e3be1e96e5af37884d2cb63486657873973ddec237eb1dd6de4d66ebb2fdb874d6c168f 1b3662ceeb0e4ade567db2f08a5e4d0a4a2e7a14b2d41ecacf06b7303fcdf23cd26e8d39b2e86e66 1ec2407f52107ca24ca4f2dfbb8665c1f3ab6fbef708afe5aefb6209bfb023923df353468e5f4e41 4b131176d5176b6e62fdf2bb74ae3b85d059f5f1a012bd7cbf162927d75f8645bdb9847a6a7b61be 84ee3c52b9c11c9972939997e3e6d3bd236fa6d4468fc380f09f934b635b98f0175a1cf785b03696 9eeddfae61cf77ba6bd83f5b87a53fff3b0fa1b83b49f3faf660c650bcad7fdcd3ba93279216c188 4f34a2c8dc12ea9cf30b78b0c3e6c86446cef0799b9efac9142e8c2b553e6477a638e9505a69c2c7 e68fdc8efb9c668ca5abf33f6ed97a300c95d9700885bbf9c0cae58efdc871401fd99cd9dea6b66a f63c4a5df6f0b873fc57fedd8aebdf2cd935bf9c5496da99fde5a7cd9dd7bd35c3a7fbfe945ac6d3 3040eeab4987b8ecc77d667f1abddafbdb48b96f9eff5865a760302f2fb2032bb3caf7237381a656 d9dec65d31a955b6bbaf5d7f5659d4eec481e077d8abdb4f91aab859b7f997f46af5d557b1256726 f5960c9566c16bce4c53f9b361d8cfcb9b3a26ff71c936a84907fbd063e9942b0dbfdda73e84864f 7760818c3f80a74cb78f2cb161cf43b0308d6cf50af3ae4f90ab4e72406d3b01431e3aec9939b53b 3c796df377e6d1eacbcca725bd956c302a6b48f33b73a866684ea426141dcd8685914103de8d177e e4cb2fdf6508d147cea197c6c8a692e67268e63c8d4248936453ea37fc76769f013c515ebd8da5c3 5d1f93f14e5cb7b80e1b7b72bbc3f95a9bbf8e9c565f1c79c16b386a04cab7df6a867ad069ead94e bf092d46e3c6dc0ea70d783dfa2d3cfca81a6d7c178f8ff58d7fb9d6f1d3ebfd7f240d6cca1efbc1 fdea65b25c543bfa305b1572583191ae99c8d84ebdbcf3645a5b4cf9548ad2fee40bb087ebbad5e7 a7fbd4a82c6fefcdeff8069a7ae6936fccad1cd1805739c68f2a4cf2d7efc8527d532f6a753c66cc da3e283a359f2b7935ea5af6bd402cb73cf669f7aa9761635ce5bfdd45a5af8fb715397bbdba23a7 005c15a9fc9ec575be9babeae8a4da70a0185a27b287ec798ba212a98acb900e4bffe2d394f02605 1af33ff8d4ca5547be8bd417758f68ee6afbc6f0924ca7274f2f6eef335ec0dff3d54bff9eae6ff8 f785acdcc72fb6d2d720c17dcd5e923bb220d555f388e17c57acebe8185b73a09dd9b62daa3ab4e1 78b6b45cee7cb4905bf1637a521b37f137a619fbf1a263ec27fec1f0b5cbc7f075ae9052fd7403c1 a4bbbb3eaaee83579f9c26052abdfaa6ba096a3eadd7bda0a8b6aa1dc91a55ee236f9e5c9e838d3b 323a4757cd8557e7bbdcbc1c1d89be0eb439646d8bd8e66df870c2aca8f1a02c97059cb9e980df8a c0f4045435f603da32fc925ed3e349d0d5036d3fd32e0bf8a075ecfa5713108829df373dbbdcaf95 2665999ceecb3205ee696bf6ddccebd2bab76b8bc6dc70fab5bdef25c74bf7e056ee49abbaafb064 a57db310ab8e5eb05bf6bc5a1bd8f0be115a913f8e2ce434de9a9bd6e268e2b7c5c5d8f7b60fc397 0e6f3d1e1d337aa0de60ed32bda15ac7ccd1e57b8409e5be5bd6d4d7b6565147b56d47f9c6d99912 06deef5684025dafb9d2bce789254b46fc92652d67a57cfeb94dfddb696b36bff127ac7b1ba457ed 6272c37d3757496107d2b4adf14cb5e15b5db590d8344c8f6b7ac6bedb0b0cea39ece981321ce9ec 77f95bad691d7db6d404e2b82df71bc763592e7daeea1b21dfaaea519964343511455fb41925db38 964ad6b7e0c82bcf68c915e13097b643fa2c91c8342f1eb6822c366aaf96d8e83517891c0f8379c9 7cb43bf6e1f73876233f3d4e539fb1fbe9791d7be96d7dab5a28554cd2ec6946c3a564830ebf253d 3842a67665916af9a1e3cdb27c60bbeabba18d55f563ce9469b5ba520cbcb953b2f7d9b9645beb47 a9407f33726582ffeead493b5c61a43abe54c563ff5311d942a92bb4dbe3a5c0ebf49def2d06282f 395cb9f842eebde28b6b6e12395ffbc82c3e07efea6253df899f65552a5113c71ce95d0b6f4c1b46 735fa8e8ed686669bd6431fde369cf412c974bd54e491def235bcd582f5f313e78b7b4d4f061a9b0 67a772559622197b9b3ba95ea99ec4e3ad79179bbd64ea7b81f779810fbe147f2f2b322fedda7671 249d5b45355f9e7361657ae674bef44b5164e7fd6d89b54a7e9b9d0fa95522bfb8f1d7e6d29cdeb7 fb5ab3798bdc6f159ad8c896eb1af1a4d5d0857ca65a1e198ea986cc4b55e6902f2a302d8b25a7e5 eaf22633f2a43ddc69485430ea88cd6bf84bb814d9693c133ab97dd24a31b4e7fb2a722ebeb6fcab 38aab959eefbece35cb8bc0b5c96a30cd69ad61b6cbef60c19f765c40c3a7ac2f4b6dc92e95ab218 4a64bfa4b7ba72eeb4b8f9b5e1c8dba3c787eb3f7ee8de6562ee9ff9aeded9db8df2e8baaea8fa50 b3943cbe514bc85b1565fcfae024eaf990c540452de1b2e53cfe7e241abcac709de2ebab0e8baa67 4db9f0e1af387dd0df73593bbeb1d6f9fb65f3a1fc3b04980a11700cfab86874cd61ebd461d31c51 8d6eee4031620f224f2f46205ba3934fb6c6fe9c3cbdbf71bb072e4fbf3a9e1eab32b15cd9d16b33 314e4eb6a70d64a3a94e8d65b5b4f21053f6de8122f9bbd4642106cf435110c281c00fb22bad382e 2cec22686f3d4ebf1f9aec424ec68dfcea3a64566d74ce5434754b6fe77a4cd7fcd18b3abc0f39aa b1a6698a69070ad986e22a595c4803a2c7ccb684789100feec9c7f4f48e243b1e1e1cf5e32ba3feb fd7d4b21a367ddc7c671456d0c57d6560d27fa75fde895c763b1a9d84eb3226ff75f5362304b11ae c303cf0fbc1a5b1c0f4a2c379d8822bb989734b640db0e83154b55bad62bfd6636346936bad4915a 4c2866745892e732d8936d4cbe93c55b17227ae68120249a93f161cb7370a57cef6293b9b2c234ff f6c620a6cda2b34dc1c120e43ac134d7da055a73f4ac05f556eceae7e6ca3c4c3b93d40f0dadbaaa 31cb354a958d53911aa78d21dc2441e14b9d315fccd82536596c9c49662d9d397aa7de4a34b9cb18 14737c3ae47984d5491e600171db9693a6ea76e7f82bb7d8e2c3e5f58c7d39f48385a0f67ba00cd3 b627019d53a8899aaf7e1b89d4d7027133cd0782cc71b2b0b1d616924cae4788f304eba6f9edbfbc 4e5c891d9b7756c639e74dca1f6bdc55ece7d997eb845211dbc5d0e0876ab654cc2c1a3cebf0b934 fb018767244d713d9abce49a3cc93b4395e85d9616fe1296155c591c1bd8b799eb61daa530c6a0b0 1ca1736f10a3f061f544a2219647906f99296c362bad4090793fbf7f8e66797fc4def2b4bac7e1f8 a6fd6e7de7699ee9e7fdf6344a036faa25508aed4a5f59e937479f9433ab56b75455d6bec432882b 4892a717275ba0b04e5b2cd2f5784d53016892247f647142fab20cae128a8ce92545c3a04fd541ad 6af3e74f43a27d3b40dcfe6e58d8bccfb382b72677f9035fbee6fdfb30039fac2709b70aa552aecb 77abd947ff3dcc0e4afe212b7fd11cf4ee8cc4ac7cad36b203f439a91f18e159291b4c6c9163e48f c55c85b1ca8f43cbf570e28b7cebebf2ca48d739f3b02d31f89ee1a9405d32a42056297cb4fe6258 d6da10687e1c73083abb940a5b1f310bc40971f38d32ebe7998cd9855b66759ceb06cd65f6711dee b325e3fc803e30fb7b30062abb7516ca704f2363f4caad4c4e3a4560f1f6de6071ced3991cf3aa66 72acd6adb534f4e9e69ab9d8643178559e1cb2e3d2862a76a420dbf605e976718b7a32ab60ddd24a a1699914c8ce76c6e26f47f9451d630674c7914a2540f347d822e136f0845c2f6795b24fc7d5b3a5 8de74013c2af41e543d8cecc4af138939b7c23b0d4f918140ecd0f281c212291b60a0a31d64864b5 008593f7028533cd80c23afe3db1020a1ba5eb89fdeccb5e4f6eb17e27e3489d97c044de8f98ae28 308ecfabd2c6e5ac1a6730de64ac50dc0a138981b12c62d390a791757e4fe48f750bcd49c20385b4 ea98c898fd453103cba71270c6d7143f205ad64d446d26d21f24f25a02c4334f89ec01406a0a95c8 ae0c90ba1d00c4cf4689441f8034fc22401cbc9ac86458d1b8d5d3a2fcc93175c32b6ee5349182f0 d311e4b6f2bbd814f528aab0ee933653b04f3c93dec133ce90470b0b9c2950d89cccf5ae7904d23e 0b14b8720507c87bc001347f2d01b4409889585e22613b91d718a088b24e64720128476513193229 872e924622ab0e40796b035001cd0094d8080025eb7580c299d089c8eed3e854eac7725608a212d1 5bfc45e63f233cb770a32a43b2a845dee4ae866bb9a78c3a5e532850ad2f9bebabfa6faa0ee9f601 01a85f4924591702740144801e642d11df01e863df00e813ee25a24d1399ed0006638f44da7980e5 33c5445a36c00a443f91cd1ea05f2f0730c02b00bd1e9b00bd357ea3a7b5df941edadb548ecacab6 a23fc8fc7178f6f80c64062c561b79545b0036fec52a3a1ab137a5e0974c31d78fe72c941de60980 41631460fc8904988e26dfeaa932c03aa105b0f0ee016ccbb6520e7d6d8e000ece11c0334c0cb05b e39dc833f9e4dd16019e0555803d8211c09e54f2eaf3520058dc2b03ec54ee026cf5dea581eb6543 c58fa5da445d49c5603c154ac3cc8073f2d21f5efebbe9f56a642b581e36cc827f58aab912fc1232 8ba3ca24c5e469807d650ee0accb035ce9cb0077f61ac003d801f8c86c003c9af4001e3f4340e48d 6d22d32b200a0528911a99c85b0504e2d553d689c253408049f2868c89833f99cff7ed00e0072fd6 3ec5f3432d74e1586ee2f25aeca98b7971b6fe8c183fc376c87e2768a4eef242a3465ab9525f5132 0e3912929afc44969b122072df72fa3514ac0142c27540989205885eaf0688d92e00c49e1c01126a 2c01891f0e80d4e55722e33c208d3c0748d137002941ad44861120e5e233910f034866ec02125127 89dc4f65535a3fd34d510fdf531acdc27f8dc182f5acdd9812465017cf7ed466e1a80ebcec179a99 6023de95b43640d4251310b776f2dbb059f5affb3885ab3fa7ef5ff49bf41c55184f00a5a0dbd441 ecb817400d4f0050b18a271249805a516e22b33ea0d6a51da026e7e4d5b0cb036a2ad700d57dcd01 d56bdf5427debdd32873b1effb476e853422ba88cd42fce72a4328996865bf21564d63a853d7765a 5b5ad6af983ff43965bea9edf80757a9c775fc7fc44e707340773b3b40efa0076032c9d9c214efbf 74e1ba0618ed5c078c5e09016364d3385d461c170023e96a22df0030f4640d18a6f852d13c9c91b9 a6f2e40d4c8d196660ac09cd6dce109f0f8739e00c024016d7de5f53745ad8afa63f45fc8fe7f867 374ebf26f5d2a60cf33fc06fca5aade10bb003380fd8c7ecb7995cc9019c71e8024e525789dc5f80 a3030a700c6f03ae701e020e69c580430b50e93066e1d4bcfd5b11ec22e1428ef8d22e4ddd864b65 6f0cea4ca9f5c714fd9fb8f75f6bef1fe6fcdbe02e0d164ee1ea9f7489ff09164ea31d92e9576aed fd837b0d09f0345107bcbf0f01df480602de7ec12099e8c88077cb4dc0abef08f0e5fe0bf01a814b 3d1329a41fa50583bee3f97729cef704659da95ed17fba2ff56827edf4afb537b51da7c5a4e0f9df 8889ff1bf34de384ff4df2fd97f57e92ebacd85cae801855de405c614c225be7cfee71fe18881bf6 02c4705300e254f9bfe421a050223ef9fc1aa1fc982323e31e1161f58e34f6ad6bfddee99e2f6575 709222303c7ebdedf830675a937f62116ebfc9ed2636d6d30dbb9ccdd71d64b85cf589fa2a7a35ea bb4839d7e3a576afde16e6db79cfa3f2a830db987b6e86af92e5948f9d2761ecb71effe6204c2ead 5d612cbd24e83fea4cbe26f5a1de113fff737a1e73a7bbde46e251a14e1d43bc29eca322256d62dd 17d7fc9afeed1f965722257e9497da75ab2fe6e2ca5ac0c3a93377be83ca6ca3b7bc19be68f853bf d0688571b5d10bd97d779266a9d2e3e5f8deda1ec6d23dff1e8de41a31fc4e80f1cfae61ceed7fed 1af6f3cbfeefc806ec7d395f4ab3e818a29b704b8db7e19a8f5acb68449637cb902dc78bb950bea7 48f543be661e8480a90f7fb36180c6f0e452db2313fe1861e37b734f8da54bc48c4642c88d944724 0ec3d2bc348426736d60411ba71f5971bd8facbedd9e87f3f31ebe9f5dba3e4315bad4a5e074e2ce eaf7d7740231bbf9575ec3effdfb679fab11fc9afc491af841b5a4df69754a2d811db29b6c7dc21f b2edf1bdc18dc6d2599e0fbf7d341a4263b01958e0bdef47c6f5f88f55f67a4b5daac8e3d9c3b7e0 fb7fb8657358fbd229d0ed8e50925af741c568499f412d18698f61a066bd433374d0425347be7653 47878326b46dff911b1e540e47a8e6f6d307ceffe1697f8cb2ca15c90da1214e0ee090e2fbc8022f f5bc026bf4f0b559edfab8d3e804b4d26a5f5a52b7dd296ac3d6bda74c5a7d499bb5a497b90c46aa b20a5460ee9aa161c64d3d57bf35e64ef3dd8037fd6c8a54bd98f0912324d43d56b3ebf865d7aded bb8d63cd976cb246bd81991a645349b7354b3974fa587cea98fce33fddbbdeb11390fab5cd5ff46f ab2fd4f2c16bd022825169caa571ba9385d4d4a185da985b8b5fd2c0caf6a3caa2e2bbd8aa56dfd4 178dba47ad5a753c3ef46afbe03aaa51d7c7cc8b7bf9f53f4903f2a32a806eaed2370e74458655c3 1db9fbae3baaacafae8a3bbf6d765c95c89453f9f3f0fecf2f398406f5670a79ff186447722e6c42 d359d8b072b7adef22b7537de33def758fc4416ddfc473359fe5d11a75d1282f1014d6631fa650ed 9414a9ca7f34e54fbe80a357fa5add765ff37ad51d59bd86f35d0d3b4e58d98d1d68f78cecb95f3c d9f0a9f6b5a2d693b2dce4ca6a210f7a686e86d023910e9a48cca6ddfd2f3b4d0db229984ce3747f f874effb0d2f6e799deaa5174caa1d691e55eea36857e9ab97734506df873b32be1f5785f39013ba 59d8d1d13ce2405b82b02d324fd9f091e0aca829481672115473d3716d137ff835633f887eab3583 fadca77a1c4a079dcbf63eda6549315ac7593b5ac79d2e340123ae893432137ed786538a9672e894 f0fe2f7cda576cd31de9a2ed7c17e59aa3176a1d075a0f46b6858f7f10d286f7ab9515f9879d859c 0eb1b9695daf267e3bdc8d7deffa367ce99331a8770ed683720ed5b90c49691db3c86b026cabe5be db72ca327a6aa9a37a7ea2aa947f50bea72ca484ad91a0e8bcd650a0fb719622d547f1300c85d93b 391ec126a5e57faae49f03db55a14fd981566dd986779268b99492b448e0242d72add70c5f6cfec5 a7f17039d6d9ef6caa5dc2f55213b2cb75f9bed8edcb7dfb9c26c3bed6c7ab3aaa3e5fca77ffcd28 61834214e8cc53a579bb2e95e0fbcc92a3412e90918f31953693d33959b3570a129103a544a29644 c078989e3c696bfac8fad2aa766aa78a0b08d2b0f3c5e6efa96f73f3420463bf193106f5b0683d28 b9bc769904aa26401db3dcb7fa15f5b5ead7d551250c94ef2eec2a613d1a2a3ab54f2e74c1665eb2 b8cbba045fbf07d995a08bb419212fc953d59cb89ffac9dcb416ff92c885f8c5568460d4ebf157ac b0e53bbbc197172c8c293ef22bb7f82810edf40c6a86ea7254f3fba3c07d0f0f9e6d9730cb24a9aa 9a8c31b1a0756d8f2d0fb0075e96eb5b421d05034af96e9365964e4e7f7f4d69de9c6a254b393972 149e3c1925ae0d69f3cc76246f480cc503c24d45ff62ad84931e1c056ed1bff1d7c603e2852f4d14 079e27154bc2bdc28d974a9fcb20cb2d3bbd9a19d6a89d393627b5aa89a49146e9cd9c469eac0fab d771d0728ccdb66eeedaa4abb783b6517e1e5e257552f57865463d2825875690d2c210d152bec990 b2fb3504a9962d25bd74a8196283a939c269d2f08456751af0d7fdb4cb77fba7f16f89fbccbf17c5 c15938709f76f0e034fc906367833cc59ab6af32cbf5c66310811ad2ebc7ec40e30a0551bbef5aa0 f6a0e127724daed593d6b2be759c516588f5bb36426cff70684d9a3a86aaad3769a34579ba28afaf 0b4ac6730626d5d95c413c6a4f4c64fc2723b40b98c4dfda458d97a0925d1c5aa56a51a16b4d6e32 6e753928d31db1b3dd76c1c2d263c74479eec620ad4a86f632539cc6f7a84cf9b2f77b42928cc7fb 3e1968da8eb8ec2f19a2633b45a2e39035e2b29c0e83d1de5ed61aa1367627bd4e2f45e63aff7a56 d4ef513214f83e2ac95e315b94a8638b12d9b0800aedfd12e6eff4a4507c616dbca8f4d6bf3becdc e4b59639c83d6aacb93d5a4c243e3dc679e59af4c6257bb4c71921b59fb6d6945f3b9cc9f895fd90 416427070a3ae609a1072cfc61065d5c8e5e6bec5d697fb1318eb389dc3c6c9c157b4dfd2dfefcd0 5e5baf4e1d33727b26f5193735593d5694f905d6e5cddb9525dab81685cbd74e86d4fe0d2dbe566d 980beb78868362b6c0c2579e625c512ad29ba526d304a29529bfe35814ad0f6be429bb6e915ce3f6 a3eac455211784b0310ef8a3337de2a5ec278fbd8f76112bb353034b66bb6dd43046119a83d917b2 88f634b218375d34fbc9751b76abb4aaf616da34b5981b1c5f6b94d5e9b2525af12f5d3a945559b8 3ae9e8c90f08912aaaab35ca652536cbe69d578641bd7d96261a5181f2cf3145b6a03d475cad5822 ba0ca411c28777f14155f3f112deec62effb6e8a95adcf06cb30e51b6a4c865934e7e518c43e94cb 48a1b1fd19c70a95af3c2f60e1e591df892e91df496c327ef6966dbf5ab4b79551819f5a5e8debe9 42df68a8466e5891b7fd58174f334ee61f9b2e572c3339925df49a0853e1a06c1ad4473105f34316 713d4b74fbc16f90c607638fc63e58c063e35da860e5dec54267f0d7438d13df469665678c389961 5458d7cfa7425596407e376ee2f97a35a3c0c7bd51839bfe35cc9dcfd625c7f3c9048917465aeebc e51b754a62366e58c57fbbfca66e784d7e8b0dc556aaae7438859ad08d8054fc10358ecb158f04b3 7a39084dea972cd93a3b5fa207516f7c4813196c5226f2e8cc240834170b1cb20c74094180a715d6 d3be53c0c9c8cfef5ed06f90ce532a358363b47980d9eeee95e387af7cb68f2ac5acb45ddbd08862 7a9012af76996fab9c8194352125d271d2b41b677e4766468b05bd72b94c364a955073c573b1a3f1 43e42a15330d83630bbde58f40d175c016c87323cae2cf3dffc53eafef1b9d6de72f64f95c640a1b 36b96ee39d079daf3f1ec53cb5241438403833d711846af63e509b59b9100ca1d77a1f41aa009f32 c9b40164f4524466b288a481f966fddb050be473e42291d30be4e12e032c9930aaf76b669bcc742e 53bddbbef652b42fd7b3bc2bdc7a9e569ce80b915db64996c15e43826ad62884e8adc31caef4d50c 3a47e337e2e63acffc7ee37de16060ff328873825642b3fdbd4d436fda2e42a353578140716064f4 d9d6cb642dd006d6490841bed5d9817c1b4aca99f471900f2935915d00f25d3b02f91efa4964cb82 bcaf5a9571f6bcb548344ac698f9eaf7288952581e7d892d400eff7ce91a375b0c4416c10b2c5d1f 0d49f24252283ec23b390c5ad119249a5ebef9fdd77ae6ba05f084c6f5c337330d8e70267b3d63c0 16cf14c8bf327c22a53228c0e30a28e4413b25b7793d04053a3e8002a37c12894950601d0d14b85c 3b91d51614303b030a789e4f645e750d66b935ce627f9aa27d797799f9225fb93bc5af2a681c6c0f 24a67a278b29d527fa0a94def108e7fd3c9a2f73d902b1dc02b898955fd007bbbe3239b7fb0685ee 24a9a49b945a98916c22b69cc8d848e45d0385b9dc0585fd7c0e0a07fc87a5fb00148e389dc82c79 4b5c4e5e3d65d3057b61b54864ed261fdf200d7b8db7b77acff1a6caf26677a558e8f9fcab3e4f7a 6d4a68ac5b18c8b42fe78a64a7dba671358470d4cef79142cd4c2e7aadcd34037ddad9a4c4d7e205 1074fc0508f74c9d904859a40052690989ac5480b4513711ab99c87c08900e1c2552bf24f2ca0264 da2e0264863900e94d0700e95b31401a289248ac01c4a977cce34ada94bfba3c2d79b0d8153b76f5 c7a1ff227383d053233cc5051f1e7f0fab0cba98df89422d998cc26df60d439f4f196460f3fa0648 744aaa3c159212df650ca0f9310750fa2a03b4441b00b57d0fa08db80350079924524f791aea9cef 000d9c0240ddb300d04aad0ad02a9abca1babf02540f08801a920550f13332f819b2512d261bca34 877585fed96914f551e8b22b9233697adf5088c7f52362c6c6e10abbd68684db91fa633650c87473 20ea931980ca2404d0be010374de4daadc6f69803e000fb0ac5a0618d17700a6bf1a00ab29834486 8b443e478099e577223b0c6056b994c8b50e303b9801cce11f00535e740a88b9590560453bd454e7 b2296db153285e6bb9de9fec986c147bc9b488b0c8eebb56c633c79b8ce0365f843b524843e1e281 fe2decde4500a69d0880551fe45feabb118a00bb7872ea20463606c039bc0670b7de0178b09b007c cc6c12695d13f96401de36e8444e1ac03b5e0be035284a64f901b8592d02dc2ad6012e9fe6aa9ded afe586b99d0a8f67a6cf8f27bd761a1cf3f375b6f5bc834f6eb68e78c2ac0477f6100f1939810218 bfc5ff378c4e996f7324fde3428e1580bf201d109492cccaa5b60f88fa670088b9b1f89338413c88 57225d2405c48737ff4f0cf2b7078868b803c44acb0262929112d90680e8e8cbd2aee06fa4d6eb30 179e4669cc2dfd6a876e2e439f50f3ef0a8abaa20977ab5525b32483e2dfc29c8e9896937e4d8aa0 53e69b1a9053b89a3a7d53909982df36d906e4ba3602e4e7b10614ab5f00d5bc428072053291b902 a832554b643901145f3e034ac8a18022a6654091562705c430b4958f936027f17910f15a530c19bf a8f588b71034d18ab9f5d29493cc72fb2afd05e56961a9f1f87f90ea7f7a8effa5be87439a32f207 fc92e80cd086b705f40c7a0226d32900864778c0e075133044a60d986c2b024c8e7e01fa79a0011d 7b36a04fd408d0abd9e18f79fb3526b61c2a90b3341d1c2bb4f5569e371ab53454bb55fda58768d6 5f46fe2feefd8fbce3345bf8df8deefe9f4e5f79f201ec402101fb382b80236be9ad082ef71803f6 e51d017bcae700bb9a4b809d980dc06e728b44ba57a933795dd35d35e9d6b9b0c44d031f17d8b7d0 85ac0759ff4bc9d3a4e87f59efff5fc4c4ff93f9fe8b59536b6f50604132cf35ffba7a6d780df8d2 f00d7896a7018f9cede4cad11882e22dffcb141efc0cfe69fe364b5eae47e2eb67d6887f4166d91c bbedfeedd0b4e57eedf5278de3d7407fbccfff3a8dffa3887f59ef3f21c2ff5a7b77dbd2df24873f a4b75f0162b31e02f155be01313a22405cb5ca409cd06d20b6376b207ae607887a4cfc37e6fd1bde f00c73c9dad5dc58bff3e68e3464e3563dbbde2596b9c6999f3e8278848cdb47ed50e9ee23bed0dd eed56167131bd5eefae248bf6404a4bf920ef028524ef7c9322cdee78bb9b48f16f028decd91697c 9ee1cbd767eaa31c1a06a4ffcbb69b5c02aa35e1afcff84f0a425f32b3a94176082d9acfffa8f28f 55769f54736e7fc5573cca7fc151dbddf30738d8e3bbeab343ad3bf92b19bdea037a197236bb988b 62310d651832c21c0973d2cc833ea5e9debaa9533fff31c2b8f2b4437677ab4c3ad4d31fdf834f7b f4ea7e8623e5852d86a16a9f86d0740d0d2cd890fed935ec12ffbb61d8bf59b2a91f3adc7f52867a 92e6d474b7e1a7cdd5ddd93522e5b0ec2fa1f6249c4772733147c6c3dd0c9fb5e33076cdcbe45293 ee13fec8bfc6d205f98c5e1d1a1a290f34370c4bd9fc50fba0d860ae6589013ccffeeee2f6dd7c96 ef6d2af9520fdfd166d7a7d45a270e3afd0e7bbdacda975ef3dbe6df9a922c98b15e228d45abafd9 f3d4259b064ba479087f92067edc2f35a14ea985cd4ef87d511e4ba7e2efd6f768542c5786df9edd 188652b73798ab8d513f32ecb0b771c4590fdf308bd42adba50eccffb6ca5edae2adcddf8bcf565f 2e7e5bd2dbc907a3729d687e6743bea9c347bd31af908d06bcebce52a4eaeb1fdf6564c57759a8fd c728fbfbdf851dec567fb6e24a370c4bb7e2fa31d481f9511ebd8d857fbbfb8a8074a99d4a7582e4 62ddbe049ef2cf7e5c7db7253d3b5e3052023f45aaa1d7fcd72ddb98dbfeb061153aa11f553b0bdf c5c3a43bfdf9b18e9ff6f7dabe9dcdd4a8bb807b811c48d5cbf8fedb8f6b38af0ad95eae2ae40ca5 725fb6ac5436ecf4e8fec3a15327e27f870c18abe6775c899b7aa6f568cccd216858f03adf80a31b e1bbe88df99334e0914fa9b66fde949acf3e352fee9c0d2f109e8ec73e335eb553faf8d5642df31b 6ffe091b28cfdd915ddb39dff5f8e6845e0e76a0835db4e7cdac675bec656d5bdc06b6ada229daf0 4d12d29e4e4bec79c95999569742aa06bc64bbf54d75dfaaedfdfda0469dbe3f07b117f0f0d663ef eca97ae92bf76a47763e95fbd8852afdb29f775f33177147a64fb82a1cd0ce37ea171d1ded880eb4 1d29b64586869dac402b96cb5e1a1672c507a6272a0b633f6cc786af42503257e9fd28871ecfca0d 3db0b84322a7ac1ed8289602fd3ff8f47fdca7757cdf6978ecf55149dda715e95b73dd91de6d38df c5b4e784f661e2e885f7d28136f9ad3df788a30d1fd0b31535e8df8d150b39132f73d3a6bfa6c78b 5963df2fe60d5f2ee37a3c2ed37a50ae09da65d62d6b1df35029dfa37750ee57f4a9fada854775e4 735955a52fb2aa32e781aab28d83f2bd5c3f43edd17ba508ad094d9b83343083d6ebff8d4f15e91f 7c5a754c8febd58d7d77d8327c71d937a8e76eacc7c3f34c0f9467a45dc2db46ebe8cfbd26e432a7 72dfc9dcca325278a9af0d0fa92a21fd9c904ad830294567865269dede99254b209b7234a885b2ab 80b38c7c07888c024b9736336a94486fd3db98d12905fa750f0f9b69898e9e9f94aca88ef3a6c7ce 19c3172c520f4a02ad5d26423135c36a9e5abecffd3ff8b42ce7c3bafa5a6d5aaa8a455de5bbdb0c 94b01e4f1428decf4af3e01295e0eb652b473de824232fe4216d46724622808b8afb69c48b74ee6b 0871643685c03dcf050eabfd9e26e22f7b924ee451e12f07ab97c2e8b4cfbda098ad3adff9594d9a 0f2a1abeee51da65f540cbf7590b2ecbb0915347ae8128dfad41283a592b2ad0b12b972cb65b2ec1 97b129479d852b238fe9ef492f6933583524fcb36889fbc9a12ffada6322c4f3f742086c62c75fd6 c285ef54fd6ff1be9f22c57e232f70afb36e71a376dce554c1d9b2df279449642db2e1887452189d de784881be6db586bfd1d3f0bf175eeb16044a7dd717a832458bb9d2fcb1fa96ac52eb2347ed7a46 46eeed82e4c96d5cc2df1346f4cb93a46d324b5908cc992a70f0dae03beedae105f4e815efdb4f50 94a94c8f7bc54cc8a9aafdcbb361c3ede0c2eabd0cc4cc5f258ab1a255995e6144837677e19c467d fe4ea3d685a0361fbe1428afe72439b1859a0bf492656e7b4d55bb3ee6bc3abea3b49253bb98bc3a 6672d2763d04e2c12f3c44fff54943e768f59c4c636b31c45f8e719e17943b5eec8717aa28d7a022 373a4212a70ea932071cd361f54d7224653bc33663677763267fc4d67445312ed47633c950a498fc 6b2c2b1ac9388f80686dad2551acbd7e5f83df609fc46fd457493bbe7acda035277b106da309f7f4 f2d0de488a69d19c5cadb50889427205e1dcaf67f85babf0e645a379293e96e6b538509c27f7eef8 1057ce760aecd40e30367beed3cc62b2f9b9549902b429d1abfdd7a4b11252a5768cd3a6c8f52c24 9bda674b9c0fc51bc1979a59fcf6b931b8e4950d6c7858b430a529afd0c9257aa1936bc025f235fc a849752b8f7abe6ead19d1d579aeaaab9373f45bad9510f7cd4a54b38c0bedfb2ecf4b2d2553546a b7373719b6ef5c66c39e59334bdd992503bf98c284cbd2555c45a95ddfa2a8baec17c9e3aa2393cd d6c6202e997b95e07d38c0efb236c2a5553bc246c5e3cfcb812977368386569b42a1d55945e6ed4a 1381efd745216ab88f82cba274216acef5ba17425d773c7cd4cddda458d1a4aea62be63d90647c7b 62854bc8e0bcf4eec1dc774665d8b932793111c15e19c44ab7aaa4abe3e844d567e18d8ce1cd9764 83538e685f4f08c1cff234de87f3455c8a65051ba9ae85a9e8dc47c3eea587ea263b47e671fd88e4 d5dda7b0cab07801ad0f95bc77cad4f344ab3b830f389a74fc7efbcb4f830f8455ae35f06fcf99b9 6860c41056292b4f5e2fb9b22b4934b962f947b68017d5721d4e4d128cab579fb4975c7328bf18c6 6420e97ba2a30a317e9fca37ec75d4bf98ca1a59f4fbed62a8ee8d7f83349a15ae12b2c811069277 b84a61c506ed021a4693fcb686eff2c4b3fa841bca06c9b56ab090bd1eab6eb61b644659116d1ca1 c7ae90cb8a99299f46c8d8cb2d08f48b95a9a8ba82ebb2375625916b8ed934fe23a980d367269cae 84686fa13d29ba72bf1249b131fee0e83df616de7b0cd8ef339aed645f88f5c80124bf100b854aa1 4414d073a398af6963254fe20f076e0c8866ae35a1fbd91bee2db3623ffaf50df42c1119a834f1e9 cc477be99949b6d9c964eccf3a9351ba9f4c795462aae2fb35b436c435d0eed1ada258ed9726f937 4214844d8d2982f10e636d5882d3b51345aff10771adcf2ff840d5630c1caebbb4d13ee30d52c0c7 c702264f6ff9da78f5818fd8310b37761f34d77a7da8ecad9ce3b3e2ae58868692ed42a5d738c84c dcc724a361f20ecc7aa31730e53c0672ef8e02721f3e00b9f379057297c61be4b6d8af6f2a4a391e 9afbc63628bfd15da5e4de634d6c4959912f1106c3656f4394a9a8488e3a2cda5fb288430ffc11f7 2f58b9c6c5c8e2b3da15d67c779bafe78d1dcc664a71b6e76237e8b925dfd0b0ab65339fa7f9c30f 99c972c86420642602b37333002c9235001be32180cdfc3a91fe13c0be4024126b00b6ab6d003be4 36911d00b06a732e549f0f8df83c0dcaa036abc83539d284eef02116cb3643b305a9f98390742dfc e6c873a1fa25ba7be8899746e10d358e688c14d687437e376fed72970cb98546c8719f0989e305cc 99e51dfc7fec9d5983a2c816ad7f4b0832098a38cfb3e23c0b8220c82488a2a82ff7d75f20b3abba eb74f7a93a55759f6e3dac2c954c163b4288f8d8ec40f6e73740040409a49e048816f844f458e0f3 3a9c04e27000b9557580c6bc57c4a1a1710aa0301e6c703fac00f2e85b00b1e370206ab1a370cb5d 6305cd671579b7e817273c57cf6d5d359f699e303addb70744ea9cf7e21187201f63c84fd409cec3 bb5dea829a907a4618be197601b8f878e9b146e3680234bdb60339df015a494181b4f040f66980f6 62e5406a9d40c42940e77488a5390ba00bf21d88440374596f037415df0174283b001df583df6d13 b536abf7c2db44f532dd997dd0fdeca157cbeef37c9ee9265e346dcc068954faee21d46d3f8e7d50 7da430f63052405d6456126db8d44a1a31e17d0e62b3dd053ee58b0bd073fa05d0c7080158fc4401 0c894533690c2957019612fb00a3e14520ec0160a5b703b0f20c065805cf028cd9f7009669ee0196 80af8158498041e376d339e677d5432c1872252da6977b28996ae6b09de7d327e395a6539901995c 6c3d347a5cc1eb4184547dbf316d377e200e8cdee092d1b763622f6e028c3a19006bc62f001b16ee 005bad2180e98f04c06ee934c0639352207a0be010310a84dd04e22a0087ebd7406c04e0f1412102 c4083604982f07c7e0f41e00bb6418809d7cb65110d05d053bdf2685cb0be965df19bac628d8a490 26472f2635bd0e28aa78b1b144a3d48be344f30a10876df870392b5f634dae1c8443c46c808372f4 00334e0cef00af9e00c07baf60eff37212e02a9f07b873af01fc55ea07b29903fce2f200772b3ac0 dfc603e0d72211c8b90cf0db640a70930a8e41b2df0097b9e07777dd69ad5ed2b7a591769c44cb88 26bc5e165aa135063f4d8ab439c6b2c97cb2952441ce26f07ea284a069468dc1e513eac7e2f6fc06 f04c3f90191feccb765e00f75f001074160144a94300622ca701b145cb80b0da2d9080cd71c4a13f b290bb5220c619107ee11d889102c4b319d6c5b82f0071e17440680d1810225901c4ea34ab746f93 4d91b9af275f79394360f2272fe72b759ac0688d44d30a11768188960325e53c3fbcfd09469b13ec 330b99941320517fd220c1d2399010c6559070bd3e2089ca1c90993d0fc86cec1448709864e21c07 2454ce0462b501098fa352c78907790689b31d2620ef1b20a1d6d625d3ef6cf3f7f4749a15117b90 698b8d363d094eec54b979ce135d38c7a0d3f59282de5b1505bd71fad3d887a7084647c0d70d7a13 8937a2efcd4716720459a75219900ad402a45f0fcb4e186b408dcb32a036e2195013f819c82401a8 ceb304a8ee38d8a48a0b819cae80ca0e484091e97620d75d31138cdc72cfd3629e15db484807d3fa b9da4d169c493d81304609b517781606059afa3328d7a8f427878e10f4c3abfe39f1f81bea1ba1cc 68e5bb4573079297bb16215586f5027182ff11f9344801a90192b7dc1c248db30a92e6f009927292 01c9add507c929bb2facf4261f5533cf603c3e4d2d96993e091d862d8c9eec2a30b86e321f41fb88 5360ec6b3e74847bbfa61d47458fa38ce38fd5eefe83f9fec17ac7616a2ff306e9e088411a4a9500 eded0680b692c126b27c06f4ae8944b51fba41a4e9de660ce85a4e8e1600cd1ef6b71d6d37dff3e0 57a9219e04e50e5c7b3295cf368c82f695f57ecd39fe5a53f82fc037dacd37cc374af28d16937b16 43cc6a4380b1d659908927db80796c1780f1332a6014e90118be9102cc1e0a3e58ec36806924f47c 45d71586a24f7cb2f4f65644620347e5db2b8b47f7337261e19088d6ff89a67ead28fcad892f49c6 dfac23f70d66ad18e447d5de4e09e44af909c8b5ed03c8a52b1790c3bc6003775406593d884876c5 cb20bbcedcb2e8543ca56eb1a398e876cc2de289d60ce8fd43ffa3353f0a6044943eca398e607860 274aed8dcc7cf511e519873525fe5447f82bee8d4af78659bd1f151ca2c5d9a25abd5366058ac5e0 bc5c448c3828a8851c28f05716140651613350c8bd2fa0408de3ff407a33c803eaa2494fa140f636 70eee5ebe8766fba53ffde7736f1157b7eb385a1d5721e2363e04f477a4acc8d4e6ee73d56bd8139 51fc3117564e522ace6671acdfd895dcf2ab1ba907f2fc416b96c2a208555db0d9b6bb772773b0cf 858b066ef21ccbf9bba9c1715504ec84c6f11995915db329c1fe8fd5cd124fd79dde6f6112b9b381 f69625a5521743cbbb9e6e96ddb766d7af88ba497490e37b96c0e4d6ddc0a5ee6b9f38b0d09412cd f62025a6d42e23cc12f57011b14a5819a155e1bd59bdce17aff536577954063ba1d29ded20611d2d 1d86c84775d3c3316fadb1477a4d9eb7d395396b1b11df5d32f7fb2902a95f8bf3460cf55cd767db 88ff1ddfe3775855d61b4a3867871511ecad388e7b61450457d92f294d8b8a32a4258be3724b7bf7 deccdc5dfdc9de76103f7e6cdbb15e109156076c7ac830bed67a437c4dea436a354e4e98e58c9914 17ee926f2e72f7eb78bea9e4f7f3e2dbbdceb8a694995591dd7cfa562efcb441ec77d14a4bc9fafa 63d5b0a81e4268305af3285aec8af7462384e3325d72577f74b35ba932ad6c5bef437b8b0897e106 978de99a3c1d16ab94b9582d67f474fb515576991d85556507d27c531c85cfdf7c2d2c3b7d8b0b77 da88af1f13a92b411344b313e35e12c98db469ab3d6299c76668ae7877382ef2b9e1b8d41e45c2da 3ccd46e9c6118716c77099e2fc1919fb6329aed9394a544e66ece0cff79e8b9c3b8fcf8bf709112d c935e3ca7a7efadeebe56903526b7f972a3bc60d6b3a6253fa6244dace6638ce383c6bafae123b2b 12c6c0e5f2d7411e0c6311526daac99eaf50b51ed7f3b6bd6ae2f6ecbecd433e905b35926f6ab47e 141908e9e4f4cd9df989d41c6cc74a77208e716da78f58eae08c48eb7c1f91673c361c333812956c 5d2238cbdc2172e06e717a907b414cdfdb23b9fea64e967abe84577b5c3bd5ec55d14abf2bf4ebe3 2e6474971d69b4173a887dd6dabd6cf2dec68379504bdb941b2db64ced5be49b0081707424e10282 212d8fc2b87027ba1a41de2869f2a3be40c85007b987cef63795fba21f5c9cf99e2f96e41ed76c6b bd6a7c7ceebe8f9beb1f855a37cf8ec48ab10e626de1b63211b1762f2d273e90aa45b7d89c936b9a 9b6bb9392ea3ad86bdcf0c1b19a8bdaabb9276ace731f25af34e4aa2b66117ddda66b8526b9b510d 04328c7fe0d32fa9a711e11d2c0b72b7c735b07a4427c36458a93f0a19eaa2d1ee850bb6e2b63169 e34e7cddd2e649bec566b387a6b94e1f9be362416bd85cda68ccaa05bb9101b56b7dd92cdfebf978 f355f38ed378ad842f882ac7eee86a95ba952bc214eb541a4c3f2aa4292d75a9dc2e546f65c447e9 32f22467815cb432f22a7b11328f285ae43242d0dfe2d3d4bd4435ec6d23dd605e8d30f57459ab2f eb52a79e87b441cd93dcf0b256dbb4fd45d557bd7595ebfbbb6a958484cadbc4e54a8346d40ae4a4 cc723b9b72cac8adfe28f58abd5851e3964491adbab98279209b85716b312bd02876ccdbaafcccdb a76d213f637b8b40fc63d4e05145e523b38a928d23972d6d6a3281379aac2f6b2a56dbb4faf1aaaf 94e3d52a51c02ac2b0fc677c2a4df95ab9cdc8ad32e2eabd522fafb225fcae8f8b6c599d15c997b3 2e98fb271fd141e879c8db32a5e53358dec9b9a7f13397274534eb597026bb99b0ad6c29fd5e657c 776d64b855319ee1d6d96620fa2282d151ba71d4dc11e18d2c46ee2a6fedfaae40d62a5cc4a1dc4e 37efa55e2efd28e15ee15d644b75a4483e078982c94f5385716d972dd0b143313f6bedcaf90c72a8 e75ce5d4cee589533febe99771b694bc2d32be8d71c1542d2d33ef6bdf621a05fec1407e0c4b4b5c 27bc15916e57afbd341a63395a9148975664241dc8baf9c124a36886403f02e535ef260691bb6250 192dcf9f45cd8bdf0aa6b2b60b34a898f959b3e2e433f1d62db7ec0e5eb93c3e83b29eb6096704d9 0d2b2432bec52533dc446298f745ca33c2422b338d9cd74c4b1bbf976e97c909adec736bba571f1d 529a7430536c077ba6286c94488e07977292a63ae3249da414ca3e2b4fcab6db74f425ef17dfe570 4daf76af95ca3732936aaa5af5577819b56e5091f20a7edee1ceb7dc2a593c67ef654fcf9606dc29 e39b632d53a55993793bdc956964377706ba49efb4b4d6e134e2ab28ad7056822680954a69e23397 625b4488bb93a65dea24c7dc72423984b9a36637e6445e8ba31bb942de58e2deafe713dbc4659028 494d9e78b6af36f12c4f908f86af52f17a8b9a41f9da76407de4171787a72394bfd0c1c0a9b0dadf 322f1c0f2f04991824e94c936e2869b9461cd36df1a9a4d122d0e9de0cd829cd7dba2956241e492b 4ebc93e373214e39951a41cdf1799acab87a895c359e2db290ac4e12dbdd7247bc70f744d486a91b 2e3e96281eaff82120c68e6034c03a87f81ec38ac333aa3e63306bcf29b60bd3e75a937ebf0bd53d 44a74ba7613f51980ece486e3d4fbf33fbe9e8c6b4f28893eed282490f621935a53f643135ec6e0f 29ca9dcbc9499e372867bb0d8f86ca62e2955c0dc40759c85d6289078ce189f2249d225ee56191a8 bdac0e7e5812531c811b1cd6ed6c4e18ce207774b06c638809db4524d5ad0ce34c63cdc36e1c3bc3 97c7215ce652ef7550dea8d76f941d7281ca614c3145e3d121f3b9f12e9eadac5e2fe6f01e7869bc 64db3499658de4d987d524c3a832b568f445f2b62d1fc8c2257b4cecc2a7ea5e52e742ecc7ab0711 7376003ff0670c4712af1476f4b2050c6ff7c39e860e1c7e88927b788b8cc9aa12679cb6032f6b3a 0479129586366da10d155fe42ae6eff75acc17eaa0bf398abd762276a8d78bf0bb5c3e3a3053b099 6c2257540770a6feb8bcd2dd7bea4a93bc6227ed623e4c1ca3166f4b21d7f7fe21e197b302f1cede 78a2bebe88788b338e98022e06d6353017d58a691f65912e8c92f69e44c6d573363e6b9ecbb06bc7 3b70be5a9e421b7dc5c79e49478f717ee3190355990462ac14d617008db5360770a9a20098bef93d 9ee1fbade4c168d52a1db75cd2327726ef2e13896c355187993673787e20683ac6d9d4b20a6be4a6 212b89ca2b2b128dd88dc7dbc87a8bf532230e650b2d01318feb703888d0ccfe1ccf34f65778291f 7c383ff16068f3c649a8a4b572319e9ad662607b1a00b19bde00581b067674e005b24d00d8c8d702 f1970096a62700cb39d01597f37e93410ead2a34104a4573a886f90239cf7c1099462b05a57bcbf5 33653aef6b325364cfe43df853096e3b3f128d7a42c4dba3cb1e23aad31d622573db7866ffe6e0bc 85c8d0368d6bb1e73e758ed520e61a0366f7059aa52502e0a79604712c17ad271d4f07b3a038935c 8378453e8178b5e407720d36a8cd9a209e6336204e1a26885323b8d385b7c3ba478c9a15c45c960a f6994f679ff49960da0a02d1acd5f5934ed6bd5279ae714e3cf38e9600e57938eac4652871c07af6 698f52fa888b675eef0d74f75c2ec63ba608c49ba6807851d34130387103a93f417c2ca120bec5d3 810c2a202e3f58103f76b6207e7e06dbd9b317883b74f0a9aab441fc348a52ade242c201f1a581b7 079dc9b0e6af06ad3281b68bf92b334f67417f8fa7d5ca1b4a599dbaff49cb33a573a27a7774a271 192a386aa66574583d0af1f92eb7830b4b6817dbd3260f5a094100f19b173d241bf7c83340a0ae17 88000042414420f50c40f2a73a400ae911409a320f9056c60c440f3669f7824fcb483f1043040833 0c7e9748a7a282eb5561976b16ad6cbe98dbe66b7406361621eea675c18592ce29e753058473c932 51fd00fb78a7de5751fd190fae73abbd0817dc1417db6fdc1d40b242b0d79a2b0164510c76bd9fba 0039194f8068483c903a0510f75800c8156b45583a3c1ae4fa14001a9bd90009ba7220521e20f7d6 1020674c06c8510f7e5dd9e61acb717a58696b783342fb597e9ba5192c35c053d65d8a25b3f5944f 3e10e596e0f5aa43c4cb8e89f5bda1864c33af63c4a12b0331f6cea7f6a0f5b439805ca003405e39 15a0e9a905d0f2d903e894890532c00339a601ca61e540fadd409c0540f9aa1488ed0274df43012a 2025802e0f13800e3bc15f1965a188aab741b9f6ac3ed828713f5fe85e0b99e63e964a2766753c95 de4a1075ab0767b5adaf79c4a150ba60a7856e21e76753878bc7e331f616061268774811a09d5cb0 c3ed20f8e3f2de04e8fd760158b2f08c38746111c8f09104d8289b0f64de08e43a04d8b8b4094451 0136c97900eb6804c0badd2ac0aa589828ad1b00cbf218c0c85e232ab85e9ca85223fb62c53cd38d dd52f4285327a22cf8f0d4b9ce55de897a46bae3dd5de98aa624d58637a3841e13dc43606bdb3a06 b6d62710a54b638d8b09b0552270a9771e00bbaede004fc40880d79a0cc0fb6219e003d00178bd34 0944e24130a9312240dcd83f01decc2503b9b5005e9c6f004ee76d80637002602fb75beebba341fe 769835b2b13b5f480f46173a75d68b447231b19128111e579a828f8e59ea066f63433bd6c41fc6a7 31f611d5ebc4f4a403225c8d57271ec07bab3bc0b90b00b893400081764840949c022026994620b3 300bd95d01625a9402393a80e8315020470610b55a171075740f889c760304b5a40111af848ff914 cf566590abcc4af53f21739b187d207342524b3046eda4377c579ddb272d8f8cfd154613680c7c52 df71390e08a98f02c2e31320413e3f96dc8b0a220b660f24aeb93948e8533e909b061287b20712eb 1306129b720124c641b7f84c40aef8205145322091398ff2de00ea671b20df8852cca3ea3151e198 44c3d48828bb3cfcde3c3aa5570c41c4fba737fffe06443b0b7f38faac861126f97e4d40fe03fc02 b2b22c02726ad73f16bfa352e70da04a856320bc03a8741c04324f45ac138783addce51c90d7dc09 90ba1f03e47a9d07e4a639c971077f9091f46a8be94daeb5d4ec992c9035a695c61373818c171b16 1243a4c41ff18a8cfd67e271c4a1a3dce33097f64fe0372a351cd2cc4fe67b9980e4a0c981a40b9b 81f41e815c7190d4ab79905c69c126a3fa2e90d719244b3b1424cbcd2a4832609e031b3ce4d05978 26f5696bef34a8f2215ec24f56251b153a012a39f923684b90fa8c52c8a13f9c44f439c2965f57b9 fb5a5df82bf09d6c371187fea89a4bcb801e0a36a06fbd78202e0368295707f4e63403f4a4760474 daba031a1f7e941506a9abb2c9d5eaa339d3170536b91e5b6d42a9bcaac8d2c3f3310c87939f943c 0856d436510d8cbfa61dff6979bb3f81e72f6585a3c20edfb0de28a5b6b47800c6ca11205388d740 26de1902e671df47ac5361830de600014c2de8370c498e00031dc4287f3be532b3110927d45e5472 3b761c2a85cfc885dcfe235e91b1c8d3d765eda272c251fa6cd58c4e361f79c67f97df1b61d6a874 6f5430374aa855d219906b130390db281cc8751813e4ca5a0ce4f04a0664dd4b0f64f52107b20760 a5c7b5ee86aade67e1633e7872c6b330549a363f0d46a03ccad58ed28ec3507d5b5de24b25e18fd5 e3be61bd1fb52430fab382c36815ddc2fba8df1091ded7b2070a3b7c068aebb2058a2d23068a7499 0105556e83c222b82e14eaa6010a54f1f5277c1a3ec31f25a146ffbbb74e2bd8c3c77cf246cec59c 6b1757e1382d5aeacc291ecbed73ddbc75ccee6dd4d1c7006a6b8cac75546f30eb2afeb8d13bbee7 74ff18cc238672cb0763a9fb76a60716d697e2185377c28c540f7b777c37786f41dc397f3321b9ca 8beeec84061ee2875d03def951a2ecd25e517ab41ad7577c1ad1c99b96e2ed4b6e3f92cfef9e2f19 837bfea8d97554d71829e39e9668c957b8e4fd254bf9c94bc2f7e9f7819470481ca30822cc08178d 32888736b1cf9d35f28fca081ac371792dbf7b6fb5b02882d2d8b621bbbf513ad07cad0d18614d9a 6b7b95ba90f87296c7d808ee2e72afed71c6b5312162a851a6f15777c16e8c81b79a44945246d674 fec0025011c771aaf75114c16533cb7dce2a854511322257f1087957f79fca56aadeb52d223ac6a6 17b7ce1bfce85cd62c6edd5626ab3f56294b7d2d6769035eb88b73384e5be4bc073d2fbe62c51957 2f74a66f69bb9c0a9d923985343a396927a1d10439e756236d61ce86e6663c8e4a07471c5ae1c877 ed633daeb05aeb4741d9d66bf4def4a00dbed6da12bd665137ec696bf284d4576312ef2c19dbed2d 96199b9d7b2b733c2f3e94e9ccdf99cb59e5adaca76f41db4d1bb0b29f481d459ab431571b2b03ef 3cc62dc81bb1693a3634173d6a98f294323b2b33612dd9973170c56438f10866a36af72351b6148c 1b0c72331cff5d39d98858cd37b98e3ce34a1d7dfae657d76923c63fa7d0e11c9f4212464eda2846 8f957e3c33c68d587ec4265fa51169c7aa51c225f3a80f53d7589bb557589f651ef12feb726537fd 4da326f67c79a8f5aac18cbb2bb0b178b741cdf31d699a9976da99e2b583dc52992841b6adac53b9 68c1b028d33872171519083388434039ee61ef45c4ff8629a73763edb9c0b1b3ac7b64190f0a0e7c 93ba0c96a5e2bdeff1b967bf14cb809e7f28c57bc1851aedbe953cd1157ad564b741b4998e346ce4 3aede4b8d20e62de0c662bdce0a364ab366f910f5c688e2b2db3610ba77763d69c161a19a4b96c64 d0e5b391c182f18aab5ef1af854f233019c19f2863f2a33c6bc450dfd2add66da0af4e173a65271d 69500b33883b6d7218161b10a58f4a033d5ad6dbb8a39f5b6c56bdb4c89b7e6b9a6bc76fa67cebdd b0b91bdcc880075e770f58aa9e4792b9a8506bad446cd92a37bc6f2b6fbba45584d93d566964954a a5917b84770a2b8dbcfc8ce46fb34fc3bcce0e62e4f2119d6c91ee866e8ef333a699ba0bc586bd75 9b8d59f9dd6bcc2accb8c1bc2b8bfab2515cd7f3706557db748afb5a09ab4b1ff5055a4ab54ab6f4 cadb1c9c2b0d7a74ad40ceee596e6725b8a4ac5daad42b31c5a2c60f3a45b6765b15296862144cb9 8c15cc638f2d984a510964715e2cd3a23256baf832e2d05fd96994a8fc2d3e2dc07fca3e8d1ede17 866a39907ba3d248c1ddb2347df6cbed0c3c2a292b302df50ae8b2843fa86d91ad50fb22f9cecb85 71a3aa1768787cc9cf3adc334a863d39586ec9e673b93c2574b2de39b7cd6ea62f375bca1874b694 d527d9cd62fc71db214ae98c02f9417899e793a895901a5c1158fd559626c3474959246fa55e2e79 2f6a9be42b62a7e1e0962d4d1345f2b9a30331728571ed542cd031a3929fb54ef57c0639b773ae72 ebe7f2c46d94f58cf8225b4a257619df2e4b996aa66f30efab7a671ac538ca40fe30970e6601fd40 b443ba5d5fbcd2edc6ba1a21d5667afa11d0b0cda3e60e5a154d04e1cb421f06f1dbc32d92feee0b 39c5f57c269e3ae7965df21248f799cbe3e358d6d3964876c37244b64449213b15d2996a3a2a46cf 080ba9c83472668d813cbf936e97a1613a98142ee95ebdb5a709e8a0a734197ba4d8ce94485178ac 9c34357e961c0fab5a723c6a2492e331548fbeda51de7604f4a324e3084117b5c3cd8d6a3fe84533 b7ec686a763360e58c6fe60f196e9c9198b7432b8c30cfeb4c23db7602597a69693df3d3ede216d0 0ab782e95e95c76802c8648a6dca748a8a5f0b495341ebc971afd4a76cbd37a36643251c40519914 6992cbd9e049e6337e92cc6777ad84e7957709ef4edd02b926a39b381da93f2846ddb25acd60b152 6f51b817e8d9fe92cb1f7133f3ac6f5446781287b4a4f35cd012c32dadec6a3bba576986bba1f177 5f4a69c24a4bb10dde4a9af2d6498e3be28db24ffc839a0d4e80ca90178474ad3845e6d3894cc2bb d42a89cd62db237ccf9a13dc367320aa65cec105014078a3b1cce18d667188c3f1878cc388fc8a2a c00467a388eed797f32b51811b8f58919212f7dcaad97532cf936b7c705d5477449a380eb9142560 ebe4d8349649bab45f52b6b35b5399c49a235d632f92cbd1f198f0ecc329b1991956a29489da86f0 6fb107c11d8a300190560217fc531e87ab8f0626276a630c5d8b3caa9608132578f685e8c1a81a61 bd591ba126d01aa13a7bb3e70be9468b7c0be95a692de22575125c6b1cfb75cf6e8f1587113dc588 32886b192535dc6d85e46499dd51f3f17945aefa83195988b5e689fb28374f94147c410493e22d51 15537b5c8c9307bc712eaa383ced5a985cd9ddb00eee020cbda608b4dfec6611fd7c682049e63182 2f712c4ce781b3d3a106dd98bb0f15563532f62838b558d07de7b1ade21dbb42fb540962b64c576b 084f14f5cb2196bbf5637e66dfed5e23049d32d6949a4cbfb7072a7bcff289c7fcb8265e45664ad4 a6ef092e6a5a98038537f3dc1893b79b1986beb835aaee151eed0f3c194d50888e0cb7cc253eb9b4 5ef06521e1700e72d3d07acb54a162793a88ed4e974dacc2964f606ff177509fa6c9a01388350011 d5398080a7461d343c756664365d69f6e644e13c38c2d9877ef399839073e9c17c6946089aca3526 87c4e315e388d76db7c60f1c33c7e3ae3fc5304b98a0038c1d07e70e7a189ff8cf317ce1f31b78d1 ef09702ecba9d0faa085541d2a8ea17bcc2f55e158459d51e03d778ba0ee911d0049eb2580e48412 88fc00d0b1990410075a009a6dd6009a17cdb672eb96ea05ac912e77c18acc5fcc1d9c79270d3fad c0a49b1a196d83721927849064715b138977f6bc23a0e2748d1dafd00cc3f3ee0c25d3cb0992ca95 46f042b9b29097b94fa0e21aac627e8be663153b2f813737364083e05c00e9f60b40b71201e09892 073059ec06725a0198aa457410a6ee4f0027b7690023f92e807c8f07d073e7b6f462a5542b2faa4c 69d01c92398f9ec219a8bb7bd05ac9bf24ed56d5203d563c26b8744124eac6698723b3fa0ac3597f 860e2c73868c2eb309ecb689f0794f6893b1d818d7944740d8594194defa06c0042a0238d3d3015c d5ae00ae1100c0fd1909e005540a64d407b010df06b235002c66834f575606c0eb250be071ee00e0 f6e3d1b438a654ad9351e25871746c9259bf5f8718449b3e52e65c732857c9186409de1e09618009 b8d43aec30a5d75aa35a135a206630488b335e6904790e3c8c71ba33028ddd7604e0a9b5003047f1 813464002b82096007f2a2a03d7bf01f64ba5809e4c8823853e602712c1027fa108827a80288c7cc 3180bda902e07b05341680a954109aca14182d99c8bc8d1c146597a7e8ac76a1f255caf88acac3ef 0de2ef76582f535fa314fc5ec4678bd904bad3d430c613f72168c6d5c0e5c39982788a5a8378717c 00f196a681781b72407c5cf7417ca37cc1d2d31a881fe3e340e67b1057a86013e56309a438df2b83 f8829c077233409c3d62f56281ac96b460ca9dcf53f144064e9150945d9ecc8c360e595a3ef50448 0f8f049c4b1e703475e25156ac6c107ae22fe1bcc94e637c1f1d8166530f02d302d36837ebfc0ac4 8d31079098ab00844c5981746f81480020792438a5e6bb5980346e2d80346bd3409c43709a65af00 29c178204a0d20e9d11a2078d60e26fe18551530b7521c37752642fbe1a95365bc586a82e51ed42a b5b9249e43601022365170994c4b18c11df7c8a450dfc1859cb28cbd68760e9a063a098cd1b3c058 630982cbff0e207d5b04c83e7502c865ed0662f8007993f14006d13aec287e290194287403919700 4d648e81987780423d0a2077a40910cb08fed0711b1ca1b2ca463579a2c4fd8c583b10b4be3463c9 791db99377657649f04dd824c48eac61ea262923d6751fa65ac1ab37b98bbd36ca1ab46abdd01bbb 02c85cd80244358340be2011a0e5ba0ad0c9ce02a8fcb801f4d18202d911813c321fc531505f6401 fa243681701a402fa96720761aa0da282ad6888a5901a06bd807e8e8592cb10e5bce6d1b9d34d369 8d3e903975839007b9e5966ea256402dbcb35869e8f09954e075b72bc4de14ce7f1a735c1ea01824 fc41a43fa96f845429c30458130b2e4baba60fb0eb0503782c4d7f64216337bd0d308f9a04b2d903 ec4e9e01667131809d2b39801d1f2cc0665b0960fd210058bd5a2e3846a99c8da5939f29e6212f0f 47362b0ff73f9079f3413b1809f126ec258d4f5afe276ff2400118bcd7fe80d1b16087d7ece50fea bb7a007c768901dc29108040e51c20d2683d905eb4a6178105036efc4dca81ec5c40000605f8d528 01fcd099007c439d003ebec300ef68b5dca3074accb11ea33f91f9d6fce4e5b1ebc6c3065a704d5e d69a260418e3f4698c3a46952c421efd6718fdc1a1c3ea1ddf80df4d3f98b499721224a8e0dc9360 951e4834de53906876f72051300c9028969f20913a91208136eb2081a12b40bcb4087713da38f875 b1decad61b56e96b96798a4922d80732ef25391f9959f00d024ad2fa0cda57181d7afab01301dfaf 15273e128f1d2ae2d021c8fc64beed30c93738c9904a91051472d901f2995602e1af80bc607140ee e759402e891e2059890364b3eb02328593800c660e19642296a2aa2c51c175aaf8f2f0a81a4b78df d311c62f28d65a5d3f831679a3b1d8dfe71e7fad7c1ce5ab7e93e9fb15b3261e616aefea00921a61 47abc709e33748b2570a249bed6a2041b893a9c51124d1ec1350ce390da8d3bccf285ba79ad616c3 627229eed209583213e8c4a7500822d1d7674386dcfe5bd6fb89543fcc4419c71178feca7c23c2fa 25c9f74f9835429a85aa0ae803e203da58128096a022a0c7833ea0e9db0ea45ec33348b941988359 4bb49bd4aa3a8e16008d4a9e90b5dd3e878dfa660a123a32fa19b9089447f10ad9f89f728ebf9698 88c07394a51ae5f7fea5a0c3d7d5e322dc1b25af868f2e7de4d24a3e04328d4b016492c189957988 2bc028190d303de1099844860669436a81b4595fa787bb5297dad59a355ccbaf8b300f58fac3dd97 24f2b05cc847da71e4edebca7651f26c946cfc15f746e51cbeb2debfaed5f6914d1b61de2887364a 9f3d3cc3b4049027ed2dc805f35e90db1e7c90ab3049907d1f1a20ab671720dbdc1e5339363f88ea 6e477544c080a9663e2b7444eebee61c47c6224f919d3f0213e1eeafccf9ef2a394c1fc94f5b51ad de2f98b7909606a098ac2f402956388112197c3d8bde240d8219631b14cbe335285cdc13282c8be1 43b2ff27f807aa1c9b8cdd4fd3363cbfae76c8a8ae5c307c68267069736924aac7fb9abc5fa07372 0170944e91c94a5a6b961699f6b8aee7c0ae0be51fe8aa585c30dcb49caa1dd48ac6aaef5a7bede4 1a40f686cdadfd0a814a3b87137ec7ced2c1d9b491eb0dda464718823beb8d76c82c39c9a5d7eda9 5d15b9393b502e4b7465122bd17c3436651facb71e86589c3dcba17b765fae8aa8de581c44afabcb 95f8143e7af4aaa8ce2a5c78c358a3e627555705eb6d368d6bf65c292043db6b27e4cb6c4afb578a cfa76faa56efdf9bb78ef078c3eced59e428f043f2ff03f7ff03f7ff3c70c16596426325e35d8056 93f3000eef0a20496f66a1835d3b8629b51c43c030d60ec3b722b77ded44dda89d9f4adb238a1e2d 6ab5b456a0a719e40949d9c6fe72cdf1cd23f611c8acd6634bd351314cbe2c9be9c4b98abb2f2808 a895a98b65a1d37881e9ba55909a5a7bd1cd3c3b76024df648d3abf77bd3d36c2067b77218d7dba8 cc55f1f1ba9e2a4ddc786c384b29ce3e08ae642fd4e4125ec14e37bbae2d0be10874b32b129beded e9eb1c23182f7edcdaa7f63a366984919e1f9a63e628ed99b8273fae3742c96dd47210eecde864c5 06824ec865c7e8f49271f340829cf5b2ec9e5d9c1db6ce3237372ef6a3fd0ea31e262ddffa0dbce9 1de38fc523a6ea8a5f61b97b3729babdff039eed5330ac0cc62f6187fcf3cf2f81ce43056cd781e7 fa701d3f8fab1a629c7c2feca704dea6f765427027c3049fa2c20479f2eebccd64766983d4b47848 d3c66bdefc8e80ff35da4cbd71611b0d7e5599371fa5e4b19d7d03af33151da26bb6a5f04e5b9fc0 17a341fb36917e28e07f8af68c8082e1b171c908c444ee889dcc721df66b4d7aed0acf63b1960867 3b8acbd2ed139d82561aeb5c4ebaba947d335e5c5156edd5ab9f7d62b47472664d0d7bf8c3b5b230 79233cb7ea758ee7f9d740078124e280f367d44734f3af6903ba5a0f3608693fbc63104f73570149 9abd30b4de35ecc800ebea5e0247d46e9680bc57e38f20131b725b138e54a1cc389f614ead9361bf aea607d673c82833739f85737b3b577d2c9030cca5e0042184375bcb23f561567bbd72a22e76cc61 ab604eec6e8aef9506e87aa88e7629293fb5675779898a5e72bdc7b7b35d6e5274833e79480bbdfb 737888d3b81c7447ec1105485942cfba6a1fcd9946d51662d81dc3c1a181be1fe0234ab145a61874 4bb373f6416b66affd1b1ff4cd8976c9604fd7a50b1310040d1057bdbdccdefab977cdc398513fec a7f3fb213be41f75f4aa06a78ab6e3ef5cd3ff1ac8f039e3bf89e5af0e6494a4f09fb1fcd5810c7b dadfc4f2570732ace7ff259657a5a03c2ef41a1f3959a478b6af7eaf6faf7a35dc2e54c5e1f951a3 158be7c0dd6cdc9bbe8166cf6f1df7595863890caa912b943899f09d3a8d263a7d4a616a569de682 4b4128c16ebebef869f9479fc16e7edaea77c80f1ccdcf8434bc7efeacd51f389adfd7fabfb60bfc bbcf6037bfb9f57fe0687e3aa4e142c9bfb7f5ff389adfdcfabfac0b7c87cf6037bfb9f5bfef687e 4d48c32ef07b5b3ff219a62afdded6ff155de07b431aece637b7fe7f3d9a5ff8850abbc0ef6dfdc8 67f800f0ef6dfd9fec023f14d268d4f95b5bffdf8ee6579f4e7fed00ea1f7d8603a8dfdbfaff7b17 f8f19006bbf9cdadff0f4773559aa6ffc5aa65afb86ad75ecd9a81d59e3c3cef12c4d1aa559c9b09 6fcbbeaedc8e6f4de31a9046ea047232f34ffc34922df2944a8b49753a98a49545bb1c7681e8e72f 946b2f2e05213d9023fbaaa4ccc05dbef331862ea3e7478667cf65031c2df0566f46fb85ddf5de7a f7d2d86a3e763287e1b33b770b556d6c85abd3fd84542e5e2d795cf71ea9ef906037dfbb69249155 c7694143fb6a33865d98553be7079145cf3bb062add7f02a9ba2c05f0d947fdd75a2387d6a1488e0 d0694c5d61d52e6f8356ba4c50c5850ad8f1e63f30f9713d12bf46c2aab75f5e5cf1b3f0697535c3 f5209099f6797b4da2e7b23e61ad7d5297cd66637ed5d5dac5d334bfeb9f4c117a9f52960ad4e96b 1853dc5c013a7aa90724fbc41e92def1011249b85ed997173f2d814feeee38cd371b74d0941e0492 6a9fcb2216d6bbb45eabcec08a4db7b221cbb58bdebb1dae1acb551e271ad67c75969ebd1477507a 1dbdd6f3752c72ea4baeac866f096252b1ef95f068be735357db6deece3ce9b341df84d4209068c7 7ab12fd4aac7d35df3c04c24a3d3c95e7462bdbe9e2c807baa2d0b77c5dd37c291cdd1db80fbb168 eb0fb9a20fef52fd987e1c5afbd7f3174ab09be8a7cb56d85b60d51b9c1fb8af062eaf2dab763fc6 add839dd37e3cb9a64a0afb8adb17eeb72a21bee55cda4c757250f11d763d1575db972ed5fa4fa23 ed1e5a979b2b068777fb2ac16ebe79e77f1497c27b3727f3b0fbe7adeb9e028b72cb8ac907d43c4c b1f0691ca3636107bd5f3b5b27ab9ab2d59925394abe9eb18fa5947596abc4d0921af1a475686337 4bec413b4bd04ad3cb0f49583be1bf6f7531ddc6d5beee94deb92c98a7c0657004adf41a378e0ba3 af9fe887a4e9879d79a2b5b7a95cd3ac79dc98b02957ab735d6a14d2e1fdf6433b7d3989bddcee24 68e391be37fb39e3574b38861e0b25d72ee0b37610d0991ab89cf78d6ebd4e1898b71fe889fe413c 594a45533316a71eb710aac8558e5724b8900e429fbe48626fb09705b6dd92f726cf1c79fb00a9df 4858dae46fdefe21716677d83e6f8576d33c74da9a71dc7786fac92c26f584d31a9ccea94a08bad4 396b4bc7fb3e7f90ab36bf97e061762faa98c1093a34e204aa54e4f6740de1f9d9cadd73eec33cfc b884659cff75037b45cbbab587a9aa197f552dfde415a69af144d2a7f31a1aa8970cc4298532cbcb fcc8d949b05edb887dcc58094368b4da8f9fa925ef64dee1d25b7c6665afb9e559d8eceecc66f7ab c57a259ae1f3924140e19a8193c44527c7d9f049a2774acdbe975d650d4b1b9937ee8b83ac97a722 f13a8cf6d6a632e21d1e19f199ab3be1564961bcbb4f97d35dc965e7db27db5efc9d844574fee9b3 ef1033cec565a35bb39afac0247c2da52678359f82d0e3eedeaec8af457e26c57bd1ea0462a26cf4 f7934db1cbcf67ef2eb75a69bdddfdbcecedca0c3bd8f26cbebfad51f9e1469c50a3ff51c27b1eff f4997e1293a63e58cb5d6d8cbc6075b97acc8ecfcaeb2e8bb09b39e0ca362b8c26b7d23e6db6aadc adea37768fe4a6be7de1a5da96d74acd6d8ca29a1b5146dbe16ee2e8bdbd96d57bef578b66c66b37 cd2cea6196e7299371de4ae97869cb227b530f897aebbe9f374bfeee594a3fb68dc2fabd69bd4bf0 ba6b0174a5a116be1af02ab922b3627269185b7a391a6d33cb1432cffea384b57afe75837f9447e1 2068378944aa5782368c2b51866ad7dea4e8bb2c898e1d7b76dddbee903f9e6fef440853ce05ef74 b2b6cf816ebe12b9b3592b421743ec6aaed17085bb0137964f5dba6f61bddde18913a9f0ef1f9730 687ff3f60f59fd0e9fe18ce067ad7e87cf90a7fdacd5eff0f90f41fb21abdfe1331c0efeded6ff87 a3f9d5ad1ff90cbbc0ef6dfdc867d8057e6febff4f5de07f0c697859fbbdadff9f47f35b5a3ff2f9 3f7d6f7e3ca4c16e7e73ebff6817f89990865de0f7b6fe5f8ee6f7b5feff7eeafcf1908648f5f7b6 fe0f1ccd9fad962ee695e8e0f52f56278ecd3d856f7deed0a961beb215db8cc5fdb0fcb9214eed9b 010bdc5d97a9d9536f6f27b08ea67952357bf3f7af91f09ef487d51c5973b5e7c97075d40f4ca753 8fc02a1cb43e770f42ba9295b387a7d57331f732ad0abab3cd7db67b350e087233e28cfdd065417a e91d8a8734b53cc14eac94c0155bacbd220979da9717ffa3dc1f234ebbb53ca81af83cea9f3ecb4c e033474c229f214ffbb0ca1434cb275f96f9468fae19bb75ef460b49f9fa917902bd2358b0a65a3c aa11af39a5d2b3237a5ceee2fef74a4839fe7583bf5ad542ab95d0ea3db01ab6bef20c422aeaead9 eb760ccb6f241cf35dba78c6e1367d1a48aa0de9dd2a151228edc45e312db195a9d3f0c46795cc75 8dcaa5c5e9f16b24f0b909871c1f56597e777259d52ebbda0bf75cf2f61c7f5ad51dedecede696c5 35b2ae29945ebed1ba71315d498d514d037442230b70ea34ea589988c9cadb5d1191aadeeefe5542 02f5d7777e48eebbe2f274430eefc0a77ad05cd6f12aae8e919e4b4120f0e9c042e4331c436fa6f3 b3c52d9b9ed928636fa39db5114dab96b09349dd8953aa2c532a433693c79b1a4b49af6e053b88d2 e1f94312528e7ffaec5babd4dc2cbbec09bafd6115eced2b78eae7cd59bd589cbbf24db8cc40113e 3f998d7b5cb5d32b4465061d2ce269c80b9577e20e95ea9541e2101f1fdfbf46befa0caf9e547fad 86564b9f5683effe852ed1a27dcd52e6791bbf5eadaa2bbe4382fe8a70af6a77b64071ebe5d8d12b be80ec67f640da9f2be0d00aa63e62b764c6be9188a7fde7dbdf21f70a3256af4acbad7cfae4dd72 60d6bf863e47814f4ab0affd4ce07344873dcd90cbe9c75786aab88baa7ff446cf87ecf7f8bb24c4 b3dea1b5bf786257325e82867af11f9788a7fde7db9f5647ef5a6855b95835a51059bd98b7e7f042 b7e9c0aa5a35a2fb277a3f76ba9dc6c2fb1682c9dbd193802bfb9ce84a42216b1fa44aec1205cddb 39c2c0195cf76676eaff1af15e36ab5e7b29b77ab1b2d310a65c26312e1ff8f5ae170b0281d531cd 9f1f59fa64c6e1be1b3254477560e5034ccabe2d5b92302c1807691a33446570d005ad3d08e95ce5 bc4f1915f7ef24ac6bf14f9ffda378fb59f578ed15fd5a6855be4c12523e306b5e2e63e33e7032f6 736dd5ceeaf103a0d2826f46195d9786712c8deeba24e84df5201de38aa8ec8f8aa06dbac710f829 fbd40d39f1ccc3d3ff470941d75fdff1ead5bc7cc57dbb1a181cc98eb39e651c67c4d9177a6df5ce 3b204e0d6c123b6849a4ab7f80c9d2e12ecba03a910ed2233813f5167b41d0bca9b8371d46d8d359 fc10f1b4a479e0965345fe35e241042db96c4faa5ce8435d70e6ad7158d7c2991797967de5267533 7e790db5a131123ec0e4b64e8932e0b6fb43fb817162ef72dc0aacd9ddecc74e6ac33bc47bcb6786 f6965b1e85ddee8e6ff6ff286105957fdde01bb9b544f4e05219a1e2cc5329cebeda55ca5e716dcd 3c7476196d9465423089f0c7fbabc1c9c0e6d7874e120bb19d489cf6738132bbf33dade2337e4ef8 73eeda54665cfeb899efee95d97257127aab9f91f068bebcb82af9ebfe32d63715fbaa209bf32343 11d68b7d2ada482533ea8211064a61bb5ccbb591b3387486e98998c04ee3bd25d587bcc3432c9f05 de885b158421977f0893dd966127dbe7b2113ec8b8e5f38dc5af11974d0fd7ce3cde2b9f1fb8b9b4 6a771ed7075239ac07a32e0beff47157cef4e49af19c1c8e447ff401262dafd5fb0093d7abdde30a 49aeb7db4ec7fd5d196ff7b73c5b62233ab905323dfe77098fe6bf6f1589e3c45e63bb80d3452bb6 2dcd0c3c5748aaae11978f9ceb25e406c6560e4765db1013f2abbd3fe7a66d7eae424d6ea56be163 32bbed71dcdc95bb6c6bcb0bf9d6b6d6c8b44340d9d9343b686f033f9ffd9f969075863fcf6502a9 583549cd18c73db53dcdfab1f4f1d9bdf1b2d878bd0f849741f7f6238e70dea688edfc8c4b6edff1 656a1b7b57d39b034b6736710bcead8fed7b7edd793ac535c65ba5d529ab5656fd9b1c528ee8e74f 8bd1c5ccba719c6f735a4a51f74ae96c50b2b83c4f0e8941317cfa73bfa82a26575ea85630373a99 eb9ecb9c57ecc67597a6a3dc97746ce62fec35fb5acc8a4368c1d84d78eece9ac87c4994b1403ae4 7748b09befd84a33eec858275bf392726ff27b5914e6ef43e244a6f70b456aedde536ab2e9aae5e5 2af96ead160bd4dccc57b7e57ef640dad2ac5ccd874875fabcd2da941f12c6b41687ad2938c2ce44 2c3fdd895841bd5f23c16e9e741f5dfad9859bf0f6aff3ce83b43aed09954370aacd0b399744ae4b e7dccc2e9d74e7b1b1e78b0377be6ebafbf3f2993b5877e0cbd66668ab56f1a1e8a6dfe52c73f7e6 2e6685dd78c61b16de7a8b84c3918dd645c4ebcfc877f80c2f043f6bf53b9cfcd7a3f935210d91ea cf5afd0e9fe12ab9bfb7f5bf2f68bf26a46117f8bdadff5f8fe6177ea1429ef67b5b3ff219ae2efd 7b5bff27bbc00f85348429bfb7f5ffed687ef5e934ec02bfb7f5239f6117f8d5ad1f37ee770d53e6 97af1206edafef7c873ce939b17cdc721ae1d54fc66748ebef75e0b311cc51c9d473ed9c57cd8d93 3e2676f6fc720bc702e7abb73e9cf3cfcad1dac620d57c264ddde476cad9786b33c7782f039702c5 bdf5c3ab0f9d4ecad6530db1e0fc9084a3cebfbef369b56e2702ab67ee26b1b994d7c0f7c79b0407 7354c37602abfa64e7a41f85bd9d3dddc4f3cadbcbd6fdd952ad720cd74d3ee95a26a84817431096 37a391df8410d280643eaee12deba5a60a898b32dd3ece3f231f3ed7904278d0c2087c2e2ba98f2e 309502ab9b5c60d50aac5e579cc314faa29dc331f9bc260df50b960e5c9e5dc7102bda2d24d2be2e 59f398deee8809d5ac4e9e0a33b939c7dc606d7e23211afa9bb7ff519e344faffeb07afeb0fae993 120a97d14c5e3b3621ef1d66383fd8b94a563917c94bc8d3acf2736199fb44fd62883e14b87c6bbe de81676f1d9dccd193e625316556483f646fb0b3e5a2d4d27f5cc2a4be2f2f02ab541055d24c7c58 6de799e49fad726a6035a30bb6db5c49766ed7d1ce4516b3ac4ae974316346fd66347dd2d78ff833 a663948c68fdf48a54c7ca297ccce7984fdfee12572a9a52c521b59f910f9f0553226e52ddf8c3a7 bb9623a46acd82d6f7ad9563571dd17657e2f1ec4dfbbae5b369c77cb76f7fe05ea40ae998f60c5c de75f2646072e6781d76e3d293dcdd0e029f320f0dc23ffd9d8410f29f3efb469eb49a59fd61f5cc 4756832e3092429ff98b893881cfde3564a8967af68e2bd3f285b26b0a9d5894dddd8e4990deb57b 98361029ea64c857e60be98d4b603cba89d2d537c4f6d0d6fe470927ece1cfffb0da28a602ab83c3 ad75df142e66eabafab8cf73054fed1cd252cb7f4f3c539073c0406326a2b180c64ea98e48a84cae 431d6f0e9692f638831ce0e7201c438be8cab70435ae683f239f3e3d2d716b93067f43e114f5d567 c8d3cce27d75493d61c1c9f429fd7c6f3e2f168ff30f437ae1e023b53725cb90ca2c3a21e67dc4e5 4a738a48b1771d173beac817fa05ef2c104749fb470921e4bf6e10c99376f2abfb63231081d5f31f 5685e00b856cd95ce0d3995f689ad83b193e13f8343237536ccc7d9d682dfcd3b8e186b85b652ebd 2805f52957c4fe43822a89a778f47c4838cd17afbd117f3bfbe154337f46a269d417ab8879e43eac b61bdd436835ef9226bcb8d06d8a7732cfaa66bd10e266a0a7d8ed6411e417860adfe42a98b81234 8d5f44853cb88246efee02d95762fba48d84430efecc68e79f910f9fbb3617f8f4cefbab629164e0 b325de10791cf8f4a2e253177a436fc374f9a319a7868e3644bab6ea50e62743e5665b5382ce98fe 059f9a7bb35f77787b54bff35385f17806f79c7f970842fedb06bec3d3cbc8ea55d99c3eada2545d b82a937626c2d2f6d5becfcc436522e889c4c908933b0d65f96aeb32678baad41816c299f401c52e 8aa049a3e367bee4eaad72eece33b965af78e7723a74fd19f1e77d22c476f70abd21aebd7660b537 7ee357e55edcbbda85261da7a5b6acfdb03fd0fb4f833f59765d55aedbe5517e8e9c83d4d09be201 ed3c044187a6fbbdf9cced79fbea7f264be61949de6d96a6b12b35de9708db053fff47f1b35978ee bd9e4be24ad007fe4a947cecdadba6f8cbf8668173594c32fa495a8715064ee76271ab66f39a782c 93282f3589feee806e831eaee7a6abfdd82157fc2c186471d7a4b2e1f2536ebbdb68337efba46687 2d676d94ef9090a7fdd3678f1b78cec21209b8ab39eb9dcbaa26e2ea18b4b30b33d6d44f668b3a4d 9b7e57cd0af6e6b83d953692c8ad96a2ba7dcc84616e3add4f6866c2cf092f2c3211264d4e427c3a db6df17198e1d85b6dab7275bb059d2af733f228acac8907d55822cae5e0a79b8b95e562cebcb5ec 19c7d3f6789ac5f0a4b26ef4bac71dd65b4aa22f4e45f5111f09c3c56eb84fd3cc80cf126e9f5b35 a5c1ee7e5cb2bb32de63b77c233fdcd6e0fc782376e8d9a689d18b48426cf7e5c50fc9bdfcd80e6f c83a8f5dac443d381f19d397799888392da56655c5b3214a7e97ab35e9d072c28a5de2a990ec0bc9 c5aecb5f50b2c5dd80d9e40a4da1b5dbeed8d6f6f9ae8674b2d4d9823bd1d988db2f60f205b3df2b 1184fccfb7bdd83bdbb9e2368c045f1e7c722e0bf64933db85a2728fbf8e47c07a9884e6f28c304e 62a93d335830dced4966770f5dcb6d5fc7717e5b7b570bdbd83253dc1cf274390a9a855636f1095c 5bcb4fa7baeef04e638dd5acd6cfc8b52b4df397b12043f6aaab8448551f48b0aae687b7c2b1fa7e 8807f532bdee2f0477e7b6f5ed6d2b02e3b6918e83c7067173ef750f86632bade4c0ab81a3a32b72 aee24b33c113cbd161452e53ad797271f687a9c594eba7bf4a5897fcafef7c87988d44fe69e75fd4 23cceaadabee55d58f35fc9839f4ff2f6de7d9b628b2b5eddf624e08e69cc58018403288a2a8e458 faff5f707a6676cfee9ef7ee99677fb9bc29fb684f571550ebaa4569f55782651834d7dfd54fcc34 373c51cbcb2e5ebf21b5a7279315a0de8843897c1c9ce94c3b90e1d83cb4a2e9d7deafb7dc3d7d2f 7bfb2e560e7720537c47524dfd927cbcceef5aee4a98089f19ee60dff683c3ecc2ef98dba9642581 401834ccf5f55c9756ba93e9f139b156845d46b0037550767b76ba26762f6e40ef864e85db25b1a2 b03d65d2a7ed8401f1e4769be9812b7eb61c159fafcc6724befe6f44c56ae075255751a693b04e4d e9c1afe20dc385a602f1ec7b4b3e683474bc2372e9270f1eae14f67d7751d9a5b87a7b3b4bd447b8 3229cff1c225b7daac5efe660311e6361240604fc839621b394ab09199c9af757017fe22f10f57fe a0f9a7029096d50395a16d84e69b9804ee11b6c266829d056efb6404ed1a5973ced47162957d776b 6abdcbc1ac8c56b19f66187287326aca9bd6edbacde984a48a7a1396cf7a2390af9abb93ef9a8b3e 0dcdedbffdc7ab3a73ee19e4fcfc2589cf9b3f0efe21ea1738e3a4f0dfa27e81332e4efab7a85fe0 fc2e68ff10f50b9cf110f8dff6fe77dfe67fd7fb1fcecf10f89ff6fe87f3539ff63fedfd5f1802ff 3aa4b19ff6bfedfddfbfcdff0fb5cfd5ff1b15258cfd0c22635426e6e47fc0f9f89333f6056254f5 6c308f7f28001907fddf380fea6e125013d80edc3b350ba88c60fa41818e1fbe70e6d47961c1edfc dedcf66e11aa401d8dbddc8f3afe9a6275b2680b7af3f894352f232b1ab5131f1a85aaa6d6b655ef c9961570cf4cefe66d79efdd7f2af154fd6fff4184ea46215d866678b0a3405258c3095ce784066d 5db07ce62db69dfce6bab260ac7a30b74c70344c41a08dba3c6575275713f456318ce7029a873d15 adc372ea332cd0fa33b065f7c9ae9feffb3495706fabc444bbc28c7bfd870290f5abffc9a4895494 a890bb69d06937dc8092840835b8387ecf963b4efee46f2cf83e3f9adba0441be64be78c468a10f4 63ae73d2fc52eaa27561f5f60c59457fb26fd179bc5b22788ce6c7e4edb6df5a57587bc541bb4433 87cb3f940fea374e919afa41003b416742a17e70b9b8de4b94bb4edecb7d32692471a0cd1ddc600c ab9f147437c54a7a3b373a6b4ca9707b02d8783c0735ce788863d27f8c343ea5a21b135c3758d5bc 5802753d53b3e6e9ef2536217ff65e841af43ea8813bde4dfd301385b483090b3fd01fbe27904acf 29402dc242ea126beeb0256f10fdbca4bba3c7590b72a8a2f54ac8e3c9c3de678f2ed1e69c47cac7 5f77797b2e5e9fc5b9afd47aba7ea6e8be22b3fbb7f80f0520c7641452a1607fee9e6b6ae6b39792 e38730b5f0d9ddc9f346ebe7c029345b070b5968bcb9bb33a241b0bdb34e8d2c450b70e15b157222 783b8f3449fb71e171eaa6d2c98cb2cf94bcb3d7dc3d65f69a3d9f04e9f3930e9fd75f973f51bf71 ea0dcf0fdbc2d267d947e0a55a714847ddbd85eccd784b40739f384b0611608a4e89f9bbc6e237fd 290c57f663a2a4e202e47b52bdce99e215df0be94bf3f57064aed2bc9fc4847d964e01c97f41e26c ed07cdff814a9537d3df50d9805afafdfec98f50a3815a18d5b716c299acb98faead06d9e6559d2e 0c35ad4fbbd63355daf98f4cd84ddef3a413fbd0b7bb75ac5c9c763f298314e59cc465f62ec96df1 24657738ff0f05205226e2bc214e40b588d9c74cd9c19ecf658495f72e087ebc7ed2760aabd6d6c2 b320425da615833c6a0fbd5bc34c6d506ef80f7969247ff37ae3aadedf6cde5e219b3c49b99125cd b0e74d545852160b6a97fb486c42fe71f055f90e959afb7db6e1fbfd0bb5f284f725889dfe9683be 16116737cd987b3e17271ec63199d3f4eee5ee6a893591b82fa664e286a7b5f4a75af61c38a7829c 5ca7d2d27cd8b4c545a0de05f5bd9085f5b8ce7f55e2c4e3fb1680dc4b83df50dbfe66e6bdfb7084 aa0b6b4f2cdc7c77ea9375a7701b60163e4b50a6d9ed9e8de3616c68c39ae6dfaf0d3db8e16b2ff8 14a19ec38514c6de69bc1a255d7c28272edd8e27dc2fcfa7501617175e4b57847f28df38ad5a7411 4d1373ef8d36e29bb4f7de516b6f649fdcd89bacdaaad05a58b85c24cd46ae2e6aaf69eef1c8bdf2 e64dab43e6b5dedbdb976ef66ecaa3a0a4ff567a0a1d36beb0a1a621aff70d83dfaed757bea2d5a5 3f253621bf6ff97b8950f3fd0faa1f9c57d32890b0efbd5901f3522265390ab328dbe5662eca8f11 1fd77b8e433fe65a2336ba54acb3795e0f6955bbd0cfc45d16a5e14d5288d35584ccce4d401e88ce ef9169c059efd0e28834ae728d5e45fe25898dae3f0e00e2c051545f1dd70fb428aa231b0e3cb14f ac5d992434a7586d65adcac9efc49b63cc1e17b244a8d88392afa6e829179f685dce89c5513ee57a 9628aef19128544aa9135fcfd8f1b7e15a05e3c17a77c463a933fd64a9ea40f987022a89ea0054f3 a3e8cac4ece65120634e948d9f2c767365f46a69a314d083fab6f8b8088f91ba69a0fb5b7581484a bbba10cfc3ec953fcd820c23de9b042de05788e6eb1395e15aa828b0b448cb0c48ab4f86db3e0d86 b516b7bf486c42fea0f947022ac5c620346f4dd7efcf77a82bafabc04bd99ba57dcf1778e3c8edef cfcc4bcfa81bbe33ba12b527ae1c7d993b0fa75a5c70793a1f1b8454b42e07619b43767c5d7ac625 9d1cc1d26ffcc8f0e919cb247a639196f2f2831e8ff6ff4062a32b7e05957a7b101e848ee7bdab11 eaac550a1c45ab8d4c9312b1e769eb88eab3b2ca5e89c01b286d6d8d9f8554fa703a3b435c2a551f 1b613b6baef9c3205c73ad805cb34c1fc719bed5da3209a6b2ff7cccab42d169612c52e711a7fe43 019541bb1fb850c1f146023a7773e9bc1f97f357b4a16fc673e83b842f4f57fb54cb5d4252ee9d05 a2b59633fb232695b6ee42d8caa339df187833aedde6a2f8edd67386275b2893f0cb4b5ada16d774 daca6da97933475039afc5fd48e21fe0fbd97b7f0aa870c35ed096538e2b37a7a8b3c003c7680f10 e7aea6969d5bbdd4385f40ed9d398fe75cfb841a7cfbdb53e71edae71bd76c5c09c9b58ff290eded 16f183e7add16f0f9e4fb689c96f4f9dcf97264ae54b3e765c6e9ced3f94f8eec9f45688cf4edf8e a3e04d343acfa7d2035d0d9eb7fd12ee2b5df6259fa5969738159f95bcb0bf24331c3daba559ae68 e49911bb28d027bb05d1d97211a6d099835079a0558f375e6b1c4b2ed3201f141faf14921b84e991 b0218dffa158d85c0ebcd142366dd5ac2d9fa75e717e33f6bea93099a07f9eb4f4789e263d9afd07 ef626b83e5e5a5469fb1d3efc59db26d924fa038247e9003c278e001b19f2e0051cf2e13079b1b26 0fe4609839349d5e7eef1d27d04f25fa98bfff07cfd7647e77d0a07e378efd724bdd84e7554479b6 ce9331db94602f35e72987dcb329c2a7a99b9ba48eb0dd3f12d6cca60f6e83e60eedcd22fe367b46 e99ef6bd41fdbc034e59d9f1c7a2ba1bc2e9c72e7106da564213d676fc02ee3f94c7b490bf7d96f0 468ca46ef45b56f1616c73ceb41a5751772f21c7c1e93c93a57b300549b70659533bbd035db1477b aea12e76ef2989ed46fb31be3dddebfbed745226f10b038e385a75683c6f391c9ebff9e26695bac7 89c7a6249bb77f28f7c2387357115c9c5df86a12c8797cd511ec4e61cdbeb7e323ad74eef211cf34 625f8020a19b1b0176c06e14e632dbe9db877065ecd4f0c2596b6dd6f96b6f03ddae230c87a42986 3c25746de0d26abdaf489b75cdbc1e56f6f24affbdc4bf8ff9b3f76e7845bf9f3389e25c420ae19d a70bd9173b328f79bac0656aa4312746510c53ab9dd8e6886d561cf01b755ebc444c450d431ec05e ef11c75fd97bf3fdf919ceba955e351d37bff48e6e6949b75d78d90982da22e492ed0537c80ffea1 803278e6011c6c0f9f4c3a3548830ad9df46c206a0320ea7a092295cc23d5a4978e1750b3b321a74 ec02b39b994f289a496fe8c2ce84c3f060e80be368e8759b35b6774330b647fb646cb1f7d5a86653 0fa312d62cad9d96e3a4f02e6d9ceb7d82dc955f923f51bfc0199b29ff16f50b9cf1b7f9b7a85fe0 8c33827f8bfa05cecf10f89ff6fe87f32741fb3fecfd0f67bcbcfabfedfd9f0c81ffebdeff707e86 c0ff71efc339230f90cc91883953a0224e779f21209ec21875062aede635acf7eb298fbb325567d6 0f7a96ba2550f339eae3e646867626927d1386bef7686397787e5045a33ab12f46b593ba1b550431 f546e16d3ff855e5a19ef7c78b3aaf63718afb79fdaafc272a344b7f5041457dbc40e558402359dd c3fa05cf7b83ebb9e1e406c9a1b5deb2f162b1f924a65b13a1a1bda1876fd2d82d3cc6a83a86a05b 13f5a45bb07dd30f174fd30f1ce23d7b3ddcbaa743e9762b65f2a7af4aec72fc7100e08a1da1c23c 092acd75144d75138d03db7a838a842c41c511b4b091914bde30e7779c1cd7985ae5ed6963e2041a 717a1dc2d881d4d1a80d3c4eb7625b9a80d58bde60d5d884d41bbbb7fd0cca8a779f6ccce76d8955 2f57b8aa4b8a665dc45f120077c30240dae7e3374e9b8882fb7e273f43e0de5987e6fda40747968b 50e7e5be7de586a855d6acad895bd4de309e1dd2d85720e69bd37f7cca7a33f35435175575edf80e 8347b299336fb7c444bdc2bdc7e962a556dc9ff2b154bf6bf9a944a85efe1bea689f8d288523a816 b329504da09bf0205d8ca0bd93cbde90e90eed6b785859309cdc99dbeaedb3206942e8d1a8e70acc 37071d7b5e62f3fcaeb5dfaaf3786df7813a578eda15d35e8a525d11d2d9add4d8f3d12c305f95d8 528d5f013c7b47519ddca90895cc7d43ad43e94808fcb328e1b3b518d5c727f6ad73c5bfd9e85bf7 68982b8ad19d1cc27d1c748d8e4dfe0eaa184f9654c37be6e4389f0ae209fc50aa027b3ebbb3262f 07839096d98441fd92fc27e78acb836ae1424588480654076c9c1104147c31fd7e4181bd51599cdb b72851b0e0fb8434b77afef7ea6e7c2a7e73d033cfc7932b28eee3dd72a293c1e919caae7abb9d5d ba7f9283fd9b3b0930494442937fcac752fdaee54712a186f9ef506f7444d9cc85565dd8069dcccd f404512cbb272de67c160f16523cd2e6aedd638dc32bf854771fe99dac310de8f6e474c378886b11 a8e809bc3fd5cd67f739bac8ec22144e022633d2099d11b11c7e49e2f306dea6a2a862260d2a845c 00d59acefc864a144f5b9f654f86372215d83df9e234f67c636fface9a3b69cb1bc4ae71d6dbcf93 a2f59623ed99b884de5df6f9f4152f62e04f333a0eda68c271d2895d5352c66e1dc40b59deff92fc 80330ae96254081b8e80fbfdddcdfc54778fb9923bd5b8b15320063b6b13fab1e76bca0699d95ef5 0e597a3e5fa6e03c32e8fbf5a93dfeade6b80f6a8f530a5364298b2e381115eb47b1504eef63d9fd 453e96ea7f37ff2900c6df51ef1f9ddf51fb01fb410d288cda7862e1a4bb728b2bb8d9333d700ab7 196e3de9226d1aa0723148073cf42e7a8cd7083e75b37734dc97ae3a2175e5812704b1c9abfe61f2 b2c2ca4f1f056866ee7e5d00cca4f29f05c90faaa016bfa1ee57a5a033396d3db176d1dc597a9f71 16bd75db5eafba4b4b4bb58ee6a1d292f4a0d17d3e276bde55cb8ba6ab90cd7a106f329094e62601 c4a2ae3e84b53a938572b9123bb7fc9349503c9ed3f6bf2e00a6df39804880f9c6b94872a0caed20 9f0d04dc4b89c4cdcdfa78bccfadadbec6350bf78a23b3914b6cf47ea9cc3cd0f4e4acc2d9d55d21 5f8b87fc76e60fa940ef74e1d99093b173aaf3d5f4e0c299f39cc099399fe6f6cbf3e1a712bb1c3f 7b0fc0e7d4efa837a3149df1493e3a97d052144d0a7767ad8de214ab1ddfdaa68a45c309fd96366a f3d347a19cdfddf40e2a288e26cae78121c5ceada444f73d011f16ce9ccdbe75aeb97e9a5ce3bcb8 b1ceab28b1cec16159a72411bf2e0096df59803cd2ece7ee79b3a1f060077ce04a116a4a5cad9dc5 bc29585a3315ef316dbcb564b084ee6aea35beeda608766d90367b065e8239cd33794a2ce37d8aaf 4f9e5c6c479e19900e2d26ec910f2620ca321394fc7808303ec59091f0c75f12006bd9dc071554ac b01c364497f3c3ccb4e8cecafdb9bdbe158e660351e508711c5f05224a17baed93e850a1c6f4fad2 13baa49c29cef7e25d1076bcb9786f3f9e24035a639a49f4f00b2d5a8a4e8b5057a15f2720d22f84 a763a17e2ab13bf8836600fb8508d52d72d17d340507eedae5bd373a2d46d12cf4cd6802bad04670 771f21decddbbeb18214baff1e9c5f566b214f0c69232d1977256cf1ce92732475f529867dcf3026 c19477f4f8d562a8f3687da166327ea7e46e51a693c695fd7501481a8a6ef5c972741a150ac8e7ee 397e89aebcee642dada4207aef3ea83d0aa33b71330e4b5bf1c25af9fc3ed33d79ba7126d22a93ff f6487763117e2b968c3d497a52014bea4c24b654ce2b52c72b3d938f0b73a050d96b3a1ed0549603 e22f0940ca5034816a7485d07c23553fd05cd159cc02df708c69f040574c4545cce5f1da9ab6ddcb 404ac599b43c1bb51a12746cd4851d41b538974f755816a67b0c6fb7fab4a4258674a6026654be74 478fd787b339962a0992bc9b5d91bc2f0717f23e6c9dff5e6277f0fb96b036f3a299fe7226860715 aa7ac2cb8acb8e37ca735a25ef2a9e1b3622ca127d19a88123cff35c5ed4eac73c4fae7619ae6316 0accbbcd414caa3080e98c9fae5279f35e8f1f2e5f95f826f9981e3ae426cb8f489893e6c42e71c7 89eaa9cc4652e17e49bcb003f9a0c2e35240a5de5567d1a562f35eef91dc467d568afa953c655a17 be16d0f27c2ee8a2de2d047cbbeabbece09a7168595fb9942222e151c5edf711ba5e53249e64b284 d1c1f2446d3f810e7671881c88fbb2b9f732f3ee9ebaf0f348a4b8ecfaf3fa55b1d157e20e2a4094 7d766657cd434645ef2bca5c5f6dc7b72f7cffd192e74c3435de75de021766660a73ea4ee2bea1ae 01211fe15428931553febdb8b333d60fad684610d775c6259dc5600732b9d78ebbc0e9dda05f2d6c 45b28a44d2a87c41e282cb3f0e344f3b45d7cfd24274b35425fb401bedf2ad9ee0771125efc8f96d 342bad759e7daeaf5750264b1f3614747cedc95a0bd91edcab7a38b45df2b8ef19637627142bfc6e 2895a5ad14243e759d93adaf6c337947c5cf375fc3e72360e3393f1d6eaeb41ffc923c414f10bcd7 05d00fb4d0dc5c6df5568a107bf1de0fa75b37b8086476e7b162954fd028c8e48fc8058588e6d3ab eeb9c6beb1133bb3de2ec58d27db59a28be24abbb8c4513d876dd42ad86e563767bf814626893d53 0e836de8b8b8d3d4e43fe553d7f95dcb4fe5918ec2f5c8068eafd02783386792d65b7a1c6a1d9e2e a82b466e383b0a921a0c61af58691f1e90eb6e143c9fdbe95b8c130f7c91de871b758e2537d06d9e c570a857c090e7105e1bf8b0baae99ede6ca3eb43b2bb2d11bac9aee64baf4286c1dc916fbaac43f b917bfaab75c93bf7439e4223dd0f25620d39ecef59f32a0954e3e431af34ef1e06dec281b3eae87 116079b151671a8e3d97e723863c2461bd47f8f3cade1fd555d3d93e97de716b2e3bc1d259842c16 3fe9b5e0fad87bd17f13d1e455648ba838bed5d0715aa8fe925c287bcf880fa8c5b36fecd4a5519f 5d1d11193d1064a91f271e7bae16aa5bb947da7841eebe31bc94ccaf6bba5e5935a3ec6f49b72ea3 45c89cd045ff2560a838a2b6e838c592737946d0f3598ee2e7d92b25cd94a5a0cc16a59b362b3c5c 77aa6ef4ff90e863fed2f22301905b00a0bc42da00ee9f250087661120b9f116c057d202f02bd788 243eb4f64ab8f5c6aeef9692298fb93cca2e4f52cd78793571de0c9cf16b3ab5cf507361cfe4e6c6 9ed153d2ce5550d6524c42b616cbcbd3781acd9be6a495cb83f38ae263402784af4a3c81fab7a85f e08c573cfe2dea17383f49e1bf44fd02675c6af56f51bfc019bbeaffdbdeff70c641fbdff6fe8733 b686fe1d6a360465a2d901307a3f0124eb9700525944a920106d80f45aad48f61f5f00a92af7b096 c203bfed26b3ee0bbd206eb245749cc91c1dd967a283dab9670db3ae5d646f2dcc1a6d159751be50 1caeae66798a6a7a9df4ee0fb0ea5ed473fdc0ff48624bf567ef8172128a7a5fe87701bc33e58832 0101a48bef23b13d80308b0e40ce4f2afaebad0736c4bdbdc0cde45d017dd49c538beadb977c14cd 7cb319cfa1ade5acb0b58a669634ef4b8837b164ed62aedd8dae1f8a59fd0188eef59ee6a4d36d99 81b958d8af0a289790df383fe70deb9f23c0220c90197988d8b201407cae0f2ae937039000b1027b 65a6bcb0512dba23546f38196dd5b7f342676e2d9f1066dec3f7c1c4166fc6849305d92c3f9aaa51 516ac693e9ed1ff749568b7783bb5db19e70c5aa167d2d5b8f2fcb37d4db280aa992b84488150420 b8780488560b41a5f51c82caa4c6834a7b1a5fa103924c643d6e3881dd146ab5ec8bb61b5937a1bf b04a32b43531337534e17ac819da29753634aaaf6b0e691b7769ea286aee3e9122449d512ade8afc a9c4bbc1fda019941bcd17285b68ef1b2adeaa008456285029a3b1839e1a838a3415a2bf082f20ed 4adee3f843c54d29c98e8d6ac4d85a119da5b9e9a67626cc859f027fbdf8168ded26152f45f49d07 bfaa3fd4797d7bbede9917af6c47147d69e46a875f9288b306be711a39e573ded0e31a40e4270d2a 633a092a776402aa09ea149ad2250c9c1d5ef2f8dcbdeec846a96b2fcaa7a90511fdb5b9a19183a1 ef43dad83ae1c9a876b24faddd522df5cc31d7ebfd929494ed8a652f8d4a339eaa9fdbf5702ffb8e b1fbaa80f2b01385f4b5e94797a7f2f51be7d36640855452a05a44e7a195389dc2c6e412c41e7afc 937b1ebf4db79c736732b08baddbcc7c1223cc4470e860eca259896e61de453f1805fb2e3d80762b c1a5b3526deeb8b303a0a31c248cfde995a1f1bf9778d6f97dcb7fa1ca8b3a40bc7794feab5e6cf7 528bb071144e019539f97e80ca65efd55af4dc4cf23cb697c5d2c27cbe761b13f1faa4515bbc79bd 99f93cc3aeb98bdafb764b0c6fcaf6267c2c7346661c833cf13a8e9f92b5c1fa97049497fd1780f3 c420ba92d66f5134f1cfddb35286f8a8cf0bd98872350fda0125f92c2af87ebf2043de4b14fbeec4 2acfed25375f59654ddf1a666a7ad4ed43e1a45197e80c9aa6d2aeb2cbdf9433958338b9777f52a7 e165bd934e85012e65d6e5f89af679fdaafc00b51e0dd0be08aaf54e3ea06074e6f7fb84e4092ae5 79a91607b9279feb7d764b98af51fb46391b0b3e5677c661c5d25a177b4af7a9e9dfaf8ffbd08aab b0e5d34b5298dface58c182544683a8d8979da597e41624bf58f03503e8c22d42a15a1225d35ea78 ba092a6b540aade2a8e0071774ee093621b9729970dd2c83e71da587b76c75d49d5808775d1acd5d 7ef31486d99d5a34998362a8ba70f6888bf1f1a18f8a28656b634a44c7b99d7093012eacbada5a28 99e7c52fc95f392b29b1052a2271fa6c3725f5f37e1fddcc234a9477b3e795e114ac6e22f650610b a7cb5de398b84fb4445347d5953a582a8d9dbb96f9668a90ce89f02616ec8120acd3b9a300e1fe9e 7f80eb86df1cf815bf59328b8fc496ea1f077f2fa0cc4f23d49e300448677cff866a5fce51c7d7b2 de481c4d23c411ed145e4dd52e8f6abe69ce7259e368b662fb411326d7febd94ac0d15dbd4a6b270 5ea1e2adb8d808706d71e22be7b2c819c0a13883bfeeb97d89c1b99dbb5d71bbc776c9ed4ecbc517 24b654e3d70875047e479d3d400552daa0f2d6cf7ea07772aedc1a8d9d45af79b0b4e45b320f9db7 a6b3f577a0a560bf745f9d4bad2bd1f7fae7617632901623641265df95154b6dd31cdb167271dfb0 c7a1c9b0a4cb1f5892dae22c39c536ac3d9d2f7f4940f93a8d38e7971140a6eb27a8348d6e742e25 2fdebb80643ebf24bb457a7679f85e459406a9f71386f43c05b67d5ff993dcade63e5a172eadf44f 687d3e102a613864bb640ea5c55b99a34772f942bfa65796e92147926190c39ea195e186f18ce12a 92c9fa23b1a5fac7c14f25421d8500de3ec600d9ecb50f6a787032172fb586d24e319fad9be654ee eb3d879b3fb3b5057387989c798bbe4cee028a9de6c74f6b76bb22d2b33a5cfbaef69864ba38a372 0f737fbccef6776aaa38029de4789a1e2e3082e607439c06831e46837a63fd55f9cca14d348a2aa3 47a814ad83ca28ec0754d93d45c333f3b6b4929ad7833a5e7e660b95e11da27d2ea23c5a4a77dfcf 9fd35ba3293e8f3d846fce9ad5f811e9269d892e6bc7e5465a9077b3c892f746394ea38ea87366a9 8c733852a7c4f24027ee6dfc970494c12ae23cbb13809c44233aed9181dfefe9a2ad5e6f96e1709f ed749fd37e227587706914512ef988b26a9fc7be9d93d6a153e4c9159165b9c4b3489fc40a4ce5af d706f9e81c07845e97d6c4f651e6c93295164915bbb247148b6033d864ff17898dae1f34ff2900ce 6d2254ed3505c84331a373091d79a9d189b5f0c7eaac25cfeae9be7a32d9db7e389f29b45195ce93 b46e4b776f9014ea3a9c60b96b32ce6f98d4054fc53fd5933f420d0622912c56276a096c702026e7 f5a199adf30793051c81a3479284d0c98e84fabded5705c095dd278d4232f919a8246d3b6caca703 a70036a4eedb2a7ebf79e34b44d92e46941934fe4921e9541a8d34a121910e0b9e9845cff4954115 5b59977cf222208c0d963ad8835ee1d03cf62a7b4a9f747661ed18ffb8e32eac57e8fd5105f4de29 cfb7bf24009e510020edea3c3a9752aecf76df4dab82d98b073a4a2f6ec62177577c24194fa022ca c7f25422fa94402cd433fb124c85be2402e5888db317b272e56e449d5ae80752e93a7b5a2a87bb30 28a576831a5cdabed55e7d2bae8fa3ed3893de6f5f1a38fc486277f067ef0198162254ac87820a51 f3dde9695c7a9eba4b488dee9498e2c39a7e49a837f854126a03a1b910d7ac582b9234cab7a9e3a6 bfa1beeddcf9c0f94f5d27573fc5259dd7dd10ce3de3ad3aad6d9af003fcdccaa7f099d6286daed5 7973b3b881f566a126b0af8adf0e140320f7cf8d20704ba161b4c05256113680aead09b38f02b9d4 a4c7324cf0de628fb023ebdaa20b3c3c3c2217764a343564b90f1b32b67b67d7bb5dd2a910db299c a3f1cb1cf0789ed04e9b5bd3573725dcd4b0470ec4f90d8609a504068f96e54830f8ab62dd1ef313 a8d499a5bdcedd26b77dbe465d406f9a3f6752d642da345881a78b88c6c8cd567c15a0d6091e9035 359b3e744c32bf7b5303387e40beb9cd4ab91ebec824461b750ed00d74f3d7189ed2b66ba37b26d6 3bf3caac6b07535a110ff3ba6ace72762445e74712175cfea059f3e628adf526bddbd5ace68df3a4 28d3a7a521bcc55adb6e715c793d67b2d47973c4d32f82683e516edf7742792bf7e91bae8cd77a04 367023b02ec09067295e5e5def2b99ccba66964a2bb29181574db7d458d29d7c67d909aba345c80d 978b4162b98f043b7c559e8914445d6bb0c19dce0c172f1309763a71e0c2d5fac1a60e838052fb7a 86acdd90d29eabaf910830ddc20bb234c09e4b6c86210f6cb3ae19bddd8aacb7a9a577ac72cb4e50 12a31c05be2cfaeffc0d7d8ba5272a8e61134d9d4bde5c9e77e26f33cfde46f95f921bdc2649793c 6fd1fc51150033b5475d0a12462bb2766d12878e1e0abb14b3384774091543ee47fd5373bb9bfb4b 8fec27979d28095870bd0e82be8566031da790ce5c9e1506f3590e9acc9465169d2d4a10362b3c6a fbe91a418e53c8e80893e77e739be075f4e7127dcc5f5ae477c5d9f3a6d4ded1a80785045998bd77 62c34de3cae051deac33561b43547dbab2b7276cd9f15604fae69b0c9a3a21f27c9645d499b2408c 59e10eb9d3359c0d27cf5d3af1f9985a2e3b36c87471bc6fe5e071cd2fd74764afdc19355fddc990 1ecdd6912cb0af0a28b12d0d9474330120ba3601e5d6fc934997c7af3c284f267350969c0b289f1a 0950a6964d50ee6c17000a79064086ff00105f0c42844aa603239b8182da2059f72d27dff3090c9e f8cd4c0ff3a8fefce806e29c7392a64b5be874431a0f271bdf3d35ebb5c6be2a3f42fd02e7c720fe 97a85fa0fbeedbfc43d42f70c60f5ffc5bd42f707e86c0bf439dfe07273beeb1ffc1b921c3dd7f07 edff877ae946a8819f0490dc994694d81594b14c0194efcc12c0a5f22d122c03e0a4d48d88af1b50 9e6a3c285772f15520c4876910ecdb41ceb7510bf609db6afacdf56be879990cea75fad5bdcb926d d619cd33bc856e36b4f14c6449cdaeee778fbe725f7f4162573d7e8d023ad601944da400f41ccd40 797db881f2b154027051c300bc1cdc017c9072d15ffe308206bb88d7174119af588199f05f411d76 0b3ed9d7ab9e47ea5daf73be8dddb067ad5cd686633fcd7997db274b49b2bcf1ac438c66cf69f2d1 37eeb82ab77babaf4a14cd59c489a4234e0f8dfa9a64d4a8c3ab500496de7e2cd56b94b2c0a65900 f02d3702305127010c55cea116a31e12af844fea7ad1f3de8f86d72d5ffa6ec8dc666edfd271e73d ce70b69c0b6413e29abc5ed364fac14dc14195b1c9e656b0ace547624bf58f839fca7fa096531b14 9445e90ecace108e105b7b80e4e2094caf5c0648753005b0353e46b0b36bb8c52b76d04826e34d80 fcd6e501795d5268b8dc991dba83ea69e9bc85c7c119f919d92a9c76277ddf7af20f6e0348553e4e b6b7623ed85ccb10bbfcaa7c2e3699b501a04e31fd0d55bd3e005c3f54000c660440666f1b20ccac 0290ed1e8da06906c02fee1e6ebd99e7bbc964daf3e39509b0665bee8061278e28ec564eca13287b 9673af063e80e234ead91d753955be63e4ad580db68a96a2b08b99eb2cbe2a0082f188735a8e3821 22cafd6ded09e08554054897a300a2753edbb7233e5d8be4b902c8d9e00032379ef1ca84efbbc37c da632e3ae2f26ba1e348d5cdd4493757b83d9b519ca99e7637ad35a74ff7f480676e453479503488 c22f7bd05c9f8f8970f1a77c8a61bf6bf98bfc276a938dd2ffb7130d4bc569008436685029efe202 74af092a93021657a19f4283cdea81dd14439f5a96525e4fb7e23a1b37598e1751f2abb93d7b4ef7 9602d8b36e160bb7fb58b9f2b7229b3f2adb14b1bbd4af10766e39de4aee06f4fcab02a036697e86 c0a61e5d3f47a75574c6278ca8e373ad28a02936a2142254096a81ca1ddd86874cff14b4085bf3bb 95b5eff51f99943bea9f2067a2a16d7bbe8a572672c8c18cae2f97070f55e45bf192a7958ab78bb7 693b3b3cb4957de9b93e81cb7e711ad666f3af4a84ba8f424ab5e2a5b3db3a1aa0391320957a3b8a 66938f726af7151e26482bce089ca08f07ed5a4df480ac3e5ca931709d197a7ed905a699371f8f67 d5d825d603cdb173cb27d32bb26aeebe14956a8a389e8fcbd456eec1dcfac4efd68b53e23d46a5c9 b83cfd8bc496ea0f9a3f02a0096dfe17ea6cdc89a2890aa09a4012819ba935fda0dfc4bcd7b8c0bb 5204eae491b26561ad43605656a98cdebcd7e32df41e02ddaba897cca9732b95a995b25d09ccc7a3 3ff1973d26496a6b214dd615543c3fc1549ce3cef8abf28df334c87eee9e841e5dea07652b1aa378 175448420c1bede4cb0f50a4e1bdd4ceda4bfa08e7a00df96a6dea39ddb0e698abf90605eef2b39d b8adf4694ed97515e4ec4ee9a11cd443f294eccf3652665c5c8a73d99f7f9cdb9939156eb9fb4458 5ecfe3af0a8030ee37d46f9c8b7a74517a723d1085460a8e41064488859a7bd2a0c5c74fcb438c0d 51eac5ac49f587de5935f4fb252f9bb7f29df72ff6f3f296c34eb1701ab5b4a694b12b1b312ffb6b 6195bb2f8412cf4ff9c7f038898419f3988b0d7f24b1a5fa83e66fa88f71842ab81b00efbb0e403c a50f2a6f4bf2835d32f052e56cc5415fd999bd6e843bd3303856f7d3e0f498153797db66d67a5c9c 77c994f952f3b3cfedac852505b55985f98d89cd7864c8ac38fdb19c73fa69398d048b849a8f387d 33197e550074e48c6f9cb717feb97b72630754207b101e485df45e56e0b9d96d16b6d743af6755a6 cacc703879af252ceca8aed9aca89009e72abfe48726cd51d516ca0710727bee52605b9de4906d86 c315db541a739630da5396501a9f9f72b1c2de98b58cf6e8abf20dd55de43ea8003670373a975283 c02d3f782fd5741da76086050b3f2915e3b817e36771b551a638bf2f4172773d3465e6ccb107f994 5dee5461d3220dae313dbb4c0f9e66e884ca76e8c1bdbaa641025e30dd447ecad0c5fc84f106f098 f18a99e14fe563a97ed7022049344039894733673bb3054896f6a2730919fac1f6c2ba59da32ada7 2b00e3b85827b5a45e2fdf6fde7976db1df5dd85cdeda9d379dc16c432d2bc70647ef060fa834f75 379551a124799b4f9bc73c9a5c51934c664127dac9393d6805139a1b07633a5c07c3af0a80ee72cc b98e42fa2eed00d251fccf6a94da1f461dcf1e6d953fddcd4376fad04670c778149a7e4945f8c354 f17616794e3548467ccc1b3cdf88ce22864fd5556a363958e4ba0cbf09e486d60984ace26489b016 c7dc4b9f1dcfd5c7841a6ff551fc6dc4ed6df055f9865adae7015ca84673a78de187a68a0edd93b7 da477d3e15b4d7d8632244555737402a5dc9e9241e69174e7dd127855278a126b479b63f69cad465 3656498ce5cdc3613b79efdb105bdfb7fcedfe503b275704ece973e2913a8dc925440d8eb915dbff 7b890de23f0e00e444f3ba72838ee69fb5f601208f77101c35a8ed2c72cdb571ec3fb1073a9a9111 e5c2ba929b367ce1ec204ae95f1b4ac49d2ec3756719969ef521e908997bf5602f12e6be7376e387 96b6ef75b5b91d5d12c71dd39b6ff64d10a07b0b5c2607bdcb0e080ca7fa5f15502e1a26282fe522 80d179fc20570df83df0acc5d50ff8911c3c3357b3af6ecc1c7db51f153ffef1f89a7c9e130b51cf 1a7bce7f9d0926ed5ca863d9339883438dcffbee3dfddc257ad1384f9733457c36c786b89c77765b feb65cedba4469bef3968fd19e5852f14ae1e7f50b02ca753b42e59f5132723952a022f443774ad5 f3da2bd54eabcfac34beda92294488daeb9cd6889a0453e198efc0ad78dd9399567af8f1c11207a2 612ae49e0907fc6e38499cb7936abc6965ca0937d75e15da140966b459344d0c9f3699c53621e7c7 db41e7d2ff82c47d13bf82f2e26d81b2e94100695fa9c04d86a6b97f5e6fea2338bc23cac322a264 64f9cc0c43097e7810df39ce3a8cdc3c8f8fcf893e23c87273b90f4be26637aad70fdb6926cde273 cb91374bf91eef6481dd0108308ce815d7daf33ac0cab4bfd8147069bab97427a3af4a14cdac05e0 6a0106958443b8d916b6571f7e6a736d954771e271e10ba5b93c9fbd7851a70483ef48ad2433f5e6 f988928522ca545cdc49b676a37036da668fe5055e98c7fb758e3412dbe0777ead5fcdebba964ad8 2b8beea55644e5da581d6ee9e91ae7afc38fc4eee01f073f1510254811eab689f8bea6b7d435b98d d27b4425cfe98791382dafd3be58433a078e2b1b3293a573f7239ee9e944f37972776faa10c47d23 f79d34ae8cefa58d3a3fd7b1e78ae9ae0dfc305ed7cce3724536a4c3d2a334614957d2f765c71c78 8b70782d2d5839ddfdaac4d9daf1b2506d259fb8df1e4b4438036ea49e963a4e8bbad40f84262bd7 d8f7b635a4d1f0b0208df9033fd0951ab91b053413d1b5c488aea24674697d6d6c7c7765efb5d7aa e9e89925ddd64b8b9075e3e5d545fffdeaa3e2a484a2e3cc648fa62e9284a694b1f75531378513ad 2e131c710ec93d2b3e9a3389ef5ccfd19f95f495c952e9d8eea620f19426ec15147ba864753b7d81 c6669ddb7631e4d18b226754a3c8d5e16dc4962423b624b7e8bf5e27541cbdae684a7e69f3593ee9 ceb3b75a62b680fa85a98aef9a9104b33fe553d7f95dcb5fe42e0f16f8a5934eefc53bc21cd83756 2b516a37ec1c11195a11640922f7e12e94762986bce105b9ac61c85db256646de82de95633b1e07a 50bc4717fa160a109a3a8595f92c6b3766cac2e8cc0af7e770ba869fb329a47bd804afc5f9835360 c7fb765319d702c2feaa5c1cef1a5b43d20299e32c73bb60c7e740090e1dadf8deca9d470e2f488f 2af6441fdd754da3c64b8f40178b3e2860e838f13ecc67199b9e2d0a0a3f55d7b23c796ef7970962 e1ead820f6fab8e6e1f688ecaefd5113ece3dd1286f4f0901976937c69c04dcfb5fe5bd1fa5f9553 16a9aeb8d66db738960271baef9b1979a38e479715897097453f68ddd094d8323f76f77910ce0ab7 4974da421834c12bf3ead8382c3be39abb1c8ec8ce7c3af4b8213aec267aeb013769e0fdf7a57ae8 8f0b30d593d730dfcb6ab0dc55b6bd7bb7604dac8e4a30efbf48f4313f6afe08288e266224a60e8a eb6d1a948ae2189486a8084aaf660240bd4774bfede70f002ab4afa064630094c45b0594b064f4ef da8d2d2865300114836b7ce9044523e183a2d28efee2985a24fe00140ff565a0b5fd955f57b7a8db cbe767b64409636bbe180dcd52ebddff82fcc9f9f9987f89fa05ced817f8b7a8a0481e33ffc539d7 234e142600541bc66b04dfa34e62d41d28c1dbe8ff08cedfa12a52fd2fa89e4362714833df38f3c5 e6e0474cf133ec3f445d2da5489c0855e4b3a034784e4189a74e11e5260da04b264a0bf4390d20f6 f088788f6f507aeb5550ba9766a0749c90a03439ca11b169805236f7faf44d382c4472ef80a2999b 8558a982fb87be84b9cc165ed8d2899d5af9417f62606775a0578945ef0b028a04167112c00045f5 9cfb046d614708e07101d0f914cdafe1de34128603e58c166586172bc2ef077500151b2828391805 4a92a44411f61d509a3652a0842ce1489c61880dc032d84ec0ce6bb78698335cea4b6bae4ce34cda 78b4c2a9668d06fd47b0bf77be20a0281c4e5134d32628daf73c28edc33980baaf2b28a7dd222863 5b14948fa608ca9b9c0bca48e9e3aa435ca601a0417b11f16e9988f7aa82d229eaf812d5cb45bc87 5a8871e751a0397e144df67a705ed50c6e5d90cdca788cc3b966118bc9a39f3876ee49bdddfa91c4 4fe17ddff20d552d5aa0f8d622543e850288a9de4179922f81f25d5a0038593e83f2a3e583323589 da90610740ca7c05a045146ba86e46c322598c86ef635e0ab1701957a6047a5d9a07552c9af504ad e1c19eb827dc2c39d04ab384c5ecd1aff30375dacfb46fe84a6f7d4140f1c6c85134cb1628159c02 285d0b8b4fd0fcf933a29cc000ae83358087834b242488a01918944f7c34b3418e18808cab00a03d d001d4a826c2cdb40d85b0b1ec06d53bb3f21bac443943f022cc551dd96896852fefefc471a4666b c9780e7dbd796c5b79e6d0e61704142d3e427dd76d508a2e2bd1006dae40b9bcd5005c3c56017ca8 6c007ca56f919889e8d0af01b8e1c76954a84dcf785819307c6048d233a85df8c0275421ef375b56 d3a3d03ceab2e3216da19dcd41df31f2e6feaef3b3dbe5150cafab27db579043b37da949efe64f25 9e75fe71f01b6aa9d08dce8741ba04a05c7f0dcaebb301e0c53dc202eb2861add87780548b2980e4 5b8db0faf027416380617e2ba8d21e6de71537f4338edb7f65d38e98ca22ce081fc5b9a79d2d1107 03197bfbfb7bc0a337d472c7ca73b6195cf6a56ce74cb68d96ecb15ce30b028aaff3398ae620e25c 14a1e85c5a6d4059f5addf9622120d8074c5286165902740ceeb4c60378b359f52ce238f9d0cd7ee 9078108e840b27fb5c5ae99662f48155886e9626c48edb06422a9b0787f8d86d917fcd14bc321d9d 6dc9ebc9de856b9f427b16978f9e06ad7aed0b024af9ebefa8fb4a19403881033891b30192abb700 42db075049afb4b05e5da47c1a4f54dcb7b4ed39a7d72bbed8d86877b7b3960f8433e1c2f3a69bbd 85a5d95e2ff9ec9660f82e0edae39bb22b2d954a6e343993477b2077fb78e73458b75a92a8e5ead2 1807d5bf97780215bf7e43edcf5c50e25a3080e4d32e3a8d2a4e14cd7184ea170ea199269ebe3f1a 24bc810260470e966dbb98d2c76634a5db18b59a4a696d212d3e124eefaa9e6b49ebb6a0ede475bd ca563f45e45d742efbb0323c71f6b82749e9625b4acffca638bb5e6b626ec957be20a054d52ea084 2e224e658000c87beca3013a700182af5b1f33a5352203674b3c3cf0ec003793708a76f13dac9bdb caadaf3bd9c2e2d9bf25f07bc62991b7d26ac52bda15512f07f8ed9e5b522e2fd301373a25d6f5be 94a641479c817b4bb80e99bab04c1e6a42f184c5cf147e5eff5e40a967fe8e6acc2aa00cb9070073 d121f214dbb1d74bfabeb5bab912403cfbd63673162c2e2b466349359f7c0b8fcf1b5589e67ad767 9ed85e2ccea6ce6d76713af14cee2149abfa5b4a3f9d8e9833af5d61d9383485a28b35f83b35aff1 586758e5cb611bf9827cee9e1fd41dee7d436dbe08001ba4072aa967273cb408c27b35a717e7b244 6ceba13b49c34ecf8b5a3f8b20f77cc96c5fb5d66b72216785b5ccea83dd29f9d41871fec02ec292 633c1e3b55e321c0c3d3499bd3c24693db72703d926a8dabd6334824f9af0828cdcd6894b2647423 0c1711e73a4f02242bfaa0d2f4da81a36de2bd1fbc24dd11ed9b0b69a681be814e5793c947b645e7 6f5878a85f9c1b3190415d994bf2cac7c4c27042f29b4546e2aa41de64899a50621bf6abc936d4a0 ce3644b7c63648abc236d616124bf923b1a5fac7c18fe477d4bd07a02c5e0565b17a04082247215d 67db7e509d6edc6caec95acf4d4d354851319fa28bd9ea3a3fc8289633ac9c7bf2e78995535a9f8d c5e24c5af14898dcb1645ae0e8b049dce9fe369ba6fb73b641b35bb64607e7539569fbb70ad3d62e 30d33e9fca5f1050da3ae7cf10b8d03e8010b2165d4c1b11eaf4e9c745936def4534974ee1503b9a 4608497a2f3d38df9785e07935dcf7fbd2b924a16848669be2522d0ff86a693e675be209a3b9db88 a252a0a91cb32e058e33088d33692ab55a5529c12222115630f5ba6dca74efb52b7d41408901d128 d5c500401dae0ecaef210510caf6c346ea5df7925e766a3da577ecaa1badb7b87b5c86d1ad55f385 abd23294401e8f849c54383eabbcfe045d96421a137ab8dd2c8f671a22c8e2fd7d22cacac823ca7c ad4e1673b5ca312b8f2ac7e973041f4fde08a252a951e94ff97c9bef5afe9408d58f7a5fe7a2904e e50680fb181d9de5a920687745d841b97ddbdcebfbf1337375a6ea86e3f96b337fb99f8747dc91ae b34d4aa8f4f710eb45cdf13606622239385e6e9b05f138a4f687eac510f68d7bc7d9374e9de6a19a 44aa44798920847aadc1e422d78448a5522b7e41a25e4f44210de4e03381a29e4d00ef0e74743a15 7c4f28d573d6f398286bc9593b77873a57f46a638c78e152ba71ba5c704784af44923b927821de38 a446e526991ef17ca2b3433d096d7774edcd6fb936ea6c3964d5dcb54324f63af78d4e16de9b9b14 74c0a577912897b2852f4814cd7434b9879f212827df2d0007121dd629d5742e97bdaffbbc66de21 3828de6a77265e6157e81d733a8fe719475ad79e214fb8448279a7c502852266958459b87368ccd8 c9aed7e96cb609a72fe069fde4e069e5d4c4c53dd1daf617cdea3658a4ca3bc74895f675ddcbff45 e26ff383660065ca0a80daf6b7872391c9f5e8bd12b9b3b9c7dbdc1d2a3f2e577b7d8323c4f9e62c a587e7d33279b104b3a5842c404e493a971773e4934f570e646ddefe14c36e4bd3ede8e16ef1ec21 73da1428ccc7d45cbbb9c92f339dcd3941d5f17102ade2ef44ad1c4d2152c52f4814cd4ac489bde3 4779fabfdd3d13dadebe5d1f8bfb2da915af64538832aa3c55398fbd17765a1e2c5930edf793eb8d 2a2e3d9f3400a9c98bd4e138d18a3b7e3caf6dd3b7ea60739de55718568299b5b661f475c59cc0eb 4a83e9615063187f1b6c89ddaa9b5c7b55decc32cdd217044093ee1540a7c21bc0db51cd03c2ae79 2fd85b5bf14eadc9592aa1863cef76aad223baa50bc4008ab702635fb7b1425ff667e308bf5eee81 1ad6c29d80de33db699947363762dec636dde1745d4b4e76ab26229d97d4a10e966dd76cadea8f6b 7b8db8c7fa7ab36954d7f7e056fa91c496eaf72d00921637506e0c126eaab536d55bae2f2ae483ba cbf376193b9516354bd4b127ccbb6f75c28af9c581464393270db4743ed095dd7d2776b2e6362baa f183f14c267e1cbef429513c341a4b8f1a4e16dc60be43df927641c5e931898e40b1b9e819667d79 cc4a95286d3996bf20a09c2055007737c17dc62984d2ecd3cc677fe8ad288bb594b3e3bdaa62705c b99c67e4c63d3ad5069311b9cfdf96878e89e0bb512092f822dde236d02d2daf8d8d795f9175c95a d26d1e2cfa6f2a878ed3a7da7c967727f3ac3a21670a96d6226162bb7b3e4b1611741425e45f1050 5e28f78760ac37ca412dee4eb29038f27617bfb22202af99a97390e8027b368f787afb269a8f776e df77f6f12e23dbe93b177ba84ceca1ce630fb5b75c7ac7fc7611322f127d0b2e3f9767b7f34c593e f4e84a1cbca690d98426cf8330983c890939c19be66d82b7bade4fe55370f9fb81de588ad8b5425c 5772aa71c479132be1ccd4324a473c4936c99a622f0f1ef626f77ddb90e267f8d58d3a2beb1872d7 dc95bd3b82a5476e3211ddb414d79f22115d2af64ebd782e302bdcc349ec9cae2688ededc7b52025 8cc87e5f1b7a22fb1e7ad2a839a427a7d917e406dd8e735928f59642a5fc58d2a75c7341d62eb3f8 46b07b1fb4f736cbdd8a1b75a237b0271a0cd7fbb2be587a04852fb8eef4808ac30e3397a78838cf 2a90322ba86f75ba2e87fa04af3eedf1be29fb239bbe26871e7fce0fbbc93b321864cd6e5f5ca6e7 3d79f3ff187bef6e7595a47ffbb51849e69c330a485044946402519202adefff69f69967e637679d fd9dfb9f0b71edb5fd7453745715ddc520d921d99b958dcbff01a75ea33457ec9632dfa70ece7853 cb66fbacd19635da9ed7aeaba673b1c87eb4761619751dcdf3272133c36e2c3a2dd874f1e7da54e8 fad811e824874affe45027c3f761b218c45a633918e4aa4c5f5d94f87ec62ced7af9077aec92d5d2 b9636e1a76a7f09e87ed757793fd33e0cffc1cd59b2a4ff707c71c6e58e3d45a9fcafb01c5e36049 f6c32135cf1b397e62d3ca765cf317d250ea950f8341faabf6331753efcd70fbdabdd0f6bd4b967c a763728edf29f8f6bb6d6f7dd02e876eb2b4b7e5ecc35cabf609b1a6af2295662b8b341ad21cef35 bae860563fac66f4ff010075a62c402fb7234005c282387d01360f7a009f7c0f3f69bb97fe06f83b 5f04b83d9a005cda6d01debd5f018ea221c0bc290e3045ef006cf52101361e8a006b593780b58b01 c04af334c0725a017e722b009ba6ea11f3dd57de3e5b2abca4a587f9df78912c80f1c76427ff67fc 412a933ffeb3ce99774b74463f3a93d6608ad206d836fdff4b1dbb6622f5f52fa9a56b31915afdbb d42e71c37cf5d0ffbfe84c7e068d571cd4fb54007aacde017af5e03f7f56e0f87c6faa8068781120 467d186c37377328fa2c01dcf8580067db1f805757b0bf3eb71ec07c8202983adf43047780892dd8 109a85ad983c2a3fad61b2adb8700eda612d1cd65e92109582547953f0661a86b9d7ac8ffc1900cb 335067fc52a1c4b60d5037ca00bc349e40898c0108a5f4fd89086e9b263cb9afa0e8970c882cf100 f8639a827a372580f79e0380d7aa0cc0bef4018a7e5800b35a00e2086d430c1ab139cdf6424715db aff7876804a98e5cf16656b9e8583d03778aca3a71a07e8ebf0260e50d0ff54650aadbb30186a2d0 3645690e0841819eeb67940605f4dc869f3e3420d4dc111054cf014491814d0acf5580efa3f18f41 43e708e07d4181a2031be0f8043624a5c10bffca7623fbecf6dfdbd3b01b0cf45bc33bd54735173f f9e567e53d201e759042ff0c8075e49fa20c5825ad010c593e01562de5011e982420dc2774b3e63b e861b39f1e9cd3aa6c92ecd70161ce5d406c953c205acf0620927d55f87328005cdeeb00e7c227c0 c96536a6b47d25b2a36b2fac8f9749f23e00c357c73b918b9663b166f5e11af5e2bdd7a611eb28b7 737f06347245005817d5a1581aeadccfa1733dcc50a0d083f757e1f14440216a0c7e2282a4de7041 16cf49b23f00847f4301a1a56177d30dd8aed27c1bd30bc1888bbef68c1e14f886eea25a7e07e346 e7b547d1819f75b8ae639d9ce64390ea55eb535ae266aef9ccdfe6929cfd41b2b4f7df277f03b4f4 f30660ebaa01f50a0ec0b3c702208e351a144e131b14db28068ab4300645e8618362c9bd81c2338c 621bf3b0b85c7cb622aef79c4555e595ec2308053b52c2c6bc747f07de247e85b65c08c0556ff993 ce6ee858e1bb6bbf2a48dd1aca8dc26d6eebe895388e7297fbb0f33f00b0dd0dea94da3f913436db bb001f3f4a8000d33528668507286a13e8c504d614145f88048a7ae51eb9981546f5ec2e1f366f8b fa7b678f46afc8ed33af7e7a24071f86b906c3c32ef0b520c6bcf921df7489496660bfba48e2aa9b 7a0d2d5d6fb30d7e611a3078ac464eeee4c997cc9f0130fdb90598313cc37e3d429d5aaa020add0d 078ae3d313945242019486d959fc24ba893b180932b885edac10bcbb4a2dfd1acc32e5609c327bbe 913396ded997361e1a4a9abb428d87731f23992787d62bf66b5dec9a532c57bd126051383fef187e da700662ecd475cee88c67d9ff20d9bdfadfdffc0060f7171cabed09946a583e2052951a28486701 144de02669e96461f7601e6d3ab8f88eb6a7d34b41274e3099be808f683ceee1fd56d32d66918953 3e3e98a47e5a6daa49f62bd85fee32a705d6a815a166ee9bae5feeeea272f2737ec1d8996b4c97c3 565efbf690aca60090f933a04e6ffb1f9d3f43e76000ddd6f76b9ba47ba1d40b28c5b5466b1a76f8 14ff52424d0bf28586edad70f9ed9616adfcd395ae95476bd7eede072d7d6a65941573bb204de986 0d19f35ae83cbf97f2b256393539b5a8c7e30196646ebf2c8e6a19c1caaad3d73e05714cff1970b0 8b6097be9717a8d74dd2d26403140b1fa873d0f240192996a2a65e19bebeea830ef21e9d988047e5 3f67c729ad9cc7cbb87cefc7a7879827b659b99177a77b2de2fcececc833ee5c3379f5d43cf981d1 f10798f6dd23b896b9baa83a238e39e5c2d019058b27a97f429252fdef6f009efe8800cfb057a837 80522f7c13da282a82123ff662ee8a17deb193eabeb22d77e55985c9c6ad0869edf136f3d63dd306 81893e3ae92badabf8d9d52af5d3f6632543a7d15d1f97fa60a8ed349501a69601c7ac8aa4f7a842 3274fe683abdec91e21b9963b15e4a1dec00f9fe195067045d8e82005da362f60d88f8f0134917b7 d51dbcf0a4176de174f84ed5dca67f9d1f27eec398ac9fe26423de55b0364ccc149ed7f2f0199f37 f57ade88aa5a491f561a2d6dfc42a72ab2f8b00aa90d8da395039f63912ce50f0f3c973daccd4fb2 3eed50a15e29d92d3e3e10eeff00549916ff25755c4cb27d9736bc97da5067bcf6c2b7f6c8bdb25b ade2599ed87704509adb9f5a2d49a99a570657aeeb7dc33a6fcbdcdb904f564a4f1b6d5c9d6bb79a 42604cff580c99d5a1e2e90759280e5fb2d0f2f272fd71ce40dcd2fb80d553fb607efc2400ff8464 e1d8bf4f004e40a3c49b7be8c86da1ff53983a9d7f25fcca5bef0d72fb548066c582cb15e68d67a7 7eef5be799baba95fad6fe1c2c5f1703a46a9e9ed950405df01172bc738bd2810d0bed9fcced2a33 dfb767cd9db4bfbe5c697f63b2d25e59a5a5fd66fe95f6ab0990f6a349fc7f009498835d3ad24c80 9be3181476ef0e28d5e6c9fe9bb846761f2ff55b09bd3b5acd3a1b8920ecd1b9593557cbd9ece28d 85ed69bf3b9ff4f1c37fa8e4ba1e1e6d5acf1e6a5499d8ef844b5deae70fe3dd28e508a2de59d9a2 de2da477e919f1dd0da5ea6777b4dbc913f6ddd168c4bba3548dfe8c7f495d9da0d42f0540e1fe4d 8a4cd1bba86965cde0340a3c9723b4f0111fc48f4536edc2d5e9e0a373a7d74b4cc048f38caaa1e8 c5528a572438d417f3ef5e9af9c8eecbac2be2546bf5b60b996236660abb6e6e8193da2e06d157cc 5d731f71e2e68038b9e662510368f42b92759dc9117628fa2fa9445ffa8062260fa5caf4f61d3f8e 864f5426b7e7bb4c5877e3717fde6c4021971666758def0aa5b5b3353a284c667d3908dac3d9ef29 d83b1a21654464d02d6ecc6731798983b0a6ea2bde39191aefc8bb8f60b7efdfcd4a7c7e3637ed19 6f51eb196e1729effd674089a51dc037960508d94a81e2a80c7546c2e6a57eda49d55e97c3cf7b5b 35cb8249679ad76bfd5e4c9f8e59a1a9cfa6cf855a3863e2c147067a522fdc96d22cfa165138456c 68ba82f3ae1236b9608f2fb8d64755b996bffb70fe57fef27c5f8e85b2ac44c23dd293f483707f2a ef3f03aa84e3117e8301c5cff6b8a2d26c471b3347f9d71d473de2bd3a34ade178791578533ff738 3b36b29f5e59b5487d7ae4bd2fff53e7b6d152a4d1f7688a8b5bcedf3087cd87f7ce4584eb64406d fd51b2d3f52823c9eb61b889d79fac9a62434af9705b558ef9da577ef3357ff73ff0e3aa2b6d1819 79c086b73d9e0125b4d3781f66a3be5bf5c7658bbc20dfab707627e77e6ead19fad6061af16e948e 1e2d8fe4bd61d3d2a48c4b2219b2a7cdda4e3ff9adc6856cec57b26b952d561963334bb692d097b5 b967f2a767c4e4ab4e669d66cfdff5712fc76cf7b179b352c8bdfe0c2871b20704863ce0bdd4823a f79db25b9110dfc25aa87dad7ba5c41738297b716ee4c7e38b66f61e69c599b1b543e77c1bed33bd 3c2dde9cd96ef3dc5b06ff5af76cf633b9bfd693ed3acda055aa4c13cdcb84a2f7a303659ddb1145 7dfb391abb7d53f4f976ff30134f8918ed23279df673fc1580406918c171ad27282ec9b4f3fc7e34 f37a2d1c2f3e53334ee9c1c6d1318366545b299a8a9b39e50e2131a9ed55ff3eda919bc26ae3043f c5e885562dab72838a68ae0da61730245e4ed38567b94cad6bdc64e588a8bce273ee7b55e34bd88a 55b93455dcb6bf9449a563ca6c7ae19f01889ef8937e48f648bdc3efe06ecd065bfee22d28d1501f bca2da3c862bfcbdcd1fc532b81f7a4423b39ff45f951d818e7a1bd731e7fc9e2bd25ceafdd8ade7 43516788fcd2a61ed6245ab9cf15ba0c7ca5bd6cb789c41d246180f520f7f57966d9aa2ff3cb4d40 64975e918d57d5e23cfa3300b1bf2aa0881bf65de15bab6bc5038ca1009757e9b2bb3f84ecb72a7f f05cb2054b4ed3474b9a7f8f1ff1ceae88cd661a3778995e0fd8e9383b5f6388cbd1f7bbb6a7389e 35561b4d7c2e3b0b01907d4a27164a541a2e32fdd36eae6b0377aed3567671a4294076e956484a74 f1fd83a435ff3ef91b001106eab34150cb5be173228d8f40532a45361839eaa38274ae1ceabbdb8d e344bb983b6f79b4140852d94c716a7b86b179355b6128e4d4a6cb772189a4a99ab34c72a8eda4c0 5863bf50c7c4696ecc1bceecb25a656664e1d39a9aac264ca95acb8450dfb3b3cc7bf39c4c3a7fc6 eb407a1b1343e96401cc69bfacce55137b9172b861c8ddaa7e88b6b5035211de3439e5634ee539b5 a52aecf4c3df1873564f72a84192433d7d97efad8c90313484c5575935161963da9fe7afa3d90cbb 4fd7d382c31c26e5e0a758e398ef1452e35aecf546fe71c9413c94d176783afe1996f6580ecf2df2 33d616b5dbecd01a35a7e235a286825408022eb3db7fd7644a21182af74cdc417a8d4563aaf6f056 aba677e7c95892a5c5f7c8a98b719abbcc67f9c57d46e27d774a956aef89cdb55263675b4747febe 551dbe95f160d8cda8cce030cf6b8301aa87fdafd92ff7556ad5fa0fe0cffced9b1f5c6a176fa0e7 faf2f8b06de6473be45ceff2bb25db5a2fbe7d68acb7b2b57cf38e43f623e3bd187d1f99f92cfbc0 6624fa284fcd95db9c16eca03f595782c9d8118264a9d5b8f672e95133bef2436970dd0d0e13edd8 ff9eafe7fe18731fbd59318cba17ae8a76319f6d75a816a03a547b20fe19bab66006c7daf23bd8cd e46cd21a5ebca315066f798d55f39954b4eacdf9d96556952636ed1dc67ceda60fdf7bee3c889595 d9ff1ad3477f8c2cfc7ee6ca45bd19c17cba17864a773187cc754c9ec73b85802fb5d7ed6dbde5c8 875eabf6bd24eba19bdbb14337dea778dbe8629d533d36a7d69fa1943e99be346e175b42239d2e31 e8278392d16c549fe7f57273b22e427bd836a9e1e030ac4cfb9973982c1fed5ea8ebaa435505babd 6ec2eee7bb43a1e91f7b62e3ad13bb46379fdbd7079827d754ea7eac651e37ad9af79453856c2ab7 b2292976b900ce7e693dbc454547f733457efaceff0df067fef60d40c6db39408a0e07f2d01707f9 5af90c90722906e846ec020c1b0b00ebfb37800dfa5f80d50f1580e1df29405f1d01a03aa70374e7 b9005dd752109b2240677fbd201d9d4f5600ed1db710a10ad05addfa7fb118db119301665805daf5 b5631aa73f0320940475b6de3cc83fb103c8f79b3f252690ddfc0b50efda071827c1d0f582dd2178 18741f9c06c0968505c01a53187ea7953314fdf1016af7b30035762580c2890da0fb2503719500ca a50c286cf04cd439f09474a33b554c561087c99bb25eafd7dc0cfa8be8f2670064ab2e60bfc6506a 548052e5e915a069056aeaa746008b9c3dc0cba327c0f36a0e60f1b705b06bf3678922267012c086 3718d336d137c00a8b3cc0327a15a0617a082170007d3c8e007d566e003daf0308e8f4aeb071103e 1f59efdd1c8acf407688bb9f192bb77f429255fff7094054e35f52915ced08f572f0ff2e23046072 6502f04ee6007086f3003eb51180770b1d8023530a60ae2c03ece85900dbc0c8011bad50883becf0 1631015851e07f5a530c3580e55a3674d9b7614c7c6771542e16dfef8dfb088243beedfa99edfde1 92e8c8fa3300629a2440c4bc009052f30890c919da9057c27edcc11c3907b8de5101feba41afeb8d e10037fa3d80af374cf2504201389a7b00cceb7d0076db1100535e1d806dab73805182109bc5d088 0b0b1c7a7a6a250cb91302de7e258c834379f8f233aa97f8692e595d38cff5d4b1fe0c80f83694aa 6150ea64a400c4fac09feecd0b00172f2b40343606205639e81fac16254014b86192e9e7924cbffe d31ab6ea02bcb74a43d14a313615af1b53addc3c2e8443215a57f7a7902f68cfb7bf7e84afb7f04c 05b12e7e3d63fb8a1cf3360a1e4e6c3eef5d2e75fb15c906e6e40890afbf8402cb1b80d00b788f0e db2ec01e5a1910a99802c4f17e01c467f2018427570071f3a68018793c208ac429a67b252f7aacca a968fdee1442b7326a85fc75330bebc31bf7de4ab9a408d04b124a7670d835debe7a99a63dc3caa7 9de2a813d9415df2adc1ed61df4eddc9edcf0028f6863a831ad429ae928712b40ff0565803c4b2cc fcb88368fa0a0a73f19b64d01ba0d027e67139e3f151f57ad642e1757ebe835801ef76fd82bdf693 4bfdd58ddea3e058cfd3c140ebcbfef8bcbb7b08b50b5cf261a69f8fe62075df97066f7392393a37 c4ed25bec0c5d222f3cf80a35bbc0468aa09a55a1b68fd95130cadb4620310ee820385fdd00285f0 9589ed73a911f14d6f12368f1cecc169fff0ead79bc9fbd602a55d7807e94531e74fb6c58a773a15 bb1ef2e992ee7523890eddb5afcf6adaf11ef5d8f958e0dc02b779970e2ed63ded9c5959b14f823a bafd193f23f420034768bcb381d07580492134ca54b7090a950d0f55d2c9c26e2213f1df5ced1d76 f5e1eba08fa8608c67243fcf1a676fb13978eeaa4b7e1d8618e2cf67b95d7f78786ffc68b497ebfb 5e151313b086f3bd654e56a7f7f5eacea3f333f7f44f82cf38c62edb7ce832f236ff0c809279a873 b010e127ef04b010890131205ba0b05678501c6b49a745ee1e7cdfa17d2ebc868d55c73f9199b947 3e0cc12d1606ead321c3fb03c651af7b14d65296b2fce2a6de899b664e28cc6e28039d3382d7af97 92f8f4cfd5f3333676c5beaff740ec68cad27e68e9bb94fccccff1570094c712a970be415de20a6d b4056f9ec3b60d0af65d0045d53243f1768d5e0ab243fd73b95673efc5cbd0a9165af443c44fd27d 307a259d661a4be67ebb943befeb2d40d297fb39553c3f55a47bae8683d5a9f1b9a8c66e5c707599 ad45daa861faaaae1f5c359f271fca82ac9b7fc68fce1f13205770ce44aa50ea76f605446c747ed6 a146ee89bdbc7bf6d20bf2e97eca2bd4be05a786522d3b224f334b53e03c711df6e52b25fbe74b25 bb75ceb5db383e35a536627496ed9a2eebec54532e2049106b19626ca9d3dde1ad2c0e63ff68e275 e7b8b2f0c7b140a7ac3f03a08702ec529e813a671313e0be980685b209758e3e42f842dbfa2b0daa c9ede999e55ce838949d7b7476a06c4dfbe8e0b67417abcbe3e98b67a1486a46d800a61ec7d740fb 5a4a5acbc87251cd6f6f7d65f16c0b0ac63dae472a55090ef624e51dd6d9e079289f1e0fd9999fac 1f24d1dabf4ffe06805eca50ea61b3039821dc61e47f8452191f4a5551e12d13d1d1bfa49e1777dd bf39cf566f14df33d612bb99855be7f2d4f3f37343e57963afa49210571fe6f7172d4bf71ce5e2a7 a3a369c4c8b1f0a9b50eebe68e929d53fb24f3f39327d7d1a3b3f76fe21362ffd86f474bf3cf00a8 d3a4a0d8ddee67927e1f6d40f4af1950d0e34e5c79df989732dd899e39e154a74e4d2ff6e0ee7826 2913b9ebbabb699c9b9c393164b6cee8694693d4b958d515fcf3b48e45430e0e1541caca75c6a8ed b7d7fc4c0a8973f2a200296c2e3d292ccc1d49b2870f49327a7788a1f56740a9e5e58f548017ae0f 40c84116145edf76d4e872cb604ad65877bdc88b8f7dd74ef269d6bc3ebedf8a8a973a07e972d988 d5ed50cf0c83a5ba184c360a71fa28876741bec982d0f7f6ed7ce12bf552ade2eed89646bb413c3d eca043e8ec066eee0181dabbc115bdef06c79cf91f2429d5fffee607008d6b50aaa3487fede5229e 200b8ac4b7f98e1ad8c4c7a8cbe2198c78ea3e61ea8249b4f1ebc5b58fe0b42f7d8bbafa1877d50b 735a2805bac41db88d22ffe4a141ff2c4137f9b13b7e9d58d41e01be3d99c5de764ec5bbed9c56ee db9376b7c56ce0ddc5acf534c5492afa1f0058be0975c667e884aa29e767f6cc67a1d455bef1fa88 97ae6b9fc6031bc456cfbcd51eeb2b875ac6590474680cfb26a6cd3e95b6425dc4d9c139a7d772d3 e67692fcca6bbb51df30c55cb87d6d6e2b36b7210cb725dce7ea46b82f1649a76d086e73df5c23c5 da5c9ff26d8be6ceff037f49c5f23728d5c7dd9fda3d314bf89560baa8579cfaf158b4cea56afd56 0ae7cb4bb3c02696667cee254fcf6d5544b15e61ed584d2d47724bb9ada4c3b523ec324ffbb8bd8c a1a7b2f206ae505ef7d27c6d29d438bf30e3f8dab876e5396a6a09259fb104facb99c21de3ae7fc3 4f4af5bfbe0158b903a54ea303206af5a42a4a2b1b06f10cf5cc9190b68f72ef71639a1c71692ea3 e9e980b4657d2a3f6d75c592e92357b64ab21855bbd2d1d8cd77d95799dd925f23193a3745963cc1 41017f70db63e5c34abb6585ed76bb0cdbad7c4fec9beddfb9cd6364725e38baf15c6b7af93300d6 19ad006697144070630f1ae838596df7ee994668a73223c55cba53e3b2b96672a77ebceaeb867817 5473bbb91c9d553992df038d90be18d1dae55c6eb25de5315a78b65491176ea4ce8600b7d6c71d12 ad53315d64268de292d1965f639d5af593117a7d503a16dbf9746eecbbd739ff1900635614c09b13 0d14725b1f148ffdd8967717dec44549bc78674336bee751acc3a0b0a95ab52975747b9d64f63cb4 a48cbf1fd0627e376ba5ab5b8a60fa8233c8937c737f1158d91ecbebd1b27563a6d341482f062241 ddf40149a336a6d1a8b4b0e993d13199b15db8ac5399bcf14f485af3ef1380ed38faafed7105517a 3e1b0d7e655eda387d717d9c33bebdd446236dd551d851b7786c74cda91c35f3fb7dba6edabbf982 fc6ee96e541084f2a2c9853436618fa8c3acb3dd75b20c8ebea6a93365c6db60f54805f86abdbb2c 57b6c1a8ab75f76c5385caea46ddbafd338d2279edcf00d85d6600d13b69efb74d1f2d7d6ccdae95 d14f25f29fac9f8639f0c705faa4c9d1e181eebf69692ac1f952da6113dbdc322a1f0b7018c5b828 bed5d9517b355ce7fc2645af7a1991629a99d38aeb365f4bef6814968df762b9f45a84b26cf44ad6 52405e89d7b9748e9cb162ae53edcf0038716741211baa8f569c9a5eed427b7cea11e84c5b701ff2 58679fcc5e315d5b9a46567947d6c773d1b47c795be99be64f1e7a3e0bb8834d64d82c7e2b3097a7 dca6a92d33a52abd29bbaa8f56c7e5d6d01f648816b26477751f92dda2b05fc427f7b63854f737b2 fd699f48b1a11c7e4592524d8e00f74a826bcea899892ad5c129c4bd9176ee9fa7c7da6734dda7dc 02299a37ebb55d877465e372c85c08cc99c8eff7b333972e549c355ab7a0b71dc939eaf1dd9557c2 83eb2e458f9991fb37973c5b5b1c81a22ed2ab9c37d70b7a613e39b1ab790e05ea7c52cb6af3b1f7 d0e6ea6a79f83300f1384af7d4a9dd3bfbe4adaf4f16bbd191f3caa37d6a704896f36c198c9c0881 2b467ccf48976064ba1cb1baf058af17d3c78129d44f678a3bb38fd5c65c86cb5dc8e448f9b3282c d4c2aab5c8ac379379fea8b3334cf714d8cf437f5af072a5893d3aad2676b1224f18294c5c8ee9f2 fdd8fc194fe8d5f6afa558ef18296ed5ffc9a7ed0775aabb2d654e4dee43330a9bc73e2f66f5ace5 e935b66b520e751faf7cf6b95abe37f7e45104194b6779f13deac6629cd6adf92c2f7b3312df4453 933e642736a796c6ced6ec8e6b11468db67d4e1db552ed78f8d6a2fa509acee7c3b7ce927f46b238 69b9d35bc641ee77947b2fd3db1feae3f6b6282dabec87bb95182a9b3952b57b6c2da5baef2fbef2 255e8cbe0099cf72a9d2ecb2401a33cc2cf6a754b1329dd86c7d35297b756ecc375bbbd1b65b390e dfc75692e81ac47af73918e4f96f7f8c5f8bfdccbd3be9198cb3ebcd2a9cd3cb7b05f4cf3875cee5 a6ba5cc71db9c38f5b5b4a1a9659f05d6014b33be4961d9f4d2282c5e8438b3312691fa7857bf534 7678d41c6d5bd16328f53c6f102b51341864bea9fef784e5fb6314c37bc60a2bf5f276a1de252bb5 6ec7143a934ee1b5a0dbeb8e2cb59cc3ebd2aaa7dbefa6afeb58733b63ff4aa9c2e3afd010c36fc9 2f67dc14b1f7b0c402b596a798f238b5384e487446e633cd49f9610f47cdb7341f1c86b3557f9c6f ad7b790b17bad83325760a5e28ffacb96d064acb915cbde91f4fa7c65b172f8d6e5eb4eaf00e7ed6 549af56b99a7185767353d5bb96c9f850a16a59b65aadf18970b5f8dff330e1e43357fde88a9b065 4efad672147dc9c7f36fb31e406307e8f0bda373fdaf96cbf5f237afd0a12a4aa5e5ec16f5e67650 6b36a409d6a91fe6df7eed7b8b47b58c6d8eab79579a54b017352b531d922cad07ad65919f14934a 1685ed02e188379cfef0f8811cb0af97d5b171b368a28634f0ff0c9003560fe484700672992c03b2 efae08726ae602f2dc07006496ae03c4befe151466070ac4c50148846700f224ab00395f860039e4 1988c51ee27c0188907621b82c845706c8a6da8110e600599de8ff171b8b899799231d3e84ea2aac a673c9fe9b9fe33f01e411b70f724700a5161006e49ae40ee4bee52bc8c7cd1454d96e01b4091880 8e290dc2f700da6ee5015adad6019af3270089a1652331f7f3a41071ee3780b8a50062074fbd740d 623800c8edb88408851fc4cbc2898df1b7c646453059bfdd17cabc82e8b6fa1550e70376e92d9bec c5852a8b6b90db6c25905f2c2c804c992c54c97601ba2db200358d3340b54c00507186029436601b 26df19403b7d0142810d29bf60b8536987004576b035286c238ace4700f95c99f8aa80c4178889cc 7513d1ebbd10952aacf0f6e028fd129ff775b0ff6e985f01f2950076a987cc416e54813a3d15ea7c 2a0f80dc75f85bcaa90f308c140056f37fb2ea18de0c0196dae2000d7cd8867b9904a80e63707477 d601ba4e3f202600e28c01745eedc43774398b8991b88e68431343d665c5b07aa4c5b7d7e889aff6 f929f84a6bca7afabcb0fe4112b0fffbe43f00f96e08a57e8a0b905bd5e1e93c3e006412b9001da5 7128303b06986c88003b60309639301f80f1760960c3fc00608d110d3042de032cfdba02f45dfad9 f58dbeb9547c5b9f0b31e17e3a115328cd42aed266df9b466ff77ae9edfd6b372ded8348f6247f54 5c6cddf331e69ddbdd607f05c8b3d901c8a315a8f336fea9739b8f3a1a405e0d7891bd5e1160e7c1 0ce0a52f0c5e3aab07c0f3ca3749f65701e6f627003b726cbc7a6de4c82ecb9788b9dc9ca8bc4c7f 4227ddc4434ee79aefcde13c7fed347871e5b3b7f3bfb55752c1df1f51f2c19bb27dd931d3ceeee1 d4bbbcbd09ddf5af00f93d3e04f95e9704f92acf0184de9c00baa4df0063f71580b7b78b247d7e04 f85d77e0a76fb2e032a69c6f357a10d838aa743026e4f9f2feed6b05e3dd8ccaf6eb2d2fa297543b 20af8ee95483feb33cf533db32e3cdb28b9d7be94d5517db60ca9389b7f2fdbd7737e6d7eeb166c6 f9d0ff4192e8faef6f40fe548452e763380aec0d1ea0a927bc8ce01a032c7cd46074e1ac003194b5 2483ee47cfbc9f0905512dbdc5c5b6f70ab7dbe5ab7bda6e83c347d28254f360f9e3b591544ef2b3 4898f56623a4e45e0c6ae4504b9e7aae19497cf024afd85bba7eb4064d5abc19d7347fc5db1be642 c923ea5780fca30275b2333882647d78072f7ff202b043b35f80d34803108d0c0588eb5d8bb8e9ed f97e690c78c9b5161628d94acbd73c64e69df490f5102790dd251e5e1cab1d3a4e91cf82e7daaae2 0ffec9f6ee618b5e58870fcd99ea9b3edc0c504a82c22b69ddc5f3a33fe24f42e6cd9ceab6ff3b40 3e6c8d606f92502a8d2589c9f61df6662305f0a0d584123b4ce4a4e5e3fb3d60cce0bb28bf7cfd1d 2626e02d96d79a4b38cb8153d27aab6775816eece0fd3cdef74bf9621da73bc74a1dce5f731cc4c5 db3cbf985c97f8667db1e89b7c7e70ccfe24b4f25ba38d18bcbe17b6ac065e63fa6f48b283ff3e81 7dd985521f5b0a20c14802589786f7cd7899f949f7c6f76b930a9b4e530cbe1166f8b391ff74cde3 e1eb942f6de2b179dc5b7759ea8cac512a5e9993a7bdbdcd5555bd5ec38bf593bcf72eaf4ba900a7 32763eeb9e848d4119c13bde19edaeb3d77b4b49d4864c97572787ec5acdf543e65700a4381ec37e 9528800e3632c00c0dde37aa90f959da7be05b514d74e6af83ea70fecc330f2e8df0d7271fe3be2d 9de49c352e7f4bb74b69ddb9debe83d9e57e42d6707c0ca593c704d04b39848e21bef0af1e75849a f61904736da8b7b65a7a3a90d41c8f24afdc53e6becb1f6fe3137b5c65f7ccaf00486b3686fdaad2 003d9ce12d9e79b800ff6a5940c452f3fd868170303e512bd732c6c2d34bd7d53b0859db3c759ec9 6abb1b3e21b18b6de2cd335fb98e4ecddd7c6974c806afcb62f5a8295cd5d4d2e62454a7590557ce 8bc65841cd2314b675a523111eb787fb5e100e6574ce2658ff1392803d3902644c42a94515f6aa73 87529b810b88be958dcb837115f626def1ae9bd4c4a9f2eeca966bfb9d955ba1d71b8173af0b5bc3 73a7a07bac1892deebe903ec35d546e16dad4e6fb2a49cdf72f2d6c5e3cdd5bd6381f8e60e775b1f 1cca55622d3f3f4b49e694e156ae8d1ac2decf10ec7e63a4995ff12f9d940da79af24705f836ebfd 2426a39a86147ede566a685597de099dc7ae3d1c5bd35e9abdadcaba7e61a39a7b7a65acb4d12386 453dc5452dd5087763e5725b5047735dde1cec6559939d4e0136f0b04eedfd46a1bd6fea0abd6f1a 8424bd67e9ad24e57fb62b244736c1fa574095709646541f4a9da1da5fb57b8838957dbf4f27c447 dc21f1f4c874f93e5a9d3b26363c53d7f28051ce4df4fb307a2e95acb9d5b46784aaf3d6a6a6e0b7 66ff5864be8b436560b372bd64c8fb16669a9234c9c4bbf87ca8ef0e6467b93bf09e043fddb7bb01 aef3bb414a661330bfe2c74fdbf250aa1f43a9464387365af17e4ab5bfe0e09a76ade099b6a37094 352f9551e3ba2e2f16e76d0e930cf9addef4f4abf256e7df734e21e6a3f2e1718fdbb23050277bb1 040779e93b83724232f16c44d5dbbec431552c8ad9a2bf10b3edcd2ec156cc96185ecce6166b713c e17ec75f3a513c0375be870634d0811fdbe5542ad008f076aae76ae27258465a8f6f8520ae5ceaf5 c2f4d4adfa1b5dedf217f5924f054aa1c8a50fcf4db9286ff24e6bdfa6a9d10e7c2bcb5dea91dd88 d91ba26f11a3ee6e48704437e4879f6c965847dc5cfba3ede65aebf05b64d14f5c8e2dd26f32bf02 5ef82d944ad6d6002fcca15499f423be26853e122fedc76ed33f9a98337a5d7899274e61cb191aa9 91c16bb3e54057a89ae51c9cde3059412c6fec0c2e45015fdb298b7a5f9cbcc3f9767eb5b8cd527f a8027d004ffef19a647956aa0cf9c7fbbb114a456c23d0e3022f5814b616ac7186fe150031a5c98f 9f7619b1005f09496504d20d5b2dfcf168335bd63252247f5d6ffde729a42a883144d71dedb45e33 8a35458ec70a75b4e5eda61a49f1c5ccefd4d2ac2c4e55a4b3c52ad654b8eb02cd3f0fc29ef3c49f 27855c23aea6d9170ffa5cc3bd70dc06790b9c57f159beda75189e5dfbf4af8006aa4effda1e87df 8e275068af9fceda55694b6fb9e48daa06f459446749a2cb1856ae69edf42e365478af9347560ef7 7270626ffbee9b806e5dea9016915a9dd89846d010d68dfd90af39cb15fba68722db398bd7755f0d c0baafb9dd757fb766d6f2dde0d90e61acd9976032ec6b6924ef8af839fe1300f235a05446170051 3de96f09a9caf723258e6eabd57072a977db3363d8b1346d7e8761fd9dd4ca47ae1d4ce5d756dbec 7bb7a1b1d3e2c811d1d2cf3bbd36f41847848a6b55b880117bec3e359caf07729b67c6db894e1ba1 1bd2c6d36ad1c685a2994cac728cda51d7ccb7b3a5d6fd29bffc1500ad5da03bfcbaff2c1921f696 fa80d77960ce15b4f7b334cd38b6cf230d15ab3ba58c5dbd839f6b60f2ee2bf4f6fd3dcdecf47743 1131fc696d9805f3e6bd3d92e1760db7c8f62e626b9d9ecc27f4e9db63a85b965529a2f7f3de288a a8050d8ac0f91545a45496bad625869e91fc8a36f835f92b00bab027d040d3c22bb24694a555d3ed bf925420c7f434b40587476e335aca529548dcc1fda020a7a5f11cadef906675b1a5f848dc544ae7 13bf1d8f9e5ce783c56bb56321cc34102bf4e2290ca8957fa457ccc33a2e9dda20583a78b7b17c06 ce72c9d1ce7af9a42ff48a16a5e5ca12d78b1f241bffff7df21f00348ac78040abfcc35f2c3b37a2 b36b9ea45ca7a59dc658e7c8352e9dfd61c0f777a7fc5411afc3f47b6bedb8c2e6e17843a1d13eaf b9d0df1cd8233bbaad278542f27a2a0671408ab2688b583db8477be952ee9c0cb8e29e146ba6478a b85927c5747f41b6980e4db63a058adc44f682f42263fe2b00761fcd02a56a4cadec50492ac09c37 74b3a18fb79996525aaaad7d5c6f35c5cb077436ec89e78486dfb1f99d9841b838b76ab1df9a4cae f570b36516cacaa057c6e24995979d785563bae8b2c90feae42ee0a78bb8ed8b8bfe61f59c7f1bfd a4a4d1a23f7c4de69fd7879a2bd3333507f16831077c7ef22b007e8d9847532d352fcf54b9aa7f15 bfae5857b52977eac5ba7851f6554118b6eb9cfc2645768c62c9b3b5b5a11c726ba498a93378af3c a28bab26bd722b85ddaa7eec9f972dbdf324bb970e581cf119ba48896a7b3e8ee2e5ec4472da6c4e 2cd2b3998d766633a9379921ddea6c4af2afd9f4c2dfa7ff419252fdef6fdc6520b66e64dca91831 ea5455124875f9c5176b3f992a4148bd0a6c7abb2931e4bd2ed345e7f0a0d8259e5a09faacb87c95 0fed65fb604fc99e66c36e2dc5d2227dc092ad8bf39c96bfcf16a56638bd712432b9ab87c6849914 96e3e7f3ac8f395ec98eb946bb3b665f44e7dfe8fe0a4b5d729573e3f1ae6833c9a81f84b89e5480 d919a85816784dc7d693999fa51e7269b46a98e1818c9eadfb425107afb91e8ad9797e6e15670b2f 6ccc30b53e9c52d92e39b159924b0ab5eec77c73ab8f7cc9b0474df08e87dd0c8e0d62e330181ce6 9dc4871e0cb0eaabff35cf04fcf4a9f4bfeabdf42b2eecf65bd633f6a17aac76ea552983d24581a7 37c87a82a8a91557cf00b2bf4fcfe6c668b29b61d79e36b1997632118c1dbefd1cf962fb357ccbd5 78d8f9763283585d6083419629f7d539d7e88f51bddf3356f7592f6f47eba4be80dc3137ecbd43b5 d2a94e213cb6dab64cf26dfb50302198e06f48729dff3ed15518892bc5d7a02a29ac50dc94031959 8faf2bb0aa5287d73cd309dc2985d9cdb1c3fad351f36d5283f868f07d75ba157b06c91fba179ad2 bad8933a770afef8d65eb7c6768befb512836efa4af7d56c6506a0d14586d9fa6149176adfbbd6aa 8dcb6056cbb89458cb7879b36a6ce46fd5d8766abf42b186b3ea5e6e9d4a1b7ba0236b153060e986 b3c4819ae997e363ccd2ee7bd889db99be3a29e1bdbc89d63a54c56bb5cbaf73af558b9551b395da 4c1bd26443d60f8bc5aaa6521dba6ab065b672d964f80af6ce88e542fc3d942aa9582ff2d3bc59d8 92058f90e866e2d9e0f1738de307ce6fe307be36f915b2f80dcadbd5e586b083d906766476ecce74 9ebd8f6a3677ea8f4ef37717bb7b5ebb1cf41fcd6dbf1cd4e35306d4d4e54f5186aac15cb3950b7f 44caa6c862255b9e104547ad160afe295d24de57af8cc7f75b0dfb3a4a133544a6835ce4c5206faa 8b59d6b91596d97aa1c9655a156861923052ff09f0677e8e203b795540e619b540a6160d40e6325f 806c0708206b1b67906b2dde2017f48b205fff0c417e29f2104003f9d5f80971f9823c8513106413 e202e35c3a4f432c7610def527d1b5ebf910720ae49908fee91a2dff205ec45131c60ecf1f942273 70fe1d204b4750679c6e83cc203d04d9bc4082ac511741ae19ffac4fcba972045532f09f1f1b1390 bf9e3620ef554e207f5bbb10411ae4cd166c88b9ed40f83390b7ca0c04b707f97bda8298bc20ce19 90b789727c5957eb31e67c2a11357896132445e7a242a054c2757b550ed713aef24f00d95dba0a05 e6a0d4436904b2536d0955aef7206736ef205f8b60a75d6e358020cb39407a290962760548d50800 5243b23ff14d8dac405c7a00a9e74988a900715600d220ee10620c013d02728d5623ea986b868fd4 b31eaeb5732dac4ce47a58696feaeffaa25ffb15c9cf64f51c945a263ab06bdb50eadd5d81dccd56 40becfc1eb7ba9a501d2cd3501625c970079b46488830590531c01e43c4001224b7588f708208726 f4220f1b11e26500e4d87ec6cbb490dc3791d539e3e16370abbe5dfade7ed723a5f90a38a6f512b1 7e33412be8bee3860f24a7fe4f00d957a9063bb2d10559869d80dc088746d6c774908f3c1f20d3cd 4fc10ce43de900b495a2013a5e2b49b2ff09d076ed0bb12d02b464b7e3ab939fc54bbec9c4043e15 23cb92f588a63d3b7c38d5cfdb0d46d82b8816d520fcae3a416fdc6cfb9f6ca1eb1fa5b8e30fbb4e b2d9cf3ddd67cd5f0172e92a94cab4a1d4d7114afd501c54b9380364577d0394f81000dd687d80fa 0316a05fdb006850f0e255bb9b8eeedcb810954c32d9ae103ea9c324ac9e54eaedcdfded7b8361ea bb614eefaf5d5601818c9a883fb4cdb2a7f7323d2f27983df7dc50fbeefcbeee3b4ba7d2797045ad f92b408e68d7ff3281490fe49601bc1ff6f61620e2cd0468838d017aeb9500d647c600e3e05cbbf2 6d3d2aefcd67c879fee7bdb15decf54682daabb34a0d8238532483feacc5fb1f401efcd1707bf133 e9f8e5e5a1fd2675396eaf6ed9299467bd2743d6870f8e0afab677da77ef9d66a9658ec85df55780 dc70d280b6b94c1e4a646700296112ecc8745212e5fa01588f2d03ec39483c9bc8f6793ee4f7d4f1 dd1a91d6abdb25c360d098e7fd717958f60c72d2f1f2da7cea5eda02e362b22639662d383905b1e8 3e9cfe246b6fb393e2bda3b45b56df207aa69a7f75cd0ca977ae9752a771597dde3fd54793e33f01 76e4bc01bb561c80fcbcbb0088363d00f4d84ed674a753007b9c2ad1a35619bf837d857ef532d95d 90baa62ede69e27aeeb576fd3a5651c79d62efc7b97d569abbc1a35ee116f676b9e3efd27477b00e e4f966aa4c0edc0cbe895d2fe2a871c5a251ef42f50bdd4be19be99c1c436a185ba155f91520272c a1cea5f65397231f6e5700adcb0ac02aac035536d2b1957d54c246c6ea059fc675ee4f2e1aefde4a 82ead0eae4fe70bdf4db16a766c602224958296e5037c7e678703b65c9d50de9ad375772a3ea1733 ccfb67fb334e4ce0e466f8daa90e9d53637b9d758cd6b2dbd6bbbb6c431b9cadeaaf00b9230badd5 758700199b4962f2a9014cd25c80b7e699a8f2a18957ef386cf839a797bc92c22de408f2c95139c9 eea439c31a06d8c3cccdede88636b5fc9540d8d2851eb2edf383a1c7e78a735c9f04dc3a18c13465 1aad8b90d6bb815bd58e9d6c4f1b446157d5faa7969a35c5a6322f4e6a09aa3f295578fc1b40ee2a c069b002a054355e030cc91900cfb85e6c8de7a9b768e772be4e7b45b730d61b0f7fb41bddfbf71a 634e555bbe61287fbbdca5967f7e2ed1d4c96bbf92ed0ac6ab76af19edf1adafeff7cfa506bccf56 1bb6da6755634fb19afd564bca7cccf615343bef1eaf8f49e748544bcd032d44d55f017b73df0279 161902142f737f55192941a923e0452c8bc7c1e7b0ffb8b76f27fbac7985c27d30f876ccfcf7b0b8 52d5f1f652368ae7938f5b4f6327b3912e53fdacf639f54b5a7a536eab13bb3953cec88853d0e146 3d2e0df47db8239ba4e8dc81bea5bb87d226d7939f8da82db3af6753aeeeceb5bdd791abff049003 c7d65fcf7950b2cfc30bdf307eea9f870de1e4fb53ba143865fb1cde0f17326bcece54726daed463 3039bb992c7f6ad535558fe5a1a97da9d24bcb74e3949a274c5cc1f26afdb8aaabc303535229f9b9 f0f732872f9e7b8ffe22fb4d67d7d93762b9bb6fe6f58ef43a704d693758d4fe8324a5fadfdf803c 726ac37ba9328036ba10fe4af8e15fdc7945bba1ed61ac623d5ac3c2c33cb5bedfabb52d96ceae92 1a9e5ace89d18fb989ac8da9f4459d759f9e4212f2e7b88ac9a418fd81b1ba15993bd77b7bef3b20 f7cd16bf9376ac634a9d9a94dbc5a95a6b276bfdee4eb6a69de45373d79fd4ea096aff84bf7422ad 01ecd278b30178934ea2b5d8f25376a0c8dec9598f30d5d26a70a0c13fdbf052ed5ef193a89fbafa 27c5aeb409868a2a22dd4f478be09e073bee47b263e1d9bdbf0f8bd27b65b5a48e11cc76f20711c4 ef687d1547a7621ac24eaa5d8ba367b997a02366e6df6682faaf80f7d2154addaee11c5496a1d415 6b44cfe6e9ea4fd5e9fe11e45bb4898429f5c21a45eff41a2ef346cf6193d953d3738db98a363de1 685d77da812d372d5948655efbede39192a49b86ef0e9250175541188999fb95dbce10c4d85c9606 d85c56647573d938dd046d786a36b679c5aefd0d494af5df2720df7d7400626ea154e6b605f84dd4 c34db7717cf88bfdf09e9ab78757fa28eecf8d8bfb34faf95c5a4f078d9abaa8a5270a61dcf903fb 5a28726390b9eedb45dbdb01650d762969929880985d362a5b64dae86fc8c39c16cce0a408c5ce2c 128add621982ef089461404887a6601a52ed5780fccc854e12a6429dfa1d76e9873e7828a82729d5 fba012b54de4f5ec9ebd696963c8226be9999af5512fb97459298c9ec3c3d3dd3172335fdb4bd1d6 3fef3e73d511b5f738dc9eaea5ec6639f914058bfb74f8079d5bf095277de0847af1cd09f8a3c0b9 cb59e24071ae40b5f9ca916cf06b97aafd0a182504506a1f461578fac9875efec63f9a54ae61ce74 ae7e2d0d468d53a7765beb993e7a5617e13c54a8eabd7070d672525a5fdea6a74b29ae7db73be5a4 6ae2b439b6b68b4afdb521b4e02b94e427ce57b7f726d760bd192b9ad5dd3a2abaee3acaed71b63d adb713b45891a9d4b9467a50e3eaaf56f56f485a931c61c800da003dc753800f5f9c7bab6b6d4b1d aeca57ba8e957f7216fae8d42155b2a0ab47fb96f60e6e6685c8dbebaa25c57161b653b76f5e9ca1 d2718b49ddab60e3c0e19d9c0bb88daa2426c0eef6726d2d6ff431a3ac5f3c33aaeeefcc085b228c 42e26d4619641bcc8747eaebbd83547e05bcedb34d782f1566d040d3ec63b37a556f17072b9ebdac 9354b8d4bf19aaa8928dcbe8b8be76f687baae3ff6e1319f96800ec78ff1f73d1491fd85d9580572 27acaf1583afb7dd3bd7aa5ddfebf8b1c930df1b576232c6ae4fe7afde9aba8cd81b7569cd93b830 d54c908c02f4740b6a4cda07955f01a7d0620360bb4ef2f211d0b154a25fbc3c28bb60c823aaa0e5 8f95c2b1c267eb723b67f0d2076fdea4747b17ed720b87d8de8a76f7270f7d931642b53be0b9e085 1cd93df7b8ac87c5bdcb4c50e143cf331c4e2d11a5b3a2d31eb52a1ae2794585fbdcaac8a51aabe2 3cacad2836a852d8e951fa153f8f22b0561de004b778566fafca8d5cbc89937823714d0715e26847 16bedfb33d6ca7b7f0a5b8a84aea7645e5bdcd3da47202a790557e238d875c6758a7d69f382532fa f6aad267f66851b795f85eddd74ae2402d9fc2bdb1acbeab4b7253b10cd29b5b69d2abd56ba46066 2ba4a080cab2e205c55f01d0a7da79756ff5aea5bccac4a5f290705d91ba84424d5c5c16c734f6f3 9ec2dd3db7b1d3ed9ec0e7dd0dbf1d8e6fdcfb6e46302648616c2a9d6daeb3f9d4844130b0a6ccd3 555ad9b7d369e9189a43fad7d3876c15d2e585b45e4e17dd6a5d5d488b566ad1f1f9f23c1e774b09 7e52aa9d165e58748a59e29f0007a7e6e4c9deeea5ab691a9821675a984aa6007ea89b0226a55739 64c368d30cd76d61289b9a478bf5f82b1e99790b3c1834477ee925a7258f57297a7b6aafd8fd69b6 743f164bbe3296bc8890c76dd1db20eff9f13dc1e6e9ee6338d3d18b32d3ccc57ba6a96f623629aa f86c92958859961de3bf2238dcadc4a0ad4c3bc64ec16d806ad323821f2bdcff47d77db6a9aa6c01 02fe2d2473ce28e61c507206110c204a2afdff03bdcfbd67664fdf2f2fda4f77bb2c8aa26a511446 8193bc6296ba5d4730892332d865b541d209b920dd35b9ba1d572f6462aeda7be3bd0cfa4574c9e5 b6d545bce6fb8b417059cc15fc799c23565699cd4a9dfbd4e4b87415c5e9b25f6e4e0bb9603fb169 cb99d8931e3a5947d5eca424647293d2e19bff9f3cbcb65c36f78f7d4e1bd50a79b9dc7d17f9febc 9ba3d7c40e21b9b606b6974a905ef1589d1ec760896331b11045f23857de9e3647a7c5fb2c7bec06 d30bc1a2d355c1af4c0b7ebd37b9b2f3d9a492e10fe3c3f1218eebadda6d74d27868d4dae39da15f 6fb0c3f7177e0ddf4a05a47c864cfb93e6057eb6bf612df37a4eefebe59c6caba02874cbab3c9373 05940cd667b03523fbb56ad50bce7c5cda80d97c6c8fa6c541e630a9d003714ca2a439a2eaec63e8 cb56ba92c5b087385f227ae47384c04febc4602ef707b2759b0d4674fdd4d751c6e84f9d72d8cf08 cf3a6e2e030a375bd40d3ffbda0b3f6bd4f37f6230149e53cc6136eddc0a6f852930e7a9851ddbb2 9544e9e8def22d3d6fb36961684f2acf4b6ef42a8d8921973dae097168d203656a8a7d7da96bb8b9 552e78ded1efbd4dc3f4ba77da0e7fd668ed1cfb20d77e2985746d56223dadb5b8b9b86e718b9edc c2f3febb85176ef5165e5477cdf8de54ff273fd5527c2e2e4576e2fb99e32be724351233bd25d352 ede9b9f3390f5f784f2508084e97021b20e77a153737954e6f53cf11dd7d1b9b768ebdcfb24d13fe b615a83ed98ccf37aaf1b535b6312e9fa5faacaee93593b2ecaacd395eb5f4c1be951ad22e965d5d eb978ff3ee21213c979bf90796f04e4f6b3fdbbf90f6cea3c80de55cf644f29fcf6ed64d2f181579 6b3a37cedab07dfc4a7dd409b4dea62abb1d9759bcdaf460fd6de1e8186b7cad5aae81dc0ba57ae6 09556bf9642c977c4cb51419cd4af5cb77ca4df480973ab93951c44b936981a80d57f9716b42e666 bdad902d408773663379bc32e54c1dc1f64bbe9920b57ee3e7e0a176db36b4475437cd43af9ad2c5 9ece79592582c25de8997a48751a07d168e108ae3594e545ac679cad5cb5e9e450dbf7bb76f938aa 3f4af40c738bdcd27b16888ae0e5c78df52b9709db7e36ffc987582da347c8eb7afcc08173c2928f 81447a5b84888ed182886e799270dc277c2588e821f26f00f8856000ee20450085992a8037eb3680 6d6f0410823f02e454d30022292e403c1c0168ceadffa486068b314017de1ea0c7be00505135017a c97a007d2e3f09460ea09f4223614324045b807e7b2cc0f2da196010ec02ac50f1e25cd971a2cbe3 fc0fab83f07f91e66cfedf9f00f89bcf00789c2f25f1366a4994429af3adcf00623e698066376780 d6e21740e77406a0e77607a08e3603685c39022cc3c800ab822bc0f0719020c1009b43a59f04f19c 6827241d5f6c513a008ca4c484cf353e375befc8eac2afa8c8df9ee1adaffde085db0ffbbf0148be 94c4b929257126bd7880346e69e7162031b306e8acc90394332d803e0621c0d0a000b00985036c87 ad00c66e6980696f1d607af301b03b1727bc9310c34e3581ee2504738045532a3e9fcf6a9cfbfab7 a88446afb05a77d2163a70bcf33b2029c10f1a6df20fbe172c7e0520835e2e89b25d4e82a6925085 a807d0dd630b30e428016c5c4d82d8080060d7410564208f0099d6ea679e4d66f8144066d4b4128e 2f90598710c86c9a85847d3336cbfa205e44c832ce0b032a2a29a416560de116342cf9e5d3ed73f4 0e38327c73b5d10fd1bbf76cff4d3a07eabf6f00b21824a1dabd24d464c80bd0e96408b064c80b30 2b4ecaebbb7641a689c02073d49201c2a53b0119f03881cca7a6c6a6bcbcc505e4f88eecfb038e36 07a810debd6c3ddc53f8cff33d6bede33c7003fb1034fb0dcdef8c7ab7a4a73978bd88d3287e41ad f6c71b6fe3d8432bee0fe0a93b66fc1b4974a35cb2cf77558062519af2bb4c01f611999f42234717 90393b6f9089a7d83f19f4bdb4880bd2f514956fa1121e6aa81d3c4fb557706a8dbffe9b5d657da6 27d5fc4efcecbdf921347df7617cff92a68aea4d0a8afd9c77ddb77b21ddaf5b7889695fc0b9d2c7 af537e243d9ec7a10c1e873a1aff0640cebb42b2cf852454b698846a159620c3c73cc856041b6477 ad0864352b1faff0623baab4bae3c0abcef7bedfdca67745f85d8637df7c64b8afcfe01abd86b483 786ae817bd8950697bd8603e7ace157eeb5a182439db57c17a1cc2dafbdea2d0cf35eaf1f1b52fce 63fbd36c85b6f42e86f6908183bf48a7f6a65b8038642129d05b2d29d0611b64e6cb0dc8767b12c8 5ea33bc8150f205e69b75c48169c86ef9fbdc15ba0a3f56bd4f7696f3a8ad4e7d9c0aecfdc27f776 d7a3cac7b9718dac530183da831ceed285669281b5b0bcf9a2cf2555ddd7ed511373ad69b7f8b99c 0f4e7cc97942645a14199aebf62238277ffefe0d80446cf1cf2529eca2e220132abb24c49d0c72ab 56daa6c5d64a0261a3b940fd1e392abd94f2a2930c5fd733d7de2f0ece7d35151ec74cc3b8d350e9 7eebf1c1eb3a905f1f5b0ec2ac3de26b0d4bf388a1956973bb4b7ef655cdb53570cebbfb14186eb6 0a8cc6324e8f1bfd5570439dba9a81167439ff37005ad14a00f5733590c10101b29b980439e2ac82 1c983b5175daf7fdde23022f247033cf42eb5e75f6ee6b70a71d7a710565e267bd4ec52a0b96aea3 8695b4b88fcbb2950d4d7b9743cc92db2a9ff78561d770b7ccdc68b85941a789fd5de3ba5ca4c6c2 1ea8e2620354223f8e95afdd09156553f67fe3e77cd33d9700366c25a11ab511c8418d13c8999e1a dbd9f93d687f6f8f175a1a3cdd6bab9d1cf344277f051e68da93da6568656ffce652300fb45956e7 cab926f42de3f8ea7bfabbdc077a673ecd699cbc4b73361a3e72266a724ea65428a75aca788386b2 fe40bff28c873e72167fc79209aea1b494d4e037003ab99501260d93316a7936494a7370fa59a93b aabe3fe69b38b4d3e9a3cf82695b8f5683f6ae4379f6b5ceb555f952dc757b67a7909b1b1e061ff5 b7e7085a78d70cad9fd51eaa34d44215624d5419bb6e55365a79429edd767be95283cf5241da06e2 75a841e216663e6259dbc669aff3b15b45c2a13a0c7e23a9a34e122a5825a1d2dcec9fc4e41e9303 fa4cca9e31d405a7de2d4bd7e1f3615b39f71198b75b2e7f6ee4bd96ee1be7891651547a22503ffb 35a7c2c3b6aa4cd8f6559e1f4b2fe972ad415211eb14c5ab70ec89e5afbb160e93b52ed4b18fcf3f cd1ecc9f8ead2fdf6a16414ac4bddf70f81b00bd46e59fa6133f25a1da4a126a4ca639d4baece376 8676af2ebabff583c9c95a4c2786b953b79ef11a08a8ce86545d1b5053421dcdda6b057b07477951 7949d23a6f5fc45b4771c4ca418f858375cdf25e194d6fc1e25b7362c131d64be6ba9bb5c7469d00 4ef0bf2c1fde404ac4f6792df80d80c170196438be9a0cfbed19c8f7d863747735faa5ce888553df 54faf69470d2b9eae6bd092bc68bac383a8eb63eead76a94147d96c165b3f99a4b36a61fc4db97e6 05e731d685466e7ce7a9d138e4587c88b2d1e95067fbfe63c27c925e2823d7332e33fc707002fb65 4639f9c3c016955601069677e16f00ac954b42bd1b35905bb8539067d97df0ee7c578fe79daadc06 70af7ab12bbbe5b911b2bc8eb7cfb606d53e91a2df82bcbc5cda6d69335126e2beb14fab80704471 86a73e65956303c866e30c78315f288298512f57a135713ea4a74388a633b0fea0ce7a07a2cef7d1 37e59312273feb85bf01b053bd0cb2a5f7cf35e99cf09a80fc8ddd3e97115cbd093092b7a70bbb60 565fc8580f3e3d5a8366e445315ada5b5ec67a46dabc95a6585bcc8642738c6ff84e3d4b71783194 d9416c98cce8cdbbb45e513f94993f17a8fc164b8751271b63c8d3da24ae09dfef69fd2cfd0052e2 d3fa92097f2369efbb49a823d000b9e83b098f1b7df438cd1a595b5bd0d865fdc03183e6b8be46dc 82b4e9548c55439757bdc553da2e3954ac639b1affa66b3817cea1050b864f9285aa12cfa0858341 67b1f99dca7f27d169d32173c7fbc1ea1eabafd6967419d44ad0c1b1fa0ebe293fafa263f5eaa667 cf9fed5f24877dab00b24aa199d4d1fc2469092b955b7fab2056be5c83ceae04600dac3a1d1575b9 adbcdabe64a9b2ac3ec4fa76f0e5fd7cadc4f106d46525e93965d49d945601da581e586a19d595d3 b551bf1e1fe58a4f3e7355946cae77ad03adfbab036d3c9256f87c8c13944f4a7c7863767478457a f01bc931dfcf83ecb7d302f95d3e7d4ea173400649810e96b0b903c1570f33c9796452b9d6e495da 598acee0c80b1e1ddf78b6ff8d38618ce6d9e1f3da6426376948e796f31555ecd68fa74a03138ff5 526492adacf73c74e100daf3b3767d072439bd0b6f076446df01050f12f69f3dfe3ac629e19ecfd3 c16f80cc7e984b8ea5653b781d6a9d3b738cb19fd2345e37e5a311ef4e2c5fbefd9ce81c9e13a15d aea513c7f8de9cbb7003eaeeb348cd40994c91af529639c74f37b53b3b3a42f140526d8f3db0354b df0b45edbe9333e7780713dfd276a2ed26db89be52b7133efb4e20e2ad1a2e131ee36007a333ff87 f4dbfcf7cd0f2013acb23f09bf6721b7cb5ee1c50c32abd2fda3e3e6fea3cc0f20129dbdf1e583d2 bccf0d2e97033b466a1a33ab2d1d7ad9da7fa94d75553c559373edb171ce8dc8b61ca5f77bee635c 3bedbe2d56da21c9d7d8669a42b0596c5ff9b5e54e89b5f59cc86bcb82bcb52537a34dee4324789d e45772b8ff1bc9b1a41640be1b138fe72c9f94269a4f3bb706cd8a1f75326902695752439ebd15df acb2749a4cb6da5dd10582e7a9cd0db24ffbb81c1c4f503d43d251ae7ac029a7bb27b8db6c373ec9 bbed6c47b19be59234d69b2dfb5cdd9d38b3aa15a6f8aa569ca4f746ad6a08eaaeaa41275c55ef35 ff0fbba0fcfa8da48e166aef81db2d5f25679394e6e5f5d52462f991ad010a04eab90d59e4253fa9 6b03c99d0e1d76783c91583a51997c7f571ad9dd5c9c43ff7005fb2175cbef269cd5dcce4fc260b3 62d9e5faeab2c7d5e34ac9cba723df97adc6175a30ccaeb560d813bb6066e5fb82c1c7fe82a9e1ef 05936dbefe224da9fef74dd2881e3b6e7934c8fc14a81e56e98f72c69a1fd1451e8013d96e485dfb 2397f4b3a5e020b482d65e9e8d967b1838fc6ebaae5cb6e7fbd0dbe6712a19e00ec9e27aa7b1ed15 29eed22beccb17c46e160126b08bdec231e7c2b51ecdbe95737df6ad5e4fb30f58da3379b57467f2 709cd0c2ddd9d0af39bf11509251bb7e2a347c3eb6cc8f0643cbaf747b65d32ac07327063059c5f1 8fad69f0d84d07652bbd252dbb2939fbfe7a3f08b62bf7da13564746b6966fccf396b45982166173 505ae0ccb43d1723723253bed46e868e6fd2d4a80c9fd32ce9e5a7d913b49bce24255d97639a2d9a b789a9d1b7c9b237fbe1fe1bee465e642fd623f3d5f15882644be842023d7b7c5954ac44a7fa7ae0 ed4795d9757d9f19e622ec9dee73200d0bf361eb990e3ce63032decd267b919f61f3cc657aae2f5e d3f947fb4e7336529c58f4a83b29a1ec7c7c33226a5c6d4c2e2317cac123522bcc46cecd1546248e 582332f3b246f5a37849b1fe22f9989fed7578dec306ade95fe53c59c1e2b193833988dd7e4e4e8e 7cef3f6fe5b129711773c1b9396d3a9fcbfaf8f6da7ec6bb6bbd397227fc74d470de87e14bc493f6 94385e869dc2fd49047ee94370ec2c7dcc0e814fcd2681ef7bd301c0cfd440ca4dae03e2d5ca0f88 eb783720b62f6d0061ba9af2f34aff8d9f2348d5a0132cedf3439403ab3344dd50273c0c9a763a8c fa274a1356a759af270c3d331088b0bf4008dcf31a03695c19f6d5437fd5473fdb236e581711cfb5 df466fc5816baf38c7fdeed63bc0dd8afc2c779c71a7df21cbf77da71eedce9d4686ccb4bde7cfc3 1ddbde1ae2db5eebc9b6bde295f90d0db82a2caf9d03c6f3d908a1572a161f22f7fbdcac83e76581 2f7c65b26a67b861eb4d337db5fbd5f0f3be8af42c6954eede7caafbf3b0ad8c30ea348ed6b24dbd 9d7d8bc31caa19931fb13978637a4399d49386c359f9f55943466a26fda9d596ddd5b8968f0b5cd5 165fafaa2dc5edea6624ec12cee46fa45d8e7c97c1043aac63ccfcd8ff90ecace46dec36b0fe4459 f0639660bb630acfecd543b7dab99fdb2fa1f16a05ca0c6ec6c632dff85ae37203b9138d7ae6d96c d7f27ea157ddf4cac3ca9ec84ecbc7099a165ae9752ea7eb0bd4b842fc208cfcd73b3ef30a0da1f9 7187ebe691a8bdcf2371cecee922fff90df1a84c322c96597d8f6d64f44e0eedacfd6f88834828a7 4d672f7f9a6c5bef41b4691219203790ab24d54c9231aa362ddd2bfbfed62dbbf2c12fd1b36e50e4 5695b820eee04f5e21632437eb985876d9d70bd87e362ba3cde2b80507ee9880e237bd8144e62b40 446f7d4b53aa049e8112c8ca6f7090afc1a7bac706bbcc82b82d821853ff4439c84364b7c462ab26 670ca6c9b8474ef64d5b23cbae54dd94e8c97d55c4f3e2b640949574edeefcb84eb3b94c4070195b 4239d43d332c426f263c8cd78b49306d4c49185a09ea0b223a581253e79004d1010388e82e37092e 97045dd57f0340fb5e3a2288bfcb208ebf97d21740d92506a09a5e02109f1cacd023da02c8990b09 0f0b406ec74f90500067cb3500d78e7d00e3c102c0f31e09e0832000988f0c000bad3b80cf7c98f0 c1006c6e7fe60ec2ee7b04e027b18bb513cdc6983fa6a3c5b449fd215fadfd6f00c40d92108f719c 44378693c8c56c125dbe0ee08e3501f0a47500f0d6907f3e66574b3e7d474700de433900ebfb1680 efaf1140d0d13641a001528e648074fb5602f304c8047c00321de601b27bf700b2efcce2e9734f46 0bffc885766fcc869b0cc18625176783fbb196a6ed7eb6bf01207d0ae22fa87e92575c5254c82b0f 609aec0004aa2c00529092cf6f958d043af9e836040164b4290364fd4a3e9aeecd00629f49805c61 fe2741fc9e1800858c0740612819a117274882518e677b088f4c402cc32bbc3c068fcc860f6ac799 e03f89b1e09fa00eef37afcd1f84df00d07d0192825c423f85d676334994f9024096703ab19b498a 0a14848463524a20f6019a4bea065a0f1a001d8c86005d9cd72019a230095b259e17f84b9c7d3fdd e8c2a261b49a0ed1a89861cae1d57c7683c7ad9cae67e33fdd26f966d40effee6e09e9c54735e9d5 17b292f7e903d1933ecf5f0150b00649412a0880e538299305550568753e01c9417104e89d5613e2 3b40c335f8490d85af02c02a8b6e3c17e46964e59d6d5464603abc4dba7258390fcf8143328f806c 5cfca00161b04f4dea45bf8d1d3aef5efebaf03ee193f4468b82f8d46c547d664a0fc53ddf0dc55d ec79d9cdd77e5683fbd9fe05804bd407c06b1b0648a39a0168e5d302a87f5d006cb46400460506c0 94e933e10ec5b928538a4af9463bac7646a3a0315faf7cca138e7e5bf6c4378744fa4fbafb51bfbf c47af7f51a78db8fa78cce796f8c62ade72c3799bbf960453a1b9c171ffbe156bd1fa715f9462f1c f11ad88270e53624ff1bc90e17be00fe7c11809c473ff3a1517fd203985c5e834ccee44066d0bb80 cc427b45259e0281bb16b33e3db3eb6fbc75c75fa28ecc3ce599df7968ab4a3f0d78a13cb353f2e2 5e329aeb16964ee46c6e48c629ef46f5a437204caf2149fe2cca0036246b438785648dbd8668a154 5ebc18adaf7099f92ffe3792d234936364514a2afe44ce27c37f750032fde5f6cf62dd598cb5c37b 3870fc77a9e4bf71a99c5e90f4d45eadf8348eb3b67b518f63e77a3eae1f0f7b4f3eea659abbbdbd a572ebb68fd695e7c4a70dc0fd630f4768c152f54dff623ccd8d79f15dd62cf65eca792b2ab2e140 1bd13830386fd4bb35ee8774e0f1df373f00d87a253bdee921002be62a205387462013ea7b909df4 c4689bef193e33b42faf61f3f878cec97de8dcca0cf620736ae5c6d6569dabd05a8d6cb9375fdab0 b825ad2974fa5994e1dc3d69979c205d4d6bf0f6cda25ac1ce3b6cdb349c23b4d23da6c6e8ad0857 35769095d5487e4a29a22ab455fe3792ba89c000ed2e903f89c98cd1fc794e6176131ce20b331282 d64c115e43b329bbd61c980f72f674af71468a6cf97dce585341ae5e168adc336df43c354b7d6d73 dec916653881251a0ddc33754a869f7a7b3482345697ab6a9cabccd4c1fa900e7115f96e680a523b 28b2f69ccbf2f4d417e54cab2efc0640c85212aa4a212053e39b7f72a8d93792145dfd7e7c7d3494 742d6375bcbf6a2dd1fe921fd39a5572e9e5d54bfe5b859283365f305c2bdf329a489ed0693ca9d2 815422b59e5fe254b1d7d095efb1775794261b2b081394e469bc184b26e19ca4bc56d1447b8aa9e2 068b64b1e4f83f48ff924eb8fcef9b64c7b7618041874f32f2379affdccd4d9476411b21565ea631 9c3e1a9a3cb715727abc2cf9b56a96d7ecdd78e272a4333097d1f80c5d550141f75482a1a78ae21c 370a9a3fa67d0179b6e164c934145b5acebfa168db784e2ca37a5fd8936d52a8353495775f9ace1f 6956e59b634a49917e03a0580f02d8f20c25c3fe472319a872a378f50dd216fa3d58f203e78e2f1a 57621c0c2ecbfb6b7b3eb433a2d1ca362c2df47b6f15c404a242ef4e59419d424b9ee70b23e932ce 2ea582513e899b4f59141eed9a29d4b8d98b3f3e79946f52e50e4707fa9eeb183fab28b2e17ca4a7 682c9f6b2b29d26f2447fc3409d5f411906d870d901b8844e05d40d75ddf33d8bd83cd602b4b11ad f361b559e87e4667b5be7a37d4e1397ea6356d627fbff2dc78e7a5957c6d246de26520566ac65ca8 17f91d7fdaa91cf7360d9deb420f870db516ccf63352930197f19691562f991996108381fcaf96a2 a6c87f91a654d32d4099f91764b2189aecf86c13e448b1ff5cac3edf5b70ce84b612e8a1b99d829a ee57d1b1d67f8f4f6a328c55e533cddd258b1723f1463119b172676a42fdb2eaf1143c9f707e6fb2 61a36637ad026c7fdb5718c9d9de1938af025abdd6ab345679ae69acba172923b68d143d454d917f 03a0ef150099593d39dc5fb5664015e7a5c711ea0557a844a7c78d95bd8c5f86d7648a9a30b8128a c6873bf9fcc225696dcc6c71672d0381d42728ef814985f3fd56878d5c6cc80e0ad88a9127d88981 859c4c4f5ef52b350f872195e3bdcac96a9c16a735d41413e8f48a47bad54f4575a3a448bf01b0c2 3606197d9801b97cb3e1611efcb97173f765cd00ee9a95caf3a177a34a4645caab9ebcb08c8db40e 215edce79a17a1712fa7df86a7b37588e3e05c8115a2a8c9c82f0b6790f279464fe7e73db5589ff9 93a53be6a9f8c1d28992ebc2b18ab666a4633c38d239778c148d741c424d917fe367be00718a4116 d966933a4a349d7b868aae509bf3cc6b3244354e5fe6a67e3e374836ebc59654bacf17827b9168a1 d9860cbe83a32e87370160898a9763c6f9738d9ea1549732fbe4e464b7179b5369bf4e7b9dc7dd53 3048f276f6c8c6be9d3b504f7f72a03c893d50af8291a2a5288757a3f92b003b32d14f162dac9f56 c547e3da0e7e4af37c701e0f2d7a0cd3650c94e97d1e499ba157175c5099f2f46d72e4b8989159b0 7c3c5868720919742063743619235385fab47ddaac47a3e37ed65892c755e3744846f4eaa11def9c 3d4704d81e87e5a4dbcff5a97d2f78eb29692b906e9514e93700e6aa49a8f2bbec4d73d8e71a7fec d7a598ce0ff215e6ae8ed2d560eca3e70acfcca2c877aeb72187c7c53d3b2c0c0566d23d58f4bcb1 4a135dd48526e1d3f5302d1c1feb76837ccef2fdc39b8867870ef33dec713123edc477fbb683ba22 947465a7bdadf2ac9c120c3d45dd41b587b283b217e93740a6fafefc9c3dbf859abb66b5d81edf8f def910bd1d4d78aceeb2b9732cb13659db5ca87c33ac242c7a8c6a9f57b4117e18caca5774aab8e9 3aa7caa1151febeb3c46b6a671f9c0887177cf33f7d14e12ee9bad4adfd3152eb76894b96ce68d69 bcc9bef3cd4dd687c94df676525394cdbc2825c08cf81b20c3d59168bb979223beb9882e97cbd8d3 fd97e1a888d5bd4be5a56cf19dcf37ed75b2707dfda1cfedb84159f3c1f474a328f254451ce9d8a8 453649ada2d781dd05d05e58daf99d3c116b3bf8ac0fb698222e363943a7d6abcf4b5fddc63d7fb5 43bfb5d536f2b6abadbb5252e4d52db34dcf9ee956fc8da4bde7f35e3a30bb8adefd7d76a6d9a736 789c1c79a9e7eec2694bd92c8c732a65b1c1fbb49336a52379bb0f480ae437876035650f3d9fd5f7 624f4b5343bbe4f41eec104644b719812b6ff2f4bebd5e3f8ee3d5cee3f64bb7edcacb06df7e2e5e fdb8b47835bcd5e255d8ca8b17341717de7b28a4f0bf01b216a8b8a5a91cff3c4c18dfbff420ff7c 2abab772c587f9797042636453eb694d3e36d48279e0162abc17d572730f558ed3dd5870c9ad5148 46fdd96dc3dc2c9d9ebbbe3646f1ba4c2f33abda81a82c8fdea6bf78b7c5d53cc4edb40accb9cfe0 3e03a3203bc7e9cf628e4f0efc2c8e86dc3f385df63782a75fccde42ac1a9a955be7a511e58b2717 91b9c7fb8ce3305a58b28feeb1acee897537eddc6e8d19786f2ed43cbfbedeafbd75a55f58ac1e9f e56975b04c79e9252dc7f214e6bd85dfaf7e165d19cfcd0564d99cc9d9fd6806af6c72aa9506e674 f240a0e9e40c8fa6134162a6587e4e4fd1eff00f694af5bf6ffe253971461f6b3ea3df7ab0ccbe95 794e7d8bf567f7c57e0782436d8677ebc0850f65b3226eccd21b9cd5651b3a8145afe295e6513cec cf937ecc72f66d86a7990277d2167a36d2369719b233bde9d475c1c41c160a93253ceb8ceda9be1c 6f6a03695cf2b0705cb273f8b844f3fbd1bd7c20477774458e767cf7f01bf7cea4179dc95de7adaa e83bbdb022ed103ee07b5af14d2fe78d3bc914f0f3d6b84fa4a547aee9d948d20f1373df302785ac 158f6daf571a97476c6ff42845f3d13e6c1d46357ecb0f9ff38b313c35b2ceb0f91ac7c45bb91608 668bf7896ef795a6ed06612cdf06e1f35c1d84d6703608a9f96610ae879b018f54d6bf71b1b2b4af c56fd49757d63514daa749c0e8b6e61d4f55c5de4d28495d91dd3337fbe2c171bc59d6d2d11ad1f9 5c76fd4f19d7fb32faf2fbb04064fbb0f36ae3dab13ac6a7c46e8b670a16d33bfb39adb7d0568f5e 7ef7fc74ed5ab7d4b57130ee6e72a6d02dbd5e70b7c408fdeefa862dbb6b35a92e960fff215d5fe0 bf6f7e30da6e27f873d1a9b15763567173c1a9e6c7f73ddc0f8d95a3fac2ec3bfb9e7ea24cbefa6a 81632b78dedd149f72e7ae8fdd4e0dbf40ed672e5b6c37d7cd66ebdddea7b72eb618c45e36c35940 36f97a5b6ce21fc16e48eb28680c3bbb4203c62aa3065c2d48f5a40f9ea9ab727e5c5787dfff8d3a a96423a93a0a3f9c789bc754f9334a7b9d7b196b5ed6d5624e9a7d0f196a5c6e96b7833ed29ff772 cc79d26e6c7aa35677c94b4dde471f0db9da0c1bb03847ead30253ac9db752bd968baebdea7a118e 2bbb4db82c3b51ed546e2c364a897add1fa5b63a4b57832b06bb125ee470982d72fddaa7d8fb849d 628f0afbbf21dd6ce8c37392f4a10ba1fc3ef4d5a5bdde9dbacaeccb97e8d1032a6cfbe0de9d778b e7dda819e0c1a00179bdf4ba673d2374f95afec558d552c83895eae7f42e37916d54ea64e7df225e 4c86cd44b597cb2bc76935a733d376d614d6c3cc66bc5b61b51c4ba3c70d7c419b65fe83362bab0e f2729b47843e55bd7f493ee6df3742a776fd32d9d72724bba2992e42bed5e683b0cd8c1e85daae3f c48bb3ce6eb01835e29582574d1bb42abb677e556e42f1aa189ced6d21b6d3ebe20f9bcfe9142d66 4d6ea36436c34efa0824d43d873a426f2e26cc1d2e7788684b1e44747270c2a90a11ddec28413941 446f7049f06388c0cfaddf6051b315937e2f7636d65d35e6e278c18d1ecd4ebae85c1fcee6666de7 351ad5bfdabe57d9186aa3e88ff876812843c33cf264f0acc956da99cdc069a1ae41b6117a3dc561 bc56ea43440b1926346609eb6d8246257ca4ffe47a2f7e126c2bf39387ee5cda49bcf822c1a4ff13 f4ff47fc516b49ffa90225309d4702ee241c83f8e39773095c3bfe04c834614dc69ffb4d8a3f8f8a 9570483b50f1277c7ee32fd6ca271ceaf1b7f2c0e36f353b89bfbde53641a1e2ef2ca324ac6f0911 88bff3513e5672722bd24b021e9ab541273467eb5e68ee493c5cd616bd7089eff07f49275ca6dbf8 f3c66f496ccd7bfc85964efc2d1c9ff197883ff157a2cb09dfe4a3e5c532c1a1e32f5dd712984742 18c55fb58f2530c9ef5dc34efcf53ba384fd1240f09d041092ff49a942a5dd39c17101d459a3315a 27cb51b6cdb6c302ce0e82cd934c48bae241f942fdc17f9cc8bf49a21b272116364988a2e925d82f 00d536c9bf3f647ed2ddd0613d4af0760022fb028016d205404b34f9ade5f80ba0e3a50020a9d004 d0654324e8730079c83ea1c7c4aa4c2af16464d93156cebda3f96181852b8aad05159ceafaf5953c 7e7b2d21cdd9bc29981dbf5b376afcf259f257e26f73931490a8245fb8fa7d0368df8e005cfa1601 bc61f184cf02c0db3905e0c94505f0b47c4f3826bf327d6700bcc76b691598806537c62ee2285ab4 ec6594f3837d68e925265ccf464a58aa0976707b7aafa0da6e636f0f0caa2f561b74bdb827cc3c31 c3ce9edfe37afa5488c5f4392e4c7fe5cfa508da49a20c3c0071f9178047260ce0efae0e9065719c 70d825bc7980ac880b4086a2174fb5fb275ab86136dcc050252cddf06eb0efcd86be9bf479fde352 d9f9cd824bbf5f7e5e7e777a8d74a5be5798a1bc17be0c500faa22d5274a347037cbac17ce0a1bcc 1f57b23a7b94bd78727f50eef87e689ba3bf88bfb19b84a8979210db83d79f5c2fe2f86d8056b733 804ec1cf8a63284ec8f14cda5be1b5ba7906fbf732f24fd8097dbfc94bf9ddf9be5b2f7e0cfa1e70 b2136fd828af3ce83b3b3e5585e69f58d9365cc3c9bb6eae31fd3eb6c6b17cf3465cf7eaefcd891d 49c7b13d808974028c254f0b8435ca40bf02a019f24f94b0c7fa00c5842240b5210e30f8b9045809 a7a2cbb323068f18d5fdd616babd78effdf2e472f6fbd4faf5dc13b32669157073f761dbb1b813e1 94b2eafcb15b5db6777277498606e253bc0670f672eded666f7b203ef216b20d5b9769dc9c988b6f 3836f3ca637cdea0fcf05c320ec46f00e85efa59511996171e40ce1800e8f55301d8901f004c2eac 228bab1d8306a652afe831e1bdd16862b867307f38ebdb2a7854c53574a32b83cc951b8c4a766cef 9a3651a670eb1b5a536bdcf7b617ddce3197d966928e6f4cf321b9666150cf9ccb9b6dcba81df889 7ef4b653edddee8e353aa88cb40e870d7f2329cd5e12629df5fec94fa5935031f02440a64fac8223 c4af5e62194b97d07b66348d74ca9026de68257da6787d9a9c89d7946fa19cf0bdcc809e352f2bb7 6a166eaff6f9bacf0ccfe56769613ca4f5c9a8c382ac9fa6fe4d6f657788d66dddea6a1f87c70a48 ef4c1812ce5481a1737a2faeacaac25856196af817ff9426cf3e00fa1610803d577590394083f0ce aac357fc7106ae792b0deecf4eb0b001271eac49cb142fb9aa6b9a452276ce951d121a07b7907639 74af592be82da659d7fcee0cd7bac266aa460365a7f61597533e93b6a50ccf422c6bcd464dc6186a 2ccd236a21e584f54cb406a3a9b8fab62729e3bf00f067f704287a7efc245396160c32b3531564c2 2ceebf0b58f369d490cafd4913357b3883fa97f9adbc3edf961dc62027334da7c8fd55f35fdc5beb 7594af2af04656f97cae5565347a7414380847f2b48fae65ec4330d262f4938192f228e28beb3957 116eafcc48a876aa4bde09330b9ee4e259ca944fda85c95f00a44fb900dd442ec0dc285dff872f47 d72c537d49f53ce2941f6e68cbf0217bc9bdf9f4e672c379f1339d7a3c8e5a4f7e8baa582e5c14a5 5f7b2a88dc01f23468a19289774bd2e2396c8a766b3e104bec6121dc7b574aa81e81c6bbade58b6f 705091a3b37b826b5fee6b3658e94b962b72f394f4b107e976fa17c98ea7eec98eaf782083175190 2da9a517f194fdc7614ed857debd5897c5e003196ea358d782437da88aebd54e51389e93f58ba1cb d9d6db910ad56b206e8e4e7aa53089decf0bfb6d54e7dd73d4e39b056cc6bdaec323d7c1b632cb2d 1e0e8b174e0546dc417d86e86dd6f4371e2f531629735a113bd3bf004828df01464cde20c3b5d225 8d82e379059c0abab4ae42d5302c6d3bd4cfbbf01469c15d2ba9c4caef2be8acb296b3a7212d15b4 ad2a6e33a7abf058f26ffe39d902fe840959ee3d10aa5c87663b2c176a1306d4ad2d43d0b0c0409d 6d5ad3e8b18065687420e194019537d4ec5a58a52c53e6bf01d0f23909553c052013cc334fa39779 dcfc116a5a9a3a544debe349facbaabf55421ae564a347a555405a356f73713bf990c2c36d49fcf3 ddbaf0ad6effc93164376243bb8db2fd72afc448f35e8b81acd1909e20eb0d65cc648eca5af0f574 590bf069551ef64e85c7739df0fe6179bc1eaef394b40afc6cff05a063eb06b0f812c7cbfd30e35c 6dfd667f5cd5b8e4c99c62b81c2568387b7314f4964f425c0e9a6265424d843a69ed790a82058ec5 e0331b0de007db57b080914204fe9941dccf1468ac15d4a9f92eee9f2ecffcf2542cf6e8e3f5a65d 8e955afd433e9e51877c78cc8a3c507f58a4cc53667f0150dab9814c0f7c83e662183f0e8f7d9a84 b474e8649cef72a4e89dd5805754626a4b17c786c40a93aff3dea237e47c7ab7e17ae8996107b9b3 cac8ebdb8d8175eb454fb3b72fb58095ccc99aead55351347bc7edfb3e239d1e7222ebe2f47cf088 28cdd91c4ebedc3ab4f4fe3261b54af97935ff0d80da9f07c8ec8bb087eef28f1b3b2a5b972536d4 8de65e9355c0567979bef96a624559c4bcc7db658ea5917406312b18ed25f3adef4e0cd226657aca b116757e524f2adf62e353a9ba418fbbe5b2443ad763876c60dce4d0ceeb87bdaf66d47d0fa3fc5d 644e1bbbe882cd135aab9445cacfdbd90f69a2ebbf6f0056a83f93c66988b917217ed8dfeafb6296 1b755d0b254a56268b2019b4f63991f71e259febad977976c0e91d66f4fccee86990df53e6acc753 f9cbf47cdac0a3c7719fe9f9a44b74d256806c30cdfca12d341b7bf6850f777167bfdb0df8bbbcfd 3646afadfcaed612bcd956f673ab94c556bec1b394e95f248dfdfc1d2ff13aea6cedfbc39a419269 3cc7587a9256a5db4296738acb0b8d5293e17a92e9320a8ea2b4be1a372893e5465421e36e4e9b53 8639ee5944215d17b1c86633e31de82d02f6dc0160bbd886aa3ba25cee6fc7b9da72a3aff7fc6656 cebb9bcc234eefc8df64ceec38c1596e6658b4d84c236f9632fd8b9f63c9ef6ce0e04ef537b74ba1 7230f56eeda62993b8278bbb03c7716cfc3c32e36ac7a6b305f173ba6261f9543ee2691538d6d4cd 827c4e74926c3aae78e8d8a6b1e7cbf67d070e6eb823ac07bc1d27e3fb8d917db637d97561be5ede 97ecea9a756fab8da517561b653a5cd95f6bb1b25fda2c65fa17694a35ddfe1c4e1e7a1b3ced6f0f 58e7437e68aa9fd55997f3cd8622343c4e6094ede1402d15553d95cf359f7c2a87dce1fdb8b40edd 013ade875f7cb3ef6f36f44e7a1c951d5ce7eced644d3f37f3fd313d6ed697279f5d178b6a7d55a9 5e87cbc3a94c2eeb2dd35a3cef7476f134dafd651d95e7cb5ac84effe1418effe24f052d2e11d7ca 42174bf7d18aa968b7f359dc77f3eacf4ae4f7a5402d5ff5fdb19ec3e803733fddf7420e867772bb 5bd9c1fa09df4eb3c66c8b79d07eb36817b84d2ee868eb354edc563769e62f1d6d0e2febf1b6b4a0 08159ffb1aba9db353519ff7b24764defdb4d31b31e75d8f9ccebbd67a32efcad3d16ff8af6f25bc b7fbf4c32c9f4f9636c8611739ef0aa6d014f23aa3aecafcf1c07df67ba13a5aef1028323667b7f5 dee4975a76bdbe46e9ed0aab7baf4eacf699ed7255355572796c3ef865c347cf0bba5778cc03b119 cd7bef7e66264ef8d6f47b0ee75365494bd37171f7998e8252733abaaf47d3913e1d4e47dc80f88d 74f60306af5f3602b237a345cf6d456fc3b6b84f061d1c2f7f0dcaea41c2c1bf44e4c644ddc5eabe 0e4e4bf7415d17af8f172f3a9b467e1e76a8d61c37ace10ccc3fcb99d4e81e67c46b2bcc205a3d4f 27e8d3fdc9dc9ecbdfc93c3f2f8f2f1b63305e7597f4b8e0f65ee355b65a1d174ea3feb8b0ea250c 1bf86f3c9c2be299dbeef5a67eebadab74cd7857dec7763633ad99c691b42fc24ff2bea791abbba7 2fe7f8f53d9941f151984e96dffbc4f0476032d7ccc2d8aac6cdf1ead920c64569b71cdd861a39da 153fc2a8726f9d870ec77b4312ff208467cd9b04b5413744bb1cd804d57de509aada4b4fd244cbcc b7899688249071eb2fecf1acf3f8b9f2a4644b95bb5827fc07fb798dae54895deafbd81b729bc276 745874b3e3c5646eaf864347ae4c8917721588f6a1761d047d3a4c33b73deb81f5e34db6d2173be3 6e9f409529fe355e5b5c21db1c3e6e90068e7c3faf9e7ea572bdd9ae4ef4665c53eacde61db8a7f3 6ea3a71fec7a4f9feb097db1f61767077ba63d1b755c5f3952a55671f95ed771e965f66b1f42f42a 6f2e84462dde81be9962a431199227bfdf27a05ea76b7e3383ceb5c31e3b5b0cd33a6567e5b41fc2 256e1f96b96cbb5e18d65acfb7d46f9d3468d96aed6669f2bee9f7dc7393cd4e8366f7d3a83722bb 7868444cf3ddec328f42b3bb7d979b8c1a2630cfd25f686259727f2e4008ed56e5c5cccad307f96e 768cadb1aff0cbd63ade4fa71848939043d2c2883e5118753bd7d2b1de7cfb68b53e6a80761d21ec 5d4d2f36c4dad467ed5a86755e55735685aacb1a59aae681d3a9d8c97ea86cd6fca952ee148cf203 35a3f2dea15be57d6cf1e5daea06956bed5cba2843799f3468bfa1642de62578cf89cf8ef3daf3f8 ec89d66e323ac84b6f30384da7bdc272487eea235c517bddce16a11bc9570f2bb5e9785baa6c1ecf 4ea5ecd0dbf261f07309af9434ca6aa9d5adda45e63cf20a518304853ead64f232faaae6b446be9f 9b7cd96d0ebb5694eca27bf1b3f90cdfc8584f9dc9585ed2995f97020cbbe134f417a2a3f3214754 73e9a588d36384dcf770eeaaadeae71d3dd56ee3cdf0581f4ef03133e8b50ff8acdee89f2fe56abe 5f2f948e3d2d5b64d7d75e4108b3b3fc773e26737aa94967cd192165ec5547c3f6bdda056d16ca0f a453a9f930dee8427f9290f3e27fe69c722444f4461644e0ed0c44f45b338818e06682fefa0b1e87 877f6e2bd87fb0c279e56c2d6e86b48ebbe171be9de1e87684b79e60d64876b3919ea42b25395728 04ef019a872beb7c2ee3773b195b701a580da55ac8cb5ae330b7ef1110d1ca8f1346ab043609a7e5 a75365bbe67f66c9d6d388af9d7f26c8f64a5a821326110bf59f6fd3c7867fc16469eb7d88a0a5b5 be9d2ad24c067772e83ea4058eaabb41eb04f6adda94e62b2597bde5f262944732abda20468fd957 0ee92495078add0e02114d14fae1e7639a9d6cc2a69c6077fe93f3ed2dfe13f44fba37a99a443b04 ffcceafdb378834e25f1b6cf0994f317712c455c1c7b181fc71f2cbd8497fcc467e378f511e3583e dde25881e238a627b938de18cd389e644771bcdd6f13ee4c1ceff24a1cb36b3b7925b8095e18c7fb 7cf227fb51f2177ba196e075e3f8d09a26f08738260be9e2c0f1c0266e1172e87861862abc03936a 068129e361605ae32830bd65fc17716cc17c0cfa2d2106f356f2aa5ee4925727230642e195704262 b078946340d47a31681ed35620d9bec8180c7b62026bc660e53b3158578284059420e712fc6a0c36 954ec26a9470ddc45205a523b5811be12c241ec1f2330bfc6d690cfced9a81fcca5a45fc0a7541df cefa9a2e98f1b3fd9724b696187fca92127fc6173579c5cbf147259cf8f376bff1c7c74bf1e72676 e30f1b4ce30f37382468c95ff0b0197f8c899b2045093e127fce95423cca57d20a1dc376bf1569f4 8988a61d631e612138848b4553082ce578f57773fafd6eb4b9ef8bba5fb3afb6fec879c1fe9af338 fc92307be4ff22068f4312980df4f80ba109c4d8fe49a99a71187fc1269be036e2afd718c65f8bda c45fd963e2afd2d56365bcbdc5c8437a47b3ba1a4719cf43c365dbcb850534570d6ca3d50ecaf509 e1dfbdd3dcdf53d7c3fb89a2c2bb39df3fbca47f96de53f894b22af684a467c955578f928bfa6ad1 3134a9e8cca7a7c25fc41f0518f177d939c75fb1a303a8e73a000a4404c068a602a070d44db0e600 728ac778926f0951f6514f470461c1abdbc17634f1fcc37e1ef9f51a05bf4f2736fb6e0d6fc93944 739aafee3e37f0f8677de6f5a5c3e129c386f08467e5bb33f76be1635ddd666fce7558bc7a512e7f 6dcfa39c1de49cecbfa4b354fffb2629c68e197f5d2289b23ad300dc1ebd016c2319007fc946823f 04f0bbb58e725e740876f88df11b70ba9e856b99af5ed37b788389ff7eca2ef6798e8e0dccd5a141 d1cd4c46694ad5312b6ccfc993daf8b1fe46dbfb1ecbb1b7c66b62d9c183f12d51d6100b6afbc5cb 98d3f2a691a572e6ecb2ce9ad9d528f31749884b1340d3bd0e60d8bd00f8738e00424ef33f93c8cf b766ac1f2ba3b0e40913bf0116ab179e691c9edf779b73f5734f73ccd7c87ed8e1c0b93f7223ff76 0c7ae0fa9ecdd06be7b2ccdb7c8bae5940337b9634fd4e2de839202f9396ac9af37df1755e498b34 67636c6feb82eef406c9b8356ee4f4ba98cf6adee09bf98ba420f76700f962529a5eef964497fb00 b4ab1501ba2bb6c332bf6bbf3b66d0f5209223dcacb15f3c2ac8f2706bb53ae91c283baa128af521 c6a635bcee1f17ad727c5d304101e679f1c6cc9c0d8a678be9b4ce256c3334760b656d549b55416f 4c04476b9331a4b2ef5241893b415e11c27b4e19f07a56fef6b9ccbfa429d5740b209633003ce444 801cc83b40b70b08a06fbf18151b42f9cd6876f139594085c7f66234ae6c73dbb73ece6971d16c89 34cfe1853fdbb9ab7a2e51a165ec51d4d1dd432dd08f75e2ab373e4c7a6f94468fd46ad2d982fa2a 77682e54bc2e328aa8b41fb262ec3e325a77cbd28c128b52b67d2c88e67d9cfb8d649febc98ebf98 2240cb370760d0162403eb4fcea7b2767abe796a1d33ba3b4710db83cd3977997a4eeb6ceffd9171 b78a1bdd0d5a94f64e9f1cde39ad0c351c4a3715d7d4a70276215008f7832ad0a95e9627f0bc23a3 9a31938c599996b217e52aaedaa58f70e5f6699b26940151e61f52b3c41f5a682125ff17005e990a 40e67915a06abaeacf9289c3ca27f2bd8f23da8f9b7632ecc1abfe344dab87188fdaa2a6376fbbbe d68985b9da2f9cd3cbab8ac4f8ac026730459e1ceb968c3587ae3467268194eb52b07889ce05b138 f09ac20daa8f85ca9639f24eb566f1f5a3f2e14e3e5ae65a2c5a65fd6e586699e85162bb8251f88b 9f4b1197bb0c9070710658b3eb45c508f3dcc55ee2ef64f4a1edf8b0a04c13b0b65173b5586398b8 a8f68fa58e024f1a637912edb6d279c05252ce3e8be2ba7c3b8bc5087e08bb01e60b956ffdcb939d 765a05f87ab86b70545f1bb2befcd9b1ecf8a03191538b198154cbf4e72555532ab44c9fcab4bcd9 16ff02c0e09eecf8b16603ecb8f5bd2fb6d71f557fc8d9c46174b81808923ed0c9384c4b86d66546 be220fb8ac3c2d9b0d6931ff0ec5359d5f0a77b4470ad5f988e3dd3a6bf00d2ad9a7d49d7d72edfd 3966d9a79b617b54bec608416f407f71764bcb9fa24a232323a474b45ba4a6c63c6dd3a8ccbc5f49 299fcc5cbdf81700c1d3a9b2f6e711e7aefdb7bb0842f9cabd1cfa322f65f6e72db15f69fee0ae28 32893ce54c8740453bb7a90af791dee7ddbd3be71bcf38bdb99ca3477986eb206d85e566bd0b135f 060e23ae7611fdd5f6083dce88654a5fde7bd4acd85e9fcc9d209e96b57970ca3fa3c2d1a62af5e3 a68d55532a29e5bf480ef792fa7329e2ffd0759edbaa2a5db7be16cc392b20a82048ce41040411cc 59d4fb3fe0da6bcffdcef69d3f0fea5c6d3180a2aa46afcea0d23edd141edaec77a7b51d017b5b5d 6d0b17de3b8328e522f5996917c6cece4a87d7792bdbaa99ddd61434d48581e98f6ac4e830779435 eb78b734a01bfbaa732befd51c54bb2953a905c8c1154e5b9a5c01f981143d2f33a98e2273719fc9 5f457e392f8b495ad34ed14ad1148ef1aaf60bafac9fbe24081d5dce59045d6eb5c270be266c52f1 4574272ca14e9db297afef82a4c5c0d368deea79b1716dbe2afa6354ef692f0e1e6968cccc34c0b2 4415033c53f1b1c0538aab6823cf3ec149ae38f7b7c4e68b65718fb4ba62cb617041ce5d34a1bbb2 4ffc750615132cdb29d226906e9bbc56d1ebbff0cae5b8e02b9e9f0a11ec45af6bde0caac945f2fa ac23384eed452d6655979bb74639dfd00a939b6e7617056dc13e3aaa6be5866a1e684d157f334b7b 01a5d4e0759912a564ba14f16ba951970ea220184fe114ba79a1573bb5794da84f78b0eda8dce342 1f39532b1713281dce64b9668a468afa2f7cefa567c8b653f7c3217ad4bdd07d1c8c5513eeab6e3c 97047b0aefa9f92e1ece8c2bbb71f561b772d632f96146cd7784ba42386b500eeb2f4cae8a395ada 22b02a35d481250a0f78259c87c975e9d9d33bafddd92cf7841769854b6eb8788cd8f79891d9915f dbb123f3986747f3713b452b45931d8950ed17bef7d29f06da5a5f976bb2b434bc0b55565da0cc8b 8b0ae552a65cc9e0fa10215231455de6d67b65d5c9bde5700256e4ea5e4872aea13f120ff69e143b d9132f28d3872ef44bc525afd395887bee5a170e2982006b53548dcd6cfd2183b7ea02933f5d2226 1f5a59261f75da4cdea935537c25d5645bff85efbd7476f2c66a0b42b213d4fa8ab134b458b5fdc1 449cef9726ad3f1eab89baac6754a574c642b996751f52b3752b88a2dd6e0b971a060b7d9ec5f83b ecb35f07f162ad72f3db7ac17ea0cd8a1d5ba703e36580175380aa259ab4880195dcb30c452f259f a2bd1940d146dc4ad1a068fe524b51fd856f277a9a2252108d36edd4a5ea1f79ce4c66fc176d4109 b064f6248cd2320a3d96c305c749dcf2ed899d057816d4ea3ccb1b5058e362ebd6e32caa3d623f5b 90601d8e12d8ec91d59969575ed281666ee8f2d3bf510cf22ccc765a23b58ccc78704192476b6093 c745e74d1ee5a8491e29af9ea2366bae77bf1193c0e87ad80c26c17ab5ca2c3c754dceff48fb8db0 2beb73b64d29e56219158fe01d1706009936011e06b75b0e9de59fac731d17d91cc835193f1f424c 913c4fe875b744d315ad21531b70b4a0ea732298f11ff94c9e262b80ec9acf06a17eb00931708b73 62b0cc3e88816ed5890167d488012e55be4825d57fbf7c7185669f707bab5efda016de6cf7b545ac 4538090cf392ad2b5a96cc50e2918d10de70a6430e208e12e34f3a3e53dc2b679a1a1e016ae3642b 14cb20ddd97e8fc333c19ca772f7ac3d3c70a46cdf54b237a9ba84e6f736d367897a4c87f4aa8cbf eb20828f84938a8fc8e88c8fd069191f75ff45052dfdc23997df45d1b8cb2efdae113aceb289d8df baea7e30d763eb242bb3f1811206a283b2b9f57540575b39826ac8c67cd65e5e225269356f64efc4 64097de4d4082873ec4e9fbb1a3a9df30831455a228fdb8ba5818f47cf15b6f41a27ac50a4b393d5 fa9dd61a9a90b4cb4fc889b59f90209c9f940edde2a4b4aa1726a57931ff0b077622454124b697cb 213577bfd2be79397b0b6de2ae154928ce296ea1aa23aa21615d3219e9fad3b8eca7a327fe01730b 7cbc9a6cf0ace8dd31fcf3c86205b75e9d04ecac3799350c64523eec88716495853183e2f3d16eb3 8c463c577a8e5acd791d3dbe88192a29e81a3d3a39009506400e956a8f2c2a654f992f5249f5df2f 1b043a05be5ccf78ce52d73d8b67fb9e618e2357a1485911401da718b23d1d93978a0062de946c4c 2a55111ad7a7196ec49f306bd45aac2354ae0157b4bbeb7e90ab2194110df653cd06014bc070f808 07c4d0540c6938ecdf6df8f518efe11171ccc299b23e843315df81473dfe018faad6071e65d4378c def8dff8aee92c87a7db6a11597a605e5750a02d5b56ea1d943a394ce5b2dd0e4df135149b5aab11 34def4a0e6f0f19894e091ed40708669d210f6e07428ef1f7c704596f620d9219e60e932cf0de8c9 b636a8e5f3fdfef640607d6ebe17fb4d74e0f68e95cbad27de9c74f4ec75f8a5d2134dedd83bec1f 71efe09f9ebd83b979fc8277abf7d64e925285f393ab47fadbe9844a0d875ddeea277d1323dc6902 76d7a309d55af6905b795787f01a50ec1d1bed3423e8f62f54b373df00930e0cc25c3b7eaa46db5a 5dfc363a2befda40977cb45c202ab426cb42ab95e3c471d387336273fad256cd6248641a6b959f36 8baebe6e4eb7f95b73eabeafcda976bbfc20ada59a6edd71d8d8587be9b0359e7371ab96296725dc f70b8b99b9b8403c16ad29561ab487682fa8b5219cee56fa4d97cab593a9cdbb595c369e35413dbc ab67106f56957c88547b719baedc0239995448d75417a8e88bc9b1028dbc4f39aed5ebe5f9dd1995 111f924b1f319fe47868a999a4b5b05172b2d963e9d3af9dcb88d2bffcc262332dedcd9b7b3b6af8 beb9112fd79ccb16adad4a6abc949631c04acc0845fb85710f2ad0682d1d07f26dd4f33f0d0aca3f aa4a4ebc963eb5f2b994cb3ab9e2d42c770aeb1282e4377d92c8d7bdb990138a5b337b4a12f96c2f 4b5c32d7ddad90d14c01ca806457049ee5cfb7be80b9cd368121dfd38121ec47c0b0a8ad7f617e96 1b677d7c510ed2a9c0075cce6b9bb3def8c261647981a17d5c01c1809e35bad73c55686145f35367 83ccb3a29bcabde8352fa95139b73bc0bb5c7bdb7c67fb05b49881ba4c0b4040660020d8110310ae cf0108ae1800c2e7d709b4078098603dc123f9ebdc9b0388354f7e5b2c6100517c0b4054e6f20be9 541d793e2ff23eb38b7820d4ecd9b9d397f0fce141a07d7d098381adb6ba5ac32c37fdd33253eb54 32af321292f77ce44a87ccb5b6098179bd7b02d072fcf9aba1760b09f846822304a0bd26fe95547b 1cf757eec5a2bfcaa9d6fac720fba7baed57e95d760174a809bfa06c4a9d3dffdabf3cea00ce357c b9846954ad3e51307847dd2e985fa4c35ab3b43ee4ab7df5fd2e7a46f3966b91dd1d00bfe4e4b218 affdff44f90badfc5fc1f71bf4d7df6bcb499c6dffafab3715d2c1e2f87b3460a425f1e2c981c099 cf2fc4f168a92410f9041c974011126cf5381ef75609dc4b1c4faad9047233c10b89630c9d25b0a4 04f7791ce34def2b10e3e426c1fc94e0f088e3690148809462abc8359f1f4ae93fc7dbd5f8811fcf cca320f78c3b796357f7b2ee1f6e11e41e6f74ac1dfebf4862d2c5ef6e462493604c25c0e9042b35 89b2e125d08e498040724813aa96600f260176a709a4e4e0b05d724878c149000509c47d82d535b6 0ae7d7f333bb649ee34d5c7a78cd6c3aeb7c14a40e7827af247ea39f4bf9ba43ce8b6bd3cd049723 968ffe40ca3dc3ff0b714c89c9895ceb6c022e89931a27e7ebf44ef67f2692531afa499c4ee5f33d 1a062b27a1fabd7f14746c9a1c12e6c871cc7eac18396697cf8fd40c9f4e17da3fb36ce7f4f01ae3 c7a330e73377f263946fd1c0695d77c308be1c2765e67c213aeab9ffd6fdd3fdbc5c9f60659956e7 39bec676f80fe855f483387e8542fce2d31afbfc96895ffb861cbf27273f7e638363fc068d77fcce 9f4af1cb025af14b4287f1a222114f1734b8676eee280fa2bcb3ee6b6697a6b877aa1eaf6f9b7976 776391e6e5ba773acfab3099662f279faf5c7a25ab73d6e8fbf8346c2709e7c8d6dd039ebdedf724 bc3decc2d3eab0ab8df4fd7697e1765b6e896f7f10bffc6a1298caaa5f1339d3e3e34f1770e28f65 ece38f7479c41faa9b8f3f23b5158f39037c162ad2f81e1e18f2b6fda8c2f59893d46b8730ac8b72 f6bcf3bdefadcffafdb83fc5d3f87ab24ae5d7f143b772c7f10ead1d3c584d57710f85c571ba635a 057573bc7596917ac4f7a159810e21bc69eed70b30bbfb03f471dffe207e3b9a1ebf3f82147f9085 fc8f44c967531f3203249f82ca73ba959a776633482bf85f4fe5e2f0a2ee01ec0cdb007d423d403c baadba71f04775fb505cb6fc3d55e846bb8d841eb77b6d70dd9c56f06bd3eb08c54853dd56f8cce7 46eb778791d619e6e805f955edb82a9de3a34f8f7707bf96f5d2555c6feb19bb5f883f9db51e7f44 997b0150517b6532f7e89519e3d76fd9896749e60b37217a942e202dd74e2379d439ac0a6092af5e 416c2b9633e4469954f948171a6af87c83566ab59adbd872fda9b1e13ad57fd65978f308f0f53bbb 0afa6063553604d8674a45c1e3c7fc7229f1d1d1bdbc82b33b184947e791991d1c6389ee7f21fecc f76afc799a4c12e2ecebbccf849dcd2b5bb26fcfd54e8c6f9d8e713f23e3dcf3b0dad8c0f698838a d19d865aa1351d436b47a6b1c0db2a7450846c7135b3969abfa96c173e73cbacbc3d5edd7a426174 5d9e24e1b394bbdb8a7b5d36d2ca49ae862f68c75cb41c7bb1e50f76a6c95f16ee717a5e60127c5a e4bbade32fbc80fc5e7c01ec2609751d2d5e59818c92b9c7ea944c049cdde923cbc13e9a3ca3cd00 d6cf21801be9042a202a5e7555cdef7a7ea373413cc12d10cb73a3c92e7b7348766f0869ba6024ba ce530d23673e789eecf7aa1adb364997eccc25ec2d961a485aabd5dab2caddee7e4e6bf0755e035b 1773fb2c9e4d2eb901527530ddfee0056057e195a976a557962838af5c4e09efbca5bae75796d2f6 910389117c3c5aeb7cfeb35d55b5669ca4a683e2b25f9fb45cdd9420671e1a1307518eb46ddf2f82 3df6cbe99af4c26b77ec057e1eaf16055bdf5bb3ecea6e95f7e5fc9c69e3ed79ed7dc34d7ec29ac6 d1bfee8c6ea772d72fe1e3aaabf4feac0f6afee9175e80fbe092b36919afec7eecdff980495bdab1 98ccf8b727cfc32213af4f825563aaf9cdbab45e2ae7e0e6c4fc3e6b7f16af9a9dbdd7fb8b29dc45 ac603d995a33d9e0e69b8ca7cc99e5713eaff305cfdcb7fa1bb325e357e3340ab24677f969ead729 973e15a10fce555333fbf25683efd7a7ba80dd9b8a9e84cbff85a479d6c57f84c9c9323a61adaab0 3d4f0a64f85e0e46ebbce30efd26fd10ddbb5d593a280c1fed5c8f491ff9b5d64db36c55ac4d7bce 565eb0b9d72b5832971b50c629988a863c9374a3d70d5c5dd376a10e42858b665afd8f369c390d75 b1cd8ed411b7d215f7d4de28f99ef090fd1b714f719309ae77f9225507fffdf2ca8c26ca57f0bbb7 2c7e71a0aec9ad3e5f074410748191bf6b0f07cb4151601c005f2f1604f0d85a95b8fe9c37ba44d1 6c6342d3900f8b817eebf8a8ae7d5ea40eba154e4bfe9cf6d0daf03059a8768709d4d1c5392acbc1 f1a5e079b826af480b91c9ca589542f610497473144bd563ef216e67f95b8aeb2fbc3232afbd7275 657d1ef50565278a737eed71df555c7ff7d88c96977b69e0a0db2ab12050ca986ff37e640a97d3cd 501ad59cae23bd9af68c889e86d4d9a1fa7eb8b86a9b7b5acd965eb2b28c2a9652a8b77c79b59fee e5725b7d4ab4b22f4bb5010c899cb94be56eb189886be108641e82e8bcbfb80b9dc1f6fa0bafcc2b 9affb9f0799e5237d0becf06b380213db9ed8f9d97751e2cfcb98dceb7604335db176ca5dff6da59 7bbed7e95c4043e87759b51f8db69a2586908287d8582928da4c26efa620978d8d21d1afd352aad9 c5adc8670777e138158a82547c81fc8566585ead7702bebf5fdd131c9edc9db06fffe2fa45aa0ea6 db6f1b7d6cfa59ef102e866af87997d955fd8a924b905e4c6cf7ba05adaa3a86cc4e291474ddcfb8 dafc3d3aa84e4d88150fb60b4a617d68c8b3debb2f97ef0d548a3c8c90ea458213798a4a4b4c88ad eac211246e1df197e3f5c2ab3298ecfa667639c3e8d21c3ccc066cfc56eeaca5888f14df4f37d6a2 e9eb0f5e59b3b3bc6a586bbee7b60d75ed5763d63be75ba4f33684f4f65c90791b340f7ea1a7dffb 22ab21a795a566b7ef8d5278b5eff28c22b352749bd7a43a1e76c57de139145b721e134ebd362374 f59ec45f21c2e20796bce64cc03db12fec99611705a2c302b32bc9b8996fc2ceb859e4c64c3cfcc1 4c8cf493815c7fe1bbba737eb71fe65645b67240413b767913eb33275b6431ab86e890a19c766d0d 2d36676a2ec3197210afd6d2a6f2bc887ba8f4f99eb4002a092749690bddab3ee0af5a38e235ec4d 718f202370c34ac9605f9bd18a1de5893de3928b3793db641ab4cf2e089a68322e4db46a579a2842 4fbab86edee9e2a27cfd85efd344691b5d8e3766f4aa2bd28a0d22ce7d7e2ad422e88d3053aa8f41 2d5eaa2d35d7bde0f22ceea8527d837b62eba91f04993cc4fcf55a28f02058ab71cf3cdee3cc9582 b0ef8e3d6517972dc76606e7b41760b079c6a5fd4b6943132a1553ebc7b64651730aa3aae8c0a6aa ddcb79b6b9651fb3cd26bea5b8fec237ce2369f8f3d02538d9ef001eef38831c6d31781b3306c30e f875de4b6853aeac921b761fed04415e951d5ebb205bee39e06edcd0f732acddbe54d8d125db6233 6a0764f01c3262f23e47d1644991e852185a145d7b86b3add0becf1a67a54c1ed4f69814c19c458a 0d271d3d49317fb9a7b891c273774d71f9c1bd157bee7e27cbf375a9df9796f786cadb9e0f30f396 94c7b497528364aa916b8882fde8f237624a73f3be61b06fef1eb09973f9c4e063e4f5751067f93c 4df2568d2eb7765d2a3c7d86146df789d92e8bf033ce130df2585caec80ef53c13ca0e29107dfe39 9cdedb1b637a2f33fbe9ede3dda6b78b754d7199de22f5fc83cb3c0fa722e47620e5f5d5d6e82bae 45f2c262f67ab0c6357ae32a865f405108d7751e3c8115d65e6e26ccf2d595e9a09ab4af32676d28 3abedca8daba0eccf8eaa0346b6ea70df2a85b0352827623e262dd67c9f5eea4bdc0f4bec59d29dc 9ceff0f854cbe2686fdec7d19aace068aebbc13ee6ecf20f84c9e917ced9cec08d32b9adee771635 d59944bc6435460f5e1f9a2b4c0e510fe4a12296baedd82c782bd133a00d52b5a740cd78f1619092 5bf1c92e363d10ea417d1003719d993e3a8ff214768b2dfc850d407c916726387a8838ccedc40636 b922eb890f2e9f93698c3427c5458f9f14e5473029d270fa40d9a438ee9e26c57efdf80b87c807dc 7529d4f5a5b1cc6a76a9ce2ba602ad7915efd313f198e741d6a982358a997005520aa13231d885e8 d41c3638fc15b2737cd4f703cc35f6e9541dc388ca03cb978699897f962b1342597726a5fe6b38a6 fc1631ae128c38da968ece88bde067f430b89751e1b9255121101c54b01e0754904e7b54986d76bf b0791cb1857f9c92a9a3cb71c58b6e714346d3e757969323788c09fd0f0a32537dd424bbe77e015f 580680e5b14b6b52b2a0c998aee8fc68ab1dcc110795fc51b380edd143a0ddd14e770520e7ebb38c 287aa38df4711619de0b9b54e81aeaeb960ec73d2384e7f701002366610823c4d384e773790bcf45 7603cf4922fa85f5ca7c5a4bb38acced52749ffff199679f182fb5467d9c03842248b168b53935ef b5d46733293d5100ed4c9dd7f04ed75b43e8ce8fe0d8bbb0305a2a68d0e73c76214751a2e4280e17 2897cbbf41cf878a60b12db607c1f9800e66ce901b54266bbbbf6930b73e73843b7d6601297d463e 877d86daa66f95ef478bc3ba1fc961f003ffb85b2ebeeabec5e762db803dc6522a514fe01f870fce 946800267b8f670b5b9e0a65f4c8140118c55bf7c1ece13ffa6ca75debed1d16ec0993cdb4d76e54 d22947f7f49c9a5d796e78dd1eb2db76b44df6d6015934db7e8241ab6dc6b5517b182ca5d65b26a2 96dd074b2dbb3e655b8b67d66bbd1b65bff5cea79ffe075f697f117591a579250ba966a3b9a7992d 9ee1b7c02ed50b313b36af08fe096fdd711d3c5686f07a030ca8c9e6d6ed1999436bdce0f6cd4279 073456d74ebd41ba0ad428635bbc1e35ab7c9d3e6246bd66b97e6d47e58eb5e6167e578ffa261553 aad27432a9764b59a372393fef15952d8daa9d1b6056a56ad7a94a99bafd0b765050fcf909c303fd cd1402a5d6acdbbc557dca74eded9053eb40c06336d36f0e4da155188495d6ebab43678797561614 77b51d1f049501def54a00f35a17b1fcec51cc1d3685822f155b8552eac5a4586396af36ee4a6e13 e37e8e0dced75c8322cbd943bf3ace8a3960916daf7299ac1023b3eca1b9d5720d284c87b5eff607 d61e5d46c653a4372ab9f05642325733990ad8e10878b31f4faac5557f683e98ca2094d84ce75e63 ef4d0f5b1e6bbc718fca6673e0177c5fb3b362e16c00b7cdd60620bc97f60200c4b9495a6e54cb00 e4990300da570800ce791a00379030c1f30dc0d0b20bc0538d076042da01b0187400580a6500a61a ee2f983736dc6bb85f8ec44bf472d8e2cc96d3612d75994f28e30d0d9f6ad418d09296eb18fd45dc 080ef6a92ab7e26d6911c0416e4b6376a63f3bcf016813ce01b8f2b60178340c92ff7c7305607b98 03e070dd02e00f3c02869d8b000c678a070ce576ea2006868b671d18866b02185e2c17185e950f80 64741440b2b295c05ffe401f47dc5e3a1e273e97e3f3fad7623e09074374f8bc7cda033a3e95be82 7ea3325f5d2adae2be2be2ddaf45312beeb0e47081db2289ad9a7c5a8cfc7f24d5961b01c3e97e0b 0ccdee0d18eef72500c94ffa0002de0900d1e70680ec463b00cd55f3005abf0eff1a64b9dd5f0bf2 d7c30b127facbd20f185bcdb3a6b1e50286b76fa94056c85b818a207e3fe80818bd5f6227b00eaac 78be97e774f994a70fc53033280e97002c831e009fd8001816cd3009a7720490c1f40a208c740710 d5480dfe00e2384f00b9ec5e000a744b7fe5deaf1ff96c02689f4d621b3472094ed03f6f63fb5392 37bc2578be7ef057d7fd2aa63fdae952f98a90ff914ff34002b69ee004fd954f792e41a4c5319eb5 13f457b155186f6224a08fcfcf4cb93d9dcaf2fd1c6f8ef96796cbd61e5eb3df7b142472740fba37 f14ede7ae9d2f72d8288f557cbbdee506c77e50170fb074db7b1f9c15f851722137492fd8ffac457 fd4d42cc24fb1fb3bb048fe49026a3dfdae9f76852f9145979c6d3297b4987c878cb07bef7824741 d86def41fb7eba9397c2e35e5607995b34e0abb79ae9f4aedc273bbd361d5cba4839db395f885570 564bf3d5e94e93de2fa436b8749b8485cc12942709b2a30435ecaf8c7e9b27518ec204eb5b12653b 096ca2b5123c86497439fc398e00eae1d573fc3d683da47b597ee937faf698df6a46cebd72af625a 5fe0da5c747797e3687abd7496fcfbac16fcf2e93e7bf54ed0764a1eada6211d3e52303f8cafaa79 c86a8cb1f7c089fe0bff0486de870976e03fc1ca221bc7d441ffd304da7e02f3f4a70958b951f5e9 94b0ce03df81e09d3cf5d024b60676e5e23c7139a225e67c99bcf9b39a2f29c9e92c1927a30cbac7 9819ad8fc89e3d1e9cf6223e649577655f3041681b8d87e9c2ca8687293e3aae875c24c92017757b 0d36ea8e7adc0fe2f885e3f1ab0825019eb250fcf2ce4cfcce617afcda6b6efc22fc287eb55f9767 ae683eeec152c9dc981957bab640ba71e9925c2aa99ed548199e1e75657c824d3b394b6f9b39b859 5d38e4a6bab29f2639e26edd3bf85b260e379b3d95bf47270e2a849a14c2eb7975c4053627684126 c6d4d5d20295153eeb29ab42a52affe06b22376c227ef17d287ef797e3f8533695f883e69cf8d345 a3780cc2c74769b7395f0f38763fdfd8eafb34ef14f2c771ae503be0ed5a774f8e6bd0ae5688916d b31b631b71999d4597668d8ffa5253090d004a4d16eb98a5fdb5058687e063655ec1a442d656c5fc 61ec57d09ee8d5d991b9e49f6d63d95a350cf744e675572e3db51fc4eff1934c2042f1a7d326e2cf f3aabc80c264193b8114dd99b19fae115c5428139e9087bd3f14bcf165bb5bd6e24d17ade4c247a3 5809e1d5a0bd5e74a6d01a70985180d55562e50b2abd228691e4afc3bbe9570785a5b7798c775e83 983f9742f8a9b84a4e193b7afb26da4fe76b80b1917ade5abc77cff9c2e60fe62fc4eff03689dfef edf005e435ea052c5bea0bd8328bfb4670adf30353b483f78ab4ad5099d911d89946ebc583bf06d8 59fdacc89a5ff26978ddf8caddeb53cf6bf65ec3e5315bc7969d696be65e5a53c1ed2f24ddb98f3c c781b6f1c68e79e86e232fafbc7068646479c652b48aa5923b9f51677b5ea9860b33dada96192df5 f90fe24fe9867e2df1076cfa026e45e195194dd5479591f98b66e1c48168d5479b9eea536b5bd9e8 abd5ebbaf26b1e705c1e17c5e7b25b0573ee001a551d23a0da0e2c2d207b01f8231b754f840d7045 6e3139d69545ce9a7d2d2344c50aad629212cf29685a9c575e59c46c5469c110b8ada39fe295abcb 9262ebbd2ebdf885f8233626ff28d2d44cbd1d843675ccbf67835db32237a39bfd6c045e2d9f8a29 3e4381d2523a628e3bb86a5b6798f76ff60839030b77932f2ff260a369f9cf61df2a9104320f3b1a 3eafaa0bdadc4e4e92c9e58b73e320c22b43ec5867fdece672ba9224e9daedd8e735a83b77d4b9a6 a4628a8a80b4abbc0f90fd0b7fce66aa483fc924b853962d8fb792f4ea875654aa079e8c97fcba64 23eef570e69ca156b216cb4e636dad18ec6c951eda7b4e13ab82b93d9fd3777a99cdfead631c7325 c810f7c3897e698f485d796b82deb7378666d4014f83f693a36a0dd71915f9c05dc599dc5925bb42 16f2b48c2ce542d471e5825373a46092b57ff0021adaec2b4ca6f70dc42d89fda6749f84af83db0f 4ac56dddafc7d9bcab1d3a03db9e70d4a2504ada4c943dfbe66e583d1a47bafd303a773cabab53ae ac0f8a764b33e4555f837b67447d65ab846a7920ab022d4a55d2b291a94531370676b29f9dbde5e2 f4da92d66d87962a6ad91237f58e27d6f98f2bd6a77747d81777f60f5e8076625f9993679e1c4927 37d7d3145de71d79e03733aba67b538f05db56766d2b6877d299cdbcb61bcbc671adb8fa35b3dd69 8fe6f9aac18bfc477d51484945b7e3ba0ae84a4f99c43aace4822d26fbb3372d176f0d595aebc442 aac466246ed695586c54b5a6b0dfa194d06e4573feb4be7bbc4c7d570a797914387c2f63d93ff85e f8072d01f2b1a871b3687883472bdaa7c0a55a98b7eccfd02a58013ca998bb643831a4534dd407db b1adc13117aaa38a7e525c2d8c957cfe9e93fda0f035c396bacde4545d918144b9ca58aa62ab99c8 1ece82d8901a96205cb150686b9b07af148675beb72ece383dc3181cb80cfc141efb8454e717beb7 d1b51ff6d83d2b2269a9e3f5f25819f9cd3706b930aab417d33e53983372bd68c80515d11e9b23a7 2e82c25c71affd405ee5c707b9248a0f2944828c5475762571cb669b62e350e8098739840822424f f9b3a370bc323998dced504d734f4e17e53bfbbcbcabec5c5b132c024d4ce67d347cc696f82563cf 662e33aea0ce0fbeab26e757b6c16d957a960c02b4385e5e6b3dc87641bc6d55f869d138b1a79cf6 b87641753467d2a351f2aaa7c925ffb294e84e792b6e1de82a36ebe45b10053e2f74865e8d3fbf8f 1d5e890a3077afd7269cbe1f336c3c543476febead98cfa477659c5c5866b22485d11e00e8099815 8d6bf03245da04689ced3a3ff88ae76780aff2d1f315112b460326eec303870b7f0eb5cdfd1a2de9 202203eac8df77947cd49d4aa1cfc8e2f6ed39c2b1768c840e1f9ff8cbb013f37d7b90e1ee34933e b1c2413ba9c9c64630602df83e6291778d629c312433d99dbea43d2173a60b67ba48056a6f42cdea 1b959a35d015555ed59754795e7453383f8871ca328ffe3a2f844ed5227c21f74acbe638ef560fb1 a8a8d636e45baba4be190250f267b921d1de632236950a2f74769339afc246c0dd17de9e83ab973b 1befb21f1685eb05e6f3826b8cb3967acc84f210dadbee88a41194252a38213635935769f5d1d926 dbcfcf18e389ceea435399d53b159fdc850ff71fd827fb07f7a67392bf0b3b6b4f3748afd7de634e 66d146e78dfba7ad43855c49c187f58f44ef1a25e128cb435ee536f4778de055d1d95775b064516e ba6101c8ba3013cb8b99dcec91a3fd6ab14217f56e9b5a4353889ac5063edba0013763abcd05b9e7 b81d29b4b219e224cf41e244533221674f1e21dd37ee3fd87d5fb0f1dd7e711d0c35697738ac8460 7694664bd0dee08b95fb468c4bf1d856edcfa92445d5eb5b385a61961fe08d3efb42298205d885c4 4c9ed705edcf2a6b9aa8c0076aadcdee1405aa1faaf25ca7d2d08c254ff559a352ec93fbeb644cb6 419e264ecfa349f4d06638bd01cbf7545b92fd2908378429d8729709fc25fe58cb0efe58b08b1f9c 5f8a206f8cfa985b352a0ce522fd3d61d18703f22d9bd3f3dacab4ee97c456c17f730f7018b3e867 d56272cfcc9826c8114751aa6050d5c9c69bb1fe75336b88f50b295c5a4fb2ad6259e23c31ab8492 db74a637e20a4ff5ce809c821755c5e7838b8f0f9f74faee55cc46b32dcceede38ccaeb20e365a62 5fd8d8481f2e7e70c23c560d9d738df3ce4966e464f935691e231bd1505c6d4b9b925ce21ed82866 81f3fb419786b51255551468d6d81dc8afdc3daccac4d9062da25fa356d3dbcedc4d757e739d4243 e08dcfed521e47c6c33af6a96903ccde9d302cdb2e7293e55b75268549f63c0e72416dbc7acea9f1 2a685863122d2d52249fba99f917e97ba3d2ed819647daba78cff24b88849945705a5386fa1011c5 47a76db19dc54a8c1b659e54d8d26fe461c9bd08c5ba34a7faad3fc2635c647024b015ec233d6d6c 7ccb0758566fef261e4edd26d382992eac4c0ac1a1349ef532ad71f9361c8e227d418deac59c81ee d6c60ee569b488b672351c6d3e42036dee0f16daf4c339da3497e60fb6837228af1ac145703e11cc 59f5ad4669aff31891ab6037b58ff2daab5da2c97ae64576a0e6058f192f994c25cd61e2394c7752 ac07e3f1ccfc247d3309a92326c217a33aa3acd0fd60bb47f9c7fb869c90d20791cb6405e946cbde f0cae6b1e1e04949f0c30202d844fdd42f00c3376304c3bbbe02c30662c030dfd77f21c4dab4ecf5 dd9d64133954347bb3e14ccdf385a12822950efbc994ca544dc87ca670257b9d4cedc961545fe74e 683b231711993ba526f2e1f555190db5c584829f554982cd6d308787fac787de70730b8d16d005ca 5006002eabf71a88ed2068b0826d6a40bc7b8b41c97edcfae1e00af6c386ca0f4ae04e1b941a81fa 0bc96efeac42a0f340b1b6c389f2b59acbb41423bc7edfb799c2ea5c26bba30b80b983cf7dd46855 0ef01301d750c6ef1dc1fcdbce0d5651be3528d7db503f7cb0789f365db65f235e5a925216ed5e93 81d3d1b37b1c18e7aef878009d0bd2aa75d4b28776fa1b486adff5d2ae6db4f2cdb6fe5acfdaf776 5d6edf4b45a97d7b03e20fbc41b5acd98413e8f38e32d5550fc9d3e2d14dc61467ed7666fbc9329d 724c87cd20330e4bc7c7f0e69e0e206ebcd7bde6b1e376068d5ad436567adc7a95a342cbba965b2d 541bc32d009b4f9b6efec03527c786d1f0bba3248102fc63a3b8ac02f535b768d72ba7cab4b659dc 9d1a3bbea47ee8da867962f58a36e2eb1516e4eab30dc6ffc07183ac6971fb9d65c0d381a154980c cb9b1d674453b8d025b4b7589dac34298b7678eb094dca8b636f9f09c336bcdf390d625a37ab87c6 26cdd62a67b4b8ad2895d9a3d2dbb8b9f24d071a651d1a8065a8604c4bcfe4562821bda6557cdff4 6dd1f69a4071cc6f7b85e599e20a85c9f450586ebc413173ebcf8ae32a342bda144efd601101cdf4 a96ff3dabebb9abb802df15c3c702cf65027b34e971de0e3bbd21cb53a4c010670f8d9af9f7b87b6 e94241a3644ced6a27bfd04b88f19672d1acc6668eab9a9e9144cecf743be101b802a518505db204 a89b5b6a8907061c8803037daf000363b606067eed9de0de0706ab73f2dba9120383300701031bc5 7f612e3d3bbefe1e7f56df0adcfcfcb615e9dad920a628d903477b30ae7d15c7748da031142eede1 359999873bcbab28cfcdbc38696794ecbe526301ad6dcc00b0147100888c170048861100268d0d00 2d3b0f80a77607808a1b1c80923b1680c8420840e20e4886607d00400b4a02207b76fa0ac48eda07 a00d920ccf1b5bff81f14490502565cb17ee026530957c8199beb0053aaedfa016bc38170bfdc6fe f46c7d2ae1a1ce642febf23d069cc2f45cd53327b7c903a070e700280fc9dfdd609c06409a6d02d0 b61a0070c6bd0170ad9405e0b1d90260b53506e0cb450686157d0d0c7bc33730ec67bac070bc6781 211384c090758ac0909b4f81215f557ea0e16636142fea3e6d696cb12b8ac443aee0e3cd69d987df c2b4d26f82f54fcbe93ecf359e3f86a597945be636dba70ee88dbef44f60ff11a3ff157cffe8aafe 16180e5b0f60689eaac0f03083bf926ab3cc0208777001c4d36e00b2c71a00f2ac13009a8f6d2099 0fdc01b4c53580a4271cfd403a5add2597c3df1a79f5546a12e61ac32152f71afda686655b9351f5 566bf9e55d295329faa90e7df0da0b007297e63fb151ed2500af89d57fc5e8d4e4fb47f8ada31700 993049384ef40650406affad3d3197feba7a4fcfff5a90c1d8faead05f79fa5b5bf82ffecaa75fd1 b489ff8f7c9a77fe473ec52a09d6fd38c62a93bfd6d3afabf762c656fee3c44850488fe6f99935a3 a753810ecff106bf3eb3acf87a780d2fffc00f71e3af5596236e11b8556e74dcf66e358bddff47e6 4d75ddff386753fc557853ed14fda07facbd85f1ffc8a758f2afc6fb471265af90c06cff31207fe5 53a75ca19f59a6c23fbc7a5779e07bd47c1484a97d0fdabc7f2f2b52748bfad6e146df77f75bcdcc 65aedcbb9b2e165f8ee359ffd2f18eb3b35a2a2be77e883b27a3862c4fd0aee31e63beecfcc2df28 ff954fd118f9aba0ff18a517e73fda29e283e96e1e5e2ddf7c14f8c6e08f7c1af57ad88dbe8d66d7 1d3462af5c4c4997234a699724e1b52e1d77b13cab793f3cddc9fdf164548af1d16af48a878fb800 0fe3cb75bac79f457147be1ff296c99ac2b6e609e9a38b9bdd94e07ef037ca20b9aaa8d44c7081ff 3681b9f4d3049e4e91bddd8366ef75a3afcddc957bd6ca17f1536e9c2f935af7dcf76be0e94ef4d1 1314f6d3fce618d3087944760873705a63f19095497d8fdf446747c6ca7a5b73f5d3a6b9ba64a3ce a6d90efb0768b8be8b34be862efd4910abb5f11f5883ece88b24b0689060564b30a97c23fe4f13a0 d90407e3e914cedebd2c92c1e5382c6ccefde5e5788282cbfd886c8eef83d3b8e6f65e675fda17d4 7d6d5736fde6b6669b9d0d9f3507d171ba40a2cedac542b5baa1d777eea5aca15337cd3d03e4a69e 570ef4c8f99e0c77fc629581bde03683bd998e425e0587ff8338b6bca4e1cd5ab9383e1ab5f83545 90f8c52f67f1abb54d5a9fd4e46f355b12ce17aaab1e91cbdad87b849bee66371bfbe166779277d1 29a35da22eb778866a1c66d60f342cace1cda11abcf44c3b40e3121800c168b2c2ba02edfbee46f3 4b5037f0a8e3f1bd6cdce096db29186347b12cdce9a31266df81d9c4d6dd6fd5deeff68bf41537f5 a407608af1bb0675e3b77d18c76fa7cd3c56bd39731586ccf4046f346cef0fdab3cd7e7913a2deba bd583f976010bc636c1f642bc475856bc2cb0ff27ad62f8b76c98b90c35752ad45cfde72a7359065 f3094e5d89a438b7dbd9cf1d55ab84f623afc68b17daea2c0096c7acc9539d59b93943ce7d6442cc a79fc1f407f10b0faa4988fd4efc299d91f84396c64f2f1ba6477315551e3cdae8a8b3e55a7437d4 630e0b1c4ce456537265fa15e6ec7b75f3b6590a9deac53d39bdd8ede5a08ca309e3a2030ec5bafd 2cad7af6900ee1c57b9049a6d87e8f5b6408d9b4b0f3359caf06709a46cd4bfeab63562f286e7013 86319ab909ad1ffc01a58b447df6833f51a68af4579dbcd733e5c1d1d187f97d794fbc36e2517fac ad43dc5c150f5934c9b89ab47b1e42aa7353b8b409d84f4f096d040e4f0b7b7d7e2cb2d5f3db5ade cb050bf7a0da3c2822ed397912a17979b1c54d267363ccda6e68187c4b0bf4a39d7be8dd9adfd654 a18eabf7f79457e17e85f94289ef00fd45aad9a4dbf8ad8883f833ef618f75f6363901e75c737bc8 9df3a179dc3ed6c0a173f5a93d5359766a16e4e8a71db9f8e8b1bcc8ae0b96351d21c13cd8728779 05d26f26132cdf667db6cb19fcf55e315a7a367db2583fe110a8778fe458bb4a36ad81c053531fcb d14a1d3637776521134d259379e032366d0972ee0570926f5d59894037cc0ffed1f7c105757e5c82 c19e86f6add0caea85244f9cc75ff1febe3bb9caed91b32d79d05be420069faf51473037ecce34f6 ab8f6fb4bbb5ad2ebbfdb3dec3f0a7a61df88c068a46517d22bb863a8ce2bef2666ba8327a8e674a 663e9765bc92f1e4fc46b84a64b39a0ac46268b99858bd3e0461abed7981037d4ee01a0bf607dfb3 f99c5e40e294d93bd06690279ac174cd953cfe22beddc1483dd9a824bd2da2f1689b9b6b7d6c0807 94d595b2a46b37d54b9b80faf4a3509d0b9fa3f2fe94ef8aedf43f4ab68ee6e5e59ea9cab8e975a5 d53b8225322a1362544744917e84aeb0431a2781fb1c2afc1123c67c67b516b80b62f29cf211384e b950ec0fbe55e2f3027995f5317a20dfd630442f60cbdfdce88aabf9f8c71e41fcc522b0d6dd643f 665d3ffb2744d39d1ca5cef72d59f9108cad64157e95e48cce4e2e78d1450af8cf4b2a9f735931b2 071591194fd3670a859dc78302cfef70fe78fa707cd20c16dc357bd873aacf15d847b98ab246640a 2c3c1104e6952378c6327bec0fbe4b1117301e8f77ad290aaff303b093f4afe3aa6dd3cd8f452693 c9af57bd7d3aeb8a0f9635c89641e5e3ed49257b7a8972315b34a5193ff0c4e835894466c11f857d d5b80bfc36f8082dfd55e4a557aece77d7e880bb52d2981bdc762cfb303a73167ee95b267d992f33 ca9ed3429a0ce0133ced0e47023d3937b85ff88ae7a74f179a6cc04c0e5a8542bbebdebb506d817b 15605e03bb57fd5293f749c7f7cc2b8e37eacbd3858049b3adcb894cbcd184fdec95e63742eb5659 f3277db0e77b85c995bb06fc8bd3243fcf3eae9b2a6b2e735d7688c328b308788a1949b141bbb771 44637a0c50fecb862942a9b254a956e2bf98ad9737f607cfe2874aad56478fed60e11b7cc13e1756 7b8e85d56a56f9f8020ca993bb6a7011db2bce7699a4f8d2b3256eb8062aec0382e64f5759e17be0 c2e26ef9eb8a032560cb3ebbf5333b74074fe68d1119c6ce6b656674d8a72a07bdecbc601a072633 6a85592a45166aeb5938b3df33ba4a83b3eaf2c9905be8c9ff017b0cd91fdc84f5163fcc56357cbd 24f7b027f54b7d7b42dcabe61ed803fa807edd54a0f735c0c8c560f81459d6ae0b6df20cf33dad44 727a7e28b2c9af263bbcc82ef376d62133ce6df64ce600dce8a5d8fad0384295a89523342872b206 67e121379dd1e24c9ed53af715c969744c362168401c8e61da7512e26ccb1322bae2880e30677f70 554607625fff14a6c1eab2192e552f3f584ccf979a71623719f565ec6f32917fef4456bd5df83382 94b99b3a1fb04fff8033eff39365c69386c664eb84f395bb056a4517dada965abd3717aa3c3ec5b3 a85ecdcfe8fdb046eeda548fe4de7b8c6c3a5d919072c692e8acfa8fe96556ee4f5540a7a67dcde6 a77d56e7a67d4c647f7081f490dcf6782f9da7ad98ba8fb8662b035a95bb53d5b5f922a3b86bf726 5591db9e3f13e29ed3996c9e4508a8c38c6d654c7bb590a60bfc4da6c8777e4e95ed9e37636aa324 a3deb14772c79b77b2f90a01e2b878970969dc4bab8f4e2f3b613455f92b371db4a70e6e28f72b0e 0fa20e161f10128b57228fc5739ac3ac0ec4fce08c42673a9a5312e6f3a68b3a23f40c9a87a254d5 86919091c98c791792017cc741e34e6ae7619ca2fea1bd51dca002be09cfa205329d31d45c2077db a546b6a0834d1ce3f78a9016d52dd1a5b0eb54dd52afe9405f15f107f46ee1f0a23dc45ed482c116 b5fa020378e73c714b7c6be27c6ac464a242e933ec9309d36127b95c96fec1d1474326cce213cceb 9ecd919d6757a0a1f4a65535f3c032e2f688dd383dc4b7cc38be8754b02c5c67512c95c97d25e891 2ded3326e442879e5e678834d5ba4aaa75e20f4d7771135cadf161fe7dc45e41e3818dbae32c065c f5dac4d56ee02417b789b11f6ccc71a98a1e466baf5e1fad8d081b51ad0f3ba28a7766347b1de91f ecb7798509826032759f35399d4059e15c0775081fa475727a1941aa0cef8c332d6fa89966adc93d a2ee0879f0ca4c3571d0c01f1f09c6871307c716fb238b8dc4ac826590be35c11cc49be427dc66bc aaaf2ee39270788dc24eb938a23e5caa0ea2dbc91643b9fc50410ee43e42c48a5f4684db6484085b 8f4184a5452382ae523fd8dc1e65cadfb3f5a963f332366faa24a83a68b12ad5b3f90cf7c857efd4 6c134564eb32584d41789f2e1663ef317cc332865998e0c56b7bbc52eac8b8741f4c47a1316347b5 a2aaa0db7560a11c75f1d066bfb9434403ba221d58cd0c2fc5b831ecd3fd217cafad381812863e14 77da39e8f939c1d0f30a53d073d34d17bda0a75b277f103af29df67af30a69e32f933054a10f2ac5 c1b52a48f139c3a2efe77dd66844db2968e6569325c7bae372970dd15d63f3449b56b5844800d842 3a1b6d38541b0b6cd87f5ed39738c077ab28c3305037a17833f3218b75f6e0e7b0bb838e35288213 d4ea0dbc6596184cf1e562502c0acf41e13cea0f0ae19e1c14ec35312828eef40741e568d1eefc52 a72d664910daa21aa78f2e4a6cdbaf728feb3243afb28707d93e5c0ed898ba0623fada70862acd99 303cdd6e2054c9dc21608ce7c1c94e6f80397e3b184c5feff1a0683766fdf57824f4a99a69f636fc daebb1eddcbe573f4f5f5d61bc4e53dc6e3b37403a32b1933bbdf2fcd0e901f356a7bbad621d39d3 c03ad2ad34f98177d624d6f6f645d694676d42f18fc14038f7cd1a3b7aead9d9763d7f4c9379f361 3c6bba01d2256e0e84befae9c37efd6a9957bb875cb4ea0a87e6b97346c84f47fe58a5f66d1234db 7a230bb5c14367d27ace45b6354742adf9769e6ed366b14373dcd8028da5d46b3770f441350a9928 68e47751b991f749b4813db85103db93a9fcf0dd7ef1c7125f6db7447db83f4fe5ead502790816ea 3419d1394285f007966bf34754a89821f4ceaedc7ed8b8181d85a8484dfbc8f28d026b5af50002a2 7ad9aa9f6bd16cf6ae319545ba1a55ab6daeadea4e6fc1d5663c99568e41205524aaec942f37f158 568d42bef428da70c90865a36438cea73ca84883f20050a1b2da35fe039b9848cabcd36f292a6eaf 0851ca1b303ba96269b6366b11501e5f749178b4b94fcff05324a20139a2ddce65a01a4d87db4849 3e9767ca5ab1831747f90f5d70a5b151c050dbcf13d8729b2fe59fb7dcfa88e472d52e57cf6eae07 38cb2e412a732898464658d7d3fa0299b67cca03f25d4281f360eb67daec36971196602bb3bf0dff 836f49733d3e01aa1c25fd250f3bb3215d3982ade9b3d62c4e4a87fe07e953832b38cd81dbae6441 5e0ba0d1a4718058fad477199c8a54013b4458e6b038c1401f3ae1401f8645a03ff516405fa84509 b82bd037e31cd09f936da0bf7a63403f1064a07fea86092e1fa07f4eae49ff131ac000b8be81fe4d ee24d80fbf48f5b4746baa61606b58af628aa7d39261f3048a92e7cdb087792da8864a4c2b0be590 ebb57be256512b530adc5a4373b5d2d33bf0b9f0e2e0c085065060500346c060dcc581016df0c040 2d58c0c0e1c2af0e1d9d2fc0e03ace0260e5d606c01e8b01e0b8a800201d4500a84a1900d4201000 97a00680bbc11500f7dd2e00fa11f903fdddbffb0a0d5516fc7cbb12e85a284df1cfe7fff175a6db 8a2adbb67e96b02e501404a944905210104511042bacc55adfff283373cfb5d73de7fe581fd93257 9b8e8901c4e8d1a393c55ab5f1e2bb14417070fb899ed2853d1c50ef7979bfbe8d0bf42c6b65a0f7 43fe948572003fe99f9f560f15d0140e5dd09ce77a80c86a2340345f4b4068dd332082570610d7cf a922715c00a47a7500d9ed6f0139a4bf21a7800c0b1420b7cf012077871320ef6105908fbef4c150 4e608bb21df6ae0237548bb7b7d2793607746b37846162ba99a6d093c2c5306f605119d954fc7c5b a80e52c7f643fd53d8e8ad273a7453b40131183b7f34df46d6ffa3abfe68beb31da00a2d0028e106 036a6eb380ced17d4093d908d09d7d1ad0bd110168a36302dac5361f5c3f7f37f1885f7c9bc2585d badd7c49e9093739c3b1ebe5b841b64eb54203a92fef307f3776a5f8c284b9ceabee804b36a703a2 caf67f0afbb7f138d1a1bf2e7a4a3f6fff68bd19fc94e8d089dc3b05d9cf5089beae5e51fd9bd53b 3a02066f15ffbe426e3b040c21aeff4636e44082bfa25e229725d6d3c4849a68a77737d14e7f7c9d 897cdacc7f3041fee637e0c25f57efc27ed0f3c5e8fe16bfafc728c6e19d8ddeab7bba5bdcddc22a 76bef17bfe795d207af62ac6117c2d0e20faa2dcfbfa051a5fdcf396a1d6894bf5bff5dd7fe72124 55264ae4ff2d9ffee64cf4aa0f7a36c2ee7e614adfd3ca8cbb85d0f1dbdfdcf8edf38f5b563c6586 d762bfe45e220c0d2e90832eceda93dc9ee1897a8ecdd4049c62fe583ed9f90e73bcca61f7486cde a31f49d785dfdefe6d46ee0f7cd41f2748b4d3e4a43161e303bdfac181f87ba63deb536561fa81b5 bfbf3ba3db3d2d07e95b4e0b8ad7a219c017e53c6f9cb7cd1579d6ee27363ed07b3e36de17e914b7 2fdd939d7919c7ab981a1c89289b6c60762bd5d9fedda3b67bf6d4bbedf86b90db8aaf4273a3a69b 9db59ea7bad1412e2891597ac911b23e49bff8abf076ca1f90853f15ff63085cc6377ead7d1b8f6b d1d05617c8ee1fcff068788f11cf06272cd4b347276f960e0fd9ac1ee88d89ec7d58c7f669532576 fc5965b6e2bdddde403e2aacf50cd98dcc02d35fc58ae9afb0ed61b7244e686ae1627a75fe76b65f f7c39c7d3ae43c3dd18959d8ea347ff157e4ad643ec0f37f65e9af4add22c41b1f1dd433eca0da29 6ea183e395273f5f50095decd97d69b3cbf56ffb4d442e4feb6d6bf2dd47101d78eb1e210bebbdb2 4b467a79ed1af9a5531d438b8711200b3a5e11731fbf70b3904e7567399f1a85d2a73d0ca2cf1321 8022a13cd52badca14de35a07fe15353edfbd527a1da9f633ef501534c4eedddcfba547c2064fcf0 102ee49eddeef9ad7819ca6b1dd05a84cc116b8545b0b32476c5c9c2ad43e1fcdd2f2ee6ecb5b89e 8564713fe39fc8395cb4b04728a53ae920e2f5ef1d3a80162e32d5d107eb1f02b6e7a3f07de1d9cb ee7d324aed4b1372b32fbb4f6d5e722744f00f2455fec1a1937b3c1efa67082cdcea25aab5f2fbb7 7c005bf1ace7233303bef3b4e5d5cb3416ae5362e66dab26ce3a1b520f9744c70e4b077b12741937 0caaa56d34dd9ddf87697df8b9f44e4427e537167ac11ba27ecd2352697a32d658c57d1de663976d 71a7313fdce7478b7cbd312a5ad96f47e044e81d7194f3befe8b3f557e15e967f0a8de846c267bb2 ef8dfdfebd84e61b1537d72b7bdbb92c5ae55e76263c766808a5406bba7fe4a5296232a68f4d99a1 77ed9abe47dcddaf257ee28ae166c220f1690240eefee9544ac0cd54dbc571676cd647cb62448fa4 35a438eb667fec54dcf271583b7b858145a470fb3cbfe0f6809e376cfc3d467ff1397d4cf6f1bc16 6bb7391a7e2750c7f1e0f9daae9efb6384e166b81ca78efe3c57aec4c1566a65a768d3aa7b37644a 4f9eea519c8039e8b91c52198e673eee8d0b1566365adecccd489e8527675308af4ef77403c37d3b 5f181a19b23638f5ccefcd66d0af5f15fbe2d3239bc86c76fdb1a1152ccfdf372db6bd27ad74664e 98c1a88fffe28f78ee0d9ba72b39cdef247f055637b71fcf67587b1146e7de647a2c4e76de6d9f4a 4d5a76fdfbdbb8d991408e0ba7913082d253cdd1f49d3ddc3f6feed05895c2418ca1d1a07f65f603 2c34afb65373df3671dce5fb2e0be03e932649cbd71dc94a1f1f4333f4865b6331837246a93e6ef6 a2d598eaa98af15d5eed552091e855d26c33c1e385d1e87575ded60fd3c6ac1c5d581e2cb277fe12 ecaddacabfd02dcfa34654e4f2d8e8398af29ffbb89ecee243936c7283f8d8eb0ef0d6a46f3b9fe6 a0ff84d7d3befb787ebd1c7d20e777d6b45c8badf6457a9933a2973585dcba6a2ca567d3285d48a1 b71e2e06bd6a9e58eb3df991d38e5b1ed72cdd60b4469ea5bae7254676cf139848f0789de6641cc7 526db7822795e5bbc37f63a8c3ad8d5c7d87c8af27ecacecbb39f9351b41cfd66d6806fdc26090bb 603679beb7fa13ad2ef7c1a365589cd475ccd9bae799c2603a370bcdddc690e7e7a35196f2f75ef7 dc48f7aa03b9ac1bd929a6d71799efae08ad8faafdee65705d7687449851ef131853c72dbea55217 84565e5a9afc45b260721ab575741d67ebe545be8165a767f27d9d4c8bb7cd58f2b281b37d0cfd21 bac99eed914964fbadaef2556e2d6eee51e69cbe8b66615dd00da55ab57b9b71d3ed69a21ceafba8 bbd20d7b7ad0117c77d5faa3f35bc3c446a93b8c04b44bd83b4e1ddf6ba6f2a6ddb9e20329a5a4b9 6b430e1e4546e6c76f5ae68de4e9991c13dcc427dd3ca67a2a1691c502345b0fb1bc4fb5e29b5ba8 ecb6ce2eb887c346499cd894111cfaa9413a65cec7346cac2285e86d6e7dae070b81a21ba7bda923 7e76a4d99982af61bbe6f72ed0bdf6846d97785967f5e1cd5e2a03dd0bca7bd7409476bddb52d26f 60c89db63a93f3591c4812ed2352f1fd6e8991bda5c5485d50bfb8e8c334be9faf3af80aa8856ad8 3bc0052f35dc7e9789461b61b91b5a8fd3cc1e67b18915a8746416a9e06540fd77b907cf604c475e 2cabd9ac2275afdbbede759cd5407d3c37aeeaae52339551aa6bc5bf5227a5ed741f72f81c67e5ce f2559396508396a4edf4ab40896b0a0d44f59d7e8b15db46845de5d012f4994f0b35c1a67e71b6e0 1cb18342145fcc662938c040bae0ce17fedd31faa39d7d3f07b37eea9d9b98c55e7adedbf6b5bb6e ea6141b327f157b6eb3a9b37a53e49b8a3324bbaab0259b19469c31b2b9961309567fc65250bf9ec 5e5a5a8d9b2437b48cb81e7a15b1db2950c26e29ab424f017ee7b80d1f1d4b206b1df4e53009f873 5fa2127c85aeef316e9a396a8332376cbeaabd6afea31217c651c9be0fb1b0bbebfbd5f1dc14aff1 a4a7b7fccf23b0039dbbb7563ba33e870eacb6720752995a695ec95c8a8a2cf088211772ec505a1e fabe544687f3e4a49de71bb11ba42ec23e0703a1b750ca9d13ba6a76ac4b43e62fc4d4e5074ffdc6 37fb2598bb432ac38d4292fa030725139c6890a6a3517389cd34f952f7fcba9f1fe94de93e206bf2 d7736b75686d6e54f28b898e0ed54977c41c8e6a8bc8038593f0b23c3beb985ce0fc9624efb7a254 36debad83da7fb62758a8c853dc78782511dae3b27233875fac8fdc55fa648911f727d8cbbcf2f02 3796ba5f95a3fdda54e2f62bb855db1ec930edd6b142b55bcb3cf98b233b85d8d5643ec343e37242 26a1e7161c73de79d8a045edcd922d2e742b657f4a942a2335555537f2bce8dfa555fb5990ca7bf4 3b04440d614861efebbc60b45d45402a07b3d3efc5c30e86a43dfef226963cd1e6f7dca3123cb8f1 ee9d6fbf110c697b71c0b7d3cdea800deee703cb9b1388cd95f3349b4b01aa351fec885f24ef2318 3dd825f79e34039bdfa3ae144af9211ed5eed64cabee7bbb19b1e8de0e8caba6dacf912c16333309 ca742e225c1f6504c33b563b71258377b05ea5c55fa9b6c8135ea7cb3d14d7e65c2872db6ffdf0dd 85d7f6a9e2a6cdbed80b1bb2769aed40ef5a6bd163d99654bff499c8b6b68c5aa58a8c9a3d528c72 df510c644c885fec2adea13d5fddbacda9f33861e3ca91ccdbcf5be66eca6e6aaf5b27e87bb35153 ddcc582ebca68ea8393d574026b77d07db569ebc43ca45eeb11cd539a6b126dbefcb936bb74920b5 3379b4c786cbce90cd9755afb5b82c562d69f83e31d1137e31ead281e85df941d1355de951c77af5 ab0b50e6ea95a74cbbdba44cb54b5068856b526886c213ac074ba935d3a92ee983738039a75425df 0ff2e7bb518d4efbee7df358c8c5c27224c28e30ece0a994cd7d1abc2507d0c9b9dd9eded3ec0cae 425fcf6dde6ca2ad25d3a35ba5a9c5316b6ea630ddeacda477666644f75034a08e53694359dce246 9e17993c3990749cb8ad1f0a31d2821941e58d3441beca18419eaa4d825c15f05fac58a2d6fa3e3d 51af4b4f84b9d21836efcfbc554ccdee7a7f16ec5576112d441d9a8cf95baa30e080e859ac90198f 5b329dda305db57ea57737314df766f30a752a6c50ca5abd28f282173872706b2a6473249bc4e835 f8aaea04b53a2f9a5ea5746cb6765d8007d4b986736f5dc0e61ce46173a2f0c6e6b9711d9b3de618 363b788d5f2c24b4d49e8eb6726bacb561d4f6a259ced01ed65d235ee38392079f2ff05cb3bedd1a c7dc2f83d672c79a74ef12f7c88b55f44922c56f88b136be365f8f6daae94dd2a526db42ea78b061 099cd78c369e83031913dd8b85155bd5494309b85503d27637543bd68b286cad19c4b84a03c4d829 df2dbf88314e5710c38011c4108bf55fcc6abb1b9744c93876fe81f673bb515e8fcfdc5df5afca41 52efe692bf2d071e2bf8c18036b0b4418ca7a482f311d03029d39e34a29e1b35a053f4951fd0ad9f 7ea2faa73ce4b0eb5410b3e760085a8f98baddbf8a75bc4d58b55b66e8d748e1b485dd987ac38c7d 4161d09cea55ffe0ecabef67bb507d1f6df80f967a3541d03cdd8449f1d2face6c066ee02366a52f e4b551937b2af9277b142c9f5972a9a1ec335aaf3f24894564e01d24a3a0fa84e26b37f125d52820 0ce1093708e056f5b8a94e8d675ce518fc5dcd7e267e15811bc09542764740cb43edbb69092aa392 595e9fb7d37295274ea57d2e2e963e3332ae788206f352cfeea54a3d755d2eedfcdd07f6b294c04f 87677964155f5c7f669a68cf72a8423745375f5235808efc7884476c51a2a694cd268119cd4cd334 d09dbe546aa3f185abcc0b3055ae1e22a684948bdd625fc3ec22066b7ee1f2584585a1fb3ae51fa9 f2333fde68c53cadadd09cf72c73d930d5ea6573fc6c9a118fe42553ec676ae9088af58c382d2459 aaf3b397c9e64ac13f30291d9f5a927b626eb84e436f3ea99252ac94df02e6e6e27607cbaf69735f 0808b044468daecf98b5fb5056aab94cc0970ef081ca3fc6a54646923924a52f5adf861d1c4b5d01 98eb590fa0dd9b03508d990374189d00eaa0a90f7c18a0238405e8ecd407e8dc8a00ba686500ba24 db005d899fffd9cf3e3e60b300fdf42e09c60854b26c5e19f286b1579b4934cbb05c160fcb07e0a6 99c399d92d6e5be2c5de6758f9fe18d787a7bc55cdadeb6ae97065f9fca46ad3e9f5a487808678aa 8086d9c03f9874406392e97dd01e81c6325a80c609397de0a640e30d7f27b79f63d0065889b30156 ceac3f58670006f92d80e18107b0eae80530b85cf98042120c1f8b5bdf8ab681a4932a49c86b2655 e98c827796553ecd344570c7035e5cee9708de3e78d5ce61fddd2a5fb2948d9a4f350f5cbaba5c37 3f3f886b004c1b7cfe349c10000b9e1cc0b6520f60d7ed18e0196401703c8801cec169800f56c8e7 fec4753e780e01bef5b700bfe97980df6916e08fa6079a79fc9b06f739de10d02c54880403ae9c71 8ce372ac74b3144e8b7dae8c70fcb2584aa2019a79757242060cbfa8166aa551a911958d1c4fe4c4 14229fbfd2f69106385b6903bc4ff2005f4902c05fb6947c4c61d1054d1a1e82a6112c4073518f01 019f7280d07b182076b80288dbd50364761c0312162140924d01904265fac1fb0948316a005282d8 047d05ceb8fa38f0f45f31fabb527844ec42736656eec860e1adab8550f58b973767678b9f960ae0 589efd53dba7ac1fef71f38efcf5f77e3db4c479effe35f9366680b4962740aeb82ca08aa0012831 4c7e1b6a6178803a366240bd5210a0eb6701d0883b0134a35c002d43e50f944602031b097fc5e86a 8f6aedb46235f93ce4e2948fd522b69d1789b8e7642a0cdcfda3920b876e72d292dabafde11fb937 d1a17fe31c7e732666997d22f7021a37ef801e3030a02f690e308dcdf06f784323f7c1ed1b36e19a 80218845122c9c587bc9622ac15ff934f1757ee5c87fcba7d4f283d9f9f16857333ff2e99fecdbc4 d5db961f6e56efdddfa26addfda239bcb391e326b108894bb5122d6efc2efe26233c4e5731465ed7 a2ad162eca2d442fd09890ceda7b3838c3fe6bfe23e922b3fdf27fc35f8537d14e9dfadfa4014cf8 f1757e5386ff1dd3cb95ee6f81abddd9958cdf42a84bdf727ab77d158f5de15ab42ce5123546fa05 1a0eccf396980ecfda633d890fcc711623d3e7e614f3e8e5842de4f4d129adeb894b55c33b07fa60 9a3faaee7f4211fe958cf0a9cd47ffcaa78926f92b9f2641c8afc9fddd292cefec923add725ae37e 2d9a58eaa29cd9ef9ec2b376272af18126ea31e2638d939dc1881336a399a35320da4762c58b0717 e2d5fda71530f6ecd17076fca53fdd8a0f6fbd81a6f3cb1a5e148b9159529aabb83b175676d51356 d8bedff931cd7e91c8769fe3df2a13f93441224b7f06839bc2a41b1f5db56bb1f7185e203b17c407 f2bd3ac5adf7fee8e4e2f3e1215d1ffb7777fbdea78d597ac7c7d3dc56bc59858df292cb6b3d4d55 1249b543d52364c9355776596e2daf9a252e1ee6c458d0e79b37f789f6619e76d799592795af850b 3edf08a5ec130d8bf323f28bbfd5fd4798fcd14e17d855dc3eb1648d6038a14e3618748ec47ca0ed df6aafbfe34fadd14679bcdc353cdd4f23643e9fadb068bc5c3a95f16649ec82c3c2ad7bf1fcddf7 6e73f6eabd6621196666b949540aa5d41509a2ef466d68c9ca53bd3cffb651fe416fec7ce438787b b1b529787623c87bd86598fbc57f5599a094befb99673e46c6e3f4e121f0a55d58cbc1eb2d6d2211 32eb10cbabc2b00bb7860a73bf51f9ba86e6e961ad37e31fb57eb8606a4e589c56dc20e2f02080e6 f862aa17f1f5148e3a47dfaca8372fee8dd21e765a431307cf13ee6324c92efd4e4fc76f5f398cdb 99f1639c9e19f7512888b75ffce8d0ff55eaf87d7d5f20d33aeedf32f2194b237dbfb2f3a5f3c2ad 9e9f7336bee466b9d11e0a8bde1e09a070f399aee5f78c7f90f79c8f6cf6a2173b29cdc3f3056b72 3d364713c7e2be6b04ee33d55bba8c36f9dc6689e763dc9ed4f3a359d96a8cf2da5374e47c381dae 3d2c1e5621f135d85df9d7a0e710cf5ffc96f839919f46aaff1acef7ecda1cadb5a7602c9de2eb9b 3232f7ebfb69b8e8cca3a0f2b9a4fc230965bcb3454093db466c4ca8e1bce57af045745bcf82360e e4aa35e6cb4d679cbdc8fe48e4adc5a8082f778e621e2fc36daa901a6a9b1e3c38c03b66804c18c3 b6a1f47735aaef90e2cb7a2ced9ce5ca6ad662caedcc2ffe946ad6d2b1b9ee1ef7dcf6b48f1a3bdc 5ba4e8576fd659e5a4a09b87077e3f358e26f7c3e9e6be1b706e1cc258759c5b68cd91840e5b89dc 3d0d04a7525da9c3edfd6e0e6b52d6191cd186ffb95b710bfbcc75b7f6a0babcf66fc63ddda740a3 6a7dc503ab05570c93633ed3ded986781b05582df6968776a1274bb5dc2f1e0fa9f4dde97559a7d1 fbe19ddd3dd7684edccfc3104c039dda587e4cbc656fb8c90d5cdfe497e3cec0bb8c4ae338e354ce b9cab05e45b08135663ff3f6a2d2b107f158eddfdb73b33fceec87d6ab7799581e5d9e5b29bffe5d c83703b57b31b3bb313044ea0219c5a240f714f5d8d3377b7da5c34ceead1953a6ac211c5aea9eee 99c22f925588b84f76d3bb123e782d5b4df810eccecbc0bb5de78309583f3437632cbe0bf9a37236 371bee11311ef42b36b0874254ee8fbd2b6abd2b39ca62473067a5055e31f915af9bb97edf36a4b4 e71ac57013f6d4da7ddd834ed558d7d94f87574b0765cdec15c86e7cb2b42edec697aa335b24298a 245d8194c73b5356dcfeb1f88b7bfb73168f8fc920bf6eb0cdd7bc707a1ffdcb7d399fa49ef3d168 651c7a8e9691b4c169154dede1ae78e8d327fa65b521b3687648a7662c961161941af1d76ad55ba7 7362afa2c39abeab11965e9bf023edd832a61abaf156ddb3763a76f167e1a9de264a49a5ca7b4299 e84d5569d56ea13ceddb4f3973bd41d24c5f977f20e447c504d7f2b8563cb43f5fe837549bae95df 211c1e4e93b76fcdc7c5cd703c3ca89139e8bf50b5ff70f489d596a3add909730fa334f98ca06e43 82f55d60e37a3df7b9b02d33e4b5067355bb83e9b5d76d6ad050bdc384a752aeb8505e8cf74db854 5aebd34349694841e61e435c9ab752b224a6a65371d5693f44f9b986c4b23585844d63501636e55e 29c1d988516827bb426999b6a0d4d49ef827b73393178e7ed0dc01def3adbebbcd77ad8c5cfa0666 184bcd8c7a5d33baeabde92dad598f12d4bd4838da6d9e3b947a1f989c4a671d4979cd17bae2198f 81928adfae1c4cb1b99ccbb25b69be0fee5211b9e6446580a122c4ed25415b089e00b74ab78e1158 df45af8e311c543a082c947f11db36036f0cae5e9a2f2e50c6272f4e3c2ebdb865a243f79f2ba76f 650e4fcd90bb474bdfa3b5b976eaea71f7329f01f51eef8a2acd81bae2ed4b4d851d279b2fe4e0dd 16643e32bbd2a2629b92b85b8ca4e2e81d8acabbb816a148b90a7ac5cf740ebd77bd63d243e1f314 aebabcdd3c5f79ecc095b8ab295438a7d428730ea896129cc81d5e8bae74aa34ebc2b9afc9c24be5 d47854555a2bfbceb19e35bdaa7d439ecebafa5eb4740dab9c832e21d6f62aed4b4f85dd593939a4 665539b73a62d24205b454bc9578311aa1b2a80a92216c57fa50d0fb8b69e7703dac3ae6088a13e5 b6a0a6797b75a871b70acd73ceee3e6a3f8b83b8edaeabc536c355a136d32c947f716cf5dae8caed dfcb619dbe662759533a0f4f4e7dd5f70cc4330b47b2dfabde279a86b51b8afa10a6df8dff8acf9e 377238aadca4c5aa99964a985c16a3eb08112b9d2529ecf24756a8594fb173c44a5ac74ab707fcb9 234df841de5d70372b3e72230c07eda76357dbccf3cdb681371eb0d3763766dbb75781cd18a944e8 ca08f7722b7cad8a090e590f4597eded090a1afe363b5e0dd0787079165656f086fddeb68cda5a3f 12baaa4b6515a58d3506729e1cada492bc8e45f59a05c2aed3280ab52357eb580d01eb3452232639 69c1b2c337736b85bb1d81c98d2c74dc7ea53ab3f624f0f7ed54eefa6439192fb3d972c4b4049ded b70af5fa9159ae821cb3f44e102363418991a17131c14e094b8d8540ee2bfe7de76747db6ced9bda 6bbba5fbca94d20f4f3789bcad3e668daed2962f8ab4842fa6d82d3742a1c7f70e9de361f6e83498 7b8e1f042988bbc355941b1d2892a34cb9ddf68025b55bd3a9de4e71e721cb1df253366bb6372de1 9c84ce312b1e1419256752f4462e9ab496baece9aa2366e8aa3e2e53fbbc52a4764fbe906063323b 745eb65655cf3ba919c72c3ce3be3fb84446a5b6f5345cbcd94adb7aa9929c1bcbc26ead753bd67c f3ddefc95fd0f29abb73cd3347ed35d0f6e871a1dd7a6fe0766a9dc259be9aa5d9ecbeceb744baa5 b60aef9ec5aca29dc74055b0a235033fd33032cd51c6a08493a7bbaf917db7bf21fb329422b11451 4cf0355c362e48214134ca8ed0707f59d5269d349319e2d82eb63af355a41f5bbea73efde3405a6a 1b55d80345ee34e690c88ddbfcb0ed8dbc793b5db81c58de2e3f5af31b926e8933a5cc4405adc628 2befab72d05b6cd5a2b5eb5da4e15159a7cc42cb2163a5179236743c10d75e1d104e7d89361f36a3 34dd26b26ad2fbd5bb498fdf85266d5cf3bf58a58abd46106b1b641c3d2b19fbd999c666291e7fed 3cda60e8f8ca74371b08bdb3a6f0433a27b6dfe94587e52761b725a5618f516bf48ade5aca91d65b f3277508b619cad4d365323e16eba46d614d126f696dc209863241f27babe91e0b5e13345a6bdcbf acbf4f4f3c4336aa58f83c0b9fd63c9c61f90afbc0f2193f8fe56e4eee178bc28dc3a6a419a0a3da fa9cee4f27a373ef332b5e75a985eacb9f0b6bd0c19a5ab7fd661f524b2a331cbd5d643ec370ddf9 ee2c26cf4ddf27f1f9754d8c4ae953f339801fcd49b39d698271a7884f45b78e73c52d81cdd46b1b 139a58b7b11c2bc3864c6fe6e87a5a8ed16ad52b22bb45b385f4e49a87f45ad72bd283f02cd24bd7 3209be92eaf738d3eb02e1715511190ee6b3b45518ca67ede2212b259892beb84d7787dcc31035b6 434612bdcb5538e236ead2cd4ffbcae39cbeb4b0d90b7858812d2d1a2ba873689475f58a6ec8590a ed3ecfdf9186ec5b4f1831d2b566fda44b7cbd5f5bf46a17ebedd686acb685efe12305d30503afbe fc561fa6cecc09a6d6cb344c4da729981a8c40826000b528b76c17ebb6a7f6d2068c56e3eef80a45 b2784fb62e0ac8931ab6fd58ec31dd942d9183cf44b399629b74a3bc3630c4303caedef74f7a1de3 ca4e6db8a7821a617423f8412f6398f6778fea9bcb66abecbc0a55d346af59e1e3195fc90d4a2624 65cd102a2e725f9f4d592df9d532b431d432e47ce668ca2ef32a4799fcabbcba816702dfabb55ace e919d5129fb96e37b36735fd4cafa50a8d86fc285577d8fc8d3628eb20c9cdd66bf079009817aade 7fc38daa6f995fd9ae92871d0a92ccad585ea7d246590d10a75cd1b4b0a41f86eb52cddcc445f37c 7f15d1802e15cefc182be08b7b273f6a70769ebcdca2dc84b4d2d9298b3239265682dc93a95ef2a4 d9b8fe224989bf34da03975761637b2da4bb4fea7e9197adf456686440d8e68cbb43ef959c41d05c 4dc68a8ec0d5e3799f84011937a0d2a2522d36ea4221f7229b703648514436ab75b98c7018a99982 b9eba7578dfcf7b1962e0fd1656ac33be75475b14b81bd8cd7407de3b0a0bec5faa0be4b6d3fb894 407dfeec82fac20f41dd7def7e313a164f6cbf7346e11efa7865d4a977bf89fbec76cbbd26bb590b c2f7df150fd2811f16b6583d15040d0a1c0c7618099579bb51b8f466952ccfacb2292d82d3001941 4580ccc6f807f73640e64d1520fbc90020874cf881b407c8e30900f2546180bc72dc07c1e75fdfc2 f6ab40a1802a0134854b004d230b805cbdcb07fb678221250c59b3bbafd4b5bb552ec82be7f4e80c a370cf2ef8d982b2bdb18bcfb8b08fd6c95d176ec57b1e82a01c5db836d14636b7ac9653355acb25 1fe3f33980466308a0e76cf3038e030db0d44023951f82cf8536038dc6e3081aac9e068d761af960 d8018d2e35020d0d1c3ed89541435f48a0d15bad40c3e09e1fcc0b09ec5c5afc66db19884862ddb4 962d89666bf5e2da23ffc4d4c12422529df114ddfbe6007e139606a9754f28dc4a11935976a628b0 8c3d041a6e0a06c9ebee1a8f0602b09c80030c5eb60146d63480756c0760a3dc227110cfb413c0ee c52cc0b30106f05c530238fc9c00bce6c700a7cc2ac0695101b8c42d012e7fc61f2e4f6a09ac281c 8b3ad9469bf21ab9431d8ad8a6d9b217ddc8f17cb2c33e17e3acf6acebe324fea32b1885d1041332 65fd497c2aa250807ddfcb896dd614c05337e68faefa2bfc7ab60af0b83802cd627f019a4c3606cd c52a0f889a450242207b80e8bde78018af5e80588cf1c473bb940d409c980d20e24ce683b099e047 8ce6466d463c0daa089793de45dad60f6f5cdac987faa80bcda00d580e0ad4b3afa6b511defa5358 628a4e8cc789d65b0f9544524d34df3d64fcf1d27ec5d59ff084547d01c8d6f10648af5702e41ba5 0125a54d40999f6f9a9af453805a7104a0e2dae7efdeef35a0810b009d826b09f4fb25d015e56577 be1d018314b01f313a1fdfaef53b6a44d0c62ab9f9776dd34b7dae66fe9fa6682a52ffd49494f39b 33f1eb39fe57b0f0ac94e472d0995d0ed05d8700f445d4018335171f3c5f80c1670dc03425153044 71fa8177faa7ccfb0ff9345127ffc8a77f5f71f61bd00a7e5e1dd616ca7fe26fbfae5e375be2ef6f b124ddfd62ad7b6723dcb88515dcfe9b8c20f9d745dd9c5fc593bfb916fbf1f9a2dcf2e0bca518e8 0c7b3e151fda582f4666c37f64df2659aa9fe33f8270bf41097f0b4c7c9d7f2c927fe5d37fc5f41e ef0f7a96cadcd955aa740ba142edbaa8a51ad7a295222e51a3c05ca061123d79de1255f1ac3d1a6a 7c60ea7afcf936fba7986b8d4e76d60a8f5771b63d3cd4f5e540efcbb9bd8f4cc87d7a00941d7fc7 07bbdcb86227c908dba2b73337113b3312fc97765a4affe8696ae5af822eaa0f3af0867776b10a6e fc7abfbe8afbd3e902d9abdb191ecddfb1f18a3ebf7fda2f1caf1d1f3a3af945edf09043f4406f7c 7cefc33eb54f9b3ebbe3cf7e672bde5d6503f9cef7edd86b78e68f23647558aeec0aff5a5e7b5175 49c445fa1f2908f4f5cafce2ff904f23fc9e169dd655dce9e205ea9b467c2075e764a7a4af6c7724 16cc7cffeed6a27dda28edb60bec7cd828cfe969ada7ac7374e07bb70859f45e2bbba4a596d7ae96 5b127badb470917e6dfeb60362cede4efc2ca4ab46b8680b7e589cddce4124ca85008a2c78ba55e5 af773039fef866bff87fe5d34ae696537a99f3b6d1aa9c6c90427e5cb2693d686fc58b21aee16951 5ec5c2435b5e95abb578e8dbc1823e6ec773bf1179f3f4e7b1f14d1f0d89c3729673b7eb5002db7d 10718773a066dfcfe956aae4a6f09a817db36a535e6c5c550f3b6bc1c469ee8f13e28100d765727f 40bfefef04ff9b769ae8d083d6f1f0e86caebb9c493fa303ebbf975759ce2e5c5880e63e2a2033fe 4e3543f1dda083a8dde40268d614a77aa1a14ce155abe79b1065f9c856703cbb264c3cecd80b274e 6310b98fe1f4e8d28fe3770e3df65be5e2381d749ba3b053d247b9a53975a4727c708a9bf8348cb4 e8f88b7f957a814c7977a01787c97a4b2e47cbabd49ace7d24fb393bcefd73625a97eb54cf6dbe2b 85be59da66bdb81b973d6c7fad4d1ce4da701f76dc745dfccdb8f4bdc88ffd4f973066df88360adb 9835ea64c4b1b3108cd029ae82ed5085eef7c1b64797067afdceda075beedb68e53cefc7f7ccba6f cf4122a9e2d46e95e0b7cacf892caf932aa3432bab2c1e2ac2cff8cb410954e09b53781eba9e5d71 6613a7ee6e5c17f3e3b1af47cf71fa79ce8cc255be34ca6315d859a619cc29e99daf716cb8266d6e d8cd2fe4c1ce8a8d410fcb39f6312442bb513376fdf32b0dfa4d45ab5be34c43353d6a393553317e 3202bb7632783c7f34b2d7e7214152e2b5a82d8f471252e6eb5a781ccca738ffcdb60bd6399498ea f804f33e3d5a6b427676ba0bfa95c968e6302b67550b2f4e797903438dad1407fb2d0e0f109269d8 715ea76c4cfe34f0d7862ff68960d1b51edadbb6e863ce35df137e69b6cb9fe1196a8fefcdc6c83d 45b427b5eeb21e6d27a15ea14a674d9f3cffa0d63ac6099252e3c39adbefc47cb05b78979d1b682b 45f3ae25939fd08519e1b61c811acdad63cf813a256f082fa58d1d077aa20be0c412f49dc5316f3d d167c562823c6a010d27cdf68361cd8c2b09865032bb46febce8f7e4e6c1ed9573f052ef9a4aaced ce8bb456e711b46b2d0249bd34e4506d0e8f67e54e9c6fcae830bb24f86a36dfe3e73acf9f8fe3ec e81ac50b7a372b4953cfbb7578c36dcf5571243953c6511b0d725883479a3d0047b74fe5f22babc5 90b1395dab2f33cb4d7286b05f42bd15b2aef7e4f7b3a96fba1946d7aab5ef435adb8f71453344dd ea9e2267dceddbd7857ab943479510f594328ee2ba42773551f61ecd504e33d3b31482f543e207a3 db2f2e6a58baed4364fa5c4e38683f3d657bfe24d5a2ad51b94d7e178b87f5d680b5cf8714d11f0d 1ab2d59a0f1c331bed16c667e676e84148e9ae6b7e33ad1d2a5c51336e16dc8d8509d6b50b4baa8b 9d9ebceab4f38a4a665043717b9d91c2d4dd99ec7bd7839cc9206fa92338dfc00c298fd0a228d9af a910edb993a01ad64350c5ee5da814b95b82f8447260abcee4e77c5e7f1f7ca2284cc7a246d84363 c4a83641f438eb15dd293318bd3a46b1ddb47bd0c80a75f8b4dc692874fef6375d7b9405eaad502f a864bf59519e98802a93f480544067c4cad3da4e9233d65997666ccd91f22113884b3ddc8ba5e3fb 2574b14eb5b37362aed3a3e63e7ff4eb27dec25b8f1fa0bbea2dc1772e303cf6b2eb06567f869b6d b89ff899c6d4d94a45dbbe7a9f01e433026fe6461bba17291eabebbd82a5a14ad3efe26177ad92c7 41ac7c664d4f056c4e5979ea142039f32cd4a4d9926a4a822c30e2f2d2fb0e01511e4ebbc22677b4 85eeb232edec1bd2b6531f869f8a9e25886f4c3e1ddb30ad4cb8e62c7b6adfa9c6b33d3a65efbf38 328d5e7e35260bafc0ccbb07b7435583a1394d0ffaeeb5a2999d41ebfb313dd50b18ed58a3a9eea0 31d7d5919e9d28936573254f2fdc51cef2d643120e5e5a2ab8a7d267c67e81c5f2a680091a5ca684 eaa1c975f66e4fed206064f1fdeed1e331b8b6e686a67a6b3fc0addc1e07e65720665f4f64cc7a93 cd8165b1ec9365cbafcf046973ba27386434b9b0f48e1098e20dfd302e29a96080efe281c5e58166 94e6f58e5ebb8e5bdd663947282f585195943477e4ecf435970a87c24e5498e67708089bb508046d 302e74f6f731d431c62b848f8b2782efaf532c8f0d9a3237bc7346fb415be3b65bbcadd87717bdb0 ec7e5460d326ceb4f8f37bd4ca69c68e59e4368f1fcc1ff37b82ddf2207f9722169d722ee53d0972 3faa5c5ea14d5187a159606ebabe7b9785ee60c933ca8b7b93325fca752491536c51190781a0459b 75e780bfe20e32ce3ef85824323c16f325ee6a1b35ce69cf9bedc76cc5b45de1fef50bb0efb8acb3 be2d8cd8f46d346f75a8f3a9952f76f38cd405141dcdfd01ad32d8865651ef4143e7fe9d86d6da2d c166cf150a732928663cb0c00f4e9dbfcffa29251cf636dc4cd3fad04b50a90bc6245e757a4b8a51 66c10a5b1a363b07959df0f1cc58f27861bce7aea7d38d23dbe954fb99810b6da6877d9aa93a8fb2 edbe42b21936e05b9d70a3b6f24261c0482b2aa0d7d8f448abb77a96da51d32655f3a5af6b883c36 d36bf258d51ee4312bdec9c39dbd255863c2ab38abec1f1977762e1d0617691e5ad9abefe8fbfd54 ef129d8b2807f3372d46b84b0af0a94d7650eca0f1b8907638b20f87ed499addb4816e7c1f04ecb4 e63dd98c15a65bb3d6b5d412d2b91ab3d4912623d778965e5b9a445753738bda85599faae7b91d69 c92b407c7a800631e8950ca25958ac08fc8ddd093c866f041e15af09562ebb290696ed7e1724c72b 2e7f4cace6c6eaa90eb5fedbd295b7b512a5a2bfa5855aa143f0f6368f73e49211daad94db673938 f25a33f7b16c092d68cf2c37c4959135f14daf1f668eeeba3e44edc102a17aeb37459e60e87b7992 d641ec111726708966905a37c739e1d9a4965904f7ca53156f85d2026f398f1bded2e32bdeea6c2f 0996ed70539e3677767aa409c743dfcff667bdad2c395db222e872188c45a14699148f87a5af72db 9e486b8ce5b81dd32a30f92e23af9921bd699a015d9d876bca282e0e543d7edf3ff7d50c201bf75a 9eb8cc2598208a26de1cab6eab49e36f15f7c6d8104fbd8d39164cb3372c97ed571aa2d4101b62ab f48dd0fbfcc9bd34c4b2f341ca3c27982f9701e4791e931ac6ecf460751071a6a3446da802aaa94b 255711f9012bd1ed96746eb684b980d21bb5dea0f6b381409e4eb31e89b1b71131dc95c2cf5382fc ced39ae397746ad29e7dc7fdf42485b3e1ae8885b5670de34f28d958609d4ea31886261a09508042 2be384e8955c1181771bae7e10477efd80968e082ca1f12f66d58b5775176406fcb5c41beb0c1c76 6f66c99167fd7a4fd89d7989a39c36c366a73b82d6d43a4a629607371fdabc89bf9705096f372813 0b87c208cbe7aca0b158cca246a9b4dca3d105dc5075584f21db07554474d742eac7d2fa7b79d651 2da3d4ec43dba9e166bc819d8b94864907a1abcf7a6a0493c26c0f93d4f20893f5e9214160c3413d 79c77b7f5a6eee7bb5e73b5427fed391e430dbebf4b398d44e1dc916a38c2704190ba9efbeb52643 287063998921547d4324b2537901a9edb4cf3c93f20675f4b5f76ae7a8b0ac352bd51d7cdbb52e30 5597dfd5e76b53aeb6d857a3329d35da15ae3733a1595c994105dcbf979777b5515e1e14bbbc9ca4 bee23d943fd677507e59de26f099015f73d0fb0498cb4376af0d2edb50c98ed68e587dbc0cee5184 e49678a9b194d99689e6b3bc41b1fc14aa2275a85380ef8d7ca9eac138564db92a5b09185fae64a7 8befabc3a079f7ed408543715a5e9974548680762a6da6ab7709cebe4b4543c2b02282cec5427f58 75f297c76c9f1f4e2c283fecdb5afe12d1b342e3dd5f171ab116257097511ab65f6de66d54e3fb41 7d3dc2ef809656aff5b813332fabdd4e3f647afbc8b789519d22b199db47917d3daec2f749a5f019 5d42aa044feee922b2ad560b76ad8915b0a7c5e69dd644ca93a95d2fe7f24f27eb5bf9209bbee09b 4c8750af99dce3f84d4b484bad5a2315058e98fa3c6a5db05d9e2e60bb7a1129953e5ba9c8ec04a9 4862a60946f5a353b1e62bf0d2b1d1fca0844cb810f54f41dc33fb99834ac7ad429ea31b8767f93b 81563fffc18f9afef54055f8fbb150aa1552204f9acd5b26bf9adf5395a39902bd565802f5d41501 f534467fd0173fb898a09e69f91f2cb7a05e65de1f9c6ba00eeb3ca8d79011a8d75331a8172e7550 2f9e0d5007edaf78ff39dacb040317662a864e2c5f5dd71b1f65691aae3a83fc60c2cefc619f3a11 8eda64eb331edd96f6548d4c3fd04a47432ba5da4ccee729380069d91b9f41fd887d3ee668bc40fd f92e83fa8bf80e688014c6cc0777092008d7ff603905088aee01c2ac53006931f50f1e0240d8f118 206df90c108e4300c2772c8010d7394048faf8011ff7f3ed27a49fcd4d4ae5a0e02cf60c3be25a9b ee577e60b4cf7c83a0a79d6e43ed743bb51b3da42b42236c94eaeaad9af388572e0d218517401cef 0e90ebe905d04c3a05d00a5f0628bec101da2cb000e56405a0dde300a0436606d0607b0268f86989 d0306e2402f1ae2f0374dff4017ac8dc00fa7836007a3c98003d791b80c6d5e707f59759159eb036 069fc9e5aa33bf7588b6b9658bae1c92b7b438c29634d3ab0f024aaccc299a2921a884e57c6c04a7 745149620c5015fb7c666c14400399974183de40a061dcaba0e136f10f461c682c9e5dd0784a0ec0 f2f1026094780698982e7c302400663635804d32f30fa217c0567e136051bf0fb073ef08b04d2191 bbb14db5602034dae8a6b95751341bc1330913a12d4e5e36b3323d414e3bb25f0dd1a65a3a6c092e 372dd69aa97af655f953ce5787c60819fb633c0e66e41f936fa2fa4219368998605515e0fdf708e0 91b1024d90bd8226b32a83a62cb740b35fe983e6eab101cd38ca01a214b080688c5c40b09d1810ed 5ae503b2f64f311a5ea45a1bd6bf92b431da357482ff6a3635c0e24eb94767b5c4140d4e43b9f1a7 b66f593f152592ea5fdbf11f5d35117c93b0891fc1d71b0362d3880059bebd00d99f7edf23678a80 2a6149ce2dd5f85c4e546305018a1d098052551f5003e4f2c1b6f4410c75b91e49ff88d13fcee85b 26859746c2a136e96da7e5de5eb01253f48f51fb1f85fdd57a131d3a917b939c89447dfef5f7fe27 bff7e7756d3f722f730374b506037af0ee0026757201034d62c0e0dd0a609ab890c4f40286e8ef01 4342e90f90ec3ff2051225321128bf6aeacfabc3928cd62464a0b7fbe0fcf81bde60c10f3733c3ef 6f6149dffdc291bba7959d700ba1b392c422dc72bd7cff6f2c42e5e77d6b1839bb404e7b7dd69e7a 7c8627e74c6ca671e41477a26ff62d313a612b63f6abeb2638d08778923867ff5687e6fe6e87ff0a be3f61137f42067e222692b79ccdce773f3f79dec2b297b92ee061e15a34c7958b721ed7cfdbe618 3b6bf7808c0fb4c7c4c67bce9de2f65c3cd9997df77815f6c691585d8607b792f2f77ebdbed9a7fb bdc72ee7dce1adf8e2be02719277fb9373fb4d4658c3cbba9a208947f83fe4d324bc8196ee7e8e35 6f61491c5f17556d765162717d86479f5f14f188cb090be1fbd1c9a320096528e7d3fbb796c9edd9 0328ee42e4096d17f811de44d412596fd909161d3a232a4296337e65976ffad2a995c68b87656e17 f4b5949dfba48afeffc210fee60bfc533ebdbfb976ed96536bcd0b643d5bb1f1d809276cbad68ec4 7c67effdca68b4cbf5b9c9467916fc353c3d0511325fcd577671be5a5ed5e06b95f5f6895576419f e6b7b98fadde894bd5d96667fcf30a85523a8d055187e60368e959531d6aaea7f076f9f2cd7aaae4 23c74b31c18f69f68b7f69927776a6df2f11fc7c9c627a9839b805a2b8e30fd74a92414cebf5c8cc 74f015b66298c543af7173bf0184797a0894596e7cd6c205fd34c3a27fb30335737602680ebca95e 780653382aad7cb352d979710fbf78d8494e4d1c6c5a711fa30cedd22fbd377efbc92ebc713ba35d 12a3ecbfdcb2ff964f9583b53f3a697ebe5d20ee2a4282db7a49aca787b98f58d7197f334058f4c4 5c00857c79aae7c59a6f96d986176bf4b723f06c58642657936f4f1c541126c4b9afba6eb3df73e9 bb678f7d7aee8ed3fe7e36ea645edb516ede7a3852d12b0da36e961c42fba935d89aa570009ff18d 7d1854d6bff82d31c9e5f86a9357960f3790d3d59744a429f3749fee87c549713cd5b38fc047a278 e561bbeb61e2d4df57d7c51eaff1db79a6c7ecf3991fa7bd3c34eaa433f02817561acea283124e71 8932c3486e77866ad9fc6e57186c35b73f800fd1c4361be9a81f0fe96b1f7b2c4a96d362db1699ba d8a6db29f83f60722f2f415265a2f69d304f9badb724a72de86dcc84e20360533db3a6bd58598a13 a7b6fcce3a5dfa321b8ed9c7d21f75c072e12cb8dd761889b3c3502d1eaf83adba7b0cf4ca3d651f 7a97ac6d22a9928dc4d55adfc6eb581fcfc82dcbe98d24f3715a1a26d3ce7b86bfd3f746dbc8647a b3e2a1d5cb0fd8efc28a2e678713bdb4ef7f30d7dc04bf62f42eac32c385ab225ca08ae7868fbc52 e589031b45973e8f1ba390366867d11e8b43959a1b03ddbb8fed235e9dd9e88cdaf407947cea370b a3efae6f6bd40f8045b1a7bcf9aa642b666b56478d801268837b0d3abdb9b2d47a855b66a42b427b a94385ed4dd3ec4eb57bc842b21a237d57edc7abb98ad9deec173fdaa9c36af3a8915f7e9d90a1fc 22646fd8ccd36ecaa922238143f2ceb270c80fbb19081bd4eb4aa77fe99ffad67d97f5cc77a1ba30 d93eb533c2b47036f8ede0d95b905eba27e5f7453db22e555dc5b20d6d1b56294dd7854ef770b4be 9bcbbb686beda8f6b6b25471ddb929ceab52919fec4192999d3891412f5849d3fa7029b54fda426a afc479b200b1e7746fb36087b3b11feb90eab68b19d689ac3a3e388862d58ed3db7cfff2ca61960b 895fcdc66cd7c6a6d191376e4f0aee737d5d2b6cf5caa41e6b3b997b68b58b92ea1e8371a1dbc879 15f57cd8a36a937951ca3d55e5154ae3347972b086728a392f242ec02f52563b7f7ef2d111851506 b9827cd5be6d945076f8d51fe8f4323607adfd46db68bb59a9d41d4f9e724e1bc9950c3fb08802d1 1fe17cdd62268b92d91e8635233f2972fa3aa0756df730465a5d0ac26e1f5c36ea454b9dd4e6217b 53ee2ef6bd3c15ba44e7e4d75a2ccb6cd3a8cba97b4048bc78e2a41c5aed8ae2801b0a51763a1720 2973ee6c4b937207d6980e6f1ce2318f58f43a011797a15582e3bd39b9463883ed82dabde5badc30 fdbd6e86bde38eefdfe9176906050c31846051e99519a9a2570731db3db9c5ae7a39d343e591e57d 8536fa2bd97b7b7b998d4e5729c4ef2f89bfe732e242844ba254a46b6231369a828a8fd9ce76b657 3aba58fd8e34fe10773fcd04be8b397bdc2ab6afef1bdb76a6fea84df2f0867ddcdfd11fecafab03 6b4c1fcb5636bdf30771d54d4cd103acb1132c56bdd3461182505d339daa86fc0f5fefb9a63cae6e ed1e8b30c938e76ce36c9383c16072ce19ceff33bcddb37bcdb5f6fe73735d55145639a047434343 38560a392752fc6770f84ee47b9fdea7e5416774e05a8638730b587de7b8bdfec55ebdfb0f3b586e 3336e63f8b565aaee016d9e338b35974b4f2c56fb9e50e776d19f71e3536a477f3a80f8c5c41d7b2 0b4d1b396ea495e1db5a333ee7afcaa119e7edea87ddbcdd79cda6efc36ea860c5415459adeaad67 65e1d4e7f652a96ead3c1b9e43170f221a947c15c358cfa8458e6b0de775c7bdddfaf6da86a73676 a2d7d6aea51f2d0a4acba9e336fede02264b26f9f2e575c6ca82f6648c074428865c351d7d70ec36 f40cbb1f6a63a87cd072d629afdacb50518b06d95196d9c94a59dc276bc5ab35963f6c7694fc9c22 db683b80a4c7a0dbe0e78d6612f7beb351b5d569a6561a8d1b1b443383f0d3e102e24ec93bed2c7c d5b4d79346d5da1de3ae4569db91d9dc3c97261b118772e7455ccac2c27c1b7dc4820cf91a23fa50 9c50baf6bc49dad8432d2db7d16baa4374bf35b4b21ce4768aaff5b3f266c24a72457ab565fce42e 647c19acfec11f7d9f90dabb38bfb80e3bbc1d351ad63a74aa35a8ab86fc68c5faea99215c9b9c22 0e82f4701bb7806651033ef89db48dd12a77c5c6c078cc9399d177b71bfd7db99df4e1187ee8501e 03da78ae16b53c1310eaec92082a2c6c0dc59fc315795b32fb3211ced6d28150804437aebc784646 4db135c16629a495d88a98ef52f9dfebf261e55ea3b3e7eefa25f694b4a565b551f704cfadb0a4af faafc78473a79317ee205a07b1ea79b96472a02795bb9d976bf4e785bafe61f89eae8facb136c905 4bcddcc77b754e2717d5396fbfb3b82a3ccae6942087a1f2b6aeb272956e6bd2a1bdf225fa91ef8a 6dc55908b7d5fb2d44e53e2b48f9b0c13fe3fb947f36b34bfee9be163f2c868bdc2789bad22eda89 c3614be382662dec6b6e280c1ced672287db9ce31d77b855176da47c0345d8e83f285a4f2b96b20e 25eb8a96df1ddbeabc9e1da8f0879c29ab44dd2818e69ee4edaefd90492a1dae1cde4758620c4089 ed29260977ca7584e8b46df32f9ef87a3978b5377c70c947a438a353ac72463818734679bdf80bc2 743eb7c0070cd539b6eb36ddfab099b3d4665adb51aeffde889aeb1855deda6f0678f9c6b088a1dc afb036692d3175bec355b52497bf736b4a30acd5e51d3a8864325a8fa4c66b379798c16b2b5e7cf2 22f25bfe25f4243b2748af1ec60fb435cf836dc1e4caa45167a7cdc988b539e5ca2ca21bce78d432 643cb83c62dc4f6dceb8676ff6c3b7b82d953fd0207b01bb4ee75249eacb26d90caff79ceb4322a7 3b68cde0cb37b78ce99fdda3a459ad7e5159937a51ae215d4e3a76f786c4167281d869524de17e15 7a826c9647fc3b5f5bf083f968cf6798dd7762852b772e1f2e272245d69e97696659ea690c125e42 7a4318031a6f1c8e54ed1a9428aaaab814655d63aad6636754ad864f7f9890e718f4bde962db7aef 95a45619679b415cb8baeedc8575ebb0947e3ab41a0ab8662d9725053b7279a9191e20e1419f1141 1e7122afedf5329fe9f743aefc1937d999318b58077b8fd9e21e59323e4dede94d52bed315a30b51 fbe912a568ba28a6f555d921397ed725ba7df5bbcc077f7c5e79bcdfd99af863d3eae18ff166823f bab331215cf7939150d33fd17ed8d936cd243facd2fb4bd337b4bde704eda76eb66f98a0676714a1 22d018919a9b7c5e90cf8d0c67ca3cc4c25c4030fe24f9c67fd0dbc2caa089e6cda30e1c5ca7ea37 3122cf969990ed427749dcbcf18148475e0ffc1911795c7939149668331d83b6781db5c8c10ccd2f d50f320f240dcd8ff21d341f8a096aaecd116a8e9464a873e8777cd3b959e56d7d159d8761b49a36 bd4265ecd9e4ee6c9465b220aa8b0f42cab5571d111eb3739e337b6a86de9ac9933caf9b19e2f658 6084e4dc393c5e67555c0d191b4b1e460533fa8d363af944316aadcedf8107b2c0f307a454e79ea5 b592944a58f211e05acef48a47e7342832ebdab570313dbed8b819f562639bc4c52334f983188eb7 ef36c814373572b04a02306d369df5bcef9bd7e2b66c643c2029f8edf135c38addb182f05090e419 0c02806caf8d070e86eb0bb21834d3436f66855218654878f72af2706d20ebc553a6ec149b9b41bd 70251651a173cc4ef30f96dfe7e571fb9dcbb8a352b6bc2948d08c685420fb287ec3b3a0a20f6521 3bc918d0f4dc696473f4a29dcdc1a356d4d807af666176da5404b13af4ec61b969d7412b28f7f5b9 a9c1f85a925aaf3dc5270d0265566f374f5e1a0b806b4ae9817847ef5c6c29e36d6eb814bfab89b2 66557966f364230739cd08816076cb64fcee4dcea01261836f1f0d086f3600844f6d524cdf800834 121021b053ac8680a88c5f80b0863220642448e1b50141955bed77b6f4acedf9fef7860e86aa95b8 28546e5badd1b0aa4f0a0d53213e035988af4b862dedce28c54a581ed7f621407c697a2fb666ef63 0e1ac2ab0c566a2c0129448714cf1720453e9fa28103d2bc8980b4442bc5f0bb6e0d90767198a2b9 05649dcaa45833806cb82e209bf408907e26fdf3a0a8a698360059ce0ed28f2c24cd59f3f8acdc8a 95ad9f0f8db1434cc2a81c3b7e555d6702fb6731e78a4c85a15a68f45db182ebe55d1ef1ef3828f2 79f9969dbc9d3df825bc51a5640528e6730214ab7f00a5254540f91095429100d59a39806a171b80 4a7a63408d0a8714832ca0c63c07a8cd3300d4643801d4b4057e3af4aca6022a96ba2926b314e775 9d38749ea1caf97baf8407338b6f6a3dddc6f4baccbabcc39ba8a6d1f599cee189666008c687f9c2 bd32fa648bc5f105508de70e502fee0468d2bc005af2af3f1dda9a7d005dcb1701ddab30809eee55 40ef590fd08f411b30796c9a223e0386948a29ce1248cf440d304a79091806cf0246c30cc070d030 457d976276a97640f31d94a1fed9a939d56f2d50ce886cacec8774534872a4cf543ddc20d4699e47 4217230a0f5d2842cb3efe0674b57b07f476f3020c0f67fe127cffb88ffd0260cedd1260e1370358 c63300abae03c036a508b083d5e21731b1d4ef803d7d50c07e863ae080f60d9bc076295e30e0d0b3 0d386c9c004e086f2966a0d2bb6cb2de521ade2c910f36da8a11469224a11d761d142aa45c7f98a8 cf417251acdd2968d5d914ff2f1d7a5e24ffb21dffc226fe117eff244e5cd296dc8c10f0e82d06bc e1af013f42de80bfec2920649a0e10506900041ebe02c1789240282fff786ecdce140855e9916201 8790702d3a0da4fffc2346b3396dcecf103aa639196b62f372c68379f7ac654bc7e6dfe11cffe8d0 3f49f54fdef1bf73267e3af4df3edf7f070c0bc30110bbf61148109e0792ff9280144455208dfc05 903662064837440232746f02199b6f802c5820c5a4e4ade11362f52a07e827468b1fbaf6f5aab355 d39f12234eea959aad5398f5ee98fcd7f9fa2fb9f7d79c7fb5e43f99c2ff8a98f84f7eefcfdf6b5d 76409e8e7e73048ad0968032f61b4099502ba0ec3259a0dc8f0a5073491ba884b906aaf87e035512 0b7f1b647ff902ff59b9ff473efd850cfc15d39b62717d3e8d62e667e9fdaa83ca34a41e49b1c63f f46547be4fd08e7e37b703eb36277beecd398ec2eb8a1dd4aefe75d1be6cc575ff42c4c7f1f9a095 36677aacde4eedfca878bc79aa7e8c9043f39fd8db7fc4ddafa4faf5c8fe34d47fb24fff877cfacb 68d5bdbf7326a8e93386c2fd435fd8d7bbb9b1df37e7a04357ff22162e5b41442e449fc3cff50f4d 9dce06c1fe3611fbba54ffda478c360e31ca39fb4f55a9eca1a6d2dae5bbce700bc7fdcd7a5bbe7e d6c4bcc2ae1aa593bd3c874be74f28c217bf1c84bf770dfb2febe94f9bfeb3759832fa941f90f3f4 6f73ecd9bcae6828be6cf9fcf44c0fb68be3cd4c3687a7dbdeef3f61f5bcd7f7c16d679ed5c7d6b9 93af8dffce7ed6d5cc035a1dcc6b7e7976f7a525b7be928b88c808f3670337669f4eb9facbb97dac 47d3896adda7f9d111fdffcb43f8afa666c17d525c14aefeb1869f0fa2cd9db891a01c9e0e6bed13 0c0fb6ce7556595781fe3dcc8a4ebbec25b7cab717e2f619cd63f216cf3ecd73324bd8e77406756f 8ba9f9bc6d2673f57698c0a3cf6d1ce44b60b475797844ac1b5cd2c06fcef0dca80c866d16390f6e 5d3f3b8844333b109f22f4c37761ccf7f5bf5bf9d5a9ce07813d1ce2c2f8bacb37c4e79a18ce3fcb 76a1935b889b1a32fb34d2d1df4410d8c95ca1c4099c50ea38c8e2c6686be3f68858225e72f0a94a 42a7f5dc4f5225f1f6903b0afd41c4caa38178adade2581c9cfa9ff891e91b1981e84dcc69b9975f 18fd68ee6d37918b7cce3fabec3ff8af269ecef278fe93fc56f458feaa1cf3187b0ca6136e3c99c0 83d66a8c8ebbc71131efde137addfa0cb97d353d0574b5183fdb55348ef936d9fff41a4c3f9163b1 0f0d23a537d127e59e056ddd686e1d2a11bc78b6bb2b9f4ebae8d6df74aae4f2bb3d55fbd022d036 7ddb58ad73af1cb5daf274dee2debd59f336ac4d7ff823997f8cc92b3d9199d52e5f2fa51772451b 53f31aa86374c4d849032e5586dcaed01e88a7d220566ea5695f7f1556bd89867d0ba85e7e8c5d22 378f3dba2bb7f4eea6430ca88baee562a78a0b4887d897c97683b6b9367d4e1fa6363fb05adcfd52 6d464aa9d77826eeb2a166b78f7ae2b854dd803f766d12eaed9a85ebdd3fc8efb9cedf5311972da3 6df75098fedfcf00d627cee3ce248dc2bb34888819112b978dd487facbf40225fb2082a7fb46175d eea34e153d0ddb87ea75daa68fd765abcd5eb72deefa3e366f51eeda145fb967e33920beaea1465a 7b17ea9fb189d78dbccfd6a1f94caf59a547504d9ffaa80a1f06eb4ac0963ee1b6bb16c2aa443a21 f192c21f82c3800ceee692bcfec4e8e5b93c737e4d1cb691612156ced74f6f22d7bf7ee8c885dac5 6e5048a80eb189c5367d488c167749dc6624ccab8d677fd56aa8f0b95f4fdab7a46e94e174448d8b db9ad5578eb582e2dfabeeaa0daaa5f616ae84d90f15ee1c420e6b74cb0b8ec9bb17b039fb5b75fa 1de79df51e60aa78bdab5af3e4aeda70df225377df0456fb239f7ecfe62cd9c1e5a4898dd818ccbb a5c82b26a0b3236ed70e09a26beb5c6f82e6ed73c71b835c49ac8f1cc3aa673fed4acd6eafbed7a6 bae43fc32a32cb4f2b9b3ab7aee01fe510ee57955b4863f57770bacff301679f31ff06a3bc2fb6cd b217678755f7e36486ae7eae1d9d8950849dfc6c67da73d7e8d86e49fa17ce746ff3ad6c56476bd1 1e6ff4bc3350e4a31c2d2f4bb243fb1fb8256a0c68ca8fe8d618acdc57ddccde891a0c6372153554 bb52b9b76be1c14e3a217d5a0d82b67e9e063c965ffb510f3df892addcbc78a9bc3cb5d5fe4e7dbb 236882b959ebc039538a2e3b859653b13d683fb4d6b670b0b0e5be68d6f89e69920fac533ef6b1e8 2fd40bdda3c4d5e70b25338a7eb117fdfc6da4741afdb4e67b15b6a5469620b3b579bbf17d3c6b70 4f7a56b6ad1e1a1e6e7731b85098e5df7da9e24b37abe50dac46ec69e464e28e06aba55bd6ce3b67 baf95c1cbb0a3fedc58bcfdadea28a5a1b24662dbc7bd6cdda0b0dcb27af1e97d9cd7b6f5cabc36f 9d6608a772597fb42e6dbdcfbd7a7a1fbd443feccbd16c3b330fcddee0553af851f0eaa8adfbaacf 36caec0aad21209babec7a8d4f78f40bf7e0128588df6bf6386fb0d9195ec62a046ef9c8349d99a6 7e3b69a7b87147f6b2d298dbc833da589bf9f66c11a5c3c3dc5f0b90498b64a9dc9a5b4c9973d3c2 bc7bf984fa5354faba122f775a9251721a64e5747552e8b552ecfaeaf8b5e8fdb0f5bdee6e1274b4 ef61e2517915fc4cd14d6deab3b5a59860957af69e0faebafaf11fe2fde10d4befac3b3635da99c5 4dd58199b56bfba357ddda1285ae45c4e4d03ca8d2d4a4d7d6ba7c0eab873297de39c6adbf068604 dfbf274d8fc302a97d1e86a2194ae469590045aa15b6b6cafc80e6945269930e2691b025afc6cdde 5fe886d19a3e88db3139a37a7dbb3f09dbd76ecd68e4de265725982a1672f2b1e0f7275ac6d3d5dd f71670f3c226e3c00d9cb4d191265bc423b4cc8633a894cfe745abdc4e5eb171cf81b111edc9a5fe a2999d1e7fdcab0e8cea5b337683bc96ad5d08d53a97246561b8ae529ace3b7208d32b6917ac3252 0def7ffd69123947ea29bcbe785485e88765742176c9e59cebf590553b6cf5e765a30eb71ca142af 1b84fff4a7050f6a16813317064f3ba0c39745624bd464ec375f6e9f29c3b81b65df9076b5863e90 7bdfdc415d2bae136de4ef665a99071b75da2b9c545b661eca62696715046da27218ed0469f72e58 12a5bb2db13939cc45d6ae7f84ce4a91854ef2a80b9d0ed5fb0b21122dc0f4b61df6d1773f22f8c6 779aa899305ab9ba8d683eb83d2cc24b2683a2331f3d808dd1e1d33cf2c2a3dc299a79a3a78f19fd 757b6bba26171d6db464ab5a8ed3daea0c6a0ed4a2d51c2bcbc268a920cdf55e0eafc7ab8c4ff08c 542f68dfe2563c7935566437ebb2d095c806ff18c4335e81e857da834222f7ae0dabdcbb9ee9716f fb11716ff9d49d4d4fabed60583df5bb74cfad342c01312ac70221f82f45225ceb582bdac17e03cc 26263fcb02557cfc6cd7280ae919c921b55cb32ba9f6755c5696e6de577c32d79037cd6257ae6872 22edc7da4caa9be1463c1d7a679163a62fe19629c282680a34df9f3775ee533a5639bda28ed9f1e1 7c67cde68a674ddfff9e34d6d497116b72e3ee0f531f8ab7b13998f5dbb7a554a9bbb36239bc3451 d11b0518e1785a089bc776f8361e45e4a90f33fbbb66e6f71fb5788550c537394ede1c1c4d26d496 23d547fd8a4487fbf64feede9ffa2217e7c642049025ff0c85031fe3e183fbd487394eff5c097652 a614363f6b868c0bbf86f42a6c5ce88008583a28a001edbf2a5dda3f3ae9f7fa426f8ff7476bf3cb 91f959e26bd8e9f1dd3d2ee8756e823bbd97481baf687059a8c96f5dcf3c9eea2c6edd14bfa3df64 a2dece4bf5cf9614dbd84d14b90751162247f4f8e74aaff17150e9709f7b77c025bdd994cb164f1b d60a3217b6808980717badafca41afde7b91c60ccaa3aad35e9f3cc2cc896c8cb224d9e8cc5db251 41db7fc1ccb746dc86d944dba7df6f8eac52b54ae957d3d7662bc1f10b2fd23cc514ac0f5bd44775 e2e1434ec72157897e3fbf6126c2edf679f1af225fe2d5964b732328923963b232d92979f659eb04 d599050777190f52d20ad1721634b6a8efa91a3b7b90c7eea740362593252e83b1490810dec17bd6 6287bd964d14973e968d4be7edd7dd8df7e0430b8f3eabe6309e6737dd2663c68d2273ac84d7ead2 f44c7a2eda04bb23cbdd0b52d2cc2d0a14f4d27d4a0c0d5ff997d43e7223d63db33631809845bc43 1804010c1d560a128d8baa49d562e3bbf90945a98d3ad95c2711c9868711d1d97f56f843a5cfb83c 0a32d83037c7b18c0bab6879ddaa2333025920f6e19d4766858b814cdf5603cd516e8aa2518fa7c6 62dd7ee0745cdb84eb6aa068f5b20b07c937c1df3c9b23d250b3ef925a2ae433d211afbf841e7abb 7265d63a328872dc52f5ecf142b6e822445cdb5489e81a1e8d3fa63511efd7a606f63ead5d4ce78e 552c732f7451d396c6c86c196c10989bdf4b7e2ffbf575c29b773a5221f4b35f3c645be322adb0ef 62bdc1cac5fd7c5729eee37505c69f976a6ffd0e57adccf2d6afd24eafea435ddfb4abb22b96a3b8 47e9596a5f9277ea071205587ff119727b63bc2779a4e8ece0ab0be0fd6d7185415eee889a57ed83 cccd4a0181e72dacb472676c09bd9c1478db2dda30f1a42ac586aa758a0ce88d0bedf0becfdf0ecc 3b17776c34fb791cb5aca1ba6d68d2857659e8861259035d96b389fefece78a4355bc6ed9eb6daba 015727fd50ca6a15d76d88a6c5b40cc918943ab4baf09788d4ecdcb2fc304bbcd8e235ba51f5e475 c49fbeb6412d75b9807730975e2d1bde14f8a578cdf7b800e406929bcf8141f5fbdc6447da84cd66 c70715b2f3698db7f0887aa6b471062014375b80bd844f8a2b0db0f72800d8b93a07d83a28006c93 170136d6cd14a6d58ed9ccaa46b2d5be3f2ab255075bd39629a096a297cbb5af7d54c1ab4354e82b 932c3b3b416feae01a3742a2a647d48adf6b785777e6f9fb2b49a059293b041bf93803f8f0b50578 c25f5334418a6309e0238605f83a3600bec95753d40700df66d2f76dbb3f1112df09e91bf6204c71 58007c7e8001bea8ab001f9cfcf42333d566c1192ec37b4cf6bc228ad4ac134e3b6579e26aaa0fcb 8c78cb382837e1e33c4d12f33721f5c115b549ed00d766c375fe5183bed34490a3166240d8833e20 6aaf2920fad62145f2487187003157b114319fe25306c4b1524ff14800710ad3f79d73d914530110 97a00688abb206c49628026257b40031f5d2374f47df4ebab6bb3bcb405b63b1b3a9220df33ae33c dd6c4abadcc8b21c9fc41cc6602d234ff43a8d0f6a6f1737b82e660e79a5c6ad322b551a0112cf26 802c93634086ee12909dc9019063f49dc2ca0372f3db9504903758041454b7533c9a80cadae314f7 13a072cd02a0084949f1497f419ef6802ace5040c1231f5025b20728e04e2add16b7f2668fd2d03e 906cbbac9ce940c5889221bcfc0ccf2238fc5db7460a47b288ce563280e9b17dcb19b2b3cb606e7b 0e7e923fa56e377f69be03283dcc51fc5a912b2f40179e30a0298506b43c5400ed143d40d79b1d40 37b27340c7f11dd0031e4d71337e02f17018013af18e801e7914a0bb6a35c57302e848d9059fadb8 7557336962f19e10690be9134a9de46572c5f95ea438ff4661f9fd058619ef0a72d9dde90a6af36f 92c69138fea5434f66cfdf617e9a2ff4f880dfcc0a631010600207064c7b4903662de880b98c42c0 22e95962d9f91ab0baf949f1a1001bcc5cc0866102d8aefa006cfaf50bd85abe01d8de7503d8ba70 0fb291ff5df8ef901f755ece1078ff97bfcd4f8a478766f09982679b13063ede47482e975e0b405b f5dbbf75e8af04cdc2c3d2bf3dc73fd5f7a7b0fea21d7e52a6b9fe597bb9c8a8016e7b1e021eabef 015fe672800f1f22e0bba31ae0237701f8a904017e4f28803f64ba29465bc01fcb59cf3b562e9688 59eb9f182d3e5f509bc5b6c780187c366584c827c2cf14fd3dcce163e6fe28e37f74e86f06c63f72 efbfe21cfe153161587f196a8bb10704fb5903c27e3c01a259bb01f166e340ca964c2061871e90ca b3f36ff7b84a37fd71e4b9409a4ae314b72b906641d169645a8fbf9cd14279cecf5871407597a081 ae7a7bb7288d266a66b711c83fa7ea5f3af44febfdb5e47f444cf43e5520e6c4e6bf6285ff3bbff7 27f7caa52b9067590228f8cd018a381e02a5da7b00a5e7d140d91b15a03ce1698ae115a805baf0b7 08f93f7c9d46e96ff9d409fec72e67dffc06fdfefae3ea55a632fa488a1af5807c8dbb9b5b59bccd 4941b93947cdb8ae58cdbefa57dbbf6c45bb7a2162bb796e80df5e11a77379303d71b3ddee1895b0 f7e1194e99fda7c1f87f626fff1376fb33cdfed4d44544ebe5ff0ff974c5a74da4adbfb738cb254f 65c27ead560fc843f6b73991b95c57694d75d9f2a7cf85e89db3e7fafb5c3c9df50372e2265bfcef 6484397b503673719fa45faabb093331b7ce23f27ea108ab839d0c9667ffb35b72bb49296d0ea12c c433f7fd16f8138af0c51fcb6c7e4c72ffbba93f037283f86dc57537579179836b5178455b71eb7c 90dafd13373692c3d32527fb4f585aec26d465b5756e83f5061dfc349b35311676ab469e3d2ecf2e 7559726beab110f7c8671ed3446ef6698be80ceae9ecd47cf7cb93b991694ee0e9683b0e600afa93 87f09f0884df86613fe7ec7fb5f3a74327dbf7cdd962af0bd179e44f5c32430f71b14befa1aa2f6e e7acaead8964ab2fdbc5b6b910b7863d8f49c39f252c5f99415da6fe77aa2cd69dc0a3527fbc2a33 a3313a6366bf54d9e41018df04ff84de35dec336b58007b736260ec47b5c89634559c4ca67ffec7f 92fdbb9f18b3d73ff89797f72b4cfee392dd99fbc37a75d00bebc5cd3d1ee7ca7e749be9e7d17786 7d9aef75731378d82a8dd149051f110b93fac72d3b6c13a632b835747d205e742b7e761c2f561e4e a5ffe977db7dfd338c7b136333ebe567b963e4c2662682570fb61be0bdea1f976c9531be75daeff5 6799fde19fc0863fc2e4c79dc77f4206941ded4ef3dd4f7d1c80437744cc56c3e13998cc86dc6eb5 1988a7f9318eb9d5ad9f48c9b30f0d46a037d1165f2975fa951fa2b9b9462378be20ba2bf7c876d1 f551ec6c2b40ef108792db6e307cbd4d5fda83565b386f9ab7587c37230de21bcf712b6cc45666f0 8f41b6fe999f067f42069ccdf17488f3cfe1929bd5bf4b17a7e695e7475ba3460dcf7e591a8847d6 e87fba94d78762b6decb8fd86ee4e6844177e568d3ce3660179df486dab40f35f9d0a64fc2b9756e 69f7569bb3dfcd5be440cd486ac18de7a08b37626dc9d53fe3ebd73554370a885f8716b5a8662197 55755eb53e55f854102aab56dbaf04dca4fe07e82daa3d20dbbaff727957f468ff3d8718f75378e3 6703cdf4a1de2a1bcdf503de5d5967aeb3f59e5afb50797ea7f05ae7c62d68719767bd19099f4e23 bd2efd46ace693fa67044dea49195dd6a119bca94d1cfa58cbafb85b751e98694b76d54265559b93 15f47493c22a4fbbc1a1d78d828642edfc73b22ffaedb22dfb7c6ef03d8c779b35ad1f7e77667a53 329d997e6c4bbf56f63fedcd3b6d60fed245e7d1b17d08db8fd6b93ecd37237e80369ebd355bff0c 1752dd8076466d621d9d5a7e710caa2e72ac55e1cdbd550988fbb74eaba0473008b7cdd22424aed8 323874d543403fddbbdf563b90cf975e84177555d57d3e275557d5e8899bf6be90634cfa8c93b51e ba93ad650c7b5ad86b3ff9f427468ffd4f8f8cd3c72f17b943f337477004c1a1c553eb6553bc85cb 86f2ea9deb4666f0aa59f959beea321bacb2da64b80a66526a583dc8567054cb7ed02c35ebfea5d3 69fb9df222f685dc65e2f51a60e5c92a7f72076bf7e566ca0bd8291fb2dfb551f64c752dbbb8be75 2d9ff0b716f2d211b3e2ed55934073ce5fc8bceddfed39fb6c3ffaf0520be99ebdae20ed5335c8fc c4ddfa9807879aade1b35a217c2faaa558b85670d0ca865478fad602017bc8097e57a574ef7129bb 5e7fd4acba9f5cb3e90ef7d39e0b29ebc49994c0c2c987d9ad3d17a4ab0dcf426005f0a0646edb2f de241ebc556e38f3ae715ea7df793c0195f4db73aceb517cf5f4a8b1ff0ea37eafdbb98375268be6 b91cbf973da15b993958f3d90c7275f3133dab41e175ae105d7b1bd6eb8f45705ab357bfbbf7b35e 0cf731f7939c78d7c0339a33e923b65380a9c09e9feda65d32c2c85a6383efba350bab0da6e64ebe 6c4d72f83a979b18f9312e3daf64f0ef01abf7f497a9bd764147d3a8cf4603e743491d757c4d2d0b 33ff2fe043ef279a8ff6ddb3ddcf47a1d83e9f4da2618cdd4235d8055fef60d8b8ee2e01dfd0f6be 649c565edc7b1cdde4ac40ce14aba0f6c2eeb376e9b454ac50bf5ae66efbf6cd5aad582b1fdf54a7 dc5c9607463a9a99189d5b6fad3fa4e9499717979736448b052d13d98c5a7e4dbf9a8d32d34b4da5 b89dace465ad5d947d9a56656455f1ff42e2788b3e32e8265cbc72a2d5d1965ad28e266a8b982f84 c793f1f6efcfe9d51b74d8835ba6666bc7e6ba5bdbb3ae6f2b1cc24573cf99df7da34c6a5297ca2d b26f18d7c1cc35badabea23f368f96de8f90bef67ee1234df7e4a50621e14135bba3a7327b9ef20a ac2194ec6f6a86b425ee4d8968ba0bb17e2373221d1d6491ae89fe5fb0e9afca3187886634785e26 6eb7866872335344c96a78218b4117953ede9019dcdc5c1f39da4baebfb13692be32f7cdd9b37cda 3c0bc6f595270cd1a379bd7f5554ed633a969610fd50831afdba3a51f7919a1f9dbe497dcabc822c 14f840ede480d16ed276d4cd4a646e4f880d175785f365501778919cf3516397e1a3662cf11297f7 7909f978bc94b9b9d3b96a47f1088fbd0e2b527223d782a90a9dcb16fd7e2400d70c3bdfc00c7bb9 7c1fad4ab5b535698d5e19375139eb4fb70de92a38605a127e580d7aa28a6aa9445999af3957713b 7e4d5e3dfc961cf4fbb1b4836713890cf66bb1f1c85d848bc201814f2a28dfc34e32f7aacb5f1192 53cfbb0907c2f6871d11aac08ef25bef0f92e7dc9d04a110f5a7f586d716e6b8525b8e9f54c8d501 ece90b043870dfbb5b95dbfe583e6bcac6884468a5abf467af19a10154abef1695051c7dfd694aa9 3d17e4b5f1d4656cfab1a51a9d0f24f24c37c426cf7545365b19091dbbbbe4efa7cd8997f9cc871b f4448403eff49765231330d36973c41461f2c1144b259e2982c8650a97f61f8c8e4ae69b12dff312 cb6b290ca256b7c48d0ec4d305762db698b131d87e984c617c342411dde8803eacd42936d9285ef1 f690d76d2c27e35915936ab580158fa7862cb27a64085768ee0a42f550e51fe4a7cdcb43f4bb3c8e 1b42dc8ccba44f1e3b26972f66c6de8a4c31e238da7b0d5d6aa35303aa02bdee5478dfb154b857ed 14964b8533d5493afea91b5530c36f42fc55a91c1a5bda57f563c971c739e827951b77adffadd3b4 11fddca8855d7b29af97d25cda438d937822174064077758e8a205927f54319eef4b86c6bd0786c9 e948e073994ddc604d71d56366f179cc381abaa1971be74ea34492a708e64c93f5ae6291b4f8feba bb89d6a077215aad1a4db40260122d236b13a7047386f26413756999f11bf9e9490ddba384f6b2d5 69c9c666fb4cf932659fba46984765b14bd632ee4b0bf1648199701d94b6fc63a33eb8f7d38638dd ed7cc57b0e62d6346b7676029b175e1ae3cc4b0ebd2a6121ed5fac36b515aa038ae8270bb2013fcf c439643204b76f1078443f0decd9a97731e5891f30e598c3b1b8302d63fdd7c2c29ed2d11e648ec3 a87df30bfedf96f8a087c78c5b38f64a16456f20a3772a3eb59cab9c647c595d8b2dabb4e09f603a e5868fce9c35cf8b133337ee2f06de23793aa07184463f3643550d4fa4c86c47271bb5a94b9c4feb 2ad14e4adf61147ecff2135cb2831d162fe72f34b96711342b070a62b59e0da4c08f374801ef2388 65ca5a0acf4426ddd0ec3b9b4ad452cd8b5fada143cdd7942a636f3261a97c3586d04f2f57961de2 24d17ae35bdc0ae220bbe4a0be37614ad94c426ddfb9057944d913d9b8d5dec445eae6087e3146f0 bb77a271e9fa16b18194d331f52578e8c8f31a6876d38b119bd82d4a8b66e9524232cd021c9a2fa1 b85b542b450a82bf2ed5227987f2c51a36938abb30631477c6438fb673256a4cd5a91f5e5e8eeace 309efe59cc8d7ead0e697962f3942b7bf82cdcaad6964b3ac725e3b68509557da5833881a824d860 64ceb10cde3fa2a3fde63bb182e6942784d82308468a658228797b5d8037b4afc3e179e815f7c6b5 55a466485268c1de2ead3247ef5c9fcee0d921ef95b3da838cb3da017d6587f92d997d4b84949323 51ee5c4238aa2342d3ff3b9ac5f16d9231395740747de96795953a7cfe847d5ef5982d5ba88f96d4 ae874f89ceb89f6099e0d32bf97a1cc30499cc8afbe37957a475e85a684de07781b3a47cbebb30d0 5cffea32d9e13390b3901a7fab4ec81c9d1b99799e4832b05bd9017f7dcb00b4d2e2015ab51b00b5 8d2340e57b1ef8759701cb7dc4b706ef4354a53e9aef8d9fa86613c3126b3c4714aa599c9393b797 f15b886eab333bad947634ce875f8b22d1cdec27985e5286a5e57bde2bd28b522bfb39d75a906587 83cc7cd99c665c7fba01abedee0c30f2f20418c5e653744880d1404de10600533e31c0d4d60e601a 01a5b88a00e3464d80f1d1d7320230dc4200969bf329d2d1b26335baa138457c173e3f35b3ddbdb3 06c0714c75b77e41bcd49d17375ac4177a435f76c475c92e31bdd19d94560e342c32a41565938f57 cb944eb93ac0a15c27853dfe29b7d06005700c9c5348ef14bd62fab1804961e929d221154ee84380 8b8f03c0a56e1ee0b2a6009c2ea59f417f4e002f9d498023ba06f04cd7ad1157a9fb8b32b7abf783 5eee112ffe17df3ea308b9d6e40afcdb10dfccb25bbd92addd748f19f9c7b2b47a98d3c2c58a07d9 b2b268834d11a9017c8937007e0afb291653807f0a5b40c0d635c50800822920295c2ec5b60c0856 fc597b0976390204a79d0061e4e0146b1d10e5b80f08b172038454e101413e1c4050423dbcbbd9ae bbe89e428ba55686313816241569e74851cc7d0a3f298a628074c3929bbd2f85787f59e84c9edfa5 f2d02c1d0300a2a8b50051eb4580982ed223ec2e43403cb01920738d0320f1c30390229905a4d9c0 0059798a80acea768a4d0b90352d7d5fed720364bd890232362c400ee80490addc0b90ede26f5b37 3218d4001982be3fce1e23078f97955f4eb89ecf6415e94c9e286ed23ec03f9114d7e8ecbd145e91 4341c4f41504bb7c0288fb65f0970e3db2667f09be205aa6ffd87c0d28f5f30b39a53ce509a8e6a4 00a8613efda797be0ca8f7db07d4c78b52bc5680068d17a0330c95e2e9011add4f018dcd3280cef5 5440e7dd6e8af3dc736af7d8ae2f724de313409e527576aaa08e67dfa9087a774a10fcdd59644b9b d7f451103fd75366f59c2c00b90ee67fb568011f005d50ce3fdbf15f26df5fe2c45761a56fd70f60 b2740130fc8c054c88eb80198daa80b9f2c314dbdf73c3a4552560eeb098e2d400cc63b0066cbe53 046c213001f3d647294e4777d567c6bf4d2b7f62b4f8c82e0cd62ff579e229b571649534f3855ebe f6caacb7e2e1af86fd74e8b439bfc3fce4deb6f297aeca7cbaf0ff0a9bf863f7d5b874d4b92e03ce b29a809b4253c0edfa57c0ed151470cf5c19f0f95defef4ce1360178c7ff660a53bfc79377ef0f87 3cf3336374c8f514ea7aabf1e6726153e7c758c10ae3802a764e360cf90ef2fe69e4ff4b87fe49aa 3fe1f9bf04dfbf83857f87f9196affd17ca94f1308fdcd1288e20c00b1dae4813835aa40dc130b20 e5e12c9088b70a2472d101925cdd024929004bccaa2b6d55a2929f18cdeca25d406482a151a2d5ea f75b203b6f7e27447f6dfb8f1ffa5fd6de5fcaf1bf1af1751aff5f71c2ff95df7b1c6c802c377340 9efdf62c06f2536c01a580ec8022e74a40b12f2650ea491f28b1794871cefc6f4df267edfdc9a7bf 98dedf1667b92485b749b1be3d9f06023d95295e7c24450afbedb786a6fdb4b9c5d9db9c44841bdc 2cca57ff9ad3ae6817312f9527e29e0f2a5e39d323b675e2666cff18c1defc1063a3cb3ea13385dd 84ef6869af0e9a7f726e03a857fb29bc3f83ec4f40fd3ff68dfa4b3e8de9bf637affca6f787cccc7 e43e299d563767bfdc5ffdf3f87ca9dcfbf773fd5d7f9dda90f939de2c033a8a0b357f881109de7f 2a3c9a16b1d85717d8e53b597ab3922ffc7aab5fd455a3900eccda68a1b988486b3a7fb6a0f7ec13 359859224de47ff210c62b6741fd5265ff2f97ec33063cfc801c89bd393bec7b6dae680bb32e4444 86a7b3566a1ec5d9b1b3ff04d3fece3cd9f166251507eb2a3825ab466e365e9e9df1eccf2662e2ae be9ec7546537fbb4eae71914f9f7a9f96a67266e2682c72bf3c08ed185e88caac8b2ff67ebb0e6fd 5f2908ffd93aecff72c9fef4b4ab7f985e4ead8ffc3ac4c53cb49b10dbe2c67fb8c89a480c6ad92e 62dcfc59390ab34f63ad4c274247fb49aafd66790227b6350eb26577b4b5657f542d96abc9c12fff 2758d61b0e6ecdda6c205e47fb38162fcf5879d9583fd1765a1f9ac483dec4a68f3daba8dc7af925 7dfd07ff632ae2679a3cd7efd7e59fddb88841b3bb884a6c324ba8c7746adeae9b89f3ba9ec641e6 741f11f3d93ba1d70934e4f6edfc20a21bf0ffb6ca46c23f56d99f4b75327723b7b0ad7657de27ea 06a838ed6cabfd735a00e70bed06b729b7e9bb17b5cebddde28f55f63f7ed9bf4dd1d2ede7ebdcf8 b74eede73f9de6bb8632da964b56422f5fdf65a5835bf5d2889fcd4b142bd757d2d79ff7596fa2be d6d1bc7cdc456eee75eeae9cfbad8bae2ecfce3678830eb17b64db877aa6d8a6cf19b4756e534c8b bb0b5233926db3f11c762a8dd80083fa676aedea8953f8da12ead06a65d426a1d2fa9751f68b9faa fff3f2fea21066503310475bc3c687dca6958f958b82f4a1bec945734351ba2b5b31bbe8d2093a55 b4d2683748a7d33a379d7e8bbb9a5ff9a11989e6a4293e9d792356cd554305957dfd336a9deb46ae fba843b379a666c1db62751e3cc9aa8b73726555af7b15f4fce887db4e6d1f56c532161ce2b5f6f3 c6060dade0fff04b95f899a27ffbad91899c1cec2212c70c047ae65bbc7557e6f3de21d6c74c9bde df4b2dee7c279bd1776bdb58016afd9364cdba912d78b5899d0d6b56116b54e77ea95d7551b25759 55b1412520f949053d1acbb0cabadf34b890b846b7a0212e32fe39be237e5b5345ef364e3c4f2a10 03f7b978dcddd86fe1ae8a3e14e7b3bdaa3ffcfcdbcb76ae6f8fd1a446fc9a18b999677ae7ac994d ab4d8de64df1d6db3694d7f4f693bb334b50b3f2cb4275ee9ef02abcbe3295007f88e1b6fe5043e2 7c35824327e3040da118f8e77eaeeab715bce5dd12aae7458636729f536fe9c6f6e8e47c96c7b763 6038624f6a35c9b6e842d59ab7e3af5fc072053763c18f156eaefa07fa877d528afb3f31fa77b97b f921716d3784c23a6da030ae27ea2cae59393faece9deeacb20ac6fb705b1b5f43e2b4f904f46d95 f3dbd2b6e473af27e945daed5bd9781204896e5ccdabae2a71960b0a4ee018cd7ac3c9b2e35efadd 709a580bb2b8b54a4df96e869961b1bcb3327cb9468e2ac6b1a5ce0d167a7e0c166b93fa6517313f fceecdf14a99523100f0d7e0dfd959f97b5386b3bbda7420ccaba5c730097706d40dabd1214a6f99 d5c86f779f6b2f7ad157f7e5e91f57cbb48bcea812634e593cb04eae00c9b6ddcceb76b1697a969f a9d42c6433ee9a15719f94f7f1e3eb202ed30875325add0ad0afcf7414217aaca6f537d3a6fa21ad ad3a7c33b0aa0f63214522aa7a2b121637c73193c6d4267bc51c9c6f7535f85d3703fe52d966c26d 70fa2c677eb7460fbd47731579b2187c07eceeb090df3a634fb9dbb37100d93011952c3f9e9016ea 9e79b372792a263146cd72238fba65faa0d78cb6fa75ad8dea035dc2670b2dae9f0e1a48eb53d528 575065b27fa84a81f11ab2db2dace592f80b9d93562f434861893fcc92e8a90f7a438dee561cbad4 f8540aa01ac8a56b703e683befb9ebcfddc44747cec49cc74edeadc6b67339acad80c85fcdaa8a7e ca878b512c33e500372e78c2197cfd67b5d2efcac1d0a5b4ebd506d833d4d407d152478a18abd995 3f536c7e7c9017bdc75b2e7d38440a83b122eef6545da4e8f552385e7a79a1d92df142b382893f4c 0b838311eba8cdb5cf018cd42787c2f716a890adeccd972eecde35dedda55388e089b566a2818595 f9c8241d6b5a6e7c5627a3c3df5efa7d56c8e932cda0da2091284d0baaa23ada5555b59cf638caf4 3df195a27f68c8def616491b199d48f850f9aa1c621ded3f8553f5060bec4911f92eb7a9708f9e3d e3fa8a904d71e4b87e5afd717dfa2e8ec3ebd9eae72d8d6f45d113a9b99f3b145c36afbb97c1f183 53e4ea2b2b6cdc262685bb8372d3c17a866090dfe256efed1a076df08eee5aa6b58354137a959499 0d08c5a1484e5e0e79594621c99036dbaa271164bb26d60793ae70ce1c460267416bbe7bd4efdc93 8d0b9c12413c3b7cd50316d2f1293381de19667c6f7e1fcff4752a32e3dd4018d144da23af0682d0 1c901fac8a636bc8ef3d77f79f0e6d6d2c736552ebc3d4b83ac650efa3f79e36b4ee4375dc22376a 1eb2ae0a4cba6fd91f74f2d21619a152e57aa4c58378f9ae8d121b85922e9c3dcc16b82b1ff29168 b478f1190db9d83dacd824fc5c58e8a0e6188b99b0f4bc4b7b34fcdc8c68f8b504347cd2e8144d91 8697a130bca989ddad6e10a151d60e5885ea2db2ded05d3f7e3af4f07a30eb4d6e655c1f93a9de6f 33430d2a2c7a6a5e1bf4e515319dc9e8a27092aa2cf3140f5d191299bc0d0bed468d1078a6c7f2d1 682a73afdcc2e0e203f039c0941a6c7ac3f49969ae3a670acefcab75d2eeba00516b3ca029acfe74 c8eaa59b9064d77b9364354791d5892cfc3088c386fdcb67f9e59d84ec7b9075c7ebd1c3daa2a763 b99da1d77a0cb7a71a64bd068a9be9f464f4297725b2647f531445a63cda09edfdeecadfe9cb9b97 46b93cf70a599453f702c58e94b2c01a9f9ac64c8d8ecd14768b2aed51972eb54eb0098567dd0359 b3271fe2b82a11048bc516dea973312e94720fbce3ee49bca365be030ffcd2ccf1b1d1b7ecb6e03c c41a728830ff89d6b23febb679a82e8f465421369ac1bb53c5d5d60369fbb47b62e38274057e9f8b 79e9232cb941e01d3970ef3cd8b23cccb0b9e209666cff84d18bdbfb37c4f57ab84c6d8a727aaffb 7640d6b6cd167192d609c10e0b5bbc9b9e03ec61ad314c5e4a063a446f3d549bce6ee83057c7d1c1 63c6a16f7cc9f50b2bde6ec9abbd5825d4369e566261ce0e14ef513ebf66df6ba3abf5e75a2dd8d2 54aa3e6bb170e92011dfabed3b9ce6f73b6c395a8e187b715dd34bae78a67d487e511b4b8728fce8 c3e4bed92548ea3ae48896b951f1ebfc6ae3228bd4b07e57eba3ef676f89eaeafddb4923e3b10623 f9fc4e29395ed02ec1a87e2e39068c94666d9f2dcd0293893659d26942d9a5f48b66712703216b55 57f6d3e821dd93964db66b79ddc2a622cb78035eae3da2b44c6fb599e28268d06846fc76041441f8 33b21ef777c4b934bb105cb879e137fc99c5c5385fc262809298b2364534c12b060ad58701627e1e dd929b2b4ee095631f6174b5cf15abb826140ef557bdd0286df7456290148b44f349152ba73cfd4b 897f03a7ee9cea6210f525dc59206cce64eec21fc95c2d62ab8db4874a33fe5196075c66b88918c7 aa74a8cae8d320da70d2c0a5ecbc8fc5b5cf1cfd9cf35bd4e0e1139acd2a4fc4b22da8345f06dfad c34a6e6b4ec3abdb41823119b28ad5a592b668ebc7f90bb9d9e4ee5cf1957d497532ab0e11370bb8 fb2c1bafb69f6c3c62d0ec0ba8583bca5cdcea76ab8bbf68163b34f1fccf62ae95e7ce59dec8f1e6 6722275e33eefdd01266769bf5a8ada67488b6b36a60f1cea822d616d44ba545a90faf3d69026357 775dac89c1a1483ee37ba1a94e41feb2bec07981c852d9d70588594d602c68d40fea9969920c33c5 5cfe5b0e02cf69e70052121580649008787af60c16ad4e2e5358ae4bbfd49b0a73e42477bacb12e6 89bee6f421fb7eaac58b7896f6cb682b88ea6ec69a673ca1d163ab479c9f501b1b64ab75c4465e21 8cabb5ef4c61be0b5395ecbb966b65b513d2cf665af20432216f9d9959b563a698f69cc047965980 6c73780a4f4a71f701b20bfa00d9e777295679801cba1a4016760f204bf50690e134fd5902f0fa22 0cbf73d2811c3e4507591c897237d8e7b5f136f356d0329736966befb8a43c9dd3abed3b2199aede c35ec9b68dd82dbe0ee3f134cc779da20399838a0dfc03ed01b4e137538cfb001de466006d0adb14 9def7393be3eb2001dda24405b0b05a08914a6380c013af28f001d13708a7719a0d17a00d0deea0d d03a8302d409d86aed8c3bdeb87e90acfa784418ca7459509de7e92d137bfaca3f5fc68e59c0b5ef 1c01791cec47b8ecd27d6426c71d9828807a5eac0b3e6415ba16c008c2049858090166ee3b00b38a 438055ed458ad10160bdec33855d48b1a601d667f414931ac062719ce27401d8a08bfe74e8a16d03 ac2d4d00d691b2000b570cc02ab0fa8b347756c64436f9fc88d20d79032bab239411f9d6e7ca6537 cc9eda67bd25de17876364b6fbc4c583c4b7732a5dae64dc41df01d8a6e2003cd3f77e3a3472aa01 9cc3bb00d79b1380fbbb35c0dbd83985fbfedb85cc73291213e063a299623603f8d67e007c9a2352 1c3d80cfb60b80c75b18e0035f49b1717de83a746cd28ebe92aaf1527bb436a51ea874ec6c33bc56 bddde90a7cdfe34f28b740e6e5ff47d779b629aa6d5bf8b72c3191918c24415182a2983061ce39fe ff8b54f7e9defb9ef365d4d3b6554e17b058bc0cc6d42679bf1bf433a0316e82f5e2ea00a27a7501 712af8bf806fa11c00b2d8eb03b27c8900d9d7f7809c8537401e0004c8a78927e4367b90009513ad 58961d40e5d5552caf0f203f03165088e7012a656c0005191820ef44bce8b9377d0b99372ce3b2ec aabaaaec7879036df0c4bccdd43add3b79ebad8f584e39adf24d3dfbe5d0e952ffd5fd555bd169ff 70e81fefb1808c01e5f011a0daf1d14e8ded05a02eab3d28a4c82728a07e1614d4cf3771a2a28042 2bfe9a85a03a00853688dfd21e6440c1552450e892ad0410f7d0132834521428542e2e28587a58de 331ddb10965b439d396b51e2aa1d82cbee822c155cac27368ddae77c0b6eadd386eb4ffe2eeccba1 7f0230fef21c930987fe01bf09644d5cb5dee405e8299607f47d182f5308c200f172c1078cc144b1 6caf80313d0c30652efe8f4a660018f978078cb58bdf5c1d3500a3c9f332ad9c1afa083a54922491 6f72d238db28d005ce86f149a50c60da152e695383d77fd7f62deba79c04a92676e33fc037c99948 521dfeedf42df280cbbe921d9a93bc0ee07adc0670f5e507700d97059ccf3a801b208b58ee10e096 070570ab61fcbe757163dcaea92049b616eff7b196348124e5818e215e4f48674ae9ebf567d0fee1 87fe7141274835a9e33feded7ef98cbf7de4fec57cff987c4fdd1a1068790084307f04c27c9307c2 6ba001b1e0f78058a9c567065fa392ee7183420d88a774144bf7a28f67efee5f303ae8778a585534 0bb93ec6e6c1019a3d7e0dda7f63bdffea1ef73b4e38b1f6fe37e6fbcd74f8f1f726b8f794be8362 051440b1b1aa82e27c3205c563f303e47c5d0232ad3463f92c81ac749eff9f49fe6e1d96c4f426f8 f44f7e437111cbe014cbebfdd3ea6c145fcfde23f481ddb3ee95bccdc9377383fd077fad5daed265 2b9c940bd93f95ce8dcfd5fa09c0ede6f7cd43883dbeb684fda840ac77c6adf4dcacd413b7de9aac b53a54f3b515bd3e5909e49d9a2989ff9f2103bf537b9316675af519a686c1e3636c86f70899cd6e 957d7f9d20d5a0b63b1f8ae2f174d6b0f3f16642b7a338ff3c0ff27afbda6bfbd167679c9ba96de5 5ecd6cb0a1905f1dcc3cba3c57f385c5cd25a4f9d32f59b34f7bd099694ff33c35c111f90943f816 36b1e1019520d53a8a2620f5bf95faf8e8f7f4dd585ef0eb8ada7117f7ba907f67df4eed83bc2a7b 3be394aa6fb0b0515f1d4ab2bf3c57c8d6e26643ed85b8fdf4e621f51c24a08bdd8e923e6253e3b9 5a4473e5b0fbe91f86cd3e9f31b9a6d0782de64bc3b30f3786dc65bffd1385103e433295340cebeb e9fef37f842164d649bfb56878d843ae71dbd4eeb7e7ca870660c92d9ad9f9d3ada233ed42e0d36c ef4d45f0f0c44c6c68cf8fb7e6461a938ba532f2d1993e3cbb1373c81d86d6a0cb4cdcf0d99e3543 f9b1e9f547f223ea436362f745aa66a605ba732b45772de4ee7756aebfead8e4e8f023d821d8b7b7 bebdfb77e2ad1891e384a12eba70ab36d34ef139b1f2ca7626d8f8331c7b59783e3a58f46e1810d0 77ea1c740bcf6b18728747ffd3ddbefbda6b9dfa71cb9aa91dfc6395b5b2974267655db80eb6be17 db1e094a6df2c03b81cf58edd6b9bd9ab7b8277d6fdec25ba129a56675ff1915c63ff20d94fd3ef8 fffdf9a7ad5982d212a3ec8f4bd64b97c86180115cf86c6694fea7932ff7a130eff4ccf814d39d97 d85e675521861d6c45446d0f271641bc4bad03fa94dfb5ce2df4d8e26ef96ff393e62dfe3a4df145 03ff39d072be92aae08d91d1e27e9206a22a51af6737cdb16751b9ab079fd2bcbb6a75ed58b6fe1f ab6cc2cb939e66495a43e2eb4c905f7fc44f5e09ee1e563e1d3b67c26d0fb5a8c0272dbe75f63da5 c55d02b3d91582aaffec773d5ffe047e63a4fb41434f777bf5a81c0cea663e9c78f35a38f72c6cbe 7157deeae862c7c3ddd9b690b443de24dcf625ff7bffc6a6df905d0bb4d6b8c6a76ba07a9b65d81f 836cb7523512f9cb7dfa057e03f1c04109986c7b88b20de8dd6bddbcb5ee273f94eeafc66798c934 a02885d4cd5c9eaa6717ecf712d7b350b6e8ae5c5673b143c170b6be6039e44570ec435b6dd8f4a3 d8aa9dfbd57e8dfb38e3ea6d345856a5f4fa683d67b9b7a5c02a5219d963b1a213925781f69f4539 f2e750d96439a69cbd8a7c22df39ed7bef2141e67fe1d38383ac9b5dae14fde0d3a814efa2f3ca72 edaeece5d1d9d69777873c1d80ed73e7ac4ddf21ac16481fb27a1b7c986a57cb0a5509c2642b3431 dd5272dc3748b332aa727645470dbf1cb96eaf6c9293c89cfbd79d095f89a7b1ea7450037b6594d2 76306e953ccd3b94a83448eb872983259258cc13189d6cee8443fbcf2e31ae9b19b9e3ae6aab6f06 b1b3f5fc867d68fa1d9bbecea31af78c96d5aeb23d58cff1e16a2999f3abf29903a8a223205781d6 39b46c1239b29cdde38c69d1b860c26749356c5e310deceebb25af386aeb87e179a233696ca79da7 adaff35ee361a8a076ed5645950467a6bcb2e0a6848752269179887dd424fe2241e6cddb7bb1ac4f 91ebd8c5836cc7f68f76a3c6972eb56a576e57ade768d1b494f4b15fd1f3c74939aadd97e55c1a3f 26b8bbcedc4ca4687e0c7be9640cbcd5434adead4794a868c7e94dea2eebcc0931b5b626b9ea7deb 75d4a2b49a2a8361f6a8a4302f2d97bc27579c9e5a6e31af1797c5bcfd7a4b4ba290939619349b1c e4df7c812fdd6f537e74f653d062e3ba99c7cc66efe2b02aa7c28e95f2738d4a49af572be95ec92e e76aa59659c5e255bb63ee96a5ddf1be2fd597cc5d3fd5aa299dbd35f3da359a129a989bd2eae378 1155594b7f1f2551861162291025356463e0f58bf3d47c5184bdec45aa1ded9ce85eb78270902a75 c1cf17b7b1bcd3820fb3a8d0f81048b2e17bd3f439d5ea80f3a35e318f07fb4c5f175579898d2ac6 c1eb9661077c3b979b58b1e91a6e8dad19c44df54b34d51be9adc571a575ae97832646f8430d2916 289f269b51466a1955a04dbd209b649d93b3cf855ab4ac4b455a6d8027e122df13bdd09e09fe3a3a f11712c9f0bc1f7c078deb5e498f93baa90dfb923aa9584e48fccf777c32eba964b75a7f669a72e9 fc72b7d5c5b1d6b5f7cb4ab485c6e6ea62f60caf7e6995fc5abdae0752a6a6dd06f9ba26a1d5a1aa 88f5b9329a0f764a9a597ca74ed91c1f5fc505f1808ad69340a4b542109283943871e7948b22f568 9b42531e3bfc6575eff00281475c51b68eec60b486d85486679952e5e0301964b2623268bcaccea4 86482243fe24501de2037d1fbe684472f472fc4eef5855a6cb55b99a7d4e8c3aa1f4f54b6b1d6877 53adab2fe561abc0d9388a7ec8f7e432224c8b8b7679534433f593e4347a0f7177ee7dc4fa689f13 4ef81513d8faa3c05fe582c40b23e1bb0b707ddcb1d9777d10b0a9d67acc18697c4fcfca7540e757 8029d4f0a05640f7b565019d67deb1d49044c2b7a632c1997bc617caabd1db6e67db272b7deaff70 e8125be143ad170d025555896f70b3521296b69ca7dd6ab13a9ab52467771d897b39bf140babc25e 68f1c52b7feda92fbe536ea4b9c7b20573323724d9f76dcfb39af95699c912b5982c576ad2b0541f 166a83f586da42d49b22cd01453696fc77714bd2363c23697df6241b6311211b6d1aee4fbc02d3ea 4af3bcb7e6c34fade737cf95bcd6591b85f12ad285151eaaeaabdd56329d7ca3b8a43bb6e4e27255 6c14654f68d51a7dfe7a9b4c79d1dcacb9fef1fa9d053879883ed811145f06401e976522aa8233d9 a6cbd056aa5f2cac8ca559c08ea93ae5b14248fa627c857e0e770f824fa904de352e262ee556112e be2b77bc5bfcc0f8ad01c1bd2f2f6b868b0876eb45eb9bce5355dbeaa98c46eebac4654691fabe83 50c9a64a41b156d9d72512afd8c2398f55f9cef556e3428aeeb09f813166f5546dc1446e6bc7c473 fa859e87cb276d599f7461b581e0024ec224e585a2401e41d2eb9b649ca14304fb6b17ef5ed039f6 129c2ba6f4df283a06ed12aa4fec31aa77e12b3ada86390c60f37cd766f39caf5f03d8692ecd8f65 648a6793cc196b5dac04910a499750865db52d91c1a42e704beceb52e59e9383c57e0e618589d651 9dc909cf3e6dcde1a8b046c855c1be18476a2718578a9ab96ff258196549e6bcc589367f63f17b3f abe04558ae622a5e6ba1e3fa3242d367e28c94f97e1e5ef459154601f85a7be12ab33ac38baa9e45 720b3bd7a14a0fae51367cc4166efca752a914ce06ddd4369a7cf6a7f2bcba08a56d58680be75dbf c129c6a1caa695a0422f04c92cac2b528dda8dfc365548770764b31e4d49b670f82e6e89f6f079c4 1fd0e786f7767c0a7b17943ca6b62c129d40a18866cc8389e4ab9f3a5cbd19a3fc465a1ef2c450cc e60add89943d159b9d5ca1a11f72f5d91dcaedb3b94cd096975c1d0d6db4269b2c286341e6fbbca7 7e9bd01b15ba2b5171b51984a23f7a077cf7ed36d874b8aad18bb66715081a31c9e6766d109ddccb c5fb58ba8dbdbbf8001b4ad2144b2dac0d6aa0f611cd6cc2075211d710bc0c2f088ca1389b775de5 6bb2c8357ccfce9e2edb5ea6d3ff2cd3fdb1f6843e995b011a867d1b7abf7acbb45ca01ee97e59ff b48ac184771b0e8324d12c26954e5db45716defcc5cbdbf8a3cda998d660f2c8dc2e38333d393c5b d8ddc4fbd5aa864164a98ccc41d94360a71ec02b623c80b1c634cabb9fe33a77d0efc79c9f411ed9 7343cd64babc80a6c36289853ec39e0e41d1ce4b99796a08e2d5e111c03b1e01f00c3262b97ed7d0 a0f2740ea9ac7b7ff946a1c239dc0c47936896d2853a5d128bf90f326f5ace80eff5ae1db6d495fd c246e8dae4694459f833b73031c855757815ec95dce1d42f6583686867bac7899f7eb1b3af5f20ad 74364368f43ccda0b4f2dea6a6a9c2152cb2220088554362b90a00a956ab00a9a5c358a223406c0f 05886354006208538088d3334024f0aeaf38994f2cf165bcbf047aafbab92a657dfbe5d0093217da ebd2801be64f5d7ad9c47c8a963d8710a7b92a064d1c035e5d52f122feed16d303eb26a5ca95ae02 aa76580628be75014a648358b4612cc3652cef13404903c4b2c4004a0989a30ba51636400b7afc2e 1a3ac7722000cacc6a004507f12f61cc13a0503debfae187b3a6e602359a41076829b17591d1de7c 2b9ebcdc9cef0bea90cd42936e81749e3ed1d54d17d3abc72abc6658e3bba7358b1335adfa372195 ef7b3c40579e0cd0f3d902e805ab030cd4bab1cc26004ba5d6b194aeb1cc20804104194b4f0118ce c4efc3b711c008f70eb02c4d012c87c65ff39ddbc63285926f730698dd730e7c79833651fdd17353 cacc77af45321fed444ec2971c24b3231a5bb85d9297b74d4c3fe12ebc0eeb562e3e491ae9d4002e 82e53e140056e98a006b9c75808564359eff1a4d801df649a725ec45cc62a9ee009edb3d009e6773 b10c18807dd23ac0e1b00970849bc7f278033c3564010eb57d80dd8313c01e12124bc4574b9f2e6f d26415d386a70a24af90c65d3a14ce475e01f7af1f9a41f0fc98bc18720f1bc7a72f78431eddec75 56b0a0c9ab1297f54d1ec0c9ac0670832e01dcd5ca00ef741c804f4e2d80df8a234040bd2520b0d7 0910bcf28e658a0042c0f958c20a2044b293006271b90684e442802814c45f0664247b8de5490302 ade895dace164a5d46c5d5a856cbc8b6b27c0b77687e6273fdc58a6a364f93c45d0e6fb6522bdbcd d4ddd47cd030e39a4403e0d75642a0087669ff62becdb7f7db855cee02e23d1e0392c6b78094ed2b 202bbb1420438588652503b249dbb1cc4240b64a074006995c2c470d90cea80f4877f8fc157f5c26 be1e28d36ff182aeb45552cd699dbcd424fa1faea4daa74223dd5ce3c3793f42b0e536cc4acea595 aa526f0724a9c7c43053ff85a07be3de2fe37162f24dbf86bfa86f499a01aa1dee120e3dc93e0175 f572a080c6a354e06a7a2c8f0628e0d5092810b96b2c6b1c14b25e7c919e33c6a090375280fa944b 803adf5b860029451d5c06acec74fb68f2183ebd799a5722d44a5bc4de57be3e9b6cafe90d5268af d4fcabb61fdc9bd0e7c4769c70d57f874d7cd31d7eecbec337a0eb3406e8e95000f4a35089d79e8b 0e60b2fa1ad0cfa4dd3ba05f210be893e102fa232c017d6161405ff1725cd279587ad4a2925ac9f4 f8244c84b50a7a9abc97a4075a6db287ec634a2e52987d0fff2aeca7a63fd65eaf734f3ee60ff3fd 1331f12fa7af7c2200ebab12605f8807386a3e065c413b038e8610c0c143157088d3055c4a3a03f6 865380c3e30b0e0e5f4ef4e16df57d5c4126cf6d89d7e715aad0ecf2396c16e0ef3c37cf9f5376d8 9dffb269ffe1d05f17f4bfe384ffb490fbe3ef4de2847fb3de9fee715fdcfb13a2706800a1325900 21acbe8130209858ee0e1086f33910562104844b4306225aed0091c3b73f30bad76babccc6a9b0c4 086550f89847a174eedd3fffda7cbffdd07f38f4b7b69f8a9262927e7609eefd1d26fcffbac73d68 ff5796c31f6b6fb3b901d2bb9e034558524051a1bb89b5b79a3f83e2304381e2f25203c5f368028a 17eefc3ff06992da9be053a3f63bbc81887eb7645bdf9e7274fc3cb4c512ba47e83277cfbab36f6b e45be530c1af2b66485db1f690bdb88fbe706e7c1ac5d3596f68c75bb9661ec565c7dd7fbc4e7b17 b1c3c9b6f27c9cd75e56c6573e32d37fc26ebf410d09e14d186a9234f0cf7c81ff6740de0a3f06e4 30d5683c46d956f76eac6bc3eb8ae6a28b7bcfcecff4e0bc3a7193d5f628cec3fd2144baa7fd88b0 2fbb88566edbca3d775f6fd5c563e5679baf6580b8dfbcce459770f3f390ee1466237e264f2339e5 44737d10afe6672af453dbd7c19ba4200c6e1d379380d4ff8a7947e9d1fb36c79ab92bd6ac12673a 2c30276ecc7e4fd2c76ea664eeb5edb5bc59895a7975d06fe565908fac8518fff5794859ce6cc44a de0ceaf0fed47832416401a43b599590c1049be1d1985ce1ab918f4ba7615070c0e0d6da9103f16e 55c250a6a7fdcf68f3c5763f29085fbe9b58669374d9bfaafc9a3baf2b7270481e90df8f90e76cbd 2d528be5d9b86c165d64779a87c4f139d3ce3310cd8b6e7a62436276bc35456474a862d888de24b1 39c380ccd2839b0f7103f1fc11c3671b5643f9011bfd918c55fbd0886bf4b253aed3b5f2eeb4b3aa 6d4e1d6ca7c1ed6d83adb4c9f37df2d32bcc17d2ab4412abec9ff66b89fff4374f4bd0df4c3b8e94 080eadf2d8cb48ce885e51fe30c0a5dee0563727e1b3a5ccfbf1a978dd8706f8ae67a69063776ee4 2e5d789ebe776c04bc3ad83a9f6a7b4426131c1a7924f069966a9d83e2173fb4b87b556b768b03db 7f0eef5d5f499b9bc6a80232499c6c035ab77aff92784b779fc918aeddf8a22bf19ffec325cbc1bd 48260bddb94e8b5d782a7fafd63a76deb0dadb5ac50d0e9ee607f4516bb70256eb376f1d79d8ec8a dac47f86f2cc5780b86c7cc6c6aea1678c53039a351e75130e216f6ecf110fdedd18d7a669ddc5ce b396e309c2ca219f02661fc2d73794e15f46d98b7b613749e7b5045125282da17e3f16540fb18f01 bdb79f2dee5c4f37c57b1df1c3629bf2e5f742688cb485dc80a275a96ee616656f5e9d563d0b5d7f 9fc57557eea2e1dae42e70b6feaee79097ddd03eb45f339b7ea637b540a12e351e38a96ab734c5ad e71c51acd08a9a9682babbca676be03f06d991b7911349b22f92811c9eabcb7c2f2a1af724300351 b62deed49ef9a194193574f098d5cd6c66ef5948e6eaae1cf4edda849871b60d1971c8b34cd83e2f d3b5734fe46a4151936adcbba256bb9a51aa4a906b59a1e93a9692eb372ba3eaac5fd1d1c7b7cb6f 39f2d05d397b2c3dcd79738f99f0ada81aab5eae67d8c5dbcdb0651f35b04f4825b2f2c1a39a6cee 3ff8f4c7200b8d1b3d0f5e9e9b2eb68d5a0e798cfa367d5d4d6b8178de24d90fe1ed5895c0f3663d 27d9b7a5647350e533a7f2151d89af74a0354396234762cad9bd2498f386a998f0d9328c55103a06 769fb74a5ef136d40f2372adfb7ae7191fa710ae05955959e39176a4de36f8d76411ff6c67d5aeeb 64661f6f250f6e6e2dd35919f363c2a113c8eb6c3ddbb3e9cbbd5afb9a17abe2a76d5ba1be082a9f e92eace8f94bf4834fb3dbd7ce9cd77367133ea56fc6aa85bf0dec867ea7ced2b6cbe64ae4ab80e8 87814ae94caac469e7495bd5f8ecd452bbd6c5579e1b64a0846eb0933f47909275d6e764e8a6f9c5 a87b9d17cd62f1124bf91ec1fd1d9d8c66e01360156fe67edfd96e475f0e5d0bdaf79af5fc94cd78 47d92b15683154cad94ddf30e7eed53356fea965609747bfb4ede42725f2999febbe925feb0c20f6 5a5022ce1a9f61efea6d660055a26b59251c757045d1379c3cdaa734392d0bdf5567d11c35bbd222 7d5c4848437f8af6f942093bc2af09f52c378e657688e574497ce6494247b3db6acf3debd11ad42e 8b6ecb522b17b73c9d162b66959d69869315250357b252c973b2ba7ed8d35f98a233954a530bce8d 9e7ad7fb6355da4de6caab77da2a6afe7152c02a73974b3cfa913369235f2cd71ba4b4784f0409ad 3d0cd14d171b42235f0df953ebb8e5e36f95e23ac523c73e465d9f7dacb5652c9bef2e10ff3c3d92 d9a8bd0de3296f6c56174efdd31a576576d52bcfd6c03736a7925d6ab8a7b2de522a9a76d53f92d6 aeaf8aeafd0c596ad1e61aca90ef844a6a3a8964a3b0591567c3d3be58b1335769b9cb7cefb04b58 9184447729c22259730b02dd6d897ceb352971b75aaace895b2164e5637dcd8cb4781504456d9636 f34a8336e1ec3296ce838ee4fe2381e6411304a77af9d5ded89cde8a2cc88dfa26b6bb3413dbf54d 70b42e332cab2121eacaa7789395e1725e5420ec5696b365ac51ac9ce49eb4d22a6309dbba73d1a3 1a1b917c4d4e825f5ddef9f366f3e179f199e36e2f8c605f28cdb18a6b68cce8397498b4f2f8baed e89c515816acc3f44dadd92243e1eedda5f0ee6a41d93be391483f9d5e7e5a423bba79185bdf569f 3d775a9e8741dfa08ac79676a70aaeaab4879692a6c9926c6a5ba538effa92b41af8ba84a3dbefd4 2952c62d10fc637620304d32e2db2971c5dd5d6dcff548f7cabec2ce9b55419866c6ce1663d2fb1b 439715522d2cc6159b5acf3a1d8a60524bb2deae3e8966ff4a112cd3ab112c6bcf88660bbb13cd1a fc9d3abb8b6204fcf73ebc3b07a3b4ad827c6d66da6babaf5f6ad3963a5033ae929e35ace2c2860d 694d05b2b853154938babc223007afc60b48d7e77aed798f7d3d562376307bced91493dd32a576f6 c8644ae277714b97677aaab0a47da48006618172eedb22d980218b38d97240b0bbe10c17cea91bd6 e75b38faeeb315acf864a6589fdc5eb17e767beb6cf2e74c63f2f1ee89cfdc4ad7b4995117cc50bb dfc681321e3fbe83565c9c0d4b22a443492cb0b6c25f9e69917bc047912bb67306abce189799d07a c064467648cff46042e777a3656159dfee0be8fb76a55cedf326f73b2a47d20589205a435fc43bd1 c8c49eb9a78fc935f96bbb46356f784626270e45b2dcc9408ce03241266bef8c4220bcb51bda365b 9fdbf6a376578afbef2276566af6a5501dbc9c965cde6f5cc9b99315b1b0d81a7c27cbc85cf17c14 592ddde319a33855e94aedf29d050acbedab51a8f5a82eb5cd4b238aac4953f2c0791b92eefa4722 c88d1ef8adba8670717347b150a23874a42a3a0a4dc23a12af7487b0856ac7fccabdc179ecd8d2f3 76a13dcaaf2ad41186fbfa35e0bcd13789dcb3ebfcbdfa1a17f6e6fa05cff40ecc0f94895a6a156b f4c4151b65cce2af9b6e89fd80b4cc18f5b640579665be50bba912e5e51c933c34e3533b7d1d3789 f3e4d227f8ec6584779b608e3dafb92da618ccf7c60a3a9a2b6f34cd34f388d99e16e0f9e3154fc0 08efe471a71be6bc43769ff5d776bcf6da7f8a59c6ccf7b37eb4d8650fb7d7b9f91cf859d77b330f 6bbccfec8dbdf299697d1b1fc8b389d092dca0e30aad256425773c9e768989f08b5c58d5ca02e599 2847fafe9525824d46c1bbcf8285bd14d6c594b5dac280e3f451fdd19e205379b04472abf501aee2 977b7eddc84079fca312b942da11b3cdf2da4adfb7bb56ba58e0bfde4168d05abd20b0d31948250c 1f1a18a919f49a540ffee86ae71c16cb3d2ab3c2635f3aad9f7355f3ee617145b281484f6b2e2f5e cf163bb22c838e8f2b99f2ba824032fe9bc37bc290c1c0d9e5d14cbaf37dac1429d76716bc382d5d 18e5f6adfc269d09f344198d72f56561953d71a553a6fd529fe97eaa9281dec6908252d65e4e9536 f1f27f46f68620dfc4cf20df02647c649f1d90b76b433003a96d23779072b58709be3374b9563b1d f4ce633b578cd76520794caa25f079cde514716531a6c3940af672aa904daa28e1f7f595c7527085 46f27588cabbec87cf354c4ccf9e16642573ddc076fa71c836e29d05eb40c3b13882a08cf17d3026 65541a07305bcd5e00c69f790013ae006032538b653c043065df005c900a0086e93ac87f66130083 fcd673d6482e4933370e527faf29f0682e23e862f0c3cba596ecb17a38b268eb417d21245580062a 21a88484a9b3198f547099c993ec8acc74814242da07d029339d9652d929a5032bcf5b005e9a5e2c 5117c02b288ac5d8c5b27c02f822a3b1ec8b00be965d006f5e63006fe7c9053bbcebb2009eb55a00 9e130b0087f5b373da837c62892f05cdd6414df3fe5cda89bdc10f32576eb4c74cad792da1fa44fb e5a89896794848c56ef079b2f9a1335d45c7a1747e8782f83049424e11ef2502a4cee8b1d8d558a2 0640fa70184b651ecbf208909006b1f471800c082596691d204373069011f90148f089ff40fbdd05 883bd8c77f2ffdaa3d5beb7c19979b2fede51adfa7bee545dd5e88c7797b9818e1d971243668b414 d448f67631b14faaa42195d54cca93279acb48964f41e906c000721ba00085ee3440714602281f98 00d50f0e401d328825dedd5067b904a84b5c1272ebb620807a292a96a60ed03ad28c6515bfa111c4 ff61193240aba510a01a7e8da597b5a297934b7ce6ea782e1f8bce50590857a639e2dec56b3731c2 53346dd9b8bc589491b98f6a49fc47b9554cbffc8849952b3809922e62e80a2f00f4acf300fd7415 80c14f0b600c570798d2ea02cc3a4d7ebb907b7780b5323980d51c166041ba1c4b18bfa52def00e6 7cb20073afa504109bbb31c0ca6b00309921caeb8d9ad77b6f229ed9c6d8516a70fa4ae828d38835 b44d8f3a08371f7fee780759649d72eed8db68092a074b59a2e372f4f8b31aa1f8037cb1fd410678 164e3e0627b52ac0c54913e05e7e00f06e790ef0edee08f01df3896580017c961663096a009f93f1 5be6fb13c0072d14e0c36a05e0813e03b863e663b9093f39e1f151f99631813cff8e66e146997a44 63af7e9fe87eb62d3477bd3bb993c056d2a923178f5725107e9525623a48bc9fff312003029dd400 a15e3d40d4880010c3e60810cbfb1a101ffd0a487a970624c3274e481219a98044e3439c4c2d2340 62ce1d90e90205c84cce01c4e313ffd20b2600719c94f4a79341d46c44a6646cdd78081265ee186b 559e91026984e8ac1004b9d365e04291d437ff14f687437f712ff1167e73d572b3f38fc4895d08a8 0c12014af0b68072a117a0261e0ca81bc4036a5b2bc7f26e036ada5c016ac60240f55f02a0c2f8ea 8e6a4e92b404aad62701653bb59f3091f9ad929598bef1e4d257e940b5f2ec02331c7a98f725bd03 4df182fb6ba8120e9df8a1ff587b13bbf19f9c89dfc037711027ccf74b5a0b2ff16bf21dc6d35899 237ef7912bb9b1dc47809e7b27400f7018d0cd8306e8d6a40f68bb7903b453e7006d984d6dec38b4 4c5035981fe2dc9b2615ea8c8f3cf47bdf337fd49ee374865eb77f0dda3f39f49f88899ff6767f22 26bec097be9ddebf28e69f667209eb55523460c3b109b89c95dc8a605fb72560df9d0f60cf1c03d8 d5c306ec7a3d07ec66000136f255c0f6b4de5f301a3ef11029cd7237c419bf77e9c92498fcda7cdf 41fb1787fea2e73fdde3fe62cefff4f7268435f1f74ea6d22fccfac37abd2a10607908042b7f0682 324380a03a3a1038aa0b04ec13bf96de92807ff47fe274b3e5c95f30fadb6111abf0e957f619cc0e bf46eecbedffe2d009114f38f43f037b7fc07312e3f01fd6fbcd0f4e3ee65f9dda92d4de1fd2abce 80f4e63fb1bc58207d561e2882ce12145137038a5a5503459befc672defe0fcc9bb438db0a095255 c54a2c7dfff9d452c3df5dd906a76708ed9e0f6dd17bdf8d4d23759b936ee606fbb5fcb576a9a017 f721e2e783cc51677ac47dfb9d91e2515c62ea41de62e53dd484dd6de50977d75e865e253dbd56f3 cce2d6a08b7fc26e9360d924732061a8ffa3d4a47b5c12de90d51e1f03ab3eb4b9dcbc1b6bb977ad c5555ec8ee767c3aabe13441aa597376081169b91f11f87a17d160bbaddc16bbf5562def5607a3f8 1380bbe812a9fb3ca4b3a9d988c790a9f12699f8f2b2519ed8f9cc60bcad1d5f7f726e13ab6c4251 1380fa5f2326fe84225c9ee78370ca9e5a9f3d7e789687f4415e2ea55db6a9c96bf7332f2ecf655b 59885b5c8d4f6f409f6997a331351e6333aa7c9a95c94a6fda136c5aff36421b7bf9666be4634e77 78f69aa3c1add95b0ec4ebf112ca2f34d31fa9d3621f8a827e5c1677490210925e61894136a1a97f 55f765a849a0ec8f5576c5ed82e5d928874906b1838e67233ab3984101b19f667be865824d96b731 391f3e7ff70fb3a14197b6b2e13330e130e42dacffe999645f7b9b4c2fd22cb1978d6cb56be5fc72 67551d79edadbbedb5c923b60c7c76f849b254bb86d80a8a6aef4fafb01fb7ec3753f6afde66df3c 84e469eeb9bc3d2b09061a1dca556e18608e36e852752b0cd9bad7ff74da415f7b0ec29e099aa3ee bcd4fcee029d55a5bae860abf2baede1a5dd4fb0ac5f285d5ae756e9d1e26ea54fb32b5532fe73e0 a2be92ea338d91b1571bd00275ea26da1bd7b35be5e1cdeba6e25934d4f2e0d3ad9948e2924d126f bf1cfacb26175d582e2505fe30d46f506bcf78d7de9d9569e6dadbaa89b7c98dc7063e191403fa10 955adc25ac34c5875ff343d9f71a9f91f7cb2a0b4dbd6e3d2a7787f5ec32987816167c2f3c3c781b 6d5d9bda5e9c6df3f676c81b0edbbe64b236fdba59b540ab0d6a7cda4d556fb3a2fc2f97ecb10b89 93a4c4b1972ee23f31b2f0e470fa09194800eab3eb7c4f048dcfa0776de8a9f1bb1e19cb4c3d3bdf a3de3c5e607bf0fac1fe4e1ab8ca8e47bf75fb103c4cdb1752d5dab99f726bdc27e5576f23a25b95 d2f1754e58561795cfb279aa8c6ad75405da150be5a87ef9ba54cb263d9a94e3ebc58c39ef90dc1f 599303c2fe4992fd7634eb60737fdfbcb59ce98f41363ba3dbeeaaf6ec38db3a3476c813b6b47d8e dedbf44dbbd6ce5df75de35e1e54edaadef71976eb39b1112b347ca2f2997b85cac80ab80ab40ea4 72e48cb472761f95cd79e3ec9af0056a1bb6408f0decd1d8963cf9f42e51c0a5f5c3847574dfc437 3a93db6574260f904412769a20b4644ffb0f3e8d4a95ae8b6d83baedb3e95a2d101656b52b4f5deb 39ba742c25fd1a56461530ade830be2e4736be2f9b387f31e775e66e5a05e96dac5a42cab0b952ae b4edaa48c9931c4a3f0ceadfe68ebaaf8e15ed3c5997353e071aea6da10f55090d374ae8214051a8 152f7f4ecbaefc393b7b7914ecde8924a3d9d7ee9d6740ef2ecb844327843781bb716105bd323297 7239aaf6bfaea1727633299b16b9ff0f3ebdf64adb4e665cf24462ae1f4274a5fb4a61a79dc7c451 0b4ac24dbdcd8497daad1890f25c55f28a823748f9b39b09b25eb8968a514078c5ecdd1948f33e38 48f0a7fd75428aabb1a489b691edc7d2d9c4b23c8ee8c5094e463301faae8d8d1a4995954f74fa72 5d8e37e1fd8d316c3a2a943c7ec4eb87de56d3e9f7b9a29d4790abf169c8576f53aca34a79ec3ba7 29610d1e290a46cfe4cf565ecb3a25ef8b51533e174dd67d49f34e9096e0678489b672e684ed042b 0954b6d6e00ff347c433a873e7022f43723cb5ab717cc11ab0b7537796c44527e6ed64837f6facf8 83789650564ec55ca813d58817e3a27ed4478c7629308416b400aede26f7822ae572921256114dfe 6cf0b2ac93942d434fdd2f9a8a12147388174a563b98484869be14edd96a27ece8db55a0c6c8f7be 27dfccf0307769f82cc79f573adb2b910de6356b4d1815c9dfe8b13324e81259add2e9e7bd479728 65d6877aee2339c43d44e6bad5fb84a99767e97acd20362753672f9ca23eea7341796338a5a8e2fa fbe8a202c2012eebe33a559c128b6231d73f96a42afcaa8aeb76da139d121308bbbdd417ea7d2fe2 4f70b0e4d9203a701dfcf6601f7d24cdcab04e301a1e1f5293deaa4c673e585080d3b539552bbf5f e466d5f93ef54dba4ec92337ebd788dc6cf86572d3c90f2375eaec465cdf1a8a7acbacd5dbaede9a 4f4cb5ffc05405e27c41ce523853ac286b525a3a5554aa3e1c4adc54a64591381f0da1a1bf6afc69 9f6bf09c8c7f1b08729d151372a2ad476cff6eaf984f393830da7274a723ee08d1d95e1a2bc0cbf8 22c5c63c93f4a8c8270e2d6c4ad0b7e1030f8a6201e73e79170fe4f5040f14b04c26a5f8538499dd c2e561c5f0a56f7b2a831c565d4d02fdb2a2e73f6a71eed9a264b36956f4326d5238d02c2ad00181 f3ad7b91e36ee5bac689a75e850db5a9cb7cb6eb2633f2ee3d067a83116d7e63ffe65b6a5bb0bad6 95b207f687dca1d3ef1d769272ef2cc134111d0fae561deb65b713f4a58a575499dc485489f60eaa 4cab2334bc4f174902773dcf16e635b1cc8dca16e0dbfa795b7455a5d428cb39eea04af640118563 66cdf0e74f99e4793c93849c4ad20666c3658a8eaf4e0b32a3778b063dcd956b74ae596f14166caf 5d4026d301e5641711b9f38f2b92babc8e44d3a05ef8655eca61f74bb38015859d8aaa4ace45c663 6f8464b2d0052e5b210ee751f7bb8686cb1a3a80a7c3faa2d5269483875e8babc46a6eda25bc9d20 7345dfbb9522126d3591da5022dfa6070cd7e3558a1de44e28335627083d353a389d3bec85425579 6bd47a93af5004893a643d2c7c6f13910545ed12cd7575845f89fa1c17fae33dd6fbeceee8dbc967 d054a34020a5b353842be9b59d5f5698416ea3778f3962c62039624e5a39e7b9e9e736123b6ff666 fac939648995a573f0d8a01aa0adbe9af92f87967367b12cee165d5560455ce27aa31ecba6f27992 9e6e866861f1acc0d40611118a68d768b2fee8148953655222d8f3a68a77f88d8b8b995480f52bf9 107d9fd929aaf1ea261ee5e6159e2dfb0086b1fd77dbe4b1222ae6dca153cd36a2752fc369e94dba 13f5d269b16ca9e9eb830b329c308b7c0dd62ff6d947d7959c781e9598d3a5ada65219af88f26459 38a19eca3de4bbc8aad332c3645a19aa507d36508ab8707062bb4e7f72044743382eea248ff577a2 827e0ab2810edf0d1b85b4560331b6c30e3ca76643181e5c17791bca1c725e8e78660fcd5a3e1388 2d26dd55963a141a9946ea336fcf537a2af74ae9505a4a8db8d177d0521f9b0c1b595dbad6baa3f7 ba8ccc0f635de0d66d25537a7812d1cb96f9ab6baaec7bb41319235f640acbce9e24f7170d25b8fa 338f3d6ba32cfae90679148ad7f388c96e0578de392830927d9a79bb0a7d6fe1e57194f4735e47ee 65e315d738c33bda2adddd37ce5078ee7f20c0dfb0545ac644608e5a36c8958831c8099707c88947 01e4282b7e8d3a74bd551dbf5695dd7e6dec2ed1582b468b4e715999b962e195340ae01e074965a1 f454a42b2d81a5b6d90d4504191ac5c5f70d46475d3f8be4581ccaaf491cced5014ee5a8b52e649b 84a566f8335f4af778a50abda6760352f3413735ae451330f5566b903b7eae20f70e9206e9b94f5c 6aee3c7140ee528e40eecabc416e8b48b16c1b2037c54297aabeae56fa3d59974ea5d1444d1d7b1d 69739e78fc0ddd96d98fc26b4cb6dc130bb51bc192076b42e1dd068961005bc188b930bf2b9bbc43 bca06c3318a7d3c5562907a9dd32911abfaa4c2aa33a22284f9a1ac8bbeb2ac87b4833167b18cb61 0df275f10ef2bd0302f28d784acc37de1ec8fbd339c857eb2990af9932c8db741be4f5d6f7716cbb 6dacaf499ab92ed2d6589ea79c8ed8b8bb2ef774a3320b8d718db6f23d89da76098e08da2d0a5346 108694735d38bfde48b96c73354e41ef210b5299e73b0d603485c4221462a98bb1ac139e0663393b 16330030b71a0398a777b1f45f0016e2ef0d0b1305c062a9096009590398ba64015cd896e2e3ae13 0218c9acab6f6b7c3349aab6513fba342edaa4da118272cd633fcf5185314bf8d7cb51589dc67202 f6b157bf5640a6d10dcf6f500fc9b6e857167abf5500e6d9e31bc083545cd3a2187ff4b1c7c67294 01fca6cab1d85e2cab2e803fd83496c6012008046269510041d1e4db20a9691b20901d7f873b0f03 f8c159003ee4e3f71f9a272b4b38b752808a1bc52c226389520a1dbe7baad47f90f9228397286a5b 97f15efbc4a3199d2be437cd15916d853c028df2f534987bf9a4431942e5530031cb3040bc010990 ee4b00c894d700b20baa00b99f7d803ce83096d62296f70520cf5afc6bcf2b0d9057bd0c9037d507 c8e97004c8794e00643d740032e96d001221af3276166e4953d443762323adf444b838990e37d08c 069b9e1fec0201a70d42681665d480fb7c7ef3ced2995bbc2487a0f7201717c6a601b2ade7018a4d 5180723b1ca07a9a0668a04b001d452640571f0760c06827e416594e0086a2db58ea0f80a5ae7980 4155016069508b653402e843bb02f42932003d167c802ea9532cbb7ce9524eddd4f4f9b995769b57 2408a5429f1b0c3b2d1a75429bb846471383c4c2770d0d63bb8698e9cec3426a5a7c61008506485c 1120017a667e335fd9e37fc1d5fe4105d81cab00ecd9f2019e3df5012e15e70037a72780975100f0 621d07b89cfe662007f1acc1d0d3041033ef37c0d1250f7028de19b0fbf609b0874d6a6f747393ab bbd5360932e71f4e69c8ccbc5a409e61dbc146d54505c6f5939a29669f7c2a5fe10bbf0aa32dee97 f138f35113ae9a7c4ce2f3fd0bfcce1c40709916206af5312086f70d20cee63d9675161017e9dbf2 6e6e026223b70131b9ae01d1eda701d173144034cc101096f6fae6ee0aaa212e1fc5cd2e89d3154f 377dcd697d755c20af5c1b577a8a073b4af91b285d5341ade1c7e3d58fa45fa6e86f393f4835f11c 27c0f71be8f06fea9bd87df7e92849174ec8ad91fd00aad3c501d5cd1601e5b51c4095f34310af76 cf80a2e3f1a618c604149a1d030a8252804aa7b49f30112fc35df9e782d9d0eb343a218a4cb68ba0 11d2c8f4cf190bd8061c17d6d1cc9f5c8e6f6d5f2bf45fb6e3c471fca7c7ddff67bebf80efb7991c 9d9b43802e571840f75326a01bf500d0567a016865f40634ab3389e71682dd581e4b50b89ff2a0f0 d89515ab2b67c48b2c3ed8b298db91d75a6a86c2c5433fdb6d1f9bc0413bd6afc2120efd87f5fe69 6bf7c7644c19c9fd9b7fe3de24d3e13f98f5c755fb1f572ffb1a8d017be2ce805d9e11c08e3a3a60 9d620fb0462e7e4d7c15002bad6c99c819795edd61afa409e437573df2378bdca5bb1aa5aa77a7f5 b3057f1cda49c444c2a113049dd0d4c469fc1fe6fc57a6c4bfb21cfeb8697fa7f67e5dbd029d0d80 304fed8010c6bba750650520a8f73a10b0700984b415bf961115c0ef1f81d899c828b36ae129e255 7e5de1fa7eb681664f73fcf7c8251113091b8f8728e1d0c9d8544ef8ff2ce6779cc43f527bff1dde f0edcb56cc830b28c69b041461bf0c8a883106c51491245948d7070ba4edd403d2ce9eff0f7c9ab8 6423f61fd9b7a7def3a915a67f5cbd8f8f397ede23d479dfe6a4066eb02fa7af589bfcae052e641f ca9f1b1f083b05d08d3c76735bfa0b0384bd761829dbb9e09bebad6ed757f4321c2f6e75f2390fb9 159fc4de2656d92450360c952cf8572baeff6aed4d0cc8b7e26394391b0fc8829cbbb126836bedb4 ed9c1b2fbb77e2267c78785ae9e17e84efc6bba8308eb6955b75badeaa9968e567f6b365004f168b 2ededfc41371e7381b719d9fecdbf863222b1561136cfe51c6e4c66d8de8a37a4d321a12076f2f2a 57ee09434d9206fe5ba909a2bc6c99e639c9be3d8a5331b3ffd4486417515972537b848504774f29 7ad1458fcc5c3e0cb89976698ad36cbf2845958fa04cec745e1d7bb9873e3a541fe5e1d9dd5787dc e15a1f88976b3b0cc5cca83f520aeb5e54729ebdec3c4f772d14b4e2c2b687ceca5d1d13b89bb854 dfbd59b3ab95a3bf0a4c626fbf0fc8ef21c7ea261dafe64fdb7566504b6e4db35d2d8c2aafda74b2 525b9b31391777c300ff1c06ddc2f91c3e5b875b28dfb6cffe485a7dfad0600125a10ca9f8f7e6c6 14e9c2f329d1b19105d3de3ac7629b3ca4cb017dc1ebad40b087cd5bff736e4a2022fdd098347f7a 858dac65588f9c5ab79edd73ed9f3084afddf807a07e9330a3b99834741a93d30236b8b9c36f64eb 84ed7fda1bb90ff53746cff89c6addb9fe687456e553d0c196a75edbc3f683e0e06dc6017d5c4e5b 013b5b346f9dc9ba293e873b3f5426f13e3b9e3d1a7a669baa47952752cfae493679f09fa895dc55 e3d176b14b7bef6c3b53cef124d375c897e4fcb86419f0aafeb864bf9912d388d585ffdf8acbdf07 07c7beb6ce8d76aac59dc770b3cb2fc8a678bf7f1dc4befcde498d91b6531b50b433ea666e5df1e6 d545cdb3d09de7aedcb5ef6287c5d72a7bec3be4e53eb17d112c6be7103fd678a0bfabdd520fa94a 595cfeed925dde2a9fed90fb13239b3cf8ef6de4a46158e2e51d74492d9d149898267f42063ee17e 508f4a9da137af4c961ebcda1c5c1bdf5f5d6c9ffa38db069671c83389d83e8fe1b5730fa66a4191 64abb7212654bb1a2d274835a2bf6103f179e9b3d09c8a8e569ae5c86d85e5ec61b930e74df462c2 d746ce5875d1a281bd8e1d03fb40efd276e4937fe4e7c9fdaf9737813e49ce6de2eb4c0cb2df7c01 8fd25ddb6773762d10737ef51632fdaa04a489f51c5b4b2b2c057b4bc98c2e955165f4a840abf05d 36f15eaa9cdd8db3a65518c0267c5a1086cd2d6803bb5dc49227bd34fd3044aabaaf992ded1c85e3 245f20f739a8b76537a7766d4d5325a214a912c97c6229a513498225128af6079f2684b7766eefac 1f7cfaa593156831d3ca26bab7cad96da6615a54be6dc247f61bcd62ac9a6a6460577551f2c4e2ba 443e4b7bfd1056cf3a03ca37ed3caebf353e135ff0dde20b695582979412da2741feecf3863c6a94 eb32749e0e8ba6401d8bd9c70595e6e1d991e683d9429a0f0bd74412e73d3acc75ec6c78f0e5e763 f8173e4df8ae896ac20f3ec5ce23bae4f191a01f7a07fd173e1d7e1c2dd0b1a6c6a7e94e7ca14787 aa9417c74a58e3a68a82c94b79e42521a73274d44ec5a8e93e8ad96b90922c314224f8f961455be5 74613b69d505cfcc4cf8c3a27de3fd5a99e17ddb6af10c7e9bf30c01ad9283a775aeaf1709d04f06 321943d32221362ecc2412dc5dcca15aa02ef2ea6d32455529bb8887c5ba888a8202551eb9c09075 12a916231f718b2643f9d2bc8d079225f07d71d5e747a22deb33613bb23682a777cffc613a7ff34c fe9ee7029b62d8dbcefe2e6ed96efdd9649e6777c9846d2ecd28625ea33fcfb61fcb6b98c47424f1 17c9364f36b739df68f10e351ad15ab04c63ca8bace7140032297964ef3e32b43b658a66e18d49f3 2055902c1ee513dcdd438ba25d8c2790ed902d0b9e56acf187a8e8f1be598e278d45b5c705d5f698 bd6dc64b56227747266c821703522a42ebc6205e1d2cd2d5422e6587d43cc2ce94653e290ac9d74a e46ab9a927b350920f1d1f44f1d485d8157d9a2f19f881fbfa767d4619742ef1f5c3a5982be67657 48b2decdb784d4ca6fd1f63820e2f14a4cf0aa159c3f5c1b0ccf181d91bb903395e3c38dc9de956b 8d9536a9063310d1ef8d7c068485213db69c6561ead40f85dc63fda6aa4a1e251da72412bbfda246 1418668037dbab33ce923e895d5e593d16d74be6a378028283ea5db8dbe5dc3b65e827cb5095f732 e0e5cce14e4935a1fc055d2271ba6685c2b691e28fafcc836f5aef17cf5c3e29ae6dd0287b3fc805 b6a8540466b07615466d0f4d7a9299d5e8d233d52ccc2ca25fc85f8a11853ed435e9569a17627fde 43f8e99122b0eb872d62a23ef81228b43fc327c87015de11cdb15964b8c1ac581aad849bbb76eb19 5883c6d13136b57c45ebb439559edc2c5e5a21c742c275f9d67696e53a0b2dc53e539937db77360f e6bd3fa798a18fa10c04c46f90266d389a50983d6a6a01966b06b582873685d99306e9de371de250 7e8f097a955fe2dc9d3d61dd62f583865a1f453ed14344a0a550834d6c31cccfebfa356fd10c13cb c1cacf2d39487650a78e7dbedba63251d28dd2fe96b1547906abc5b9690aa2274c0b7ca0e7514eca 0d73ac921221467b9cde0c54e93fe27570e355a8dcc30cb52a1f08caa62196dc8eb02249a5318df0 eb5c053f9f4407e7b54a0beb468def8d15f4454d66a8d23ced113dca3ce1694e83f30bb4c3e7112f 55cde1cd7298f56ee09c394a53324bd52d33ebed9ecdc6e8989fd8ac74e8243e733dc85e2d45a7b3 9ab45a4882c0c8758aeb466f940568edbbeaa4a303942acccf937701c12b0f0a2fd277d21b4a8038 623e4a30dd2e8507af90c3f9c541c17aeccd405f9d4fedffd87baf4547d12c5bf7592620bc470821 2187bc43de3be4bd90bdd94f7fe0574446647655efaaeeca73b5f362ac0c85623135f19f06637209 8a77d84941edb2c42d3763a867654b17acc59d2a973f145973542d14367219c288af02788ff7dac6 068f4543947fa45fc6f0fb2e55aecdfd0bc9a2f17af6edb5707592ddfc361fb79bcf64b4baa52291 c8382b8787f283d7884e8e5296f10f2e9789e65baad1fa436c843f9ed0ce3c5ffcfd24537c2cac05 5fac70c3a9a57298948fb269a7946043d638c764276e852e1c762d6aab3e4794d8e657a4e25f2912 1d21fac11fb5ae80f947cb3896b8276b30550e0b485f1a1f98b25903c3f2f75cb5d07ecf0b71ff1c 99a9c69601ee4e0c36d37c2cef1d9346437c45f4181193b57478c1a9d4334ec9e5d41a931425f716 6e38f9e07bc7d98d1b817363a762ebcd86fa1b9ac93157915e95ee1a5d348418b5edcb294a62ac02 a90a568db89d23014cc19fb83dc1e376778b250bae07d31d4b0329570d2015a902247d7381641618 9050337db9e72be2e3ede6f1ce7a90764643c79a18a382e99fc9929176fd140927b28a7ffbdde973 0a67a994549b8f834b7531dc8cbff9d87177e7b08d7363168bc8855e5db43b55d9c770ff6d6596ac 9f6b52489f6535a24b15a2f8b3504be2f16d2f8f259f931accac530fc829b5f0a5750572c652beb8 7e4df3621dc8be123853801c60be381b0bc8865941a93759d71d0c52915dcd89cffbcd4254493809 bd775946b4f48a969595d4e46471c250e2393bc605c353df5cd25a3f58d2a87a74f1c25ec863757f 21ba9d86870f069de0fe0607e893582a3de260b6582840a90703284d4cfb52abf872ed0195b057be 5cee4025f32c50fa270e5464da00ca68ec8192120c50b26603450d1a4553582ced32b48689d72bed c40aeb5ce01a32c2d37c223c8a0c236a8e7bcbf2d62972523dcdd0c26dd5c2f9b82d7dd8ccc67dd2 eb5ccaa36481b812dd6df6884db4f015a845e8e14b1cf7a5e92fff7056813a8a315f0afe528f9b1a 5027193d2a4fbdc61ba0ded2d3175700ea93490175c5dabe9c4e406de72250b35611a839d32f4076 b44a9f0e967f51ce851d53dcab79c4cbb5e9bc6728abed5b9125adc88be7074ef303bc4ab033eb13 5846e8f5a2f5a4e494ec113dae71c2268e7204da085d804e275e409747a42f3701e84e58f7a56cf9 b2c903dd151abe3863a0e7f8de97fa07e8052303dd1f65801e64fb4037e42bd04d01c114ba443abe 7466391acf2c91253e9e8bd28e717e937984cc5597ae190a5f79ab52fd5a1484e8f9487329331f62 d8b4df38795d7a11b1f0fc8a4d9ed933d013daaff39abd03032300867d33884327120a30855e1498 c62b034c512ef9e2b47df166c0b47227604a171c98725103a642e781c92c47c0d8832730b196ffcf d4660f188ddbda152aba4a8c66af81b9536f4ea41bfe14102f0f0e9dccaa169577553a2c46c48cc8 8d3d97610a9816a2147589e1efc4f58e85dcf2d5ff45851b30cef001cc60f306e644fb25be622cb0 d45003360671606b76ee870539fbe9039bcb2c7d39dc1020ce27a99fb9188d32b045730e6c8a0560 0d3201ac444f802527d7b45e7e2e2d72791a469dd2a1a10fa6cf22b298ab2c3dffc1cbad8f2e31cb d39823cf7920f1f739f28120e30335cdddbc7ec2e880f976d324b0bbb65fa5b7148113281db89493 06ae7529013789b67de94c809b627be03ae9a72f371eb8aebf6d7035ca012eebae80cb0d48e0ac8e 0d5c78b8444665bf88a415dfad62a5e96468447ad3860e55bcacd99b7c5696bdb6c5bf430b9da57b a44ca9d1128f8f95b6df040af33f703516fa01a3f50dffc37d3ca6e41f16e48fa901af76512e079f f332c00f3235e08fdb3e08546c05fcab7701fe8ce1c0afca0af06bc27fcb7adc017e92dc033f5559 e0bb5c1ef85a64e7ff0282b6b24477652ae464fc4d31879d5e53ca7d33273c1ad98043b3b956c7a0 d4ed4ec10965c3fc5e5b50d65fbdc70fcffa3d71a293b441d8768a20628f06887a610262f3b50771 9c7f81d8dab32096cd28629de95d09c4686e0ca21cba82481d2410decb0a0817770fc26626c6b674 751d0d0fe6d348efc1f5917f5bd2537281b349234539e79c89130f53fe52f26f61bf586f259a411c 1ad1e7afe338c874f8e64c70f53f31df7617a4053107d9bf3d0439130980ef4c05d9bf9903599e36 41a68d2548effb07a4f53002d2c6f1ffedb47206a95a09b8c0374ca4599557e1e92534941df1d9e0 532328d18a18cae0a1cd31fae5f65f461e74e9eb390e2af90d3cff8a98f8af265f64ed4534f38b7b 730f50158607b5b688839a53caa0c6e7235059f30ccaf5ce82b21da440d915fba0f4120f50ea8a1e 93fae4253248106b55b06f6361d83ab619f178aae233b99941eb10ad1b64d3fe157f812a42c52068 f9ab88bf8c9043d3e37eb15e14e3809cabc8d57b3ce541a79c3e844f1f64540e8f5a18844b6a04c2 917315b4476309da334b80f6d2e2d1abaadec3d4e3b5938ced76c61657b31e31bea4ab3fd6260a62 461c1a0571a0b17628c10155128cb00bfcc57fb6f67e712f82abbf26b52157ef2f2f2dc2bccab10a 5112db80e1165e6034420a1889491e0c3e3586c8eee341a4e72a687a1c82d1c15778018fe66d6bb5 a40c6a30fa5120326c23dbf15f3874d09bbf66f5fe53cc8bc21b16640d8503ff8a6e4870133097d9 05c4ccfb1162b50d0bb15c3505314dea428cbd9dc0bc8d783077591bccbd3cfc2798570e006a57f9 957dfbb5f6fe9aca865cbdbdd3e363376e0fbc5078dcc94ae2ed318e0a37be4de257a9e7852eea68 4d9df5d9983d45975df1f0a9e495fd4c4b19dbe28708469cddf34baf4a75dc41d8bd2dc66629387b a2b166c8c68b92647ff950ff49a93fb1f400c6fa03cf39f17b3096e09e597f6ab7e279dcb8a843ad 79ea86aead637c3de91cf05a21b05aed721eddddecac45777d4cf707ab1693192ebb4274e20e1475 b118ebfc7a9e79e187591e63ee53ded5c889bf8beba34bfd591e7aeddb09d97811e14559b288a1a2 c95c7fe6d0fd8ff741cff2a3715228fef6149d75f6874f61f4d8cfa42eb62d3e6462238d2966e915 da8c3b90b2bc7fddae883f03700fea949f8db44995ea47c60e57898e5a52263ef49c5c7af06c6782 e7d606f147b1da4f7eea9d9e4df4a75d37475f3beb5293ebf08776ad5dd5ac2d4a416898786c8a18 2a0a19f89929e15717f8504f5e3233430ff2a3c8d605de98a566ccc0294cf9492b98ef39d9a5d7bd 89b4384e4697d26c318c9e6d7710f7cc757f6c9afb3e3e8c9c7a36a65dbb6e46be77195778754aac 88b57765916c4b07856ba9174569b622e958a31b2b679de768df722c22b5a9a7fc8bd1daac4c9683 99c501df456ed90aef756b88a6223ff4556a731bc450d10429e443458f9ef73fadc3a78ff72e548f 1ce34a97997d229d75564d764a7432d7aef2d152cb91a56af3d2e09da67e635adfd16164d7790ef0 8113ff7cc6f571ea33afe3f3cfaa6633e4a1ea96f85b655dd3a0c29ff35cb9aacf8cb2748f574a4e ecbd2c5e26b4546ca597b9622434cc164c2691ce3fd7e114b21ba3505e148219ac9bc0e68718ea37 692020560daf39d93803737876e2afd3ab3e4e3c887a0ae7b8dacce6a4aa5b086955668b452a25f1 6d56f8632851ae6a58ba2c5de93f8672d1b5a2fee15a856e0ad1c1fc739e58e42dba7dca8d8b67c8 a584ac9a9dd55e85acadf1c72c79631414226be7a364d8661e4f0db1fcaf9737f075fe163280186a c0562a257ee69477d55daf2c9d3e736451d4f17549f5d453f1d28d7b45fd957c17ba090bcb3fa766 283fc8a498dcc7b5f8dc386fcb397c6387b3b66847b3e4a195b2f3ea209f59b7dc5a86bf3ffbe95d 3fb94e4b9fc5d3bfe08aca298d546ac9cbd20a2e07fd9f57e62badc2869ee51e770d013fe4974408 1a41ded2b121947ee60bc4d2798b10eddc386b947329badccae1abde203b2b2e675972b75cda7979 b9b599d3fe9829853741845e86bf9deee96a74ff4a4bcf079e72ac0795d2305a48b6324a38e1b9b1 78c2646b796b505e362d4bd216f1b1337b7c430652fa6a18c7ef8dbb2fbd2792dfd8e91fd653942f 60cc7379cbbf58cece0ac52802949992eaaa19fee299e9aa4166d2d2432da68efd782de5c473ade4 659ceb255ba9ca28e1cd8bd344375b77ade7aab2b606c5ce21fed9752ef17175e12ff574246276f8 19802ed3ed4aba9937eb99e87a786e444bc9c2d2d8cd69ccd82dd89451cd93535f8a7b24280a016d 8c684da31211e1fde2d35dab2aa5d4d78bfdb2d36ea6c722761af83a9fee2b6a59eccbb22c4eb6e3 9fad598ca7a448253673b47accd6acb6e9b6cd9e9937ec7174ddb7e751fe935f1bbbf1e06cc884fb 8c38d91715d1184bd35ba5415a8f88522bdcad2fb6e1aee3dfd299e179c0d3c2a69e9afa62cf90cb 1cc16804f4f383149d4455a68e3d994f78d306690df25c404e37ef38be1fbe63b632c263e4f9cc99 6ef32d9b8cf70e47d73dda8c966262c2d88db8b4514d86834367e438534a11c78ed5f5cb32d6d25b 85f230ec6d1bf3b0294d76daf3f8ba6b56384caae34e43535326955366c3ee40b193194fa1887844 a142a1aa2f890eda83be303ae82642e53f39b4dfc142283e6e9d3e313b567898793d748dae3b8773 947f6e6f46d5da3e0d193e44c4497fe8884632827e7155558fb0921ef636ba1936453da13d0f495b 1bd42b25f573a9d7d554a4df5566bdd554b1e3a180aacbee38f69059a2434ba5ecdb1077eb7259ac 96e585284b2ce9cb2e26ca72b4f88349fa9b27ea6666bd788693ad332b58904f90e63236fc448519 7737e4c1e21271aec5a37e79905bdddfa502401cf6d6e435dc2de96fedb98fe0daa06651eae76c71 eab8694b6a4aaf869559b71a55c8979390dde53a2fb3e1675d2a75999eb8a7927e3985fe5970b61e c65fe5b8c2471a2b9bebee8c1e67565f67f645f7820d9a7d315414ed49fee127e2efd3e6d64a573f 173d11e30931964d8b249a481b690c1e77dd48d72fe1589c3f68afd565ab0d7577ab59b3d9411dbf dc9b9a1a1e5fca9cbbe30a55b953f2d26405995dca0152953605d390845b3e29d632adbc7072c735 21ac797ddec8302ed773331776b8e9e22c267e5426a4256d3adb3ef7685a2a1fa9e549647c69ebe8 a889be85c8528d452215165cddc2baac62728f58e0b98d9c674d5c8f72d84b7b1f3a9e865da21735 b4bb1c9485dcda28d982b55668ae74940b95ee43da3c272095dd1325fa9f4710952dab09cda866f2 373795e6a39a53e2faed41831d2dd64316673e2b8634f4e0d04933f152882a8e8f61b2b2d0f32135 55e911cd45e8485c420f2ea4460b51b48d16ba8765c1e6b0693269dc3d33b6705f8a71f8d08ceec9 7542fb18e7973afd143d2537c7cff2eae51ea4ed2db5912a1539c81d140f476d2fd69dd25db8401b 04bd3c24f9ce63c973cff846e1062c44d94f9948b0789fcd31994fa446bba56a9f5ad7ba0b72d75c 9f428e79f910ada226e25dbb9ac09e2bbc89597c67830d5245127b4e423a0a016a1e1bf9617c5ecc 88d36dc67abf675184cc239752880d0fc2715cc31787b79217e29ecc9f2e6749da56f7a223995be1 928715efb5ae1b3e7abf1fb9414e7872969ac0d9712b4db3045116193b5b0bae3a69f73c8cd1acbe cc50a5d9ab425657449bb89a302622436d839bd3fc1db3dc090d134e3081c80c6a40d8b91510313d e4cb542deff15d33972eb58ba9f37194b1f0e2d88ceeca4b45f7760f561b77541cc57f308bb75caa a99e2487d767518b960e42c412b6dc8b982f39abd676d9d4a8b763e6fcde63a8eafe45e75f0f82da 14432c25ec6889ac99e148288c7171a29d31b278cf4957b161bbd0c1e0315f00f1b9074f1341086a 2484302d06c4cd6b00e12d7640ec9b0c108790516ac8e36636274c2a494f6b66e2e4ae651ac7c350 09bfa815a3ce9e0caeb0a5fe5baa9e194fd43ae3337fb7227bee95ba6fd8496cb862b27a3a789a88 2ed8b135556ec40ee4fe96f5482553fb1037368913463943e3bd475dc2def18e8e61a56902d2fb43 1e4275b1e94b6f062147befab26120d42859102aaa6d0895422708a56e2284d259abd81e0e82eb34 bbd81b5613cf49dd8eb172c98c5cd5861286c288f9d272615e7c890dcebb099167ebccfb376d070e f6d72d932dd65634678a0bf2a03d16c4adbf5ae3fdd4e880bde7d32b96a4174f98368f3884bc0b32 c386eeaa0224d189fbf22e0019cab67db9ba4092790f488ae27d7153107a577a10fa643d085d0d0d 429b79a610e766dd8cb4aa55adf1229e89eec3912872976bc4b6c9c89bf10597143a19dc140ad7ad e7f13da77ae6866df9c08694cd962eecb22bf2d0a05de27618cdf178a9e8c28c8e6f802c664ebef4 1fbe5c70205b2ae74b45f3e5900072122bfb320fb0b4bcf2c57d0239b325c4a17bb8edcb6104647d e2bf9c1f9a4016e4526e463addd4c54b57e3f645ca18a7a9668687bdb0acfa17b48c2ca62e84d858 25df8291397a7ccce95ed9f48238d0abe27c474954624d78a1855fe2ba18786e6196d51740be931b a0e8e91528e5fd064ad5495f2a0250f1b30194a5a47d6957814ac0c097ea16a824f9f165a60215c9 148032c273a0240e078a62fd3753fe06ca42aa9b34971c8a66e1083c1369bb10d5a6b4a2289c9364 a4da7d4708b764f4cd3f32cf07874d4a57867ebf8e9464377684977e2c718be92fc0aea953a0b299 2550bde901a8f9db03ea1005a043febd3f1dba2940932842cfffd9c8fa727780a6fcdb7a9a3a1e80 a6f338d00ca903f559948186de0aa85b83066ad72a208f74a6168af4508a4cf4205f322890e5cbcb 2b5a9215cfbd5d08f11d6ee4ec0208c9e4fae695aae2a713d1ad701b1cd4ed0aec7bc505eade5901 2d6c77401bd819e872ec01f464ea17b6c50460b06c04e85b2fe1cbab08b4976a0383efe640dfa317 5f8e14d08f8a09f451aca34f7322fc5fb40201e8315903ba355da4daa357374ef7571543e7661964 31ff0d99376b47928f772d8c71d3e327552d0937a27bcb1eb0d4e7bcf10b5bf8527aec81de8827a0 af6174b061d8f21d186bfe01a611a280599615605ea718306fc1f6a55af3e53200e6135ffbb27d00 0b191e98eb2301cc6de27f865df504ccbce4ffb37ebf038c13db2746f6b26b56c6a3aafe8cb6026c a791b589f9c365ae354584cc593bc162d45e755e44afb0bc62212b00fa012d471c9dc9b7ef5fe0cb 3cef9f9fd4374602db6df3c0eeae1a70442a095c6a5702ce403785fecfca1438f17c008e4c822f67 1938aa111890b501b0e7cf0dd8f55307764af781ed5c6f71976b748d4baf520d8f9daaadbabd93a5 94f858448c18458923aa638616492088de9b7dc2625043b8fb5bdbf1f4fe51d69743075c15997cff 027e2fb601bcba4b03ef64eac00f5603e087ca1af846e3067c110bc2261a11e05342c9974b107fdc 44a63e9eac9bc0bdda43e0cead0f9aaaf99b7fdbf5ef7624e7251a3c7c920a5dc11c8ee8cf7b04e4 9efbd76f857d4dd17f311e7f3dc733132d06053bfc34fb82d0a91440b83d9a20ea8d398829388118 c90288c25e0431e4f75c385c1a201c3b6b1086710284a6e2bf56922720a44d321a86585f7fad9a8d 709a7c96e5fdea1e4048fe5dc14c8657189518a409f60bca7f99a27f71e86f25bfe8f3ad99ff1df8 fe61f2fda2ccdaa40fd2415e826c78a86972d46641968e5190a96411a4d37908d2a87c06a9247220 a5210792e12d40223facd1be164668b4a6bab23e35316a9fb22c73b959fedd98abfd5891a869a8b0 9f9ee37f14318146c8fd912ef15ba2f0af105f440c874c90da3bc340add57550b3ef2ca8aaff4194 cf7d05cac6f980328de8a0649e880e2ae6760b8ab2e0a23a056e38533cf7a54bddad73f6749227db ab51128ab992fe631d061eeddf686a50ceb712c47a11524526e3fc65f72bcb01597bff827b5146c1 2f57efb19d009db29a103ead17101ec5ef104e5e440813151bb4477808da113cd09ca58a60b4b2a7 b62301ee8326759ed44a589e8b7e4de4c8b01d78b5bf2b0f8538a08c045411a2a9687add4fdcfbdf a4f6a2dc8420bce10b57515a426c5d4248d538cf211a6d3fc138f9475ea3504b81c1b3feadc66cbc 874836454324c727b5657db3111f95e994d9e9ad2e3e27b81f49d1686dfed5d51bb89fbf8df974d0 5778df629e61f547317f19cdf6cbd08b721b10e6bd1e063f06b1bd1a4b8855af3b88dd4f34c416a5 38c41ca60131cb5fe131ac180273c7a1d39ad9dd34fe093965f19f2ed963f49b7dfb4c346bbedcfa cf6732bcf8ba7ae3b3daf9915c16bc7b661b7b78b9a3f8ba15afd8e75ab95fb08b3a1a13fe62cefa cc218fcf428a398c254dd8b99197baa9e2bdc8ea52e8a597d1e3a6b1f8745ad7053e8c28c8cb8b6c bc5db7c85cdb552db54388f2bf0e369bbd903ce3e32b42aaf1494abccf98887e274b76c217b7782b 9e9dea451d12d5537431ac1dc642a2be279b98b3e507cdfa5fe69db90379d75d8cc3ee703e8bce66 b33c7457537ed13d4da4f5fa33bad489e0026ae8b5baf9c1205ed9219f3172f022be8b18eaaf9001 541bb2f122c28b2673fdccbef5e6fbcce1b9df16efe3f3dac153b7d5c52e7ddc6785c316c9cb2168 da9cecf5c91933aad0537e966427d2520d327049f11b80db559ffa60103946fb9f9e97ece3232fd7 23679f5a9759f1fd4e896feeda55850cb58e2da5dc52eff7158a40f80da00643b9fcc520c88b8cb2 97fafdb6426ed96fec6d304b6ac6f4afe14995989b63873e65c7ea9aaa8e2e25b333ec2a6cbfffe9 ec877d7c309ff46c982fba6e7ab2ea328be1b65362dafbf6aed43eb7a57d2338adb51ca5fd6ab6f4 26def0ba23d6790ea78a63610fab9ea2c295daacb09c55dd6a19abe6fdad1ad586b2641148457103 a87d2869e0cb5003dbe46f6108818f2ec81d6466e4a3c32f35b22d6d65ee1b8a70ac0c62cd8bd34a 36bc7629d3883e4a796710cf97ea9fb15dada78894539b65ad568d5c59bd6a9eb746957535352fef 1aa94d59ba39d792135d62c5cb90928a11ac1b3c7c51e8daf171c1a43b1c42bbdf2459fcd431b376 38637ce364030e8d0aec65de51efaf59b2636b1558655773ff8efbb1af916ed8abe6d9d8a7b22e27 4315fe9064904b558df3a5632b2e97d47b542b5e7a49a3a8bfe3b14237194d144c3c97cd0fec6229 f759d69d5c8a1bf5b3b3ca6d99254fe19b9d0f8f98ccba1b4b654a316793e13f55fe6b9095094b40 a3b850c800daa0ffc868455108c8821a0ce2faba6451506bcbb8960bde009a05d33f2ffb622ff2cf 89b3cd5b21e798fb2cbad75c8ae93eb2b352eb9d25f70dccce2bb590cd9cdb4ca6a4f7c46f64eb50 4b57cd7d2c751cbeec94866bf5e465d61d7f5db2dd628b4e9842b3603d0fcb93f53c1ab8357044cc b2b4102053f4770a57e034aee6e94f13452020b8fb335fa0187c596cbb152962e7a5b86533c7663e b376a6d54c493bb4be41ad55e3314e1dfbb7592a88194e5ec68f4db295c60e096f815d12dd1cf7b0 9e6bd55faa10a5e3e35a468ae3e78e199b35df8518794ff74cb7f70a4097998f5f2593c5f8ae2fef cb2ff9854f114c41edfbba4fdd0aaf67f84b5746743279199e98642b71e5bf4fee776dc14c989499 f2a59ab79ecb56f08d876571ad7a7c5c719af194d4edc6664e6b10b3b5f1d474db63d7cc1bc77d74 ddf7ae5101c88f514d4718430e0d221127f7c9e9974db1a7b72ac64d6f556b96deaab5c6beec9648 d00a47bc3cd8d210454355060edeff2b3edd3231f212924db725ea663e62c4a2eb5e38112dc56219 633732724635992e478eb3743de2d8e5b67e59d6023b8fde2acc17616fbbdf854d99b86bcf5396d2 acf052573f9e555453a6375553b127a11280e57c19f691fc66e9f4fa351b053520fb6ec22413443c 254c5ea6db146f089b06be4efed93f1a55ab79366418dd23c7c9118b38e92715713252804f79596f e5d570d8db4846b85b8e5adaf3104d6b837a26af7e2ec5aa3a6ef5db2a7e5f8d153bf6582be427e6 c9f9d48896d60b3a2a9572cbe0364a12d8ee5e12b8b526097ca7e2cbb48ebe2641fb0dea265add5f c28befdb4f93b93d2e7e61b97d44233e6bbd955d2ff508edaec2dde26c1b36f9dd497bee9e776d50 233f9aa508c18940fd9c355a4de91aafe29e292bb6698615f2958fc9f9442925b3d8b8289532bb86 b8733f4351e6f4b570dc4e9f8226333c7f392dd27cab591bf291480df88861457d7173bf31c9a09b 811f3a60faf1cffafa31f34de36ec8f9ce496f1d48bfac62cbd52c5b9eaa9fe37da4a6b4d344c5af f78532ebb03bc58eca27857c9a7e83acc85366fd6b14a9944ef8dd09155871b768cba2cc0c238253 9a07cf11089a0805be55971b9c77c98f3933e2ee51c420f3f90c34663c49969954865b32299ba37d 191a5f2619ece799757da726eed89289d9f3f9c7d8db98a7b78de411715d7562dbae324f4090a52a 2f8dde50ce1fca0399551343fffc9d9848a5526d25eee685ad58cdd68ec27155bb0a4e73f8e4aff8 0ae75bb31bc3dd9590cc994dd16087783acd82ddae30a9d3b54fcf756d4d2de5fc9d621b278edcf0 c8dd4d96f6528f14b2b76b68f78986bedc3cf83a275d6d75156b28f51814a5156938672f1ccba947 35dd1d6c143aa1b93267eca792a8168662ad24f64485a0fba2bca78742830e4d056d155ef36ddd08 c68570f7ae71e67a76e1c1be961560878d05c362d8596642e4cda0b30d3143153a4685dc92bd4148 29f79644b87ff7f0f635c6600f6313c77a47bb81c51ad21e8b153aefef068a2f9b66aa41b4d478da 2a33c80f5dec63ba713d3f346cad9e959ce9eea5ed545b8987c575219cc7b5097f9b2b43be130b0d 78a3b1ea718ff061c4c53a0f971d51a11d8b0d950b332d645f4ce85627e85cb4c3522b77a592dbeb 3916aaf7b7c1dd1ad15c3fca78e74077b0a79a5e60f1d6fc0aa387c200ae0ee3806bf916e05cf4e4 cb8e40c7cf2c15aec792ed6c498de5a0cd1b2a3e22c2f1f4e1a99211e922178783bd7824d995d05c 4fe67c679f9e20dbf53934643fdcaac7262bfd1e333d36074cc699ce6817ce5b9a293f4e54f1f0f6 c89dc543e8986648e2526738dc6b310a6e12c93866d1fe2dc5b8387200bf87a6e0efc117c00f060b f8e29e04dc5df7836b687cd8f7001fa9ac7f9dd2ce6636994c2c111bd99a594c97f9885eed87b4e4 62f15498eae72c553167ffa5cf5da13de7068a3161c7e1c7909945fd12c941ae43339b68972a19c9 1159cd1597214dd003fb28d1ea258fb8f7cedc70b3587963835dcb3f2dbe063c10899d0e4452cff8 32758028c5e640a4f63720d20d018868c4064226c74028d80708ba2ee55f6a2397de1f93712ba9e7 f568c53f44e8d165111d6c6ca6ff44b45c74a2a91def8df74b6e90cdced871911e33b3ca6a40bb83 7a8f5a6fa21d52ce3edb44cb1b77f01ee58eb157e3e862d6edb88149f47204c2fd78402c4d0c88d3 d8aff34c1abe54d02d2e717e3481b8d45c20aeecc397bd0cc4b6550062965b00d14bfa6fe9ddf4dc 2495cda5ce4e3119cf742ddd701883d73e5b1d57f2a9e203a172a13588ec7833b55f714067e72cc1 d313869256c1698d66ad7a9fdc1b7a9768ebb3161e935b1d0cc3f27d0849a5a92fe31584a29f1384 4cfd0121bb1bf2c51321948d9b10aa6d0b10aa1b1d5ff66b08e5ed37f887340d42894b0542fac27f 2db2e5112016135636db4be4939d493c196396b21e8944385e23241e974bf3e44374b6b3337fd7e4 1d37b82f57ecf85d5830739c9bd2f9e36a44eecb993e715d5dda786f3668615822ef2fb563fb754e 6773086dbf1ea89017bffad2f4ebf08e149084fe134b8b7409c850ad0724496d7d59e240fadb1084 deba03a18b7884d046512134d9e46cee9d2826e2072919dd6d5e61ddec61bc4a39388e8ce5884357 6617debcca7b0e6a93154b34d30b7a6963534a84fe88b87ef02e1e9bcdda1836cc3781c44a5d20b9 f108c8c47301643373f4a5e30139f23020c72a0be4ba13f6e59d027293adfa721d210ebd2df96f9d 11feffcdd631207bcd1690f5da15c87cdbf07f9b5649d73db1189fad3e49c379ecc361589e7985fd 7c08f1b4d29f8221cdaedc6ba31dd84976be66683ce152e5c16346dce2fa008fe35e90ab0ed365b7 e3ff36c797e16e08e4999c014527d740c5a667a0eaf4db9724e94b5ff4e51d05ca49d8be2c1da01a 91a92fbb0b50cd2a0b54494a02957af78132ee4fa064caff23e5065ef5a4f70a95626cd74d440cd5 bf9e090259bec83c5cd45ffc83746fdc70123932f3f7724b955fa24bdc16f5091e2fa923c89cd67d 204f97a15f183e062aabfb25f68a4ba0f68b3dd021ec0ab451c410b92d1ffccdbcc269be142c5ff6 45a0ab918e2fee12e85afce1ef041701e8783303b4961e01cda50168bc9503ea1e1f27607228456b 8d56223c226a616515edf3529db884bebcbcf75c0769096cfa219f681e1fee427af9e6629f5666fa 7b6df3a20b34315a031dd96e7f82dff00518a83d81d1b638308db40ccc706efeb0203753355fd643 605ad11d30a5057a809949271560325c90d4bc9f0143edfd4ff821cb405fcecbb8ff198a289a45cb 28e9b0bc1dd479941983a81d87c3f84e17d7c43914c18c2d365ef75dbf6999c58fc252e71d02be68 3110f36bb36ade0f17f217fcbe0158331dc44e4c6460f7669038d12dfaf26903bbc8cc7d395f801d f88722d67918c0362635608ba535b0118b06562c9681256b87e861ae17d07c4fc74caab95142972b ee5492548367f8f8ccc619e6e03c42fefe74c126f9d0ee47d30250fe85d1a8a65fc017b98f67050a 58afcdfc20acdf9ce19481902ab3cb009fcf3580b71623e013e216f870fb093c4ff3c0e3932470f7 541bb8057900aeff14802b6075947c8ca66a7ef34ebee6ede843154cfac3b2544223c846ca7e6153 b27cf9d5b4a0b6dfbcc7a822442eff51d84460a8fdc24244333bf522085ea803223f5c8148c01584 7b9904a1770a83d0afa2f451c1e1472058db1b08e1910a02de6ba09c097fa7e1cbdaec6adb6a3ebb b7c4ab760e7384f71428c5f50bca74eeaf1f4dfb33ebfdbaa091e7f867257f8e98f8fa55116145fe de5fb8b7b71f811c6a9d4096fc4f2c938e0cd2f1930269586f82940cad40d2571888f7b209e2c1ee 8238483ef4041eada1b99ac155672db94cf289c3d6a08e982bfd881009d625973c09bfdb8eff883a fe15e3f05b257ff87b7f63bd28a000baab3f527b55aaf200b5979440adad2c50e3e12aa8cc720aca 267205a57c1540915a5950a8e418e495f2d1dff4aaad72ed5959e80fdb6986bf774c7c924efd3445 07ebf00bc8b7951ce2d088a6fe0a71f847b8f7579cc32fc28ac6b4fd79ec58f85988825e4dd5bf23 d9c2efe805c2a335fa7a359cca581026de4dd0fabd1d6856820e93fcaa27b5d4418d5d4a8d1cf136 d5d88f02834debbb0e911f1a7168649a4573d9be8d094cc62850e2a7bf18597b91c5f85760ef2f2f 6d9090fb1be9451909eddb14a2dbc91da2558507e3d5b6c028120e18786b09917d0420d2f3224a75 391df29379b749e9621aa5f696a278e24781bf5cbdbfb27a83067d7b8310f83faa04bb5bbf635e64 e83df0f51f910dfeed0422b7c3c41c62f2640db13d7b8478e94a40dc6899107bf23588b9db25c40a d90fc4f8900ee66e58fee7f874001aedbfe6ea3fc31b8afe9b13ab36b2f626e9c9d7d51b9fa5f78f e43276becf38e3e6b91277bfadd5f7f35ab9afde97fa7b00677d56228ecf82491d924782dde5ee4b 717db413ea4adf262cf7e94c9c45f2993acf6d8c55501802f2f0069396a46b64fb0d190818ea6ff8 3408194016542488a63ef0ec9cb867d6207bae2044bddcdecedc8a67bb78be589bc271c0c40a07bc fa2e6ed7d16261e3ef13c5b53a7f06f07ee9156765f759edd6169f46c959e01dbb3d73ad487f5a0a 69b34995b17663756fdf875e73c80e0631cdee8f53ea06716844789d814d8fbf0cd5adf3ad5ff65d e43f3d5f62d3c0fd807ea2f8db9dabb29dcdcebc8fd7c724ac56babbd92dbd02dc169ffaf03ecfdc d3cf193312df537e466213c9f588b1ba599323fd30a087d14b951dc483d1b7e398adf466c954a4eb da29abb32ee483ddb3c36fdd565b3a916ecb09bb58b3656e0b28a3a1668bc90e0a91fd260d042103 dfc96b8105150d364356d95fb1b7c8eb396a713771e8554573d895d39961f4d40e0854ffd329d47b 33cb6876ddb4d8e9320baedfe1d7d8a85de5dfd3d6b1fa58b4d4d36dd56c856fbb86d7799c1bd1e7 fbee0c120c561f6724b636cbc7c33572b3c85599a332a894c2d4bbbceb4672a83064900d5caa9680 251048459017998dd1482e84fe505aebd72abbcbe7f76d6963ee5beac1bc365b6aed1b8ae0cb4b69 44ef57dd19c4ae4122c2c6aacdec61b2462e7b996a9e6be7bea3c31ac50a7f6c54cb55add1281ddb bd5e497dcca7c556fcb4297813d62b98a102951fe4092bf7d9ac7b7e4d12939d352a516490cd94e2 ae824671a1f16b8843ffca92458417e5dc065108c1b0abef48ae127f6c57f83d3f2def6ac94d593a 678ebef4ef25d5abbf8a2dd3c10adeb01a2a74132d26ff9c3a7cdef24f3fb98f3bd0732976f047a0 ec3a6fe7fd7f9259b725f4e03fff286ed2d5f8ed933a4e4666ea3863572927cb72c81bfb6b14d75f 475c0561b788f0a2bb6b04d5d0a3e7085de452d4c3cee1ab70cd97327ae4175fcdc7599b9fcf6db7 3a09dcb29b5d66dd581e33fc6d7e49ef3a9b7b5a7a6edea9e3e0114a69f06693ad0cad243c376c26 4cb690b506e579e3a74bf60a71fc764cc7f17b781f9bf56a8f18f98e05579de8e7afec53c4a1918d f79b8210e08a9f06d9999296eea6903af62c39e5c44a010e5c0749035e3ea57ef82a4a1a487873a6 9de866a5bef55cf123943410e40b7c76ea323eae26f771fc94bbfe0c6a1d9026f33ccad15282b7a2 02d6a91a553bbc34645a6122c775a911396ef4ed579c32bff9a7d6535458704780ecb101a0b42c16 0344d6e2e372888ca74491f3a51cf665128bcdeac344cc5697b6e9b6e67993b94f4ad1756f598bf2 ef65d3a826373d43c6ef9388932556118d36cf7aab58fd84bddd924366588535b5e779ed688356ff a059c623a659d1d740b34c6a8c04054b20645ee8c6b319c4a1113eb59e6e18ff82c9009f223a192d 4517a728ff5c5c8cdde0f030aa093cf0a7f93f75c690b1b81071327145bfb8d1b0deca27a3616f93 b4c2dd722ead3d0fc5bc36a877aaeae7326dabe3d663a2e20f71abccfab587427eceac7fea2f27e4 7c26d293592afe9159ba9f9759066b2209c24c7eb37452a318f2c8a22afd2360fa8e6a8b1c27ea56 bfcc2f2b3d428fddb0b76a2dc2dde2601d3679f7a03d779ea73df7cc5b1bd4645cfd9c05524de902 abe29e262ab3ae1598fa14f2958acaeeb09a9459ac9b974a19b72e09143914ab85f85a94b9d55338 ee9292a0c98f9ca029af257fb9cab22fcf82a085c922cae545ee72b4ced1ba468d44e819c15d94fd 502ccd354bf68fea63673f54f16bafafccdabda1621bc3892fa7a5423e612fbb03f622fbf7979eb4 9e082f49087120ee166152ace6d2bc705c1754c129f54cfeb2dfd87cab4ed538ef620db86e6b1d58 7bd9e72382b183be67b096b56ab15692f8f87237592bc526d17efe659281751b017d64328e386966 e3372d3543d5f9e564bb723e4eb7a4f5e8dc904ac96d4312f0435bdccdcec19db458cd86a6a24c73 aee014e9b5a0f1e29ebfec8c0b1f918d3be79d72c0751b1d9a7dde46223be83ca2cce7c565985422 5d63086c3fa16d3b72a629eaca52ee6a6c53f9527f41e5cb7d92ca57441dad75340eb1508c7e6174 00f68d9ddb3986efca71ad816dce15aa3c1c4a1b35dc15abbb45433851c59aa025a4327fb93115be 55216b7c44621a9c778cf6b9ae931c73a6969bb383766ec55a467dcf7c1ea320718c49c5172f06ff 3c49da4e3322e52ee2518a659a59b254bab542d5bab2209c56fb4e68464cc12f4fb6825f5ed2c197 2389b839da8dd07724314a293f91155a7b35ddb5322f503369d3af05476851c1df1d417b751abc81 456b5ccff894d9577d5d6287ccaccc5ae3719585c8acc18cefbb2e939a7fc6f49c7ece687b4dae68 aaa41fa9fc3d73a7d85e172785d198234e55374c68a74f0a37fc1b0fac678f02d609af51e80c43bd cf80b54ff837fc397604101f9fd01e8f0e98d6f3990e4537baf140c85c9d749d955c68df67626d1c 1b0861fbdce11e5cb1c1be25bec6628c5b62d2a96a810911d5123defd928e434abd8556ad92cb5a8 42b2dba7387c3621cbb6eb866a0d774b9c31ef8adfa4cf1b371a0285f5b1bc0aef8a9b84c489ac02 d6684e002b8a57c0926701b0c8300798e8cc0123c91b3a7ea6aba952f00d7b7cc28643c6be2b3ec2 bd45fca42ca8ea5aaa88af85d08c2446dc63b8edb2ef69a5c54cf3649d09ad8f553a4774cbd42a96 2f535c532b915b5d2e876acf57953837e40ea1fb0bc36fdbfc1c8f469d0dd61f74833438f8b0bb17 24cb2f12707f8bf3e59806dccc347c79bb802bbd17e06a41071ce41a6037fe00d8768ca323534acb 46c4d8bccc8522678a7b6a898f7e965743672bd6eba705dfe985475c3cb10df61b1627332d866449 87668871852a669d0ab95dd995d061279489e6605fc43b9b79198fb65f2d6c40127df8e4893124af 825f443b75007ce23e019fb2942f8e06f88eb17d19b501dfc73680cf1fe8fe06ef4fa280fbdb2ce0 85a6077892e7d08306c9c85993cce58326f5db507cab99147f9676587e8b6839f754c4119becf4bb 4c66106fd1b9f1c7a18abd4195dcbeab55524a5a15a279bc16f1e86618dcb06356a85d8294da7300 ff6cba40008c8160cda52fdd1310dae70d44d8667dd94580482673bedcfcf7456a3b208c70080809 2c20c84b1f70ff0604f07353b5d9a86a582f0fc468254e073785e1fe167b2383b9788c4636bc579a 2cd84f3334626652b34bbb9adea2d611cfa1f8daac4e4a4dbb42b4c87b01376192c72ca35d0042cd 1681c84dea40387e4b898135f3a5b5f1e57801621941cf7b12a79900c499377de9fbefbf28035ff6 472036351a88a99506a2ab4f80a8254920b26f33bd77f8687c6a538a511fbd696d2cdede7289f2ce c265206cb8e7bc366753dc7bc4906ea54be7d968701b45add37893ac2a831ad1b2a922de1d5c0b98 d51ee681a897fd5217930a108f571342526a04a1e8c0f5e5b28790addf7d691110aae1b22f45cb97 470542f5b2ffbe3c75f6e5c443283140543da47716101256fe2ba1423a152e3da3b19c735722faee 40abb63a7d49d5c2eefc35965bb1c29c25706ccc90cf7e9f66ae7a9bdccd2f0de2e25915dc74ee45 0cda73bfba4323efffbe65194219c2418be9981d08dd066320717c03249fbcf8d27ffbf2a4818c64 342085550a4823560752dc4c81944a81515a9620f4fa1421747e6f7e12e96b31f178e071939f6c94 f0f335a595fc7814d0c12f32375bafcd9796cfbcd084768fa30125a4d50e714d761ddc7c69550cae 77bf3663ef4b87a841682b3b4072f9369089f100c8113e07f25c3c00f95edd81fc90b82f19de9745 04454c8068fb3269018569ae2f9717905e4b05729fa900d94fec81747a069005dbb1f0a61b339c7e 4fd152db22852ce6425bea9eb9579bdab113a5bd64e6bc32a336567f18d2de5edbbf72a9361087e6 e4fa8fda30abf99348bb3fc1afc20756e4dc0aa8f9f604d4c3f8f8d2a47d392b403dc3715f8645a0 29ad0fd46bb605ea6c0250ab4f04a8f1d601aabd47d88eb2290b28d3ebc558d389e9f7564141a92c 289085bf1fbb176e5866f70899d3059b9b871a8bf8008fedd61dc884537f2aecf51c011593a65fae 4aedeb4be4f4451c3a75db033d8979c0c0020726ac88bed40c5fae36307ca4ee8b3b0146889e80be ef48a00ff538d06ea2037423e2015d2c25fcdf529f4615cc8a8513354391b7ac44898d73eec33fa4 9d87f26c02644e271b6bb2cedee6783cc50f816c8c7bbfd7f60b46ff64be3fd8e5afd889e33530f9 c66860ab4b15d89a9cf4c529019bf5d0f0fa6fde44fc7c07562bf2c072620698db6900ccce3f4330 3d2f054c9e5e458c0161aa8b2426a30c73317c7630c198914f36b4f0af4424b3bcc5e39fe6dc5f9b cce477185d7aec51d3aee1f39fcafa83f922b88a38210299012c0c70ef8f61726cb2093c4c27e0df a01f819b7630ff4a4bd480abee8bc0c59c19704c0a80830a4a86f53bb50b8fc6f798b2f242ea37c8 fc3cbf905cd2f15ed4ae445cf0c111dbfcf09423788f0afb653c3645fc87f1f82fc037c8990828e6 376222f0f9a230df2feeed5740f08c1108b3f60684eaeb0142b6cc8190a38258e171030436b7057e cb53c077e91cf059f3a0d9c4dd524a785897256724f183e48ca237a1f507a59ca0a6a1158ae03daa 0de171541682e20841ffa2cf2862e2eb3446e91241a0c32fd68bdcb408ae3e641764f1f800e9a921 0829b96303a4045900891b8d40c2135710e72109c49e7f741173de5ea34436a532ef614c8c943b0a 4bbd062cf1a8a53edfce7d57242a0cd1d45f51c77f9a1ef717dc8be0ea5f70efaff0845fced5ee27 04ea7c64809a0be541656a3d50c62fff6fc5827f75f2144c90dd6303e4c22230c06873ad9b47f32a b9a9dad1c98b5415205f55f01fdb176a1ae2d08115faaf090eff94f5a272fe94da8be2097ed1cc20 2d0181cc089f2f833edb0d418fa97b089fc738844b920edabe5f022d67cc41535fef2f8c8e27ca29 ba9a2d46b1ac0ac28fea50e77e461d230e8d7cb37fc9ea4599127ff88b7f0becfde52ffe055791a1 3718c4869cb368d899190e8dc11c014a803193651ea2de3d01d198e580516ccdc020f807446a0349 d22f4e956507d92c915892e68ff6a1700e14758c92840350ffe5d0a830e4ea45dd412663bf3b0877 23576f90dff057d21b0c62fb86f3a2c886c0cb1ba37aab9f84d7f1c0d204022cf668427c9ba942bc f25e409c6abc2036a2158815e7f9ffe3ff07d6a01438eeb0fbaa6e138d6bbb4f5692ee9966ca3b8e 9975cf29ce5adc3bfcfd8c1fc42630fe699917e3ca3a6d3635bb9adce8d0cfe19107d58e469beaa0 1e9312d3657c5d5a7e1276e7a4a760ee95d3bde37b66eb0c8bc6bc1ec3b2922fa5f47cd1de66c765 b897bc4a9f74c49aae74ecfad19a0c1aa5a27b6e51ed1ddb9eec1ea96eec099d9e4793fbc1d1d1a9 516914b326d426d59c4ebcdc661e0fd5898527b7a34b273ea8ad85c62a0834d92cc7fbcf2ebdbd86 0f71832c1f3dffe47576eaf2f32a0c23ca6db94e16eee95b76fcf810a5db2b3a10e0df92ffd7b8ff d7b8ff7f1b07c00ac11831ccdc7e0cbc5d3b1489b3361990a2e7eca962dfc6fcab255d650982b683 f6b5f95e61bd126e42ff2929c78a20579a8984b236e4ba46bef05938353a5ff5617a417f1b195ee7 4b66bd121dc6760a1738562de6f2c6fd86eeb5e42436cea6de50ef648c597a6d3773da2b7be42831 cfefbc64215f5f39c579b8370ffa7aabc4061653ed2425b37609616547724f23bfb9b3637329b688 3671ca853b899691ebf6a36c7031d5bbbd9e9b813adebe87d5cc481a6de85a2ae874639aaeaa8bd9 480d79f3c7f5c6ba7a7719f3dbddadacf65871bc61e7b1d3369b1743bb290ffafebd3fe68f5167da 3bb5f4c6f67c7cd89fa0ebf2ad9042c36bbd45e8d17c60cb8dfb8c9706f79c38b9e4ff0fbcec5519 822fac836df2f79fa8d7ff7ea3d16c9f7fb7d7ff7ea30307eebfddeb7fbfd1fe62fefd5effa5d1c1 9090100c9e8e1074338247def5147edd3f4a446353688594c1751cf822c55d7e4d6e57de35e82fd0 b98dc731e432176671ef9d62c7975a991b4a6c97ef25c60bc188a92731dc3a8254973a62d06b4b29 ee5f65d57576a330a18f8ebaf5689241af4dff0031766295e523209f563e1fe39293ecae9c3176b5 634e1ae6cd22d5292f2b7d6916a91f9debbc454d3cb133627a4e5faf452f7eaba6ca387f7f95a721 9999fb5da21f8b688213dd16fe4a2e8f8b9db31612cd49d0a2e396fa3c024ab00b45dbc21e6b6a51 bf55bbece10919e7d879de867ebf6aebb346bf2e17d9a8c1651f06f6bab15be15b41ff243c5aad14 82f635eed37079f84852d7a57fa8b04fcffe65f7fc4b2383e8c67fd0cbff7423039fca3fe8e57fba 91c197adffa097ffe946a21c25bf972fc9c8965f52479bbda4ade7bde4e6457b294faef7e85a1db8 83b1c8dce6b9f3f2babcf2fc956be7cae7433db73835bccff9709f46ee7b6b3cc2b676e243af8b1b 4d5c1e9a13f397049fe6cfaffc0fe5bfafd35fccffbad47f41feb54ff3bf6ea9bf98ff75a9ffdaa7 f99bd7fe7f6c13f817eaf417f337affd7fedd3fc675aea2fe66f5efba84eb409fcad6bff3fb109fc ab2d0d46a1fcbd6bfffffa69fe833b5430a3e4ef5dfba84eb409fcad6bff7fb909fc5b2d0d3681bf 77edff779fe63f7d38fd8f9d3dfffb3a834de0ef5dfbfff34de0df6f696023fb7bd7fe3ff9347fcb c9f46fd804fe519dc126f0f7aefdffd1a7f91fb6d45fccdfbcf6ffeba7f959aa3cfd51eac8d35e2a a9f4eecf6bede311fd7ee6bafc9cd7978ac188e77a3f5a39e92569be7f77e4cd763e57f6eb8d143f 2e8ffbc1cdd567f09af7277d0a49f044fb1f7ff85fcbef75de9e7e9dab4ff8d974420180b85be5ec fb968d7732976debb63dd717b874d23f6a75ff095bfdcdaac68dd66234395d3684c162d19d9e56b3 715ad94dc9e2f6f6af8abf987ff5ad2f295daebca46964f6a3d40faf3f3ce6ddf12662f67d2d5c3a 99737dbed89cf4b7271dcd1459d9e62eedfaea30ad048e28f71699751731eed59fa536c670e2ae46 d331bf5457ff41f117837efe56aa8ce3f797c2a89147747cebdce675e375a9a49af6493f37578717 ef8afbd4d0c9ada5d3a3e0de3ecff2fcdd14aab374be509f2cb3dbc6684f31ada1268e07bf2478d4 fccfaffc0fe525956b7faa339e8cde079143fbba6262c157c767b596491fcd647dbd9fc8556eb389 efad65abd24ccf3fb17c66163af6ec4961ffc88df6bb7861a85dd7e5fe3d9e6ffc5b125863ffefef fa51ead69abf649e79bc947a31ea8d6ba3e6a522d08fa3b78a260f6047dded722ad0ab532fa62f06 1b3c3acb78604e8a652548e9181d9a1d6b18769ec9fefde1d8bd614a28fca7055d40b5db557f1b4d f9a5ead2f3d96432d1dbecdc6c9c55f97cf3d7b99adc65596fb611eb27626984cfd29cc87695c9da e9a9a3a372d086baa987fb8f891be98d18cbeca6abf7c45f248868f8072fff5be2d7d9a8fcaaf3d1 29458d2bdb552bc7e8b472dca78ec720d46053ee3ba3d5793c81456250a4a7055c66c6b261b0c356 72c4f79f0552e88d0e2da99b89b04a875e6ef47f5f0213f67ffb869734e9555f32965ff807fde4fb 1eafe1e173ad8197f6e3bc7ed8b2874864752e457acb68d6f26639e2fd19ef8f776cd86630bcff92 73a1deb8ed05636aba994f9eea3035826df389a5f09f9697b41956d10554d8715ff2bb05b7596226 1f1e877176cbd2adcdaad16d4bee93ba36e63369b899549afdcbf0966eddfa6f7c7fef4d0a8967d7 3e1c5e1d3695fbb40585c55a327725ff91046120ffeceffe057949d789dfd244cb7d869b0e9cf71a cb6e5d8548af9ce7cb75635b9a5de0956c10e03a2dde6f83518b8aaefa1f26b5ed8596ed6d6725d1 fbf6e6b33ab6f6fbe2b9791af1d7a6e6e18fffa104c311ffd9dfbd64ed547fc94377e58d4ee4639f e40d58cb523bedc6d2bdf92cd7aac094ef65b4518bf47283f8c169755df3f4ffd1769e6d8a2add02 fd2de68839e71c1050242328888048a6f4ff5f70e6c477666ef7cc992f8ba6f469175525546d3625 451489157f2c2f0b025e6b5dc5c301a1a349e1dee1d6b77ddb1d19ff354065e9a1a09a09a2cc8ec7 a3a9cbca2e25c425aa5d1a8b8b568ae1b138ad734d6916a35f68a61a5a9e06476dda98e258ce5f1d 70e80aef5d44d8ef3b264d604cf3c0607d9716bf8bf7af16fee80ddf05a83c32a823580fea561cd3 ac4425174971ee1e7a7c4de8ec180622494a6c42174219785f1e2cb95c93a1e03abf27abfd0a161c 6af5f01f15db28df4df7d111284ed1045b5efe0aa274ff6f14fb4efe01ab67d6ce49a40b4be25c0d e27ced14d4e9179269938b60353e96cfcefa8017556adf795c24acefd03a3a0a600b4d30a887cc62 0b80a44f9bf84e9aec32ef5f944ce1c5ff1a5a626b3b92eb74c762769a8e7a1a674d4601cd57f027 a1f4cd245ebb76f25880291574e4e35d64fa6296bb65124776b93389c3ca9ca1e14d96e1e0e29517 b7da5a94b7bba2a4fe18efd56dfedf77fd0ba034bf8e41895e1d40c980c2e10a994f828ad29c81aa d4b885e33faeec133715f3e8bb6ab8af9a1bcd3dddd1d89adb97f5f260aaf91bfb406a05c1a8ef68 4977d5b2a605e8cb555f2d2f755dc7dac64f22bab1f2cf92cfab7ec0338aa7fdaaea073cdf17825f 54fd80e7ff56dae7553fe01905537e6febffefd1fc96d67f7b7eb6d27eb24adf33e9dfdafa9fed02 bf52a5d18ce0f7b6fe3f8ee6f7b5fe4f7d6f7eb24aa37b6bbfb7f53f7134bf5ca5d1c8e6f7b6fe1f 47f39b5bff3feb021ff08c9607fcbdadffb1a3f96faa34fc98dfdcfaa084dc27ef2e704170500aea 0a288b95f062fa9ace413518aa4113bb54fccecd39787ddfb7dcd12ba83a53cf58d9b935499be5bc 7736f64a56beb700aa6aa093b3549e7fc49485168f4977dd37fe42141afa67c9a710aaaae33f3c2b 61956af5f08c9f5b2f02d398a8a1e2a1ec0d4ad728cae1f297a7e3cc3646cd5e22c6d6828a0cffc0 e231e54e8cb39ade63094b9dd42c5f5972d0532edbf3e7d91d1dee9f42141dfcde6bffa3ea7643d5 1abc080ed25cf3982d5a76f9fbe5e09c6b8e6bcbe3a0696dab16f278cc7682d12ac98af6aa5cb4db fcc5472923d79b9934e48ab4b22ef8bdfa140795cefdbfc157cffbf1f80ea6a45b0aa824c669505d d04bdf1df4542f965a97434b626fcbfcddb5b6faab63a2b91c6e1c579568f1d8c2e5b64ca72f5718 a94b92d94194b3a7abaa08ae3e1092c3dcfd5f88a6b8df28fe00408932276fd5af9ec56506545161 e577ecc6cd1dd3a352a888ee2d38a946535c1345d37dc3719583fe9c78dc4d1e1eb8eb6e439e246b e89dcfbe5dba88c3eb4111525dd8e1576df7fe794441c86f14ffa1bac7bfaa36775950a5af2b2fb8 b555e7dc0f550b38879b28a5060f3c97e8dffbec0c5517e33915857959c9ee75994bb74cf1e24877 a2b9e7e9228e647edd689a5c89761fff0dbe7afa5458a5e5c1ed3d865e93d9c044d5b5fbf2e35727 9b9b96ccfb193e3cf0ec2530ba45a5a789d3e4568165672f371497b8f49a194a3ce5e7519857e0a3 98ec85353a3983315faef52d44bf22f5bdd7be0b503afbd3bf5441e5c0e782c68b5bba63c491ec02 bfa83cac1e7c30c80317dc63152c5a76e6b6d96c67f221e8afcf4faeb21527cbf9eeb4ca0a18a757 ca47b6de9658a69513afb47b2bba3f8928a4facf9250d5998072ea44807267a6820a27e57d023f2c 9cd9f57236ef7a1332dceb627f675cd4d33244be767d64b3c30b03892371da91a75198771e857997 7f8579a32445466f3154acba91ff1b8092fe9cfeddd334a21f76f1687d3ab357455a0c2bb45cbc83 621fd32799a97b835e2c247930dd3e27ae83ce697dea75bf8679d9c43bcc3ba5d84d7d458e9f318c 383733f4771105217ff8867fe1ab6a49264119de69a09a0d0a2e5fed4ccd7b05158c6ec1cdea6323 d8a9ab63605c1bf36afa12bbe5a299b4902f3f2a9c31f7ab6c73d6a9d3f4566c5243abdb2127546c 48640bf2fcb89acaf0af208ad9fcb913f6d178a8dabe85aa02a907662e517432f1d7d070d41aa38f 979ba45a486d564a959f5da59e737b89d92a9fe12bc7799a6d15892ccd3412056a446345320d41e5 e395bdd57115a6a29b5e78493a8efe1b840d9f9d81f2e441814ac178f844d3285810c3471fa3f339 8bbc6dae0cb89a457b22f90ffb744ee787f6a944d67cf688d69f349b99bfa864c9881312bf4c1e15 249fc6b5e1a370400d04da5bf955edc788a283ffffbbde081bbe10aa6eed50f5900e4720ec2c6db4 efddc64d39540ed73aa87897a7506d9fa7520f3f693d53611d5bd369aee845d976e4d9695bc4aaaf 3ac72d04fbf8ee5906074cb55e7b3bada7f7adc139ffcb888e26da8615da98870d1fa38387d9b42d a8cf03752123e56b1d3bed2fac4f3dc4054f974ef0733ae59ad20ba7f9daf2442e5856248ae9aa8c df7155c1ab67423b340e9dfbfea8952dcccfe47cac3708a2db44efed2f03948fbd39a8c42baccb31 b8aa09f05ebb6258a17a61e96ab4d0be70d5b30a8fc52f8025f3b35268098dc80538ad8fbb7403c1 6bcaf37868ea2abd27ab177edfe1b80b46bbe8157d55d63acac96bf303888290ffffbb40d95e2d40 05e0bc61ef52fb2bd6cf3e2eec9cab0aeb2eb1e4f0748d62fa3a79a1c4e6cc21946129831b0bb572 c0a14333b45b443756b0e0309a607db7bf445fe4084647a08fa109764a22e270cdfd37785f3d07d4 d2498f4b0ba54cbee0cb005c6d615d29425cf3a274e991199b903966bb3dee526dfa606d0acabef3 304dacef5c0394ef887134c1f019441c8805641653aa48faa43577d2c4eaef96a960f2c69790ead7 9d9f44d0a86fc7cac63a7b974160912715eb98acbbe826e8c47e9f268afc3d4ae73958eb7e1d0bb0 a08ff2ed6083887d1747d2bccbee964943d8e5ce960c2b7357853759f70e17af2f7bab6dd2c17607 e5631fc53ba4faa3372857d7512efdd1f679826b1b9aed5c370a35b51fb7e32e59bdef5d98b643bb 62b09346721c2ecac7c276573835b765f536d818b03ed960e547b488cea6663cd66b0b7bc02100b6 c61bb1e3bae9c4c8ff0600e2cad1bd350099f715805eabf09ac50c1ee11c65570ba77deb03a8e653 6eb03f765afe91eee39ea774ee5e57af675cd0ebf6dc5822b3b6c5e5796f2d2e3dcabcf2a9935910 efd263fb50eff78731b26e7cb01715d1ae467181f7f627f156fd80673493fe55d50fe04747f351d5 0f784633825f55fd80e7bb0bfcd6d6ffc52ef0a92a8d8683bfb7f57f7434ff61ebbf3da32ef07b5b ffedf90ea6fcd6d6fff92ef0f92a8dbac0ef6dfdef1ccd7fddfa6fcf28a4fa7b5bffedf90ea6fcd6 d6ffa92e00a06bb305a0a7b306a51cc28092340b87572e560595177104d5653bf09d76b3edb7e929 e1f536cd87cb52f56870ebc63868e08cc5186c49a72d6eae5f29f6a1ba57d1b8b3a862548585a9f9 49d15232c7ece99aa7eeece71105bafedcf91fd500b341a5c8d741b57f25027339043e319976bd7e ae43b8dcb86f3b42b502d967078c2c999151f3d65e318fdde87e326aec54ba1f84e65d2796c5e8f1 b85baa4ce97229cbf152359cb9fe0ac286efb7434f73054ab5230bca693cf41c69efb14075712783 66ac1178416ad47339ab4f3982de74edcbb552b5d6657d6a96b62bcc781c54f28ecb9d93def11549 1d5190729bae3396ac4eb2daa5b12bb3e7e323417f0b5110f27baffd0ba16aa7054a593facd2da9e 01e53213aaae8d06a8d24fca7763bdc0eb5f467d77644d19274d757c4b69666a6679798e6e481a87 acb3d3a9e56baf8e79e8a848ec91ba2ad3e149d6f20d45aa55554da407162bf66c89fa49841ff3de fe8f6a4774c2b99fdb0455234bfb843d04eeebb2183822bfe0ec253202a6a6651a86b59dcf745f5c 6cd454adbb55f232b293f5746a2f19fc85bce0d5837826136874235f184d18f624d033e257f077cf 3e15b6fe540a3db9572b30995474345e701c3e5dfe020f9dcc78ca599b67e7f978cc92ad3bb9888f 55c17617cacab597320217d6529d2776672f5bdb8b819b6384d7a3793ba51733865ff015f2bb8882 903f7cc31b611f1db5c3da8c6fbeaac29a0b2a56b11d34730ee90d4a8dc0115ffdbebd8216bca93f 3709c3666e0d7d70b88f6e391a1bbf736ed9fd4c6aa0f7c5999a9757e240d576c218238fa7d951bb 700ade62b90d94a77e05ef01d4bf5409cb0bcf50e5b6efbc7c32accd82ef6494d1c0daeaa868569f 4cfaeed76f4d750616bdabc67787d241688fce3e4eccc4e1feb5109206b6e1e54977cfe7694c6077 54ec9dd759a9d8e4afe0ef9e8b13174efbdd28ffb4d9f13b1b957093fd54f44099bdc2a70313edad 45c34dcc92fa2885d46f8578b12d63e764efd231b24391a3fa2321458853fe1a1b2cb92d9a415864 53e7987d35c9317556a77e8c28a4fac337847d74d1096b33bbfdaaaabefcf04c1a9e3f99e4e5e8cc 5e71d782a1c5e861550fe77b306b25d4c52655bade3b8996d4926f1df165dda3bc4e616ad686fcd5 20271c5c82166c95b361a641c568ba3db439ca7364fa57107a4edbef01543f15d62a2af3a092c805 e19974dd735f147db057eba76ba2d46868b8d7c3591fc734a06815212f377352fdfc8cb32de10c09 1d5e11dc1e57becd468c550553da4de85bca071e453e05fb443e613dbadebcb73f89b7ea574ffa16 7a16cb20d8bf461d3761ae310b4e3c9d47d36c46e1873b282e0475b9183bd77a7c91b9d0dea6224e cbc7e6a9503db438246d7519bb3418d0e4c19b5083cc69458e0b0f8210bb9933210436ff014421d5 efbd064ad575d8fa8b42d8fa97c729bc3e759e3e316eb66dc96dee1ee6e362de4122d7d19358c028 3bcdbe4ba4948e8be23a993fad6f8d0a87aed12643e4b5e8c7aae8de623ca0c27a9f9049d9de1019 31461e97607c3de68799f3afe0ab275a85bfdc8d4a8ae1c97413f3fab357c3821e89cd3d80d79a3e 66f8c66ddbef1ee4e31051cea7f1c517f2b097e68c7ba1c090992644bd52649914f056839803bd7d 5ca9a711be65a8d5e11e03d40111f2a737a228c79f3b9f0228f5e050956ec0e1c5292f826a964f38 e250aa1ace565b6a3348936f102717af8dd9295a85f03264b282209bd3075fcd3380f146d7381d8b 2792e42cb7494759b2b977562b5eee50b5432dbfedee9ba9fd3cc479fb514441c86f1483d21c0d55 2fbd5d78861a4bbed3dcc4ac8dd42dea89566bac686ee52c1ff7d5cce54954a7625627081e2dbf64 b67d5f9af470c6bae43ce5fb441e3f3c71ed997e459566388fe4de92a8ccbe15a3ca58c73fb6d100 453abf0250a2b81e28f9730454867cb416a9cbc909ff4e679184a2c1fb81dcb24fa74b7cb17f8a0b 9c6ef3a896dbb29d2dc9d023bc2e935967723daa7d5fc791f35a3fecb9a6b9b7b5b4bb6fa75f4fac 6bdf92285bd28be8e0ae97fe421484fc67c907004afaa507ca25e44b0aaaa9bdf2ea6d7d935f326e 87579e9355a085622f6ef358dcc833c1761c45d5e9049e5913ca70bf3f96a5068dd74ef1d3a1895c ce7b823b5c31df5d6b583fd331506ed874d138d18a2142a795fc14c28ff957092827ef7d50beddf6 ee681063d45c8ea3e4d6781a3f9faed7c1497dd007ae2995cf4c5f974c6aea6e53c4265eade258ee da39e01037d99355748931cdc50eebbbeb03ca7717d1f5061d015440c4217a456671c144d28260ff 0a4079ecf783079b44b54906e4e456731a653f9c27a344fc04af8a4d96cc89533a7148c2648e3992 c75d6aab1cac4dcfdd771ecf17d6778c34ca77b4229a60b41a220eac0e328b05c3f09f27e63b695a dcee96e9fe7197933ac4bf108554bf51fc5d80b25d1d6a89f6cd96f161a5279ed5a2ccdf6df8c904 1b284b4d1da474d4c666f360add7c3d0aebf455f44937ce7dc326521d42a5e77d224aded72978409 2b8bb41ba2f68437b96e122e2ab3c256db60b51064e3f38842aa7fee68c02f4832c6de3571be85d6 7c6dcc4b4c5f9be9e4c2971fb8312f397bb22cbc50bedd4deda4118060651eebc0c56b66b2d5d6d0 6abb2b56761b6397c742d48f1bac52a736b5472b0a77afadfde0bcc61b3325c446fd15c877a7c88b a7534ee6acc46347f3e53d41ac9f26f98e43afa634d6b7341e993e37177893c9dc3658e9745f5be8 cd5f37ad7b6245b6eed955c7734aab8e1faf2e033ade0851e82c997ea1b7ecbf0ac310edd9e2c557 bf8968c1f0efbdf62f0028ce1401546c0e00d454770052d8f0c2659ce3e18c5f1c814a6b1a5e605b 522cc010adeb5b57ebe03735ede6b95defbd5a4267e555dd20014d5ddfdde0ce730f31cef0e89dec d3e571b59396673f368270d59b6697bebd965becf388a6b8d1f657543fe01905537e55f5039eef1b 2bbfa8fa01cf3f2aed57543fe0195dd67e6febff7134bfb9f5df9ed1cde2dfdbfa6fcf2f5de077b6 fec7bac07f53a55117f8bdadfff67c7781dfdafa6fcf8f7d6f7ea05a10a150b13f04d0c84400649d cee1d0ff9108277eda185436b0103c5a46b49ebf6ff78cbedf5a1b478fdc499a1bb072c265e2b7ba f3ea3ce60e278183133fc98c9d5a6c056bbe9928a64ccdedfb239fbbdef8e58a50d2671ff931a290 eaf75efb43b533f8a25acae95238fa0749501927a6a0625d2e41236924fc76d61a7a5d5722dd814c de1d5e45524ec29837eca93d9c5917bf11f5342bfb2a30e63af1bc3cd48ca91bc814b5d401a19faf 4be48ecb507101ff2400d4504aa1e268f8ee026b1f05a5da2354dde5d3a0821767c1c3522ffe51b7 525eefa00cdde14ea49dc91037ed797a92b596a76ccbdcf4aeb307ac4d61035da5f1bb393138ddc9 f1b2e627155bc91c0b82b47347874b1d73dff39b68fb79fca5fad573198b5265a1e8618ece3cd85b a6e4f98811ce04465a14817284aec4da7375635b854123ffd0914bdbc076e991eeb6c773addfd437 6a5cde63b7143ca194b9b43dcbd093e0cec7818189541bdf7c005110f21bc5a1aa14b6fe7a110e92 f038064a58324a41ed66c2861f2d7c67a35e3c1a71d36ea2684cedcb43e2adf574e399a55ba16834 8acda6dedd0eba6a4244068a144f4dde19c40b6129ab38874a7744602e7badcf89bd098909b1577d fd9300d0502bfde5f9e56e545e0165779c7d67cbfac4c694dc57d54d3b62d19dd93956114c38d804 8679be1674ca306a6a92d39b4a3eff6cc9da02ed4ba85e9e5c4c33bd3e3b5afa20fa6c8215e28b31 c65f1260f5461484fc73e7a3f8bbeae119b6be0445f9a7b3d0b3862efdced8915cbefaca3869cd9a 599b951265423e0c6616dc892b9c5385f6aca4acba938abcbb1335a99e4cb7ceee4a1c8841079b89 fd25be13b89bc5f1974d1ee5ae5d79f551440f94fdb3e4abea011e03884f85ad6fd46fa0d2c47281 993b2cbda015aa8e6be9acbd24ddb9a937b4b361d990a7f7b566fa9635bddcb5342c40525de994cf 5e46a98b0c3c8b8229025f878627b104ad4f69744371eb9dbe6355965afd24fef05c8fbe7a065df5 7df51c91f9f0eb842e3d06f22547bcbe32769e4a2ccc6ac195ee5e3be6aa2266259462f39691ccc9 3d7ff6b4575164e3cbb2305e261ba7b42777f965f5b1e0944de6c0c28f33c2187164fd17a220e43f 4b7e0c00adacf2df54cbe9810a2a6b261f341474e10d92a167064f64ac2d525c3df64e20ebe00ca2 84cbdbf248bf64a3bb4f5d88059115d987941726e53a74ca6ce42aa73cb72d0e6aee27ec8e7c604c cdd9ee683cdd5e7d0ad184fdcf9dafaa1c1ab6be52d8837279a681ca41ccfb0e0e2fdc71ef7ab657 e374c644abb5b5414c2d599b70b2a340683f90f0613b76ee6ffa49611ac732a7ec2059e0b6bd15c4 22c576345b63f699c690b657fb1ddd82118c22b1c6fa27117a1aa5bf7b4e773aa85cb5e84777fc0e b4983ba22908169c2c661f07b3b3d15f794cbcad2b9387fce08bdea53b8d01314115e3a75c799ce2 6048ceb1d5c310621ab3428d6e37e23d8a227a6b1248ab3d393865d7ff421452fd46f1b700a0bdfb 4795be1a075086f13ba83c83a2d7473a537be9ae39b3d64ba48d8e9b58686934c32ae54656938eee 2b7a48f61ca3ede024998918a72deb09b636a7d2b4a355f3143d702a24d7beb7c938965a1093db70 4fa452d9ede7118554a3ed57d52b3109afa3431c9405de08cf5069c81d99e58955aa4c59c3ddbb09 fd1528d35bd16509194fa9d733c74aa630576e3e0fa957c0d6b799184d1ef629aa0f0a3932b15521 22bdb8467936c7c52db538e6260881af6b29f8270120f655019049879e8bd51154e286e93be179dc 9684f7fa028f865324f4970103554eb5fa574c9eec2fcc6624085267a6f1308258eca17c073475c9 c4a898b64f12e746217b94cbf1327ecbf9ad839e4acd0ebbcb893e949536f62d4413f6efbdf61742 d520acd2a73005257a4f80caf06579f40ccf9afa006bebafbeb157f359c6522adab121516d7f2b9e b30986d7ec8cc4e2a3619496403379c1258557f1492c6672f2588c2379bc640cab87eaadd7dd1f4a c715e66015013b8e8fc79fc47b00754d5440297b8972a5652afc3a956d472cb45246fb0255d51c97 81af8f4ade903c275e3a4f36ebd9693b25f75cdd03271aec860a959c5c0d427aa1fe7143b5e27825 aea70eb5e054d8376f4805eb5cb6d185000dcf682bb47faa533f89b0e1d3a167558d5265011334d6 45cbd45e98af5ec0aa78350c36ead052af7454cfa94d2775528746873bc4066ba6c7c9476ac2f64e c4351dbf1d4b63da383cae357b6f1381bf6f19760ceb9ec2eb13e39f4b68acf6e820e3636cfa5d44 41c81fbe21accd721594e6ce1c540a2dce1d2d77373d2e20b7ab413f8b52d7afaec50b143f9fe0bd 1a7007695766fa7330a0a63b764bace1d201bf5bf7e816dec1f42ee7bd738515cc279b3ad67b942d 942b1403343e8592c804ab4048aa3e6efe2440a9d7a9bd0750f7ec2278b04bf6510fcf2a4af992d2 a5ee9986c44b4258f0467c4db3643eadd27c557c9252770e1145a1dac5b1dc7d7268eaf47adf3151 14639a2889be48f414c54e6534b127eec854251c243d55a2f9cd6ec148d99f0428518b3a28dfb62b 27ddcace155854161730bb3cc4b9582bf2a8901e30c1868cba002536934732c7dca463f98258076b b3488482e92cd6778232ca77dc269a60dc3e220e13336416cf6d90b4d0dcefa4e99adbe524428195 e5d5fc31debf5af89dd7403949343c4f37db8af23ab917362dee856b796af2b52d9361fa7aa6444a 9d55f3b84b9a8383b58eaaef0193a11d7c4147c1e886cc623563274dcaee9764d8e66b97bb0cc20f 5ccc0af026b7abc24585e96cb58d31dbeea060f39308b0160c2957ed1e45392e94603e8402bda3b8 e6d9b8d37c656691395af7712cdb8ced3b86944147fe0042a6af596fb74ccee6b0329fc170f13ac0 b6da7a466e77c525bb2d6b1b6163ec76d206abe0eaa6f6601e6b6baf046bbc61474b4fbeb79fc76d de2dd317d7c88927b587f4593203086a6a4fe8a33632d943539d8ae80bb72424cd61d103657051ce 3f36061c0b3635239b5ee3f502b46edae1e0a5690fdb2bf738e9afc8f664b4eaf893690864b90ce8 fd76c9f4296cd97fb1c70f20fa7dcc6f144bcdda8e38c9873acd04abd490945ae80cc7d2c63c1ca8 b516c8143cd6f0268dec36c6b68daf9be6935a755c875f325d5f5ebc585f5bf0a3b8b94808be3317 a7bef7fef5b559f6359f6572f1795acea542d4b23369d52cce968566e927018a9d4c0214a74c0d14 67e5e89c068a9a8903c802f770a2621741b9cbac41799e9341b9374b06e87ad90daada06f10f19ec ec3758c9f39c829df78e62bee31d77dd85d70263d46b3dc4934bc5c59b13cb2725531ac55903c603 4c6f16df4b1abdb79fc2bf543fe0193d27fdabaa1fc0ff1ecde7553fe01945d57f55f5039eefb1c0 6f6dfd9fea023f59a5d18ce0f7b6feff1ecd6f69fdb7671452fdbdad0f4ab1e49747e50d2f549df1 916a5a0ab14907e861d3f34d7286f80d79267ace9074bda3a317bcf62ed6f1daddfad20da7cf984b 3536bcdb15f09bcd2b9e6c2ee3267bbf7b22a611646cf517a2e5dbff59f25d849650121461b10e8a bbda1440508a00a53af400e50a540665cd8541d99baa41cd1ca7fda6d8ea7bc4a38e799d462b5a36 c7a5a72ddfed199382f31c6cbbce70bb5d38b1fb0ab14ffd236b8f95dbcd5cae9ed21d4b4a94dac7 f61b654aa7669f4214528db67f57253b33008dab14282d5be18060d62a830a944302ec3c55fdd6a8 95f64872307299611e775ed398e28c1676600b9b7bc14e2197b6353f70332be3d2b0b9cad0a499df 9bd145fa6ed889cbed856d0f57a967aee5a2084f3f8f50b1127a12d21f9e9b46e8c9cc2c501697d5 773065d3417d7b3e523daad8cdba6ca63e76783546d8227a532da9757c9a57b35f340b62a2f9280d c4a1814cf0e57d9f9ba1badd5a322aa382d355a24c4cd2c4e9f252cfbfc6ffc23ba4fabfc5ffc2df 55c5d11c40fc900e3be8ce066517af058fcd08f3dbd5a9e6027790734e7b68629f2780b2960aab9b 9b5e3fba21f9284fc5dcdd3c562afad194db2a0093a11a531b8bdb243d4495796ccb495a381a3cdb 316d2692176cf879bcaf9e442b84a03542d57e58abca9c01e534e9bcb37a8306d4473dca1c69ee50 1c179cd4109a5af2dd66cc6d6b6f18867305f7e62993525f8961ee260c9cb232bf10cdeb0a5f0fe4 db195ecadb9e7a7c571ae3c262f7b2990903be3ef83cfeeea94dc32ab55651aa2c1f7a36a5baef24 47d114d7eb990bcd11668382bd2854e6a6ba78708f6ab5aadf5bccd457f9c3e6754b0faec9ebba5f cbca70222849c8436b5eccb831be346689ad484d705888f1d0f49414bdfeb7103df5fdbdd7def8aa aa5acd2faaa5dc9103e5a9e282cada6afa476f84badc61a13be9cab868ad85caf27157cedcdd6938 9a36845ece2d53894503a86bf1d200929150121753c6f267a25c2f8bfeb2db157ba7d94c88e7ea5b fe2cda534e66a9eee711d6e620fdee027fa9d6a8501536bc285bb6e9f9480771470758b717605634 35b8be322c68cfeaf4f472bbcdd74bfdbab1294b32aebe7769cad39718a44052785ab782304ade1a 27a1670ff94b2b19cd6fb8157b1cb36a3c1c077f1aa16a37f234c2d677d70b50ea9ff877fe69f058 074d8f9ef751476caea331b49d031bc8ac74eaabbb8bcc6955b87524a5b0aba872c51f1a97d62e3c 1d0442c217f81c1b3b09de36cb5fae93329f4b6cbbdc7a666fd992301c334850ed7e175174f07baf 7d5575dc26801270a8ba3887aa5a2208f6a3a0e1f57b7dc4993551cdda3c30e85167320b3d780ee9 dbe29e10e53be62917fb95d545b0ef3fde71e88be49c66d018f0392e9be4d65a1662b54cb5c59657 fb25532bb727f46190ea7e1e616d8ed36fcf2f37240f4b50426fa7f00c950e8246f3d57439b3b9b3 e5c4413375992919c7657ea18d85e751295e455e326d593ad389fb4d381109fd34b33093531ec065 35418d310673c93135db6cd016d19b516efd7d6385ea8cefddcfe32f55003589d0f3620be1e9a9f6 f48f9a557747d7cecece8ba8fa30f74ab4d0cc9d8ee51737794963b2a12de90be123a238ca1ce4d3 fc62a8dceddc3258242e5b0c661c018ddfd034e5a5e80ad55df82392199b13b2af5f7b3f461452fd 46f1175528fe6a0168c4ad40e9ee85aaa3fecbf3675add11d7cdad55ea1e54c37178488febfa54d9 1ed7886477c7d479901b9e84f4702df1058b5758c4cc443ff5c8343aac4979b7a14f02ba96200756 a7489c5a449748ceaf5322f1a47b9f47589bcbcc57cfc375fd575ee761faf2fa19a5662fe1d7f271 182eaef7be34ceddae6e6f209b6813bef40a195c10bd38c32b445f640d0893996633a651f4606b90 5caaea10133efe3a9ebd5cee98ed2ddab87cbdcef055028d4e9defeda710aacec22a2d2642d5abb9 01e50e740e1e874dcce51e4ac5d497caec1e746aa2266e9329a52cc7a3b09dd419052b516883c369 d57dd25c05cb9d9856757ca6988e7e2513dbf99d48f7abce3107c59f78319bc81ee0f5a87528bd57 80a8a0c30f20caebfc674958a19bb0569bb9e8d19367b4a8f440f26db7f374d2c2b26810296ca489 2d91bd41c54120e35bae7ee62bcc44c8d56484431b778239ded21cc5aec72299c474f9b8bccca373 1abec1abd66187149e7b6c9dcb6096b66862f83cbec19a2b62fc7900a8b00b3d47e528f7bc00834a 7c178d3a3d7f570e4c3de7a5f50494e9281ae111b25dd7edcb802f1705491df439839aac18324def 294e7fd1c4455c0ac7c2cd930ff73ea5ed4d6865634e76f2c4daeb431ea544b58d80e7708780517c f1461484fc73e703005003cf02685d8b52658708a8ec25c5116cdebaf7aef5e05614b62d19bf6df1 cb30d7b88b1920a678246747cbb733dea834a66375624b666a39fcb899f1ecc110bae73dbe84348c 9cc46c34a0932fe4c56773083fde7690c4258feda64d64f3514481ae3f770034244355ae1f2d7d7e c47cdbc12e66657dbbdcd627c495f153bc195aa630e15ab95cf97bb9ecb36d645fa0e395789b9c6f b9d9718b777678b5f23aee6d556431cf602414d864145547876dd1474ef4238da46299da6ededecc 771974b7fb3c00b4177361075df74025f6d83b82723eaad9d7225acc44c6f146701966855668896e f95a38c4623b2df14e2712ed18998b99d0115e109d83a977c67b62f55a62346ca1e8d3702974547f 898840a43564dae9fbbb0bb34bef7231ad095f89dae22f4481ae7f967c170092b53c289daf83a091 bf6db5e4fe9c96f17593399f56261056a05ce3ac7262cef4f5dd3e5aadf5442803fd8e1b0b245a94 e18043d3fcbe63162b58df4db4d151e00e1171102c91593c8d44d9a764947d2a46e15303dee4ad18 5c54fbd54fe11db60bb7a094090aa07ccbf655e1b27fc826a4bfce93fc4b38a94b38e09a3456a4f9 6aac11add63a38ee52f1d5c15aabc73d59b99cb1be73d042bb8585cc627db09326ddc46e999ae577 b9cbae0a6f7258e7fd314ad84bb58db5ddeec281dab67c47af9f072851e5a2c622fe59c6569c724e dab1297fe79933dbb94ae63b1f7a3f0b88f54b8d47abb566f71dc328a37c9be922629f99efa43183 46abb512f0268bb170f14a9eb7bb221efe738d3736c64e723658c5786e6a662cbdb60eadea1a6f6e 7affc23ba4fabfc5ff82ee52075abe9f5eac781a5b67be563caf99be7aa349a93d3f1dcba273debb f05a415fc7f41d4973bc036f32b3e7765798673706bcad7e595f60db5e5bd87eb0c6ebc7e9ba6953 cb957b64b72bb27d42433c8855c707dc3260f2d2921974ef9f473454df4eb083f83a7749ce28f232 cd979c29b17e8e96073cafacb1be358391e933b38737e933b131b62b768dd7dae28a6c5595654035 1e4ba637f4162fae0d16fc6894588c13d34c08b4b0488868692ece0eb52f2155a1359f65aefd795a 36a79fc7992601c643d27e4f27102b7bd486bbeabea39fabc814146a70f1728986831b0c3af456ee a13f5c32dde26c318e05abb9380976f3f42538cca465829ae59480992a9bd769ba815ee2b4a8bfa4 89862694c9ae96d042941f93b255b3c706de09be85f063befbda1ba0b0583a21bc0c282c974d5078 9457a0a89778009d323e28d5f506280d0708283598b05717bc44a09399668054ba9ba022ef58ffd1 60a2aba78fa91af0ebb358deaf57a08667c9b3a9870f31d875e11dee04cb016527b22bd4cc4ae8d2 8093afd1a7f0f68c0650bfaafa01cf687ef3abaa1ff07cc7d37e51f5039ed1d3abbfaafa01cfa80b fcded67f7bbebbc06f6dfdb767b45ac22fa962b1ec574f505b0128d93e01c8ae04a0b4f49aa0b45f 622194f792adf7b8970c905db2ed3fd8ccc6df37ba9c5f1796ba6777c897874ba7bcd71ae84daf55 c84c5d725ad9390196c7edd7c626cd8beaa3863a7ecdef5593197d17d16ced1bc5a080eedcafaad8 ae058add6938bc1aaf84b036c700941e501b94007308eeaca5fb26aca7fd06a377bde3e9bcf1da25 9d75a9857777bb76f2e5807636cab371984ba1e10cfabd89fd52f6983d4e1e6973a19a84514e6a5b cd11f393dbd366bb9f0228d0b8070a4c32f464b0d63b98b283b700c2f76258a1bb17287767e17070 661c7daba0dd3d273867bc4e8ceaba7489429c670de19d616b75b74ff3f9d31e5fd7053bd9445bd6 acbb185ae9c7053573024719e5b171d00802ac6e89d2309a785c1707bff32984aa5858a5523ef756 0545918001a49c2ea0249d62a04ce2fd00ab5884df74b4bb1b2ca9e8f7d6dcfe16e93ba35a0fb127 5ef6649dc1f36e651c1598cb40cc3d6e31b2f68052bb9ea1a1fcfa8e25495c0dcae79d22f885b9ac 142f0349aba0ed1f239aadfdb304142ea4f7555522dba0a871e1a0f575934029b84589d2fcc0b71e 06e175a792e10eea54ce49d0bdbe9d1e67512b0b54c12cc00bfd51cae77ca372be25eff51112a55d eb3849b534724f8dd541998515b15d856565c4cda4b2dce99fad6dd0f814be7a1a95fc174f2871fd 72eaacd9322897ed446088e2c06fb116e1b269d170c6a77ddeced47a2353d999fb072c4c05a3aa28 aaee64c7b646ed924f3576a493b7e4022d2a331c6d5e255d0d2bf27add5e0c363739b7526247ece6 b0687ef3de7e14a070e7fdafaa06df09e7a8e61f59bdb36432a8cff8be470db5a31b0b94873d6f23 052bafb4a398cd63b727f677ab6f719a6f5624358eed35e5ec57edabccf940becda4a40cb14659da 39afee050bcf7da297224702a38cdb27fe9ca97e005148f5cf9dafaa413d540dc45075e44759bd99 6801dc7adab7bb7cdf7d168ca3339134d3baa6f645b3d42a4d8d3ad141b540dfd13731cd0a4ad68f 4bb2da3ca812c236adcb1e4e471782b3cde433e7965daf8a54091b0b03be3238256776939fc97af5 53f89b67b16cf4c239ea33f4942ab72fab8cb4527eeb41f6dca1aae1f6a22e3d4c95468a86d93547 3ab578c16a7294395e956d8991618e162f269b96cfce44d145bfc6b8c233b98f09c31e5b106256b2 c79f67b73e27afd826971790ca1b5148f5cf9d1f03147cf10fd58e15aaf2d9fdd754d9c42ce3517b bceb0884815b4a4c793c8cdcaa7877d8cb508bb7c4688a1b8d5051798798c4c59a35b97367c20862 3f375384a10e19a7c9351ff067b794e6b3dd798d5badf67d763b1dd6195d6a973f8a68c21e6dbfa8 16d3dd02284e837e78862a1dde29a8014676535e6f45769d7459da5bc59b623c6acabca0d3eaa17f 9b3fc78b6b293681a5fa81389cbb7983125e8b3177123470e12ffbb3ca5de153344ee30acc25cec2 b16789b997ab5da6baccd66933962a7f0aa0985282af9ef02bf4b4aaa16767a307f55d37f57e4836 be6bdb0b43da9bbb5378b66ab586792d81d5da4ac14d4e243355d89c3d728489b1128e9fc44d86e6 178fd3892b7686320b639307836e2780a985bef46110ef51cee65ea388a45afa0b5148f59f25dfc2 57d5f2b8088a64a20f4ab91efe4e41f58f994ac28d0b9b96a56022fa786c5de3dea5aad99b84070d f91ec8830b91bd4713769165d3f0e95c1becf9e54e27b9a23367196359b8d056eba9d14d34eb5384 d5cf509dcdbd4bd2945027fb59a2f429bcaf9e5f540761ad6ae5e1d7bc4e82bcfbadb0ab3ad3e9b2 69417511316cdfd0f598fc4c291b4aac4ad6f2d43fd387eb5c98dcac0d2ff3759483580a6730294b d138ee081439bfde4866748d1e8f235ee22b49f03db4458cae689d483417a54f01144b0608bf4bb3 b04add5ae8b9d812a0acf2778f62a1842da5675106b189e415f81e8e8374353be5e2d70ab7295dc8 edaa238ec4c3e494f5c51557a2623b664f4c30baf57a922440389e8c8d50994876e1c7517c28b163 a6d9ac1d3389750397669dd2bf104507bf51fc1742552df8a20a15fba3af799d05e3e17231100b1b be5b359cb6b8d113c9a5acc0f509906cad953f0fe846f324ed3b434eabe18bf7e2c06107a1dbc110 239f6387204e579439ce67a30b2eb38b3b9e770ee0b09da5cb876d66d43c6ce44ee55300c5b605de 5d209c508627fdc51894fc3315d461467784adf63477b73ca4bf729b859a5bc704d9f453eea5f7c8 6785331eaff21ba7d663ebadd994ee38a725c9c74a3b623abf1e8e59774d1fd47d533894d4beb647 cb8768ee8999985bc4ccc6a88d3dd472e55300c549f00cbff6bbd0933b4c40b9ad537e2bd8a9f6e2 cef8469bd2736a6e758e06b7578339b252e77a7d88820dd2a782f6acb066bad8a5a95e6b449ed2d4 9cc8b44b5b5cb9aafbc3ae8b52fb9ad33f617879aaa22e46fb68c77e15d00e81f6d04e79d4f816a2 e8e0f75e03c5ed2b5425f0122865afb3f712a861c3d76573c7570cf522c7e3a1e2642091f72a791e cfd6ea494199175763d5221de4d34d52380c064496d1a32e806f2fbdcdfea1a92866cb1c895253f8 8400865091614cf59198d02fee4ee5fb68c73344fb530815d32f5014c8f759a084f8f3e0118768fb e2dd384d9866f9abb1406312a93bbdf324211c84fce975e630bd62d120bd4e5209cf2a1f15b2dbc1 cb377dbcc773ab25d6395711e4357c124822ec12bb299bd56029de0be085708d868370ae329cc0d9 e5b3fb298496b917801a5615945385b947a1fbf07c3425e64a99a205a98b3413e749f3d03bdd9cf4 fbbee79edf304c6f68ab542a360c88fcd3cae04817abee6da2d2c5a89a3d46d996be414ebddb0149 7236bfcb889e02cbf3ba0be7af48667b1b43c3ed6607c6df45f470f9378a41d1a9c7006496eba06c cfe7a66a1b6545755b8d0ba8f425f14c18f1135c48b4b8c60b9bd14f1960544a4278a270afdec3d1 a703f6ce9ecc6074670ca1dca0db7a2f6330a90c91d4a5b5da2d72e33d7cddec38780d49b7ad8a04 de16aee1e56d49b1069f42589be3382851d39ab9adf2d1d753d98c27e6259c3dcdc56c39adf0f7e3 e2c512caa0448f64af4be6b0c9ec08fb59ec608d4c7edf9139157dc1b0832698c50b99c596999d34 599676b9cba615854f47705191a2f451ebb82ddf6be78d8112f63bd055cfe53e0500ed7709676469 aeb2ceacc58b0713aab0a22184333d5e6198a2e4525367188b566bcde1b5eb221adcee3b8f44177d 1ddd059aa02d0c49f31abdcb9d4501de843d1b2e5e1563bb2b2aeeb6ac79f18d8164f21bacdaad6f 6a26365ce34d6db76e7a45e6c78842aaff2c09aaecc8bb652497b8b80b8d13b2882573cd9d77a047 c6992773f4fc722c9fb36ab45aab19dad53c24cde9b15d4ed40b7051be35b6bbc2e3bd5aeb6c8395 edcda66600f41db9ad7bef355a99954b40e715d969dd569d60692f03e69c5832c358e95350130c86 480dab7b1032eb0cc992995474879d9a5a97c5511b8db687e6cd42b0be451e90e9b34bc3ca2c76da 966fa7cbda42f7daba6951ce8a6c51cf70984aa7964c4fc82ffbcf6b3984d358bc38bfb3e0c7f1e1 629cccce168973633317e7f3c37c963d466b0dbdb71f85f4e03c58108732c636da5594949a5a09c7 d2dd3ac654e5163205932ebc4967871b63634ed74d935e2d037201bf97316047fbc5383ea5e6e274 c4cd67e9c579262da7d7d932bf5067b9dbda982a5bd89e6e4a077f5abc13af104a6aa2a15661b2ab 05e50f20fc98bf76c45163bb651fade7964c67d6db039eeb06e8c8619ff026958b6d6a9a985c759c 6576c10feac5f92c952ccf9659509f6e8a767ba2ed1efdc9aee24dc6c6fe311b630d7b35ae39de76 64115ef4e38e23bc03b0513300f8d06563e4901c26994120c44e212ad2a700f26bf906f263c803f9 d6310bf2b7711714e4d17b265da47a5700b5b309004da86ef817c000546ec9004ae3e165cd785402 6d951907bb78170dcac4920bcab0a8fa465a737c94cda4fc5aa35ef20ed34ad375a4fac0edc42a53 a7e77933ebe496fae639b847e7b4f7f603f8aeea073ca378daafaa7e007f1ccdafa87ec033ba59fc abaa1ff07c67a6fcd6d6ff4417f8aabaa9fb5f540bb14d0f14008c81a23e5700b4ad2601743c85b3 6b38b907d0747c0b65f157b05b9e2a41256647c114df20f27b1f4bd7f970923f563dabbe733d2b76 0e6bf3e8555dc788f51c5f798c9c7e525fd8f1c36466cea56bffb1bac1ad8f22aab4fce116aae2dd 20ac5a36070a036600c23167a8051d5500dd86e1182b6687d75da745043abcd4fcc76018f3b1dbb6 e6593e32f570f474f09aaf9be0ba9b87ee92a958e076d06cda0972f3a6d36f4d06d1ad8851b53db3 a6c5c2ea71ad2fc646894e74efd5abd1fa28409e7ba85f3d37e73c2860d2309ca3aa388036b2064a 79e41d552fadf2a3e01e1f937e388dd63cfb548a7bad7ba9ee92afd2cced8eab0787414a27a7ff6c ebf66b3df46d3e0167ecd1eed2b4a6437a682e3a87c5a3702b2fee68961f6a44b7d5ba3d4f7ae52f 444faffeb3e45ff8aaca0f42555c8e027ec6289ca33a4700f10f1d9496a72c28c9dd896fc22dd273 0690eefaa554d2ed559f4d67e05e67360fb8e80ebb3d5e5d454b8439dd9a1ab4674a3126652eb64a e5b15e5843036e49f37bf589cd547aa97715819ed7ae17be5afa2840feea69ef2ea0cc40e8ab1740 21784d00948c13ef5c6950921ef900dd77279e1340a4ebdf92baf344cc94136f724d3b09428d0c51 de9b4b26717a28b8717b1429d136e0eb397ebfbb7251379fb19e46c0b7f17b8989d871a4a473d996 7c759d9aa4020efa28deaa5f3d2d2f9c4957725300b50a6494281d7a8635ede3f1fae4bd3a8f54a1 9cd72469d8c2f592b6169951cb5c0989c5635b63f6c6eedc62efd83a26e978c73634b2fe08d4fefd 9aba71af674d995266f7bae0cca15c9c11ed8bd19856cf56a70cfd0b5174f01bc56f80bc0942556b f304857c1202c5596d0620bc4783123630833b48163c57ad8fdd413d43da22a2dfad6582c898c546 ae65dcfbd8fcdee8829de611c768552b353689b3b764d594944ce2a25f9795ab7fcd5dd359b928d6 1a5299adf5ced6f6d510c99a0809014ee63f0a907f25f510bbe7fb6e543d1f4effc9fefc4b566f49 5a9a418d8de5bd2ed11a3be35b92b0e4eced6e429569c6407dbea13b7a7ea272e5e1ea968254e42a afa747f9562972921e7b4952a5937e5c30acf63a5b37ac2cba173c3ad90881b2ac9d5e7aab701a57 a1cc47f1972a282cebe5f0bbb45800e885b061c3ef4c1f1f809c3b08f243fb7c82a2e5a6cc1b27df 1f957d2da5bbcaaca68e846d4f59b42e936be1d0594bba61a097c79124cff6627912bdd64011bbc8 c61140d5cb09b184573d8d9164859feded029f69d8d96f215a2de11bc55f540b39fc050acca0f235 0535b76741b9c3db9e17f3b2ce098bf5ad6b29797cdc8ba7fbdd3ebd125aac172b29d232db94d5e7 68205503657676da83f74a16de2b890940b36921c6cae269ecd8f753725779f299d51ce2e4c6a9c2 e5e17d8edd18abd447112ae6ef5f3da559f5afbcce3ecb05463954ed1e1f592755033d53bd248f86 b9db6b3a75d762b7799cca5f21e85293b044ba7b7663bbb1d87bda736128b2f069b2df1ef9f974c8 7132b352b8bc4db9ecb6f4ccb150d02bbd9361ebd52c53b1aba98f22b4cc84551af6465030965500 35e9d57b5dd1a026e196cbb6f58cbd88c5ba6619cde07707aca2a8ba2ab446be52484f32d2a3ba2e 9d5d496c8afd4bbe2f2412d484bfac1a4beeda49a1ac9a7348b634895f1884ae9a4cb5c6a5a3a06d 896eb4fc0cdd809ec9ef224a80f967496809ddbfa816d3781d40ebcbfabd5e67d84737a61b0bdbcd 5a8f1fed07564beef560d8bbde16bd9c23dff385e4c53ef42111e4b1ba90b8ea1d5eca4c46dc7aeb 470f5fb070ff0a3328441fe94396e0e90667ea1491ec2742dc4ba4dfbb6449bf7a4d7d145f3dfba7 d0b34c869edc7df3be7afa40f05bf5e5c399a06cc2d4927ad3c00f8f9dc6c30549290e0d533237cf f899127379e1d41e574f99e2a9c56d1a61b729d397096d25474bca797630aad39c7224bd3bdcc87e 35fe249ee60122b8111c5da409ae89263f8ad0b26e7c559d8a8daf799de5a41076d0d6dd9e5b9b97 59d6f5fa9d4adce09b6471fcb52c88d1633e1702390171281999d3bc9f81b85b775b672b13bf4b5b ca614c91d5f282648a6984e033698a480c6bd2518cd3ee712a360bc7e9ae9f394e67a3e48f113df8 ffe7ce57d5851c0745426b02e8e987aa53480cfb6845b7aefcf2f9a8cf6f157d5839ae958d37a525 4b5adece7d0ef18494cda7f8c2011459643fa9d1367a6d535472312407eb44d40588645adb1e6775 83c097ede08ce78291795030378be7d87206cfed6bc98f22546cfde1a9baad775e6780a593d12358 ce89023713f6eafedd334f909a850bd36b355b232ea4ddba8a09ad67f34a098bb3774dcfd10e5c28 53942534c9d8b3dc3d9e61638ccb037e75b8b599fd5eef2ba7fdceceddf795a394d9ef847476bf83 1351a5bdb71f0028a08347885bfc4b0272b45aab8fa33667cfcdb3f4a87781a5c7b5554e81fbf7a1 e48c72f839768f892739538d52e2b9dd620a98e6834d53b4912a9271ef503f2e8ab90e5e6094d1a1 b42597fbea02c7b0c686e750a794bba3c70b93446d239d476d2691fe00a2906ab405057a66867884 539051b11d7e9786b0d75558cad412735e7f0ea9dbadd8be24e4830e77cfcf404184f9d33bf1a546 56676cbcef538c83c4c9643e55382eab68e5a0cdd2cd3d6a2b03ec70e7a3902a4a280c8ad0bac422 fd454edbbd72520ae9d76b10d2b373b98f02142ecbd0334846cb228cbaa0bc456067821410c32e3b eff5d38ad0f52ce350277e19248a6d317ddcac79589319b6d949a9d4936a3a64aa738c1d579354f6 a01f88d2be2e971ba83771fb08e86b3324d6d290dd984eb0b0f81a69f00c0f52b0785a8733aec9a8 f8461452fd73e7bb0085fbce0245b89b792f2b1c367c6961aa4e7fa0664d6928e34d54be0c866e5c 9c3156e304f55b3316df23383dc8ddcf649aab455de058c41870402fd514e626ee104ab3870632ea 8cfabb69bd338717ed11b6bdd2c469bb1ec41fdbc20dcd6c0b47bfbe5d4daea58fe23d80f2312bec a3bb7068ef5dba6ecc9d366f12ad7b32ded82dcea7785911e4c1fac5ebf4a0c21ebba7013d080a5b 32431de82344e6e403664b26e6f5974f94f1aa1964bcf74bbb1961b6e025638eb7b7783c0a0d6da1 6983dee869eab6d9b1d5c4a65238d63665755fff2840b1c4d80062a59c8f576bc5db65e4fc1f69ef c1b428d2fd7fbf16b320e69cb3a24812912c02a29243ebfbffc3b57bdf5bf73ebbf34cd5af6aeac3 d053357c6d3a9dc3e9d3ba467ccaf66dfe186232fc35d2acbd6273fd055c3464cbac5caf7518bd2a cce9777776a23a3cc413d122d071fe2bdaa7c50d8d30b57c2a1c7564573d5630b27738d8d2127d77 ec137a0a1a02dabc88f6de19f5ca7b525f35fe42ea1dfcdf92bf012007c307b58b9b37e7cfee556b 3175f99685974fa986e629a1e3cc756edc987b6cb15ec89ccd908229176975c801f15de0dfbe75c4 73e3671a06772ace151983d617e3b87744e7f0ec2ae050bf3c8a280ebc3ada121ae3bd3b43f7fbee f3ceec82d3f2bd0bd642fcbb00d55d2e3d11f3c96503523bbd89f32d932778f164465d2eaef034ab c0678931c687c4425a576c3238ea31fe3dcf73a7e517ae60db3cdc3ba2e5d2fcf0dc977687fab384 a344237f465b7643dcd39db6b6effa736b173074eaeede5d06767937008dee969ba2ab0401f5bb78 93b685e9d56b1553be0d8f10abb1c67063e93db96cc2ebfe5c576638d535a174175e9aad953f2923 42391aabaa7ea83fec17da7abfc23dddf273bb808ee0dda55f68ee0651a5bf8dafcdc9961bf757db f1777dd87c4592da88f31bbf99e781be56366d3f0199fb1b7ea254ffbfc53f30a09bb7bb5d4bec41 448911761d39fe89d903ae49d170a3478c1d7b7c2af2a7f9112d8ed689bae6714f377d623708d4f3 961beac2e6cbebb7cd3caba7cbc14d4e76ecf5aaf8f6d7c5bb0f56f7ed37b7da568aa51564d6ab4b e338682fd1fa66b4442c61bd7812e6f177a1f64bebad04113f9993aeec6cbf3d1fab9e410e5ef0f7 b48c99dc1151bb25b4f58c2bbb4bc7686cc731d7d9e424b4bf2eaaabc96a0b6d574b23798f4bb48a 26ffe5694b2cb0264acf2d6acbce892ecacf5b01a1ccdccb459bd143314d0536eb82bb3dbd4c8d20 41fcfd5d482aeeacb9deacbb3cefded88ce08a1f1183c4a3861248d1dcc6e7cb7b931387ce6a5b2e a579399688198245dd72b2f396a71766745f87a6c15545a697b1d998c4a2da9a7073b533fede84de 585ccbc3f1bcac4ec6395d5f8c5688bc1a159fea7678c7dec7e1b6e110ff84e431ff540ca041c403 a8317d80b2cd0600624f0880257a012abbcd19543eb527409a6a0120ad741b1d4462a0f2b56550f1 1a2ea83c4ec96cfcf0baa0225717b13918a643677cb0b64c7cb8dfd5e80dbb567492be6168c7f95c d899e4cbc19906881fbe24c81f350f250f388bfc6fe06f3ad389e0ff2af53774a6ebb4ffabd4dfd0 f933adfd1fa5fe86ced4d1f577a9cbcf7fa40229045028d500ec894b50e1a94ba2b2ff02c8c92f01 64331a0364cce289e8f00610b817c4a67428c7c7bed48b6befd722aedd4b58f49eb4d389203a79f3 5bd43c8b4e48aa5c1cf8905608faf31becb3b75bdd039f5dd59bce3a652f9bab147f037fea1cae4d 00956ea95bfac7790f2a6d6b0d2a40661395eb3740a20a0c90f7690610f545c7cf49468feb877c18 59853e1ce1f8a21fb5a0d32a74441e0fa9eaed1a766eb61af8e3aa17f4e94ec6675ffdb2c73bcd9a 97456a6d7771fca48f71d6845076ca936be9370020ac200068b54ba4361e118051d00415e2bb01c8 d0b902e475b641b5dfaf249016f153b599c8a2b5d4ab1eb55eaf28a43e6f38eceea37e10e42b9b80 393589a0ffe9f37e44ad75ff8a4881f75999052f3b5511b730484ce93541756c3d8337adc3bb0ebd dfcdb8f0af48437bff7b03a04b454cf49e9e895e2f06f0a3da06150dda02842df0a05ad592859674 a8c6affe6d99acbad873e8e5592308f443145cba54c58f57d4c01f93f8dafb6619c2138e02efcd8a 66fa6bbc2cf1f1dd85592f3ae56dae6aefe8b8659935a4fb7e8f73ada7bf5a959e7dbb97ff0d0048 a9253a2fe717806ea50fa8647a1d8094063f3b2491b029802a0d3c50cbb0b5c856d965789e9fce01 5b9eea3ee84e81f70548c59b67737d5729042bb7d87271471503ce29bf02cdd6c739c7aea0edfcfb 5d86919783c08d574799b4cceb14ae3fb2ea39ff9394a1b5cbfc06fe94aab089d4b0f10595c9f63f 81d2d59108aa5e35889b875335f4c7e765c0bed68c3f9d351fde3cf6d32587bb9214c4d98e573ddb e8351636322a61563dfb62deef4be6f66eda55ebd5f1d759f30a978b8fe93b5b7964dfbb86ae8a7d 44d3954f5e434adae7d7483794a557003d3bd21f52e1de22fb674cf7853a822a7a4863ba2761d489 7648704517737f56ee335ee1f87938da8301f663dc802deca275dfc4f4347f75efcd9d19f722fcf1 6dfad7c76c16a969a5c9b79aa3abd420abe958b7aced31b9763ff2d78a8acbcb9cda5af63fbf0100 052339112bbd93be74caff27faf89a064ab36992d3f8e4f58290b9ae2bbe989fcebce2ac4c3bfadd 7cd8351c89de24c6969fe1b1dc3027b43078e43ee3b951548a5b1de2be84b67f7caf1ab2eceaf7fa 641faaf899ceabad63ae72eb5ebf9032108d8c1ca3eae7c7a59a5c7f8d3fa5069a0560914dda66d1 18822a62a6e98f1f6a44f5612fe0a011e429ec68ea184c44d9f5c3467b537b37343f8b4ff6e7bcb5 fc0cd6b5a9dbd01e456e707f7ae842b5d409aab6dc2175a3ab5b4909d68aa50c84207deaa4244ff2 584912d7684e9a97679fdfc0cfa788dcf43f52dd5bf1cf10d4998f83dad856433f2ab9be4877caae d6ec4fec57d121dfae9aa819f32be7517049a0efb4a0707fdde715d53ec0ed9b37f2464a98939672 0cd8833ce968e944207d9f794dca69bd40544829272a42a928dc65f79320fe1d0018592b89d88705 2ad02b998896c1e827016efc6e253a07d74c6a7b7a4a582e3a0fa136b46c42c75f432a961fab8267 ea7bb6e6dfdf4722a37650505242e75c9381b2e8ca995d6d22cde5e64e5c1115422cb57792b03d3c 1c01ae37be02bc940a3c7ae7be7c7582453f48bd83ffbdf927fc2915b16d501987e53fe23aab4699 889beee3168c3fafb75762b339fb8d94fbefc450c1cc39c0380386585dabf3beab7627dd34da4e89 58ad20f3c1a8222db69fa6b8aea9036107094bfed1130e7cf5f466b9576bf1e21a341f7127789abf da8b3648d08b7e0300ee1e6e3f4de047ea360325dd099980eab7494614cacbbe28682fb7528ab276 f359e8be46ea78ff28677b17ed555da8aa2b5fdf4a3c8f4379365b64a402e797c4f241a8f10f77dc e66bd5d9946b7cbadb2bd9dda479a0aeedc0d1d873b3e9b3decdcfb2e73588d9f3280c7f033f5213 840ea870300caab96e1a7fda27c3a07a96fcfc4c7a3a073b4a53b358dd41d436e57dbcd6cd6286be 93cd8a7cbbdc97a62cae45572aae6020e80f39cf9bf812e6f03cd4bc927630647d24b761fbab3a79 61f98d7c19751cf7c2ea42e6329af1510229f80b697e81ff2df90b009e9dfe23f55e4da422833404 754a8683db4ef04acae961e3d347fc8a2742fdb1c194b9f6ded8a47a16b2a232f1eabaa4e8842342 f7f0a7a51ddf9b0c87d350e9da2d3d6b6cdfe77a9791759933027c3f3253b32e9ce5226531d3e098 61b2ab7598e010fc06fed43903cecf022aee55fe48295ebbaec96052ee736ea531d52cca64fda778 d8230642a2933b859e8fb791ac73f2a29fbb8bdb60f2e69f9e1670c478fabd7695a078893b1784f9 16b76d2697594dcfcb3e81d2ead54a73ded39bf6ec45ab5af34bab6427a4d55dc7ff0d00787f5693 413feffe11289da63f8ef1f194f045a7c93a35bea2bc87f5b5fdb8eb41ea1dd42ca6315099f07450 b2167391ca6beb26d44a5593236bac77edd14d70e17831c78847143eaf328d06bd89aa23ca680db7 1442d30cf9ac4126859c1d40ede57294a016fe0de906e67f28fe53aad1f693116a9b4875a8594489 e5a3b7ce3a94e5661cfe39171dc3a86244e64edf335de5ab3737d23db3a404535024ce9966f46b7f 73b02f3cf693a98f5964b9ecb9440c4b34bc80ebe4f303fa64a307af09e23421897633d00902bf86 84a57e62c2ba7ec2df00802936d1f99d040091e9eacfa788a6300b074f7deb3ce20dfa8aa319fe80 f581a2d9fc385687eea3212b417621a2fd01ceb71482bb3212d02e023a7d310bf00968bdb807d4e1 5c2d90273c5f253a88ddc5cfebcce214ea13f2c4ceac348dc1899def03bc67f800ef2556c16f00c0 82744f46523404d5ac520335d69cf9a2bd995bced79d9825055de8cd81c1a9d132e72b52c8a6bf46 82e3f784b7c7d0e11a9630f6f27ddb0a53643b066d541c874ae6f89870ded51cee1bf9ca29524007 fbe8a53936cb4d702cab04772ccbee7d4ca81533181f46f13f21cd2ff0bf2500765e3aa8a862fc93 543ac64ff2c8dd2dc4a6a9308d8f7e7a78233592b6ec2d07198ea4ebc9588eb7cd21d717911d9bcd ce29660dd9228d7e461a69d7df6f8296f074b0c187e361e634e935606cdeacb68fab6e6f762c0622 71b8b363fdb02d5ffd63f13ace1f8b9be6f73700e08fad03a41f7c40157cabe130974b5daae6e25b 3375ec3575d52b7d1a2b4ba3ca49c61ab305a251c97397c2b5c3ce6ed09a810628411f0f199e7404 5225faa0f93e7d0e4e80495fe67bbc8558f9a07de8cea1d2bd2fd0475827d1036b3ed01a5448f778 a087f1358f3e36d7ec6f00549ac00088097d40ed86c0a6846c04bd3e5dded4519179c9f7d968293e b5072fb4579917373c6b5f36df6ba5063b03778c11dd684fb6247d02141e07bc7012c9b58eadea35 ebb86d97e303da874a2836469a7b8b1e4cf6c45c3fed5bd1f4be733792bf6f719dfc1eb7d6a57f45 ea52fdef0da8e0551354c96ef41a50454a3f9cef1775f0bd0b3254d83882dd29eff85e7ce0afd3fe e17159a39de07c20ad0265c79716d9ef0fa7382f6676a7c5ea4562eb9dc61f7747493b3cac5bfa6d 0d7d79528c36fb4e794f7285cebe33d9ad767ec63eefce326aeefac57bbcebc55ef6370090c5ea61 1fc40d65c0fd1ea632683b0deeaca623b4d0f6068deba765a26ce1e45c983de2df687c2abc498698 85f827820b2759000d4c55c2d151d7bcf5c1f46dec501fd86714e73c71ef6680b1ef2e4bee2e28ae f3bb01c137b6712733dd5e7d9ada8e2f45ed27c929d67afe062242859547b1176e552fd00ef22acb e33c795ed3d72cf5ed30ba33d8d256634092c1b19e1e77152b2765a49818a46c9d9f33bd7643805a c77e096d59a3fa9e6e4f7bfbaeb79bed2efddd663788cea72d37e699edf86b2b1b71917f6de685f9 6793536fc9126e3b9c272076bfc68f4b35b99a9f49667d279e878d2ccb5394b7ab3ecae689bc7ec6 b2589b0c0e8d11fea5c0e2b404afddd158b2c703061fe8bd8b37d95d4057e46dcc968d2d376ada9b afd00937f35c37b3c929b3d25a596d909f60d812d55e173575b2baefa2ed0a7a16f0a5812df825da d09e0980f71bd0b0dc68a16497d9b5500f850d9b01c515ddba561d626cff181ea72227978f887aac a1ad57abb50b28a8bfe506f178234edcc55a593cb7eba26a61abfb26a0569011b04bb4ea0a4be415 27ff86178d45dd2dbee7165df7e6446ffc99b742b4387359b136a347dfd1acfb2dce7e5caac9f5d7 50f88539179e797cc94eeecf1985e7c2213ef39cdb11cde3cfbd8b41f6363e5bc166f6e5c0ba7823 734b63bf282e9ed8085ed4ed691a733b273ad3f6cc6546bd193d588ca601379a4c2f93c572124ba3 f5845b2cd0c9a470c0c7e266478de710791de70c491eadaa9a9ec07bfe06d2ef37fbea72c6c69638 a66bb95bf7341bbf1b87ba36c47603d73b6f7202795d415a5f5cd4df913273e9b73ebd8c0473c2cd a8f7f8ab50ee785ec28391b2db47a3a2b904c36d7dfc1d42d63c3740dbc3c200f12669004c1feb0f 2bfd7a34aff788f1b0d96b7d57bdae2b62e32e3dc7a6bf015086bb24283df63c281d540324435262 adaef916806bd81ac04cfdf2b31ccc3a6f5041f6c998967b0f001cd676007e1dd804c90407abd564 f9a81e12cbe19afa09b9cc10c0c4701d3fda3411570f341b616f51096d4834c2362abe02fa853e83 de7af8f0c37243fbc18f4bf53f37ff84df90fa1b3a7fec9bffa3d4ff3f9ded31953ea6e4e1c29f52 79ed0b20566b0378c16d016c0e5850e97c12d375469541a5eb0f41a59ad8a1953cc925a2ed0780a3 76b27a781f4af16373aec707e8318c0f99fc3aae4a7332c2a21517da2d3475a6846d1a33033a241c 3f6cf6defec5a9be3c30cb18bf8144ea8004e50c2326f5ea3c0054cee60014865d004bc62e9188f3 a022d4feb00804010215ba30011574710295392fc68fd07ec7073c0fe21ad484a297386a46a73939 891a21bf09ed53488764ab2107e72aacfbe11a7e7b609ff73c3eeb795ee6a859ae34b8997f217574 fd6fc95f00e5c98c4a9ac0554cf44689d451250fe07ea9072a9938a939521040e533f50052761050 f97666b1892ef0b8763bc8d1bb797e47cda4e1844ece84d24a232f7e2bec34cbd3c0d39bdbe0bc3f 9d835e2029c99b904c6fe2b38e2bd5cfa19b277681739bac1da7e44d5fbf0150de2e139d6df9e7bc b5f2b4f24c5efca808e0736f002a53e8002af65b02486254016493adc5e6b7bf88deaf2611b5ca7d 39a4ba7d2bf0571d109cc1060efa24def1a32c33f55946ddfba3ba4f7bfcae7677a565f99508cba6 1b639c12fd896ced508b6d38ca7ad6e114bd7f03a04cece9a45e552969a0a377f2e27725007bdb11 a8f0030c20a38202909b14c64fbcd2f849cd522d2c420a444410542239180c22dbbf6efdaff739bb b02714828e976db953577ae4f6eea28fd06efe7dd46c0d5d5ad6633df0adaab38fdfa73d15bf9cdc d17f75b0d9fb6f4837feff4371227543fd29151c6c00cf25f8cf40e9d2110708db5141b5eac511a1 47b5b0373467c1b02213fe3866144fcc1c2d5756071fb7605421671dc2ed9fbc1cebead4de51f0ce aee47ab47518a2b777338bbd5fed78eb99a187fae6e8c0850fa1403b693ebfd76f0094b96352a55b 5d02d090777f061bfa510115ce98026475254035bfbbc70df61186bd40aef880bc4e3da9b8c15d45 aacbce7d64bd6c23c701cbe4a725ab66f69b6fbc381abf48afb97efafd056146ad1dffe05f3bdd90 84e9db2874d9f431badacb447af9193a9a7ef1ccdf0028dfa97352af6f19409797ff47546f258ee7 0051741254515e0b3d9d0d821126543cc9dca741e46ec9834ef6a3290ac97cdf30df040cc25717bf e6cceb6d003f040a6a3eb24f64642c4bddadae4e67a4a60f2845ab6cafe6fd9889dc7bcdef07eadb 9c3a6a6bd07ca430fe863470ec7f4b40d96212a99c93480d33e19fa1b28ddc1c54733e15375e0735 88ba27cfcfda14e496efad916de6ada3d50886fcab27dd7473322f5b8f9ca6464691a1f33a349954 7edcddfd5efb7edcb4c7ea5b9cef547c6eb037aaf954155f845e8aef5e3d8531545b8e7b8cfe1bf8 53e73d311fe12a1cfd84cafe9165a4be00d579f91c759a23c5175623dbbd676745db74bf7dabe910 e8336abf98c7a2fb918c12891b9a11d7ddfb5374816a9d6e859b3b23915b17237b0aa3f10b39ce3e 30793cac8a92c0af1e92f0f9897e90c4e6d8917242dd48a1ff1aa01c5f13a976f696f4a564aaa970 c37af2e2074b50159be7b02fd4244fb69b6fc73876731691373aaf01d64863a01e4ba889eb1a6038 ad1ae754d59e73cf1badf73c2508aa5f39be7f4ad2f7ecd4a5d93b1a4ab96d67272ea5f355b8175e 77e13ed8bec5a2ff7585edc0d0ff15a9e1f1df1b00156526a9da4f52abf404246d146d262f7eb38a 9bf92c158cb31fc1bd77b20f1be30adf77af4937cddcf93cd5f5a7896a3573c0a8a4a148b77eb9ad cbe0f87acb99091b4af9da3e5ddc8aabf1a622dce96357805ec292475b059a7bce3b325fcde45f7c b58d39dcd35be9bf813f7442a57252a5c6f60390229686ca1ed3d8c188bc38843f5b9a9c63488e66 9150143ff95caf6adcafd39176b4f18deabcbfd4adffdaf2326fbe14492248535c173a4955b959c0 3f72df125f1d159b1c46b52657ebb93f5cdb2553b812ecf971b5fbc3b4095c89a861fc0600545713 a9c366eaf221be0019d089d43db50afda1897b4a5166ed574d55df67540fccfcdd2beba858e8dd89 e93e6d0237c6554ef2c7a9b2d2c22245b1bce8e842a5ecbff96aa0865c43a3b35732cb55596f600c d81e99db5ec20ec35fc25953677b8e6d27f08c5f23dd53985eff948a4db53fa37a8f620b5405611d 44247e70efdb3d6dd9574a7e7e9a4bc7480cff8276caf91db5b7ac2f9471881e24d9f7ce22945908 bcb98e54ee8d5ccdab0d362eebf9b3f41cf64b78ef41975166de618064ac9949dc629969d1bc3320 60ad0492f11b00d0c0bc0048d9e97f86cacaf744e7475ffb1f7f9c1a858ee10d88b70fcd7873e17f 9ffae1bdcede3b25a1a1804d3091e7ebf95edc1625524016cd2bf7d675f94a0df70f96a9749d0b1b 176386f73245265babb6ce8bcd7c7e2e40f1f95c80f1db59020b2bc121adb49febaf9148d5932a0d 480354702d91ea3f5bf18962167e6e505cd9a77c7c788d82e0fc28c78aaee1adf647ed934c5dc918 c6482aede1b40908e896c4390bcf5eae54c048ec60d7d52ee341eecdcc202b3c2ff38f3cbdfebcea 94decb8fa95dc891d42e6ac97459fa3ce94db66ffc067e623996ef0b8011fe012aef3007aa9db81d 91c172ea2516e7ccea8ce8a5b99cae4efa519adfeedd2e08950f3386a55b78e90b0f265cf20d697d bcd2ba7366878d3dcf7c99a2cae436cf277dcf5f3c4a77d0b409504857aa9247cc1b116f678e116f dd15c8e3553749d3ca1abf01001d9d44e74c33936e9f4974eebeadb01f7ffb0e2acebbaf7105a499 fa8cbdeaedb5965a126ff14a7365f98e94c47d13edf027eb31bb7ad1086547399dba647633eeac44 5f85dea29c41a123d421ebc11010446307e32e79ebe33452dde3dda7c2e32e4d3d08028afe401a38 f6df9b7f02809828b18704eff9c75902550769f9dfb6d8b4ece70b7adc331d44b36872ad5ea81bab cc3bbb97b4f97ef3427d3f69705d981fb1d7566d9bf69bec4ac6cfeb639da1e1ef53245f2cad11f6 7efec6bdd5243e85d369e93494d92e0656f00e9b90287b1ab21be31456df668ac7af91f4a5ecf5c7 f048f74920723b17bf3e50dd2bbec29cb9300a0f1d6b3279f5a24513452af54949ab370de195373f 1c8d551196eb9f0797dcbcb03c6f48e64099f73a4de2bd274f741c5ac5fbcafc791af193109bcaeb f41bc1518ad9f671c157b7c74247bc1e17d44cc7a66be799c2fc3592be54e1fe88e9ae7656b9d067 e61953ca6d44dde43737352cbe4365515ca64d40d2a4f751acb53e37ced38f1e0bf87ce992c7c9ce 79cb1666d4d36476641b6ee178c0baec89eb7232263608f3a85450ff70af9f0b0788f03aa851996f d0bdec5e93f179af1f54b7f0fa0ba9a3eb7f4bfe028073b5446a84daa0aa2dc1f3ea0794fe90ee17 3568715725cf5aba68f6acbad0fceef79c1f9a12fb0970f75230f3f97345241bd4c9a98c08bf28af 533f347b9e1d4fd31674c6f24f4b3896b4bb7e804dc3450f4590dfbf76c3f6fe5409d07d4359f27b 2c7074b48a8e9ebf81a4819e122be94ca7bfe64d6ae979598b1e76ef880d52c939f045ac614385ef 5246f71a9f0607569c1d39e64e0ecc33f2fe7ea8d68b84890bd2ec9cbedbf70c536ad7ddf10e1dc9 c3fe35e3d1a3debdef2d64e3ee5b1897df51769ce6ebdc75296ab7ebce2bfcaedb3be83b0a7dbd7e 03a0b2be89616fc60b8fe5dcdbdc4944da2bb31d8b895582a4b94be7fb6545ed32bc9496cf3432e5 fc98e62e74437daae4b97a7170402d322769d8ac60eb6ca17bdc153ef3c3017276e8a9fa26f7f6c9 17761e1519bb5e580bb6214b415b769c1d6e875f89dc0e1fb0b21d1e7ac6df90ba54ffa11820fd8c f864b9cc4ac366a7b52254463b116d97d16bf8f1f1cbaded95ce8f7b6f483717ab2d79fe6e096274 9bf0a74590bf6365ceb38e9547f839d4de7e19c55b51ba6969efd09fe1be1365973b6653c3b691d1 bf6cc7d59dbaf9bc1ede6636ae1513bc469b5c6678dc64a5bbf81bb01a9df652df53d3f96d5429a7 0b2851ffbe37d770365b33db7547a49be2ae440e9a7c17179eb7f969c93fd0a351e7c9039639737b 77d397f65d7767ee2ebd93bf8d59f2bb1d7f98d2e62bc8b58d387bf73639c59fad9575edb02eea53 6675df4bf7e431ab6d35efada01709ada0776f966038ff358cdba739be85b03393ca06b3e07a0834 67ca85ed84f403c9c6c54e54c0ee13b37a786ea8164a54f1e14fee076733db0d82e566f3e55be866 9eed926b653960d7abe2524a330ddc57d063f35c1a07ca5fa235f9bb78e2f7e2026b7d1b8bba3718 cfad33b39b137dff3c6fc56b23c1eef137a4beceff2d51ddad3e966eee7ecab9b35cf2a3df8d01e1 9387eea97895b5c3738ddb7b171b87db98297e37e224535caff236bcda965ff5258adc5a8b27a6f5 1758c39ea499069673a213a613419a67e038ebc621390df87c9a5fa091a61668df27dc726b4d2645 2e1a8bdb57613cafb4dbbf0179012dc73c4ec6e34bbed6e8939d4e2d753f1cef3da181b6cc06b1e5 7a21b7566692bc82b493b6781ed7cf39d1eedbb36e54f6a69771259ec42292994cf2507e2cae8ba5 714efb4223655f4c56d0cf5c63b8ad7fdb43c8860603b4034d0688df5df59fcc32dd82d5afc77ba2 6771329740e57f0da1a6b9c38bb880bba4bd2bd7b0c2ad05edbce7b8b9c9f1cc78696c97ebb98523 e834b858a7093755a8f1bc48a4db4a47c5c7fc3a4c1a9930405b6db9ff4c46847e3d84b51e31cae8 5d970f1fdd5ed67b752ecb97d58eef0fb7cd6dedb0f57dd89fd6bcf6ceb672efa8d45cb5b3d57f45 f298bf6e40f185ed40f1a490a0d80c7850aaa94f50aea87950d6c92e80a0ea1a409b64d10b719b57 82c4e880f8412381300390903d02881a252bdcf43b3144796f00d18d28c1e9e7541208b55b003a34 26f1fedd5b45cfd6fc18d5e91519e2619b0cdc46780a28fb750cbae40dfd354031a4f77fea1cc589 cec5fb05cad377e1c79ff6e17b001af73600ba5a2c802cdc4af0cd03c89e3501a4a552f5029660cd 2590b444796803486c8304643901e8c668a1348d9e637813d5c5f62924f23d3a70d7253ae8f28f34 1eda0fa61296e2f86b80529e4bf4b206058a57480025b36c83b2902d0168f4e803484577002e1639 000f641bc0c37a11c08d632781b3fa710d956b448264a2833e9a191b42e8c568af9c899170042790 7a11367acd438bf2f6817bb1f1a06bdb8c7fe9dc187fe0b3672f664e84c72d0ff8bf228d814aaf89 d44b52abb76722f55d179217df7793daac2535c28221806bca01c0c7b10860e6ed02f8322e279007 005e45dbf8516ed2312a2ce4b8da3d98d133908208c3cc6cd468662ba1e5f4d39dc521b1275701bd 3a1dfc403b535e6c92576f525c5cddaf35bdb822d1a1dcf9a449fc1aa05415932a7db974d2401339 e5ddce07d07a5d0170a13df9a9b4837504b0b9974025570812e0088083f738aedae62e7aed3ee7a8 a164e5d01ed69e21618f82b0bddbe702af7245025a367b416f58d9f8c369ede44d06e573f2d402ef e63365d159a1d1d52955dfe9e60bfb1e9ae4af014a3d256dad4122f58ccba06cf321806ee72a8017 f32980fde20954a6ea0d54a851149b08538dde25661cdafe711b76ea54fa98c0e3ae72d0f384a71f 1ef4c0670b41de1f72c5aa07daad9ec73d989d3bc77122d17664edfb6d2fdaf08891ad4799e2ad2a b5b9bc5fb325fd6bfc7c8a981b87a409c489544f5000d478c500ae3eea007ed0f344e58000152dba c7c7593f8a5aa5311276fcee30e8df91adcf7ac8d9fb148a92c7e3c8d3cb1848e04abd76decd9f7a 55e756def69d95704f4d5c1b1e8aa455ed70d737263fc5973df82aaff6dde29fe78971314389267f 0d50429f89ce5aee0cca13ff9eb4cdfc07c0e74c1354dac63291784c3facc4a653d0a2d6a51c0441 a708fb57d51d78026d6c5d7944536e61b6159d35b6326d8d9ff8366cf6b3d6a13842ac2a77eabf4f 99dbea45443661863df6fc00ebdbf531f984822151ded558cc6cda2820af7414f8b9fe2b4089b612 a9fd6222952feb7f44f5c25eb5052a5cbc0248433e4744c55183c0f23d7f5233ca5e8e267a8efa69 af6c5dc99096b9d5c49fb0ebda497f39d0ce7975e687af197ec64573d45dd61f3c868f0c6927eff5 db5226f5d2fd7dd176bb1aaf55a6fdebddac74e8bbd91991bf062889c1f1a7091cea0c2883fee3cf a8def6b00d2a49d78a8f40a0c3de4090fdc943b0dd1b84151cb859ed58cfa3b64c8cb9f5e9e97f3e 1773dc60a4c72cbb318c85d5b575151e00bdbc9894b4dd156d69959691a698b8d7aa01a69e70e4a2 3637387f73c6c2f546b5e4f38d1aebe4af014a4622a62475993f22fa61fa94031562db01c86abc8d 881d45f960bb49f3407985c2f1e5c021f2b54ed947e3453ffa137382501b43e940b8ae46cf8ba61b 9cac5542e271af39a8abe208f6bd392bb6722381dd53ce01bc95a3c69094af8de755beae114e1e37 215a1eafbbe40fd2d0deffdefc0d7f482d6796973fc2cf61574a0c558bee0244d96cc3deb17bf2a4 73fde2e8adc1c37a274de375e6d6c8439ae15da334f9ce3543e5d03b56eda58f51f14f9ebb51a6ad 2a7efc782983f62b94af07bf207dee85ba34cbac17a2dc4f0c0af9d8be88b2cc71a2ec1b8ca8642d f2d70025f79be86cef7fbee2fe44f55620ad0090bad28bb1d5781d0c63e8e8aa85dcd93ece73eabb 9b1503337bd20b46b9d16a69879a3851ed2abcbed1fc03532ecbd359e6da2341fa2e3a9a34bbf61d 5176a75fb1583d5684cd5a4d4fbee075b1b0e77557a279036e73bc319933bcb1dd92bf46529b450c 94b7f8e5cff8d38d5704c8d2e84584862cfde9d8da39fbe69db05ad8534e8393beedaa656cba9b8c 76f09e55d57687fd5b0f3ce63258aef772062e13522e745871553615619b934dde18ea018f904191 7b22dd36575f5d3757ab5921b9fa4d63b97a08180e5b54a9bf90664bf8df921ffc299513ae3ff192 498fcf940022bbbdf09cb1a65e614eacecfa0c3dbe2e53947b2c43eda123c34a7c6f460474eb5fbc b6fcc99e2692b42dafc5d54b3efeb8bb8563525fcfa5c03db581c635b233f74a0cd00ceb124a9da5 2bad25dbf5e5134befd72ceb7eb40beb955fd4af01ca3094e8b4f4640542977ebe4655ac5222d58f 7b01bbb80cddf26b32b35acfdece14557036a0cf49bb5b1327b805d0a4a88c4b8fa624173b43718d be9702fc39eef9aa52c7b9069f63afc4e3abb2b41abc2e6126175f2ee715c2c49195c672301cd13d 5c8658e572b90487cb854558fad7f8432a547478001b551720c56a09542bdfaecfabab9e8334f3c3 d7857b2d1e2b91c5b557ae9306f8abee43f794186de4e55c85ac891be8d3132a276acad766e3edd5 b6628cf5a4fb99edbd45e932343893e1f3f790c98c40f92c7e89f139dfb3f66709966926c3d71866 5a9a527f43fa6bfeb724e94ef51380061f01c0df9e079041bb14378e46cb9321b8695953a9fdcc2c d66303be2d8e777b55966e97376ec9221666a4e2715111f6b4d3e16b9ffdf8eaac8a2bd66f3fd326 7061559464c0171598cc6ba29df3fadca56ffb5b812edd4a5d4a5b5f76f4bab6a1e89b1431e742a5 42fd1ac914da4a74626511545a13ef6717f7cfe90a77b4ea6e10097ed34c1f7e948477577b09d99d 7ade9a9cc2b7664f49b1efb16070cd325fbfc9adab8b0c862c43588bcb7571d9335377489c17af2a 4bdf3eb04a69016c5195e13a4b3eec578bac21bdd435443ebe0849ed5495a1f49a4ffd1a7f4a7df6 e53f8e3d40cc5529ecb77b9083ac46d927df623ffa43c19b77529657b7d181bac88b79fd679ff42e 4b87fc0b2e17b97693a9b39761b177193f942933bb62bb73b15a3dd165103254c5b765b266d94f02 af9400d134c93aeeace025d17c5a38f116a90b791cebf43f21fd35ffbd4946a801fe47447fe58a7a 491b458bfea7f7c85816717e3d4ac071b5e60e426e11b19c2bd9eb8c92ca58ac0ab5c1d6e5c8f927 c786fb63f53221e20e932f5c26e7a23b5f53e909a1e9639e53ef4ce0b124e06efb61e2dd63263e31 d602390db6607162b40b86774f6306777b0cfd6b803231c6ff883e460a9c0faa3b34e7ecebeb34ce c6cc977257bdb61c9b6a3fe24acaac178f24d5804e82a93d65ce2186d6b5cfbebe17feb98199bce7 36696d430c29346cccc90619ee89f6f246e0bd09cf9f867d5ec7b8ab176099cca87214cfdefcf8f5 e974eb22c6512de6341c1dcfbf463242ed133b489712a90bc50bd879d534670486eb68b4a2549fa6 456546b7b3d2c621bac273a5ecf8964c73d7c1194ebd1c1741bf454cc1ee16e87dc1a8523591e813 4eb331c5cfa6b739b1dced84f1679e3d4ad79b76ccbba17f58f747e543e91d4e0feb3a7d3ce69fdd cb7181cce87f45ea7e48af49b7a7705069da778078cafbd53faef6067403e8bd83bc8eca6cf7a024 c8be0442a30e6a9c57592dafc36d8fbe886ae6ce1403d6a5d1ce204b5a1bab425021d6c207547b84 7deb6089e5603b1d058ec5c2833994bf2f15dd2f4a2e8a1458686f72bdd9de249423ba6fcc1854c7 f6f4af0120534d74be3337672b42c747e1c52fef76eeb4f971f9a5fdc6d8368f7cfb6a3fafa14442 2c7f05e3cb62513a9d35e125d25540be4872d08cf110350a27be4cd4302937ef1d577c7576d8ca30 8aa25c9edebf32bddbbeb1a0bd9d5d84e11dc1dde73b62071d7796c6a45e8e7d1d5ca95f2369a359 327ed6f292c9d3ec4c3b069d3fbc7e12d472369c571650767a2a4b978291cd3150fedca7cd8f77a0 70c5e688207b4e4701fcca4dbd53ae50cc62854f5039e817b18d9a3c37ddbf1962bb6fc622bdeb66 9eb7ad2f77fd2db37c22dbbe3d9b6ffbbc70d89e9df2797bd6b3e4aff1337b8e16f49b3884337d8f 14fef4faa96375c9b79cf192cd68c5cdd940a62f1a6b4465aa9d6bf6c8def0b4c639798a9fe6db8e 80ad0ecdc7116e15fc43b5fbcda28d6150d9b7a7567b47cbf16c1b968ae88fe7763760379c2f3c36 934b3db3c90cfdc1fafbe96fd75f8725d75f9dc47f8d80d952c747dec90e7e5c7ef262c3cc78abdc 9ab199f665421fbeab344d1be9753a1ec10e0408cf3ce4f6291f39d3a36638bbc381354934190c84 bd8344c6ce6f38ceae4f469f2deb7f4b9bcf0a696c84d26cbcc9eef0dd7ae1bf2febc2a56facd4e1 3bb3da940fddd55ab7d7ab3515a62dede7faaf7806dfc3e8febe0b032583efc742ed931bb31c418f e8c3ecd523d8c35e3c49dfb78795d562e1608add068a9fbafd3d25cce6bb0136d86dc76437f5726c c4ececb299c9b8b2568ad4635d240477756f9b60b5cd16cb4b63d96a2ed1223e5b3c09e7b4c03a69 e8a57fb317f5e00027b0bbbf866616a9f4fb8dc25dc703c16cc4433626d001fde8f26dfc5afb3430 a88ac887bab074f67463f9d90dfc7971f3e586c826270d5babfb06eeae20a3315e1ae864b9445ef3 fd026bce4f8bba839ee744174b930c88f7193dbc3fffc82ff00da78130294d8739a295e03b9fc4b7 0d31e1d68ef26bdc066abd2bee566effcadc57bd33dc259b387bbd560e7ba75ed977ad2ab31d4719 e9275079e6e9ab6dc9b69688a9f98bba750333f77cfaceba1155980657019e0ebe426dc2cd85d6f8 7bbb76c7e25a1da5f905e62365ef6f47c557f634bc9f3aec107216cac0a0f8f700ed653e0324ec97 7f903ce6af9bbf415ae7be7dae5b5c75cf778a6ce021be2f1ff61531bf8d4a4863bdca32f3250ae3 87b9852fc959376c5d26b150e4c75ff92b8f7377571d151f4f63783fb8e967a28181dfad01e28a4e 1feb717ecf622f51aff5b97cbbf48ccd770285873a97d5abd18e35afd7e6f6a569ebfb6c6c5a2236 c77e0dbe29635d66e99069380f41bff7a583e66fbe9bb88c064bfd8b56e744b3844c07f1ab3d1617 d27054340ed381719a2efb58b7b2ee11c3ecaeebf2d96337a91abc332c98449bdba8544b4429ba95 7b1d99e6aab9bf36eed4466840c1265ddcd6d1e146ab3df9fdb3d6c8eeed2ab13c06887b3f825f03 14787b0e0a156f0b0a480307c5722080e2db7440e9c8144149a9b44129b8ad40b93a677e5caa3dd7 00e5c5384ca094407909b5136ca609e45d824f6299adfa7c82b3960078a0bc9e6663bd7986e3ca5e 6a4787e76d10be1bec306c92eb7ee07446bd80f49b9d14ed1fa48eaeffdefc8544ea6b060a1db003 85fd8400c50d2481d220eb8192a997411959f440b9fb4aa4d2f41594a5a29920f98565c98241596e 7513508b04efc4c4552ae79f5fa3aca50469bce8ad14c4fbf2a81099fb7535aa3dc96e8837cea380 eaacc741c7ec8c7cff5819f84c3ddbfd354041f3922a9d65129d3afa331114d581024a4c3300e54a 9488a1d821281bd5a4be5cf10aa04ceb052058c9247ffb2200cace06098475820f1eeb4a8d8df7ab a992807fc515f008a2e3ac500cdf4ab316b64ad37ed0ad8c525fa7cf1c3b737f502c4cbc488d46de b865f67f0d50b0c3ff48fd1089f95fc3efa09cddc589c41a02ca8e3602506b8e0268ec0a00da6eec 1fbfc0d6ca0368d76ac44676398ef732b38d91a54944cfe2878b8e7cf916d5a7a35768654f6188cb 4e3970e24fd30f926eeec585cfcc1b8f9da5fb85de7377865ec76eae8a0ffe863476f07f4b128199 052850f01e14d7f7d487aaea7fc474973f8b1a8036f92980aef411401622012816bcd82082428cf8 c56684d5917154b766bb904896f0614b3a5e037721a4bb5703bae0bc832e970dfd60b281fdc1196f bbdff17ee4ce446ae928796ae314d7b3857d6f8e67f6c6698d7e8da407e5139d420305c59b95acab 331ff3a7a5d94ef27ed7e70680debd39806b56b2865d6ed418b5643fc21ef74248a84a2370bfc638 e8f58c8d1fbc02c2bf6c72d7e49720aac7cd076f6f1c6e22f7cb1ab09ba3ac8e53bcb8531b229f2b 0bcdc6e9b7350b3183d51b1b18f377a3789fbcac3b37fa578062135e8282d34393175f6492da6c26 ed70544e63baed34f61c5b025886c8b8565cde42fbb171034fdde67f22efe979dd03c972dc9b08bd b52b06bd933bc7e65747d156aab39a626fa7e88bb1bdadbfca96b12bf5de58219cbc88767ef9a40f 8dad7961c1e201caafc983731ec3c78492fa7f431a3bf8df1b501c559709b687e4c58f93de6daf2d 00dd46854465be0360cfd8c4070621432a0349415f456cefd305192f5bb2ab6edebf0d9cd2fdbcb2 b7de09b31eb5136b55c73bf98d8964fa6ede8d9e16be08362a3e7bbd6ceb11dbfec0f8fad1cc9863 cd957e2b7516faca6e4e356d5a18a4e8ff2b92b6d95e81224ba7de3e3c4d35c23a00ae1e7e3e7ac1 8f5e0f5426f1367470ebe447e193f766b3fbd32dd8f4c7d697d38a65c295defbc43a8b17b997d1e7 79c550e6d09f5d1f7c7da63c32c4f16dcc1f4ca4df0a4659bb7fe38e062f90e91d8d36cb7bf5cd2c 532fc7eb4a4dd5d3061fa6e8ff2b40f1d44ba486622275aba679313417c0b4540695f6b61fbd0fe2 32600cf1e8cd1ce2e2dc87a86123c746f4c6693df50b3c7d7dd37c7c08abffc82ec4b95168617b7d 8da284a6dd304e833fc4fd7ee872b6fac28cafda701a8d5bfbd91b2ade855b2867cc5d2afd553055 fad8779862f04f481750e935a9d0e10a947a4f0c94efbe0ce01c4803901fe9c17c9741485db733ef cb0eb7cefdd926ada75fbabd5c43724dae5bf81a4a8680f4cda6d1d4f65d30bc9b2b6fa5be3b12aa 368f127d23efb7d4abaef89987a1f4fba12f87f10e968077ee49bc9c99495366be92a6263693b20c 3a4c31f857243d7ebe0625d43afe19d7398703506985507c9ca0e972301818d591bbda790bbb7a0f b197b761797322641f4609de049a8197f2f76370abaaade9b473a38e9d89c26ccb6b393a673179f4 ce3292009515293befbc45397fcd0ab78fdf14d6627f24acbfde4ad8c099d49f266c26d5518ae1bf 22913a49aad4f862005a75d4e4c537d23319f350843f266def1bd83d0766d8c99b3c9cb626cf16ce c6bacea81afafa5aaa15d069ee871b3d6d96944bb29a95af5f7d20092f6a2ee5caf85e5c768fa4a0 62382f9435e1c1ef2b51c499ab59953b96d401771c4d179cf9921284da887be65fff8e9fd9f3b55a 27dd093a256d747a4f5efc243d98af0a85dd17a8b90a3834acd7bddf7b46e3dad2502b575c7b782d 49b505f179eb69bd480635272f7d5d069172b7595b2c5248523b4f68cdefb9e0c899de37757773f5 3aa45edfd6d1bfb6601766a963abc7bad9687e6d518bc5b525ecc6d7967efa77247d09dd8072bb85 03283868a002ad428014db50302cf390b3abd4d220f277877e371ef28a1aebfbfaec703f8501773b 2b5b43060fe049528bc988ab5bb72c6c77719d47074a8f7b12ca9cab4b97fdb575a52996724589ed 9e73d685796f0b4c0c29adcbe04e4d2f97fa677109944eba1648afe37f4522759b5429b124018c9c f59fddd1f1b3d828f993fdba601f0eefc2f33a5ac2c666daea6bf536b4533b1ff1a28cb4aa2a496f c912d7bd51ba8159d895a1125f3d8a08d7d8629d2b715e8d591a1f6e2e81d8c393be39e7196e7431 996496ff9ec5eab679fe4aad313369eb4b86e3a209c369d9f1bf0294f2d8e667f6bc1f13cb7fc627 5271228cacb197f3f2c9dace6a5ed7f14396adbc8e2c82cebd4527abe138b7a2e4190c64516d9d9e c28ead857cadf2cc718d98aeb0dea1dd607b57787019d29944c42b8f9d453d93661939cf67138356 1431a68b1fb84eafd6d6e89cef53cbf35c522729c6ff0a50aa12c9db8f191ac07b29ddc54d07a12b 5e805bb258f7c5eccd97016df769f8e8ddda5c1ab7e044cfe52f36c4a5e5d993045ddc997c4d8bbc ab53c73fac9f99942fe1bb546780eef598cce5363bcfeddb9e5e99b733a515bd1bb5d5e601f9c8be 90644d440fa8edb7bba44b2a3e4d314eb325a4d77f0228cd2f1b00d5e53380f597019082180441d8 f3ec8757d6cd1909ddb4e70189541a9d54154e584de59c5dc544e8ca73fcf1d6d4b956c771599fdb a759462ed71a5c60f88f8d9ca540ead037ed34a13479bda1e0f8449187b624132f26ef12a73a5f21 4ec3619fac25e62979405b13f2e12eff1da0448b89cee3fbc7f0803f5e227571f77d0e48faf35ac4 4ec6e68d71f776f3ee29a01142f25c8187a2960951a10a912c4734cb2a1b2c05eb72f5c61123a8a5 fc79d9bdc1f4a6746a51fbec62441ee2d19a38797d1c776abb7422c0c9b7679f7c785d4e00ba3869 2b33a2c9162744f386fc3b40c97d6e01244797a42f65129de7cbfb751ef4768f65d5db694f3143de 2ec1f0254b1d368d4c914a96dd150eb5db86b3e7ebf3b5b78be40bc7e14f665e6b05e7a260676808 63cb14325dd489775ce9e18e5758e0dd5ae5781a40cd2b1619fc0bbb1ea625ecbab03b586412b313 43ba93936f7fff40ba17f7bf377f01945bee26e9f61516247f546f5ebbd266364fce7564b65ea9b4 36dd2b997b68889ad5c90807896c720e74585cfba31a7e01a623308bccde389770c4a51e3903fc24 0726770582388eaa38bdad754f97de678a71e3c2e1f8a53bec71169ecd630e6fe60f4aebd639ceac d5f4f8658c0936f6dd7f07285f8364fdb9af314903cda726eeab87dfa64659e84cef2deb33532691 3c17f5c242166ae22be2a87105b90ef2cdf1e523948fccc292595a2f8c55ea20e6de64b3210644a7 42e64f21d78430702eb7b0cce9333ee6e9c2eeb07a7518546bfe7807d1ad3bcaa3f09c6fa21abc9d 1e56f7fb24c5f85f01a022b24e1ae894718b3371fd58342bc3fbdb52870aa85163a9dceb8e797cff 66af417964b35c4e4d17509799e6f498e2f1bda1f70f8aa2eae3ae4838c1f78133e4dd3b8d8b2b80 cd32fde27111d5ea87f5b73244776165bd37470b7a5f13347d5fa3c9fcee5d05ad7ded404ff766c6 98a418fd85d4a5fabf25491bddef40e5b4a39f81311de895a3dabf5dfa9b817443eb431eefdc076c bc83b0cb7ca01a4ca952f99ee1c1be46bd606a4612eaf248f4e675f61483e086898cf83a2adc25cd 3272b89f0f79d4c017c8fe492f7afbfaebb0dab51c9edcba7443dfd2bd30bfa50babf6d6edbbd35d 8bc98d538cfe15009e0a3bcb94f881719bea5d953a8fd30554f2c2a1be5023ef7d366ef7bae7fb17 9ed0d597cb534d68ef929d795026ceef411f1fd1adf5293b8248acd0f90a072d7eeae8c313ed7dd2 503efb668b2fef3a3dbeb53db3f67c137e1bc48615effa66d4dfa6494e37a39adbdc8c8addd1861d cc7e30fc57804ab34398b11275eeeff6b8ad4c5a484fac34acfe95e1265da6f8d1daa4ad1d7a78f4 5c31a76fb3689d72b39fc760cbe7b379bc8f8ab38331830ee873953befaded57deb987e0b1ebbebf fe76e082ec86ebff3fbedeb35b552558d8fd2d4a10cc8a59cca298900c1214c4040a08adffffc25c e7ec7dcf1e73bd5f9e5e3858839a4575535d5d5d9d2bcebea7516f36c2157a065d2adad41490cfd4 ecaccb53b3726d4f4df4d1f90d6948f59f0b77dd5eb5ce8bcabc7eecc87843b3a74e5369c4644330 7bc7dade1b20e5ed81c15a9be980d6d7f98ee3ad4afb7c96ae1adde29270a78d054f6c06d45bd768 aa8758ec5c9d3bea3cf30ad365a2d9b8f5f5667054ff4c6773ba30b11db33359f0cdd5a4d0be1a93 c2e494195fac4f697c5920b5ff372e7076593d3511b4ae4f4da7a13c3b242142333dcdeb64abd4a7 b81dd01d6c75595d36748d28198ba04db95434573ef30f67e7e6d9e3bd3c439e5e2319cfe0fe1487 abf3c9f9486e26c51a2d8caf4f451f57868e3372612818d5672d78f8aa99c490fd36e7c3a676127f 42aafad61b84ed081984952cf65758b5515c3146cd1ba13eb29b8678b8db5576433df3db8cf982e8 724c6616022731f36fcdd666662b739b52181c4ce822fcf939d3ab9a85470cf1c1872f215b1e729d 7263102ae5ce40247b4332d607699181edbaff3d497c5f9b7b461fbae4ef3d73457f7ad3b257ec4d 2bd37e0ff5502e4153fe0d3fe9a3496b7c9756edb01a6c08a95fb22bec8d79e05bf2e666e9b28283 b9da1ee566a8715e4c8a179919b93b411a72ad9d3e107b6b8b24a1bed3d7a6c3471fb2277e6f5a18 bebbd67af8f9df2203a3744c6b2733d662bb1251b5962b2bdd56fdeb4c9adc285e375b48476a8427 d96e8456f99da0f7f92bb4bcbaa9cbedd7bbca2de300df2ae12d4397ebb770f68572cf9f3ab7a53c 3eaa3f89faa00372bdbe367e8f7b53fc4e75a9d265dd293ef96d7bd3e4d9962bf262f3a52ee4662b 3b3b34ba685b2714aa71ac7f2f8d737d546edc6ae6aef7aa5a5c3baae2ef0954a17beb7ca5f839a5 41c8f2661890e5cd283bfa2be480cc54f9b989e577dde12b43afeaa63f3bacef8fd1068bce64ac94 b37de8b88fbbf8ad9f6d6f1acd7cab1ee3e546a88535223eded2f33debdfb3d9ae8f8a5aaf36ad6e fa55aa419215ba531a946f4a7e5c7235302d11f09b2a7233b02e884b6f978f6f6f01ff7abe8a8f1a 6f133345f8824dbb99eb5f913a5070076902c8c2fac9bff6330087b92d40cedf2340eb071fa04c07 05a8726d00d4ee4d000a7c16a09fae017298e5821c8e830414067275a7067204da4bd09f24105609 2201e4060beb27716c607b3165f871e4c411f4bee783dcbbba3290d04376d99039cd33ff6f007852 68fd8f9c6b69069021b907285eb1002a076f80baf39f73a3d0386a825c753f4f0c00e6406ec65809 5e1ec8cd9b5f90db2985042101724c3df1ce9875721f73de81dc1e93637bb03ac77943f2223a3ec6 ef6dc680426f75c543e2b443037f3e87031eefa72374da66ff8a44c07222aa5b26017cb5e70039d0 89d2769373221d12815ce79007b969b90d721b9d02b9634b04b9bb7506b907ee27a0a104ce4ff1ac 9c8bb6120c47b1cdec16f1823cefe342e6a34417a3778ecae8eaf5ae655620dcafe444a69d900fda f81cf325ba8ea480fd5e0987fe0a000b8df68f09a00df24fb01fc54c01a0917c05b9f5e00372c6b3 0872b7a43b61d07b09b012ad00ac155f01d6ee470914345ec856392ef4e376b4d2a161541e57a8f7 039930090ce5bd3d4576f8ccb7d382194150ee67fca8decfbd3ead4ee9a5be2bf9d74042b157b61b a34fdd73a0bf02c046bb9de8753104089b5b0054faca20b70a1e2017b0198035cb15802df59fd45e 8cabae00e6385a7c5ee1898ffc68c6d1ba3e42df0f8e2ebf6b77ba193eb76915f5da8d0a1b2f681f 045c530984d6daf6a3ed397c7dd85bf695155dfc390661c53b0daca237cf2ab87b9e50a8bbac4c53 57fda7fd0dc93b4ff4055ff789a81f620172e5ce0160d9b207b0fd1306d8735507389e1901bcbed9 c5c591a94595ad7b7bd797ee3b646f0009422c530a3ac34cead9f831541dfab2395abc3e60cabc0e aaa8bc868ddbf96988c5b7371fe421f72c54f38fab45941f15a25abaef3880dfeba377eec61e8ff0 5f01e060dc0148e338fae9375b264d9a640d802dc62f8063e51cc067c706c0e5d624ba71a3cd9b99 d16ad82ad297a09ba3029fcc8eb32fad332bbe2066d6784e2e33f2898eb7736f7e92761edeb71577 a9bd6db7d826dff7c7b797f69be4c16dfcda611b858b02350ace7783e59ca1fb841c487964fe8ac4 36575d80a8fe18a06f6b0bb0fcf90830200500df0e3080bf412bba7d4be3d04f3cf4d4d2a46249f6 33d4fbf482c7fef399abdb1f2f5f327097eec9f5c78d957b8f4a684cef754f5eddb8c659b88a95b3 7e89f7d987a35590f86ce651e48ceac3bc6dadd6051b8f8639cb39f5618b9ed7be3f4827ecff5cfc 8b44a1fb34d89f4bc6c015b407988ad8009fdedf200feff198be83e69b08ef64d01b5ce6af3125b1 4f0cd99bee6ad4be3fb6b5727c278c177c95325ca2a2884e37945d3275bae78c687a72368f9bd539 8729826d1d5e473b0ff743abf81941a7cd52c91fddd02a1c19d3cc1d898d069b7e57c9fc150019f0 899cb36a22a7dffcc9ee4e0fb34b2c2713fd1ccc17553f7a2d90bc6de7358ec7236f59ee6d1fbbe2 47b90583c3f9a2fab8ebe8c7303e1f591bb6ed8d54b0f3ea9eb0e860df3bdd3beafc5465f4edd1cb bfd523b1eede4dce62d249a1f1263ccc90b2897bd60d5ab80e0c02d1d5493ef35700642125a21ebb 138011133ed1e6d801f956358e2f9c8d877c382ebd8cf1a7e62db9a8f7a82fcf69bec05536ea7bc7 e09cc3798e3117bb20b65e56790183d3968fd0e37ef7289bbe736d9a6df83130ded2676974bf3541 575be3a39ed9daa1367a218836c6a5a2067f8f790dc12fe8e1f8b2a17f9106bafeef2f00d9eb6974 6a9a88cab0e946ee6d7a26632b8ab6c715e27fac28f7c4fb72f1c160cbc65519e04367f235d7b683 8d24abd201c7533d7f799881bc0acdb633c918323ccca77101f559ace8d962b1ade9667faa21b9f5 ee301b1c0e070c6abcd43c9864d542f593570bb35e412d6cc739b5202de0bf0220f631f9528f9829 c092f930c843d2e52798f21a45efc639f7794d56b38fbb2915e16b1c5b4567bae3ba363d23e62757 f0d92397e135b3b36b9f8dfe08f1f46137883563e1c01a6238a503a6680df54cdc866a6115ae9495 854b4a9992eff2560f52e756de06022eefb24141de35114cde0d0bc85f01d09c9f867c9419c02b0f 19e49bfa0514ea741444d763f8c4930fdf9d5385f8028f5b39fbd22a115695cda42b1ec716b9dd18 f1a321ebda303a6966d5b81dacec3a38e09569462de65b98b21e1355f921b7ba72ed359a4b4f5ae1 a4c6fd6b89023e8f458180726260094531700f9818c427f83f4827ecff5c00b40bfe37e43389949f 1339e3d58b0cfc6f65e6bab7037bbd92a7d03b2f300fb2b65daf72e43bd2c000ab1ead6bcf88d7a6 d38371a01ac9ec8946eb4f65fd8a23d92dbd7f4e28abcfde25a939bc37c5e00d4fc44e8dd80932cb 1842bf947482cf5b4184feaa5314647f890b4a6687fc15009d823ec0f0700170319bc879f62ed1ae 08d2b2392fb31ed90f62fdd59d23bdb9dae522f33935067cd1ec9183ae91791416da3434f687fc62 a2aaa54ed152aa78789709d80a25aea66644b1b8c58578ba2784be2a0df8437059f1439138708667 fa9cf119a55f4f1eda41451e928b383f42fae85f917ce8e14454125a02fc8629a08081cb9bdd1c2f 4faab53edcba97167b2e60b0757a565b91194d7a9831d0cb2dedf87c4c7fe2d05361a796ae7549f6 fa2f53f24bca457c63f453ec36474020ab0d94d716ed2a0f19831e3745f74bd622ef324b65e9278b 1b20cb5a4bb3c05a8710e7d02f84fc8634a49ab600bde7135195fa0ae48b0d15147ae01c761685c3 9d93fde9655cd357d6a370348e42def793b79c4574e418108773d318a9e5457f257bd197977cc7d1 c5b7c49c05b0ecba42665a8a7878904d4d809b0ad9126bb9852e9bcfb7e769f94b715f42ab0fe666 e9997da93ac2f7b469e2fb0b7443ff8a6478aa2672ba9d44ce612f9173eb1f3da7ca2caea4d44e1d a8b31dd6a627b6b23898fd32efea86f380341c3b55d5f5744d2ab5a0494bc13ce444e92d1e04f53c b2795dccdfb923f30e587b75cdb079f95cd897b46b93d9bca30953dd0db81df3f2ae3b829f7e76de a09a16cf62aaf60667b6550dfd2b9211aa9b46d13a4b903ff4a477c37d72b7501b749d59f6dcb6cb ca8934e30c22eac6b77dd3f0ede2a3de9abb92b2f3e67d2944a1f94f1c9a3319e130db28fc382c99 dccc055776a11f9f89cfcc03e6fe92f19d7717885d033b8db63c8dedb7ed927edebc4f55b06d97fd dcd60f46f95d63c0e4fe8a1f575deb76926e3f49447df604af98b1bb17cd3e37ec8bbb6c9c9a48bd 650c8ff25ea38a9fb37a3bd4228599f6f3722b5b6f8b4afd3616867771c34f0e2391c3ab198d2de6 efe77d39923ca6f65ac5bbc67d925adab67d1e5636729125d79fe963b33e60b3e37a7000d1fa33bf e4363db78a6fe44a17fd2b92e1896c03fccad2d1231bedee4d0e6b9cedcea4767a8ed19ad967cd34 98a251c3fa5adda83b5321d60f5f7a0f204454a2b02e8c386bc8e7309a661db3caee6f0c38304c59 b7761cbebd6f45781c6ee44f1f5a7f9bc3d21aaacebbabc9fe44afd0e6c0a04f373f5c4d683db732 fc5c6a026b88a8a27f05c0fa9b0ec8370cfa997cc13b9751615eb1ee17a862464fb3a2a376aba46e 5c6d26f31d54917af9f9431cb4ad8c007fad0a674b5c8f5d35c99f24f2aa97db3284ee49bb96ac9b 1bd05a5c36995aefb51e2d7bdfd574332cd0d462dea6f385c772e9dc16fa929ec1e1d2915494ce23 304e53e312fa5700ec61fd2c48e6d5e7eaceb29ffa99b28ae56330b3ca06b4a1caea1df10a72bb9d ef8b038de304641b9e794c6bbeb9c29dc6f78f25d9609e5362b4130a30bd95119fdf0c34495d8fa5 95b59ab16397b66fe37879f1e6691adcb2dc38b416db75442d6a555ea3bc733558543f36b2d8f673 d8e22e57d1bf02e06b7cf88263bb7519069d92552bdd4ac6b7c8940f4e902fc93cbdcf0b46b391ce 3db9b30756ecfa4619fb1d709ecc6b598276e1a65ad92a854c77a395c16c6de2f7edca2ad912ed60 92b9bc41abcbc24d66658bfae28052acf120e661b547cd050f6873e120067381cb21f3c069e552a4 63da4ffb1b92172f50f727fbacda173c2c9ad1812b69d69e2829fbfea5280e9e0d9cbde55984e198 f270275e8efc5689f3e76d86dabc3730afa3eb6974afafa8ef85fc290e0cdbd4f2de3b320b6fa82b 943f552daa65dbde5c2a44d919d834aa33d253a7c91c66a3cf32c3f839cbb44699194973480ae8af 78e62ebd9a6376d07415f7d4982945ddac9225758b8092f8998df3dc5231d05df842a10d6cc78595 1d9ea855419a1de875cd77970f8df82e77345f5c3cefc7d6a2d9f3265467785fcd65d3e366df1cd0 67c365ee3a356edd788a76d5b464ebe4f4290d26f3732c4fe69ce04de6a3e03399b75178322f7db3 7fc5bd099d2ad61656f2c6c11c150ff4b55496a29654e413cf2ac7343fdfccfad81882e58e1ca599 298bd7e2405361e22250ddeae23a27077c30d38c737636da208519dc1d34a726d80ea7b9014f4dec 82c74ef26b7018d380b88e4b2a178feecb7c79b42d9ad351f52d18a3aa9c7d8eaaf306f817697d81 fffbcb0fcef8d7c24db9392d68d4a25596fdefa5c49f8c0fc670752891b2d08c96355a09e660fced ce33d66e3343320f758a55b2e74981c4dcf14aafbd478ff2041a6d23397d37c367ff501db27832b3 6e5c0ae341c0cfd68376b417c8e8f4b648999986649f4826aedf2c33ec1fcc8ed43f88e74bff3043 82bfe2c89fa9fccf50a4ece75eda6f844911c1f6f55325bb3683c4c5f2eea4371b4e3c778a39e160 7c3d1557a39dd61186ec7d6e0c84cff24c2ac5f5a3ffddb0415feb9e92c15375e0deb4f0ca77ad75 58e95255bcd571f6cd61876eac97ed9b70e27f4ab6760ba77605f041bbf2a9e55beee13268b9da94 f92b7433d72aaad5c7bb240e3ebdfc7e17d5b31bb8504c4494aa8fa9412d2ee37221170db926d122 63b59ac605fadaa4b4e8995469dbb55679aee3ec60a9537cc187f6a6059b2da6fbb6feb7c8c0fbd1 1027c027621b0604592823f551a551aa99cca25d9b1297596ddae8723534d08e35342c3eff455a5f e0fffe722809f9f24fe761d7fa04da8c069544ca6dfe3e3588de79b80bd726d9e7d4a867ce0ed92e 7e930beddb9eadb598cebcd9e4c849af218e3b03429957c6f5af539cd5a1db97fa798ce72eab54c3 dd541cc1dd976f8ac597ab999b5a2290dbb1d8c2dd6b415c41413e7e10d9bcb2a30b79b26e36fe0a 292aed0b5c91d8c15b32377c2fab522e4dbb9e1a99b235ac4f9b5a6fd4a7137db9eca3c5b421a711 1e6e7782cc99411dba72710d75e96f15f7c750856e8f73e5ca17cb950828c08adcf45928880ba398 57d64205d798650d33f9493367c9c31e4a8fea693a0f52c5f0194c14f115c46dea6c36f43af25f01 b2df7e1164e97e0d40fd470740566304e00dbc0770681e0152eabc00d272929961bb5903c8f83404 c8a490ee2602c87a2f26f093bbc4d63d011326b8670172ac161250f504660f2027740e9087c0c7a7 dd558b3127384785fbf3fe5ef7ae8ff0c1eeae7fb02b0e9dff20cd81fae70240f8a404b2ceb20ea0 5dd001303a1e27527679800c631b20f432000817a300d19704402ed11820d7c10620c145014898b7 4032d47a099c18a0a57ce29897a6a5044ae327d0558a07002df797f15c16f8e8fc3d1b51b1f570de 9559e086bbf3d90d5ed0fa16b0e6e41ab062ff1234bb75e73700a83e4fe4fcee12395da40b60414f fefe2123fd28cda93800f99e2280e6eb184089530ba08df214a0038901e832a725983a00659d00a0 5c2e9380c462abb5a9c4146cb663fc188e22675e5c4634be13df9b926c864c4d7602ae29bb7e28ee 7e0ea611bb73d7ef78cddb2bde17af7f45f2ced76500cd0d02c09572172005b004c8f3a602b4bfbc 0174f7010055d605805a9f0e40bd1505500078807ec89f2390acb3788ba9e52d8c9c9b9f89e86d0e 8b4abd6ee57dfbd0edf7e6a08dded5e16b197a705f0a7c6c72f4c55bfff202d5f9f345ee87afa716 14bde76815df9f70d9bdfd86b4b65dda2652ee12514f8f06808d591f206772930858d4011a5d3d90 abf43320d73997406edaee81dce642c7d427274697216146a5dbf4f6de563741e8edb96fb86fd8b9 9008fc72e08bf976d002d541aab4b74a2d7d69f0925fea244efcde7c78f38eabefcbcb3de2c0b577 eecbcdbf14f771e198fb63d5a26ebf21b14da19268334dec2eecfb00ed890c489c9b23c8ad919f4d b2b9ab0c835c885513acfad1c55317efad71e4c2bd75d18220ef3b417b059ebee4e680df631ac84b f57bc5d780df365fd9b63e788e7797c51321aaa287b5c9935be84f1f8ff268fabad7665dffd660e1 d404ae7c70bf5da28ee45ca4787bfe0d89368d6aa2cd46eb4f0e3a7a775990d34f36c00a8300608b 28073076464465bbd10dd9496f1608d498f1fbe5b4c44f7d90aedf3c0d7afc7822f7459478a14bc8 c3ce42c15d4272dd2d980ff2b1c6b28bfb63d9606f6c756c5ea275f9ec7c94f6c319e6c7cfb3b12a dfcf933272b14fdb879dc2fa0da9d2a0c739197e0ae3a4b7f4aa53905bd54580119fb4f835fffe29 351255ba48256cae9e4d5fc1a3f10bda3ed64f947d8aae53350cb7b838dd1e9589ecdfeb94f0b9b1 ba885e851357bac80ed7b8f447566a02cee118afce86d354ecd3b163d9786df7b09607e375ba41eb fb697d5b5e4f95cdf09cc2fe0d002e3f1339f7fb76a24d9a02397f222522169c9f53f9a25b234a67 6b612b73c8bfbeaf49e599ab4dbaae134ea847b551646edceea55ce23b673adf337d714610f53c9b 5d2a3ea3ea16b5e7c1b668e382ddb69631989e6e8d2a73aa088c79ac2bdacde4f2e1d36cfaa16b84 fa2bed37863876ce29ecdf00e0d52711d5935a20d7d2960023593991727c05f83113872f77f5f149 b90e7b7637cc3fee6b97b8f16d6e78515bc4c281f76fe69c5bdf95b4ce2d9568cba279e99a68987f 9daa75f57b64e843ee48949eb56466fde9992d7cbc32bab0a4e8cacc7774b2def5b4ef77ea699a33 ba69238e74529c7f03808372fd6784ae1e1351359706987556012e6eafd1cdd3fda07bc18267eeb6 7b3fb6c208ba4a8f4cded1c37beb8c35a4b175b106f4e99e669a79724133fd6560992df3793744f0 088cee01caea4a8816f44cb799169ad1346f37d5e0c6473c4c370dfb90ebaa0fd5aebe9faadd4b3c 490af88ebac8815f911868ad06d08dd706581e5dffd9708e87fce54d08b5db6bdcdbdddc32f57dfc c4a14b4ce84c8a5bc45e360655ab6c95fbc7e7e8b330f9e7616b48b79da80330d7f54163e268fa7a e869b0358c0fb3cc1455ed315753a9e83650f34a67a79478d690ef93ec4dbe0bdb977c377857deae f7979f90ea7aeffc06802c067580be73dde4c517d77f4ebacc375527789f1e9677ce86c62d90c993 331900d75eee33dfd3238a8ac766cb6819d17939d63fe670a567f778ba875d4356b172989dc2936a bf6e37b550727c65b5f5b34a194fe68f5b9aecca3558a3a5fdbc6e480d5cbe490db2168841beec8a 01d1b8a6b8fc0680a8b37a62a0adfecf5284d2df02dcc4d4b824ce4efe80ee286ed91df19783a92a b683ee2f2777ac46c7a6236086824d087d2816496d4285f3c37cececd4659d11942bbdd195f2914e 34f1a13ce9d9a481d4d8ec3131a87aa90988ed65692e483745117a5bdc1164f8fa12e452e409723b 7b4f71fd0d495f5a25a2d2641a451b6e401eaaa9511514b417b2b9edef2cad2ece56a3215815bf61 ffc4a1f1b66fc4f73eac8f8a78454315b77bc057ca442dce376ba5d26aede5dd96502576091fc5c0 84ef623bf17704b95180855e3299e60f157dcc0f3c52e28c8679e6b3e7e58bcf3e658f1ff6aef714 e928f0d3fe0700cded1351b5510fe095f53679f13539f4fddddebd8e8f83cbb7f1206dba1dec8faf 77601aa018b9baf6cc7eb55ce55638e4335a4bb9c9d45076696221bd6670ba43526aee7d51ecac6d 5d90ada3c37f33b6cf0fdb8f2c672848859b90ab117be27d8e9d77289b3d99c5277bba0d3d0e6dee ee1c3a147e0540bb7c0de4be9b7e62a0cb1faf33bf2cf02fa49a1f5e238fad3993b8df38b9dff5da 143fb2a6ebd5c34d9b06a7f89067145cb997694266b4525fe236efa9282eecb5100b12cb7f9995ca 0f2f0b8b339185cba173fecbceb55371ef20486a69fbe54962985b063eed8b9495164a2c3ef74eb7 7fdf3bd3c9af48ba3d5f01585d24012eace9b7470de9fb7378ae5ca00e5ab41d2dac1e79b8b23006 505fd68efbf5e5270edd6143e5ae53a8ecedba558987335d5104de34315289e6356bb14f7e1a2b1c 1a358e2c556f24cfa27b89e7765ce2cc063b77988ad35bef18d83676ee6a73672a7ef8643603fc9e e2f68334a4facfc50f007ad3135177d600e08140bff4f5bb739517f5fcd9eadff2567546e7cd5ece 9dea480e120e0ba162a9656af8929f480b92782d5712a58ddf1606597ec88fe219c51d89416a02ac 5dae49fb0b9e35f6a511923c4bc2de3b862da23ba2c9b6b69cf8596e5b355edf723279dffa88f3dc 11e3d77d4724fedf6f0039e85806d8e93600f982bc7c6cd9431ae5b840e5176697600a3df24518d1 f52b35382c2e22a396e597293f57a82b09f55c46944b99a2305075821f0b7b929b518339bb189737 fb55efc333dbe643db799b87b3f517e7d7b675fdc21b29d7ac6fbaf6390d10afc172a5af010d5f37 525d79a5b86fde3bebf61b40ae6997008e8483903b52bd9b90ebe7cff317879e98671531406c431a a6c01db51c916b99a59983d4e999699e8dd81fde22210bf41c8f04fb2a87b9b3eefe3ac6c6fbb29c a5991afbd8ef1a1b43d9b617c269937c639f6bd53a7dd78345a5b6d2afc274359e3694956ebeafeb 6c897eae0734f74871fb17e95ff3cf05c81ddee59f809f7b995d8b9761dbcd59b7238998d2e40ee9 c86a96512ba56d4d6eb23e25c6334212b42675e6cd2e177056630bb14bb7574cecb7dc6476a7ec70 c71e1ed456108dd40436f29e15d79f4038aeb3c9ec7635c91b1ffab4fa9668cc5d8e96e74541a231 fbe2d073acebd1a7d9f44e9fb6f4ed37247da9554f0c7432bd7be132d1a6b3418efe104ea3834696 54a14311cf7fe5a61d6122595d8c04387b6139cbcb248ed08074d9d264f465bc4c0ddfbd22a4be0d 9f416f13df4e93f5d716566be8bae3575374a7d354667d5d3ae343b42c9ebe85c58d62878b4db691 26f52dd6817d5edc9a2d6f596447f714b7df90d8a8d87e69d6ac7a398c6fa8b5d97611b3d7f1600d 8bc659a56ede3f62064d8c2ec7071df6c28c37fbed985719efe4a5a3c0cef733e1f67d8fe00d783e 4b9bccedd4588fabe7e16a569017f4025def96174a5017f7f1ce5e544fa780dae7719c222e0772ee 4fc6c2dceff8678a10472e456cba578ab94dff8b74e37fda02bc1d938f3bcbfe8f42f9660ed12719 15562b5a392b82c21670f9bbf0dad7e87a6517cc0ed36dc465b96d1f1e1f3787adeaae0d56076b24 3c632bcc3b54e965c3e82ed71d79b27830725a62827af2824035a2d3712ed483e72cd2fbe84c1e07 bd592f56f959cfabdab39e4ddd6712a0af292ebf21e8046aedf2e15dd4aaf65a88a1c61e72488600 446e4da3b4b43e7f646a31f31c0e5f9b6fb6f2599ba8db5e59833ebdc2af864cd395c8a68ba0f75a 6e06b3efc2bdb2f9055111ea14c71cfa7371c5ce66f1e3b09b91f5c761aaf1d86d3a1acbd01446db 9d89a9c2dcc4dc33e9c463622eacebc41c68cec46c88e7dfe096ce42de765a23d894d63eaccd791d 5588511b117441c8ec1fbd43b8ed53a147d323e2bef07a7e22894391143f0ab7f3f7a992fad07369 c73af35effe9cfd40bf29d0d564d7caa4773623a96057272fc9e96138cfe08e373797c1e17e43b18 5d29be39ba3646bbd1356b69a34b089d47abb56b8d5613e7f41bd2a17388987f3a913e212ea85ac5 17a8a82e1e107b1d23602bdbd07385bfba378a2fe897a941f0de142da8f5c9292466137ccab163a7 1a1be3e5a7731b170feb20b17f273bda14bff951e5dd2186ee51f8396c8bc0ceabc1eb5596071c27 dd06ad710f21df68754c86ee4d2443bb7222c501744c71fa0dd66646c0c6c0f5d14331d231499a95 516ee1d73ebbceb4e9ff88985a1a9fdddbd3b18e9cc6f4ba868e3696d41a7a03783ef0d7d5dda015 4f65522269a30f2ecaa5aff2af577fd041413f8b92686f4c51a51e42389dee295b99776706cb75b1 4dd9e962620475cebdcba073ee6fd20498ce4253b4ce82e30ebfc1ec4ddec8c119db98ccb766183f 7377d95d082dc315559fde292e585993e3ddd0873bb1a30d781fc3c95e346ef587f87edc9bb454ba 7b3a5ed25ddf9df3f32e75962cd0db373867b72bb3fcadb57b12afe6abd98d9b4d738b36c4aa5d6d 749e7992880fdc9a50e8a24e9025c327c8f2a450ffbae55982f1ea376873dd4e172415425b610232 39400ccb0aef9535a7fe47cad96c77183cddafd21f0cd6e71e7a96918eb3312aed4de3d06a311db9 dfe44861dc08759922e2d38e26487cb7ad6bf48ead99dbb950439feb43956ab2a73ff505d86bf9a6 9e827235fb854bccb4534d701d9508accd24f04ec5d7195c7e835a9dabb8986d04c8be8e9da2f524 e21e14779d9d26b3de521d34f3a6d8359b20555a7b7d41cfad7a84df1be2080a096596cbd447051f ae4d2b77ac6a31f762c5e1f572f926f3b592ab2d88e2eb386815c273a35be816dbc33c596dccf051 a3bdc6a69d019fa3c8858e3aba7a45e931782337ab91ae7824ed38ff1bc40f56ceb1e5f7076c20de f116c4606ffd4849061751e8585d75db2414644528d39c56872e7ba96aed064a856eb5cd72e553b0 4b0494bd145b68909698c8c717f98e7f1f6b17875e7d0f43df95570effa021524595102692c765c3 c70aca28dc18cb902da99600f433649ba412986c829bf01b40f255844046d47ed6a4b32db408b2dc a40ea03c320610a5320052092381f200d001ff26d81412bc5a00d29ac92d8ebc4e10f000bad43400 f969f4c8bf78000ae028c1004ea0960014e67b09e8d4818aa75d6517510345783b86a2bc4b282585 378b14c30d4508e1862cf1bf01646e0a0c32e10d4b44aca78154a10120653c0750fce6009ca38e00 ae864f00d746d99f2cd59a530670bdd24db09b01b81fec004cd6a4042b13c0d4e50ae045ce4f407e 1248b904ef6a7c2cb6fa714e6416517ecf31ef92c02582c59c1a78e4480ef6999614107a299984f2 b97443d94ffb1f24529a8936875f3c91725206d0ee99ccd02aea12c0623779bee99c017cac070994 44ce47b19e603748f04c6e716b2c80e3fd21c1dd0630c8a57181f86877c378b6586562aca062917d f5aad142acf6de2b75ba08b706cd047b6b23f9419ed1fcf68a5413efa624bfa4132cfd15200b5989 9c1a8e0368ce56fea4f6967b00be65360029722a404678327f1ded2280ac203cc1b299e0310688d0 dec4a7ce4a88314bd2a2655eb7a2c2e57e7b5f855cf05e7788cfbb1c2f72e143512be18e02dd805d 130b5f60ba6991d397cc93d2f3134df5e7b05f3d3cb3af8ce219ac2f7b93e645fa0d205b741000e1 9d3c801e4605c0577ef4a77e07f28c0d807ce70f80e2cf0f40f3fd0240896bba6325c6dbcf7154a4 b2abf7fa526043b7dd56c25db43806aff9fe1270f8f115341d1ff8e10a43fc4ed42bbf6299efbefa dff7f2091d7dc643cf6fc9a54ab0f170a6b6fa28da7bf9be290cc47be5dae07f906e28fbe7e20720 cbc62880e47501c0e3a00a90853f0568cde401baeb9c007a3a3f016a553231452c8b51315a3642d7 d90d82d75ba682d6ecbaf3c57320bd40f163bcc86df6fccaf4da69e4f6393a0ca2273cdc41de113a 97bcdcf4d376a9f360f1286d06cccd6bb4c52b1797d58bf87d4a0e181d78478559ce21a509f31b12 db2c26368f1c8a00c9b61b00c508ea279ef68024902bb336c84d103fa6dc7ef6bd89d05ce0d368d5 977ac5ee6b502c4e9ee36e73ed1dd53ee7e582beea2e3a93d3e3a22c6f8f7266e7dfb763f97bf34e 0e7e6be071fdda2e37270e7835574ee6fd734ee119c126b23d5b36251b2b16796b317119ab805abb df9098671f03f0ea5c0088cab7017a6468901b376590bb3c9cd8361a3fa710c6417b28665f03952b 78a70a936ebe70cfa4367cac118eba6fbf9bedad711cf1d7f6b9ab5ee4e2e8e47ca6e4c519d8947f 360accf73cbede0bf6a99a6f5be7c67471ba7636cca9ac68d2719757a463edc2f0e67345b1265b26 77ff22ddf8ffcf05804ef344543f48acb0eaf540aee5ac41ce5f2a001b649df763fe70fde8d07f3e 8d3d1ab9672b83dc1f6faf780d8b7af3d25fccface703d9b9d0d6bbcb64f971e6bcfc733d53a9f16 e9d7d32ae2dbdb694d4be1a9023fe0e36e8e55cda7b39d18c151da18c20e96f4e89b9374f90078bd bf7cb3dab778677e0380cb42d2a1075831d1666900725a9cd6413da4756ee382ffb40241aa584f23 361db7b89bba37ae568d2e24fa81cee6ea51b2a9faa96939f46970bacde4f9a972103647069279f3 d51554b3a91e2c231c3c3da32344408fbbfd82de578dbe76083eb4064d0769bf39985549394c002f 1e5075c5aad660ccfc86e4c51fb0e4c5970b2057228700cb17b700b34eca7b779a1f5e5a43515dba 0d1fae21e02d67c48c5ddb46c6b1453bc33401e6549d55aac7a4af75cdd6e43b3244255ee8b11fee 74b2fc12b4af8b9ada288f5e34e8327c1fa6650e3da08f6f53a5f29385e2ac0c56a13b98aa945044 544a95022fdfdcecfe3f4843aa690be0cb150768ae97889af8f58942eb9b9f3cd4206463deb34e60 77f38bedbd337a3c553b7f7f38a7edf0e91f1b391732db59a7687463ada1933ed3d7f4f266a6c173 6a954c44a6ac6ac374fa6ed47c8fb614e7a9784aa9e97ee5dbbb5b95ab3d6726311cca488c735024 8233a414bc446867f6370024f3c000bad99640cee1463f81c9b8c8ec589fec8ed294f8c7bd541b5f 32fa72612fe8b578aa0d274733a8ee5c430ad61f7dc090396dbc22aa87d93cd7566d2d3350f321a0 9455f5bd95efc9402f6fa18c2179d3d24d22ec512cfa8363516c99c4487877c5ad20d52ae9863241 ea4d64419a2d8514dc6f00081925a29a521960b838f849ef7bbb3cb47c9afebe7be31b43e28cd5e2 f169ebc58c295451dde8672b573dabe0efc3a9f5867e8a03d72e05b5b0b409a53c957af256dd8f25 2fd8d252a3b2da8b3cb357c576fee808d2ea1df2c02ae778953af6f8ccb5bae1338123f1590895f9 6cb128a6e07ec3cf2a2e13202007d91580cdcd44d4c970e19315bd75276c14bb64be1bccba0ecdc1 b1993537469fb014cd3839f6e1e4b82f7539d3beca7ac863f2ae3aae48cf45af25358c6440e3e3da 5c88c83623f4f876aa345e7df44e7cb6be7d72fa2b4038a4dd6bb1b39d4db3c77823b227d455d853 25145370bf01205694f815c3770d60b23378bb238e7c54a7107c5177a3cc7976cc413fc721e6ba1d 43b6c8a50eb569f1307736477579e75ca5b29c46727dda4324566d974441c0082172913eff719e13 7e083f567c568a786efcc58eeca9557bb098b4ceeecfbd0fb12f70abc5bea0e7f9fd32afa83f2155 c29452f0bf0124fe49222a9749447ddcc8d75769946e7c5ecb3848f30baceb4201a670f1087d78c9 cc0e56bfc4aa74893494cab37b95ebb7562835ed42ea0e8a1dfd9317faf2a3c61f5ee70e0f958c11 37991d96ec7cc832fbb37938ec0be07a65ae87da87a9404275f7901ad4ee71f438a6524e5ce164c2 23a5e07f4332d863f0cf2aeeab5407783924dd12844389429bc03e77cdf769b72643a3ffd85734f4 ae8f547ae8ed946a2da3ca4433ef8861f2c91062fffde5bfa7538e1f7e0f656ef25e3758ab362759 9c5d52fbe56db963d6f62435819d5b109cddce08a2ed2bd72f6dd97376964062b7ec2baf6c5ffdae 9482ff0d009d16a0a42f11f5b70b13cdbb87beb28936c3c85a2fb8b7199299970ebd1a690e94ea7c 16a452dd486bc96f1c25f13df12d01909e2764da76ccc37519e172a56591a56613624f0f9b3de626 d6a74c053437bb7a5097b65c67626f4246083662032f6c3ac175b28ec5c17ed38580ba1147b934fb 216db9df9074fb369cf4a569e3a5e19ffc556acf3ff679ea44c757a5151824ac7b07ca7ca0ca162a 74247fdf5b88d264c50b2a2519bcbe52efdc71be0f597b34cffe1407ee75f07d89add798aa887776 897b37de729798debc912fbfe95295e35ab92cfd35b98af19566ed462bcd2e33eb4cf5a2acc9cd5b 4ac1ff86a4cfaf723fd1bed4e5a09b45e892c52be08f42bb998caf99cf85abdc17e3afe4bb2742ec 8da3a990250a0c8f8c07076e765a5dd88536f3f72ba9fd61b66c31b7f36e6879ebbb716bf3bedc06 9b1e725bacd579c8aef4519856515cc1a7ba4b1fe7264acff2cb019dd3830d3dcb6e25fab8b0a515 6c7bc26f00b9a55d88ae1ad9bc33969374f6308c8fafc9f46d644a9eaf5e36e5871420d797f059f5 cbe96063f4e521372f3d36ecb2f691f657ad61310fa9f0d83d0f58b40d8410dab6dd5761d3f36fc4 fa50b67a2b0357a62b84b676f4ece8aacb335ebc2f0b34072daee57e7f714582cdb230d94a097871 b930f4d417f869ff0390fbe6ab4fe3bd432ee0d4493abb67c5669724deda0cd37da5467d1ec247eb 5f7914733176792c76f6ebe772c1ec4299dbbda64f7d1b2e9e976dc778fa9bbe754db760ad938103 5b9988595da17dad43e32d6ebc5c4ada7a71fdb8f2a2d2422fd44e5a7fa97abfd9a1eaf5c78ada31 2b29c15ea01eaecaff861f03fde9eee753314eebd91c5f6b3ad2f536fc5657b7952f75cae7077702 376bbfa9563e0c5150eadb307647dbeea2bedd28d7b9bcc974646b3d520c7735d5e5376d452244e7 fb4a61596a31c462238924e53e950545345d61ce49c59fadf2e1878d67a2366ccd44ee43cfc4d58c 9f85175e9885fa96fd0d213bd896aeefd5f2f3a34d0310ade8607fee6ff9e98d0361f8621ffb6a85 3e6d2584bc6f0638f849bb1eb747ddd56c2e52b4eddcf6f482af68cb4bd4392fcbfda1b7d86a9398 7ac2344a35669bf29c3f8b9d590469d3596fea3253f55c31a703f6fc9e665b72739a456acb692692 f869e6c17329f63f48d3e0feb9f881871fea9083d0283872934dac23483e562bc54b2cc64239e0f2 bde67dd7ce90d66a268e0f74e15c02cb0a245516bb2d3ca49ecf164d35871b7e1e42963e17eed065 16d7f034cf66263f3bdfe9b739c2a64393a94fcc9c464e26f67735b68ac3c3787e0b5e637c7bae8d f1f9821ae3f50b3bc6f1e37e7476a4dd6ff8f91a599585161b830c090e85efcf8e7c2954f7313f75 ae3eb35fbf6e6b03f38f8b4780a8f3ce2bb26664fb909d6a56a13e8589c57062faf66a323572c2d8 46abda987269679c67146f742143305a65abf8a874659ac36dc5990eab5e931f786a90c63a07fbe1 b534d897c6c3c11eb6d703e67dd90e88adb4fe0d67ec1603537469a06127e2abd46ffe57185985f7 7ecb76bccd206a9d976b75a0cf626d278ded262a0defde305d8a18d6166166c04293eaa0113e8764 201596a4305b7064a776d4fad137e3f4658d78f6fb34f3edf779b8d4fbb6a7dd9e86bce8ded0658d 1eb4ef811eb46cf77a50535af4a0a2bde91a37f5ff87e431ff5e1c5f860874644c7eff2c4090d7ed 875d498760f3d199ebf296db9af32e6e2863bb11f383bd4e32fdfe23b47a1a237f7b70e65bea4ed7 cd76c78ae7a30ea5c8cb4e9e0ad8f6a580a551f5361d8cadd6bdb3bab7b6e83d6e55bd06def454bd dddc0f09bad928be8c66a3f5ca36fc8741367c065935fcf987fe0d4636bfcda8b77a0f92baea39c3 155acfc40126ad346c4717a7d2f18f9454e0726480e4b7c91fa76f3af605bfb7afc826d37ad4957c aba6b944932d7d7b8d60878e1bed6f6541c8a3faa6feb90ff9fab0b63cd48c836cd7d0e2f3599d6f 2b8939f4e46ac5c11be9a6a58a43c06a65e96baf4ad1e06a95a2088d7ec361298f21b9b5cd423c96 2fc4bb761c276606ddad79b77e56c779c16249fe94d974d1dc8c4e6edb33cde641bb343a6f281dd3 0812cbc3f551319faf999b52a56aed7344c5e141ab528c40af5cf9fac3123302f322377baf0aa193 e3f2f1bdaae1df2779c135d68af1518b2826708738144df6093ed67f903ce6a7fdb34c82a08b2fd3 083bafd55c83cf7331f9e2fc48d98f6adeba8337ca8b06d71a8eebdfcc98ab99ab9554a5ea33bd52 7ccf9c72e533b89798e1f8556ce50a7ea15bc8bef364f99908513fa71f026cda32e11cd5d370e466 524598594c09885bd3fd6c9760a84cecbfb88c22748e19b2237b19b28b667ec39f959cda7917acd1 7eeb320f1f1fede75df7a5a79da625b4977c3427c4193eac4e63a65f2986c2b6c40cd065b18568f3 7cec3034febd7389329e349fb3c42a8fd2e45b80dd132f401c3d10b3e2b6a164c826a225189c13a8 cf0cd9ca7c13cc0a09eea9cb9121dbad5902719720567e43fccd8baff8ab846f90a9363f20d3a5d2 c06f5c0199373b005904db804cb4901378679071093f8194dce2be8b2003bae9e6f2a41587095c0a 643ef836c150482068208b056790c51bcf04fc1764f348294ebe7fade8781a0cdfb63398bf0ba572 f26dba23d370b5892661b9fa18ff8634fcf025ad006472c5381190ce808ca9a4b1e9610364e9e704 64f93193e0923c55206e20bb66de09de28c86ebad5045c0764c530b94faaaf122cd9048692203a82 eca99dfc8f139fd617888df9351b2317ac1ccd9176e7bdc47ba3b07cafcd83c4f15f04bb5e810a6a 9fecdc7fee1fd3df9068f31582cca60f401652b28974681e64bfe72e8088de0240a4fd531c181a54 8f006a306e82f003a026994fa035127c07006a7528008d845d6cc227219e1c9f87189d674f91556f dca2f96b1546386764dfcbf7a71caebf854ec0c0b5b1cf618de52b5c62cb9758f8a41b985f9deb93 7ac6ebf3fc3724da4493171fae3f20bb74b27f52a621ab3c00306a6f009c2bc909b873826f00a00f 092530cb00faa29d784ad7479175efa4d1c128df9b6fdfce87e5dff4c150dfa5e1e3186ecce01256 b7353ff06acb4cc03c9f15bf25069d27183c474ff286d19e5ef9ac3c983216ee312f50eed467135c d6f3ff2079e1fdf86729622827a27ebe891c8f6b1dc0ebfd04c0b7529a7e2eea09be3700df87517c dc2a6864b366f97d3193d9deddbe9161adf09a065e585c078d6e65effba02ff9edc55c7f4505e1fc 92ae57eff9e922e947faa98275c91b1b6cdb9d59f2e8b1b81c16f7d563bdbc3dea1deafa6cc6d36b 43b4c6bf2179e7ebe4c53f1f5f00c91d04c042ad0590c69b02c8924aa6d8eccb0488367453a59d67 fdf77b5d24a1b0e675f341333badfb1d74da79c90c377e7e7c917a0ec7d6d6334e0ee74de6a1e29e 7cfce4cef9fefd718ed6efc7f214e337b7f46d5c9bacdbbb74c4d7c4e9abd6ecac6599c919eab4d2 e555db884b03db7073e47f90bc731900689efd02b82de38994db2e409ec412a035470468bf7e8a96 47f41aeeae37cf0f09337af51f36f28438bde0a1c70be152b577efe17099b47bde6f477871af62b9 ed8d5996f82bb7c9ab971074ad4b77407b8e62689fb366672ab6792d752dcbad8eac3c519d9e680e 8c4fa5d26570dc6cf9de6f48b4794ee47c94bf3f4b117e544ca6ff1e09d0ed66f513968e1c073743 a2219e5eb14f384fb85e7db9d4b1f4b9df9b087263e24fe1f25e3d6a97eee3d976c8d763e0647484 3a8f116c6d1fe765ce9ee163cdb2e9edd95a94bc7414385d76edfc71db1a344c4fe28726f151a706 3f5c8c8c363418e86fb341a6e8ff0700ea0520d166ef0b907dbb9288981f821c6cae63bb5c14c37d 8e165f6add97bddc799fced6ee7775ec5cdb44f6e1a88fecfbacbf50c83ec178dec66695bab53814 dba72b54199ccad3faecb8b53b9b636db116cdfd5536cdc63a7a18bcd7c9e851c3a8eb3de1436a2a 28cfb42c198d0e46e631388cf5539a9ff6d3fe078979661351f5410cd0dcb606d0772b11957baddf f703b7f57be3fbc6c36a85cded499c78e7434f0ee709363fdb58b87e9eaeea243e3e8c117caca354 c164a99f121346a0531d431873233d3a890bbd8f9b8cf671e283362c171d2dfb60e3c3048f4bea69 d5e9aa58244c95b3cc8e95657f394c412a85efb0ff1f0018a97d0042f01f806ef4fa9f0304a789a8 df980e820a983d110e19dd6b6e67ec1c0ef1da9eef6fc2e986de8ce3ce772f268784be217cddaf1e 8ffc9cde3fbecbdae1e3373468e8f70ec63b333da0bddc4a9d1f0841c5879b93e2402f3f5dfa2ef6 485c5e1fb4b65ca18b13c92dd5a7d2ee5d19497509257f0380c78d1820cc3903d0f05203398726a3 2bcc4c5e1fbdd7795cd562d5f9d6a62d1b57c9d9a9e292e9a297c98d49c5e8d647279d6c8d1e9a26 51ef83c90eb207341c602a55ed5554dc5bb415ba301bc8b7f58a923795232bb9d45d939802f190ea d7232c7255a2253681391142d59d09e2e2364e314c91e6aaffb4ff02c07c2d0448fc8140aee9d700 86d3dd902da884371f56922e8f435fdb5a7bd553b5ed0fcd16edaf74e5f316341dcee88723065f55 bb9879a9f909000a6d7d10f99e89d2d0905c1d7f088991a3ae44908564fec9973742180d6441948f 57a14b3632bcc2eb044f4eaa439e5c8b149fa96cc67ca6c38d520cff83a4bbf74280760b7fa27dd1 ea9ef9b37e23c8d135ac994f67949b065689dc164d1edff774b5b7a734f8c6ef0f394f57d57ca05b 4a29561ff2969042c95b091989780898c8e36c4578d3424b906063c483d975c5938740e033a3b6cd 8d8e6a94ee5e85e7831a7bc42f03f64874297696694d520c7f0340f28300a0623b315007aefa327e 80efbb4bd177beb7a96b5b97f7e3b82f17723ab8179a1a526c4c55db6ba7f31be59a5d48f2035d1d a567667c951ae3992fb6fb8d5890b83acc837b31cfabdb0ec167ce9d3e372e4e28f6b8615976568d 4e7b7bd9f0f78b6250dee7efb3fe3eff7ecff705049ea618a518fee0ff63eb3cd755559a767d2c98 730e288a04951c25080892cc19e4fc378cb9d63bbf3daef563dc0de35229a043d5437593095dffdb 898b333c35f58597d21bdfe95e88f6f279006fcac5c704e0e0363539dcbd6fbb9c69c5b75e1aaa3f 619dce15298d2bb56455ac752d6553acf89bcfb27e95bff0e725cf955b2259f12b7bb0221507f7b6 e8bc4a13b1d2a962c25a00793e7c72164f6dbe77be35d935b9d3ad3ee54e5f0be7ce951396619101 fe85b42d51cfb8dc102aef53ccd6cfed7eef1198ca240ba3dc10f1c234006fed2d23bffe1acb9cd8 d2e9be0669bda2bf5687d59ba080cda7be81bab7bd6c10e7a3b4c38c9b54dc6e3ee232a717051f35 9bc23a0a46fc617647f8961cd11c1b0d0daeabef2eac9883b23553d8c1ee3c61c5fc1a63c586fa83 4506f817e2a2abbe7f54b4c7545a0247a910dd7dcc6b9ef6ec5208769fced3318b5eeeb56d94c0ba d61b4363e55525b04dd414b32a20275343916171674ba8a285e2febab989d53af711089acff30797 a9f3ed9a34e0384499b3b7bd4bb28324d2980d021d19b09894e88fbb0119b033c652e03f586480ff 229354b3322e954e715c361ecd6b6527bec2c8d7af5e2d7c1f9d8184079631d8d95b3f34ae1a77bf 9794d7b234d8e8a321229b539892f3062d49259b36c5654c78029943cffc11845e7c9b9f001cf798 645580bdb7265d76c86110b30998351d355d8586b86a40253d374f192134a2924111a392f918a7a1 2d82fc17e21256047ed4bef3e1b27c050b05bbec4f9ff0b8fba85d3f9b47502a113bbdadf50275f8 96721b5d3c76e5c5e4359330a2ba16574c9b17822ba80bcdfad0e13bedf681e357d53b7b0f1b5f76 54ac951965d96cd3faa23ca6126784538b0a2351793ff2499b1413122bf6b2f8262d7d84b4d12246 da747df15f884bc779f1256c72a553cf6a3ffdb237ba3862757bb40cbfe86f4900dca91211d81bdd 6f4772015b35446fa04f84107431a1a9bfb22ac0a721f886138a89c53e56f9801d99d185510b4944 ebdf6b9e06e047835a28ef21697f6b285986173cb1b26d87a8e150b40ebe499fa841c622c50d2556 da77f15ff8193d876af5565cefdf6174b36f6eda239e779f377e304bbb9baf77c63d7b134f16bae4 a0f65dace780b2d06acf867cb74f2eb8c1982758796f0bcca71a6c99e9c2ddd35b2738d1b9caf145 2170089038e665ea20e16dbd1e91c6d7f09ac23accba5536ed154bb45f2b168eba2b76c1c1297c74 75920ef07fe1a7825e085afb04056675756edce96c99ddf9711b448740957bf96c4ea1e4c88f8d40 85fd23dfd504807d925e8bf968c09499261d9c36488ca573277243ed58dda68a373324970dfd4610 1d295a1f846d69dd3a58ed15d73e4d9737a1462e074f728b3f95ea037f32af36fec497d9c4ffb434 90e5c0f11719e05f7809c8ad70bccbbd97571ff4af36283917b33c1c9cf42eb00b37dbd7d116a844 96b96157b018ed18bf68e33baf5276431e5225c683c9651c13594dab1a1d8120f3236d7dc466ce9a 2993a7d565cdbf56bd839a5f4aa56d03576a694d050fe525a6b7491583aed50b9a08f7069a104b08 838c1d8241a236cf30fb855b717bcbc2a8c06ae877e7b68faf96a9aad72d35a99fd5f19e0ee5626c 3bdcbd2928341ced04d23d4c8f4410ed80f5a9f669ae3b746bbcba822cb6eaeb2ab57ccc4ed252ae 970dfc4dd75c5c6d4f2e58cc6311367b0859e2186a8d9d1eba88ca08e2cc1809c1738d2382bdf275 a4cc2c41a48c6de64879c241ff8573db1523af3e9fdc769174ba191e41ddb4dbba72ddc0127c108e 34e930ba31cdd2ae89a656a5578326b45d8ef1f30d57efc302f61dd16dcc289ca7580eff62e8ae37 a051e4be96d1a26999c87ef1f09165b1765ff8cb51b2207adb267c90de104c8f492ef57f7a01dcba e6cbf3f39a1ecdcf732df3a1e7e78138fd2f04d6d07e38a25bbe5b05d67be8672c1dbda3f1e72655 bff9907d8fde1655be7c36ab011131e88eee13c852bfd88ba00e3d168d8d5e808f58be03b7bd7696 9c34bf08043ae7070635ef3dcfe2ec6ed78db43f43fcd9a8e33ca0d7ad98871443e84193456d358d 9bf7dd342e5ef2d3e802f5a6fa6a094ef5193cfe2fb8693ff5d8cdc8f899450475d17babafc1f829 63e4e6caf75dc6a38b797cbbee6a4b1183e70cb56804d715f46e20eb69ccc4e674f65d9ca7801f24 134b6ad627c8081b808e23cd401c0f9660a557e2c65e82eae375b0f7c775aa921d6674186f2aa303 8dcf47f4a4af8e5ab74634a26e6673440569784a19d1784489f7d12fd8935af96956a6ef8f76efc8 9fcd62f97c089d6774a4ad9bb75b732d63831957975d34ee3faf7583dec3010a3a96060fcf81b81a 5efa823dec59d7cbe04ef7bf83e105af0c8651d8ebbfb6c0b4afac27787f52e7d99ebe09b4de0c6b fb3da0a3bdbbe6ad51ef2e8c33d25d0427ab5ba0aec56ea1341aa458647ada4ff90ba6b3073efa25 7f8a95193dfd88cd2f7c65b69fa64bb473391d4bb04458346b6512521b630cacb488d9b0d72c417d b5f9e47bf11e777af3def6dcb5ccd3bb5b6c7eb307921d9c2b56da5ed26db7eb8be9a8459dd6f3e6 79ba5d37bbc6556c88cdceae3178dbf7fa733fabd6377c1eae837d5fad838330aa7d8ef376ed63e3 bd5fd8d262fc55277321966a6f238b6fd8a9b50a087add35b0842d89f0f15120a7b15247c7eb3d3a 1b0c27c74977d15e8c3b4bc2e0da84f2345ac74ac56b5ee0c1a571c75a8fc6f03a88eae0b30dd474 14aa544d1e6e556c191e945d0d854a814967799d450617f9c2c57fee0a3c89df0bfd56ae96bf9fd5 793e8dd19514a6f50bda687203e4f2fdfbe6c6ba7b241b65dcc2f52628c327a0464db758051d1d20 68d607051dec94826b1614b65ad4046adcadcbaafe4abda39a4eda423539b94ac51625bdec2a2ba3 44cefb56e1b27fdbf97b70f5720aeb1c80d950bdfeab9cae1bff26c8323430034b660af10ccc26ed 420aa8f223a9a6e55fc8ceaafee1074ae94255f38e834712a12c9ab33e3ddd322d6cd4ba0f673d68 976e048edca94bd1a156835e57a89abfca93b2bb690d4b81f61916525f61949788e53437e9b4a19f c30cf2f314433c0541a5d88929bedb7fe55ee7919ada2afc93d53bae2d52486c8a24fd0858b57e21 8adf272ffa86a3439400d829fa3eb09ff4d1a47689a364be6a44c9e00846c970b88c92c6468892fc d38892c2344ca13ea2a4f905a2a435aea6603b29bc718a781e25ed7efa8d36c9a470375152ceef52 10e7c8f0fcf8b3db5db281e0bd772fcd971fdaed17219bed57632cb57ef03c7cc8e62f445faa1c44 499f3c45c94a3aa7d0af51729de463a07ce946c9b7374ba1915172fba6070ce64e9498eae9e76cc2 4294629e6dc9e9293d1e83287936a114189642a752dcc52879b5b791c940fb287fa5cf9fd273f77d 57a363e595c66aad67a7e8f61e97b3dd7bf09cd2fd837e8fe9fc209bf8ffbf9df45a2e8e51a2eba9 95d1e5160393f62706eca01a03a7e1308581c6c0b9c2c5c07ebd8d012dbd24005fbea7c09218d0c3 5a0c6c0bdd1413304272b37954d891f8c7a1cd6c62cc076f1fc54ff992e8eff5a0b17fd79ee8f9d5 bc6c9267e76157ef8fbdddb98fabeef0f6bedbc39b2a2983db74c4f4afdf02f91b51c22be71868df af31c0e46f712eaf00714e027f527b73810fc5b9b04bc4394b90e39c7cddc539ba7f4a217d22e4e2 143efb7e58fb54e46be7eda3e5f1bbeed666afc36a84bde8da8278660fc39fdcd8549fddcfcb7988 b3eae9feb418e00655b1cad590f4ac795e76aa32bea0536674defb8be1b9da68f6ff0be9850c2e31 a0e453535f8d6b9cbbe78b717eb9cf72ba07480a9589f3fc5b8df3c2c28b4a3476f954f9d5fbdd90 a9aceb7cb5b574e0e1ab72eb7127b783c7f0b405ef2f3684efe0f7b1bc456699b9e98bc9e69a3898 7935d9e3e9624bd5e4bc9ad62aa766a3d33d5cae85612859562f78a5772050b4692b9840dde65f64 0e5456c600fabac4b9462fb5d2179f715e5b56e342bb922d30bc5dc60506e0e302b6303e35bab97f 31c13b7cf6d9c7edfe861fd12dc69ef95bea90d5aed6b0d2be1694fae0829707d34bd94390f3babe ce86b553b851f95333f2b4233b07bcc3a03d7a051f0029f8db3dd2f0ac00e979c566abe33aa762db ad94fdc67f21bd90d5d44e03bec479f613c505eadc8c0bcf7454cfde76f8939f369c896fcaba6e9e 8305a5df551f716eb9ce20bcec7383dbd96fcfe3d3a10fe58f6704aa1cc46abb113e25b0178edfd0 38d0f0e5dcfffadcd237c433efe7dec5ad87e2eba3bb249cef9e606e5587163edd4ce83a3ff76d9b a3c5a6dd6b138ddded02d77f21cec1c3d4ca0a718e0b63385bb0b7d589d31f9cc5c55b67fda66716 f9780603e606d7f7fc6539d3b413cd11bbc3106f7b41346c64bd809f60bda70f7bc3d8b3078b8257 da6055d745958e5b75cdd13ee05ff0bef12c110e03a67e7aa7b4dbdbfd7af1b59368b864293cdd36 a3106c9a3ad56e98b356a96e24cebdfa0b3fedc647535355ee1017c24d3eb512e9c6a5ce7bf6210e 21f2782d738b5b7e0d2fce0d335a1d862f8909660366e3d9eeca74dd07e3edc3227fda3797cac361 ee76ec74a46bd1e6df8fbaddd70add9d94d4327770370a56b8a53615d69a9c12d3dcf6889b618dec bc5150a3e6d681f29d6dd93836756fa8d7fe0b71be4b9ce242f3708e8bab433e355148ed740ad317 bf1a8c6f8b49a3f723de078b5e385100c84fbd9ea5bbae3e38e7a49e14fbea3f2c7bd004fc9dacd6 ced6fbdb7c5953b89398dffcbc6cce31a469e6cac2c040d6db9951acbf89ad739ca9db4ac73ee86b b10368e18bac6b94a2675da77a8a57cd0c0d95ddce6abf90564f228c0b72ee9a7ab4dfc24f72e727 b8caddc74425eae7b05806c2c9132f7b3832edef291359d8b7ee9cd86df28860691d4a378dc17a97 e902b90de51b3b50bd1a457dfbde2e811db0ad58e7aa4e149f1dbdeed627da61852fb556b815d573 1abca8dd8b1e2b623fae2a83e7a8bb796e9aadcd062c3432d47f21ce9fc49f756e8bb5f1fd1f6d32 bf6d3c47d238398723fe7090c1c7c1739233b06ff9e7963d88cf134ba3cf9809cf6eb481521f69bb bf14b7db6abfb2d709b97ed41be3c15da3b571ac9eefc3b45649cb86da1b6999fca088ea01518650 8bdb6c4c76b70191e65bd6f66c5986b877574a7ad77686a6640456ed17e2426113fc51d14a4a37ff e2f7ebf84c3531ef30462d3bc88dc7cefea0e299d6690f31a266419be5c0b06b04bc75c71b420f20 8dd71b81af694c616fa917dcf7d59eef9f15a971782943fb966c947254d9807ebb27eb0d68264347 8392cc6e6e2bc137eb29daa34a2985dc11b182faf3828d92c33633347e212e2c76a9a917ebf3234c 5eb1f3fd7014fc9a1be4ac81e1556e672373796f16f4880a86bd2a75b6d54f75aa37ab435ceb3441 5ae5595852eeb7a5915db4d170e96c148538c8d115bdcabac04552f2548ad202b45b52fe7207456c d05889e59ca109ae3dbd09d57857146a95719b0ffce90f9a191abf1017a4f3212eb51fd993c2f731 4c6f7368ea61388db6b6b764bfdb7d1b43951d188927b350b4816d6d6437b453e53256f9e365a13c a67962f386f3dc66629714795bae5a3230cfbb92956b9fa502da7b8998de01c4f20cab0b2b8bcfaa 001f16038c27dde1866fae0e078e39a4df670ecf16c7bc5add0c2daea3141bbf101782efe947457b 28c7c3e3d465b1c0df85f8ce6d18e7ad2d0d4a9269866d7fbb0ac0ac0a68279ca9a9c2501f2aa334 9cd9a87377296f51979601379024a416e8a2431d1db1bc7703c15bde6f422d88be3cd52a57f9e67e d4e7d82a83709de025b2420bf5d8fea5944ba135d87ef4e966c872079934466efc42da96bad79fbc ceab03964e87f133763db735dd393cb3d17713e42519d8e9e2e8cd77fea90a44a7a48c27b3aefccd cd26f29ce63109e11942744286172b2d26d3058435c79b7c78e73d9e229533773a991faedb0d4bac 20be3bec60348119595538660cb55dfa7d3f24f407201a29f6dd0cad0ccd5f483b51fcf153412f6b e867b9a920299d5c97845a3b7b24a09a69f54c715bfb325bf51adb5765bc79e7e5ef1c684abb7679 2415f9d142acc8fda5b0fe4c18fe90b618be258d748e7d8f1caeab624756f8922fe63964f3cc58d9 a6479d029902454f8d194d7de1bb4d1905ed4b19c55e3d05d7c9d0cad0f8859fb6f46610213eb748 fbe8db3575bfef90b9dd6e9a8375035ff0a276ce3737caa64e9ee4afae64155ada51615d5cf6c2be e0a7a1037fd80318dfae96298e236b22d72b343556c43b36f3f4fb07066c8c1eb4864d12eaebad53 73d6e184ca1d6b24b9634c8b44bbcb88447bf95a0ab49ba1952173a07ecabff8694ef799805e8ee2 943c78ab31b5b71fccc14adb444fdfd6594c5407178f97bf51d997506af416abd0ba2c341642876f a74323c7dd4284bd0faf6b76a83cd206312dfc641083429cbd8d17f0a96452b852f0b6f92577cf41 852c81d488d8478f15b1dc92db75906fbc52b815a2ba187633b488eaa8ddf885b8c4d47357076d9c 4388017cb7b99c67f36f76aab7df19fb5949d7cefe4cda68d69c94d0c0b285007fdf79a659cc71fc 74dc6425783e645ef67ac6288cb5a4a3abc7d2b381275326ed6ca9fcc5db93a90b70214b4fe043ac daf5f23ae0e7fd75e361650bd0ae8e9b91b66226f9e78ae948e514493b433343fd177edad2a50e45 67df5e9d7ce76a8e5d6bfedcecb684946cd5a1dd94241b2a1202890f74ee9a674fac74b5e29fb4eb e7b942c7935c979e79832965d5a708b5385004e9b4599e2cf3824a787dcb216ab2775c9367efb13a f53e8555476e7596fc8745967dbdbcc11fb3cb0d7f0cc8528a7b1b7fd48f8d0cf51f6492eaff769e 92f87d9e7a05f7ecd590ad6f8fd58a6b96da9cadb7df89b989cbcd8d10bcfc35d7df712ca3aebe2e 3d6fd7ef94a5d305d2b1c4065969f943c23b7f6644bd9fc3d60760ccac290bcf2662ae4e1fdc5875 21ca5f0aa670c59f0527c1e57db1897daaf81c538358c0a65470c6a62856c0a660d0cad0c0a66dbb f60b37a4bebc0509530bf7ddeadab78cedd7db063c97550155ba1d4d19a917146e908397f4b7fac2 a92207e964c5348e44fdf48cd674af515e9d4db8b3ea1588f1f2e668f052646febe5b057e4f08d54 5170703475304dc58fe8f72b7f50c37856d17c610c223be7ca662f744297e6014167b31c82f6ed26 82d6f47a86da2f5cea7afbe679c035b437cd79f08fb46fd1dee69b0f76e2fa7c52d8f173439195ef 025eb731895bf58cbcb31c1ea16c214d5ce97009166ddd2aa6cf931e366b0ca7681a3023e8a24313 683ede0b88bdfde8080677fd856b23f7c50adb17e1e0d61ac0a4e89170b3c97b70b3d8fec28d8f52 871b67be363f2e17e5bfc8b4ceac0c236275dab39a75b0cccaf8b0252fe75095f7a82fa365d3e67b 0b59a16197a05717a5b3c0e2a20aa356ad29a30559f210bc787f2cbc551958d41e931a1cca4c0fa6 c6e6046e155fe8fce456b3494b7376054bf36edfb066d7c7f7381376780c3d4b8716247b380ac93e 6643e3e1fd058d9b8b0a342e4ccad0e8dd2ffd82e70d80a30d8ed8a3e1e5bb27ed56bb9d3686340c 842338cbbc4e46275a32117caa14fe166ae862cd3cc773f6795bcc6e0cbb990de2ab0b6d88e60d02 1bd877fa795995293409db93a452184f0c7fb498c004434ef2c3a30ceede0d1b449dcd0d2c2daba5 b1db3b40e3e543cd86b5f172b77a8c979b4769bca4af29d0b0f80bce6de99dac4285bae8a7b87e51 a2e2e12255bdbbcfbe0ba1417a7d4d583e8e3a811697dbc59c9bfbe0c42c5cba63574caf41adc965 6909a3e0fd34474da81e0e4fd5f573c8040630ec489fdae03a6ef50642919a0dfa7b6fdd7ff04db9 2ff725b73fce353ebdf7e1deeba9cc9eeda9b875eea9bd72d253abdd42ef0d0d7e237b2545e2c897 6d78446eea2b57bfcb58133df17d7eb1a71ca6a5ae84a0c4a26831c6e7e76f65369d01e06814bcb0 76ff61bd5bdd793ad87676c864d941c7aed429951a760afad4de5f0e9ff6725b2db6ab733453395a 417d3f6d11affabad55004a579c46b8726d30d0bcd7662c28d8be5ef1b1774fa69b63746b1d9a637 855f303c437d68b778fdda2c7afe4de86cdc804660d658dd204840779bfefa47bcef0ee049f280c6 a3740ceff6c7e3b0d1410be36aabe18f5bcd53b589373b12c6358482a2d51f6ce0d4e56e74a8bdcd c6a3362d804975cb32d52af00d0615846ca095627323969d4fd72fe3fb4fbe5ce14f592e47c91f5c 76a5b50d3f4bf50e1aff82ce31f25b99b52b4fb169344ecc1c7fd86baeb891b17c9da4e6171c4126 660883c313b3eaf6d4b3d3682ff156353d7fa354978f48afa6614e7698eaf756a0aaf97c81af943a 65a55c1d00db22bd3fed0adcfd11e4c5f7e39253aa8508d047e30a302b786360565c90c0ac5d4a5d 87f6e10dcc3a5a0f9875f1f47fbdb2f50beaa4fe1378483595b8b2536ae211adf0a662b0a033f38b ca629385854e862c46f6ba0664355b845facd6a5195aa8025c3e5f2921cf6eb9faf9f48bcc6237ca df7d7a9a7b1d9730a00b3d04980de0750a8d4ff1d17f24d5e1d44b61ddff554e9d01301b4f96295e 1a3003b9d3bf4aafd6fd05c91b19776ebc5242b27e1d1b583279f170a7602d278b07070d6e65b2df cd2556e6dc36db3bbf5c8d8ba55cb9aca051815b93b9bcb40e2ab9497b91fe78bf00a4a8e5534cd2 8bd6e7db298ee9951b94e014d0ea1fa3873923c53a13a81fd13fda74faa9143cf37336603efd0088 06bf1045df8a1ac5f8e05f542a4a147729278aa7f12d8a7b482e8a12ab19450f601c45e1024b61b3 5174c8ab29203b8a9eda21dd3a67b322a2e898fe4547309f82aba5f0bb5174ca8151e48c91142a1d e9d358ff982360ff46e3f7e155dd789757f5f0be3f83cde3fe24d1eb7f22b56eac45dfe6367b2f6e f41d596614abf3f43f48e7147d29f31b7dd1422dfab617c328be487014eb47228ab76d29059d7ed8 3b0751ec57ae516c4c3f29b85c8a7d25c5b71dc5e660146d17c82c9a17e4e5c79afecc23f8148c78 fbf2266ff749e51ea7c789a9bd1f9d1878dfaf6e9ce25af8dc856b33fe85d43ac78abe416c47df07 604709d0f17e9291a364dd2f46c95cea44c9e0054549beb7fa399b1727a4789ad1f73d4803a4377b 4be14591a13ef351eefba97c7666b1f94117ddfe7bef74c7ef258ec0ef6a455ebf08c2e19f0707b0 1e1c5108efc34ee17edb7848ee1a0dc6b9ab9e1be4aed0a19b699d9784ae00bff0c7ca64d671a264 d5b163a074bcc400860129dc7a0c4caba318e82e9118a8f84c9424695531a5a913e5dff8e153fa22 d7f78a603fef5ac3c8bd48c6c8163379353b7efdc90887ceb333f88c1e97577dfee84f06cbbbb4c5 b9fb687eb2aeba7b3a5ccc27f03a3ba556fe5ce62b8553cd3ce78e54414d0e271c8f7f21b56eb48f 92cb2c3311b37f92c85fc3479c83efa53847c2bd38b7d0a03837faae236bdf123ef8a2a8beebe5af f56a3572feb3dbfe9e1e82504c2b58ae15dfc7683b77fb94a0ca6dba829bd76f8dee5fe7b2364d03 a03d7a41f4227bde03f36c5ae9a9ce518743d799df83a78827019887015fc3ea5fef9bde4fcf581d 3ebf905ab97062005ced62e079f1e39cbf8be27c0facc679683788f3b33a12955ac5f5bbd1f09927 b716b3650ceecfcb52bb7d1ecbdd3529d2c1355f25ce1794a41fe7fd7b139fab5325776ac07ce948 1fcde681bbeabdf03ebf40e1d0ce2f03a5038bfe2c67b8dea274b8ba58fffbddbbf22dd9573f46ec 9010f1711a099cbd17f7a7fc8bd44a2635f52059a995d029ce73c524ce7fb4465c28e5461f37f8cc 5e9d06b1b8bf0e25fc9a042e71c1a6b670aee575f578bc2ad6a18faef6e168b50802e541a6278bf0 d9535c7fb697621fe0c2a2b7b85febae8314fa2e5e9ccdf71e67d2fbdaa361d9a73d78b4bb5531da 0d9acbaff53841b135ee563fd6b8927fff420cec143bcecd153dcee3dc292e8cd13f4bb3c8e74684 b72bc357e706f6ef93873eb838a30e78a2eaf1227c84db65308564c69f935bd943546beb3a5fc74e 83b653b05f1faf67e730793f1cea5b8eed333128dadd23d2dcdd146b9cdea718b79e3e934504e6e7 78f44da8db7819f0b0fa35728757b4ddd1c7cf166ddbef5f887303d38c73f1cb8c0bcddb352e3cb4 7c5c1c141bef16b9acdfa73c56be54866ae92044ada6ff553f430f6d3cb3d1d3ad96bf2be7f0fc30 4e7b5294eddebaa8efeef5aebd1b6ec6bef502f1b305ba6cda49acacc484c243d94836ad9e0147f8 dcc8bb0f768bd531477769fba657ddcb57236b7aa43542fea31e91c5eb0759e0f1bf9d38b70d8c38 cfb67771c1aadce262fabb5125dc171e9b391d5d9695c92d7c9ed9979f9fa2a9750ada76da0770bc bbeb206cbd4278654dc61c67ea9a281b49b2d10d33f06da3d03c64a9565bfb93bb6c31adfbd6dd04 caebab406b6961f336d1c8f394544fbd93a576e4ce59e13fab48e96b58b479f4faef8d74afbd7e21 ce5d033dbd9aa21717fbd37bb4e7b7d1632354b345e72ed5f2767bb80339c3cfdbb7f39ea4cf5fbb 4f0555eb0dddfbe6b6799f1a56f7851a05b3446ef14295d33db6b7d1d75dd8d04211b6350a160fea c9d6eea903714ed46ea55a5304021e2a83e179b5915524eb05e4f7d73bc9aad98ce4e9a8164bf11b f8485be5f1fa8538df796dff087e9986fa78c68de04cc4aa16be4c43f04da22decc903e8ed1e32f8 b4a6d545c1b0d445738b135826dbe9de4b9a6b61cc635a6b6d52eab97e14d42e7ddc28b776642a83 6dc9db3ce7c3cb66935b7ed2704f29c95ae9d593beeb212219f5c346ca31d350dc759cb7885c4fb1 58149c8fb01fa8afbfc884aeac4c6f7cc38a8b40fdf2f1e5f1e3e23662e320b9958d6f5540d6f5da 47c61696979df9ed5e2e06920e12ba9fbd96f180d5521f50e84cd4ee73b4504470b6da3cdd15b301 6b2b51fe84922e6b94ed647edaf7ec8692c1454f29f7a8e5c49dbc6a89c58f3b13f62e2c0855e0ec f3fe0179f1042d7c7902a53e7ca394eefeff88f327d889d3bee3fe1c4e86e1b9590e8d603bda4bae b74bb241da61a9c9cad20a2bdd401bd251f7b7fa476bb7ad92daeb7b2d65889c861b707982e4a81a a332249709c9f8d43809d6fa1b71978c4c113557bee016988bb074dc2f1f54e21a4f04d8846f4809 cbd1672e9b24cbb5b9ef9d6beb78c25efa834f86f72ffc534775e175cb2f31fbf04071dd2f764569 dfd28fccee0901b899bb1564dd8f3b9ed6a6270fe53e5be7374a11afc97a8fcd1eac4889248312fc dec2a23ddd6222fa0d29c1251ea2b06a16b67cc0961d9eec4ecedc515c451c33f4aaec452d832c3f 9548b697402e73370f0f46422adf0c11332a7cdfbff0d375927ef893827a71a5911d4e6b15d5f5e1 a564df4293b1a0e109dfba398fd7528fc751ee7ae1ba99b4ca5f79d6eb56a4c5a8db12b1697720b8 fe742aac8835c207476acd938ccc72c7abbae11821cca62bb097e7356079a5f664eef1bcc8485b6f c08c8805412bf6cba627f8f24a4fd84f926edd222aaef8ef0cafbff851a4dfedcae1720ede4d3b48 4725754f33a0b4db7c78c6cc633f42974e96e684da6fa9e666423a2719e8861fa9007f8a6219fb36 84dafadbe3c9636ecc9d3ac519c75c5b187b1d744956c8e112dbdfc9262397b61e33721f775aadd7 f3f4e420f4a96d3b5e52b3ab6d518038cc564e4acbdd37c53ea600c278ffc28f227d8f6a77efd4f1 1e3bbfe83c3587bbb6254b5f32ecd6ddaa4bf572ffe29b3784e832c0e281e8c0c253f03023c78782 5fe34e9b7d167b729de83064affa076207c017611ebb0ac18c4b759e7ebb538d9ed6f03d1587db2b 356f9d120ab88c3b2432f330b298630cc2d92467c251e83805176578ffc28f229d4d5a823fba7b18 8382e9fafe4eb54769d8612ea405ab93076aa9c85996fbbcf19545c768b9c29a99def8d6601a735d 102eb282cb3599676dd367e4703fa13f2d1fa6d5f3734d7d7b1f96da3e2a0a951b77333f8db43eeb 335984b65f02373f4da282508bb55f1ae9ebb57b3cadd71e14a540536ca1f72ffc54d02bb654bcc0 a84d8d3dfd12d5dd844a64032fcc38ad5bc2579b6997c8e46ed189379c50a77d8b3bcf3e27f68616 dfcc93ef1598f103aad3da18ed525f6d39a20c489c51f36487933bf2c09068f3b321f65c714754ee dde3da97d8685d7f1feaab83369fafe8595959b51efa71d57a36b2d8332d87d1aa7568bf7fe1438c 5bf773385ab83e3a6e194ebf436b16a05de46d7d3ae295e76cbe9610bb3a15ea871ec9712b426787 632d6040c87fd0d0e29c50861395a97caf9a0585e4ee3e1c90a5113821f66f022596ea865cfb5f4d 58378883b13ae6bffeaa8d779f4beeb6a92e7b521dc2efa350c4c5f32a4cf17ae1e23ef96478fdc2 3dea47fef17a2bffac031574bfc6ee3d8735b3d8bb2bda59ec081b4d1e1382afbfa65c6fa92f18a5 989769e8de7328f80d9f49ac427f089750f244f568d7d60173e9acc969155c37be4378c5c0e072d5 c913dc92c74475d92f1fb2b9b8b844e4eed8eb08173185b965f3b9550e8dee231f8d02f78946e6f1 9de1f50bb7c5f812fc3c26db771a07c3daeee0edd697ee9a9abd814b863f638ae38bca848e9c4296 6d472d088621cb137b4bd4e6177f4d1e1b8fd5a933fcae3ac2a2b4bcce95d6b2bfd3fbf8033d4e71 b91421f8e83aa4317500cbd8e4b9b1d12d7838a3b3789843acb9d14790fc9c4416efda1e599c9407 52c0956cf4cccae72f5cd6553bf44bc87c67cb9d9d69a225d8d4e9f77dab4c3a3559749316cdbc7b 12443a836d7f4df52d64c5d2357129f0130b7f02d2111f23f61353f7c7049bf2e52c710cfdf6476d 74fb588dd0b9a5cf915dd15d22c83ee190e26aa82ff007172e2af227867d906ec3ebb8bb82ebc263 07d709ec0ad761fe350f5f8bfb2f1c0746d3739bbdae6d7de7c6ee676996dad052efdfc090ad7959 115a8522437fab4764dd5a4d7bf813ff0c308d5c13a8c16e3768ee1bef1174513b2df6a7c96bb164 d9dca2dab5aab0ff7d7761c2ac81f363a103cf696745ccdb6c20cf2ebd46e60bccb8bb7887ee48b5 96e2864062b44d1ba2573b41c379f3060d07e5eb2ffc3c7eb21fa79c63a29bdd5e672c74af4c1bae 25559058e506d7274b15df26ba14adf910b13b60b676371cd0e8046e966ed49c59d795d9e58938b3 3ec81ea147297c4192fb05a0d1ba5599beeb48673ad908e024064374a2c7656632f398ed0458bf8e a035940ae0e20d416081abab6001520f60a117dec042d5c906829ff22fdca6467996213cdd2dc96b 9efa38c39ebc9bd8367fbe3b2a6d4434bd661a33049bf52a43b849258de95bed94265bc39c4c72cd fc1ab44eb00416a71b6bec7c0fc1180f2a999832f29bad68b43ea1c5515d355ac3c3f4030ee9ea14 1fb6024b1c70ad8233e87ecc4fffa6affafd9b4048fd1b510cfab761f7d2bf35eae75fb0c7a7816f b89b67a05d4323dc7c03307b0592d840271613195d9908ce791257812bbcf098c3107a246e032cee 4ec5c1f9d3caf5ef3db5df1f9aef45ef454de89e3256941ef8b9d8dd482f1dbab3daf0d1494206e8 98f2b1de5980ed71a750d2566ddbab643a74bb3cd89e5aee6b5d6bb96f8569978bd0be5dfa6cceed d2993bfd8269c395503fb9dfd39f454f96d78bc7bedece96f46a22bf1cc5131cc1a529341bb88d2e b83346b5615b46b2e5a6ba51ea81b7cb9bfaa7d902f2b9c629147b8deef830abdf4a2da22eb8b858 1ff08e597bce0b616d935bbc6ae34358ae7eecd9a8aa3177a20a4d45ab9254f16f05a6e6f38a4177 b4ca77a307952fad873fc826feff6f671b2a838bfa0cbf57192b4047be3febdb14163de5e5e36593 c8bea72e66224a0cc1dd976c0e3bc0b6dcd55f5ed2f236ad4ffdd6a4ef556858bd55f236932bdb6a be5b2e5740a8e4fa74e6d9144985a68b4decac16183fe7e4afc3f9352fe40ff97cff840c7272375c e64677c604de3214036a1b868189fbdd0093edd4fe05ede64d1f1b38d12e4287367d1a6920fa8f5e 9e7536c5fd159b893b0304d1afd41ef0d6b6d205141f688651f1537b3adcb302dbf95bb1f1191d0a eca1f1cd0b51bf94dbd4166d401b6343002a9a708a8400a0122aa7383800549ede53dcca0054a1b2 e42400aa76d814490040c0b104403923fd525edefe82322be51e62739d1c9879d7b3d65d0e165034 0d3066f7c113024b5fb3db7f20db6a07c1cef9ecadd67135ead2af92bbb2b379d2795ebcfac0a759 3b03d0fd180350f24a8019d02ca5603a29021098e54a488a259de2ac01b3c63c0066c3770ccc466c 0b988d3b688a8b02ccc0cd05984dc0728a2ff4177f527bbf9054c3a1333b6d951da225ef37583e87 91f39edf82418c7d0dfa0f39acb7f79d67b17e078adfca223f7c168e9fc5097863aa074c275e0840 a6f9f957e6fd8551f9e7307da59be235fd57eedd4ba9a95d2785f74ced456aff2abdd9ba0d606397 6277ff85289a734c14ad692add428914349de2ac44113c767f945bd84a3fb5681552c89d14311445 c8629d42e753bcb42842db768a659842bba638bfa3082b0129a04aa49789d6275973830f7c30666f f4e2af5eeea028bdaa9bb1f524223ceb6c9e0d9d3a3d8e33e4f87fc000e3c35f44d1564b4dbd1aa9 815b3a33154e8d581fe42822064e6aa77c49ad2ba6475d50cd145730b5ae8fa5e0d3af21c7cdcfd9 a0253305e4a5e04e29f68f0872f79f4fb2bae73e70f82abfed56d27c97f8c6f055957bf36743a399 079d18cafdb238ec5287e7ecfde076c7f7ee4daa68fb5fc80e13776e5c14331f368ab1331dc59367 baab526614b3a743142f3bef28ee51a93989d78ea210484d0d31fc1f053d4c9448a71be627b9349c cfa2dff03ff9c7f0f4b665f4f12e7dd0cfcbd5a4ac4f7b5513bdfa244776e7c14c03f0ce8ff2ebdb f01a7157250d5e2f7183732fdbbce85d0066e99ead0eb4ff85e85b98cbd1975c4b291021fade1f66 94d4165976b7f98abe615cf8d1a177b376f4a59549f4052f5894eb1fc90f6225e21bdf47eacbafe5 ad579dcaef9f8756237cb6b8f6e571ee81cf475782e2bbf05ee4ef0355aadfe4afddbf7e86f7f965 fb2dd167e4056aa76587f38e8d3a9169368736d80b424e4bbcb007dddde09604fbbf88bec15689be 892c4649554d7159b93130c93f62a03303a22456eae9c5bc0ca2fce608bd970f0579353872f56c4b 0cfbe839bc7c972a52f6fce6a63418eb061e75efaa77f4c32511f4cbc51c04af4b7ef706ce58b950 39adeab5ce31a017d0a103f0542069bae22bc1d1f1b6857be8a5e1baef5a15c973173ee9ba0571f5 7f102550f853d3126ecbc540a9a6c6c0ee18c4b93cfc8a738d6329ce15aaed4ff960f69e276a327a f4833274533b0fe4ba1dbc89cb220a99330e78e2c9a3aeda31e442f370922c3bbc3a5b2f1c54ac63 208ba787ff2900d93206fe146f55bced8d1a78b9e115778bec64e3f8f0ca71ea3613da746711ecce 42d3db718382fb0b51b27fc8315074e81878927a9c1387a73877db4771ee5e2abd89015f79dcc877 96a57ad31a42f3821c5add933fab8007aeebcfc2617e870660674ff89ae0b39eb1f3250fa6d3e871 77f9586e695ef4f66e6e70de5769fced1017316f1f67e7563a9e8d673bbeedf09634072cf3b5eb1f cc4909c87a01435f86de1fccaa96fb1731d07af03120bf9938b73d9a717e4a9fe2bcfa8d3e5565f3 7d8889f9b91acff8730a8a1c104a9552c98f947ccb5bf493a18ba1a5d97ec59750277854b3c7ab4e 13e9b136b3ef8976875bea3bfe4eec767dd3082cb9e05fadd1b9f235d51e543562733736e6c501bd b53875ab3b5218e8f8c80bf5f27b1b681e4b79daba8bb87f11031b808b73f385f8b3340bdeb6e3fc db3846f6e9767ff6c7faf1ba18b0ee91c1f77e3011889b57d097c97eddc22a4e33c1da76b7498e76 832933b3645fc1cc4f43274cf575e48cefe42819db38d28d1c5175b6bb0678d816153a53d5f5fde4 58d02b7eafa7f9c461a51ed8baa2b66eb8ab9c452a54baafb1ff83cd8da9b87f91def3321be7e240 4b4d5cee23a7b90b9f7d6b665cad72573c9e0a6dcedff277c3ad4097c34fdaf5d47deec4e1236f81 e577dd84eaefae0183f178bbf3caf32dbaeee3fafed027f4e506e3343f5ac91ae19aa6d6585d3c95 7e96ae6a7b8326ca05bdb7959e07c21ba97e90e417537764d0ab0752b4ce6502b1a4d79ede2fc4b9 c144f8234c66fad4fdad3e36e7b5bca30e0326c0fc6fbb8feefde548b67bd3c9de0299d1c548d658 bcb5c175497767eb865e0de5be46b654503d6a4e36815965a013ae5ccc2ba9f0545950faadbeba91 34ccde8c20e520bfabc9479e50b3aa14b78e5369c6a3bc683ef696b890bfa158681d7cc13eefbc5f 88730223ff5ccd37d52865f1cd05f75afc61d86cac7d989e2ddce5c299d9dcebc499d1ee641a8b59 7cd4dd73fcd242a092534fc54a45b9f6ea2da52f75079bc7280d45460ebe90df38bb9427be4049b1 184ad276186fa57961128816ce649a8d8874e3b258b89363011f9738ded308935f437ec8d7ee1b9f 0b25dae34272e9fec59f1b9fa5cadeb686259f68eac3fb89705cb9ab7e6de1b4fdc1c89acc11d228 cc70555f7d04ef27ed9a14aeca55b5e2cdc3d815e4f7715f93a71dbf2dc5d7e7509ac33948cae5eb 8868d9e3b598cd3f139cabac0ab870daf37ebe76e5d7369be7eb6c79c8513793e65a6475cbb227d4 4fb1f4d92e3ecc5c8e9ff22fd26654327fc4f32bf25237876120b21eb6b1d70ef37e2c7623f43334 acd617d7d7e38aa4b252cb5606f8f8247f1a83a73c55968094d6ce92b8cb110db148735d61df3632 5d40a8f006c4fbb31bc613b922c5d7770389a3db6b8b6b5dbc139bf69a09dbcba95de666cf0846c4 ee5b66f0edf9f4d368fbf4062eb9bff0a348bf9bafbc73a996a94da0ab50a643bb35575adb83ee09 31b7abf3685ba98a33f5dc3df0cac07e9ab2367806928115efe26e5d89853d5dca0b954bb5c213b3 5e8b6f00509fa31118e4da2d71c19ecfbb35cb714f81edcd062693defe901922613617977eb59136 0df2f1928aea8c4a6987bb4b69c77c4041f4cba320f4b2ff8bb850a0ddbbaa01eae938986ffc85df 669cd363b5b63eb0851888e58fb5c308041511e668591338558251c3fd49bbeea7e34975bc7ff184 7648b843722f71edc5bdc672ceabcdf6b8ea90b9f7c0192326f4927e2d362cad14ce1a0d9ebb3ea5 f7a81705ddbf7532b12c9434c18942c291ed92f0e5e893793eddca90095dffdbf953478d4a9c9ed0 fe2b79e5579eb1c5e2646d1a8c806c2b9f1da8f6caaf811c75da6b09de81b2e0f6a11d1f8c9707ee b8e41eec85a423b677e2738cd495caccf06636e89778cc54755a59e42754e454114a67519a9a7535 854c6e4f975c2cc64fb250f0ab84bd47e604362bc84419209d14a6bf766566ff0b7f2ae84e7cebe1 44da8b6e1d4dd8dd861b65d3b18d5dcca2dab12a829bd740ef4866ff82092ef7e2791229195ca75a f2589eec5d1889edbde8d76df8a527c349818af3648dd26da543cd98e39834aff739b910ab045980 973291f6553651c6beb7b557a1b38860bdbab7a0758d3c49abb035b1576191f433782b72bfd8ffc5 c7c369ffb2b4433d009c8de89cf90b6b69499fdcba6b0a532fe6662aeb28d916cb4572ce93a6c9b0 d7aead328fe131ebd3e8f7e472a027dbe84e6d1bd5980298669eb43a932ab9b8aeda8433e787049e 4bdb4c79775fad3da627ae6bd7a5b50a05ffbca2866061d57c3d27cb93ba15972cd4b4976965f697 6c75e42e3b41d3f941f66ea2ac7c0efabc796ec4a9efe3dc08d1be0347ce34c90ea5873976a94827 622a15fafd164fde81117b65276b468649999e964993da0a76485a1bfb4a22e0fd4338fa1b20f079 b9bcf60e9de67a4dcfb21560d6f536335d85918baf5ab33bb73ce77ac6b28bd247fcd6c9e770e16a 8cf001817029e21d3e58747cec5928bbd8e30d387f71d75a0bf3d8a711cd25f290b8535a26673885 3ea3b16d249bbdfaa3470beb56a1c55e8f6a87fed4af28b58d0b1cb9cb7574b248af3ca2c2af4e6b bfaf3cd775598f5787715858b5f68fdaf2bc2a77975cad3f5e761f0c8adf6497c1079f48c39e3a1c 6260ed90a09fc3323b1b54c35a0caa4d2c13d5a680874eaf27e7176ef9dec80ae2e0abecdb93a164 e97b5bd8aea74d56152e1421cf7d1ae658df6bd29f75ab46e55c1c22f6b24aaefdfd69b36ed4804c 1d5cd1542d589e3fa3ebb207cddef82da1005c34d5323e5c842decd52a8c30906bced1082249544b 02054990bc8b9845314260b7dd41f2ab2f85e4e782b1d83d1d77b13b6c9d5fc846cfd5a8b9f38bac afd8d2bc219b48d596f49658e614b08693e2fe882e980d2334c9dd795f5a07cddb6045271574c937 e71c7e67590d1f7e9d3da6c0a72336c9c77754c70b113aeb8e0b48725bd7115354b35e00590c4fd3 85ed14f005862ff845b9e2ee60f70e3de1dae8d2806badfd7a1eb85d751ee8ac330fd2416c4ef6e7 bbbf385ed7a1e512c65edb4d760dc5a890f646eb0d3ba29c50382d901d14cdda8d8e85bd35395a16 977db85dc5de1d6e8ac6737f8dce8e3911b13a5d0329085377e10ca8d302cf594fd843bd2fbc2e7d 4a70cdab37e7a1808fe6cda7bc989d360f7ac6628bedac5bbe9ca1abcf9421610165878184d17303 f553471beaef2bbb5ff87960f2f314c2b42e554d3f9c6c5551b0a62c611ac272d7398c53d6b4305e 5e59a2826e792e593867bbbda8ccaa73b84e81c49c3a1399733b6f69be396393b33beb5ab9137443 ba4f482c22c9f4c971e5e9a6e777a6e347713af958ebf5042aa65eded71d05a0b17ae440b81eccc1 f90b15c1f9d1df81737b6781396c63fe85575de6b2f937bb697db935aa4069abf52bee7633bbf655 2134c71cf3293570c2af3f407cecd3f545e53c4c661ce326d090a875a69b17399f44984d4c20ef22 800951d241f8d573c0bc821ec6f664f318a3b1ff1d97fc6a16df8c560db037aa1e357818b04576d8 f8b2bbc1d1ec7c060c52030747eac20d8e28640d8ee0d01cd051c3f80ba7aff8ff48fbccb86a29ea d036257c57d638b19be328741460abde4cc85c0e348f418dd9852c039319587b8ebc42180d4368d0 1c9289000e4e64880dd816400f3ae7bedc1720ccecf713c5ef3dcce3b527538d6f6fdc5a55bb2a67 0fbbd3596fd9f9027bb533a7916b27d719b43bf369279bf2db9957946d67f6e5f55fb0beebbdfd93 6aae3eb4de7e93635c5b6857bd7fc5e8fc68f1478c26e5a8fea3428d3cf3f1ea0fe8cfa9f39577b7 4eee522db6773a9d4dc46c97006dd2da871fbcb5a45b4cabda46364d3fb2ec6663763d368ef551d4 a00f6ab57e9914c03a172b74bde7c36eed4e82e5da7d3c58d67bd455adf790935ae7e4ebff81e182 3b57bbca5c161128d3fbd01789cdcc6222b12e11247e59e313d99a2d6a1ed79bbedf8bcab8b29a7c fb228c3f3ab0ac9d9a6423f26bd20e732bf18cbc56b6b52b50993dfb8d0ab02187650b0d16e534fc 254b8e002ba5f253cc3a9be21afddc0ba107150bd4fa362eb4865b3e7f7a33e73ccbf1fdfcc90589 423312f95fd04ffcf9a07c34ee202dbdc0e546054b275716cafe93bc8d9d47d0d8749be365dbcac4 94bee85b9f4ede3a5c9bcd51e3501b1d48b78c2c42abd052726a4e5c576ce0f95c5f81cd2e4c0090 a9d752b0430054010c001d5e04c073750f809ffd1b00a3650b00e33a064c2a8099224a804935c9b2 86800920f2297ce52fd4a77c3ecbe8a91df2fd7e73476197b3b494197e8d54166308dacc4add7115 bc96fbc3dbf5dbc6a8dbbdd171aac7ea145c79457faa5ab95b9cd30130f4b5f4f79edb9fc30ce63e 30999f5ec08418a7bba2d50126417f0e4cee2e034c6bb0094cfbdf1b309d1b15604aace7c0946c4a c054ce5f525ceac074636029becc5ffcb34e38c8bbf4e235c8e6dfac04f1452378222fa08d0e0ec6 55a55cebfd3fbece735d55655bb4cf823967902492413288a2808a1131a1a8ef7fd031e7197bcf73 bffba7e11acb6fda2dcaa2aa55a7631ba74ffd80c7adda436bc75226907739693e590076f7314b03 cba72da2f71700721a05005a9c6c00b4b5de0328d3bd03e8645706d0230e0158fec202182c8dbf1e daeeed016c71cb0058380101ec311c015852580078617307f0225b4fe183bf30eabd682be3afc99c 6fb3b0f64d31c741690af7cb3152efd8eff482cf53d547658c12974200709f9574e662f369587cdb 0790d96009204f7d0da0f63304b01c74063088be02d850bc0198a8c50066ba0980edba1500070e08 80f758f19b8a0ce0a012023854cda6d84100dee7c4bf751ba4c34f6aef3fa6971553605c0a98fdab 7b7d234988c622c5e49424c3522685d24871459284849814a6f2d5a77fdd291ca4900e0916489f4d afc79b1bc70f62377e3eb2a320775f34e2ea9d8a6add7b416307317bddc871799c9fdc84a4bffad1 bc5fa5dbf41abb4b44e6b7ffe06f744d2a458d4c01325f733b986a6994f934044209533c5f699464 25c51a4c03ac0c5370e9372417c6574b3fde6ce43e886db4bc2fead1e65e50a2431cb49373cc5eca 8fdb16aa0337e13e2c5f0fa8d6b94aafe4537df4120d3be9026629393f4217dca8cb935dc3fc1fc0 878ef78b24e1413a459948123cc1fec6fb11ea83db340d914cbf0db18bd3107b851493f6579f7fbf 0d89b06980d0e891155b6a1a5bd188d973d1be6dc1e2ec56b38bfe557a16836b73d63d5cb40c7239 5f28fa790603ab788ab96b274aa42e156111271fddae393e662dd10ea90764858569ddfce293a5fa bfff913c8b12933c5b6c1ae0ba80264fc41792279a4fff479b4cbfc86bb64f92f9e9962462239b38 39b2f6203668e74e1d50380e5a2471dbf650fa7a80dbdcb5e9b447978e5752ce66bef4990b9c62b6 649fe06d751e39f5f6eaf85606e191388fe2908ae7f903fb7cf5f6b5b84d6ea327af6cbb3c2a6fae bdbebc316f0d6903f9d5ff40f2cabec4e4b91893e9ab3d93bcb69695bc2e4fffeba13f75475e23f1 96bc1a5ee6910bdbb998cfdc4ab7bab3a95f4ee4bc73beaa3e7c862eabc169026de8e839d9f011fe da48478fd868e192f6adb0b85e39fbddd85fecebc97eb76b575eb7ad2ec1f94d3fb8a19f05fbcbaf f24126edf72b8aeb19ab42b76d2c57d792be64cd97f68be425e6f9e425f878f22ef7f9e41de4a6c9 3b91d7c93bda9e12e21adce36dbd95a4212ddfe71850f3270c83cb51b60e7f6e600e57e75aef208c 93febec945d84ebdc6e4d6781c984dccc6a38d5dc96aebc4aad96b3c8ff981cb0abb207bf16f2b1a 7ae7979caac20b717b137cf9d1187b27f46579ddcdd174afc2caf8c5274bf5734c5eef129dbc5e7b f48fee1d0f9d27e06f37c930391ce374400e2f9084862720773c85c1418df72daaf6dc5a4025bf41 8ec5ea7a86b5da8127b6a02017b6d0158342c3e57a83b34b5e103f538ec52e368d450371a6feb198 047e5b689e3c3dd632ee8d29f4e60f4360e758766fcee6f4c69e65d686e5f8bc60fe83e4cd36c8e4 fde09927b02faa3fa9bd193e5ec5a3e5b70b9c27d6d1392e959ab33b5d357f33b970fb7546e1aeab 9520bf9702c2e717fbb555f38f37abed77600bf42e8535e681dc8e74e3ee9377e13120cf130ab1e7 58407bb3b7b60d67c4adf07416945c770aa7f2670e3d2ddf086db21dab93492dc1c6f6c1e958ff20 79afd2d93be0dda51febf7f169b1345cea977e34158e259aa3b737f72eadb360385995f570b5909b a7a3df054eb107351ec0e7dc20d8a3307776f9da1c1fb53b33f7d1efcf8653127796e511edd0db89 385d436b7dca3d80d964c7429bc9a8ecc4766815cb76ebe10cc63a9e55acabd799581659b6ad7eee 3d36efcb8bf58b6f4a7cc3e2be52fa992d46ce15344ac23168bbe83ec277ddedf8d2eb063488b28b 30d3d3bd2b87baeea44f6ee78332799a9110ff7096cb71d62995a6a5296fac1a935dbcea4c468b1b 3c693079c25636a5cf086db78d813c3e83fa78dc5b844beba64067ab7f9ee5cd29d4458cd7c41919 691f981a99f9c1d67d6231fe074f60b4139f99d362124b97ab1ed1238cde77d75d6433eb90ed60f1 5c7d72a016a1b627bc3eb691e6b3fb6d3acb1b8fd574433f0e93bd91b94e1a71e969ab7427373eaf c1f2d8e0898675bbb15d6b3c36100ba636a4390d8ebc89f125dd78f778d720fc5d6864e93ea053a7 2ba40535e4b347a0b10777ac6e5ba6fd03614d5bbf78663275f59945def3330e9f85b0fe2ed09b79 fe8e0425a5d45e3660b8ecf557303ccf8414efb0826a4df64fc9b5a3ea346d6f781e59313efdc87b 0bdead5ee9a2769b33b1e3a56cbc9d72cb1802e59e91dd11b8be18a98c5e78f8b2163899a9c671fc 56ddeec24415c74a5b393c73acd2ea21861cdd86b61ced91b1ac492deb17dfbef9b9489725ce8e96 f846dc8d5b0b6a5d2887e8b22964bade6dfb2ecd0921ee39e5b4112672aba98d2f7e6d6ac511bc32 932bbc37813c7636862c71d7175beaadd3a659d082bb5dd5b8c9a2a555980456c56df663d5d5ba89 094a084d2d45c9e757f2491ddd64bd5baac9ddeb8e96ac665197a0a8658fee1c60fd83ef89bf822f 52feaafdcdbbaa512bb1bec47c933a765d2cbc949d8096eb5f792f2d88b1b90b4616328dc626205e 3c23a75cd63a7d4e428dcf2617adb22c27eaa85401d4fa192f290a88d794562c77e5d322c0649d7e b372af4de89235f77da93f6c9c478ffabe329aac46a4f82a879fa72e8a33f73e1607c3d0fa073f27 fe4990ea41edb0e29a2e0ca9658b99e11ee26c7ab3c5795b99d607adf2f8cad1a8f95c89bce199a6 a133a8e6683ca12dd4916c6e95f0e4445fdddd73aef2e91624b2ee3f73d2ad089425eb94aed1fb3a 078f1e83801e4db35965841e84b9386fed8fe2e02515c58cd918083e34530432dc8d85bce259ffe0 274ee030f8dc88b91b2f0b7cc06938ed5f0d057781ab0f3a7c6d51b5f5fe296fa1fd1c6478972ea3 9798aea2ee6badb1726c755da53d1bae6523431fa41b459fa47ec0c5a307a7bd46d3eeae3842aff7 baf8f2bb903897e44f595031132d46823fab390235181f84420d2ea48bce07ceb30c2ff1a5d76ccc 6de6bac56d8c91f98b0797719d68f96c6b5bec14f3abbad7a2bd74423998e7c939346dd04e6ddc67 271933c3ac3b5f79bf080975af3f478a0adf0dd9c09f8e3426effee811009b1156010ee27c049c44 a2df7d88d93c9f15fcd5a42250eab52b14ba158c0f0059e0592fb2f9b244edb96df39de584d306e3 6a427bc41e6aca6745c04a8b81f90f6edddc5a3f6ef886be190e2ec2b2dda833ee4be6891f19dd7e ba0df3658e323a0b927555d247a8d28175568a2b96324a1a93f1085397ae38bfac576216da7db286 046a793d09051688f9a0d30378f6c294f8b2ebb5b9edf08e726203e1b8dad1b1d883d3dcb232be05 d856c64098c8ce888cd6422da6736a9b4c675d357e7185263b336cf66fdaba28051f0fbde84d2bac 9b793243a7c6ce91715cd51a06c5f400f550b815e40b550225bbd226474e7f30125d8c3185c5909c 0a747db2e40365b6e5cb67efc86d8df0c689c3dc9babe7da05f6108e9aacac2c61b685153f170226 7a7306d3152f017d6d084fda8ce03e6d06478186060d8bba67f226155f5fc62fcecff3d2da8397a9 1a549285e8c3e522372ff024396d5ea6a895f8664bdbf69e6fa573550109e9acdadfb4ebdc191773 a5172714a1aac67393de84dbbe508fab1383800d6b7ac8ca07efcab6e4f0c99cd07c9ee9ce1b35fa 2a8c20daaaad491aba7735ea3e7197d40403ef140a147ae4d3325872d6381b296e9f393439cb1f8d 5f9cfcda6cbc759694b23c769d918799397e56c930b47dad1b9fb59adc565bcddd5bb28f8d87e8d5 a99a40bf0c98af2cc614376a1d6536d41f16dbce96e68c4e57974cafd8ff7401faba664eb4c5eb77 ea7ef333d4647cab5028ddeb91afa24290b3f549220706e70dbdf8721d9293457b48aa0433cc97e7 7a7a2ae606b1740dfd1751b9a5db9bdc065617e9cf4b72b3876f6558a73ee6d9f17d391e180c9e8e 8656d707444fd8df789e3fe4b9915debb2ca9a2018fd2609f40d1e6b74df99dbd483bbf914ba7bac c997540ac979daadc841c4bf873e3e2e0e29e0d81ae6f73d9c5849d6e75679a214dda783cdcc3e0d 0482ae0f847a961a5497849a4230f03d0a6bbf3844e3de38e00f4dcd878fa6325bb79ea3c9292239 6b5e1a0f7501b0d3b9a9b7c9f19bd6f8c6aa4df841df76e627af9386fb314261d89b22e7bb8e4466 1bb031f48f82332c74d405b1baccb704eb9e4f4499cc3f06c2aa991dd458b6861f3a7b186f5a7906 8bfafa18d30aad03d609ee25f4d2d306e8a5fa96d14bada6a3976ce133747e8f5f6c9fe2c65caa5a a2b9ee45549d91fe94ed7197144c7fe3d35a73e42223629c29734d7efaa092a11b91c4eb991bd2f5 6e8b08140e25ca2f8719884420e187c5c1c065a530c35bedcec740619a01efb02e289cd14becbf50 73992d23f752a783d89bf11041ccaa0e3bf7c91ac6a76416c6b52e0ee3b823a5d86afdf7d5567eb1 5e95c6961f7bb1316720519fb66f4f758c79fc67aa6e94e339ab98aa898ac5663a7699fefc45e650 f334109fad2bdedaf3054c87d71df4ea5c51b45fad30c8446a8d10b43934e067a439f0a06b2fe00c 7039f43dbf7cebe70b10002d03bb0e317a1605d737f6f36c2290b7333e5845d64fb0da9ec160b5dc 12c1ca1b51c0caa52bff62d594a9f13725ded92bb265c772c5b4720d45d5e4a7c149b32187f123bc daa49ec92233d8c5f419ed4fc90d3cb8939f64d83e559ae7a095f168422cd486c1cd041d8202a30b 60adb4d47a92104e7acd3ae0778f21b4ef765039ee9c5f61be63b850b71dd75dba3d3e62d336ace5 af6d98cff4da704f61db707536fac5277df473f4ef3d78322fb9d164dad96893b1f36cd9068f8d74 a52f619c58120084b9299bc670656959cc982157383becef7a7201f43a174f883a5063f36edbd377 b98de060a7f50404b4858fa6740b68ec3f1fd374a3bcd51c6aa8d75802fabe417bd7477ddd442b75 2edaa1b55d0f516a625cded7c4b0d6a8ed7247aab67d6484da367cf0bf70fd023873e45c32b7ef84 3bb7f2bdaea39e99bc3af2cdcbb7028ceacc608a58b0f54173dbcac0ef04b882f5c179dbbe8fb27e 63b980a6b57da6b5ac3524f35255a2c52b3d85b972e53c80da15232322e59be4d2e5713351cafd13 ec94a683f1b68465f349f17598378b44bbfd79745831f32efa05dfaa148b19ef801507b71a539c57 9bff819970ed78932b5b588c078bed426fbc3d5b7a37d51f194dbdcfafdee0f008cac8ac6abec0dd 443977fa9cb16dac40dbab1e77f7cfd34a4b09d436f25c53d57375609e8e74de639795a5fe2ddb6a 1ad94c14ed6b194daff633dd81c200a6bfd3018886960024c77700529c2600a99200401ab306209e 29a77822004454885f7c16857a1e5ed9e8b6b936040972e544c81b5f194d3ffb16468c326013799e 9e39705f3fdd3bfde4766cac364950edf8c5790987183bb72b084aa67bbf8840bf7451817e877281 3eb6df037dae7707fadaf73e69a03fab7680fec92301b8841b29a21500778c048071ac0dc0833c9f e29efe0d5c670078e8a0004c16f85fd80f55d899ec6cb152c1fb642a7216263393ca7d48ecd6d31e 8a4e869f1d0f706fd55feda4583a37ca6e795b31c75dbf380cf9693674240de82bb104c0795807e0 3e6b02b0b59901b0dfd800f06d7907906c350f20e0ac032062970410f7a003c88e5d01c8adf400d0 ccbe09a059e5d3d300b43e7401b451bba788d23f1724fa1716b52c6fb52e00b8233ad818ac458bdc 90b75f303a85dc3ab8bf9299b6d3eadfeaa2091eca9326b42c944aa493312a461a1329595f19fda3 bb6fffe57cd74c1acc731d02681b7f032813d600743a440034c98e000cde78004633170053aa1500 9b9c08005b3963008ba830459201b093daf985de2ad0dfadefe1a16173ba108d4866cb0f30b85bee 4035cd2bb471847fd4eb2e1b955e057a9ddbcf152f6db4a9fd27b041d5fbe37a7f65b47c3d00e832 7704d07beb0460753c0d47dc015f0fbd933b7f5291bf057bc17ef0b77e83de04f07e974e71180338 8cad53acaebff87fe9d3bfeeb4e4a5d08f4932fce60ea647be9662072509594fdf4a8aa3bffa3471 12a750f0132ca8058f3707ee1f6e85383d889d10a7b3f2f1fbbe6804853b757c37ef050d8463f6aa b271d97a58372169f8d7038eeebe9af753e7f6d7f46ab9c7e6177f03cc0e92047fe3e9abf2f0af3e 7d3869a8fc3ec5e99186889652cc3a6960009e38f92efb70cb75f1416c11edbea8239f25ee9d0a87 d37b4111bd38689b41cc9eddfd6d0b2e4e37218eeeb7daa498bd4aaf6ef512111c78e92c22ee6c96 0afa2916c0d9d7e646895c7623a7f99e47587499fde24f7478047f1b0ddf427fe3fd1af43fb9d2ff 9528ad341e6e89efdda9c300f9ead3b8ac33cc4db831c2ad361ec9572919e9d7a663d9170db06697 8ee72dce667ebd3d83abfbc7aa9fec4a318992d1a0747cab13e89835d7645898dce543791e6afbda 429177079a917672111dfd220decd04fb1e8a5305b2962f46f8f98eb698855ffa70b38d9d3a746d7 23cbfbd9b8acb1c5eba18fd52e1106b72f1d1706cf660e434f31030f4e7689a2a34420b8083b10a3 a3dba2b4635667c7217553dd039b18eb7dcd33a26dc4a62baf8bf86e6cc063075edb1d820c12b3f6 193a0307ca0efe41929cb27092385ada05a3a0953c69894c9ead79daa4a7631aaa919f3ddcc26d19 070d7f77ab99c6e9d299c3b7935d2c249153cd658e6ee3f3d42ef559090be6e9e3050eec3d6ced6b f34967276727d036a267d8b6b3f6c88d59dd096bbb79d28364567703bc6a9f5643f8995bd233aeb7 58574d6cc1dd34ccdf5134fa0f9227fcee26cf82564a5e35e1db055e6655485eae6226afe1c479e4 f68fe94d3c82d34b97dece4f8864ae8eb97e6b73e0b6b9701722a7f3f63408efdbeefefade58f02b bfbeaff395355acd3782d9b8da0d061283adfcc28459e5b59dbc64aecfcf8d318b0d85af17d528b8 7bc77dabee5e0a26ee829a4bcf6d40a3e6b0c7915fcc12121f7e913c17d956f202b16ef22eddf0e4 adf2428a9d795fbddae6551dc1da69da12d5635e03ccddd1503e7b6b5bb0a52cd6d33dbf0de68972 5a2d2a4abc2a5893f792cbfbf9c556dd5416b5cba6e11fdc47cf6fe55e88a7a955d2eb7606a26bba 9635bfd7afcb39a2429799f3bed61caf8f604e6ea57153a68c33d3e2b6fb990b4cd66285fa45f212 04308df28cfdd1bd8d09ff585c48e1aae1652a720b576c7fa8e7f14dbc7f09817bbd19aba27a9b2d 76c3dbca0f95c7de3bcd5f67af572f3d5c6bd204dc3e0a7fae9ef3c986a8ce5e35a2399bc50634cb 2053dcf18b37cec90b256dba02156f5a5adc4f13a1a4946d093cc1e3e312e2c72a53e7c69d528eb5 ceeb3bf38be4755e217f42b44ea39b52a53efb379137d8d5c275255fd84531515ccf28025c95fa04 b5689898e4196d62ecc235d69b632d2a98cd676a38cb56a7578792a60fa7001f3253d63914a665ee 5d9b08fb6c7b521bc3882d2514693783953cd67af94faefab873530e964957f366bc9ef44d040438 c3195f790387779cfe4e3c567f4713e68be4cd96886f9471a3b827231f4d87ede38eac6d1e0b1858 0f3cffb514a06dd3bbd4b6980baf2ffccca5e2cfa3919d859e75a6815f5e4eb651793ba9776b475b f6c09b9d8e5ccfb1b61a66c65d552b59d78edbb020f7009a77323b3091232c18cfa96f1b0320bf31 80ddeca593cd7e4f5b6a135a2b5e27bcbab614ee8b8f1de4fb0cfbc513c82bec3701f98c01592cac 2dbad0c67133b5801e7472cb9a0bde3d93402a739cc72167d9d49869c593e549fd688fedd6db75c7 bdbabbb22c6591f6ed73109993791c1baf6cfc19d38c5958c9190305aae8de8b6eebe4dc46b4552d 623426ece8ea069d2e55fedd8895bd786e2b8d636f281f599297d5729ffb417bdb60bff81ae9b8a1 94d888ba27831de48560b0b41f3f1e7af5ce7b563189e77874ca3ba502d09d8cbc0a313e772a8275 1b7675f3a12053e365409e4164876bdda7877b9d2a8a276da56bb1c6f6dcb7baf1770555a09e75b5 d6822045d2b9a1d21c448a1c65e14f4923b923ef2fd2a56d3424d0780c4631d41346e3305daac3f2 83fdc5d7485fc6d2980a77a578b819b01370b9e7f74dbf77498af379e6f270188f7b4e1adb49636c 306bd47cc46bc698afd79f45a1eedf42532fc05747639dbbaf6eabb98d5a937207e5d0ac9f95a603 3de4086732b2569955a4cb68dd964cb8824b60228aa338489c11d293223189c1aa88b7c6a8f03e3d f81fb8fc86fde223ba3ec76fa8672c1a337b9dd606c14a54a085eaaf5aeef41e956685c23699486c 311e1b2159359d2c0919d91933d40bfe48d0ca91aea9b5ab3256e4be3e93a3d56c2977cbb38d74d9 ae43c914af9f7ce851fc009e237bdace8f1056698acf4e8888b855644580b46dc12bd4f6c2307a97 f9659580797abfe179daf739be488dd95f3c4aea4e3ee52a2abb7d0c868395b0e4fb3e98ccdbf321 1a7d9ee935dde28be7f842accfa633b8158cec33dfd138298fa9a2d3669516591ac9daaaa14bd74e 7f224126ea8eec3bba1c2113662bce4a4a24e21bef2e02c6232b7850a32a9039051272e19de1990e 64f0c5f7e5735704b7f69c3cc75320cc55128365779af02f6e2adae0a3a267f09bb9d41d2ce53603 7b88e67767f4e6509dc8dee26dd9f6e464e49a2aa0713bbfa18498df974fe2319d45b7379f1d8fd1 dd382a2334fbb0c419fd988ac0fae10a9e500a041244432117f3579ea1276fbed4ba95b9f5b9dde5 f8f984e4aac39cc6eeebee9a1d1db92cdbd00a7de658a11846f130f61f7ca683c6b92d86bb23cbaf 29a738586873127107c0bce7f0e0b6f6cddd30019e8c341ec8c74a38c5ca72af4e74a53e2ae12394 501971404ba2e0ad5555c8570d8b672463ca97e0cd82db145e3b8e5f573f1328aeaa334f76df9b16 5829f36ab38d034330473954181515574cfbdd7cd36773dba38d4693a67bc7264bf75665e6171724 9b17f7e76181fda6c4fb566d80cec9d1149c8e0e87c657467f7e37cb712f521bf5cd59ee09497ef4 400b4d715eabc182df6a0e85bc0e723c73c3646e031306570dd809bbe70c8f95ba9b0ddbb8de8ecc d16f3f189562734ca7b969d2e7671ba7c1812e5171adee53e3c3e1535f808219b943c1e89b221fe7 84f90767dc2e493bb8756056a2050cbd898d6233e63186262ab26d5ae87b96d3f8a871928d92108e a688f11689eaa22ae4936d8f2f03078c1346efcf6a8ddd4785112b691d9d395e3b6346f55897e990 46405ff2fe8106d5f38d8af16686b201a146c1bb334226d240241df8ee92d8d38e876f036f0fddda 6298e2c20e89c582fec52977cac8dfd2fa7cc02e5be19d74e7c53eee08acd1b78d75d032b3909557 8eee2d1a3de2ec5accf6c0071f14b812275ce5367be83b081361ce90d1ca1781be88b1429b8d8245 c56ad9a16c0c5b52f05bd891c976f5190548477cbc87ef07581dba53031a0eb9123fcc55260eb1d8 231782a68106512cca0451783a0c513899f420d045ea8be3a6f250d60bdfe5163df44acdfd194a4c a5f71819db836dc728dcbd929c8eaf9f9e261251b4e6cb2b37620f1e9065a263ab4e5f2e10484343 6e40dd73364b218c33229d73a093b87199900094f586eebdb319e618ea442c4be384a0cf4989289a 586fc0dde7f4a0b2eadbf88ebd47f808f03eae13176f5d1caf8f87345e9710ea1707559dca41c591 39ef6184f48cbdf58613cd9c6216ee6f7b7a4559562484b95df9e01a6cd896086de92b633fa9bb11 96c9a77d6d93835211197a42eb930033246b384f2c6d4e2118c4b488e233980ff8c16135a8d60a21 be3b6077bcd1927358f88cdb98322786589bb899a8be1ced517dc554d01e7642d15eb748a1bd0a40 fe221dd53ba3a5e2d19fc1c69dc721eb88529fb22d6845982475e9abe16b571f01e7d79d1b91873d dd6fed7c72203dcf4372d60688557554234a630f1cf0c91a1b549d3b8d4b1940c41b7e5dc3d42664 63ed88f3d0b3e66cd1de75fb29408bdcfc3a808c29b90e3f82747133d50515c6c0c6b6ff3a3c8afd d75281fbafc99aecbf146ff88bf5b29a8cfcdb0a11e63412f2d3563260c7e864451aa5e0822806b1 6b8af96defcdb61fc9670b8f24aade62b0f1fa01be7f7877ecf8dee7b0ce2853478d63b587825312 47c66f9e4660d712e1249d0ac0d8329cf4df6c6ed1773bc3b09f35cd07b480ee25a8b0c22130602f 22c855c64bb0ecb359b06c673e75a0c0b28c126099ee0d7eb1da172eb2875da0d1acb2be8ebe1545 ac6c69cfe835bd3090e1c3a9c36f87548e862f97331150cb00bdcc8d298220fb3dec6c4a7718a8c1 b9fed0166ad0e2697c92c8217ab642a1e2e04681dc212f8215b9a7f7c4d360daabeb93a01bf6eee7 6e6bd1cf744ef4b2dde96e5a74fb2a5c666dabbe7fb5af7da9d3be36f678fb9a5f61bf5818de4299 2feb8832556ea7cf28304eba966c04c69957352b2146437dd6e15a0a5120bd7a7cc395ca64033f17 ea0c5cafb693dec8ca6ebbc73c7cedb659f6dd39979d52a7676e9bed1bf4eeb7ad4793683d30986b 4dcb86d642b7c75973dec87fd2479b83231f377cec5c6e50008fd55754d1ac334972adaf5adb4623 cf8068238f36915f78b8eaea3fc5c241bb6858435257f551f63192d1f282e07701daa627fe3b4ff0 73f786f657fce7220db1556cdeedd469b3f96e7a7a338b3ffd06b5ef1e1a05498ceb6c32cbd4cbce a952db0e72ed5acd6f21d583243355b9b9552b919e9d55b41ebb2f5ffcf3bb0c15897629de94c492 2d160fa5d8bc96cae08e833eb554416f08fe62cea42bb0a97a96ed71fa6f8fcde2c333955b551544 e6dcc2d8defdd122f35727877d1e14d35fe41adb6ea495dd160ea68d50217b52a57b180ba5c9606e 95d0eacb2fcee4ea6737aa386891d722f09c030572b02fe719eadcceadd7252cc7f3bc90ddddc249 7664e3fb4cf8dc0199364143c039d7d133ed3e7acd28a35b2913fa8dc62f1c293c38f61db46716c9 438ed65e363e1b2b52e6f2667f64f437335a8372003cc476e79ebc5f6d5a33d49dd7ab95a559e909 37a998191798acc47409c0b02b3c00d29c9162eb02a0d2d9a730e214491e00a74227c56d08800eff f918009ce55600b80e1200dc685d00dc523200ee982300faf9420a25fddb5807bf9898af8b371ed8 0b5f97b0dde43f64b46b9cbb8316a61511401a263d852885add931bbac5767c58f842cdf2ea851a0 7c42cc440a9d0ef260f1238f1106808cad0e4066d30520d7d801d0f61503d095cd037de0d601fa55 9e04fa60ce48e1ac81fe9078017db2d04d719680beb4de007d795600fa0ad4ff0a620641beb051b7 1c1802d59a2bf0bc640822bae568e7c8208390cad6bfe6b1776cebd7d69ca4b6b5fd93f6caf0961f e72b054202ae6d390dec6572401fdd8a3fc2b7bf2a28009c37be756e61b8ba0760c9ba03f0a25404 e078d203907a970110e830061052da0308d5cc028874eb03883c530064226f0164dacda488d23707 35ec0b93d59da50a86fa44e446a0f4ad24428c06c267231f99bdeac5de71123ebea9dbb5e65a5c94 f0e16892ab3b9402f4db0b01e84fdef25f0f3db5ff2420575ef33f5e551fac0064bd8e00e4456601 147db60154d586001a810680155edbaf526df90080b51910c0b09694e2b204305c8a018caf5553c0 cd2fb4cee53e1fd1da5463ad16ce7c8b6f23af8958ed1d1fb9578bb81e4fd5e8b958173d6832cbb6 714907e006617c03fb36daf5e8fc0debaf87465bde1640e970ff93e4fb47f7d237001be54a00b60b 1000afb2230007f3eedfc2c2dfe20db9418a8501e0301ca4d8dc52dc929fd4de5bf2ffd3a7af5992 8ea88714d7f47dc34139c5b29724642e7d338973296cf527abd7c9c7b3045bbdfdc79b2ba753fe4a 73ff2076dff2ed8fec4878dc170d3b732fa86e290e3a49272e5b4dfcb6edcf955b6d7a737e4def7f a4f17e0defff164af8d5a7ffe54ebfc9c8e7c94f8989c126c53afd4ec376fea7ce8493dbc18f3773 1d3e88cd9d7d64c5dce84e85b94faa6cdd8a8376771ab367c48dcb0617dc8498dbdd6ab679ba4a4f e77e890661eed25900f5b359e40667707d90be4ab55698fc4adee35bdb3a47b7eb4e8fc4d59c7cf1 579fbadd147223c505f9ab4f57e6df44692b4a9cecf5f548d77d853bb5bfd5e3a0f9eac6653df3b1 8337e1961f5c0f709eba369d0c7b89f09278d180b67abe906df30caeda93935deeb95122a2e99424 e48f47b73d4bc205782c1dd24b0bbc17b36df22b77b7115f12b69ddd99db5c461bf6173f1efa1be5 b092022fff69da9f1e6128df2ef078d3a5751c3436879b70595dafd27df3baa8af2077be1087ca29 a617f513bc5eb4a3843f4011b6df2247b719e0c7ac169061c1723e2b82fd1613f9dd61c8c9db8811 ad6d67e3781bf010856bbb8d6482c4b09babb71d202be26923abec4c86978b01ddffc5df108b9914 48e96fd8df2e40f009e67febdcc66525b62eea539e9e415f5c9ce040de1ddf233a0a179deee51040 f5fb5e78de939d9c09dfdb88da67b79d6053d898954d651d8f368d357cdc7703ecb247562ef4a056 d969555a526f66bae072bbd0dfb2f5cf8e875fdb92154fae0fea5e33ecd5dc48a9567f91245c3d9b c696039224a08bc9b3197792e4d9c113a75a18c66c3824cf26a09011962e9b42eaf492f73567ac6d b53c656dcc32e1aced3afc296f18240abc0ab033bc5db90416aeb20becbca48b58bc0874eab5e07a 5adedffa8bba5f2f44a027eb35d28d6e82ea76e5ea666e1567cfd964f0aa3acffda3eeccc6b73f18 50e71f24494417be5546be523adb6c272f9002d3572a1657c456ef6c6aa566b8909bcddd6115f436 66e8e0c19330999587d8d272a92fcc65a97a992daa54b2f447a7ecd66fcc9a474fad8257f72c810f d780c5ecfc16d81fcb31872b6e6bf6b865b1194661bc330fe6b693edf5c269415ae526ecb3d1b5b7 7ca9678b9557d7aeedce9d5fa46d6897931741f5be22f5cee297f6c5683199d3841d3dc222455db7 bd4efd53cc249833787bb93a0cb1458d1f325e44d1927bd144d38500d399db23c79f25472798e1d8 62efbcdf879333142fb193eb5780293dad17a645966b4eb8add9b777d085b147e9691987ec7e639d 2c2a63f5f2fb4fd69069a967c8ec7736a071bfcc7bbf489e7ed2f81ae97b59af812787d78b2137d4 9fbb4efa2bddc0a8b95ba54b586051874f75af6b85d0fcaede87b3d908101c6f9a539d7cb9654f19 b135fbeaee3bb4985451686def4b5c683704f1323e824e326edbcbac7566809ad53b7741730c7243 13cec69a3155d895febae4df3a31545b9abf9cf6358a5121ad5062c15f24afcd1c4ede6a63f82d66 72ef36435e1b94b77dbbf40cbc63395aae6968e3b70bd0dded5ba3f26c70e13b4e3eb4f049dab918 7b7f98497613591b6375b69b5897eac9b540e9b4346324b733e159ee68243c141bd81e7febefb1f5 293aa713c9baad67d7f58146f734590dc689a772f4e6a96c379d8652afb1b07c38607d59963bd02f 923796195ca3c7a11791f34a7b0732af72b0a83cdecb9a98bd78e73db09e4fc5f0ec50f3d7e7dea8 c9162d356d196cc3e36eb1475a50b5cd9908dc930d2718980650194c7477c7bb7aaeafacb4c563b2 d38aecf2a4aecbd7875ab14a3965d71fd495fa6a02cba1561164a5ebcca4934fc452af70a88eae6b 10f9882e8bafc1a37e35db1ff58104fa22794f7be4a5df89fa618d06ba1b20bbaf2cc56394f1aead f83677a6cf9d4397c8dda4ded25f63edb6aa9a77ceef1a4f6b8f19c0ea4cebb9ed51d018e8a1aaeb c9e39338a6565e8f89b2db563da5516f047278274359992857e95cf2de92be79554637a80f8ec6b9 353beaaf205b9c7692b3885ecdb230efc77d61703cfc8b072dbc86a7971ba33b73647d3a74c014fd aa7f7a1f722eca270f672986fb497dfc588d7b99dedd4471b860641a44535bbea9bebaa90b84b26f 53b4d2984ba2ac6655556e2fa69664b47c47ea9df6fe683cb86d4670b614898f03998858cbf80c36 c2ebf9e808f30d4f0984f0b478ff6e1e786a8a14f942c705b9d5650173ac69f73956d4a02f6ee180 c5226a79c0364f75d459eeabe39a371e1e0a335f8a926975b20bc77ab05998e8747fd6a95cfcf169 5ae992ad29fb65b62b1f3759443ac785a104226576349ed5462378006ae2748f8d454c6267c21b9e 0702e1ac0e4296cfc4fca20a6779eae6b7b880ce0f38ae68695cf98c6cd9ad99cbb3b570d84da17d 2ed2cc811942bfb8ea3e3b38965f01bef6b64c77a1526addc52641d16183edd33e9e9747f3e119be 4e41d441dd9cd54469ca7651eee86e5302fd001ac1c102139dee8112de56c40bc423fa4c3984ec34 d179ba5298f0855dc7e3024bd8725c7f7ee62a852cc06e23b6ced6bb2b940933038591fd67c0b4e8 65863e95ba1d5a9b23fd7f7019c3c2f0201f642c584164cf3722e9b315311f9616a5a9881fde6313 5c9e0c62252cd46d2b5929cd43e7269903283b429a4855c4bb485b70ad615fc8e56582a75999e682 adcc739c68cb5c053a58ac38b9ccd2a86a011376d888912ffe936979e5cf85803e8dc608ad37ab12 75d5660bcaea716f0abadd5b1474a8f7c9bb9c837e719e062cb9b3f82eba12985ecfb371a139a30d b73c91afa78c651fd617bdf0a6968a3c58f892e94427f1d9883efb372280e74a428ecc35f8229beb 71dcb688b03ba84eb0f5499d6642141119a524eb4c6be34ee993112f697dd809e95e4e7a50d7f052 a2fa189c06f1de0be4c4e33d12a5a0d7f099788de1f399aeae665af89129dfe317a72140505b745c 4497d2b90dbace8e6d396b22a8da3a1966cd41d1bbaabb3ab894ae303d139f23f9200cdf72cc3375 2fc7f16dafc28e88759b09173b886917438c3ead9f14ad1bf54fc225dd031195ba65b509d5a76d8f 7cb42e3b72726ede48746e17862fe2dd1dceeb636e3838f6e6c38c964b08bfa2d40972b78608d2f3 c05f44b445319bccea8e2e34a201b9802575a6bb78fbd9fa1edf1ad7bc41ce96b1a230cdd508ade4 6c81c42a6b6e7def5ed8dd0b7931c71a5f62dab2d2a0f593d5a36ebd09428d3321493ea89025a785 a744a2a7ae357ce9f47c381fb89b2151cb5e095f46b204856c3fab3522ff82e9c16a7e9b0ed8e1fa 3e283d7ad5141308dfa87cef17e18eadd1c1ea7dc27c336ec3f37c43ed4d8e74d0b01eab7b51677b eb87dc9b970331337c4eb80de8b96c631d864c3b061edf467b9672d498efd7c8c71e6d935893ec0f 5f89450ce78ec70cb3c05224fcddd32028a9ee1005980b06ac634783329f03f06d556ce2429c25b1 03628cb103aadc30e992a960d29604b1e6b0ddfde29370f939eef5c3815a6dc510f7d0621e9e95a6 1238e92097b6f9c69e654dd8ef9e5f19cdb3cc69ca1c4f3d9b06d3d340c19a75211de0080cddfcb1 3824a2529358741b20415d516c10900c35e0f2faa70b0cca2b4fc5c5ce6982d7aec50576b08621d6 cabb4f340a2a3554d3dc0172b9750ce41237ce8829fb25c4644a3dc44433dd5f6ca7ba4d2d5bca72 e00e6625d4a975d4be0d9ee2ae99038a1f41accad20d10df8bce9e133b399706af0f83c42b99c570 d8ef1d099a251f83e062e40615685ec5b7f77d1bafa3e73e16964b04d612ab0c7aaaa312aa852313 b9a29e8b98afe70ebe8b500c4fea5e09468eddcfd5b33feb044a7fd6f5c33ebe87f37ddc9fb753f8 9d3e3e9eb6bf582f609af2afe7e5705ec894f09f24ee672f068da2d76a2a3a52ca0bcb167c645b6e 6541cef26793284e041bdf09f3355e8f1f9fbc4eacf57c3dd1d3062ca0bd1a5643ae07a98bf45b2a 0cdf9fab218c0ef65cff95cda8fdd9a23fed675a4a0079a7d305ca838d2cb8caae41b0c4e0a3dea6 dcdcf6d6972dd05b5fe1666fbd835bbf483f66b5ab40b487c613d2d95225c2be4253dc02e22caced f3adb66c3ddb25a1444f6ff4adde0a8679ec6663ca3e52901bfe9ec30fa996ce0b13ead27f39e357 9fa804857e6617d721aa9174a07c544340b6db197ecd2d20893d819c1abd5afee67625ad77e836bb b357471d979a9d0ebc66dbe7a7e6b7cf67e2d53e6f0ef5f6797e68fc6271ceb7d8f9a268d1d3b0d9 a7c78fdc9e34f38b0eae1e8f4370f4ae363e2334b73ff90f0a63badb81603e1c641cb832e4bf8b32 c8de8773b03c71b63db17438770f429c74e57a25d7892650b5a3a15ca7d379d948db2416641bcabe a596ad807633394baba6633c6e8df79d2a37dc55e993ced31862e1b431ec2eee0d97472a0d7780d7 7ee13dde157ec6cd96fc440f18de9a4f1eacbe45085201df785f2c1c1f15e6b29f3e873451df63dd f666d61f8e4cb51bb674b66d15faa3d65d9b7c7c5a0b05c2557346de0fcd41a17e6b785cf7d5202b 7cb191bb7acd3ad30ffbf56252a16a3c8ecbd5ddce71aa23a97ca884910654dabd32583edd1e7aa5 5d7b9e2beddc285f51a059391d5badd2176e2e57f9e4dc4e8f99501a27003f32e94e49d45a039092 000680b870e857a8194e3d07f5e8ba879f87f11cac7644bd6ddd78be41de55a2568d31b6da780a5a e5b8decc2a9dea79553e1f4a61196cb56fa5db53ca94e0c1f4736e8a49e6d62b6274635878b7266a 21db3bb9f902d23de5d8d9ae9c2b13369d2b43c632b7da0d1eb974c4c8e45696007c31abb65bea04 a4ee9ae57a33cda8b80d59beaf6252e025ab473f6acd22b169c79fa113ed1f951d54bac1f30e88d6 3f1eba27540f024596ec9108e78bc80ece5506773a2b2e8a52b64ee3e38cbce6dc4c8b77b780b68f 2e40d786b229dc26d09d7488141b0de84e9900e83af5778ae7574276f5780c740d3f02ba62e699a2 93fd62aace87d6f7598b26d3df596a8f9f8ae2aa8de05f193d5c79760e0379e8da2f28994de7626e 9de6900af56ad83e7f3c7496ccad1757083837db9fc92dd0734824c58e4b7fc02d3d853c037aa738 fd3d9fe16b8a450ee8bdc14e8a150980006aa4b86f0030330300302bc20058a74c00ccf7cf2936b9 14afca17f6bda63996b71e7e566b9a9aebebbf32fa5b361aefc4f1bb4f8b7ad4ed6ad8b24966a149 5551db4a095db37cae1ab6300084f98f349ea51fe3af51003cd459008c6d0380b2c91c48a72c5b00 8236370022bfc94900641d7b003426d3ceb500ec14f61e8096780e808e35148056371380a2f51180 4ea3628a75f38b1f19bd57c3898c5186c44b718ba05c61dfc13517cfc3392d17777b85f9b69927cc 4fca48b57d54cce27c3d95b2d25d24012873c70148e8a6afe6431a80ce320bf44b0e0ff4dbb10af4 316206f4d5fd16e807680cc0855b098069bd0fc04b4804e0f03207e0c7f802c0095c0190627e0820 adeb4fce6ddb390348072aa608c02f8c8d8fb80a6c6474416c2f58da590de081928f6b300973afb4 cf54a366fe1caf2a97fc7a52580ce64a469b98cc4f603f317d5def37f19894acaf52f5fcc91faffa e37c076b00d1ef7700d98c4a00f2ce41008a0502806ac21c40678d0b80ae6f55003d7b2480be391b c02acd638a4506c0aa85da172ab81cd9224736478c7ddd7d0a6610a31ed54632bd53ae7b36f86bb3 8075b6151306e6f97566a30316eaa6b131d1e84f6c2363fc7f728e7f537b7f5d6f588900ac6ebf00 6c34fc76686c5fa001bc7a9c0038a81f011cea1753dcd1bf657a3bcb14f3738ad7eb8bbffaf46322 7f1cead79d09a3ff2a3210de7f8b0cfca4f6ded1bfe56fbf59bdae9e38f9ad9d60abcbfcf16693c5 c3ad64d78facf8dedd178ddae94e1db1c7bda0a69d89bdb8b5b86c65e19bf010c45b6d7a9c5c65a0 14fc23773fd5127e0be17ef05ffad4e9fcd5a7df8a131b2b8db2b74a313bffad3331aa27d8d2ea3d dcd20cfda4c00cef8bda8ebd17e49d1807adab1ab3a7e767257ddbf6eef64d8881d9f580009fca08 aded251a40d145cb68cff3855a97cee0ba029decaacc45891418ffcadd6f7984457f38fee26f745f 31f975925be8a7c4c4376b7aacfed49970b2cce1e11647b7fba26a0071d0348a71599bd66fc2d5ef 5e0f7dbf7f6d4e7deca2bee7c4f9329c526733e773a798998b2778e3ab91535b59c7b7bc9f1d89d3 2308a9381f7db3549fede7bee60bf56dc48dd379f56ecf6cccc69cd98047835ec7aa407d9367bff8 2b772b1f31d94dcf2f6eb77f0dfa8308303d0e1aa073db7688e07a80d8e3457d61dfe77898593839 d9452813253c5438bea57af94844955ab8e8169b8700bab7f75b74d7dd1d8865ff6faaec92da98d5 505cdbcdbb19247acb5fbdc7da654524d7fc7231a8b79605bf052eb8fcb3fbc5270daebc3a75bef8 1be5479ffebca2abdf1ef1789397e1ad66e4861735790a67d07bea51c2dea747b71e7a61c198acf6 c273b0de35fdea76db0932878d59c99cd6b1f8baaee1f071ff6610b71fafd5db48b22b227e97960b b4d05816e6cd34982c41f8625112bd037f9c7acdc3e0e86a2d1f985ff47d696ef6fce21cbc8d0bbf f81be22f5af9afeefff6b4965e3fc5c355e3f8160cf010f4606c27bde3e1e6c22c98b55db3c5c069 69cacaed89c62a3b16ed259588b3458073dea2ecd14b5fccd31bbfb692434f2e6b173712edc4ed84 cbdcdc6c5febb3d86c7de6d033f82ef38e8395bde9db658fd3616e9a4cb3e138992c26c67fe03fa3 74a0dc17776a83a7e735bb8ac24593b8ef9a73f7b901d74a3ec022b4baca5abdf622c0aa9f0eed6f c506ee1d2655d26b957abcab197dc9ed82a43e3717bc3dbbb746b3997d9a2c9de760b9736699ebd9 01e4ca6b3a7c3295c9925ff727c5439db779fab218efdbc67ddcd84479eb681cf2960a2e72563bb6 3fe6f67bfc89f263a41f1e0015cee6b111460082ad0e818a4fb75ad5738364a76c978bb77ef377cc 38eb29c2aae6f6ec0734ef9ff383d9946830ceebd0131da2d553a6fe0c35a7d48099a6bd63f4b1ea 9332ac07f626f10e768d3bddc6876a35336e8e99baa515d6a879e1eb2313cac46bc396d9444f9e8b 928ef366f10740552c7c91b6a9584c9ee34bfdd23ba1c09172f9fbf6d24936ebe91cf8cc05563e50 321742b9bff63ae8e03ab7b352d679cfd49a93dd8fbbd34232452695ca7c688bd68a1d87f9fd682c af6e9a75eabcc696762dcdcc2bd95898567eb033eeeae86c206fefa53fbd4c451f34c8bee64dc3cf a250cb03e24265c685bbb24eb8b2c23b7849a9e2dde22f92e721dbbc3536f7e2a724637ed7dddfe2 c0630efb6525b8fa7eab7d345ca8b8f167f8f8114d994ce635a924b5d238dcd59ad6e9d1fc580eab c762b8695d28ca78401467a0cbcf53b51843d707e7a9ad678c8dab51d973a0e597a5a3cab6d187b2 991b0545205e3d791ff2ac2ca9c599747c0737a9336a95469746ab32328ec5f2c85801a52fbe29f1 04d5bb40b9622dac02a5c27a8e6f1e4b818822ef6c9d5673d45e8d1d12eed8137ea7eec747d57f58 c630c899703ba81a1871e8e8847ce86bfe29c2b5c220a6d4209be5d5b25c96956dab6328b519f699 72c88781b4909bfbe94e8aa4f355ea3c9bd9d1653d6d8fa05a8614e3d8b34584e12e82b3090b022e 5c2b024e846501c82e4b5fc40269364ef80b686c6f9b797e15f4fda7afd1d165eeb0e1c6a1d6fbcf d6f7a4fabe18e30e5b0b4c3b075ff4f71c7bebd90357528319dd502b55b9a7889286c887481bc82d dca625cdb379a94b06cae8da88ad11a4be67e21d87d722b2934ec273b47f0b78d2ac0b40b022788f 1f7c2e6b7c2e2e46dcd29ee439a6e555b8e2c92affe2d63c3aede3aafa6c6db0835158d6d9f9dbbd eb87db2c67ee7613d15fcdadcbd2568de4e07afa508f4275dd8c1fca6e90cbc9a154a94a27adf859 e24add6b051c59640b15efab16214e5884119e97a128cc4c5913323967c27bcb68c1936a33e4966f 21e11837aa70a5118bb1fce3a5b155671732fbc120cb8c6ea3ca0f1a63b2fcc5e58a2d7a9fcbda3e 5ab782c54928faa7ad9b99cf6e87fb74933b8576bbb9f74de46cea7a4eefda6a25a13772b8932f92 3ed55ea3ffa1eb3ddb1655c26ddddf52e68892a3640441501441514c60cee9ff1fb4e7dc3df75afb 7cb9edcbb6dfae17817a9ec1a851837854301ef571d560462b581fbf1342cff009d316b747a19db7 5eaab6802ba6a6bc856f39a8ae752754f55a1cabd57be3a6988a53541ae52c29efcf8bae6c0fba89 8c5025209d9e6255ea3b70f92f4e2337c09373a7d358b431bd3cbd0a51ee27468f36f3d771707536 f37e06777fe27d953cf6adeea538ef5cd5e2ce78b8e85d7fcf998c9e5993c5b6848b756de1298856 7a2984ba1e7759556f4f2565b359e98ae9819ed2a0d1403e149d956c1f6f17e9ccd379c9cdc6b884 ef4c53bcd9e4d79c247ae5e42d5249b5f283f0141ea51f8e937a48c6b4ca34e69d995a091fbb301f 2c0be7b77760c1d9f593c5c251696360ededb1ddb9cee7a1c1f249a2f3d6fdd2967af797a60eaf59 352e66be8bfdd45a3b0f29e6268f280d132265fb49b66464d951a5b336b624177d8d2422432fc4db cc3b8a74e395119e2f07158249431738febd1440aef7e4c3d1a9fc0fba9bd20f0749f1a8357726bf 675a6437a4eae4a344c5f4867ff80ccfebd7a51f8aeb95ad1f2dcfc48d56c760278adf8e9aca425b b606073596063765eb8c3e4ae3322bc80e9976dc677f0649ae7ac525a29267c5db8594456fda3585 577efc3d058460ff9a099c43ef04c025809f563998cf9907b5356f4ea2d6bcc4dc5bf27a516ec993 49e92ff6e5be41adf2b96a73d60ff8da247b0a4b7ebd0db283bb006ebde8b189bbcdaa36320226ff bd43b723f3d1d32a99fc54ad37e0446972cc594645e629b90b3e23decb5251f4cefdbaf022868830 ce6d68819b3f0401d8b0ce4f91769f173eabb03517c1a6a540c6ab557ce61adc2a0864aeddd2a75c 355bfaee1bc5269e5e64134bfd0fb6964553cbd20842a65e85ad8fe5c9b2326a5e5eb901532f3e9c 95bddf981749f075a1f736b48ae65b8af5580472af7c5d4983fa632f8ee0c7557885a58f3016f06f ef2980395ee1d33917e6457848b6e69359aba5f027b555aac30eb7daa513509559c56cf2a9dfd94e 3cafb150871398eefee333ddc3e6c274575481815b8dff2019540139dfac3348f802faf714f82751 64962bb8ad79e5e554d5f7be737fcae376f1fa3695aec8ead2259686e268a4cf847769b21132b5c9 8917bd4d5a73d3cf6c4b197f4aad928e34b8758dc7b9aaa7b3ec86f665d62c3ebacc5eff6d1ac4d8 7877c5c0b7cb95ee334c89c64b2f8ebac6d1901a8aea911a92e76c8a4f811a42b7fc0feb777e4746 073e8f85a0a3c27e275ad68743aa58ea4b4cfd63372aa5933141b98956f54ab674659edf454bc25b bed9fc8c2f8ff97c842f5ba523bbe3d63dfbc6d532fe87ddcce202db286cabccfe984518042b53f4 e94a09747f969e6ab7c2c2a58647684e3eb1f6910cb29902c946239af828429f987c0a3b627276bf eb3dd3d728451ce47e5849e5393573876f7c3cdfabe868ef9d9b03ffd9acf6968c98eb22097cd523 60456a43265dd18fb35a6b7195352e2e4443768b1e666c63f08c19a7503bd1670d79d06e45f83e5b a36e03a34479a40751d423c6c9803d732457696844d8e11d422017211e05f91d2e73fd0cb6dc5471 4c337716a6c9d106d318e48d693097fd8b454c4c9929fd7c93c15ad571ef4c7fbe1d811b0634e4c4 ef4ed12206e2f3673b5650baeb0b025136588b3df2f479f7326942e57cca8b8d39f922bc0dc98dc2 130998c39398968b392267d42bf8bccec178b12b93d88a8924ac32399b682757fe9e02687d378c91 2efc7e22cdf3b0091f072d1d3e9ae80a3e4acb478a57063e5217f043742cf5d830d3ce33be79b6a9 a137fee0fd3927c3f66e3dae9a8cd8075a32bd6da5fb6a3069adba358bbe84c7af724bb66aa244e4 5edd2eaeb4573eb6da1c23aced55365895a6cf68672c3c51a83dc821ddcdb882c0e611817b47f0dd 0493569bc38cd76f3ca45bd4f08bea057af73e5568529bc9d0a43e9d43e3473d7d8fb4bece14e80d 69ef1f6624de6d8dd561861b39e18c1b8cfb38d35b575cbcdbc7e75027b3e867551bfa1cc4ccb01d 72d0bae150ccfd22633abaa0105b9dc9f0e9fce8c23859f29ad707396d526a3b6e3ccba3af57bde1 c7c903fad44f1968f280aa509665d0ba14765bf58270ecd4d43d11546324d856f50b9dafa4cd3c57 d936aee36a4d1e1cab353a7954756bf707214f77c5a0de80448f24f6a23be355ded95c96dffec6a2 bc79532fbb6a4e1e7e0e47beec8833fa267d7a446192a8487a97671a5cf380d76529e6ebc566d9a8 693da457abb4ccb40e98f5e7d5bab4d8547685c7a5d26c973e65e7ca96cbe8d0c74aeef32a16ef2d ec1bd054a437f379e16509cf0277ca6105a03fdd42709e258557257ff98b3f9b6cf61c521dbc776f b5afdefab28de623b693cb280df5f02e6745a1eb1dd9230447a4308d7ae84e9eb69bf467f8dd43b2 b69ab968f9784fa0e2e836268a8c77110a9352512fb4d6742f2f6ee9516ed1d4a25ca9e76eb26bec 70cb56bd4a3ed37909cd0c34dea633421671001c6d56009efb59002f063c8003610460275aa6b8fd f23abfaffe6e33353c0aed74dc48ad1abdbaec6be6cb90997607cd43d26bd9ffb43a727d4fbdb38b 29b6295bfde64362b4ba5aa2b80a826a78e18377eb59fd31290364a73652ac6980ec4b720ae56bbb 06c8231901e4d95ca6704f0079814c0aab099077414c317101f2692500b994d20f5fee2d805ccf63 806cd94d8ad9fd87114e1dedc1e73275faed72cbee928db2acafae2b422127ea57eee6d7cce7493f 16bd0dbe9ee11378b8064e7d493cb40af2bc718570fdc2b2f5cea30ad0b35d02e867df0418c05880 55465a8a8b0d308c1ba708d700c30b1780f16e1e6042014de12b0013b19fe112132f3b8099f312c0 6447049822a5ff48cd9f5298991ffe88d1f3c16568f7568b4e273f3278b52f561161918479e6bb1b 17a1be4e2bf8b6f3fcfaf2225a153452958288084ca63b63fee8d0cb5213e00506057843c4004e8d 5980cbc5af0b5970006e2dc7001f356280ef2637803f1a4540e4d764da08b7f414f731209ae11910 b0560304822b8040f3618add1d101853fae1dbad7df568672f887d9347cbda5f319a75e9e8452c77 ad1d7c179ed3faca49dcf220f2f57c29d45aa06f7670809b0312e0d7230b881a680182af0bffaabe 9a0a88f5b20b880f35f93988cb9318901c7c0764b02903aa39e600c5d036a094cf1250f6e20d2847 4701d5c33a809ae41629a227a042b9f9c3ffaf184d16ddf9192151f5ab0ed6d7c5ba5fa6b28f6e4e 0f03e99fb1fd4cd17d5afaaff7f8abf9928ff41af963f2fd8aabd474330674263d0f69ecfe0274bf 5703f499680106f9f400c3cdd68069ebbfb404a68f118009810598385e0226e1ef29ce951fbac353 b5afc7caf21f31da7a1caa54eec9bd90c173b3adaf3d6bfacb0fc96ec383f6df817db55e725fb3ff 87b5f77f464c7ca37bfffa7be7e805b0b95705b062c00076a3db209d2c578023f220c50aff27a697 2a84ff6e2157003ffc94b23fbecedfe2fd1fbe72ef6f97b3d666f86fc8c0fcf27c0ad5ecbf31bd3b f2dfecdbdf966c6ef7c92e9cdee3a37ac347580e83071fc7b347d6382cefd2ee90dc0b76f66bedbd 2d11fc752bbb5af11a134be45af31bf2c5fcd8fdf341d8477f45deffd72e67ffcaa75aed5f0d7589 a74344e4ff1132f0c7dafb8be96d951f1fa5d578f06b05bf47b536732f586dfeb66cf6945bb9d76d 5f636c605ef5ebd8b96ca9f1e0623e57c1f9c025b33332bdc4a7b3543f9ff0a5088e54d2ab1d8246 8edf7f1cc1fa89bbdf2cd59fb4fbcdb9dd2ed95c7fabbed346f98b7f07f853f8eab97fe4d39f65fa cf46778fe0e7927df02bf2782f98d8e35676f0cc55bfb0a58bf9c0bf890878f38c8408fa9354730d e278536af4d12be1fce1a923d281dd22da3e6ce2e63edb63fbbbc2900f360640a2c4ca4bfbd829c7 b9b50b65e1d5cd665b2bea8c0bbf5084ff1586f0af76fae714f8b9657ff2e9817af08ba5702f18b1 718de1ad7bd9e2b7f07ca09fab939b396c8f54da731fd824b8eef9bdf3d8a51ddd7babde19b0d1df 5036b1b2f9427c9033e5185965ea6bb79a81571ef4267e2e55a7ca2f3e03b8bde01f6d771eb1c922 5a8acd7b545ef4cab3585dc1b35adc6b4cb7461bfa83af73f67fcba768e1c94ee5c6553f0e9a67fb a990277cda160f41b9f56da3f6fc8eb4b7eaf5ed2616f0d3926da1fb6b3ce6272bafce47cb67975d fe6395c5f1ed22ebe1c7b9f4c2afd1b2853fa3f28ccbce8c8256995a150b090f9d652b440e0573e2 a2fe7c7c1b3c4e638f62bfc56df00c1a9920e00ae02ffef75073e01a372ef7131e92effd47cf16b6 4b34aac607a109adf1750e5b3ecd3bbd08d11d3f8fc8951c2dd9b11695c3716766e4c6df9263ba55 46fd69633d1886077d3a0e91ed783a719bd3e5f8d65b6dc7d4f57809021a7cfccf98acfa4266448f 22b9628de4621879cbf6f3e069d5cfd92b6fcea7616cc6c71ffec7107f26f2436f7ff4b2d46c1741 d13c694ceac9eaa61d8f8b8f3dbecfa5bb0d6631cf17a65b99af858736d54c2b63049be007841a7b 68831d53174a0c02125702f6c11b7ec8f25d3f1bf2fd5124d8c1a830f7bf79369e56daa455b4917f 0e6b3bb53ab0d4bce01e3ed3a18b762aebfe852476fd411eddf5894565fbc3df515e1a836a72609f 9f706364c7f6fa2c6edacba0eef7e6d24df267460689d23b6af9bb9a68e28a8df398669157c0b95c de9fe6b4aa9feb5acd917cb2306fc5bbb497f68dfc30b12279d8a1cfc660b7fa3883ae8ef8ee29ab 2f5cccdb9cfac3522dd3274f33b4e70b8cee4c469799932d73277b7696bf59aab6e4d217bb40a427 e1177fbe6ea3303d1e19043a6ca0fd73b962ababd17c514fdbbf8ae90ad3a6a87526c427f08349ee 93f8e2a77c1995eeb5b7572ba185a1d9e76a8343568607882ce36eff687cf3a15d7cec097d2fe329 bda719757a41e3d0733ec1c777842ab270b21bf360cbd4e6d35daea86657eb3d54cb887aa1b98373 27d33a556e667352fc0f9e015acc9cb15ef1b52b774b97d52bb7fdae58996bc17c323d9c16bdc98d 0b95e01d1755bfa0c89e17ebe16ad82c8687018ac50f97302fd93eeda41dce18dceb3d6002c4110e 8070724193b315d014ed6222b4bb6d48b6acc4778756473dcccc5d52f8569d667760be3aa7fc13ea 609a2a18832b1418e42c39ea0fe9f5d0fdc2f1ae33cbd5ed875b45975f878f9407f1c5dc5c174a3f dccce0c12a9a8cb6eb6120424b635469cec5a1752bbaee658ccefaa3a1f0fd6f7a2d4db838e2407b dbcaa29debae915ea55b757b90b521c6a805cda7a46997a6ac099f2e72a78fdff50e9e43fbc6b0ab 4d0c0a8e62dd0fb3379d35bc6a7bb22fb7dabcfff0b508283b4d4a564fad200ebe8f897eaf3f5c6c 3c29ec8b5a3eb79a54fc5bb4b9c58770f838ae82d971ed8f7476db1d9cb091d07fa003abd77aac02 277fbeacbaebf9fd606d4eaf9bd510aac044947cbed33f552b1dbc8f350c2f4b7e2702838a145a0f 8a4a4b678f63ad1db6926e5bc8e6465a64714b4da697177539a917554d8f19b5424c3cc5f0a1ad52 67bb4fa58eb41ff2f6dcbaff70c6a36379d31dcdb2cb02397cccd0e2f23401e5ddf714f0b5f6351c 3ac4d2e93fa991eccc1625d52e1be9e1dd2addc874f06ed271e5c1c9b81de74ffd799d009da32679 1d14f6d5b6a0ed1bed5ce591b6efc33ca315294c52b5a568a815673a50120c444a7d267c9b427967 5ef272977628e938c1fb528f3f251296639e3f889751f5fec33138eeaaf19037b3f3d81fbec27bda da8f7317b01bd5afef6840ccb76e4fd0066a37eeaf45f3703cd91d779b1f1bde0bfe9683fab84aee dbd3067c6ee702e6a92980cfa82b912fa86dc8ae29896d37950e322194fae7cacbdd4ea92dc350ab 271d1ff3a98469b99d7845071971706de22239fbd8825f1cae85d13b7f179849e6f1c357b3617a8f fb41acae6a6bd653739175b6df937726be05a54eeee0c15e69d97f97b643a77819eb56d3b35b1d62 3334f5b1e60fdbd3c17aa6cdfdf55a5dc59bbd5a25ae57a5337abce4ddfb9991bb61a128c306fe35 5c4abd1d8548d8c866c4abb251c461b9640b0fb71508be906c0416e23efcc4de213ccf791d3e73a5 1629f6b7d66cb87fb466e6fafec3be24a5c57cf6c8e7673dc30593d6f5f6f46bf9e2698837ab5f81 b89f991c46dd0d39ee74066396d55b5a436be745beaf956c73a2565d73a140233d91ed52ff289df4 e14dc2b6c39738a4c3ac48beb665e1b1ce3605bf4f51028b9b22ffc9ee2d9eef96467c16eeac5bb3 0ff82e286b158c41935b4284c6a987c29c5397fd4b8ad9832b73f6ed87ed261841cb625b2a4eaf44 901d4bd2f9356ad8b9f3805a4149afc03fc7966d0f4c83b9e25c3b1f1e0455879f96d2c87e863252 ca7e9d2952bfdf5889c374fa131eafda51605bb52bffa9f2e9a1baba459e1f46753e2b3df156d4c4 f99674b40d6e9996be69a5892db9f2767e67e39108b1b5f74b616b9fc38cd90ea8530afdce6c3bd2 ed876fd4f1996cd517952b5f0a1f732f172c1bcf8fe7a8cd9b1b8c999d53be7d42d32d8fba7a96c2 79551fb99c7ca88586d43f063dd1cb2c03e1296d23813d3c133e443f073e9b76baad4842de2db9c0 e5b8a56354bf2bf2353440b80ab8b16cbca1daac610e5d66fb7ccc196bac5c98a69eafd2ce762cd2 cece0d6954f91c6854456e34cad6ae3fc43402d5e790c496271fd8cdfbf10a6486178478f4a7e9a5 621bbb5af45daee08563a75d3850a27ce8139484bf6845786e842eff791a1e2f70eeb4154dbd65ab 08455b4eb39727ae829c1f6c022a59d69812656607c94dc6da7b14d3f49f0addabe00e8d26fe94ba 0ca113453c27dfde93bc8f5b2d72c43703728447bb14cf2b39aa9d2f3fac8190a947705adbff319b d77bc5dce01ed3af7e3ea39c7e9e6383bb0f7a6a8252b2e46e8b84c00d128e179639a32527953ea7 3d9ac14fee66d119db291b09b333ac03d325a23b7d1c456fbac75c0a3456ae43d4c020708a243c91 7ce43e5d9251a931f146367ba23560f344867c334486ba8ff0e951dee0d395772632c5e8f2c3723e cf34be75da0da56a7f7473b85a29fcec3bbdd2ac7bb5fa256afd35860f5488eba77fb09a746b6eb7 49ae9aebca6ca7bbea32ddc96e489fb29709dddbd557d4156e6ea9c1893b930f5e7b927e76902599 28a9fe94dbe60b255a27a285cf78c7c0a5ec6d842dba7c82a9f0338395ce3e89ae07dd01ba368b31 ba96b8d35f2cf452060e9f090bfd493ec166f592cb139dac53a37a77f35632b73fcff177b9025259 58c244d67876235451faa467381abb916d8a2a8a0ee9f7ba2392c5a63362329caf085e7a1df0d90a 5c71a9ddfce005ac5dc2d461af81ae9f730ad5571515d956e53e625e9f2bf840ab4fd87ed7bed31a 6c7faa3dd83ecf57b09d80236c87f7c30fd1a17a452653886dfa9ddbb9311cce845a5fba0f0ab625 ed3e9de76972d462bb3e95f1cbb5c74b474b6290511e25d94042093ef1053c22f7065e5866be8bfd 30ad52f2b17282cdd078d049d0daa37b40b6fef48e34cba72c7c484a55188548ace9fa3da149b02f bbe14db96983166f697172f01a50b0ea5b50308122e8791aecff624650276cacf8d2f7b719d9de07 1904a6d3ecad72c76ab7d7cce43a60b2bfaa3b965bfc6cc75c527554ea0ec3245e14b3105ad7cf04 62dd6b227c545803ee95cc5ef3a2f747cd01be891a776fbf6e8c98fcb141bfc92734d6f5fc57b905 db49a32e364f743df76ee935859ffbd5d51c4da760e55eae56cb07adda46fb93eaaa57da5657edcc e687f49abe9241adac131e01d70857787aa893ec738d9fe7d8c81b99a7d28be5b59047f6df2472e6 5430da64a656a5518839d41b0fac5285260514875a4b83afcf504fab8b97b85b5b9087414d2dd4a6 d5b586acaa7a453d54369de1b3023d0ec5f23e40d032026cb1d417afbde2f5d05e15873d385ba46a c5afdc5d1cf207af7875a575f16ab0ab1f26f9e4c1fcb6851cd26f96e92b870365ef173466b22db8 d2ae35374fc91f1131b7d94d3cca9fd23ab63e665838ed909bf5d96355a86c19ac5876b264b38c6c 7b5f81b8e4364742093feef4e2ad0f9c229d6dfa8567a42df29f95b5cb6737de3d27378e85ecb2d7 44b3958c276762afe6656aafd93e3dde762d533bb7b58c51015e26d6b8e90f81d1c8f3de40d80883 ccc4fefe363d7d9ce5baf899208cc2e254565c4aba0b85cd65cdb8bcea11325b311067706d41e131 42aa463c2f9788cd06e414f8faca566ecf62a6c314a04cfd4d11a0cb4b2d0067fd768a771fc03971 9a22d9fd94db3cf14e9140002ec802808b5937c56607e0d2ac0a9a9f81069a97a2974298a53016a3 63802943369f51dd427fad3a4dac219aa079c4342b3d2412e8646f5c73da5e92d356768876e9f9b7 936e8c5d9bab55290b2d0dde9d4a6e21b63ec096cd1b409a71162070034a619229d6024090920110 b63b4c71990384134e2976e9e75a0292e2a90084f77d8008ad234044b80e10aaa8ff04626a190284 2e27e9cfad6d7f7b2d0eb27bc6ec25b5ab618d90b0d54e02ac2933d436dbea78f4917af38f39d6f9 386ee3bd84b55aad56604bc37d0ec9a9974d1138420100942867002ab0c514c3c6cf41dcc9302918 39c5c802e8108c003a335629ce17806e8df4a3bb2c9962a803744f86003d805b8a1d0cd063d801e8 c95c0274bd3fa578df7e62b45b5cf47addcb55d20c6592fb9e69cab034acf17ae9f9a47dbbbbc16b 5463d20c323bbb66c013a5e40d1c3a576993104063bd0830645105987aaa03cc7e40000b4a30c096 0a9d22510176acdb007b3901c0e16c0c70c6be035cfdad8d02b8bd6001eeb4ba00ef8105c0c7f11b e013170778acdb004fa01548a7aa578a477690bb02cf3e35af5647bcbbc23f62f4655960bc4cf382 1b557fd10c26c4b056efbe8de26bbae5b39d5e0ffde387ce3400f6c410804b5de21feff12661fe2b fce2a80c08deb500e1d6268088fd0d202bd80b90dc090264bb2b00b28f0e0039b9a57f112f0b80bc f43940a5a535a06af5df094dd5a6005044aed283c28e6b4e1ca4aded9f2bfa8f184d14c3175161e0 6df3d58d26b58ed8768aef16ae66f6e31df5cfc0be63fa339c9fa4ba4ecbfd3ffede7fc3267e3af4 4f5ffde52774d613406dda0740d7f21940eb7304d067b70d18404d0053cd5c00836dea80c17b3260 04619ce273044cc7cda558962d7f36b1da5b9c16fe8ad1c44a987e9792c0a301b2a8752ec9b038e9 994606e9bdd8ff8eede787fe2bf7fe24e8afe7f83ffedeffb1571b9d6c0033925f80cda33060cdac fac7da9b766eec6d74015cbd03018e249414cf007094bdfbc7d04b3772ff1a648dfabf1aea6fe5fe 4f3efdedc606cfffdd8d0d7c5228d51453f43f5b87a1f2935da0edc747a5cd4758169c071f6b8347 d670827b54f7a6f742d75bdc96f07273534fcfcb9fadce6a23b97631df11fd678b33273709ff23ee fef639bbe96af48bc2fd265cfe937cfb934f7fc6d39f7cfa157e5b69adff276480cf4cfecd99383e 9e41ae9a7f84a56af51ed5f28d7bc12a6137f558a26ee51edcbaea5748bcd686987ad9528c716904 4cf7b7df1aa7b86764aa8c4e6ebe333dded47172a4e2e3f5c0eef3b97d880cc85d441cb59fb8bb89 f9b9fd9fecdbff1380fb7f594f7ff80dfbe73e96bef7b4273bf3070f7eb998dea3ca233d2cfbd3e9 aa9f4f8f8b793f807f4311e2f2098f96b5a3578c1a87673b440eec26c0f76123a0f7592768eda44b 206dcbbea1265b4136e383620dd7673d9caff15df9ab40ada893555906f8965c7cbc2dbb08e988f9 9b8cf0c3ffd37dca4e1df42ec502732b7765e5b2c565eb7ca08de1c9cdc89323b5a4e7fb4fa7bede 45f0eb9ba8bc5de2c7fda636ee1d122bd33dc74e41bcaecf5aebb1c613e6bdf22036bb7cda6c71c9 9ea5fa82bf4be83c62fa5cb4e467eda83c2f8d664669b49f6ef537f84f0e8203672a2172bc967ff8 a343ff2ff9f456b62aeff3817ca7872abcc287a0b4a1f6596bc26f628a92e283f0d1d66ee9d05979 b5d85e06cdb0bff8f482e1223b74fcb9f4b426d192336691069ce52c169de417cab070f653abec5e c28311be4364772c4d5ca4888c3d821382e768eb06ec875afb9f7076f74361f1fac72f3bb71f3ffc af219aabe3e9ccec93fda7ad9fb665f7f96d0a6327ebbc56b776fb6b94252b8bec006ece0b3e84cf 62fe4dcd6ad19b9b5ac5b7181eda0f254436d7f6c46d9c3a13fcf0b0c71efa7083e7e0ed07ec3333 f543aebaf6b353f23892f39db7b75437352fad67bf0f8b870694f487b5033a1f58a8b01d342ec4e6 07f730a827ff970e3d3e9cf08939df673b9a171f5a627f4525a5f1823f5d17d192de6c6746667d99 369693e7e4dc7133e39b6d15c6d4d9face3741405890ff195988cfbf0ddccf4e1c7a24672dce5bca 96e869c5a03d8cdba135ac6de2c1c06adc42f7d0ab252e72d51f7d973ad77ab7b129f5bcd6db759e 113c7502b914fe0157784ffed5a17fc7f278e3a568bb44407f9d36a9ca92ddde5ad192f2d4e956d4 ac108985e1f8d6e5c220c089a51fd2c47614b5d093b794ea37afbcacbf8646a50a86b504ce0f2c08 2ebb071fabbb8eaaa0fd73dcfe2ec7ee13c440e8dd7347a347aba0ef04676ee6006179b2853d51ec ced927df2d5efaaeb54ade91d5ee9c6656155a4f7f78b2e1f67db6efd7f56e0eced3358975fb8b7c 4e6fcf8c2741870ec3c0e39b057d6b01ff33cb4ba368c9d89e96748261273f5a0cbac878e3f684f1 b17fd9cd6f7d9249debd0792c9f79818aa3a933ad4705af7166ecf94366be74f03b9bbe82766b79c cb7996de1596e6e61ddfcc8640543b07a8fc9d083ac86231365ce4bd36f0f369f5837e73d7cb3f47 93fe6476f1e9bc5c2c222919cf2c78e1fc517827fd01399a7774c8ab228bd6c086f2968b23d8a84f 19cabcc78eda89c3af3b473bc27bdf13da2e78bd57572bfad96e79352b5a06b6ae59f5cc03312d39 43750e4748e838e3b6615c3223d720cc7ba4df69e3a2d36b50698f6ba1d4e6ee92af4d9545fc0762 c95fff70b182f979b7d895beb3e78aeb86eba883aea621595b0f82a9181b232d5ab5868d45bfe9e2 739beb05fa44770479e3daf2f81d76b56d666919affcd6dcb5ab27b3b9a9de3a3d0a7e77d0279b33 069a5436c88a09e9a3818feb0cb9f95e37edf1e2adb63328e66833309e6a79091cd445635c544b63 aea5acf982a7e8b561a254b7768a488fe58dccaf4fc32a782587f4f6b798afa3cdf4d449e6e38975 f2fdd2fe690fbbc79dd4bfc73ee64c6bd9af1fda2e1e29c54a36aa6dee9ef6a8734227917125872b 835ccc76ba5f9e9dda6f777d6f4f88d3a79d993ff3da4c2d57b5fc1981d5852b516a39379494f5ee d1556a081cc89bcf7c27373a644e3a40775ab29ff1d7a22821636a2d9e5b54f217870ff9faacef4b ff32af6597fbd0cbdfd7e90597093dabfce9bb37ecaef5323b9fecb60f116ceec503dfe9b31fc3a0 a06c5f67d972d09ec448d4ce126cac4923f65ba7a98b377b52d5b5f950e29af951f45b5494b7745c 97ad5216970e7d9c971cdc36c473741a896e574c4482cd64042f1c10022dca3dfe953fa71354a190 fc41b078c67b597d6456adcbf0363bd0e177c5ca8453ef89afadabd1d0b934bc7e1001c3d64a13c6 3c00a7610c6399d6d9b9a3b4f953fae5cbd9e550d59af14489c7e78552cf9c63793bfbece566e173 960ec7c653423134235ed2f64d74374b54b853cfef2920782fbccdbfdafd013fc64b6b1e44a3776b aa30584b2c57ec562e0ee6290e492b17c6f10fdbc4d8679752c77f4ccfaff83c163eafeda8a363cb c16040053db108cc6ebdbf687508486bb43fbbdcb786d6a2352daa9ac7198a31e61cd9da089e74a4 8c5042036b215e382f3d4a497810ee9df82ad08ff39b7f05c5023fd684060f2e2ed59a4e9f6a4b94 d83e373f2c169c32169f5c89cf23ec3a7bb4d8f65cfe6e5599be8609db1e79f10fc9a9d4cf2cca8b fef367e20ee697cac1b3b3e4daf52176e228e35cd782f1bda8bf7c0bd1e463d25412e9c2c9bb5e4e 937ae4dd142fcba22b92958a2f8c3af529ff7aa0cbafcfa6c5a2099f296ba7d634761f2dd1dde5b8 451ed43865cf115cc90e24b6fd49ab816a6246ccc6ccdf18e8396bd0fb71af43effb95308516ff03 5d58c7c3b48e9fd711fb1532adf3dd5f77ea5fbfc0f0745336fdc9829fda7aabde33b1ed496e4fcf 1aaa249b6e5d86d12e295ef33d417820639d7f13a32edf1a8dddd6acb40a5a797d35e516f86bcd29 f7e28e2bcdb91bbbee0e326cf5342d339bb4b1634c43fe36ecf47e175ab4ed37421a513717aa9f48 350a37b136854bbb3185cbcd3585d395d50f6bd6e2b35133ee7f26adcde1e1d748f432c4a3ceae9f f5cd79d7bc365de359dc28dac27508d92e3015710861d8ef19c187e4783ec7282d096e75b8455fe9 71a55bda91eb7277c2d68ae305b339ee368cd90767fa90adbf687bdb2ed288356d5267a6c851f85a eb9037fd3c26a9bb74209e7eb94204ec4325026e1410017aff3ef1f8bd1241e5b85c6557546e8611 0e18cf669ff7c8cad3b7014d770f3dd9f657d661c379fa67bcd7d4b8ee5212cec115fe53dad45bb3 db9be616ef8cc8c6b57c9bad59d52e631e21973eb4c8807632ca5720a691cd20a6dcb49ea0f0e7e5 41dec6d51c49575b75e2799dd344e055da04283a233c5c9777780e5f16f11c3193f11cd41ae1b9fc 74f917cb628b2d4cc99d9bfd4734f7b9873b2e0ebea780d32e4f620bdd38633d879d7465bb0b5991 ae88504bce9a6536eef531663b1cb2f461399169b4b230a8f3e5d1a308f2ed91f7023c25690d5b12 af8ab223b841ef4a00719fc1a7855215cf392281cdd1d537710c2bce9a0374252d13b4b2e2b248a2 233c5a893343b4122a0bb4e2b6e63fcc93215e0c59c5cb079562253bbcd8c6dbcd14939b9d7c365b 9394dcf0e739968fa3832070971ec21aab5a9e760ed9ef4a2f6a006a0479eff00249ef558d18b3b6 45709fb4639f0a331f17eb8f399edb97124c61b11356fc74de68db9895d02a9441918e2f8af0ee33 77e0ee945ec2b09903cd53e3c6354fc561bf797cbd67cde33bf3fd6e6018ce4751f7899727fc3b28 faf5713d3f7894bba09f1fa773c82fead8370f0b4dcf923d195bd4545e9cce09a63b664be428f48a 444b09617c7a3ad0785ec84a98322fb4d175a9d645db277e8856fbe6d7d185746efd050245fb3d6c 171f37183eb1f9661fb71b8d6b74e21a43a565418ff56a0af986f18058882320b6904d2b48c90f21 9ff2a73fcc708dfd57346f11a5015b9ee77bea24f7e922d3ea774da111d2d958ddd67a9e44d9acd1 2a43094de3985ec325b80e509d066564b34211a481c90cbcbfba128ca4fd78f35c38384d5c3b0f1b 37ac366d50437c0d3d29f304b1cbc5a71e569e953abfe1c95a44fd92c86b8531165497adcbb95a8e 468daa7a778cea122aa7ef418d49ad2022933018505050ad6deb1e5ed56a2e7fbe941d43c77216d1 611e464e66f7ca319b4c8430a44c76bbb8a7d78b6936d0b85fcf345d51f87a6e1b9edaad40cff314 498fc78a82401e8875412db6ebb93261d76457f66a45c29b55b5d132a956d8ecb562c45caebcab07 70b9d9fd0825e763bb25740a6f8b172953295ea8a352422d63587222cfffe13b7b8a791efe99b887 d4d56bf6e569b16e7771a5648e0643d05eadd8a34c365753beb8c3bbf4c54d787caea930d2bc1105 68ac16efd555aff3a856339b52a5239da00ad4ac12e56e0fe6ca30a6a8a5deb0f315534a989478c5 c1f23e2b3caa954d8131857bbe158c4b39717a492f6ec9d0b3caf213664badd9235be22764767e16 8dec3c1ef47ef8c7c4ddade103602eb15e7bc021dd93be823a7c7e92d3a02b75fe451d67463376af 176c72028f047423f268e331ae976a6aedf52c2385ccb1e08f86977cb6d6ffe466dbb8942b340f50 76712a10d932de6865d63749cbd4e8be03ccf17a0c1a3abc49317d81864142292edf186ad0105d0f 3424fd021a348fa47828a0c1905dd080c5bedf88fbf47004f38c9bb70a8c63765cd21ade76b05e36 f8ec3f9e638d99d2541075f1b2d512603cae6175397f2f970fe3ddbb10b8fbef3d2d133fcf5bd014 9767d0940a9f144229450883a6556053486a8a750f34bbf034c5780f9a369c49b14240d391d3bf6d 17c6294e37d0d4f738680a6d3dc56e009a24187b1777ff9dd606adb72ff6cb322bd8c823603ad96b 0b529dde0d08d19adbb128b39f10b2cf59083acc0850ae17a1956e7d542ebc0e834fb68aeb27003b cf1d807bf82d45904f71ae017882e329fa7c8ad34fb985437290623a07f0b49efea3e9347d6fc311 299e0680237f06e079fb05609f4cdf0b8a760a274c7f78b818fa625573a5f7a3ed6c5fba628e7328 d5ae879baad4026979d6e55609391de3df131a85e95d079ade55aeb2e768a4f08ea05266b3be3e01 620def008916e99fa2f50b20bb722185de4c312301f2c88b29c40e40f3bb11401bf42a457c036893 2ba7b8316921e0db00655bab9f408c56b329eee9dbd8c10328cea69fc7878781002956bfe2964ceb ee1f04bd2d755179b42d145a9dad76a626b5eb1cddbb86dbe0b9aa56391462baf0e9cf9b198b9572 ffe8d0a154f8198f7fffcd6758fe03acbc6f000c85488071230560daab0bb09e1c006c754b007632 5f003b1720807d6602c081380078a6b84d712901bc168b00aff77d8043d82145efe92ad0dcb5b156 fffbdd188b12ca2a437852e7d776ed4db36c678335eccfb8c13f7b56e510214241e8df31705427df 914c2affe8d00708fec778fc0b9bf889ab3fa7ef6c4e037c9b93007e731c40d43e3f6b2f21f47780 98220010c91e01c4b5ab02324b8c0159cf5e00499c1b8014176d40524a0848397702a435caa71359 a6db9911a1a89e1206fb2346539bfa05db0ecd7923dc02b772e47dad90fb70cc4f87fec9e33f1dfa 37acef60fe633bfe3f9aef1f2fed2fc4f7a7b02eca5f85753303d4b0730674162b019a7f31803622 07d0037d0de8199e01f4364ffd22266e470730396f0d987ced0118c82bfd15a3a5ac25d559578edf 78fc2e6d9adcdb1d576116eae6176c20fcaf81fdacd07fadbdff660affe4eeff57acf07fe4ded711 30f3a40458c863016baa3dc07a7002d879260fd8dd2e7defe1f5015760e314eb3be09a48e15f0327 51fce18f9ef6934f7f31bdff860c3cf9eeeedf985eb2f827fbf6e7ea0d72afd6e3a3bca447582eb6 1f7c5c371f5903fdb3dfd9bdd02546b7252c4f6feac95a5e63dcdd5e6b5ef415212fe6eb953b1f78 013d23d1b97d3a2b92ff47dff56aececef5667bf5084df7662ff8abb7f55b4dfc8ff47cec42fa377 7e797ce4d5fbcfd661ebfcbd602e2a37f5b0806e65e7845df5cb9ebc6cc90d73311f27fe7c60f7d2 d9fe5cb5d3597898277cfe708e5e39e31d9e46757a6077adcd9e3f0d9e3be99e69a423c15ac95616 3b4963051bbfecdb9f6c5779b77f19b8bf6484ffad49fe8e6f4c3c834c43798405d6be4715d5bf2d 21737ed5cf6a7231efeaf16cbfa5dbc9cdb2afe34de6be8908cddc81ddd48bfbb051f9ea69fbac53 acef0aee3d1dc768876c6a93399e34a229f3effe611773e5358ba3658009f1e233bc67d2b69983ff e4dcfe4211965211fb8bff3dce62e677d09457f5a66e2fe8658bedd8b3fddc29277c9a98476ab173 f7617decefa45367b2d15fa530b1c03b8a9dfc69b976cb8bf5ea66cc362b6a37d92f03d83f2d3efd e175c1dffcd73ca2c3ecbc308e2b517976f8c6b4cd8c62b935b5aabc191eccc722448ee2ed4f1ec2 3700617c1b9af93ff02829f73f867a97d6cdeb658b8eae273ce4c1815dd78a3be970ab275bd668c6 4e4e23d7f89ae37e0e62ab292e3ebd8abac80e813e2ff8c74ea47e76dd592c24bd596dbe1a4cb7ca ce9f36d6bb30746abbf9e46c1de3097e7c1dc71e5e79064f4f2c05ec6b8afb215fd1fc6c148523b9 54d88f0a6be4ea2df5f2e5e752fd5a66bff83bcaf440caf7f381ccc487a0989d6ed5b3b58c91d925 5e51c9f490fec6bddb5cba773e91fab68bb35a2455a78d15db089d2a8b4edc46039fe0078a197b28 de0a9e0344fc49aa24a5fa1f1fd77dfe43754791c0baa3c25c0a3cad642f86b1be380c6bbb021858 b0de740f6e5e73917b67d43f8f76abbecbcc977dfc3d5afcf09f217e85c9fd475b0649630c19bf67 04b1de5e64fb542f2a8f0ba3a995bf4d43243eaf27f8eebc1f53a7c325606fdba71fd23be067c7bb fc48ce24256f29adaa5e79398786b19620c35a9210030b4a18f7601f45d741737aff3c68f67a379f fec618f4a88f9b3881707bda9f8583d8a15656ed6ca20fba5147f7fe4086f8e11f2faf79c136bf51 c60e78b4171f2b6623f57147a7566e4e4fdcda481e53c791e98754bb378ab88eef2dc5ce57e518c6 6a7b35acc5edcdc0aab70feea1ab9c5de4a4dcfb2e2ebefaf84dcff43c5a2f38cfb15975b88cddb4 436948d842e12476a376bed32d6c44dfd21aab8d19f7c88c59afe598cef6e6743a56143a9da6ec7e 6dd7bfd79bba399e7ea6e8c4927bf61218aa3a4b1a341da2a5526d4c1da4a21f927c735408296a68 946afcc0aa3534d76936acbedb42dc9e6722df8d30a9a9c369fcd201a8b9b18569ffd89de7edef99 d695f7b397b5429659ab129e2b66072a2066fd21719dae3a338ce3a5e41b9838d8e9d70253d2c917 21b7fd76e8b6d96a38fc417b6fdcc1ef68eee695e76ce5b79251a48f66ddb0bf5c2ac1fb34fd4e6b a3f945af0c93ac96710fd766b9ef3e65acc70097775ae452b7d34baa9fceda05df5a771a33abba6f 2ccd8dcf6ecc46593a766c43ba7610a2ff36fa232fa7df4acbaa3e3c67b1f693405a6d766e77b410 be7ead561affe91ed448cc17d5c2e32229cba5355434cd1afdc58980f77162a70dd2426eacc653e7 b477c7637036465f53e2d022d7888bcb5ede79dbc79c3d1ba06857f535deaab986665ab9e0f7dd38 703030ce93e5d820b2cb99ee59db954e37cf9b76307e1edb1c5fbe6be116055a8e164aeabc18c069 8bb56514ed566b2b95c81fc91db8ba95eba74d41ea0a034182a1b4433bee69ff2fbe0f245b6efeb0 a673d12a4a9ce36cf2c01fa3206f166c0f925fb23bb0af640fb4a34a57aba7fdcb769d343a4e74a1 0cf79493f451ad6eb45f1edd6b732fced3a66d79a2e53672a42a94b6528b4f67abb4b5e0dbdf28d5 4afc943b837b4e86240292ba078b9260e7a08afd0c3e10aee6221686b49913c837d3e21feb7b9fff 3e8efa8b9d6abd4fcb29b34c66f074371f4fe699f1282e20eee0f4aeb47bef4be6fb14d756365bc8 ec2a3d600c68b1a23358176bb7cc694b138395aa2a49622a6bf2d257aafec593371c98c850529c4b 36545c4bf083d88b275fbb8978790084ebf9521528b18ef3cfbc25f1ac7deeb542207dd5c1163fcb 7f5a59f3cd7251b367a7b8049c744c7c4e5acd479b6666715a2893e5768a8bf7551029d5a9b75db2 9eebf509d389c4126f6d0b07d8b84e7ad9f6a4f2ce69520183d5d205a3945a9e1164d36e7f1b7669 ffe9752544e8f5c5febce7093778341186a7d5827ff2bb840f72e0d4fa749baf56c8748a2da17444 b9c8a8f09c4c7836bb5c94e7aca6ae5f4c7c19524c7c25ad14d38031cceee8877850f0bfdfcdbc7a 0b77215dc825feaa84cd87474d0dfaafbee1d8ab3c2c998e73c3f4a0362aa88bc70c287ab8a9c98d d1099390c99515f1ed55143c26d3e69f93bcc5073cd46f7db6d8a82534c9b0957d75565cb49a7e17 2d71c56a726397b76a8ed5a26e93499a3b8e31de9c45eff8e594eed6f53bdddcf30475b41f1deae8 283e75d45aa31fd60cd7bf440d2b3a4cb8a0b8f52b6f7135b8027dd2cfd4ac7e37f131a533c0b2df 955e6dc19b961583edbd2547d14b22819b4d81a64d820fd2fb462bdcf4a5568e9ae89c1c4c2caec8 adfbecaaf20ed8cab0143189d4da309d42ef42ef9cb466eeb6a03a0d57070cd5332f3a85d1d6841c 4c0adf7b1a49ea1f942485894e922234fa0764c9fb23f17ffde6e3a956df8f3aeb763c202fc3594f ca06436bcfb6dac66853a035854e6ad291201fc29d2f64f871b3516b4d3104e5e6124e73c525f5cd 84642b0922331d086933f5876ad3bbc5704877edfd9486d1cc9aea4de1238575ba6ff20aed2b2419 4004f1d0c61ac16c8a23fc3ddc9ef0091d36f109a36af8043e0d535c3d7c52da0f9705c8be4fdde3 f6fbdbfcd1cdf793c1f69f0c0c2f1c5908d2e9e813a9ccaaede30e122f77f1cdb7a8c58b9b4fa322 bbda6f1b6c1539e30c24dc58ba3bbf09d4a9f454a9b48736292ce45df22ad86392dc4d16c463f4f8 9e0284cfc20ffc1d9b457ce25e313cf320656cb6387a98a4597b7471a56be8629b95b1426d32c00a d9c9f08745d9371fe1a87db805a584380fcfb5deaeff196c56766c6dc7a68b4ced76544078c5ecdd bfcb4a85c7bc9fe516a87a63abbe9663a0d8a9d1f0ce82a93e1350e47512b6482a1bc9c4637b3608 7f54eee1ef7763844fd66a84f3babfc166b7fd1593e6781e5d96cc26aa9e3322120b5d1731f2f50d 525bdcbf2d2e525b6e25249d217b48adc7ba3ffc24fec91bbb3d7c7dc05d8744767aec0b9d4762d7 e5e7b4e3c5f35e5a0ee0a2ece0394c6077fb12ab0fa80b8de0b727852b952249f59b10e1df080cff c8c2b729c4c3a225e2d9b6a36111b6ec6272e6394497722144b5261f2371cf3c2146eb9c81b75103 822d6bd26a1e4e25bbe94c7671e392ebe49a28a9f1299e76136d64fb4dc7acb9b3a36b7f26995bf6 3db220e1bbc663e0dd7697de5c2aedfed88e5ff17da855025d952e3c47f3829fa93156c77e9274d1 bbe29fcd38830bd4ae8245c1a58915ab3912d5cc2287561aa88c1801672075ceeac1bb4a5a55373b 8779f34821df875e4d34505f8d41655969101b848646d4cc84e897b8a88f79f0a9bfc21c5b7fb94e fa5ebcecfdf0c7175fb08ac03b42eedb7d6d5f77675d808fd6499162a395d403b5b35c742452f7f8 d67cf15b59fc5b4b8a4f31f9846a97dc1d49443287d4f74a15ee221d186e7ea654b327cc5b4d2c77 511b033b673548041d40a3509b428c1026f5f1ee79ab6710a2509b9e23bc9617917655592c26d592 d67d544b2de5bbd8afaa4c8a7a757ee2ecea7c4d74433f9d3b834ab3991b5ef6d38c9b19d5df4ead a05c7fb6637d76d166cafe987144168f65ae7a26518adc1ff298b2d1ce30dc1d1c1a577efa6a90d1 3a073dba991ac49c4adfc6a33ec1ab649dcf8a426d26b7b59a541cdbd5456f37aaaa7865515947f2 b1522b4e41d9d4738d72030fa4923d4246c5d3fb742e21c80929d95a5729ed9747abb40f12f36736 1fedc64471f0b0937cbfc090995fd47163fa3269787dd28b95782d7f371fe32379a9324e0ec389c9 675b44b605e3d6a0faad4d2d02a77dad20e51f55f580e72a314a952bfa456f96b7e2902c5bf9355f 3aa827ade454aa4ef13c10c7c5b4b4f8aebf2978eaeb9ee7a06a3117da3d3297bd14cd6c34d8aeb2 91b92ce684da90cd09d9939a0bf1abfa2731fcd4d6aaee47cb557a5a7754eaf60a8f6c67cce6efed 4a72de48f406f2b804f334ca3b95086c95db7e7bcfe6a5623cebe9dcb4abd4556955bc30854d9198 2097c2a8267df22fcb28e4414fabe6a65880e672c32593959f67395b1c2376a65d35c72031d709a8 1fabcf14eb06a89f3a0aa89fb929a827c827c5af2904f559834b21c97f4cdc5e3568ba398e6e381d e558b7ae4dac648871f5a5c276b811c42033fa798e09d1bc9248771254a1f15e7857ab6df258bc12 c83297778859a6ba28ae0114e776297e9767fa3acea678d50094d0788a5004d0a660a5b0c700da66 b6298600403b184971d701b45fce01b4b43f005af12480c63339c5a33342e60d7c30ce9cf17e0975 30db662b88e98f99aff3feafedb855abaf47144b56347493f864e3f112aab53251ff94fa877c5afc 6d960b6041b92968ecd1750af79ce2f0068d43bd98426ba48869d038d69414fd5e8a77fae153f7f8 d3a1cfa52fa604685c641334aec80a34b6d95c8a6b0b34e6a491a2d71fde158971c574a6e9d5d003 d375cbf47f3cc7fcd24c56f42d4206b886cfd5e62dc689da7271af94d1f6f5ebb3c9cd4be601742b ee0ac0c05ba7887700ce64af29e46c8aa092e28600b8c67229266d00d74b8314bd0580a162fa3968 5c06708366537c7a006e6eb6002e0d4b002e77650057723fa10b06f464306116525fdd9aff1f5d67 baa028d22ee16b4977046411944510115c1045144551144551dc70bdff8354f7f4cc7c73fe3cd5ed 52666142664606f13606380cd5bbce6641aa7893c94af59577e1078f925b911f8301711dc30db419 5fcb2163322a66b3d5d21310b3f411102ffa0c48bc71fd8dfe03906c9801244796120731d7a601d9 3cd601d9af6a31661340dae55d0cf709c859ad14e32901723e1d03d26986801c5551408ef34a0c7f 06480bf34d194ab58d8ebb6ef558bfc7b7bad01b6dcc06fd4f4d1f5cbff714562583b149e3b4e9a2 6b8eaf4143294f64d6bb69e6970e1dec5fa08ca60128d7b16f38319d016593cffe82334541799765 40f9da9201050e3aa060f69b38110480123b2940d5d3c9794375b62d4075bb0ea0c6ec1d50569104 542fd200a5cfd780eac397a1faa9ea7a35976eb437fb052d3f68ba20ec69ebc688e9c73aa90a586a 980f05328b269351a954f1579b121dfa3386928ff9633bfe864d24c2ef8fc2faa3fe1a5f6bedb501 68ab63003a782e40059d9e41a54ae740457ab0a0a2cd7450b1d40da8acd834a8ac73f163eba3012a 5e377e6c837e65bbbe319b77bad966add6bce9db92b89d921f96ef4f0ee5907fce4aeea0db834cef 23668ab326fe4b874eda94e8d07f49aaffce994804dfdfe9c289b53731fb2a390354d7eb356018e3 0198a08901e65e94019b09a780ad2e2f806df451c0f62405b013741ec33b0176ca66b49413ca2a59 6cd252ae1615380baedca860395c63f5f1cb84ae56fbfb3119983d54feddb69f66258df9a33eff23 56f8ef99c2ffcaefe5ca0fc0ad8b04e0b3910a786cbd48acbdac7e07bcde24013f2d7700bf7e2e01 1ff6cf313ee9dff902c94dfb893e95b87a13f9f457c8c0ef98dea4c45912ded0429e7666527ef2ab 15f3700aabaf9ef610b787c64f066e233875eeb9de5d8f3c020c236808be01b899f90db1cade557b d58f977841fcbc90cb6bf16ce6cdc6296a7dcc1f71f75bdd2cb1ef1e1a8fca2831c82655c3fe962f f02ff9f44fce4412d34bac9ebc2b1d1ee2a679bb37f6cd77a4848dcccd2f2bd00d1929e8557b34f0 2b366d5397fe47a99e2ff50677a657a2708a9a6df9c46cb5af721bda485f3f3af8707c4c1beb65e0 31c7c3be9342523bccd3289fdc6de59f8a667fc5de265108895ff67fe553f4c737fdf848a17877a1 b01529877090dc2a7f8ea6572d7aba1772166ecff432389c18cf3b8776d18b8e4ec97a1cd37df37d c8992db0f73922b50bc46c6687b9a9bc3fc867919f5084482b54bde7a054f7f86babb7161f1b6715 77a09beb49abe2ef9cdb9f50840e34c3ff2423fcbba95f3be23dd7eea66f88212117d226caa7482a 71a10dc18da378c8b603e5b6d6763dd0d0fd41961f6e4d881c6f2c149afe940ecbcd3dfe9c5eae1d eab95aa7c7cfedaaf18cf6ae578b42175a7ca2652797fb2c02b55c70c26e9d74c8a3d7989b65c69a 45a3e371c63cc95f1108fce7f14930fd38c7f73ff2051e4ec67a2616d40b397907a19d5fde02af8c 3d76d8dc7d6fcdfc24b761f63aecf1a14eacd323895ee5a61ceb420e515b763284b4089aa5e602db c22d276c139a4306a5fedcc4e1e12c32606bc6dc0827915499aa37fdd8d2615a0793e7c4951ec549 ce93382b9ecd98635f53bd710773c231129ac75130e81c12fccd259be4207cfda787c6b13cf2c925 35f49eddec7c9d1eee57aef29a270ee24e6a795e60def8ee90bbe1676e96aceccc2207d08cb958a8 6d574c7cfa990cca53f1ddab4cd373939bc86953b4bcef163fb4b1dae30eecf44781e65b232cfcac cc01c59f8797b19b1dd22f9e332c61fff5aa1b6cbabaf8975bf68f973711260368b81b6c2c8897d6 69c316967e4d5517d8baa6cf4d141fcd98737166f3517135155f797fe20ac5a3e53532170bf24034 f6d5fcb7fecd18d965c1a8574a6546d8b15030076401195ecc023ea4ef306d581cc10f9e73a139a8 a58d7edf9177b37e7a8b1f741999a57b9e5ee37a2ae1757ad03932347f180cb40eedf6e31e99bf9e 87eff17aefd38b7ed2d33a90e87a4c865af4321c3a37911461dbe5333b4d4fb792e5d597adb1df5c eaa378393032c3de746a92a7f9626852d395118da75b83798ef783a73d0f073530bdf41d697aefa7 d7d3b7ee2a7e56cff9976f55929e8a7eca9adf27050db9b43bdd5ee56877c22977ec909f07d4be2c 74be6d4a1ba55dc9ce9a09ae4159088ee26e30d9448ad258e5532aeb842bae3463423a3d15eff3b7 e589567e8c6c069839c0e4afaf73689665de601eac34b0f97aabefd459ad9f5eb1035d2e544c3db7 ad4d7a2a529b6b7e8f5d6a1d5cde7403430dbad8ad73ee0c1af6ab7d094ff9764528122d6b290b2d 567b0e54fbd5dfa8a0cd679585f8fe2ab78a840a4d2573a0e404897f7bd7b316c65a72b6daa2cff8 f26cda3cb0d3acba44c73b9a7f9a832a7b3222137d0c6cae94e9d79112ace7f304d92b0a1cab75f6 2da97b6074b58b7b23ad731aac8cef66310576567b246dec76b574775b133be3abef227e528551fd a9a646a7a2d2c8a174733d18349b856b3492b7cbe94146f036d2d8bf0bf586b6953a0dac5d6d2738 7ebca7b379b43696dbbedf8c79744a7fb722a6eefc255ac8f54d99c3282c18dcc38afa99592be8b5 30fda195cacb5c9758a6880ef52cb1edb15aadb71e57496df1524b53e7c7ee4015fba3b1b2fccc66 4aae3e5b363df4e43721fd1ecaed0ffa9411bffbddf76cf42abbb214ae51491af4bd51fd7265f6f5 4a230789776c2b8a77bcd415ad53b69320d88c22d74b8f0fb3c5894f8d670285e89356a7a28c8c46 89355e3a54eae721ffa305f0e8d8b900e9de1e3ffadf1b315bfc658caa4e7d4b2baebee594dcfb2c 353d3fdb6a16d1ac267726b0d108def0b8d1db7273e984486b69104d0ef5abbc8bea95733623de87 4d5ce4d21b41781dea8620704fbfb6709c5c6db1d36a35a97bfe960b497e26d89164e8ad36b9ebc2 19f985a99d5d92868565e48e597d72c2403a9364afdbb9663ad7c6e2d2b2d7a9b39aa9643f4abe49 42cde29cc7e52ed2ae360e3dadd6c05fc386640843b57edd4fbf769efa485b1be2fd799c8813fbbd 10de70c91766b7d645483596a0269d52682d6bb4785e495d7b7c21b03cae4dd4d25c9bc7380efe4c bbbf701976b656c5dbb85df5eacea71d623e85b4c6784c1eccef356dc87f2d8890c8d01a99cd14da 9cb7bb2babe1f2d084f3db67e370f333d23917c21235f890f5710a62c4470311442e2465615e265b 8270e57bb5a5a498b546693ae3d7fdf59a576ad990dbeeb817d71eadbfab3536c8151856f3265d26 a4ca2ed3bf3ddf0ce9fa55865c896d869c889d049bdac4f3977d18783327be304d3a8dfe7454695a 03a33e98a93a9a6e309da85f845be9f2e9d5848371d02096fca57eb33bdf614d7cec0605e17deca1 82c85be5dad2b1aab51cead4f8f5c16ff010776973db4fbacf211dcc62838ab2647b9975c084fdd4 83199062be7a71d674b552a25a95e8715f54d8dae05e61850e5d61c5b45a61e9dc776693fcf4326f 6fbf182e335bdb5d092babf71ccecc4933180d9ac545bb87bb1adf7e91e5929abbbe412364a7c73a a34407e1534d3f84b4524cd7729d7c8157e2eecff95cb9cc215b96617b081bafc8a3ded709c98496 dd65ca797f58bd9c73f3aa396ff8953b32bf55ac433a4bbf38a54ccf0a6f85061ddda61655f6462d 9872998a47c2668c4b3bc1bad07603c7c2806fe7f5a6373e85cbc5f0cd3d27fd6df1fa0d04d40c6b 2ab605522595e2b89091ccd9f424381d6757cb9ded2b5f4cbb6f0ecd2f326cafb58298f076c0aa97 e78aac9ade9dad565af97ae54eb1adca24351854d87d60d333ecb9a66b2ff14c2d5a4e8a9268ec2b 749557d6502a379bd509b9d95ecfe4c63fe0e466a736ca0560b71224bef8f92b8706d356d3f0c794 775c0d45286df7915466d01db3eb464bf23b54b3c410f9fa44585f6bf966dde33a017b647b2ff51b ffc118a926a88e7252ae721fb4e00a7bede2f46b31abd0336957a3052ca3500b1bef51524d1e53d9 a2eb969ba3cfb15c68281fb2e57928091b629de8de8e2651726721515a4d30a23445eb31ea2ad13d caea773a8859fe690e06c5c3644f4cf6a3f1e0e619cb0fe6fcb21de3674555eabd8a4c841424bedb a927dfd24d9f397977bf3a1a67ae95890d9ef41b14d2b4d025206a71a44a94346d52e535a473e5a6 bf687c0f5ac1bc77c9d6233324e175dd2134d2de1318c83df1be241771b2f4a961c341dfc068aa1a 60344d21310e0236ec149504cb321e5c66925a394db0c2e6683e5b59dfc85d7837f11c776a44e13b 10287edde51ac3a75e12d308fce134ce3b541eddd19a9e939323b5ac2f222abbda7cca0a71ce91db e10d26db759824914c9521345d12098c3054bc3fdf0c70b20e6c6c7860b7183d71a3d2f8932fa0cf 4e527511b54b988e7cfafb2df2196c0b28bf6971a89d3f34d1e96b232f2a9bf066c7ebc08b45ce77 27b32610c140f5db5ee239ee00bdd957d07a244af7dcae2cc8b49265c33e74a5e70f7d555626598f 6c6fa9af66430408772730ab9dc243765c887bd9b2845d5a470aa3f737ae145925b9c4e6050d7d6e 26165aa3ae6bc4b1d01352976719d8dde629384f3b9da27757d645ef2866e13c5660e07c4e9060b9 d16a246536bfa3a70f7af7b139bc5f870ba179eaeffcf94e1bedbd655bd2276653ef924d89c3f34c 6dd3dec28cf9c1ee947b5c6c081c9ec6e7e573bdc72ab5fdb97487b3af12ab4169d40e91025ab345 0c59008546ea8be9b7300dbc2aed55386f678de2a62639c5e26e7580ba58e60da1e1082fe802aee4 4fcbc0c99facf5a780dffb5441476f62e1d04dd5e7a9fdf3350916d66b34a964eec63aa55ff5a379 3c761ffba7d75a4fae53f91c4f3bbee38df0aad4f86eb045ab5c877e955bca618f8db4a1830a2568 89483cbd87570e7f869bf5f6a3b83958e9624bf72068770e30a8247eaa856386960a84ae6979e3bc b472b77a7693abae9a5196db2e0b99394a7df73d3329fd30cca4fac12523788362669ecf55336f16 6367390b4d59466901cc77bdf6312078f7d4c951f6d2e5237adf82d6f8a21149735df052a8c00e67 df1bf7e2b53e71744e47f4edf517c5edd09e168e33f56b4b2890b0e9e7cf7bef94a7b1e09e1b0fc2 4f76bab8e7339f5ca6941155bc925eeeeaf574ae6a6ba9f5336da7a09ab803edc5f60d9086480044 2e7500d2cc6f01c2ed5200e1cb25d0eef7e869e78467c7a3c7e93be530d395517680dcdea067ee9a cfce02199f55b45f5f4bb5fe745043b162bd6a8f8778b923165398d5dc9c90dc75bc2e84ee6092f9 5cf4615ae69a76cadb1aab94da5eed811fecce00c5af4f8012542ec6084f945be2c50394543b312e 138096db7b800a480aa0d49a02286df4005a917d8096f0f8c525a70cd06cc84ef4530f4e82a38779 f450eceb8890d71e609b6eaf82e5ad4955d0b5d8ac6b063baa6fbe5b7874b3c8e344f9794fa31232 bb4087a9b6c971946ca75aba3004259ab0628cd6310e0128558ab7184d1063038352b548c7e84931 221d9418c589f13e81123bce8112576541890446221093e101948a6efc26988c1f86e3f948445998 e93c7b840167f3987e16176877de3de6548d6a5ca5b9b0727f3cc7a96153208f4a1a2fcdabb37471 6fb4ae790615fd3454ccd800e3f4718c8d9328b77c7117a37e8e613d635ce3c76af1d788d54c26c6 b309304119c6b8ad0026766f00ab178a313602c0aabdf8173162fc5e962700866feb0023e0eee825 7c2ac3d533a80e34a9f7dd58f9b11d2b7efb27ea58f08b84c33c6b568f428c540db3f38312dcc950 a9bc554b5dd26ac3df005cc29700ef56dc18b5758cc13ec62502b88682182a14c325006e21b518fd c4da8b5b6f0be093ee16e0d3cc33c60203b85d97013e436600371e7780b7b7658077baed1807d35c 224ccdd8be10e16f9ee332bc7e2775f7b88ad89fd2eaf0dec6ad8bc222c501f8aed6f2f7f116a45b b41802a2eced7ed98e13cd37c1068a9fd888f718560a10a70f0288334301e2bdac03e253d0621836 208be821c62a05c8145b8ef1690332bd5e02129dbc138138db650099638d188e33f4e87973d09b89 b2569b642aeaa13285a445e513f14655f22acd923324aa0c2c23adb145e51f62b59086b7fb3b20f3 fc0590723ffae539fe0abe64747e8372faf149acbdf14f504691afedb78d8072fd5cfd9538315af5 41795959c4f04fbfc326201694efe701283f263e28c7432528c71745503ea7c731c6beb1a3dfdd24 00a35d7c0a44e3f574bfbb51b5c32b7facc66bd839716b2dba68e14cd70a4cf384a7913e9ffad5a2 c4769c08cf3f4ee3afaecaef917f844d7ca31d7ea45fa102e8dc5501342b0f013dc97949c4c46a7e 07f4514401fd2c34626c2c50c95b6750213a28a8c44b81186f1b546ac6411f6964b353a04ed52490 59d8b7bd3b23560a1bf2925147a872d8360b6c96ada47ad371fe4787feb6e98f0efdc7da9b388e13 cd37c974488ac9fd4bf84d1214486804aafde30e30d8360d18397e1b33a9eb8059231bc01c9f29c0 3cf72c60f313e3a77a5c6d1b23b877b3ba2cfc540784b2722a9e28af8ee5504dcf4b79206805ebea d55287315cfabb1ffa8fd69bc409ff296bf737a7714e4facbd3fe91249a2f05f990e89bf77b33800 8e18e501375579c0858c09b857e10078fc0d019e3b4880574c0bf0033a88b17cfdaf269954e6bab0 3fa5c31aeddf31bd4995b36f7ec34ff62d57787c9a55f4e11424f2916e4b95bb8b74f9df01b8ba1c 414657bdf994d1fd5b002e666fe6975000dbf3a5913f9d69aff9ed6927abf8aa844fcdecfd1438fb abb65922f2fed150ff1ff9f44f4cef74f0e497c7d943f46edebdb17b1c22e5f8b8deda97db370dee 1a549ea92b3679e62ee43c289c2fe20539d36e889dac7c409e98cda512daf0853b7eb48b7814c3a7 72685c9fdadee783e1ae97b938fe0022c3ad898648dca6780294c4de26e2ee371621f1cb26e5a994 0df15f21034f1bd491475a91ab917260c55bfb2cb7af5ad431ce1781b54e560e9f8776115d1d9d52 6673689c0f7ea044ab608fccba875d2fd53af961a379f5494ffa6e486ecda2f0da44ddc65fa1084a 692ddedbf4cae59692ebd553860bad16c112f10ba924e2f62714e1af3c84ffaa1af61057bdc7cdc7 ee8f4bffb1ce24df4da65f3c7eda0a71689ce8cabefd78727e58f7c42dbded35364ca0aa9e8dabdd b543b1bd757a5c19ac5ca6365ae56c76e2aaa03273a145dd5d76b2f5cda207b17b27ecd4cf0e79e8 bde6667cdd9d59155049726e27bd8ecd7fb0f5d4a98fa33f510813b7a9dc13fcdbcbfbb5239e1897 760fb9bebbf54331ff9325ebd9a5c3632d5ed660959b4c732e341fc34bc4ed27f969bd7cbfec842d ade290fb363b3731a536a7434d9a59e576d37e8eda6ddbae0ef5e9673a36a7e2676e4fdcfa7e6d79 cdf3c1827ce23deea0737c14f485f608bb1ce649fa8139a8667c93bcdfb78969f6f756c4dd2db0d1 8f51d629da533f14b26defd969b4562e4dfee592bd38ce008a3673138d0e338bb85c6c9b0eeed3b8 43bda7e2eb909eb8c22e37915307c8f21a3bc48ad7ddc4d8574fd48fa47a6246bdd25530c3feab69 92d7bc3634ab95b1114d4dcf603ecf68604b3db4fff130a55f2ff647fdf46e38f963954dbeeec46e 9c0893db8bb456d6e2c964973ebf8b8f5373f1ddbf9945bdae6cdb54b333159fc2602203c1b2d46c 7536f6156135467c613b8afb5760863a179ae4b97a195e86c27d4847dccb88ac46ca605e8ddcc016 14b8ff596a44bf9e9bb2ba5c9c4b3daffbd07b50287c77a3b44e7977e906e36ea9db633352177bb1 dd4e38c33b0912c93ce4bd7096b472e552f5ea22a8374b737a3fccd97699294c5c1ec12dc8fd5446 412baa99a116c9c3cbe0d236a251d0339847f88d691bd87c68f53f4e60f79dfa65d14fafc2952e17 828d9edb46414f45a293e6f79e91869cd39f6e8fc60b9dd0aa131df265d5daa6f0d45a91ab2c5a6c a1f0549fdb39a6da9d82a0d6d0d40fce176ef91dd6f688d5edad1ddce19688a363b3486b67a7e27d fcb4d4d4e73a0ad4e06992c75dceb02a1378c0bf75bc5f4feb15dd950dbee7b5baf51eb4ef36b50e d66a694868f4babdb23ee862d7d1a833a88ea6edcb74f49d0eb6e9cfcc6b59f555a03e57a79b5a83 b229c5e970a8524737b5a6db2ff79bb98bbf91bdd1202dabd213978b184427488ee6265a4f15b795 b285f90dbd32f1607d2e59ad8203ccf02a1f0d8bd6bf1dbaff99b5425dce71510fda511f0d39c653 1dec82153b830a89b52f13b2dca6df64b5156dc55a8ba595866abb6d55adc9434d714ef3a19211b6 d3a6bc7face40d5e0864f5d57d3676ad63b181461c27e9f2e13bac4944c1ded70da796ad53667c71 a5cc0f53a73a513580e627c34b91b7dea23ffea8f6a75f1427d02c9eb6f72f186c3c76cf575f62bb db5e916f3bdd5e93f43ae5b618b62b56fbd1624f665a155266415968369ac8dd4c506eaebc0bd32c 949f82bc85494586c74cb7a1e51a46a3144e6da95fdbafebe77d36acd38cf016a3dc1e1519a32a08 d3dbc6a87de44e5073885a31c685ab89ef0b5f13cf01e71bf8ca5c41f5f7772e308f5e5077ba226b cdf1a1d5e287f7ba880f32189ee975f2af4be7ec4fddd664efcd55e1940e94064c464da55103f236 ec1464a466961adac224a5b06457a5fe2310ea173e68d469ff128fb0ddb42e320fec1ba127d84a63 29d4c865dca2452abef264798897fb5b96cfd75a7d4e5d66b65c51bbe7b9a265b131dc1abbc307dc 66da3a4d97c1313b9abd4e627cf2436667344c8deac63c338cbff55086baa721fa68b3f5f3d735a4 b898ef34a1aeb36d048a779242337848e5cc2b5d37fb9982187dd092c8d6515278a13546a8e9b55a cd79cb8d5a7d6b77f915e21abc1c7d66dca649c7cd390f6fecce49e7d912aa55195d7f771982df7c cf9bea194c62c06cb56aeceb7cd558b29c27deeef385c1c39359ca689bd6de777ae678bf690e9687 35dbd36a1da43346d88fba58bd0fcda2ed3bd209aa2cebe6bd1e8893827a155e43f32d08e959a6b6 90175f5b422d73da94f826b521f942eace70adc64b6077a7a2c27667759d39c2e684d16f91573db3 d8b93accdb99ca6d085115263d6fd38f95e4d2d33e978e71a7e9a99ae712acf3cc7de18c2e25dbce 51c36f758571d8f107c3e7fed5ee7bfab1a60d8a36de9e06cdb4b2ee97ce0d7de72dc57bf49a0bb3 39d8d616413ee457af62c41704f4cdb5f65486dd6354812dd9428939d65a6586d8196cf53cda35aa 74f6d5addc3cfc6b80a930e5ce927e82f048f35a05501f26202971c62b54ba5d58945de4fe29bbe8 a41ce3c495ddcc8e5d15c3973b7ff89c3355e1a53d2ed723730864d0ed17fb59a97b057eb9e558a3 7cb3fda16ed2701425b66be1b59af26b66e4725b75b663f7a3d599394e2737868456efeab0b34e57 e9ca0daa8c278f5285514a34fd249b02cd3b2395fa74ae06e594ca0e959e0e82b20c5d5ee5dcb583 916af5d9208bb9ed9cf03793ef4e21e1cf098c84de1336815baadd5773de69b9936f75c3d1a89ab3 0c894575bdab10cdce7d71a9a82b7c02c9c1a7f5acb3d98f5fcb09238b8dd7f2361366ab5ef54272 4125aa50a70a33a946f4f45dfd7e0ccd6fb52ce5d06398aaa7f724953e64e363a357e5722e5e0e91 def66493c50aee13bb8cf32450258fe08778ee85e3237182e363e111e35dc2714b60f05e2031cb3e 95dfccea95ee7a82ee6f4bf35122bfb72b0cd6386bf48e4ab3dde1c669562de007b471728c8f58f3 a0802fc29b1943decf56259a9fe3ef30b86da9cfe97ca0ead4f94c65d2856759962150cee37c81f4 5e439c2cb696556207dfe23129a2baf841d6bf3d0dd7f1fb063bcd6b1146a55750e97a90855295f8 8c4a55f27d2b55cb73a4346a15aa0916c37dd1b757196363e9e3f7cae41ecc7ca0c883718fe40dad 3d1fd382d2cebf70e95619a78445d838735d3af3bdf1bf62bda7260dd8b64d655afd5559de8f77e4 86599ec8a2bdbc113bf5f621d0dd338b1fc61882eb8d368d9dc2a980198373ab744b95ccd228ee35 e803bb9e516e26e590773bc32142e00c91943e3c23c29af99e9ec83cbbaf20b3c79a76ac06bab7f3 f7c56e7ceec19be1fca82cfb3eeb4c13db715bbc0de5a6b6cd9725a67ece0bd9701cb147b1b6a5df c4715c2ea4d2260963e482e80e680f3f82c6012724e5128f71d36f17c0a8be932eddf84bb154759e 38fae8321cca3db42632e7977d442cc20ebc1ce94738fbb8a58a6b6f522d42705987b60174282ae2 335f54ea0a555c9b6d6a6e3ff1c314d657c128ca97fc617a3d5cf7513cf8debddabd9357b3e5deb6 2d39dcb2d5fa7357456b9e9c03f102c23cd0a94fca264a476f809d4d6f8ad1998b5b1a37a32dfab8 a48fe8d4c46ec8fb4ebf91f9aa9343d20513851b438f8673f55cbda8ac986e3c2f9d7e4d1610327c f885a0aebf0bd83a4be64365d3c9c7571c3f1fb67a9902364f9309660b9b3d4d34fd7e1ab179e960 c8ebadafeb44daed72e5e2e4275dc2201ce1275d6287d3d96a1439a7b26796bef3349c5c0703d42e 540cc4a1e51992b6541796f3e6ae184f44c2a20a7b11e48faf00eab045a810ccca7801873b5c7e30 7695dce5f919e62a2d659d65b5e53563c7fd265333cef50ca017f1d5d9b4ee197b877ebb40e65594 4bf65aeddcac0199b926711d83cdfa7de89d397edb790f5447dd8ad230296a272ec1a1cae9069daf 08b87b25bb196a559a40171396f7f71ed4b92e868543756717f0e961993fa9193f5fde15bf7ee8dc a88447d909897f32af7878cb081983482ff4432d9db9143ba966459d80cd24d88196d24901b88831 00060f03c0a9dd01b4e8da076c869be2b47d1d3cc6e6a7fc182e4e9b680087d54b8f8ec7fd8ee8f9 df4ba78ab4679306a3d06d01caae18e6b624216a2d38116ebc4b1e9aaa4716d40d37dd5c751e9f11 f3fcb19f49b56ee3b4b47fcf536b3cbb4e355fdc016c85e60dc0cb2188f14000ec4a7173826b0bc0 07f55b3106c0c77410c3cd0038d438006faa2680b7f819c0f3493ac6159ef4763b309a5cbac0586f c1bb8f79fa43b3f4f3a5ed9e727b05a70ff3ba63925d1ebb8cd87899f48448edd9bb97ec74c987a1 66f4bda6e587d78d9e76f1750bb49f4c1b20767f18633f03c82cbb8e211d63ccee0099a7b2319a78 8c430d204e4d8b11ce01b25042802ca17c8cb300106b660164624500e9e70b31e4efde9a4549f7ac 39f75739037a4b597df03ca7baaf35f56c6d667428575e93b9a0c633296652a0184a3df845bc32aa 3d107993dd1706913fcfd4fda60ed0a5a6c6d8f601ea16a6310437c6641fe3744974e815fe896114 63dc69804e5929863f00a8d75801d4bedd003a1bc2009d0b0d809ae81ca023e80dd0ae8dc57832a3 27568687ee2e830cd0f3a498788ebba9be0214fd111eeacb1c612751c7d8a05dc932e72a61b8b502 2a0def77483faef7d954a36283eebbd407a5333188218d638ce631ee3e28f9c5307121ffb220ef00 1a436662842a28edc5f8c5fbf3169482ee2bd1a10f252246dc294a6b77150f82660a9466fcd7803c 6f8c006391c6665727fb7d3243688f571369b57bb75763baaaed92a8e3ea7cf552c8e391a74aa274 c815b5d9e49efd70a40f82cad506d8e160273af4fd3afb019e4557007bb07efc2ff716e3f90178ae 52886112319e3580e7956e8c9b0df042f70070a8908eb1a9001ca81ac0537cfcde34990758f416e3 5fdad4cc6ce3f355a092d27bbf6cc7d37da169627c24aab9c99a659e9141212f4ec26ab84b148303 97c9d9d7ec0d1cca030fe0bb8e0788a2e3ff127cff8620008480dc62f4d231bc441d24da0815a35f 8ff1d601d1d11780e8e6cf3116794068750e103d6418e37100847e4600a1ac5440a8f86808f51a52 3fdc7dc4c4739cd4ddabbbef4ec899e7ed9c2e9a992ece5efadf6d2218bd5c91dccc363e40f73ebf 25e813760224dbbefdd27c13936f026bfef805374a01f2c0c080bcafaba09c2dc9a05c5a0c41192b af419909efa0ccf65050e6c846221013b72928f3bb1b28ab0b02945b9d36288bc8e257ddbd7a8dfd c9c0003bfac99f7613bf5228f923fc81959af09eb1e8dc5ccee641df1a46ffe339fe31193772c9c7 7cb3857f15bafb4bfdfd13353ca85601e59d5a807aa916a009681fc305808ee7a5805689eeef4ce1 53fcd8dc6100edb40731deee9f008c6f1d0fae71c908a55eed5c955ec305316eed7a084223b5dc47 1c957ee9d0ffb01dffcddafb6fc1370916fe5fa76f1231311fb4935272a0aaa64e3f25e4aa1b4300 4ca137010cc99f01a3d225c018590530b3c31c305b354c02997fd298e173f464e64ed5272f686f8c 42abbd92aff145fabf74e83f72ef9fea714992c37fa54bfc31f926826fe2ef15e223c96ef34fc0f1 80049ce2b701178fb080db7493ad08ee2c5400f74175c043e11af0c56f16c37f64b4fe8ae9a59bbf ab9c1516bfb36f0ff7c7473e8287b839e7ee2e7c83ef39ed86454a78262368f0a8242ed5eb8dbd21 a3b7700d98827cc5ecac7a096b44ef422ea8e1d9cc4ab353a44cfc13e35fa3903f568b47f1729092 c266494db324fb36b1ca26a9b28986fabf75a37eff27a97296139ffc92ef3e9c9c36ba3776ed79a4 1c35ef86988dfd159b88a70b39a7af67334d3f4e914cbe4fcc064f85368c668f1fad58383a58193d b8e53c76c88df2e5bdcfddabbb40bc7ff7a4fdb099696d2f6d7cb8897abdf5863997531e1f39f44f e2ed37e736696062904dd265ff4be67d38997b2ef290137e438c4df512322bf17ca9ed5a27663df9 3a538e9f8e3e3c34ced468ef330f6bd703eba91f4acbf9f6a20c175bdaefaf365167bcdd3087e1de b38961b8fe0c87d7b5188d9f2b977533ae27f8880bb9e9ea12d930caa2071f67ce006ffd0a4330a9 e5d79ff6138ac03c9974e296fd771ec2d73979d5ae52788aead9fbf1d30a41e09507b99df641a1ed 457e9536161c4ffaf870555d8bd709b772ab13d1f578bde12a9f91baf4eb7a3bc9b95d75b545d034 060b6c6b8c9c01624ce797de6831a74fb3cdcca20e27fb6965de36ff9290693db51126aedc9b4e72 9bcf318942f857e9b09f264e3fd1e727f1f66bee4c922cbaaab5c366c67863152967fde917bc5523 baed5d68763c2f91e5f1b1c0bc0038e4cecdcccd925b98457d1799311707b39fa657b6f9bb434f3f 138f9d8a6f4f98a6e7a13c91d397b6e5c9cfbe056da14922a906983beae1edd00c8d286b92d15418 9a1c611ad15c5f1996d8740d36cd2d13fcc9e54d5cb2fb7634d2ffd725cbf6e6268a9a338b406ddb a68bdf8dfca9c3e637135700fb496e09424bcd81ab0579f9c7b853ccbcc7c80e4e8f7aa5426e841d 4b457340964ac38b499487f4bdca1a16274983e7dce80e6ae9c3a45f2fa03bdd6dcf809e3bc85ccf d39f7a4f25d35fad3371cb26883cf875491cc7bb809534cf463e820b4d0fa413360bf02cd28eb8cd 5ff7ecc4e5a7a2e5d5e7eab853e87747417b6c9861af3f36c953df1e9a547f6144632d3e12cc703b 78dafd6f70f3a006b463ffb3185efaf5ecf0aebbcaf8a3e77c27d753511fd57cfd59d1900bd9e8f6 2ae361279c16b61df2b3cfb62f0ba9fac720db8ad6869ce8e58172be1a4913973ebf22e697d6f67b bfa7cd5f6e9f89cb0d3f16e4f60aa31e5c2f996458291bd1086506cf6951e87f9c4ca35fcf6454dd 6d66ba7a6e0bf49ed7ce0f7b5090196b7eaf686bc8a9b8e8f6e2555b271ca3bb0ef92c9fda66adf6 68450b3d315cb2d90daeda6a51543e3bd3501c8df4957418e5e2af9e2fc7d098a64c35aaa19d7bce 93e8e0c46eec909b339434d1f2c4dc75d42bd682f880144f0673cf3cfb8e704ee9726e9befa9c5e5 d73ba8f9dd2dd10dfa73aa8b5de64c675099d73af1f02eb54d6ed96cd3ef6dbb6589db9efa74b786 5acbef2dc569dd164a3a486d9b324e9c9bb9d33025abd4196bf896203590d7692c0573fb2cf5ea68 4ec233b5afd6590f576524f16f27f1178b9ebfa366eced814ef2932035ea79dec58886dd55ff63df 1c3db79eae34bf333f747bc4f0d22123e5dea65f8d4fcb12e48cfa5cca05b5969311e5e37570a55e ec504db7ab7eeffa6ecaa5414df6fa6643862e56abd1a938ba144c024bc23ee9557dd0114ff57225 cc88e6ba5111eed0ad2b4cc0752fb07bf5537b690b28814f2e8eaabb6905f5f96d5111a68d1e478f b5a9880c6fa7a4d6f720d5c9843df5319c75f1bc396c9b4c6dd2624165a17e73f1953a84ef9bae53 bfc8dea1fe90d589916af86f3ddb40dba3a274a037b884bb07ba6ee40fac78355e9238122b5de11e 744602c77a6e6db685be56ab5a8a36b3bc7467386eddbc199c421e4e1c04a46c8c199a60c3ae8f9d 258620ad592d5b6f4c8aec881f95952969d45a564157d1fabd1be64a7ebb7aba4c54602e7a4df96c 8ce416b2701a5d76f55d144a7a6b13d4cfa9eba54e69af877893a9b4583d7105612a48a8c0c323aa 36d7266c2df5dc487c43bd77f81cc58e396569ae581f8bce2c32ab67995e2aac325830ee57c3897e ac0ef84236868426883f66bdac15060b32e235bbfeb05a5629bfa90f1f50fcca6cca437ae8c1fc74 2a03e1a83a6d64d1842aa7be74b4a87efdbc69cdc4dbadbb129e397d27f083c9a9e68045544b4b8b 17ef96ce193e377d409cf2797f6f8fe3a01d41b39daa506382b5a132bdfe75580daf45a75a963a87 8a79bca72a15dba4e9490ad3683628ec69363a6668f600a30956caa2309cdfe5467fda9cbadd3171 ba37877c31cbf70bfb08d788d5e17b7ab6397574559a88e8357a9397294646aa27f08fc7b85687de 73de1dc11ee7ade13d5784e090f52f5cc4a275e9cdf4564a86c1f5295c1dbcd744b53c7fb095110a 37e8fb44ead193e66e46bd2ee89e12ead6d76441a5b238598ee7f59d72b6bcdc96b382968a7146cb 596a8fb81d8918cfde796d38d94ea3fe687846db8678a5e2213122e92eddbc175a6279f368c296e2 4b14599c08b5ddb9cbcb8ea67345bd6fb19df7fc2ba6303a32f7aaa7de725f2dbfb6a7ca75768e2a 55f8f1a6ef1199a7399646a877be4f5382b116a99458e896a5a03921d7ec664b2a85da8bd8b67718 d1ae982d027e281e013fe1778c319260a967b9af3434135ed3f10419e48666b465b581bb93e45ecf e2990e0b6560b5713dbd658ced07e2c3c1e3fe5c3d6b6c5727bb0c4193669562b16965b46196f464 8fad696e2ceea9d9b371a2046f782f2fcb6eaadc00cfefe4b69cddd36552c1448e2c3cd72da2ddca 8ef0fdadb9c6b5e5eb8e85f91682f5cfe566e9427f5c8c34f74fac7f55e2c77622bc30fa55db5e6e 96130b2f4223939dabfd417ea1abdaa9d7e6db33a88229eae8fdbd0a3406f2fe2cf28ebce4b6eb47 bf7a6e5dd44a35f4759abb7a2625667dbbbc54fc4539ebfb6b726d7e025291c82bb13df22fa23d35 7304c2fb255cf33f551c1b359ad820bb343032245625b336b9a2d112fefe35a8a59d24e479b21d94 25b43b6a29b96202e7a6341d3b373bce7e82a3674373d86f8d661dcd7097f536f8c86473a7c159c9 7c1daf42bd637a1c1252a3caf816aa94d865d57263aa0e48c591c789dc9d916da27db0567840585b 5c7b07218ef9b90736a894d3187957e192d9b4a952854c4ba8e5883aca768e0bc43e522704f0db3c bc005d01ce68ac0dd75de80a2fd25e01017400cd1f9fd672aa1aef6f4f1b534565364c89f6a80fef ae7af7661de5d69277abcd922640f5fb3bfba8adcc70cff69eba4dbf10a44b7afd7c93e854321a1e 4c8a431c2f94a7d8a05d764a97a8e6954c7770285564e78a5aa7f083b2f344b6436669964040cfac c10bf6d38533db865d6c22dba098bf2b19a8c5a758a8f8398ca056cd3f439b999a8336869a9f7dcc ee6ad24552ee680c860ba341fb135d5b668c2e33c9b65a393ee2e5be66a075be207e5704b5c20239 3167ece4d202ad0c88ce07c8d8e035534b2378a5a3776d374227d87d86bc66209e050aa88f803d7b 8225467dc099e72c536caad712b4b9e12cd41a0fd4c23e178d0b25a3e1e78fd4e39327ace1d7cb91 33de949e27a6b5435e7f8274fe88a1b959dd5c6cad63bae4998feb7235f0e8cfbc172e2be3cef32e f5927489c645db10e2475fe779bfd8bf33348b6ea9467a3cc2f54bb1894ef6420311065c075e82e6 f766bfb8abb4c6c57576342f16fa2b0fda92a7008217995b41cba29ffcb12f41f93ebfa173e745a6 91bd1d6b83cce3622d33bc84dcd29f6c504a8b6a5b49cf67929be13934ca4c277aca6e56767b2bee 33bb24abe33b7a165164d92b2f3b7652d4ee275dc28cd0aae83c609447cbfb4fe5b929efcb506f3e c1e84ea50d2fbd82086d0fdb068410b76e411b3e06f9305398e4fb2b6a91bb14b84d8e6e4b87cc93 956e197e33fe9e9e6907de236931a2d894cb1aad546e769f00352d07003a1473005a5d0400adf736 5073ad03f098c363daba5f8f3f19d7f5716adfef56948d764bf9cbf63273b2947deea8fd499748ee 6afdeedfd4f7c580e86d9633d45eb15a51554bf57c19e16b59964a4b197b0cb5322047f7d3759519 a53345cd49c9dda107bce3f2088afced018a35b1008aeab9028aada61ae33501c5b61d3fdb91e327 ba7412d95a6c14e63166618cd773a2adf39711db9b9d8dd5923be9c7fa32d01824b76937ba1547c1 5862f82b5dc227c8ea4371d2e56d0e0db1d172ea20d9823c28e8dd929c99cdd25caa00753800a3bd 44b38151bf0be052ce8c21ce63ccb731de6700630d10638f0218afc6af670ff1eb38d50130f1899f 253d18c0e57113c070d7053092bf0138dd4c5b65be7c3705f97a1f6cb5ee4d273bef6f326cd71695 433b77b5bde640eec6eb39d6692645ede8e55d4d137df31aa2737bb884f66ed9c83164564929dd88 0708e9d701524cb763308318c3698cbd0b10180e6274ef31ce3980205c72733982ac4480a0957e8c 7005909216bf20876100c9a7da007e5d37007e9bef18afe2b8ca1b60b89c54c100d95f3f89e7b85b 3b9f6fade222d835d865615ceb2ceb72d55e4e8972a78da74bcff3e42bdec3c5ace8e62b54de4ce7 f34b05a00c528fd168c518eb00c58e1640f1c22286b08d3139c578bd014a48458016563440a17233 866b02b4dcf0015a7cc4cfc24b0aa0693dfe0519799ffc350f34fed31e03c27c7f3639c39b8cf27d 3d5dcdf5aab573a62339d967d3706a9b7a16da8ed8ab7f97e8fc95c1f191ba4b214d5c080b149a76 d32bc7304049387641a975d3e27fdd7aa024c2668c6612725a12675e8ceb0194ea7814434f8352e5 5402a52acbc5f03ba0c4f0768c73fc127618bf83e3e227c87cfc3ba04b084ac5207e710a63cd4c11 4507bbd5ab94788e351ece16d4fd6cf82db021bd37d7357f1c2283ca529404e23cf44b6803623f10 f1b81d330d46760046344d80358d31c0f489f51bb3498cd302607dd48fa19e63384f800d402e864c c408458019626251c48cdb126043fd0a30b3548c71ad03aceb4c01a68def0093353ac6bb9964600c b0ac4cf722fa4efecd73dcde15e60cbf90bbe5f8ec614be971be080de0d12323d3691fe0a54ce287 feb11d2fe4f52f7c85dfbfc3dec708cf00df5700c06f5631c6930278a434623cbe25ef0c0f1059fc 19e384032267a9007f890b80bfa937c02fa5241916bf6c7a49ddbdc476dc71c714fa633bcea7333b d6b2e8f801566f6280bd50457c22e5322b6e7afe157b9108cfea25f8d17a7f72261293ef1fe08d6b e2204e9227580f00b249a280d4a70c20a7b01a6366fd0e9bb8a600b9b12b80dc367a80f4892d2077 855c8cab00c8bd331ef40ca6f1a7ee9e543f0457eeda2fb874f152d17f97dc2beaceab94599788d7 2f09fa8fedf88fdcfb53deee9f391389ccfa37e9778d02aaccf180524f5d40cd7a7340cd9173a275 6e43085097a908a8ab3406d41e3d012a0028a0d31705d099fef4570646395bfac9c020cabb7da5b9 4c4ff0698e50e0d2d3aa6473f1c8f84b07ffa7e7f89f926ae233fe77bac43f85df3fd5dbda560f54 2e9a07aaa0f404d5e28d00556ad506f128b30255b3fd01d5055f05d53ddc07d5dbeeab40fdcac040 b0bcd08d7ad7ea525ebbc4d838e970806042d69525f46f31ccff8e3a4eb28dff388dff9f38e12462 e24fcdb624d321c9ef15ba7bc06e5b79c07e5801c46b9b31e084d41970adf813b8e14a019cd39901 6e973bfe5746eb3f327afbbd1f6b6f92df206c1f1f590a1fe2a67bff1d8a20a66f7e59ccdfda5705 be06d546e9aa3ddae425e415fad2ff68ecf952d78433bdd21aa7a839ef84cff67c10f2c1797614cf 85dda111355f81f24ed1890dce155a3fc9b7dfc4dbc4289b24c9264903ffe992fd15d31b718fef75 f5ee16d3bd48397c46376418cd2e211bb86733657b272b67eec2a76a1e93b26ebbc1f9e8947ab783 4bd6ef879c597b05d004ffec917921bdc396ef9c3fc8a7e09f50040b2bb0de7320a9ebcf68365e8b cfda69258303f493739b24227c5b97b8649340d9ff8c98481c9e9df7ad1d2fd82f21436067f37bb5 66d6b07014836323506e5a73d70388ea93ab67676b16a2dec6428281f7ecf9a6c79fdcf1fa63f8f6 5abc79ceca65bcd52a677bdfead8ae0afcc3d297c2eb12f1326081ed70d8199406ecdc24236d168d 96bb1f5df76963a9241421f1cb4ee4021ffd4387fe1586701ebea75ec86ff87da05c2e673f14d56f a2f296de545ede532ba5d7e2e5915f35ee37c455de477ce98bc7f80b72f7d5452fbfe59cb0b5169c 01bc6fcc2fda5699d3e1ba3d8b06477dc65ccfa66d571ff6f46317d6d33ae0c2899cd53f96a7bc09 0bda6d7a49e9304cdefc442124a5c3be482cb37f1cc7899737d1d312dbe4265296ddb543b487aec7 92936527852c169897db38e42e1dcce9c3e73c8b27b677dba6a3d7f4635dc18fa4faca4ed3b37b61 e2c6538f496ef9c22c4fce942d6893a98e3b70be360a34541e6161a56b0eca9df1f032f2d643fa89 dc0cabe66383e772d84dca84f53fde79d677d4ad9d203990576c047f4319fe6694fd9a3b5d687aa5 9cb039e3e774a037eca7d1ed4c1d46ea4fdc9a30b23c89b32d682d2cc71d48f0464187f347d8a1fa b5ca0a27933c73d7e165283d867424bc0d8b15d383e7ac514824d554bbd4771ac378feeaad455d2e bebb3d4f93663d28bc5fb40eb522ba815551ba3d56d67ef0f5cbfe840c7cedc65b7add97578d5ba5 b2c0560492f84f6dfe4a16268d4feec725fb0d9d0b5a17de0cb5501a5e06be624423b763300faf3f b07977d8ff38ceb8efd43dbb9f5eb98eee36fd959edb7a9b9e8a787bcdeff9270d3985f76e8f7aa5 3aa185c21df22557daa6e0365b914b4f5b6cfef61dd67e1b6561f18f51f6c4b8d82211fc9256263a b4fd1c20a989cb35ee63bf115c46d8ce790e4d72943198bb5eec7fe652a99f76395297f342b5a7c2 555ef3354ad43a18277f27b7c1a0aa76b12bd5e984234eef900f6ed83679ce6ad39f96d3b2eabaa7 3e5793835a2becee8ad3496595f4412a3765e2a0c89e29cf64b54a3e64e8e1a10d7f1a928d0eef11 4985388f0f2eb5c46efc15532c4ccc4c1a6f261a23dee038bce8bbcde069499b7e3dc5857ace236e 9adf2dbcba413f1d1f96e133d7be5821d4a65f21da8a664fb2c5a6225ab51b1746ade59e35c5519f 9292de3d95a6ab415a3317625ff15e56cbfcace18fda5e03792ccf52af964ed7c345bb5c2f67df2d f1e21d3cd16cd5ee62055ee485683fcafdc8a75f317a7ed9bce1a9734532e3cef8731b5e56777fc0 3f1f733db726475a07f94c3ba111adbec39ac9f8db9655f30eaa2d3967c551ac4849fbd6ab29a323 d0cc1d163959256650c31f4e9046875e115260ad68097bedf8fa403835c48b0bba62258f8f04aba5 bab5e7de3bd5ec1e9be33f279fe1ebb4f6fd6bf8f4bde073eea4798ba13f92988e253ac85767dcbc 454d0ad779c93cbb56c6e08e839bdea40aab6e107946dbac1edaead399f414a739193765a46bcb2a de5c347ca3151f985b6b27f598d6b11edacdef94a35e06cd48bc2cba2fb192eda7054be9e76bcfcb bc54abd5f734ef1c53221f2f91bb9cec4f26eca61acfdd5ad9593cc8aa38c5a0d7cfa07a584e37d5 43508a62549feb0cfbac3bc3b22cdae9f68a1b07a3f3774f7a68c901d47715ffad05d4e418cf8d44 479df54fdda63c3b371abedd6e4a3d49ecd407d3b6219a7edb12ee95a123b0abe9ba3623e67ead76 de1ef98578bcf299e0f6e49a5c3acd6eb658916d0ddb65669f5e7e372499d224dbadf689ae5d39cf af4185465b697a1ccf4868868f46d4b3380ea8279c7dc528bcdd2dc1b7e656d16e4ef3e2ab3e32b0 54d59835114cdfa8c56cf744dd6fade77db951726a6dd4d0e8ac12af342e5f5b82306907726d6685 1d7e717a1a7c564c5b9c926696ec56663c163e897b663feb9f190c1edfabc7bd9faa924c0455861b 88a0234ae4692bb5ed50cf1e3ca1f897b12b3bc2eb5d4e07138a74f5ce8074a7ccf7d219ff3cbe13 2cb521df99d9d7a03d813379d9bc315c6db024f8726f5f66a1cec8050f75a11ff7f2ae38b0a5725a d0047e9a13f85cbe2370106cb65884317b8ce63966352cfb76951cf98bca457a26e5a9e8301dd056 3977a659c0bf28bbdbc952b5aa8f951d2fcd9633655e21e5c5f7ee935c6a4da8e1ecf97f7c9d6783 a2dada6d7fcb12256704455404032882a298c5840913c6ff7f91eadeddef3ee7dc2fa3abba821445 e1623a190fbb2d1578969abf9a6c93fc046c336d3c62d8af99536eb547b35ed4f2e8055bef3e9e55 d58139fbfb8a47eb78d549e3b12f80da524985da1104f3f2dd8b9f41d23c5391c91b5bc885235697 2e33bc2149b4648bdeb018ffd23ec561b668eab3f81ad3590bea607410d2d54bc4eb9774bc069faa 04d7a03c91dd0ea26f2ec0366b5a8739044b9fe172b988ee0c8f349dad8f1b74b6e12ce9ce86bfd3 9d89f89cba517cd2fe8aa3fbf6a068fe0830eadc5269f1578f6d7cac6aba8629f132fc3cbb2c4b6f 7ddc2baca76235d781c1f71e76f12ecd94ecabbaad6681b188cf31d1b6cd2fe483cb23ebeb905b77 719f6b948b5b76ebeb27b6694d5fcce1754598f646e2e9b3e896e9ac1f59549fd36654ee7cbb9043 b54b12ef25a7930a2cf8e450bb7c971ce4301f9f84eefb893bace5c94eefd236ad2e80b78df6163d 979bee759f6da87a07a99a4ae35111e758508290f540de993543ea85889c05ef528e5ff885328fb2 d51a47640b06dbec172c96d6da3de6c82cbe57d20c373a2ce973297da0bb44f14eddfa9d34d5d7ae 0cf938090aa9945c9398eca13101b1ad109fbf451c87cbe70aae4d4f53dc07ee059f5fa7f749c15b f6070193effd72753cefcd36f54969cd1c017f775a7da1af09ddba345faa37570e459d4b4f6566ba b1c5275c57f91a498becae7896597a97aa30dcf156a5cf23a2458b80ee50b76d6940e573cd39f9cc 2cb6a4625f2fc447a03e4479d620709f5ee770ed297d35d4d8aae87918b623f7a8c9f819947cd925 947c37466860b3610c3d1a97d9cde0c7717d6f07aeb338216debc8e7eae630c52bf5ea0261b5d365 9e2abf76fd8bb22e149779679be98945c6af718158fa1ed00cdf1c8ab468cf4b54ffb2d4c9a7ba33 4865716c131f3bd327ca17748cfb6a6e85eb5427c45683f513ab2b288a06417cb6a6a4ad8e5843aa 0b9fb0e906169a12c848762065dcf0d883056bbc834f14fe3d04463efb9e78ecc818b9856ae439e8 5ae8583c56354da56457ea587c75af758514522eed7677a5b18857a1d76b69949de9708b3d90ed02 25f30a4f7cee629e500b4a19f7375a1547c89a89ada24917c3e5f9f7857c748b3c7c94ead27bc48a 0a37f8a4f7d3b070bab11929228ae981deb1a0d7fa35834aa27787c0bdc841e0c8b4a071f3ed43e3 56351cae36a959bfe3f4a7dd498d1cdb9b97d96f5d6a0bdb989696b59a99f6befdb44a2464893224 f2a0b03b7c4eb97e71e10b4b56749853342b9325ac2660eb46c0a3dbd65e469b3254410e63b88e70 29ac059f76e55ea6e788a3f4233e89a40bcde1111a1f8f4f285564b05465d692c092be7dbd1ca01a 368600ed20a7185712a0ddd000a8591e826a700e86f826bbecdd9cdda20b29f9994dbea6a39654bb bb863ac9347fec12de6ac697fc491f295839ed2e29dbed8a27b36487ee5b079dc804f6f76a0d396a 2e9be9d3ac907ed8423e5df8a82a3451f51a945a98add4bc3aeda532e7700a6a1214002caddd6284 30c068430218936ac4188f01c6ea5780710203309e6c020c9b4f018667b603ba5bfdde4ae23e85db baa3d7baabb6f58a9fab07756ad0586c4a4ef570d5ea895da2849c1f54feaae41f895d82755a4c8f fcac4f75cc4c77f3190f2fd2295f1c536025c727526cbc2e006c02b41872fc8013ef3bad34fe379a 012cd00e31364f805d1522c64e06d8adda0258949ec758dd01767078801d8d36c0426209b055f3e4 f1e5dee1c7d5d1e0975bebcc89eb66e1e9cd1b356ee155ede2c65227b2fa7d924eec12d29b229ffc 76dad9d08322d42790f9d6403a685781d487c402dc5bc418a4e51872258663c4d877003ec446318c 758cdd19e023311563ce007ccc9562ac6d804ff46592434fc9778ca704f0dec60178bfb70378eb7e ef4be221be88c836cef6ae980a2db160efcd297c59277609ddc51f9d1fbbc477a25d76d9d93fd86e a1129073e939408ff559335d3a910540041497e4d0d3622e865b8e11360031c36c40f4f2fd18dd59 8cdb0e10fdc23dc6120184c76563cc74400ca45e8ccb1610c3511a104e4d0144231b7f99819c0151 6ea612717467d9d87e2d2336336adc5bd1e17d3555af7cfcb14b8c961347b16a87a234ad6304cf84 ce83fa58a90d4e4e56c3cc88cf9920a87c8a803c864a8c8f0ac815568d516bc718f67e5790d1558c 46e2b925c7db3720273932c64206e4543062ece3cfea692120fb2406c8d639fe46d67602c8eae809 c81a45b8a97b25e50406914a3ac7ad025d7d1b55ddbee8d7fdcb4f26dac51724d582b02d8cbe2f7a 31f2128908bc37dfc0f74671906a20371350b76e1dd0a961e3e72dea366fc6b875001565c780da57 1731c6bb18af1ba00e95748c2d0ba8a5508ab16a036aa5cc63bc2340ade78980969a346a80728bf1 57f68af1bbcd40e822e93596d48edb59ab84354b8f3e543b6a505849b52a93c239ef99e2123ee6d8 de09814954ef5d919b9a5fa736eab8f7533766d052f70f92e4f6e79d463fc67812e3b5010c269d62 38cf18771430b89a050c0834c0a4ca6e8cdb1a3050ef0d9874290be807d28af10c001d3e09409fba c54ea3297149e7d8caebbdef4fd3d8dcd1cf4fedb835187ab972f554e5ce1f98a760be09a1a2fd38 4198539c0166b11efe6a1ae7f0d9df48a2dfbf500ffe1e7ad7329374906d4539c05a4623c67b00d8 f6e000d85a360d58275d88b1ef02b63109016bd82460d552eddf9d6331e46f656cd55ee44745df16 d8780942433799422fb7f10baacf3fdbe46192f4f9eaac7fd58e93b8f75f9e89bf64135f9cf2d718 dd0fe0de300578b45b003c863663cca680c7e533e0b30811635f01bcd81f2601b1d48c004f1778c0 33acf5533b564b6db6e68c3f29559fa847f92e764759ca5b34e899fc9130e19d83a186d1fa3d6d2f 79e82482fe33d6ee3f15137fd2df3f11f04ffa3ba181702d944116bf3b204b384b9015f3af181f1e 64a58d09b29541fc7f5a3305b27ab100b255c849e6eed50fea1cafcc2af05dc9664adf028cd8a859 1d661c4ccad859bad2d0c6465fbfaad0ff8a7bbf71f3bffbc57fec127f525fa692fd95fa26816f84 24afadc597041e90ccfe0148bd320ca4395d04d2eed50352149e80749f92403a1a552085c4f08f03 a3d85ea78fd2329b1fb343ad6ae0c770944b63cd61e6df9de3ff9343ff6c4212f7269bf0df8cc27f 32df4260264e87df59ef6d0ae4d2fd096463c103d9754c206f8d1528a46a695020734a52ed95d25d 5050fdcdff2f3e7d2a7fc91b94f97bfe80eab7cd1d6ede8e11661f2f37e3727ec49794c7f7951984 d0c57eefe1f3a51c606717da7f9d29a748dfb3a7fc3a10c26723928f9f5658fa9122c0bdc8da05ca 67b0b532e23670b00bb2b9340d251135245284bf5ab2df0cf57f6c67323deef1a958d9bb16e84a54 3b98d51bd9adb72ef64bea9cc539e5c50ff71a1d3fe6757684da6bff0077c7ab5d2057d7db7d29bb d932736e1738307ddc5ceaf465236ee9fb3a7f44dfab11cfc3cb697cbdbc8406adef527d014f7686 5f4f5766731303af64db1229c24fc8fb4d53ffa593fd93a146b5ddf572657a8bcf599cda70384235 f208593cb7331ebe1038695dda881ba9904cc1da53a595724a5796d32ca82ea1deabb1d01ef7a6bf 52ae6dbff67975e7817aefcfc9c57d34b330e04f431309a6fc813dc557a4f5cfd81367d4e83960d5 91f27e4e7e6408ff781092aaec4fa43a0dffed43f80694e15387a707d8ae4f0327b55dac3dbc132c 3f76e5b8f0a5dcd5afbde9e7dc84c8d4cc42a04cfcf40e61537e07511397feb093f8ba59187bfc47 1ae7af70e1677418541a7e8690369c2a8c3184a6a23d801779cfaba386df0f8ccda94f1e44b86771 e7a2eb48bde11f0b42323a2ce9cbfeb464bffdd3a4cb9b186f7f02d48f157d9bf7fe2a3f6dccf695 ba357530d59db8943a1a7b9cea8f466271339ccac26e088d8570a0a784abb7d2d8783fc0c2ab1fd4 39d037f15ca6b76f66d19e45972837b42bbccb5f1af9ae2b75d44e345898ce7372ed27916a5aded8 d3ea09d850b02ab7755ae9b5e1a3d8b35636e926f8d1c926f6d3a425fbc725fb2b43dd31bf5db2f3 f2009e4eaa1eb69834fbe4a66ff798bdfdbd17d775587bd0bd74ec49d7cdf6fd4ed4b7979dfcb3f5 6dcb76f74e11d8a1fd990d6fb69a99bce24362956ec3414858d821125a26cf949b7bd76e379947bc e476941b6cc40be4c28f4bf61fa16cfc308967e04732f0ed75ce8dcf80fd5121242d59ed6dbcfb81 ae233d662790dd8b83739da88748ce73080a8ef249abb60addf5b6afdf1b6d1d052d6bd5b8b7ad3a 09baada0f5eab7c8f0354a22d50e326f323764653a796a6f5c46d2355eeb374043ce74f0faa876cd d73e5bb5599bb6c8750d0a2f99aadf317249413641d2dbfe49fbfed9c41f0f02360fcf3d662bec12 cf2d0f42e7e9dd1fb69a5a7cda3a3ccf58d8d6465b26dda59a7bbbcb35994b57341dc9ce1b97414b 31dc42576d44135b6fc850ab5e7ffafd56bd880c3bb569633ea841bbddbcaa33ef9dbeea70918edd ec44d96ae61fb9ca7edc6957ac72fc7414fa572829c8aa4e15279221710bb84f89499777f87177f1 3ed45627973f5ce35f9f47cf6c152c27d6aad65db4c883b133c3ae121a174fbc36a231f368c829e6 fbec591f696cbaf65951484dc571bc066d59baaad314a7af6c4ad4ebbc54d002575635f2aed62b56 c16cabe164345005285c96dd2a7329451b0f29c91459288edae77eb1c8b70fcae7727ac5b87f92bd 9934efbf6289af3d78009f957b8fb9cafb4e3ecacddb7ea5e6b54c726a9b6147ef18e2531ad49f33 6a5a9b565f7ed537aeeb2abcbfee74ecb43d6a41e77ad5c8dbe95eb1f287971a8e7640758a37b87c 99ddbe4f6b6529f3664b5e3d23159f5bba542cd2a58632b57bae025d4e7e41cfc9577935dce272bd 6855643c258ef2c17cb2cb9bdae694683aa642214b0f67f516d66f9ec34cb70f2e0f7be6f582d696 cf0f4c21058ce4358269b65253d154ad0aefde9616383ba7b2ef2d7b6a389c0c55fe339c945d75e8 97a2c5705d92d1feb63832fac762919c5f95a9357f28d0297ea2d4b3212aaffa1f56c65e8c9c374b b55a6e3f5fba391621be4b0ec9690c9ee265cf71a23b402c51aa5a63516a47818f336c6efc288ca5 41cd79f33d7e4a121d65c7a6dab503756ddaf66dd518a61cb7961e109a16ace05c85b94ff3e5cb64 a4963cddd48bcfb565289f9dfef5dc2a2aa33b05dfd17b05f8aa0ee4d5cc99c938edadf2e6d0dfe7 0e58788d97e4d047ea64244cbcd64c41942e819a1d54185b781d270ba154cc3df9d9ecccf395d6ac c557bce22c8617cc0ea15e1c7d0aafaf9ac5331bd99c2bb95abc966de9884555e5872909c4ae5ebe 5dc6fae630b3d433ce144a0391e294e9b5c316fc472f27afb15e51c6dd999e6fa6d746eed03eb473 ecfb1a9f33cbd781185fe44cc55c0159651fa8faedd9640b5de7214cc43b2aa41682c06b9c5be196 93a7c36194b18c17cea917b3fbccb3cc1e1f3bcc9e10162cd970f7d3f36aa48da00fa2f6db44a9d0 1d4c27a25ded4fc91657e800a380574e353de2179ad5be7e2f3cca1eded78b10a48b32a1a078ee10 bd59e98ca62429bbc995c5be58aa661f7ed5cc2a48b52d4c3aed9e502e2d26fc7c7758f29a871fb9 e54b8838acd14cb3c66dcd307b1d2f32ccc9b1e87042fab400cdbfd79e945b5538cab50bed18579f ba84d26e1239fbfa1099b27aef4c74ca9d89bac9b737fd0bdbec16d69946e9dab9551bbeb6a9746a d4a0340647a3b0f21c3917f2054aecafe76476b8db6485c973230b5071f3cd05786d7baef270f36d 72b567c6e6b055ce634da13a632930db3256e570a1c31005b450ac9094bb5dc8e43d4f99e4005e4d 895743b911c55d4413c5e8d624c6d2784e8ca953307ee1a13168b8c57a4f24d6df17bd3a907453da a48765cda8f0c2eaf3ebfaa9effcfe5eed63d2b838dba52cb9c94e4b921b541861dad2305e171486 abe744810d966599a5b07289d95fec2ac34a5d933ea5371d5a68df86d455e196941454bfafb09303 69fc240b191427c676234780cbb68e572ac511b63846670c151c0243b3ad0656ed6627d8e23c0ec6 25f069793bd334ddfeed5073fc26ad5a9625e64c5961a93abcbd7e74c6df9dca4fdef195659feb24 7de857a88983e920cb57610d63cdfb0563ac73c8d01d682f50573dcc533d0e2a53d25bac9303436b 9305b2db27c6de724694aa991d3e3b17223c531ec258f590caa29b42a38a36de2f0fd999ee11a18f c2f7821da143b28ab498f310d9e985f56876236d8f513d2b71753848a5a0ff521ddb75aeb626b219 cd59a46ee522bf5a2b58bfdacff355d1c8be83abcc6d982e419fde5384ea790e490ec6264bbc09e7 9ba711e3dd542152f940c3e7f0c7c4331db2832d4bc6044377930dba693d2e28f1ca4348ab31e1e0 63845632e757b5938e88eb269db7daa974be5d2fa6a38cd88d31f747995dbedb77a8a9dd1d438215 ff1e1af57f54c7e3e554ac114403ab5c07f4a334e39fbbc236b59de4dcb6ddcec6971e2a476a378e ea1b1e4c4c4a388ccf358ac233a1c0634b47cd631830cba8d18c1777241db491d6f0e9c121cef830 df33be8740465c4e5e690f7be2d0b3a7c990f2d83553d37ad54f41dbd723a592e97c6a5af65ba9cf 343f1ed6e6b57eef4a5ddc2e8854c7269ab3662b1b3daa466a7991abbbc69aaee406954fa9322a9f 0b4d835de672e9f7779d964d3b7e83b52c4e2247e5158aade22312352e5b1cd94b5b1ab1324084c3 5aba00f31756cb8877aa91f6aa6a077a9edd31542c87416aba80afa93467c240efeeb300516b0d80 5488698ceb1d20dae1fbbb01885c376284fd81993706bd5c06f23a95bde5b65bcdabddec1fd0c6af a1761b44509fe90d5c82e1e5a340bf477be9d1ac4c0454d83719e195938919792150aab686325734 0ba5ef62014fcb5ee52b398546afaa088146a7985277f10a36ddbf5a407f211e40d6f525404ecf2b 40cef1a622174802c867d9042868f800b9b12f8044408cb16f03e4400ebd437e3271871971ec2cf7 fec00a5bb8db94a9eab764d1d035bb5a6539534eec1225782f427258832ee2e4589971ad4a6051c3 6ca188a37a44c367ec94814aad1b4865842302aabd3309d07c8a8f5128c4185401aa033b863a02a8 b50f00dace2753b0d041880374588b3f6b9469c758af00da690380765519a02ed305a8d99df7cff0 d1ef7e32c6dcc1f6d1c47276f5a1399666ddfabab533f5d32054d57171912dd6231bcd4b46744dec 12ccc55e7d87389090962ba3ad0dc4a795f620036ace19025816256294f8183d39c6a10230916ac6 307b31f6738049fc0160eaf203b08a48c7d89501a635ba00939120c63503b0c2aa0430be9bd81230 21b5e9dd4ea975173a0e5776339d5fb6ae377f669666c8b06e40b98edea595869a6a00b9d01e2b68 6297e00ed5d49c7aa99e836d01aac292160a29bddb43002e577180b31d26c6290f700e2927c92d2a d562b8768ccb10e0587e1d637201b8c0c71f15e602c0b3b96a8ceb00e0a4730438a5c6df28c3e800 7ba66631daa1fb5ce7f69d6ae6bd6f1fa9eeb6d537988d09098e5f3736eb6f34a445b9bef56397e8 6bf1b918edeb17a677dbcc89eaa4e8209dc55b8332394b0044f94401427c718090282946bd0c08ca abc5d8b70041633d4064b4598ce50e1030fd8831c4008110b9242046660620d0e20410187c03f867 1fffb8d7a101f05b6f03f07dfaee8275efe26cb4ea35a91d379f13fb68f8abcfb66675efd3c42e51 08cd6a4e82cc29ccf1e6fd4441426b8e1e5f69270db146723b36a95812206b6e2e86978fdff58b31 2e1a2005b21943ebc6188e623c56802472a71873004892a663cc4abfdbc797252033f6139070390b 8857d601c4990a01b11943df0b8fccfafab4f7dae2655df1f6eb67a8ddbac31c75e9d41997f08edf c8bdb2e72c7f8070882e1a9d10a7786c9629d6bef5608daf00aa95d700d52febbfde6a29dfb75a46 fcd1e1f7531ec99146e9dc1c50723588b1bd01aa9083632c04402952fcf9cabe07a8a2b105549684 00459c654091db3ea0d2c11d900f95ecd4b96ac6667d3a63e5ea8f8c39c7b2ef9f897619fed8976f 00f9ae05b2ebadc231839ef32108227380076377029ae347eb57e37812ffc9d21bc2fef5d65fc8ba 80ee568631162b40bbe018437dc6d86280eec9b918a101e89696b81f680f3d01dadac41f6b775440 d7f431a0ebe517a04ba498ccdd6b9f8d35dd1a76b278523baebc4faf9562973147d2c85289bdf90e 496cbcc3037e1cc40dd8dd67dedf75e3ff504cfc075e53c03c84750cf71c23fa00e6a992312e0a60 5e86059877661e631101e6d364000ba43a606eb00f98088200b33f1793cef1f726d97fd58ec95e66 929bd292c9f34b2347ae94319c588ec17e2bcd7f05cf89e5f88f62e2bf7a26be28ee770912c54409 3a03aee14080eb7c18c075ed32e0a65c37c6760db899f5015c5f1001e7c36dc0ed1ff1972f8e18e0 96ebea9fce71f5b21bbecaabf93dc83f5f902bb025ae42e9739541eeda8f83f8e0ac7e6f51f2e8df ccf9af6aef1fc5c48f58e29bfefe67040c04293e3309ea9c0742af5a03421f0c624c8f40f0742409 88177c0908c78c17e3740642b86680b0761a3fb563b65d862be9eae85c9036bb69b649be9af46cc0 cbe8f55d4153cdc8b8fcea1c2775e73f8a89a464fcbb5f9c3cccbfc412895bf8ff06bf3f4ddfa4e4 eb0b0610efd01c48e9cd1d48749f0312533180c4b20b20151010e392079232711a547026b5125efe 3a53120786b87eac7acc043b6b583707d8d4eefc7efdeca59f3d92c4bd7f72e83f0ffd276efe9f23 e436f5ea9fe971eda8f5cbdfbbab1c403ea2112053e93290a56808e4e63902727fc901d9ef99401e 08b3ff199ffe1e7196af25f2864779657f4f3677b869cc6e015f5e5df7527e7b65064a78e127ccf9 ec424c748a74f279f210e6133e1b14142a3b1c3e4e191c3ff8f1d968bf92eedcce781fa42de36f4b 01bfbe1b1b97d4c76b8ffd44ab91e830890ce17b5be977a25952944db403632fafa5ff87f616a4ee 3e7a27a3dafe9ebd919dab72b19f6bed14557a8d50094acd23d4e6ecfd2afbeeecc8d1a4bb656676 2fe0979a97786eb1f2681d99ca749d3fe4fdd5881357cb4f57dc2ecb91725af8b276f757a57acac7 e6536a4eaea2c28cd969f6d461b963bc611b24f121fc24bc494b16def0b7ff544c2419ea8d741a87 73f7fd3e8523e4f83c68a105b67b05409b8bbe407e8f0e6b30cbf2b5222cb4474ef2574af69ff961 4479b6afa2dacc42e9fa3434e8e6cffcb08b55e84fc453e5db4c19e76f95e56894ef85c36971fb19 42f39230d011dcf6568d609be4bb8905e15f2dd9bf7c08df743209d582b0946daff3c1adbf2c9f27 e305ecf517f3a0d40a12cf2dac1da70eae5c262e2ddec7912dbec7f98b901a3d5d191e2977091b7e 0632392cbf2566084dd4ec4087d4bcb7d2d5a287adeb7adf24dc566fdf5af5ddd089162e7fd3a2ae 9bff309d687cb412fbc13752fd676ad84f51f6a3faaf641f265dde6deb956bfe98065679449cedd5 4361ca6fd6fad8637bc66894edd9c369bed31bf8c5f66800cf3a730f5b3696fda0d60efa64d0dcf7 2caaf18d1f7acca179711dae11b9fcb9f3eebaa29dea445e17e9e45f43ca19959659fb33bf956c68 9d31da3ad11c592b0b9c2cec74e25aa6e8365b64e4b59b7bcfb292aa6cb2897fb564bf21d577a7fd d1c92a570619f80ac678f53424f6c9f545ee31bbb0ecf2e1beda7585d0ec44bdb0ddc93ff61d67a4 043dfb335d0deca91a8c6d68b19ab5fd6ab86ac39b7d60d5c9f0d80aace8960865bf91aa250a9819 7a75c1b88ca7154382985ec3d38f614346cf42fd1964d53f55d91fc940b2954930e9206f7c341220 f0bb251b7c33d4d1a3ebf21da893bf3750fb33c97dfd0236e4734c5b47b2596bd5c8e62d6c972db6 4c86539b7b87d69b96906d98618f6b9a4e2e671b9761ce35c44f6ed0f0d4d2acfe5c3436f5223a38 d7a6c6e153830e3455d5b941595fb992a7630ff2a105c3159344aaca8a4bb065c6b4b9843a66fea7 cbfb4785f00d79bb2e375b392339b7b2a1397db056f5cca56552e7a86971c7b7e988ab9471f156b0 21be5678c32bf954434e055c7da4adbeaae3da67e5e76ad37aa0d4a06da056757a5fd357f6a5a563 978fab99397252d90fb56d85f94ceeaa53c188f26539af9425bc3d2b453bf65ef25acd4c82f84f86 579330fa4f9137b1357cc3144facf8b60a86436b55237b2d721f0d4d271bcc0df1395ad59f3333a8 7d96eabea662eaa9ea9beaad0a1f8a0f7dd5d63f3a76ae409a2956108d8c1a44c5921b8c1a8e9b59 5548750a65571b6b25afbe48866d3db7f77eb1c8084b65ea783745cdca74c1ef47b5822e2ffd02fc e62e310ad14f7c9a84d15369feea9bf0284c76a43d2d2e4716b6b974ccb0439986f8581bf591dab1 6ad0a6d2adc2fbc2f7d4a9051d62a491376256b1f2e8420d47e84615406657762ba96359ca10d792 5743efc567407c8a458ac928d3b64828d0b9cc1774d156e495b7ace783f1ad9ba7a0c232b7f7efaf 1c8bb679290cd4b6e498fb5912a936c96d62ea187b5a0b1e20ed15704fa34be4bc26a75d1be96da6 4d9635db0d4fa96a35159914f4554b2b6aa6a0e815e6c134ca97c9d32c457e68956424ec14478db0 af7c76fba1a232c1a4e03bc1573d5980afc15a5ef52efb7c30dc9ff2e4277ae4ac4a1a92c2a5404a f1df544e74cd55351b1dc95e56e6875be17955d2c2a84f9484627e60f39fe77534b3d4363b2a311b d2231e65a27b7bd5217b7ea947adddaef47d21dfb8b5b151bd84cd0cbd3ed7e40a732fd265b78c70 f1b6819cf2d91e0a0a745c940af0655091ebd2a0960f063d234fbe3bad9c55ee3852e8777a92a3cf c6e265bd5e881271d866bdd6eb228c3ac84700a9eaf7e5555ed56679ce3f51268794fc19bb86b43b 8b573181d972c32ab355d0ce54b88fb3434dcf66fbccb1cf769eaf15d65e05d37733dc8d4f8de7a0 b5a8c145c9d5e8dba35aee554dbe088a15343e72e619198b26dfb3406e3f36a81c9b6af192a319a2 7809ed822815fb6ad6db4e6ac28b9e984271b871f819761bf0e91eeac7d719a503bbb6074f16ff3c 30a6d92ce5e8c3716dd05cb138a53ab328226fcc94a7b29e518f71febee835c967d2ca6065b5e59e 30dc643b601d516d02a433e6b57fb835407b195471c11a56f828d72c29eda752a8811e993bf81a10 af95f92b7bafad3259f9b2c785523aa0f95935e0f9f439cc718b295ce6d034fcbdbe611b6dbec9ec d09acbd0dde194b6a5d49e3a2f0a0f4ae42618d99f6672c433dd3408c54e4ff1c96514e1e59993c5 a729c5c43fa59d37569654d9dbb283a2db1bbef34e654372560b48f1b1e3e3af7a6598fa065d3a8d af66aab46f3b45b5c6576472b6e5a4aed74f0b13c57ef0992afce0aa6729cd6eca2ace12fb3ac3b4 642b4b1fd75e81b63b7b8dbaa49e26d5f5c90e19b1e531991ff737c488b8ddf08f9787f1f84fe07b 4ec37c03ad6170341ea1f5827441b10fcea1f5dab381ae56ddc1683a94b55fae0e27fe81969bb2d8 0a81491943bf94aad514e2acd9f873592e78b39e52e5b55a9eeda72551669728af5dec074b96c2ef fd9e0c13afb169de3965a8eef58451e21c61490fc645321f168bc4a868568922be6ae3d3de7380a7 617e89e94eeb84aec10542714de410339c6bf0219bf560767e3d653ac88082d985139ffa2a627f94 b6ebdf99c5fdf6ebaa77957aae6cd7ce763ea91d1b45688cd430bb12552e04139426de7558687cba 66ae332f2ad97115a5933a0a1dee821b196dd588781ed50f51e42b69fcf3e911b86ace383c2d5df3 d822437d6fc7c6909a64a0f58bd945f16938479a3479800f43fb93e9982b2addb32105ba7ffa0e34 98e502a8a0a532d0607e2d420fc8b286d59a6bf4ceeb57adf3711a5a3bd84c94e6150fb38d69b4c5 aba6dd7b55dc46eb7b0894ca776156206b0f272766033d9b1a34b25cbc5d08759d4777a278c42f78 da4d3f31fd9e0118b2c8a168032d50287e6e6791667956840ffb530de60a889dc91ad228dd8b9c0d f4d02f1154d848686a22aebef5d1546ac0b740e5bd5980caa7f701f36233974a75407db051fc56af fb16cc8e5aefd5da4df45d496ac78d8a843355ea7883d47b69782efaf7fa52de87a5af3f9831053f 7d28b0bb704253b9887f638bf7eb7b57044ac8e32bd21cafdff09138a661ae75c632d9d18d49f789 8c043dfa391552e0a6997a87877eaa9c4516603eaf9f01cc9c3200664d09c01cd38af15881f8f800 00167a3280f17b6d40f2a1ed4667f51b423a8bfad6b40e1fa26a3e4a4ab1913155413f3259542d94 8e51b1aaee77723bbf984a03b4eb08fe55d7997074e449c5d7217473c1af99f32e17a6fb0e17414f 50f8404aa59c494d8e2d32551e2eb360fe39260518b889d601dcebb8315e3e80e7ee15c03e8e0078 bf2f00f8d0b2017ccc05005ea5d231ae25008fe5a6c75c9e5d573ebb1da73a4eb72d9baf18e6703c d4eacb609aff6597e06aa9622d5e367f873bda90b096060f62c8372a669deee13711f75f7518b14c ea068deaa9634aa51e37a073e727803f58fc581f8d8c31c90204cf9463544d8008d70140b2c53540 4a973b40ca260110152ec6d8246d3ba4d2d90344ab620091d94a0cbfd377b658bf3b3e077d7b3396 baad0bdfb38ccff858afd705b8acf3b7bb50fe0c7668111b6d5e32df9d1e1389307baca90df29d3b e73082d0d14c0e7945a9f4164deec5458ee82346198ad1c7631c398084742146b30a90e7d501c84b 9ec4f0770045f3ef18210350aca50114a73d8082eb09a0a9350590c86b00e48e79bd5e2f3feaccbb 97a14d8ecdef92a3e50e5fae3143b3d68f5de26ae80515540a8cb24598744e66f58380f9bb111d49 8241c0d8328fd85a9d80cac4eb09d06de51ea30f003adba1009d67e2c79f17a418bd728cbb09505f 4eaabda83ff5017ae0cf31fc34408fa210e35c0768d89b00745d8c003ac139804ee136405d7bea3e b643bf034f9959db3aeda7cdfb4a1a2613ed7eec1237eea2952bcd78dd709a5d2051bdb187af9a85 6df44714401113dd2fba727ad4c4098085a737c04e2908606b0289d1600136ee8931b60ac026782d 46dd0658d71f026cca07318611c066241a639e07d8bcd402583f3e72312f48321bacede50056b37b 00aba39bee6776de38f58bbdb64e6f6cd91ce67ddf8033c8b8d6f20aae96579846d1e02bd9dc1073 5302a19cf6f460240ef186b635e11c592ea490f3950478d4c3628c0980ef17497d148f2fbf01bea0 9418ba0ef0816bc6b8b8001fe6e700b78707808f88778c21f5ab80dc593b00ef9a1b809bf145075e 898fdb1f1f467e7ceaa6e7dcd1def18fa3d51d0c0f4d65cf7c17508d55cf595643bf3754212fa8c9 17f6cc8930477e5891ab6ec9ccfd3944ceb3b60965ae910c888f2cfc8d8b9c8d61c61f08466a8cc8 f855419ed58631362b40ccb9c4da4bf48769407804ffbb805cf40031848e80b0030c10f5be0688d2 600908517874d079fb66334e256a458fccad098cf9e55f7609e9f32cd33c0b4f5f94caa60234f492 031af2ad4d0d908fa808a8cca70428062eff7a2b010cab807c32d5188605c870d28bf19802f254da c5583f00b9a1d118b31c2003c98811c51f9d75af809c9769407a5c72d737d9a6024056ef19c72453 e0a7763ce8f5819911cff7c42e910cb5cbdf179da2b06d07043d5640841decda2a3dab2dba7fd58d e9ecc6fce59948defa8d24b94dde216e36a0496e10a3bd88713a029a2a7e621c684067e4628c4bfc 29b0bd0434927d01eaf5ce02ea1d9f23a84b7004d4764ad9876e1b6b67bd2ade7ccfce709ddef1e7 4ae9d69925aa636dda14abe12ec73c1af1f3c4ae5438674a376bfa4b70fcce757e2926feb24bfc4f acc780b1d3ab18e625c6154a64134940ec3c35c074ba3dc074b3bb188f3460cc5901304db70f18cd b8269e09db5670deca5b63e6a773fca4ac7dd23996e6e785ce46e49e258c057867a62813fc5d37fe ad9848e2deffad98f8e25bfcfd05770338f0bc008e7060c091b0186358071c951bc5b89f00472f08 c031960638b63c011c97cc23001ccfe5db2eafe64c7879c6aae2331f9502a7b9c87db66e9b173293 0259af1d307814bcaf80ed1517bf22e844319164bd7faabdbf9d12bf8512df4a6d8ca441fcdf22e0 f8db02febec903fe51690201c67d20c4cf974060473c103823fe3fa5b40642914b03a184949ae954 95f9d539eeca07d98b8ca160bdda35aa9a72bf061864a0c5eb84bfa6edfddfb8f7af48f57f8925fe 6bea9ba4aecc404946c825d3e3aacc2e4614bf656f8a401c8d3d20ae9d0b10cf75068817be5eb7ee 369accdd2b5c87f422bb9d0a6d7a76282b68efddc4812d972f7f758e7f362cd9a664236a672aa9f6 fe0cb04b1efddbef4de2e6a4e4fbaf11727f32dfa4dffb7539e4e9e118e4b5de03e4ad8600f20bd5 02f987b205f92783c4789741feb5e8ffa75fe0377028869f7d8e5290fa98c2987187cda77d0bb84b ffda8a82f1c57ef9f3b39bf296270fb636e1b36eee42655b3f1ea7b47e3ef8bc743dc0ae70df05f2 eb5b80d9ee4ba777106ac7f4e652df611b71fbe6d7f99028ae946ba5b92c3fae8b850ec6e93f3e84 4476fb93a14ed5e5ebbf6a6fbf37f2472bb292baeeb3490275e14704778a2a68fef83176c503dc31 ca3b7284558250bde81b179dd6d6913132d6f9bdd75a8d58d75e7e3a566759be99bd859f37870b78 644efd3ab096f3a0e2ee66fb9a779931c1014cf9639a9ab8bca725e3a97aad45bc6dc4fb4785f0dd c09f20f5ffb664937432c95013c968f8d495f501b6c3fd9699c8c78d8be0d7d5b3797ffe1e1dd64d fbd8a485ce4dc820927450379819b3d6b8a94394b3537e57cb4f5c4653266268a8bfe787b5cc91f2 e83ac3a932190c7c75b318c0cbccc9ab635da41f34736a6fef80e98f0781b94abb7fb564bf7d68a8 aadf7fbabc4986faeda1ae3dacac2dcb67d8f06bcfd09e93b3bd370d6bd3f1c4a55c7fec71ce6614 2f6d0f2325724ec3a9ecdc86d0d87a0cfc92f31ec0f376ca5b693dd8c3561dac6fe21d2ab9f1bfd9 e37bcc7190731d3ebec4bcb877b32b3ed87e27ffb136cea84241f66705556c151f0efe4c0dfb69c9 7ea3f2a47f1aef3edcfd93a1fe04a81723c38ef3e1fb5bbb1e7e7ab7f2c05776556fa56ecc7e505d da7d72b3e9f52c723970436b19ff164ff37805d9592ebbe26dbee944fdcdbe937f2e4367545c5e9d 22383eed692582da7e2dc2daf016e3ad3a5d2eb5027b6e35f73dfa7b0f7b9379a63266383a944d01 bc1a0992b6ec851fecd67fb564ffd1c9fece505b2f0ff3d574cf2204dc751881edba022b76bc1c2e 3b2325fd9750367e98b6afbf8cb68ea62d6b6500c7aa9348af1558e9418b3ca5274d2b9bf69bcc4d f8c729abbf1ad16c8036e4cc5dac8feaa659fbecb0454d6521a4069dfaf28f4bf68b1fe3ed5732f0 4faff39b4d0ea141ebf1a728db15cfa7833392d7375b4d0d9e6d1deea7e275b60db74cdac09b7bdb a09bcca5c6998ea4654dfe6ee40db750530cf1dd521b5ed9d0eb4fdf68d4477abf9d44aaeba15b83 76c3515567762b7dd5499d74ec564e69667e2754f6e3aa5961a1e25e0d17692429c8265847b59a96 a88313a7c44f0e9d781092843769c942b3e1d8aa63b579d2206ee73766d8e5f7862b4361231a43d7 fa73f67cd48b99e85dfbac005453b1175cf5cd3b56d56940e92b1b703a169fc2b5c0c50b95fd0051 2bcc9b6da84eb9e8942fbe3b294b48b82b7946fe537ceef7df3bf28b456ede553e97c64199ba8777 829f30fa1b99ffe9f226b606fb333a8ead550d735be4de6d9bfc8d6a37a211deaf17d3a7614d45b7 d3aa4ece7cbdcef6565ad0e9049a991d7f7381cabe3f38579867efa686a3f15315c0e053762b934c 295a4eb0928c2d99e2c83c4acab47d2b2bd085300bba54f5e4d5601bc858bc60cc07335cce53994b 3f4fc1f62eb75f758fbfb2d3df61f4b7441e36f16db22f136143d362d596213e06d5fa4825d41ab4 3eeb7a9d991b1a79b55b15e661382aff51ba655755bc52b4c88f4a322a4e8b23435c148ba4b251a6 96b257a093722ae8d97224affab5ef4b1132f674903c053c266755f6b214aeb0ba24e02d4f749bef bd28317324eb759a5ad6ebe2fdac2cea7e52844fa2fd7e106151d77db0077b8664172db3fb748d4b ef50af17a177a90aef4449232f4978af860337579620bd58921159553e3b44535406a9177c276d16 e02bb0e47a0e74f2c1e0d5cb93efd73067955f53293ef0569280e27bd135846b36da57415666c7a4 30eadcf3fc27921bbc2a0763ce9f94bf57049cae322c87a437550ec9709de4ef7c98ee91999e0d79 5047c94cefed9abc3d366d7eee37067dc9ae2ed887aa51d88a2fbba5355a7cae1a98326de6c882ce d39c8c450f214fbe7662ce2aede2bd33df7cff3c2501de5444b7beac66a3edd2c8caf4a62d8cec63 8fff5c6ee378f9965971fe301b72f0a7f966ebea866082255d60286c60d156935ad02c7df95061e8 8a94e344da7840bca91f0146370a5167aae15f8ba26572e9abe90aa9a03e4d85233d7e2a69a96780 c9257977200a7ab5f3ca071ef1ca59c54f4a1232fb8c286153242b532e2e8cda2e1dafd5463c3fed 8e241e8a46054e97a72abb1aafea2c9e0adb8ca97dbed737f4fe945fd26ca97fa684d60522dd5759 20eea55d8d90fdd2087f55c125be125c5378f1c3164613b192f5c8de86ebded324652f18196eb551 f9690c0077a8f91136d7f68353a71c15877a311515b332c1edbe3d1ba9834fae596ff30c85a2ec5f 7835ebdc385db39eec2aeca5585ce8c1cc16ac49866a1e38da7abcf2d4a98eab9470950db257715c e27edccff137808f78a96902ac62af187409141d452be721d25825f77b221bb64aa1681b144790d8 cbf7adde5bec16d265ce46190b6f71d541aaf1c69b971a0a69ebcac9c6bdd24b381b0abc77bf5dde 2c21e689d48baf58d33d8b1f4f6ba6895d0f745b3a9da9d3e0145159f4febd60277b6e2643e6d41c 490c0e151e7f173a05bc743ed4b0b94975b0ccbd3e456bd5e0806c2e028077778b81991aaca72f94 d64f8bedc7217d613254866f8de3fd442e94dea9cfc99df1bd956d1b8c4f37bbd5c377c046a38ccf ee5503eaef2a9d637d522a95e87601f7f7e59ca0b4d96c319587b8da023ad3c7553a206fedea8a78 74ab7bfced57cff8446fdff1d46908b0f9f8886230f18ef75794cba398ded410e3b4b0e07d39fded d9641c96dda42f63ef0145f32b0ec96cbd981a759e9d54511c6e5323b78ba49ed76c7e50f7d7a55e 56cb299dd461986b93da934f3ac70da8013efa1eda85eaed38f28bf352a52b3717902ef5ec9324cc b2c36fe7960deeda83ba8ca82dfe29d30b4c1bed96e80a9c77686d875e9020cf3d1113aea5e1bdd3 2761ab781633027d2ea6dd11d180ee78a307c9bded323586d92b98e14d18a4fb201f237eb6487b76 00d2ed72329524ddde0903fc0e34f7d66fab8e9f3e2b962563a2e9d902f5fd1132ba2565ae6afe18 ae7f26c53154cd94724fa928543628cb36cd10a27af9e90557278d3512ccba7378bfec6e60969f1c 33823af92a8dd2ee61f786ee85270c0d309149bddc969c2aa5975530b3530e487f2ab318f733c8e0 0314640859011912eac6381e40261d5fda643244ce6b1ea89a9b6f8d75077ea4ca166b9565533e3a 7c5d7ff5be3b4d67ddcab3fc6298bdb25abfa6f9d0dd39e2531be902cc352596010a4e3eb4e703ab 1aed4de634d466d07d002da0024a6e53af73f99c2a89b50798f92e0432ec8a00990223814c755205 991ada01197bfc5d4081cc488e625ce34f18bb659099287d9099122790711f14c834ab4affb8908d ee48dbd6ed359dd35b27bf5f32de9940aaad5f5beac72e5162b493b26e9bebfca95818882f916f09 48265d661cbcff7db198989ca0374a42e37d3a77d1fc54e914f920d3df6f40c667e25d152f6f4166 1fc02073c01890b9db0500a7df0d00671a7d00336013c3790058969818910e606e380230dfb80118 cf7131b6dfd7087a97aadefa996648d0cd462bebef34a39cf9146a4601e7b56e1145cba5dbf9a134 eee121df09d773f18d6e7adcb696ae5077bac5e2480a00d86107c7d494aaad006c405f28c718dd28 46984a7268972762d859004f1f2a8067a51680b7a771fc275fdcc5b87c00bcb7e2cd3af0f518ef19 8097eb278057b31c80c76ce3472b52d105bbdda267ad6694261a06143f35d68cbb93d7c455f31bdb 9521a30c29c6ac7ccb77fde2f6c723cc19778d049f328736e9179496778d1020ed5600906aff0090 c231028892f9c45090187d26c62d0f907aa91a63dd0148839d01a4b33a01a45bcc240171379200e2 f69b0069969600d10800101d290244f6daeea07de93b4bbdeeb69916e824b5e386bf9bd713bb8426 b55e6239cd1df11f89f0a8940e7863adf5e8bcbcd571f4c0f37036d87ee749a72a61e302d0267a06 68858b009aab7c62f46180d21b12a04c261ba3528ab130002a0b5e8cd10aa005f216c3c700aa2805 8016e33f3254580400253af1d742960ed034d6ef4e143899ba8879c1c872e6eac07cb95bb7512dd3 ed1fbbc403aa174b8b61969585a3fdcefa8ff5861518d825a15d4747420312a0f2b9990698337e00 ac3e7e02acb8f8c4b865009625488011f1918611bd440b8a11d72ac048c98e311a038cc27631fa2f 80d10c1d63a3028c69f60186b04780bed3f14f78fe98000dfad32e64d77cdb3c90fe4fe778824be3 1fbbc471f5b02b4363af2bcde8f83d047eec1274bab4a2c6aadfc59a784e4f8f718703b857817fc1 2a6400ae9790186d12e0f298fbdd42c6d5187513e0e8aa07708cf463f44f00c73128c644003891ab 250171ea3a025834ba02ec1e7f73ecd8ed006c950d3a88bd0cdac7742bf8a91d9747fe32b14b54f9 53b657d6d6f54a3e4ad5c82c7af322a69f7d2f097452efc27df4ac810d2f70807001038829c226c9 6df28e71a70161223c2054568a619601218d6a31a236206871f8cb81cc10518c210a0896cb030259 3701819af1cff0619e00bf4212c0b7eff8e79a6f4f4e23a44f6d5ebd7dd7022d199d9f7eec12e6d1 5c5546c765e7c72e0105178ce3b7d495d4b6b5397229de9dd42a2b960139f465402e83c2df48fe2f 81bd52623c3540d6f9660c2349a0c8e2660ac812b78f317b0352a4a9188b122025b503c81c1c0092 0e2040c2c322205e9d1120cee6df9de3ef44bb5f7609ff3a2f359067332f6f881c7f10a50c35839a df0e147a22f713483fa0cd5f65e379adfa5b2cf10f76addadff8997b37b700e5670631aa4b4079eb 13a0063928c69607945d8cbf95fdf400e54c8e4951b9d3c2016514e2ff56051f501519b6d97d16b6 7a2702f9b14b2443ed2a2b7428df04a1966df4f21cfda6cd0fd65e4eb6e98c72ea03baf3b67e558b ff282692c0f7ff2a26fe13f4bb3dfc6d9cd06f31ae487cb0e8528c8f0198d4640a18a81a0126cdb1 80c920cddf9e890fd93e770f4c6b382e100d12593e12d5f13bbd566c83e9884b8f2b328f938ce3ad 46f396d65acefc57e378301bfc64bedfb8f73f9d12ff200981ff859e152401f11c7a00b66fe180f5 e0428c791bb083ea0ab043ea13e32e81789ddb01ec787e046c77485b910a04a376b133ba8745e79f ce71f948199c189059c2a40a50063a8bbb3f75e37f292612bbc4bf9c12895bf80fbe39f0bfc360b9 fb007c55a401df3eab80b73b3dc08f8a07c08f1104f09d30b95d819f6c46800f6677c06f3b82a9e7 09f2a7765cd70e9bfc6814757967f62e93f5559a848b5b34fab54dff2deb4dc2e5bf5ac5bf5c1249 b5f74ffafbdf82df24f31da1dcaf11726b770ab2e77204b21f9e03221eff1d88f87d0dc4d2290dc4 b25fac9d5026ad2e50e7f4d339a6f6a7ef2b1ed4a2b31791827684fe63dade9fb8f7b74cf86fb144 12f8fe3109ff497dff6d1216124b7ce274c8559f6d90b38f5b905b2d61903b0dca20f77646208f19 77902f6a02c837b2adff11f32630a9c734e3e6ef5ae0a8b780d3eb57c6135ae74b09f90e0a387999 7b2f1c6187c1714a2dc6079feb4df72bb131df192fcedfee4bc432082bef75c02fdfbb8d8bbdc3b5 47ddaeaba79d7a2f3f2e0c2fa101c32ce049ade863feb93337d1c3ed4786c01cb26432322c71a9aa cbd77f2bca263dd4df55d9d9edc20ffba9939736d0e39464c87dedba67835095d88d8b22c2da23e3 fd3062ae85e554581797506fa12eb4c7585bc0c3c5374ff36b9f99390fd4b1352717e3cecc4267de 3434d6d3c9c5daae27f1a17b1ee7a36c6a349217d9e167e2b94335ad9d131542b2758955f6af96ec 2f29af1c2542d9642857fc30079fae0f83b03418ada37ad55f29476ebdf0a5d4deafbd6edfd1619b 68c6ac26cfa9834fc1e4d21ca527e2d143c6913d25c6f98b478d9eee941b29f75176382d8cf24368 322d0d746855f5567ad8f2b00d9cccfa36c9f2aa67b193c80dbb65cee5ef856e92ef76c5f7609954 6513b1ec8f0fa1b63b9dfff2217cc748fd08656bf175c8ccca20d5ef2bc7cd71645d9dd1b373f586 9ffee9db201e42a3f57ca083f5ca5b55168157cf6c0ffda0b63ef54d6c7bededcdf5bdc71c162f37 6c1f21973f9fd1ae2b3ee84e34c8889dfc3b5b768a50c5b4a7fa7ad4f60de1d4860f8268ad6cc849 0685b582eede49623bcfb26e46d839264e899f72e73f196ad2491ce7430d194e730a3380a798e061 0b4cee931bb8dc63f650d5e5ff5f7bdfb5e5ba8e23fa7ed63aff50b49c25cbcac139e79c73ce498e 3377eae17efb252557b02dd7de3d73baa76fb76bafc5aa0d91040902200882e4f2182f14d9633aaf 94945cbe225e4ab963ed58cdd5dd9746f6d23aa29dc26c2b80f5b3581f1b674236f3223d4810bbb4 6dce9c53093a644da6f91c935856368104730e96e2453fb989297d2b1fab4462c1986cf306d478d9 afa7b8d42859edb60674305e75a0a273d2571f6a51296cb27990af083173aeee0ad8b32db868c974 431c93310f39211dc53957da3675a13b65c5608a58ba22c9342bc693cead379dc889de7c7c530ba0 0b65c3cd8f0b65dbcbc86534392297aadf81dbc3dd4cc91d366fe4f2c775b24756bb4e1625da8db7 b7970c54a286e3467b30ec1a254bf7b258c73a4d0f2287652ae1986e9369bab34f2c0b7de4b64b30 cac0182fca036b4c6974f1986c6891d163a74b473de61617b90cc672c46f1f78c2dde43818362f96 f1509439e682e392ad1698d5987ec079c96efc39bf62f66dfa058f4fb0c79a5e65ea397f45c9a200 7fc48f5f0e3f9586c51c09c61f4ede6c351db55e0ac9597a504e30bb4a33a6d4a39de8b1ed1f442e 7d611cf1db8479b89b10d661c864bb5094a62039d6c22598e0392c48282e74c62390965db87fd9f0 527ed610e07dc560d2e3ad44f311cf7132cc793c4e63cbddca45976e0c2d00425237e0329ffa7d79 d0a47772d467dd7e7346abb1bcc855aedd831032b9cbaa8717112dc719e3b18a3b13895c7a623cdc 8dd3d9509432148209765d0acc4aeb5ac07958b7fc39f7a2ebdbb4a6039f601c8dbd4a6f31f7cad6 c5da538f2f14f765b63ebbfd946274750b182e0f2a3656b69ddc5e29e12d27d580cbcea12e5296e4 4ac8c56987c03a1c297e331f7461a28cbebb4f9133fa339637358e8f739afbb4eeb7fa22d8b02087 a24ede1598152f5e7fceb50cfa04ac1ef6ca961cba13d2538fa593eecb349d75fb9d8982ab9b8b96 5d21365d97a10a6acbb643ba2f25dcb989386b5556c2b257d90bac7562e48b28144099cb2e4ea6eb 09b65e245acc653fda33ad5a4764fc1e570a26c37ab38837d1d8a8defd52da9358e4655f7e90b190 a95ad279f2a462d2c51508776371299860ba346ca0c3e1553a73d2538ff658b79fccf0ae101396e4 a820baa47195f34ac4990b8a691f1d11965d322eb01632cd17633432073965ca5539d9e96ab3f55c 78c4b44ac915831dda673ae43110d4a01d705176d33ced1c0fd203672226634e0781b9c8d92c9e50 e5bc32b416ce45ce3ed8a92fc4a5a6e2b99fe0e46539eab95463a168d92d697e688af0ca66afd1ed 772c0cf2a090c5a4841cb48869af6417583386f3c5c80512687c6138d971e4d97ae6283397f5d1cb f8f96390ee56b1386d3edbb254d4c7579de36ea4e774589a4b321d3b5c1ccb991badd61c2c35f213 c582ab44080258e0ca7e82e3951a25d581c76d2e27cf8ab92041a1cd9a137e25e52c8767b1a34b6a 478251b6104c1976213596d7ddda5a31d9b63b6c440aa4d6fca62f6db94a8238b01e6a73542388b9 c199ee9687186d3e0e2d54d4d3b33bc7ed1ee94c04561c39ebef5d246537061d3974ad6d312b1471 6593eee0b2305bdaeb35ca60bb5ceaaccd1f70c6acdd7ebf650d4593076b2866c06b817adf515ada 18b4ef99879c66ce445bdd4b32371d6e629e7061108e7a12d5400ee312de5aefe276f53d6d8738af 90075ec81c27cc65e5e8d3ddd27e480d6adb9933e16fccc959afba22296b75e7c8c5ab4762336b1a 88627a6ac595d51a110d977d26c1ee49933e5beb144bda8cb149cd1a9ad9c796e8297a304fbc5b87 691ef2f84dd4705932e689d4c2c82a47508d44777489b5459c7910ebdad393e20a4b6ca7a7fdf5fe e0e8a0e5dff2a59ca755535ff391c75e6885ae958e893dbbf9151de62c70048b5cc3b1b2ac1bc466 d769e37b73b38fcbb9fec4de00d3a51d04163b5bdb69bad88c75de6a0ddb239425261664f3a4bf8e 9a32c573de985f047ad8ce3dd962625b446e3b438d9c7bc039172a80266e9e81736c60a88cbd26ae 58bc40eb391053c874724b5913e28c3a47030b7215821abce717f949c963ec67a3b223264bbc323b d8592cdb3952f1587aea5827626ddc25f94ab6369c0a5030ac717eac5bc36eac6319b6cc434b2c21 2dcc937d443165d609605c1766849177102226b9dc41436dd4cc818b60ea005f2fb30158c46a83c9 c40bb068b10c306f700d938db1e2e068b1b0df74f86cdf67a6d5ab8e271e3c7e2432c6487f11d805 174369ecab09a0e6ee9b162929932f7af9aae0a5989ed96974ce86cac6c18f2703bb2fd5ac594699 6ede94399ed2c67543ae1a0bf96217530cdd09569e1e3786a34c9c0dee6108110db4b83a0bb08ee2 03d82c9505d8deda85497b07b0438000d8d11280c9a60eb0535f017079642bcf633177a1663bc8d9 48c1cda772a0e88cd77d436b244ab64f4166989afbdc454fdb6d339b72127d3aa25378bc6b3c1199 70aa6077ceaba933b18f7a167623616d9b6719a58455eca7ace198b2540cee23db06ada86708fc6c 71098c607904465cb6022337e681d127858031be280263223000c692117e2df528604c8554678a31 ed6c0163c67402c670992cd3de922f7f01564fd6362ac8c9cdb6cbc401bfc523f6c0c110d85aa61b 6fab98ebbbc6a95845dc847d49eec2525e26e2c319e72269b610d5e358b15914b16fdc423bd3e029 83bc8ac6bdad006394ed00632e3781c96c038c0d1600e3b04e401eb049c078aec580f1825780c9d6 9b0093dd7301261cb030192480094bf580c918c18071bf634a05ae17d49e324c343b9e6489c3d0d9 a858bbc852e14947b404b63deee86d7bb0996bc2eddbe2d6b128f0c03c883251baed2237228fe318 68ed2d290b37c45c957115989cf6124c422d98d48730d92c8049e60fc014aa9b6172a1802913f7a8 7ee8cc2e094cb5581d98ea600e4c83b611981ab2004c4d7306980ad3113015fb16604abaa4a2c2ed a3b9ee3616ce38d68740528c49ee586093e3c329438908948d05833730886c5c536b7a2cee40b8a1 be1587760a1d85b6c721bb09c21635150f46455c0f406735ae0333de6d01b3610629a2605360c682 3b98b4004c2e7660764460571dd300308bae2c4c266d6096fc6b989c2cc02cd75dc0ec8a148099a1 e7aa83d86e2560d2f31565de91cc85d961229db970d1c461d50cc6ccccd2a5de2e11901207ab3798 da9dd44b84259eb20cb54b84f39592170f600a61496fa247cc93dd8d81c54a7481f9cc0c8079ed56 af9e34afcb1b601e8d8ec03cb69981791377c2642a03f3968e000be89580c5c00e6032508005f3e3 c06234fb81793faa00f3220f9b3f48d2c03ca4a2052fc4911d798dd9d4fa124b26dcab75547dd10e 3df18a6e9708c89e0ce50df95266573a103ff09740b5474f7758cee136881e5b6c357618ab6be301 848bbb19b01a3b1360d94f17c0325faf8165811f81a5efc66052868da8ad396019883e983492c032 b4ab375c5a86155868449c6032a680651c0f034b9b6c004b191c8025b5178125ddcae43b7cab9c25 629e52aa98dd161220eccd4487c17e3c4c9bb0e0f512e1d19a908a1b9bc2596ce19eb3e8ee667173 ce8038cdbc71a61d86606a75043636b603367b600b6c20ac00eb3679fc88425ed880b563a580b51c 9061d20b036bc59e03d66ea50dac3ddb12262d23b0d6781e26bb04b0e6aa5d608d2561595fd4ab06 2a0bce72de9cc09a9974a3d54889667f3ddede4fcad19842e7c2f4b69cf49f2b099f6b6149d90570 6a6de90c75ee3840584e5bd3bda61b6b2d000eec94d5704dd0d6849ad84e5623b0ad48f59cb46d18 767e44216f7dc0d6141230695580ad450caf01c8259b15267d17b0956339604b3bc6c006490b6c2e 25046cccb493b3277cfd6f31c79d89bf1d8dafe7f55011ac91dc7823c9ad4b924c170b1b3b522bb2 7a8c37ed78709b329e2d6609e0fc847848881e0e70ac610776a5e780c99e05f639f51182dc1b6781 bdcf3481bd569e027bddaa4642daeb4d12d81bae004c4e65602f7497c09ec811c01e88c7815d8a4d b2c9f168a6851dd779df4cbb5d6276b177fc978d3ba5de2ec107e70923c567aa733c92d9d4cce5be 2b0ee2d99c000826aade0073bd5d0225ea6513107c4d6c7e0e2669d8a54bdd0b934b1ce05b771126 fd2ec0a7e40a264d0ce0338e85c92a0af079b109f0beb403789d60009eb7e6001e37223f7466c98a bbb410e0f6eaa37671cb0aae9bf7f958dd33e12a51d1536e32f4f2d83d13a1e8626429e6a99201d2 34f8f1981d4a5487efb72b2650a2de3da126308beab9957898414a2581c3b9ae0207e51ac264a200 8755b4c2642503872d9d060e3bd3070eb03b0387612a0142195600311f1fb598e3031861eaed12e1 b52d3df6f536fd922c0a133f9b0cabd7829260bd57acf904d1330c7b5ce67bb0b1eaf3556f97b84b ee2f9b4049c1843e84d18d13abc9c765137b07204b39d50949a688024c16f05ba563066426e30364 36d104a42761c870dd249e3cef0cd6688a0b6e033ebcdd7515a46e8a1b45fb22792e0d8d367ab198 63d6cdb6faddfbac3a7ce7f33aa08c27f5c93df4fbb712d5374c862680721a014c2a34a0283e0c93 5d0d5074630d282646008af54401853b7b802208635aaaf4d08e476c6c74022decd8192e578460b3 1da2a44acb694fad86476c00b37d451c5fddcd28bef7db350e5f89eafafd4a54872b4cd408e23b67 f0a90e007df6f180b1991230197600031207c0186416300493018cc33a058c68b2c7079191f55bcc 712b9948d3c56c56c613898cd9186abae7aaff59259a9ebb578d2afe0a28bebb4e42bd4ef8eb4ee1 af985fd5ebab5ef26017dd80e3401e2603755ae37c1533e012291fe092de1ae052d40e70153b1361 b9e9c56fe1a9992c577c5596ca7b43c430e8a7cc60421fbe5d7ba179bf555f2f6ad3d70d0e9fb74a 7cbd1ef775a7c497e3f7cee7ab5eecc05b7cd7fb7b374c0388d04c80c99a06223e4901d1379e0031 d1b200b15df0bdc31fe0a92748c37e9445c7178df96da9664ef9066bab2d39c36dddcada8f7bfafb 32b15f630bb28076099d04e9a6c701b9c086d2be090f6a114c38584a925460ea5997d3db19bac789 e1c51b2aaf783fe829c9407579ee86789bfd185e721432d7a3093f1f8d87a6e15612ec134aaa66ce c1e5385d0e65979e763d9f880fd6454b69662fb567077fc57504e5aa6235cfebcb1c6f69269a2e4f db32f1173a6d2532e9b94d59635fa14ad230e7ae67c68efc683819b6e6c80b350b4cb7dcc22d9a93 4b2584f7d6b92c75dc3a1a02bd1b8e7db17d60176e1d2ec6c4ee24d51de06f4a5e847b11ee1f4b38 6852392c06797a416b1dac9459c48d6bb65d37934a6e6e89d74206ebc0cb3376a3d11a42e42b11d5 d878e4d8396a4727bd4c39a854c1eba5c7229565cd27accbf99beb2ddf08f4ad1a21b97134216753 52c335a3f185c7b639a30d3048d039eb6bbb5a61ff1964cb41b11b18870a11f6145ee216324acc14 5f2c9a1de5e23daeda4374dda55c758f2d5df639e5ccc66448e69c83551312b7bb2c0cc9225c80ae 225cd95b1423959a64af5477a72352a375a6353d37d2c1a6b339b166fc88d2f94e20cdf4bb4dc6a4 f40edb9d7dc057862e48ee4a6a3437c45b137bcfb59a86a3a469d621003f3fcf97d1a594eb545745 3e3f5d2f0fa10ba23ab58bf96d01a56f3a2057c4c1309c0c8eee447d1f21db9be83b38590a31d552 453cf9fdf727ad054cb4d6c2c6fc2459362dd29eb1793a3a2a884fedb610d574d95b9b4c126f381d 3562bfbacc480eaec1d171e6acd4a1a9e9391ff89b088ea8cdf8fceb84dfdf28b9f381834cf643dc 0528e16c7b658fcc425d57cc6e2ba4e2a15d066db6fe37095ecfd931ac319baed9963dd30bb7c36c b18cf87adc3dd7c4535ff2e2e46093a04223ca89a1838ce3c46a3d9a0c8bbde3cc24951c73ef39ea 5b1ceda9e28a9f798788c30f9b396724767665e351c2fd45fe8bd090907613a81f730e8d9ac239eb c7b6f3430292345634d1f56dcb4ccea268871a9256d9224606d6c844c16de66184b363cad9ff4164 7b85a87a5b7d87e862562aa59d596799447ceda1e3f3539219e4664dcec83797bce750302332cb50 41b472aed4f030f344a32ee42bf4b5c3b364509c65961167232ac72de5e43055737685ec32b7ed15 2d6d852c376dd55c8dcf481bc8931dba15dd9f921d1365eb4176b41e340215b1936fb8eccf726387 b7d046ecb89c5a2e07a092086de8190aac04d972165e1c4130b72c1f770dc89b99f19ab59e361b4a cc00483460df4e42456e17e32f5ec5caa4628855f3fb0e976c1c7c96ed10aa8ad0ea58dbcc8e7a84 447a4087967f352151708f0e2dff6a42224ed3a1e55f4d4888e66fd7bd7f7bf242f342f342f342f3 42f342f342f342f342f342f342f342f342a3268706eeedff261a7eb1647fabdedab8f21a9b179a17 9a179a7f0934c479178f8ae35fe7abf74b6476f23b688845e794e2ab28aabff773bdcb6ccde1affa f869fad7bd11ed9369aa193e61bf6e6aaf274df38edfe9bfb86ebb5f9cf642f342f342f397a0218e cb5d2ccefd3a1f0a8dad16b3d6f3afb306b3536c646fecdccd1f777489695908ccce9eb93b1bfc65 6f86762a5a01d6a5f5575d72ce7351dab6ec06d01d57859f89561d57525bd7c0577278cbd99febdd d82f67473254efe47ed97f7371986d658817a7bdd0bcd0fcfba1112ce1ee6f599d95e6c0dea47fad 4e858c53b9f4529614f6b33a45d1d1f1866de0b5b08ba4fc63d64b511e558a00f8ba5574ecf679bd 338e70cc00fce9e6aaad4bea47a2513828bdb3e9689ff9017735deb35d94931758fc85c42f07a75d 39cd0b006054fb5776b1ed74642ca78ed48d1cf6cc8f7d1f77f78d943d2706768edfe10142d9c5ed a6c14b705e685e68fec16844623ffa1d3444b965f684ebe892839fccd35a76153fa7e996a72005d9 9f7b53dbadd417a381c93338fe88da174a041c25a8216301745f69ea69e65a385b47175bbcbb77c7 e00f06b7656374be036b23717a9e87cd3acbef502f7a363f0f4f49d9bf23eddd43cf26399f75a115 4ca60ade77b6d0ac2a3fe0c487fd7760760756bf600422129a3982180086eaf21743c7f4bc219f61 11ab8f36817ef8473aa3576c97253fb61be54763e1253d2f342f344f15dd72f73b6888614f38895d 74fbf34fcec79a6f14f7d6a755eb7ab198fe6241adb8dd668b8501c0e8c9ffd44ad3489cd5b30ba4 9b06e8ce02f3b326c6d463ffef8e30d7657fea4ecd6cc1d9776058ad7f30d5cc5dd188f4a121806e eb7ea2eba20ee3fb7bc88af59e53aeb42cbfbf037b39337a82cbbe4131d358efb95361e473da3daa 6e4661be3ff82aaae786251ac4f9779e4ed79f4f1b6bcb6c1933c852d404ac985df805bf116cc92b 6d6b9e4134d2f8f5ecbadd121361f812d2179a7f3734cbfcba8f2ec22ec759cb4f4bc05aed9cce85 966e60179dfe9f56bf519394b21ecc5005b5633ff7a65a383ac7cd751ea9083239933dcf56cb7887 b29dcf857780c7569367229c9688636d89745289fb896ca5799a38a86aa9fc7c5a306e4d8bf7773c 8d315eddef9cc8e7dedfd1113d43337cd0c755345ede55555c7e4251cfe0fdfd3dc16cf7511df56a 46675288c5d2a56f803ad5232bc69eaeba1472a1ec46e02b1db38a9fd7c58f6ff6b613afac87c2f6 584fbe478f2ddaa747b71aa85dade3a03151b019aa9e64db65145ec2f342f3af80466cdaeb2d3a70 d8ff681fa4aa796c73b0a436ade9cf68aa09bb3be49b79a0dc71a91f1c8f956e02571695f0bbeb34 13434f54a9af6e1ef777d0e6339d5395e7fdc18baabf11c9fad3d52d1164eb6947fbfd1d5de415d1 cba43ee400950a5e38399e906d47052e826af6cdbbba788889dc55751ee8cfbba4a7f4902937e0a0 e2a28c31d75e7746104348af1953471d9d696faa3a0ff4dd7ab8435127c669b849748bd9596788c8 4a6a07eb309987a4fd51cff987a87ecc9697748f619acba9443cb01afb68dc60a85be2910efe8c15 989180a64862b22cb5dcfbfe6e253ad1bb3ec39798bed0fca3d1eccd71cbe6e72c334f582eb808b1 4ffc8ca6662e84c74b2f8d64ec321df3edcc13dd16f512c5e42cfaee18040c51fd3ce5cb0e783c76 03145853faf9a64b654c6b369415bd8ea5e36bc3794ce006d6f7773ea3cc748c1282b01f37720056 62c27ccd2778d4cbf7543c055d73f078c69cb01defe84ec487e85532e4db20c541aee9ac1ee90a64 47d55b26a38e2989e39a4e33907a78d5eb176106cf71c019ee33d0a930d2a5e832f8bdaedd36dbbd 6b4b74dde1b7d8cd86abbed4f7fe5a6d14eab36fd358597567ba70cc51235bdd7096b4f7bcdd4e83 f075f7365d137ed14cb2f997a4bed0fc3dd0b8eac38a989daf7e8ed729b9e31137d238d5bb45df1d 9a4ab7cc02cad1d195e7abc5c8c4a2aa60ed9b4f749b91d7042bd30bee045e174da40765cb68ad99 74168fb8555318e994de4ec697e01ae82774532fc18575b84274e0c120c4e63e841b5842f5c710cf a2cba2e236a42c3aed4a6bca0ab8d4bd8cc701aab584ce09f52bee8f3e283bf68069e52d924edd76 705584a7277d72b038168779d0b5af0f7db2f62b222acfcca68fcaac32e234bc846e9c2a990ec5d3 2a6eccfa8ce79cc5e9d1336a2dc57c6223d8ab5253b6fe30ffbd04f5850645c998bab65fa2999d1c b9a29c32f67b057e5d7a129b5dadf0be1ab6ad22877c6fa18fcc37f56b9610f2fdd79ef54738d315 280c39f7f1f0e8152f5d548b01dfb08f8aa5d6086bd644b8aaa31826c815a6e986b8de1631eba18c 2db4f2b2b6a29e872d0bf3fca4d66d9e3eae5e4b42e8aa183abac383cfd0c542c9593a718f13e95e e1c6897613c114eb6b1eb4b0ce2ace6e57bbf3dc38628f33015975c8e5f8301eb6a93ba82ad87df9 71c557e9301a1de9de1376d3dc02da9eb6bee3cee5cac33ad07abea5d3f62c2aca4ab6d0be31e30d f1c833e37a7c5ad43765df60e218e64366eee720a9972ef8d74453a3406da1d0fdc0746fb13e2969 93f3bd7d650b5750bee693b37bc1b911af64d32155506bcffa23ba92c885e3ad0463c647418f1690 6098368bc78dd22a57d7a4512275fba41a4cefe85efdfb203aae124fa8d68e43227c0f5683dbc36a 4a807e6c70d97c75378189be075ec5490d22a6fb95cd34ae9432aa1b2b5c7e584e8a97ee41ab37ab b3db620f6b8ae71d3d5c1cbcfbeec6e6a5232acb3b03ec13a500ad1cfe917e95297f55a28727ec70 553aefe82185d5338546db5b8fbbc1d55e4fb35697ba8730d5976c3f260783f864a764c80183a5bc a4d2b5a1c9ef8b4dfdf90a1fe8e806a9ffe8477be9857f3a34a221192e3849fcfc139a698b8bc922 e70c052123459f6c03567356393e1952aa506ef4e7ab32e5d324c8a62cf63a164e4e5324e6d4ec51 48e21f42e2d1114c6435bdcb51deb479249c1ae7a12aaf76f541c9d876c1ab05937d9cadcb7de43a 7a7f8ce825bcea7d894cb77f781c23f5e570ad1f4cef898b1df543e71c871d2d0bdfd13b1ef7ebb3 20b1a95057b307f7ad1e5c810e10d52c266cfb68125549ff5559ea9e5a74e46244026660a00d5bd5 e1399b78f4a8f42387f1c7ba0d6eadee81ee3e6660564ca3cd50f4e8fbc3ecc4cd68fcda27deecd2 099a26fcd7b60103e7abeb5bd60dd916c3439c5c3936ebde0981dbbb2f9df0bf854698eda73f0529 782c75a72f3c22e7a568e987f0cd442160b494dedf6bbe7df9d1015351e236ecd47021b3619a7a8c d92ca73509c3e67bbdfe545257860589c7d9ca1e5705bb18b7df0ba016bea04af5e841b1d98787d4 55aaf3bafe245e351bd026d8ad6940e4547cb668e97e6d7504008581a17e181e1d59f95af6da89b8 ee20d9cfa85eabc5e0bd77baa38086985a36d27a581b914ae76afe6065dde8ba46f699f9a38580c0 b2dc917b589fdac88d4674f3e1d1815469d257da759f701d7294ed611ef48efdc3662c655bab6b36 9b27a3b340c2335add8aeb1983db637d34e84f3630081b6d3eb8caa69cd0caf217d7de164f6494d8 4b31fc83d1d4726c3db9f3534fb31ef9261777e1ad43f5dded77b475dc028425c016da7964f86752 365deecec6afccffe860a906b688f31fbd40f6f4fb132f065f0c8f353b006beb982527fbdaf0e1b1 7df45456b20e54af29794a5d6ebba19a080058abf7eeefe3853e78ae1d10f486a9109ba0b2f5638e b8131241ad93f6cceec3b790a1dfd51610b3873d7fda497f486e5077f52aa8e6133201ee17869c32 2af63fb6c71eb445870f687433b5743578fcba2d07c2fae155011beaa841d9d51f165b8e8132530b dbd28f3666dd60b8d24f7ec2e1a3e3054703835cf82d9da365ee27dafc1a7f615a9e51b36d9624c6 9d3c4420b179e9857f289ae32e76c9c682a3f084d92faabafedf5124ed4aaa735570ac1fcfb3e737 bdcd48637c1d8d84a33d9a77e453bd9db7edaaf40143a7a2d31f35d2f2ca7c0f539cdd100feb4aee 554ba0c71f6e71111aaedb5dedcfde180c8eabf96078f4f414696476bc0bacf172eb4ec0d5b91f98 a6d4bde6519f25d42a3cba754688adce1257897d2479ed14d42e9ff7d8efe9e2ea66929a3d628b3e 0496e25632fbe907c672cf3468afcbf985bb833c9fda8709651f6c067cdfab6ab4cba575a72243e0 4a3b831efb546da08f163c8ee5907f5c299794f1f53273fde0d75547747db863b0f99385f64ef105 9d4e3a4fe6b2cb99e5d8f7bd94c2ff069ac878a644cca27e543635e1a73e8ccca7e8d8aa31353b06 78f2e0eba56fe73a42961ca9b3baab23f096db15075ebdfa1a55457457bd69362e53572e7c38676d 65046410bf23bfc70d0b557caa5314180fd5e87d878885860bb00f532646290df47147e462377366 71c2ab852493742b7ef848abccb2931f09870452bc4aedc38809563bf2a1be9713e6faedeac4de51 eb74cee7b57bd7087a4654935821acc30ac4d6dfbbd2caa1e3be5035ab342e2f5677d7927cda6f66 c3e351229ce6729fda27f778b0b11a56fba1eb73452fdfa282ee44ee41fb0879c35ab3ef68fd2b49 883c0a37c2b4fee8ef40095ccd8546d2851bba954c009019763660806e88cffcd464735fc7565f2a e19f100d71de8e2e73f476ef70b11e622ddbea60ac8b59c37d7cc8ecb02209d9b90bf8cd4929e042 0674f2dec84d53e969ec3a173fb8fb4d005fbe5fb9da250f6fb44e79a15c85fa7c1be04bccaf9ac3 b07dd01c3d092d1eded3b6d1e6464394d8e08776c32ff2ade30f3f69f561c37be19826b0c687dbe3 d1799c2fa3a5fe3b0a6d8cdc8f8dfda2995e33e67e659758e61d57dbc4f81857422c4eba525c2d4d 6f6d961b16509fded5cc96d0f8e94e10c0749ca938320fded1fbdef7db7d510f76f65ec7e6c1dc43 0799db3e2d36c89877ead9ac2465f1a534da9574f98cec7b210181cfbf468f51fb99d349c73c265c fe46a0b0cfb267a1384ef376fc259eff1e6888e5b41374f78a5dff2c1bef266b9cbfbecfb1f75e7d 61b3a3dcb690cba8e74e2887704db28d43fec1fce85c9508183c333fd0218a9bad9f5228f6ae6b4c 13573c3712fdd1a1b5d89e1f547366ec6cdc981885f058336702a9e2adf04857978be9de295cd864 26c3ab34e33a74a3cbc84dfdfe5e19f8cc3761be3597ac2ddc8ca77b4d86227a6d9a3943051f3c08 0170ddf736d8f438a18a4ff4176f0e101aa408750bcd98f53ed44be5d9ca132d78ddc3cf62c5fcd2 f6c4a4d99c3753352af278f43c46f1d50a826548d8574ed69f2423593c36d33b6dfcd91d6619736f e873b0f512d37f4b34c441098dea605113baae459debdffb5517a24cf071d39aef48ab665121f3c4 266664b3d6c8ddad49c9dd226cd09bbe417b9ffa303fe486f766bfb02487af66cee2d63c22d80f33 677d7f84c3768ac8ba664ed10a3ef0104cf4f62c289e7f7fb25fdb5052f18ff0b687bd4c4ef2aa8e 6814169b7a3073966a9d98af70bfeb83b6947dcfcc9ceb11091d332785fd60e684cd535e78e6cd70 b60391ab627c3cdf509ba2c5ebfbc4d920ef6da041b887d35733473f5c38a5a894a1f14bce8ca7eb 06c9a3c74a83d860cd64396ba8369ec581e525a72f34ff3334dbb8403abb0d6eec2a9f0aa573a660 65a443cb58f9be3fcd670829ec9c64ab8e62746847d37be0fbbd71da8548b9b37a4986660965c8bb 9bf63ecca087ebe640729250cd20ecd4b871f9144bce5b33e85b7fb4e5849ed00f1ad95d4a4fe845 b339adef8fc549ad32dbf1f448b78fb35048e01fe23ca87a49f5f13ec41ed7acd1f72721735f1acb 3c557486c839495ed7b186c7f54c2da67a5f82e3ecec7ec1d7b808625b8dff9d3f84f8a968ca768b 5a6dab2fd6c8784defeec26c4f0e1eb8a0737f4c35d8647bcd3753dd27be5a61b67409abe44b805e 68fe6a343645095d3051ef04e97223b1fbfc2845a25722c3b42b1093697a6fa209ba781bef427453 c511d3374fccea1ef083d209f4e2bacaa038bf6aaa8f8d99ef4ae7eac001d8fde1d2793451550353 dace1e7de33a28ec8fda16f4d076ebf6c513ef37dbcf3764ab191dcb8f83490f9b20ac87531bf2e0 c9a86db5065adbd6c533d72c30740a3a23440da38dab827e3ccc5acb5c9e6d64d5ac9156f289a9a4 ded44f236b09558bd92d26ceac777f4ad0e1de70c05075e2c2c95b5bd5372b45ff3226f6e20de516 9eb232eb05a7be97fcbcd0fc931e7d6fcd9bd371020cd8d62063051557b6fd1053cee57372c2674c d5854af19cc7ebf4fcf80e385bd37687a754c551b09bb6af64dadc1e82221c1f4bb7fbddfa1d3ddc a9eb3d31ed89dcee6db5196db73b794a3d1c6a1a5eebabdc1b17bd41bd5ad7b3aa04d656796255f9 3eadb747b295c9780b7bb66c63da6eb3eeb2adb6e69e29a2baa94c7cee5bd9960fa137fc8e477e1c 83676ed30bc765d4483ca4a74cd651d9334f794f2bfd53b8ce4e290ff1b83229ea50b27095f58f1b 1cf9b9b1b3f3bf64e785e65f00cd72a3185d55d14462a97dad520b713c9ef51947bbd05d1c5f9a2b 2d82ceb9db163454ca39f37741fe44f4b183f620cb9f7eaa07e7b701dbac8ef6274a6336938ddca7 d2b8eb4fb141ab75ba2f9bdb285dbc05ea7556534436ea7ea9d3ced7bc57ab087b8878e11de38f40 86aead36a7ef4eac5f6f07d18dc4694987d3f8d9e125c11051ef58c376a6b9fe3eb9553bf7010cfb 452924459bba6ea29383e0d3b82fc10fc25c6dd19f457cf9e493d8f551af182587c9351ff7b55fd2 f342f3ef85e67820f2ee852b633e0fd6b380a977d83d5ebfc171e95876deadb5fc27237f12e480a1 ea1086e93c3a7b1abc43538e969d69df55a75827b72e1cc26d6b55561fb1ccf78688b22814d463e5 0fa7214bb9941624e44a161f363081bd5ea4b47350c6fdfd9ec264bb0a5df1191e37b01c43e94948 e404a2b9de60a4d98af76e366bc67a8d916eb18f076e1b45f42d60b5e8bce646d8b5380200627e74 e0dd6f4a6dce878bbf7bef13dff3ae981118e67141e6a25e6fb7735c3cb9f5bcba3104e59340b6a3 edb5f04b3638808ed87fc9ce0bcd0bcd931baf17e0bced1899153ac4586e10e14b365bc293855191 bb3bb3b1cf479b6e9f8b2429cc495ddc79e34a28a8f7b43d844c5959b31a66ed7460cd9b5321e5f8 f62334cb50313bef1ede612ecb7df389cdb7dd6fc217dd8562d9bcd15f28120444c5c984bffbc469 b5201de9f1b38051a9ec20e0370b65f197f45e13d2021851f039724f060ce799ee958e7b3e33c421 8262858d9c86e5e6c23c2f3dd16bd991e04961c5c2b8b69dfc920d2656b1353bba5bb3d38ba95f68 5e68fe0a34e68d6229b4cc4bc53caa6e1656fee1acb62006add5528033c03571ab13ce9a2ca340cd e2530f9875eeaf99c5a2f2dc9518e1c71a6ed6569e3aebdc9ebb95989c9f39c770abb7a4a3f3b4f3 63b6a2a6f66cc7db6b688924bafce4ea1c63ef83ab0f999057d587869af13136a982f5340fbec559 6c1708fd07d7d2e170468cc06cc359209277daf5ef7471b40c5958911ca553d966a4b99041f589de 2bccd1d121ce1e2aa41ab9fde9f184c18d735268ee3c73a3620abed8fa85e685e69f11cdc1d3c6f7 d3508b2ef7c54966d76d666bdea56b2bdf9fdde8d40f87732cd26c019f9fab1c201a29bf1182ae8c 69f760d538cd2691a888aeab2a7d2fcda2d19bbd8eaa5bbbce843c8bb9bba0df78295fd436448d13 bd33c5648446d122a692a1fab86caedaae17a5455c9c79a7739a8d98a493c6cae51d6062ec923e85 52c233ca39151655e44930e3d8b0dfc394c0133d573de264318c33098bafd0fff9d2b46ee992439b bcfdc3c830f9e58346c5e98bab5f685e68fe05d0d48079c0d5f383c64ce2946a393f5ba0875e2319 3ef66840e1cb58c4272d3c9196b729c8edd89c1e8b9ebed76ef007eecfeeae2fd8012b361414f887 992a0f7a98314d3fcff6028c2197b6ed63d324a7cd2f0fdd28cfc4145d54a7baa65f651eecb7deb1 eab1a0544f5d3ef4f4a58319402f9c555a4263dd494c66942bf1ecf9a012877597e1a4f19035fc4c bfc164790c744d8e68e13746a8ba975efcf642f342f3ef8886b81c9a54a3a858dde5e7cf109ee8f3 c153154bab60310c2e8ef2f9183df4f7c6d5e35d94a62dba8fc3d3df781b42951deee8384d8b0163 b0a9f32804bee34531bb856ad4675b17c7a9e145ff9040da577bc74dd4811b548716e7b3fef00abe 866bf60aee99d0d6fc36b5e59e3e736672dbc5f12ea348bedecf149bd66646b449d50d8c8fbf3ce9 549d987ee3a190174bbfd0bcd0bcd0fc1a0db1db8e07b3fd9a1935a6db648f2f3f536787b6712b45 0ac340d7566ee662ec66291a749f9c3de4e94855e2364574278f77d033247fb80ab8e23103a3c164 890f3771fac7c0c068644876bb55778bdafd92683b032ff3c3e26068fbe5028022b633d63b7d71da 0bcd7f07cdf1e831a273073ffde845dffef37547edc9ff459732f7dd6fd6609266de22caf8b29dee ce6f85a3325b6ea66f91e179687b23b43cce9c722e4dc7ca71329d5c617ffe817e393fcbb9b4ff2e c7e7a5b21b1effeb0a6864333965327df21962ff3fdbcd0e66700ccfe7e37274394f4f57accee0f1 38fc8756f31761f9966fbc586e26c7e9ee9390ea97d3ed7fffe99bfd77a8d2fa1fc363797ace0db7 53db9b139affcbddfc2b334a76eaa70f3a7dcf4efc2f70cedf409cbf3b468d875cfff15b84fbcc4b fc03dbf6fb2dfb1b47f32786729d86dbfd668af4166495df6a03f16b265d0e479b29acef2faeeef7 9bf7bf2dfc7fe711fdfbb7ffb4598ea7ff3fd1fbf768fd0f93e8ff5c4ece8bdf6ad335e73fa85d8b e972bef82db9fccc4afcff3a5dc22f23e57c56b699e9ec9c3f2ee7cbdd5fa6397fadb74eb3fffc2b d4df767a1e4e54abf27f5e1736b9da9dbf35fedf72139add4abd3993d068b5868fca3e789c0e8370 c4fe63aaa79fee07d31a3c9e47caf03879a321eeea6e3986e83e9af0cd40a1df9ca5e970f3662d04 4b5fa090a24050f2f45149643a1b5e36df0d1b89a5395214284a7e93685e20790afe68751594e50e bdd8d27fb3962e9be9f186113e7a14599ef69be17f6587c7f5c7179252eb707cfcf1555969baa928 25ad1eade282725aa2feaa9fe96b0d0cc75f9be4102952a2d05fbf5d09f330a81f9d57c9fcd48cba 65def27493189ee16a0376727a4c464edf0b7fff5e41332caaec6bf172bb72b9c1a4b3b2f9d57788 ac1089158ed3d3f41c56369ba99afb574b25f8473029f7a3bbc9c78a09192a2a94ef87a69074f1e3 70b28470d79bf5e3cfd872b3816cf6e71ff79037ea8d790b4dfefca3fde71f70404438a8142970ea 2f7588afbfe8375afb4378e3e1ff28cad40f9dfefc83bec9f453916b8150e4da52d8fe8fa6408809 fe171a3397fdf77e6486ff353dfef907aa48fd072b7973d06f3cfdc6716f1cfb9619c1fea8995047 329065ec17d8a4b74609f6e52d0f4b86466f08f0d869921144feb3a11c03ffe404d43115c9352519 1e660acd5145215811e45d28470c6c0329c82cfdb6fdf30f8121795a64214820594a90df327ffe21 ca908e1284b1142933940a93e0679ea1210cfe21c85a3e91946491b981092249b18ca00fe3395210 20325496834d17250843782132098e9ac88b10206948214649e2612b6013599195dec2b0140b33cb 949a8993206af89b95590e02200e548a634899838b7894836738f16dfce71f8c08334b309300e9c5 c0e2344bd22cc2257024cbb132241e2f400c48a44591e7102ad82c9a82f5083c6a0e2cc39022c7a0 e6d0b0d7341c4e9914694e6bb024723c2a0461bc445f3bc13214cac441dc1a40922504606901d291 97498a13b96b290df68d66b07ff7e45695162bde9296e4448abe19298625a1e2a46f4694a3491a0d f8c7c02310a401ec90aa0a199e514177eca10f9a41ac90b34419519d25790e5276abc2349a221823 086f90d832a2930aa020912140823caa023889a5542a7fc06073785e549bf10943fd1429e9164691 b4c4aa2c2420dad2dc0d0c8d3acf41a40c145d5a666e7a2a9194087925a372046c3f7b930d924ba6 44eaa63a86874da7e527b0afe67dc2be75e31306395ce0c48f7c9c84c60bc1785e85c1aa2581d7a4 43622187c17a2101af9494215f409ee62881520114c3f288a739c8742ce24f1e4a870c87804723a5 4a02430a14c3bc3d0c525867e0b4f16445283412a22e0d058112d1787ec1e068c902f7c6420e9665 a4152026093698a3b4cc881094240b0801a772132a05e5911605b583509045a8f31ca81f226cfec7 98a04e7f87c1f154c7e93b0cd298e1849bfad00c2cc9cc175e0413a17aa2bf370e0a10a50ad0b5f9 5f806b1fc32a1135182341e5837432ac51a021a5198817febc7db6f303a0f59191907afc807d92ea a39a077a867568ac919e81a2c9b348a5311c09f5158568af19192a7f5e8199ef40d84a9ebf32f267 715de04d711d206a00ec05a7920e2a0e4112d5c16718193211a2fb079061219fd112a316876c4a43 0807959d4a1b91a469a83591aa4583cd4b9029600dac0cd131a8e33283e4858143c3c9df21501c20 c1b4829f408914194844b52a51ed2f0df9012a51845180eca08a99085ba3154402a0b212d4d412d4 336ae3450149236a188320b08f12437d41504106cd0248a37f65a31017f15a553c1c598696489a43 0c0131c21982510bd2684a40c4810d83cd817d8476aa440b92da785e8493c237080f9b07e70fb5e0 279085f337830ac2af2cc7a95cc1a8e4425a5be02495803c9a21d58222ea23a3925a84ff531b26d1 48dc1113528ca8365ea09102864a06cefb1a46068a12c3f0daa0d314a42afc8355a78ccf917d6080 b11e576c3eb985959016802d91785a63575427cb0a5f408417f20fa38a042faa10d8418a9754554a 51fc9520d0d2a0584aed3e9c8711c16592a650bfbe48f40981dc224215702d780522b162b482500d 22f98675ca50dd208c2207a75c6d9864fa73d01995131834c3322a410499d2a6208a864ca5f65d10 842f885a504253edf76c684e10654d5f31922a1150c42409550e753a235cf913e6a768416d182b42 d30441604d9a92e4e112e23b04f691bb8edd1790456c46ab3228cbc255873082ac6114196d0ea4a0 0da315a4d054206ac49720506d3cc3b1df07e87e1cc77a83ab0d3a2da3294abad551b4cc2182f3b7 4a86423491aea340098206442220de69330ab1b7ca33df80d0306338c4aedfb519cc497302770714 90326454ad023f5e1131d0a6ba2b4ecb12942c96bb03a2a995966e8ad33234610464257dcf2921c3 92be033e10e41910514f84360614865bea495079224bf3a606115a0534cd7c316746cb0935b1f80c f8ad55d07ae528e9aea722b49059754a833cc06926e2b726e902bf374907883a05cd0d4a16e9db4e 41439fd166cb6f357c01212e64522320b47e5986e19e00d1a040d1ba2dae0f843d8584bf2bae07e4 d03441d1b7c5f581dfa8f7d94d5de0f76eea0111a158a48da93bd981d2c10b927c5bc32710f614aa 1f8d261c623fee19f0a60150cbd10cfb04f8bdfd9fc575812c922c6d91f6ad497ac0efc53fbba90b bce9a60e50251454659ae500e75c28062aa1540d466b76bf2c499a9c320885a0318f04ff826b3358 159afad5f951826da391f2e7d1e2e00302f51b0de7088196be016938b14aead20b21820a0042d042 5760d5f9571635639b4613b73a47a0798a51b371b09114abcd530cfce31b04aa3b44816bc12b101a 332cb455118493aea602fc049788d006a059755d27a3d52da31544462e6a3f47690a0a354c90916a 45d399a81614d13a5dd30c1423311f7da404440a0814246833d0c8aad1e6a9ebcc85882331327733 c1412e41b62ffb3d1b6cbc4c69b22322f2d2c8b4a0a9abe0c079462b28907051a2cdb39200998d66 9035ae72088d162c08f2398e705e800d5797bd889802afa93b09ad1ad4c14519248402128a550717 2a7001592cd02a838b3bfe0d6184ab184d54a1797e6d3f8f66790d08d758e21bea11acfe9b81a772 853a5d7eb303615721d955b5ff918d4676295a5ca1f500b26c697562526d09b8baa639562b087bc7 22931e76535099074e55b24a0a64c4ca503f7f83f0c8aed3d8e90b884c50116184a3cc8a1a83d170 b5f8862a879600aff221a4c3950fd11480c8881818d906a86192ac4290c342d2d8494466866a070a e2952b44b806e599ab1da81684b220f0dfec40952b587542ffb203551a8a8cf43d1b3251ae133725 42d941c34df357cd23f11f5ca1caa6ac364340cc4643116619415b5c892c34591f453eacaa027bf5 cf3f32a12f379bea2173389097ad309c4f2bc7e17283fc6af3d3f03fa66fc3dd4e390fcfd33dfcf4 363f4e4f67e5387d3b2d94ff441054e8b380c914cdc79003f2ff01afb6b5ab> endstream endobj 5 0 obj << /Filter [/ASCII85Decode /FlateDecode ] /Length 47 >> stream GhOt'1B7FZ"#S^CKb'rAF*Y=gh1H.G("/"c\H#CT$X*~> endstream endobj 2 0 obj << /Type /Pages /Kids [4 0 R] /Count 1 >> endobj 1 0 obj << /Type /Catalog /Pages 2 0 R /OCProperties<< /D<< /Order[]/ON[]/OFF[]/RBGroups[]>>/OCGs[]>> >> endobj 3 0 obj << /CreationDate (D:20190614115344+07'00') /ModDate (D:20190614115344+07'00') /Producer /Creator /Title <> >> endobj 11 0 obj << /Type /ExtGState /RI /RelativeColorimetric >> endobj xref 0 12 0000000000 65535 f 0000957535 00000 n 0000957471 00000 n 0000957654 00000 n 0000000010 00000 n 0000957326 00000 n 0000000319 00000 n 0000000359 00000 n 0000005630 00000 n 0000000538 00000 n 0000001072 00000 n 0000957990 00000 n trailer << /Size 12 /Root 1 0 R /Info 3 0 R /ID [] >> startxref 958061 %%EOF ================================================ FILE: assets/scripts/format-yaml.py ================================================ #!/usr/bin/env python3 from __future__ import annotations import sys from pathlib import Path from ruamel.yaml import YAML def main() -> int: yaml = YAML() # ruamel.yaml.YAML will by default use the native line ending, which leads # to differences between Windows and UNIX. Force the output to use UNIX line # endings. yaml.line_break = '\n' # ruamel.yaml.YAML will by default break long lines and introduce trailing # whitespace errors. Disable this behavior by setting a long line width. yaml.width = 1024 for filename in sys.argv[1:]: with Path(filename).open('r') as file: data = yaml.load(file) with Path(filename).open('w', newline='\n') as file: yaml.dump(data, file) return 0 if __name__ == '__main__': raise SystemExit(main()) ================================================ FILE: assets/scripts/install-local-bin.sh ================================================ #!/bin/sh # chezmoi install script # contains code from and inspired by # https://github.com/client9/shlib # https://github.com/goreleaser/godownloader set -e BINDIR="${BINDIR:-.local/bin}" TAGARG=latest LOG_LEVEL=2 tmpdir="$(mktemp -d)" trap 'rm -rf -- "${tmpdir}"' EXIT trap 'exit' INT TERM usage() { this="${1}" cat <&2 return 1 ;; esac } get_libc() { if is_command ldd; then case "$(ldd --version 2>&1 | tr '[:upper:]' '[:lower:]')" in *glibc* | *"gnu libc"*) # If the version of glibc is too old then use the statically-linked # musl version instead. chezmoi releases are built on GitHub Actions # ubuntu-22.04 runners, which have glibc version 2.35. minimum_glibc_version=2.35 glibc_version="$(ldd --version 2>&1 | awk '$1 == "ldd" { print $NF }')" # shellcheck disable=SC2046,SC2183 minimum_glibc_version_string="$(printf "%03d%03d" $(echo "${minimum_glibc_version}" | tr "." " "))" # shellcheck disable=SC2046,SC2183 glibc_version_string="$(printf "%03d%03d" $(echo "${glibc_version}" | tr "." " "))" log_info "found glibc version ${glibc_version}" if [ "${glibc_version_string}" -lt "${minimum_glibc_version_string}" ]; then printf musl return fi printf glibc return ;; *musl*) printf musl return ;; esac fi if is_command getconf; then case "$(getconf GNU_LIBC_VERSION 2>&1)" in *glibc*) printf glibc return ;; esac fi log_crit "unable to determine libc" 1>&2 exit 1 } real_tag() { tag="${1}" log_debug "checking GitHub for tag ${tag}" release_url="https://github.com/twpayne/chezmoi/releases/${tag}" json="$(http_get "${release_url}" "Accept: application/json")" if [ -z "${json}" ]; then log_err "real_tag error retrieving GitHub release ${tag}" return 1 fi real_tag="$(printf '%s\n' "${json}" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')" if [ -z "${real_tag}" ]; then log_err "real_tag error determining real tag of GitHub release ${tag}" return 1 fi if [ -z "${real_tag}" ]; then return 1 fi log_debug "found tag ${real_tag} for ${tag}" printf '%s' "${real_tag}" } http_get() { tmpfile="$(mktemp)" http_download "${tmpfile}" "${1}" "${2}" || return 1 body="$(cat "${tmpfile}")" rm -f "${tmpfile}" printf '%s\n' "${body}" } http_download_curl() { local_file="${1}" source_url="${2}" header="${3}" if [ -z "${header}" ]; then code="$(curl -w '%{http_code}' -fsSL -o "${local_file}" "${source_url}")" else code="$(curl -w '%{http_code}' -fsSL -H "${header}" -o "${local_file}" "${source_url}")" fi if [ "${code}" != "200" ]; then log_debug "http_download_curl received HTTP status ${code}" return 1 fi return 0 } http_download_wget() { local_file="${1}" source_url="${2}" header="${3}" if [ -z "${header}" ]; then wget -q -O "${local_file}" "${source_url}" || return 1 else wget -q --header "${header}" -O "${local_file}" "${source_url}" || return 1 fi } http_download() { log_debug "http_download ${2}" if is_command curl; then http_download_curl "${@}" || return 1 return elif is_command wget; then http_download_wget "${@}" || return 1 return fi log_crit "http_download unable to find wget or curl" return 1 } hash_sha256() { target="${1}" if is_command sha256sum; then hash="$(sha256sum "${target}")" || return 1 printf '%s' "${hash}" | cut -d ' ' -f 1 elif is_command shasum; then hash="$(shasum -a 256 "${target}" 2>/dev/null)" || return 1 printf '%s' "${hash}" | cut -d ' ' -f 1 elif is_command sha256; then hash="$(sha256 -q "${target}" 2>/dev/null)" || return 1 printf '%s' "${hash}" | cut -d ' ' -f 1 elif is_command openssl; then hash="$(openssl dgst -sha256 "${target}")" || return 1 printf '%s' "${hash}" | cut -d ' ' -f a else log_crit "hash_sha256 unable to find command to compute SHA256 hash" return 1 fi } hash_sha256_verify() { target="${1}" checksums="${2}" basename="${target##*/}" want="$(grep "${basename}\$" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1)" if [ -z "${want}" ]; then log_err "hash_sha256_verify unable to find checksum for ${target} in ${checksums}" return 1 fi got="$(hash_sha256 "${target}")" if [ "${want}" != "${got}" ]; then log_err "hash_sha256_verify checksum for ${target} did not verify ${want} vs ${got}" return 1 fi } untar() { tarball="${1}" case "${tarball}" in *.tar.gz | *.tgz) tar -xzf "${tarball}" ;; *.tar) tar -xf "${tarball}" ;; *.zip) unzip -- "${tarball}" ;; *) log_err "untar unknown archive format for ${tarball}" return 1 ;; esac } is_command() { type "${1}" >/dev/null 2>&1 } log_debug() { [ 3 -le "${LOG_LEVEL}" ] || return 0 printf 'debug %s\n' "${*}" 1>&2 } log_info() { [ 2 -le "${LOG_LEVEL}" ] || return 0 printf 'info %s\n' "${*}" 1>&2 } log_err() { [ 1 -le "${LOG_LEVEL}" ] || return 0 printf 'error %s\n' "${*}" 1>&2 } log_crit() { [ 0 -le "${LOG_LEVEL}" ] || return 0 printf 'critical %s\n' "${*}" 1>&2 } main "${@}" ================================================ FILE: assets/scripts/install.ps1 ================================================ <# .SYNOPSIS Install (and optionally run) chezmoi. .PARAMETER BinDir Specifies the installation directory. "./bin" is the default. Alias: b .PARAMETER Tag Specifies the version of chezmoi to install. "latest" is the default. Alias: t .PARAMETER EnableDebug If specified, print debug output. Alias: d .PARAMETER ChezmoiArgs If specified, execute chezmoi with these arguments after successful installation. This parameter can be provided positionally without specifying its name. .EXAMPLE PS> install.ps1 -b '~/bin' PS> iex "&{$(irm 'https://get.chezmoi.io/ps1')} -b '~/bin'" .EXAMPLE PS> install.ps1 -- init --apply PS> iex "&{$(irm 'https://get.chezmoi.io/ps1')} -- init --apply " #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [Alias('b')] [string] $BinDir = (Join-Path -Path (Resolve-Path -Path '.') -ChildPath 'bin'), [Parameter(Mandatory = $false)] [Alias('t')] [string] $Tag = 'latest', [Parameter(Mandatory = $false)] [Alias('d')] [switch] $EnableDebug, [Parameter(Position = 0, ValueFromRemainingArguments = $true)] [string[]] $ChezmoiArgs ) function Write-DebugVariable { param ( [string[]]$variables ) foreach ($variable in $variables) { $debugVariable = Get-Variable -Name $variable Write-Debug "$( $debugVariable.Name ): $( $debugVariable.Value )" } } function Invoke-CleanUp ($directory) { if (($null -ne $directory) -and (Test-Path -Path $directory)) { Write-Debug "removing ${directory}" Remove-Item -Path $directory -Recurse -Force } } function Invoke-FileDownload ($uri, $path) { Write-Debug "downloading ${uri}" $wc = [System.Net.WebClient]::new() $wc.Headers.Add('Accept', 'application/octet-stream') $wc.DownloadFile($uri, $path) $wc.Dispose() } function Invoke-StringDownload ($uri) { Write-Debug "downloading ${uri} as string" $wc = [System.Net.WebClient]::new() $wc.DownloadString($uri) $wc.Dispose() } function Get-GoOS { if ($PSVersionTable.PSEdition -eq 'Desktop') { return 'windows' } $isOSPlatform = [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform $osPlatform = [System.Runtime.InteropServices.OSPlatform] if ($isOSPlatform.Invoke($osPlatform::Windows)) { return 'windows' } if ($isOSPlatform.Invoke($osPlatform::Linux)) { return 'linux' } if ($isOSPlatform.Invoke($osPlatform::OSX)) { return 'darwin' } Write-Error 'unable to determine GOOS' } function Get-GoArch { if ($PSVersionTable.PSEdition -eq 'Core') { $goArch = @{ 'Arm' = 'arm' 'Arm64' = 'arm64' 'X86' = 'i386' 'X64' = 'amd64' } $arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString() $result = $goArch[$arch] if (-not $result) { throw "Unsupported OS architecture: $arch" } return $result } $cpuArch = (Get-CimInstance -ClassName Win32_Processor).Architecture if ([System.Environment]::Is64BitOperatingSystem) { switch ($cpuArch) { 9 { return 'amd64' } 12 { return 'arm64' } default { throw "Unsupported CPU architecture ($cpuArch) on a 64-bit OS." } } } else { switch ($cpuArch) { 0 { return 'i386' } 9 { return 'i386' } # AMD64 CPU running 32-bit OS 5 { return 'arm' } 12 { return 'arm' } # ARM64 CPU running 32-bit OS default { throw "Unsupported CPU architecture ($cpuArch) on a 32-bit OS." } } } } function Get-RealTag ($tag) { Write-Debug "checking GitHub for tag ${tag}" $releaseUrl = "${BaseUrl}/${tag}" $json = try { Invoke-RestMethod -Uri $releaseUrl -Headers @{ 'Accept' = 'application/json' } } catch { Write-Error "error retrieving GitHub release ${tag}" } $realTag = $json.tag_name Write-Debug "found tag ${realTag} for ${tag}" return $realTag } function Get-LibC { $libcOutput = '' if (Get-Command -CommandType Application ldd -ErrorAction SilentlyContinue) { $libcOutput = (ldd --version 2>&1) -join [System.Environment]::NewLine } elseif (Get-Command -CommandType Application getconf -ErrorAction SilentlyContinue) { $libcOutput = (getconf GNU_LIBC_VERSION 2>&1) -join [System.Environment]::NewLine } Write-DebugVariable 'libcOutput' switch -Wildcard ($libcOutput) { '*glibc*' { return 'glibc' } '*gnu libc*' { return 'glibc' } '*musl*' { return 'musl' } } Write-Error 'unable to determine libc' } function Get-Checksums ($tag, $version) { $checksumsText = Invoke-StringDownload "${BaseUrl}/download/${tag}/chezmoi_${version}_checksums.txt" $checksums = @{} $lines = $checksumsText -split '\r?\n' | Where-Object { $_ } foreach ($line in $lines) { $value, $key = $line -split '\s+' $checksums[$key] = $value } $checksums } function Confirm-Checksum ($target, $checksums) { $basename = [System.IO.Path]::GetFileName($target) if (-not $checksums.ContainsKey($basename)) { Write-Error "unable to find checksum for ${target} in checksums" } $want = $checksums[$basename].ToLower() $got = (Get-FileHash -Path $target -Algorithm SHA256).Hash.ToLower() if ($want -ne $got) { Write-Error "checksum for ${target} did not verify ${want} vs ${got}" } } function Expand-ChezmoiArchive ($path) { $parent = Split-Path -Path $path -Parent Write-Debug "extracting ${path} to ${parent}" if ($path.EndsWith('.tar.gz')) { & tar --extract --gzip --file $path --directory $parent } if ($path.EndsWith('.zip')) { Add-Type -AssemblyName System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::ExtractToDirectory($path, $parent) } } Set-StrictMode -Version 3.0 [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 $script:ErrorActionPreference = 'Stop' $script:InformationPreference = 'Continue' if ($EnableDebug) { $script:DebugPreference = 'Continue' } trap { Invoke-CleanUp $tempDir break } $BaseUrl = 'https://github.com/twpayne/chezmoi/releases' # convert $BinDir to an absolute path $BinDir = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($BinDir) $tempDir = '' do { $tempDir = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ([System.Guid]::NewGuid()) } while (Test-Path -Path $tempDir) New-Item -ItemType Directory -Path $tempDir | Out-Null Write-DebugVariable 'BinDir', 'Tag', 'ChezmoiArgs', 'tempDir' $goOS = Get-GoOS $goArch = Get-GoArch foreach ($variableName in @('goOS', 'goArch')) { Write-DebugVariable $variableName } $realTag = Get-RealTag $Tag $version = $realTag.TrimStart('v') Write-Information "found version ${version} for ${Tag}/${goOS}/${goArch}" $binarySuffix = '' $archiveFormat = 'tar.gz' $goOSExtra = '' switch ($goOS) { 'linux' { $goOSExtra = "-$( Get-LibC )" break } 'windows' { $binarySuffix = '.exe' $archiveFormat = 'zip' break } } Write-DebugVariable 'binarySuffix', 'archiveFormat', 'goOSExtra' $archiveFilename = "chezmoi_${version}_${goOS}${goOSExtra}_${goArch}.${archiveFormat}" $tempArchivePath = Join-Path -Path $tempDir -ChildPath $archiveFilename Write-DebugVariable 'archiveFilename', 'tempArchivePath' Invoke-FileDownload "${BaseUrl}/download/${realTag}/${archiveFilename}" $tempArchivePath $checksums = Get-Checksums $realTag $version Confirm-Checksum $tempArchivePath $checksums Expand-ChezmoiArchive $tempArchivePath $binaryFilename = "chezmoi${binarySuffix}" $tempBinaryPath = Join-Path -Path $tempDir -ChildPath $binaryFilename Write-DebugVariable 'binaryFilename', 'tempBinaryPath' [System.IO.Directory]::CreateDirectory($BinDir) | Out-Null $binary = Join-Path -Path $BinDir -ChildPath $binaryFilename Write-DebugVariable 'binary' Move-Item -Path $tempBinaryPath -Destination $binary -Force Write-Information "installed ${binary}" Invoke-CleanUp $tempDir if (($null -ne $ChezmoiArgs) -and ($ChezmoiArgs.Count -gt 0)) { & $binary $ChezmoiArgs } ================================================ FILE: assets/scripts/install.sh ================================================ #!/bin/sh # chezmoi install script # contains code from and inspired by # https://github.com/client9/shlib # https://github.com/goreleaser/godownloader set -e BINDIR="${BINDIR:-bin}" TAGARG=latest LOG_LEVEL=2 tmpdir="$(mktemp -d)" trap 'rm -rf -- "${tmpdir}"' EXIT trap 'exit' INT TERM usage() { this="${1}" cat <&2 return 1 ;; esac } get_libc() { if is_command ldd; then case "$(ldd --version 2>&1 | tr '[:upper:]' '[:lower:]')" in *glibc* | *"gnu libc"*) # If the version of glibc is too old then use the statically-linked # musl version instead. chezmoi releases are built on GitHub Actions # ubuntu-22.04 runners, which have glibc version 2.35. minimum_glibc_version=2.35 glibc_version="$(ldd --version 2>&1 | awk '$1 == "ldd" { print $NF }')" # shellcheck disable=SC2046,SC2183 minimum_glibc_version_string="$(printf "%03d%03d" $(echo "${minimum_glibc_version}" | tr "." " "))" # shellcheck disable=SC2046,SC2183 glibc_version_string="$(printf "%03d%03d" $(echo "${glibc_version}" | tr "." " "))" log_info "found glibc version ${glibc_version}" if [ "${glibc_version_string}" -lt "${minimum_glibc_version_string}" ]; then printf musl return fi printf glibc return ;; *musl*) printf musl return ;; esac fi if is_command getconf; then case "$(getconf GNU_LIBC_VERSION 2>&1)" in *glibc*) printf glibc return ;; esac fi log_crit "unable to determine libc" 1>&2 exit 1 } real_tag() { tag="${1}" log_debug "checking GitHub for tag ${tag}" release_url="https://github.com/twpayne/chezmoi/releases/${tag}" json="$(http_get "${release_url}" "Accept: application/json")" if [ -z "${json}" ]; then log_err "real_tag error retrieving GitHub release ${tag}" return 1 fi real_tag="$(printf '%s\n' "${json}" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')" if [ -z "${real_tag}" ]; then log_err "real_tag error determining real tag of GitHub release ${tag}" return 1 fi if [ -z "${real_tag}" ]; then return 1 fi log_debug "found tag ${real_tag} for ${tag}" printf '%s' "${real_tag}" } http_get() { tmpfile="$(mktemp)" http_download "${tmpfile}" "${1}" "${2}" || return 1 body="$(cat "${tmpfile}")" rm -f "${tmpfile}" printf '%s\n' "${body}" } http_download_curl() { local_file="${1}" source_url="${2}" header="${3}" if [ -z "${header}" ]; then code="$(curl -w '%{http_code}' -fsSL -o "${local_file}" "${source_url}")" else code="$(curl -w '%{http_code}' -fsSL -H "${header}" -o "${local_file}" "${source_url}")" fi if [ "${code}" != "200" ]; then log_debug "http_download_curl received HTTP status ${code}" return 1 fi return 0 } http_download_wget() { local_file="${1}" source_url="${2}" header="${3}" if [ -z "${header}" ]; then wget -q -O "${local_file}" "${source_url}" || return 1 else wget -q --header "${header}" -O "${local_file}" "${source_url}" || return 1 fi } http_download() { log_debug "http_download ${2}" if is_command curl; then http_download_curl "${@}" || return 1 return elif is_command wget; then http_download_wget "${@}" || return 1 return fi log_crit "http_download unable to find wget or curl" return 1 } hash_sha256() { target="${1}" if is_command sha256sum; then hash="$(sha256sum "${target}")" || return 1 printf '%s' "${hash}" | cut -d ' ' -f 1 elif is_command shasum; then hash="$(shasum -a 256 "${target}" 2>/dev/null)" || return 1 printf '%s' "${hash}" | cut -d ' ' -f 1 elif is_command sha256; then hash="$(sha256 -q "${target}" 2>/dev/null)" || return 1 printf '%s' "${hash}" | cut -d ' ' -f 1 elif is_command openssl; then hash="$(openssl dgst -sha256 "${target}")" || return 1 printf '%s' "${hash}" | cut -d ' ' -f a else log_crit "hash_sha256 unable to find command to compute SHA256 hash" return 1 fi } hash_sha256_verify() { target="${1}" checksums="${2}" basename="${target##*/}" want="$(grep "${basename}\$" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1)" if [ -z "${want}" ]; then log_err "hash_sha256_verify unable to find checksum for ${target} in ${checksums}" return 1 fi got="$(hash_sha256 "${target}")" if [ "${want}" != "${got}" ]; then log_err "hash_sha256_verify checksum for ${target} did not verify ${want} vs ${got}" return 1 fi } untar() { tarball="${1}" case "${tarball}" in *.tar.gz | *.tgz) tar -xzf "${tarball}" ;; *.tar) tar -xf "${tarball}" ;; *.zip) unzip -- "${tarball}" ;; *) log_err "untar unknown archive format for ${tarball}" return 1 ;; esac } is_command() { type "${1}" >/dev/null 2>&1 } log_debug() { [ 3 -le "${LOG_LEVEL}" ] || return 0 printf 'debug %s\n' "${*}" 1>&2 } log_info() { [ 2 -le "${LOG_LEVEL}" ] || return 0 printf 'info %s\n' "${*}" 1>&2 } log_err() { [ 1 -le "${LOG_LEVEL}" ] || return 0 printf 'error %s\n' "${*}" 1>&2 } log_crit() { [ 0 -le "${LOG_LEVEL}" ] || return 0 printf 'critical %s\n' "${*}" 1>&2 } main "${@}" ================================================ FILE: assets/scripts/stow-to-chezmoi.sh ================================================ #!/bin/sh set -e BASEDIR="${1:-${HOME}}" STOWDIR="${2:-dotfiles}" BASEDIR="$( unset -v CDPATH cd -- "${BASEDIR}" >/dev/null 2>&1 pwd && printf . )" BASEDIR="${BASEDIR%??}" # if we have greadlink, use that READLINK="$(command -v greadlink 2>/dev/null || command -v readlink)" removelink() { [ -h "${1}" ] && ( LINK_DEST="$("${READLINK}" -f -- "${1}" && printf .)" LINK_DEST="${LINK_DEST%??}" rm -- "${1}" printf '%s ==> %s\t' "${LINK_DEST}" "${1}" >&2 if cp -r -- "${LINK_DEST}" "${1}"; then printf 'Done\n' >&2 else printf 'FAILED\n' >&2 exit 1 fi ) } work_file="$(mktemp)" act_file="$(mktemp)" # attempt to clean up temporary files on exit trap 'rm -f -- "${work_file}" "${act_file}"' EXIT trap 'exit' INT TERM find "${BASEDIR}" \! -path '* *' \! -path "${BASEDIR}/${STOWDIR}*" -type l >"${work_file}" || printf "Find skipped some files\n" >&2 while read -r f; do target="$("${READLINK}" -f -- "${f}" || :)" case "${target}" in "${BASEDIR}/${STOWDIR}/"*) printf 'Add %s\n' "${f}" >&2 printf '%s\n' "${f}" >>"${act_file}" ;; esac done <"${work_file}" printf 'Migrate the above to chezmoi? y/N ' >&2 read -r migrate case "${migrate}" in [Yy]*) printf 'Migrating...\n' >&2 ;; *) exit 1 ;; esac mkdir -p -- "${BASEDIR}/.local/share" while read -r f; do if removelink "${f}"; then chezmoi --source "${BASEDIR}/.local/share/chezmoi" --destination "${BASEDIR}" add -- "${f}" else printf 'Unable to move: %s\n' "${f}" >&2 fi done <"${act_file}" ================================================ FILE: assets/templates/COMMIT_MESSAGE.tmpl ================================================ {{- with .chezmoi.status }} {{- range .Ordinary -}} {{ if and (eq .X 'A') (eq .Y '.') -}}Add {{ .Path | targetRelPath }} {{ else if and (eq .X 'D') (eq .Y '.') -}}Remove {{ .Path | targetRelPath }} {{ else if and (eq .X 'M') (eq .Y '.') -}}Update {{ .Path | targetRelPath }} {{ else }}{{ warnf "unsupported XY: %c%c" .X .Y }} {{ end }} {{- end -}} {{- range .RenamedOrCopied -}} {{ if and (eq .X 'R') (eq .Y '.') }}Change attributes of {{ .Path | targetRelPath }} {{ else if and (eq .X 'C') (eq .Y '.') -}}Copy {{ .OrigPath | targetRelPath }} to {{ .Path | targetRelPath }} {{ else }}{{ warnf "unsupported XY: %c%c" .X .Y }} {{ end }} {{- end -}} {{- range .Unmerged -}} {{ warnf "unmerged files" }} {{- end -}} {{- range .Untracked -}} {{ warnf "untracked files" }} {{- end -}} {{- end -}} ================================================ FILE: assets/templates/install-init-shell.sh.tmpl ================================================ #!/bin/sh # FIXME inline install.sh here instead of using curl | sh # FIXME consider using packages to install chezmoi on deb and rpm-based systems set -e cd "${HOME}" is_command() { type "${1}" >/dev/null 2>&1 } if [ -n "${LOGNAME}" ]; then username="${LOGNAME}" elif [ -n "${USER}" ]; then username="${USER}" elif [ -n "${USERNAME}" ]; then username="${USERNAME}" elif is_command whoami; then username="$(whoami)" elif is_command logname; then username="$(logname)" else printf "unable to determine username" 1>&2 exit 1 fi sudo= if [ "${username}" != "root" ]; then sudo="sudo " fi chezmoi=chezmoi if is_command chezmoi; then chezmoi --version elif is_command "${HOME}/.local/bin/chezmoi"; then chezmoi="${HOME}/.local/bin/chezmoi" elif is_command "${HOME}/bin/chezmoi"; then chezmoi="${HOME}/bin/chezmoi" {{- if eq .packageManager "apk" }} elif is_command apk; then ${sudo}apk add chezmoi {{- else if eq .packageManager "apt-get" }} elif is_command apt-get; then if ! (is_command curl); then export DEBIAN_FRONTEND=noninteractive ${sudo}apt-get update ${sudo}apt-get install --yes curl fi sh -c "$(curl -fsSL get.chezmoi.io/lb)" chezmoi="$HOME/.local/bin/chezmoi" {{- else if eq .packageManager "brew" }} elif is_command brew; then ${sudo}brew install chezmoi {{- else if eq .packageManager "dnf" }} elif is_command dnf; then if ! (is_command curl); then ${sudo}dnf install curl fi sh -c "$(curl -fsSL get.chezmoi.io/lb)" chezmoi="$HOME/.local/bin/chezmoi" {{- else if eq .packageManager "nix-env" }} elif is_command nix-env; then ${sudo}nix-env -i chezmoi {{- else if eq .packageManager "pacman" }} elif is_command pacman; then ${sudo}pacman -S chezmoi {{- else if eq .packageManager "port" }} elif is_command port; then ${sudo}port install chezmoi {{- else if eq .packageManager "pkg" }} elif is_command pkg; then ${sudo}pkg install chezmoi {{- else if eq .packageManager "rpm" }} elif is_command dnf; then if ! (is_command curl); then ${sudo}rpm --install curl fi sh -c "$(curl -fsSL get.chezmoi.io/lb)" chezmoi="$HOME/.local/bin/chezmoi" {{- else if eq .packageManager "snap" }} elif is_command snap; then ${sudo}snap install chezmoi --classic {{- else if eq .packageManager "xbps-install" }} elif is_command xbps-install; then ${sudo}xbps-install -S chezmoi {{- else if eq .packageManager "zypper" }} elif is_command zypper; then ${sudo}zypper install chezmoi {{- end }} elif is_command curl; then sh -c "$(curl -fsSL get.chezmoi.io/lb)" chezmoi="$HOME/.local/bin/chezmoi" elif is_command wget; then sh -c "$(wget -qO- get.chezmoi.io/lb)" chezmoi="$HOME/.local/bin/chezmoi" else echo "unable to install chezmoi" 1>&2 exit 1 fi ${chezmoi} init --apply {{ .args | quoteList | join " " }} {{- if .shell }} shell="$(awk -F : "\$1 == \"${username}\" { print \$7 }" /etc/passwd)" exec "${shell}" {{- end }} ================================================ FILE: assets/templates/install.sh ================================================ #!/bin/sh # -e: exit on error # -u: exit on unset variables set -eu if ! chezmoi="$(command -v chezmoi)"; then bin_dir="${HOME}/.local/bin" chezmoi="${bin_dir}/chezmoi" echo "Installing chezmoi to '${chezmoi}'" >&2 if command -v curl >/dev/null; then chezmoi_install_script="$(curl -fsSL get.chezmoi.io)" elif command -v wget >/dev/null; then chezmoi_install_script="$(wget -qO- get.chezmoi.io)" else echo "To install chezmoi, you must have curl or wget installed." >&2 exit 1 fi sh -c "${chezmoi_install_script}" -- -b "${bin_dir}" unset chezmoi_install_script bin_dir fi # POSIX way to get script's dir: https://stackoverflow.com/a/29834779/12156188 script_dir="$(cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P)" set -- init --apply --source="${script_dir}" echo "Running 'chezmoi $*'" >&2 # exec: replace current process with chezmoi exec "$chezmoi" "$@" ================================================ FILE: assets/templates/templates.go ================================================ // Package templates contains chezmoi's templates. package templates import _ "embed" //go:embed COMMIT_MESSAGE.tmpl var CommitMessageTmpl string //go:embed install.sh var InstallSh []byte //go:embed install-init-shell.sh.tmpl var InstallInitShellShTmpl []byte ================================================ FILE: assets/templates/versioninfo.json.tmpl ================================================ {{- $name := "chezmoi" -}} {{- $filename := printf "%s.exe" $name -}} {{- $comments := "" -}} {{- if exists ".git" -}} {{- $commitHash := output "git" "rev-parse" "HEAD" | trim -}} {{- if ne "" (output "git" "diff" "--stat" | trim) -}} {{- $commitHash = printf "%s-dirty" $commitHash -}} {{- end -}} {{- $comments = printf "commit: %s" $commitHash -}} {{- end -}} {{- $versionStr := "v0.0.0" -}} {{- if and (exists ".git") (output "git" "tag" "--list" | trim) -}} {{- $versionStr = output "git" "describe" "--abbrev=0" "--tags" | trim -}} {{- end -}} {{- $versionDict := dict -}} {{- with semver $versionStr -}} {{- $versionDict = (dict "Major" .Major "Minor" .Minor "Patch" .Patch "Build" 0) -}} {{- end -}} {{- dict "FixedFileInfo" (dict "FileVersion" $versionDict "ProductVersion" $versionDict) "StringFileInfo" (dict "Comments" $comments "FileDescription" "Manage your dotfiles across multiple diverse machines, securely." "FileVersion" $versionStr "InternalName" $filename "LegalCopyright" (printf "Copyright (c) 2018-%s Tom Payne" (now | date "2006")) "OriginalFilename" $filename "ProductName" $name "ProductVersion" $versionStr) | toPrettyJSON }} ================================================ FILE: assets/vagrant/freebsd14.Vagrantfile ================================================ Vagrant.configure("2") do |config| config.vm.box = "generic/freebsd14" config.vm.define :freebsd14 config.vm.hostname = "freebsd14" config.vm.synced_folder ".", "/chezmoi", type: "rsync" config.vm.provision "shell", inline: <<-SHELL pkg install --quiet --yes age git gnupg go zip SHELL config.vm.provision "shell", inline: <<-SHELL echo CHEZMOI_GITHUB_ACCESS_TOKEN=#{ENV['CHEZMOI_GITHUB_ACCESS_TOKEN']} >> /home/vagrant/.bash_profile echo CHEZMOI_GITHUB_TOKEN=#{ENV['CHEZMOI_GITHUB_TOKEN']} >> /home/vagrant/.bash_profile echo GITHUB_ACCESS_TOKEN=#{ENV['GITHUB_ACCESS_TOKEN']} >> /home/vagrant/.bash_profile echo GITHUB_TOKEN=#{ENV['GITHUB_TOKEN']} >> /home/vagrant/.bash_profile echo export CHEZMOI_GITHUB_ACCESS_TOKEN CHEZMOI_GITHUB_TOKEN GITHUB_ACCESS_TOKEN GITHUB_TOKEN >> /home/vagrant/.bash_profile SHELL config.vm.provision "file", source: "assets/vagrant/freebsd14.test-chezmoi.sh", destination: "test-chezmoi.sh" end ================================================ FILE: assets/vagrant/freebsd14.test-chezmoi.sh ================================================ #!/bin/bash set -eufo pipefail git config --global --add safe.directory /chezmoi cd /chezmoi go test ./... sh assets/scripts/install.sh bin/chezmoi --version ================================================ FILE: assets/vagrant/openbsd7.Vagrantfile ================================================ Vagrant.configure("2") do |config| config.vm.box = "generic/openbsd7" config.vm.define :openbsd7 config.vm.hostname = "openbsd7" config.vm.synced_folder ".", "/chezmoi", type: "rsync" config.vm.provision "shell", inline: <<-SHELL pkg_add -x bzip2 git gnupg go zip echo CHEZMOI_GITHUB_ACCESS_TOKEN=#{ENV['CHEZMOI_GITHUB_ACCESS_TOKEN']} >> /home/vagrant/.bash_profile echo CHEZMOI_GITHUB_TOKEN=#{ENV['CHEZMOI_GITHUB_TOKEN']} >> /home/vagrant/.bash_profile echo GITHUB_ACCESS_TOKEN=#{ENV['GITHUB_ACCESS_TOKEN']} >> /home/vagrant/.bash_profile echo GITHUB_TOKEN=#{ENV['GITHUB_TOKEN']} >> /home/vagrant/.bash_profile echo export CHEZMOI_GITHUB_ACCESS_TOKEN CHEZMOI_GITHUB_TOKEN GITHUB_ACCESS_TOKEN GITHUB_TOKEN >> /home/vagrant/.bash_profile SHELL config.vm.provision "file", source: "assets/vagrant/openbsd7.test-chezmoi.sh", destination: "test-chezmoi.sh" end ================================================ FILE: assets/vagrant/openbsd7.test-chezmoi.sh ================================================ #!/bin/bash set -eufo pipefail git config --global --add safe.directory /chezmoi cd /chezmoi go test ./... sh assets/scripts/install.sh bin/chezmoi --version ================================================ FILE: assets/vagrant/test.sh ================================================ #!/bin/bash set -eufo pipefail for os in "$@"; do echo "${os}" if [ ! -f "${os}.Vagrantfile" ]; then echo "${os}.Vagrantfile not found" exit 1 fi export VAGRANT_VAGRANTFILE=assets/vagrant/${os}.Vagrantfile if ! (cd ../.. && vagrant up); then exit 1 fi vagrant ssh -c "./test-chezmoi.sh" vagrant_ssh_exit_code=$? vagrant destroy -f || exit 1 if [ $vagrant_ssh_exit_code -ne 0 ]; then exit $vagrant_ssh_exit_code fi done ================================================ FILE: bin/.gitignore ================================================ /* ================================================ FILE: completions/chezmoi-completion.bash ================================================ # bash completion V2 for chezmoi -*- shell-script -*- __chezmoi_debug() { if [[ -n ${BASH_COMP_DEBUG_FILE-} ]]; then echo "$*" >> "${BASH_COMP_DEBUG_FILE}" fi } # Macs have bash3 for which the bash-completion package doesn't include # _init_completion. This is a minimal version of that function. __chezmoi_init_completion() { COMPREPLY=() _get_comp_words_by_ref "$@" cur prev words cword } # This function calls the chezmoi program to obtain the completion # results and the directive. It fills the 'out' and 'directive' vars. __chezmoi_get_completion_results() { local requestComp lastParam lastChar args # Prepare the command to request completions for the program. # Calling ${words[0]} instead of directly chezmoi allows handling aliases args=("${words[@]:1}") requestComp="${words[0]} __complete ${args[*]}" lastParam=${words[$((${#words[@]}-1))]} lastChar=${lastParam:$((${#lastParam}-1)):1} __chezmoi_debug "lastParam ${lastParam}, lastChar ${lastChar}" if [[ -z ${cur} && ${lastChar} != = ]]; then # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go method. __chezmoi_debug "Adding extra empty parameter" requestComp="${requestComp} ''" fi # When completing a flag with an = (e.g., chezmoi -n=) # bash focuses on the part after the =, so we need to remove # the flag part from $cur if [[ ${cur} == -*=* ]]; then cur="${cur#*=}" fi __chezmoi_debug "Calling ${requestComp}" # Use eval to handle any environment variables and such out=$(eval "${requestComp}" 2>/dev/null) # Extract the directive integer at the very end of the output following a colon (:) directive=${out##*:} # Remove the directive out=${out%:*} if [[ ${directive} == "${out}" ]]; then # There is not directive specified directive=0 fi __chezmoi_debug "The completion directive is: ${directive}" __chezmoi_debug "The completions are: ${out}" } __chezmoi_process_completion_results() { local shellCompDirectiveError=1 local shellCompDirectiveNoSpace=2 local shellCompDirectiveNoFileComp=4 local shellCompDirectiveFilterFileExt=8 local shellCompDirectiveFilterDirs=16 local shellCompDirectiveKeepOrder=32 if (((directive & shellCompDirectiveError) != 0)); then # Error code. No completion. __chezmoi_debug "Received error from custom completion go code" return else if (((directive & shellCompDirectiveNoSpace) != 0)); then if [[ $(type -t compopt) == builtin ]]; then __chezmoi_debug "Activating no space" compopt -o nospace else __chezmoi_debug "No space directive not supported in this version of bash" fi fi if (((directive & shellCompDirectiveKeepOrder) != 0)); then if [[ $(type -t compopt) == builtin ]]; then # no sort isn't supported for bash less than < 4.4 if [[ ${BASH_VERSINFO[0]} -lt 4 || ( ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 4 ) ]]; then __chezmoi_debug "No sort directive not supported in this version of bash" else __chezmoi_debug "Activating keep order" compopt -o nosort fi else __chezmoi_debug "No sort directive not supported in this version of bash" fi fi if (((directive & shellCompDirectiveNoFileComp) != 0)); then if [[ $(type -t compopt) == builtin ]]; then __chezmoi_debug "Activating no file completion" compopt +o default else __chezmoi_debug "No file completion directive not supported in this version of bash" fi fi fi # Separate activeHelp from normal completions local completions=() local activeHelp=() __chezmoi_extract_activeHelp if (((directive & shellCompDirectiveFilterFileExt) != 0)); then # File extension filtering local fullFilter="" filter filteringCmd # Do not use quotes around the $completions variable or else newline # characters will be kept. for filter in ${completions[*]}; do fullFilter+="$filter|" done filteringCmd="_filedir $fullFilter" __chezmoi_debug "File filtering command: $filteringCmd" $filteringCmd elif (((directive & shellCompDirectiveFilterDirs) != 0)); then # File completion for directories only local subdir subdir=${completions[0]} if [[ -n $subdir ]]; then __chezmoi_debug "Listing directories in $subdir" pushd "$subdir" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return else __chezmoi_debug "Listing directories in ." _filedir -d fi else __chezmoi_handle_completion_types fi __chezmoi_handle_special_char "$cur" : __chezmoi_handle_special_char "$cur" = # Print the activeHelp statements before we finish __chezmoi_handle_activeHelp } __chezmoi_handle_activeHelp() { # Print the activeHelp statements if ((${#activeHelp[*]} != 0)); then if [ -z $COMP_TYPE ]; then # Bash v3 does not set the COMP_TYPE variable. printf "\n"; printf "%s\n" "${activeHelp[@]}" printf "\n" __chezmoi_reprint_commandLine return fi # Only print ActiveHelp on the second TAB press if [ $COMP_TYPE -eq 63 ]; then printf "\n" printf "%s\n" "${activeHelp[@]}" if ((${#COMPREPLY[*]} == 0)); then # When there are no completion choices from the program, file completion # may kick in if the program has not disabled it; in such a case, we want # to know if any files will match what the user typed, so that we know if # there will be completions presented, so that we know how to handle ActiveHelp. # To find out, we actually trigger the file completion ourselves; # the call to _filedir will fill COMPREPLY if files match. if (((directive & shellCompDirectiveNoFileComp) == 0)); then __chezmoi_debug "Listing files" _filedir fi fi if ((${#COMPREPLY[*]} != 0)); then # If there are completion choices to be shown, print a delimiter. # Re-printing the command-line will automatically be done # by the shell when it prints the completion choices. printf -- "--" else # When there are no completion choices at all, we need # to re-print the command-line since the shell will # not be doing it itself. __chezmoi_reprint_commandLine fi elif [ $COMP_TYPE -eq 37 ] || [ $COMP_TYPE -eq 42 ]; then # For completion type: menu-complete/menu-complete-backward and insert-completions # the completions are immediately inserted into the command-line, so we first # print the activeHelp message and reprint the command-line since the shell won't. printf "\n" printf "%s\n" "${activeHelp[@]}" __chezmoi_reprint_commandLine fi fi } __chezmoi_reprint_commandLine() { # The prompt format is only available from bash 4.4. # We test if it is available before using it. if (x=${PS1@P}) 2> /dev/null; then printf "%s" "${PS1@P}${COMP_LINE[@]}" else # Can't print the prompt. Just print the # text the user had typed, it is workable enough. printf "%s" "${COMP_LINE[@]}" fi } # Separate activeHelp lines from real completions. # Fills the $activeHelp and $completions arrays. __chezmoi_extract_activeHelp() { local activeHelpMarker="_activeHelp_ " local endIndex=${#activeHelpMarker} while IFS='' read -r comp; do [[ -z $comp ]] && continue if [[ ${comp:0:endIndex} == $activeHelpMarker ]]; then comp=${comp:endIndex} __chezmoi_debug "ActiveHelp found: $comp" if [[ -n $comp ]]; then activeHelp+=("$comp") fi else # Not an activeHelp line but a normal completion completions+=("$comp") fi done <<<"${out}" } __chezmoi_handle_completion_types() { __chezmoi_debug "__chezmoi_handle_completion_types: COMP_TYPE is $COMP_TYPE" case $COMP_TYPE in 37|42) # Type: menu-complete/menu-complete-backward and insert-completions # If the user requested inserting one completion at a time, or all # completions at once on the command-line we must remove the descriptions. # https://github.com/spf13/cobra/issues/1508 # If there are no completions, we don't need to do anything (( ${#completions[@]} == 0 )) && return 0 local tab=$'\t' # Strip any description and escape the completion to handled special characters IFS=$'\n' read -ra completions -d '' < <(printf "%q\n" "${completions[@]%%$tab*}") # Only consider the completions that match IFS=$'\n' read -ra COMPREPLY -d '' < <(IFS=$'\n'; compgen -W "${completions[*]}" -- "${cur}") # compgen looses the escaping so we need to escape all completions again since they will # all be inserted on the command-line. IFS=$'\n' read -ra COMPREPLY -d '' < <(printf "%q\n" "${COMPREPLY[@]}") ;; *) # Type: complete (normal completion) __chezmoi_handle_standard_completion_case ;; esac } __chezmoi_handle_standard_completion_case() { local tab=$'\t' # If there are no completions, we don't need to do anything (( ${#completions[@]} == 0 )) && return 0 # Short circuit to optimize if we don't have descriptions if [[ "${completions[*]}" != *$tab* ]]; then # First, escape the completions to handle special characters IFS=$'\n' read -ra completions -d '' < <(printf "%q\n" "${completions[@]}") # Only consider the completions that match what the user typed IFS=$'\n' read -ra COMPREPLY -d '' < <(IFS=$'\n'; compgen -W "${completions[*]}" -- "${cur}") # compgen looses the escaping so, if there is only a single completion, we need to # escape it again because it will be inserted on the command-line. If there are multiple # completions, we don't want to escape them because they will be printed in a list # and we don't want to show escape characters in that list. if (( ${#COMPREPLY[@]} == 1 )); then COMPREPLY[0]=$(printf "%q" "${COMPREPLY[0]}") fi return 0 fi local longest=0 local compline # Look for the longest completion so that we can format things nicely while IFS='' read -r compline; do [[ -z $compline ]] && continue # Before checking if the completion matches what the user typed, # we need to strip any description and escape the completion to handle special # characters because those escape characters are part of what the user typed. # Don't call "printf" in a sub-shell because it will be much slower # since we are in a loop. printf -v comp "%q" "${compline%%$tab*}" &>/dev/null || comp=$(printf "%q" "${compline%%$tab*}") # Only consider the completions that match [[ $comp == "$cur"* ]] || continue # The completions matches. Add it to the list of full completions including # its description. We don't escape the completion because it may get printed # in a list if there are more than one and we don't want show escape characters # in that list. COMPREPLY+=("$compline") # Strip any description before checking the length, and again, don't escape # the completion because this length is only used when printing the completions # in a list and we don't want show escape characters in that list. comp=${compline%%$tab*} if ((${#comp}>longest)); then longest=${#comp} fi done < <(printf "%s\n" "${completions[@]}") # If there is a single completion left, remove the description text and escape any special characters if ((${#COMPREPLY[*]} == 1)); then __chezmoi_debug "COMPREPLY[0]: ${COMPREPLY[0]}" COMPREPLY[0]=$(printf "%q" "${COMPREPLY[0]%%$tab*}") __chezmoi_debug "Removed description from single completion, which is now: ${COMPREPLY[0]}" else # Format the descriptions __chezmoi_format_comp_descriptions $longest fi } __chezmoi_handle_special_char() { local comp="$1" local char=$2 if [[ "$comp" == *${char}* && "$COMP_WORDBREAKS" == *${char}* ]]; then local word=${comp%"${comp##*${char}}"} local idx=${#COMPREPLY[*]} while ((--idx >= 0)); do COMPREPLY[idx]=${COMPREPLY[idx]#"$word"} done fi } __chezmoi_format_comp_descriptions() { local tab=$'\t' local comp desc maxdesclength local longest=$1 local i ci for ci in ${!COMPREPLY[*]}; do comp=${COMPREPLY[ci]} # Properly format the description string which follows a tab character if there is one if [[ "$comp" == *$tab* ]]; then __chezmoi_debug "Original comp: $comp" desc=${comp#*$tab} comp=${comp%%$tab*} # $COLUMNS stores the current shell width. # Remove an extra 4 because we add 2 spaces and 2 parentheses. maxdesclength=$(( COLUMNS - longest - 4 )) # Make sure we can fit a description of at least 8 characters # if we are to align the descriptions. if ((maxdesclength > 8)); then # Add the proper number of spaces to align the descriptions for ((i = ${#comp} ; i < longest ; i++)); do comp+=" " done else # Don't pad the descriptions so we can fit more text after the completion maxdesclength=$(( COLUMNS - ${#comp} - 4 )) fi # If there is enough space for any description text, # truncate the descriptions that are too long for the shell width if ((maxdesclength > 0)); then if ((${#desc} > maxdesclength)); then desc=${desc:0:$(( maxdesclength - 1 ))} desc+="…" fi comp+=" ($desc)" fi COMPREPLY[ci]=$comp __chezmoi_debug "Final comp: $comp" fi done } __start_chezmoi() { local cur prev words cword split COMPREPLY=() # Call _init_completion from the bash-completion package # to prepare the arguments properly if declare -F _init_completion >/dev/null 2>&1; then _init_completion -n =: || return else __chezmoi_init_completion -n =: || return fi __chezmoi_debug __chezmoi_debug "========= starting completion logic ==========" __chezmoi_debug "cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}, cword is $cword" # The user could have moved the cursor backwards on the command-line. # We need to trigger completion from the $cword location, so we need # to truncate the command-line ($words) up to the $cword location. words=("${words[@]:0:$cword+1}") __chezmoi_debug "Truncated words[*]: ${words[*]}," local out directive __chezmoi_get_completion_results __chezmoi_process_completion_results } if [[ $(type -t compopt) = "builtin" ]]; then complete -o default -F __start_chezmoi chezmoi else complete -o default -o nospace -F __start_chezmoi chezmoi fi # ex: ts=4 sw=4 et filetype=sh ================================================ FILE: completions/chezmoi.fish ================================================ # fish completion for chezmoi -*- shell-script -*- function __chezmoi_debug set -l file "$BASH_COMP_DEBUG_FILE" if test -n "$file" echo "$argv" >> $file end end function __chezmoi_perform_completion __chezmoi_debug "Starting __chezmoi_perform_completion" # Extract all args except the last one set -l args (commandline -opc) # Extract the last arg and escape it in case it is a space set -l lastArg (string escape -- (commandline -ct)) __chezmoi_debug "args: $args" __chezmoi_debug "last arg: $lastArg" # Disable ActiveHelp which is not supported for fish shell set -l requestComp "CHEZMOI_ACTIVE_HELP=0 $args[1] __complete $args[2..-1] $lastArg" __chezmoi_debug "Calling $requestComp" set -l results (eval $requestComp 2> /dev/null) # Some programs may output extra empty lines after the directive. # Let's ignore them or else it will break completion. # Ref: https://github.com/spf13/cobra/issues/1279 for line in $results[-1..1] if test (string trim -- $line) = "" # Found an empty line, remove it set results $results[1..-2] else # Found non-empty line, we have our proper output break end end set -l comps $results[1..-2] set -l directiveLine $results[-1] # For Fish, when completing a flag with an = (e.g., -n=) # completions must be prefixed with the flag set -l flagPrefix (string match -r -- '-.*=' "$lastArg") __chezmoi_debug "Comps: $comps" __chezmoi_debug "DirectiveLine: $directiveLine" __chezmoi_debug "flagPrefix: $flagPrefix" for comp in $comps printf "%s%s\n" "$flagPrefix" "$comp" end printf "%s\n" "$directiveLine" end # this function limits calls to __chezmoi_perform_completion, by caching the result behind $__chezmoi_perform_completion_once_result function __chezmoi_perform_completion_once __chezmoi_debug "Starting __chezmoi_perform_completion_once" if test -n "$__chezmoi_perform_completion_once_result" __chezmoi_debug "Seems like a valid result already exists, skipping __chezmoi_perform_completion" return 0 end set --global __chezmoi_perform_completion_once_result (__chezmoi_perform_completion) if test -z "$__chezmoi_perform_completion_once_result" __chezmoi_debug "No completions, probably due to a failure" return 1 end __chezmoi_debug "Performed completions and set __chezmoi_perform_completion_once_result" return 0 end # this function is used to clear the $__chezmoi_perform_completion_once_result variable after completions are run function __chezmoi_clear_perform_completion_once_result __chezmoi_debug "" __chezmoi_debug "========= clearing previously set __chezmoi_perform_completion_once_result variable ==========" set --erase __chezmoi_perform_completion_once_result __chezmoi_debug "Successfully erased the variable __chezmoi_perform_completion_once_result" end function __chezmoi_requires_order_preservation __chezmoi_debug "" __chezmoi_debug "========= checking if order preservation is required ==========" __chezmoi_perform_completion_once if test -z "$__chezmoi_perform_completion_once_result" __chezmoi_debug "Error determining if order preservation is required" return 1 end set -l directive (string sub --start 2 $__chezmoi_perform_completion_once_result[-1]) __chezmoi_debug "Directive is: $directive" set -l shellCompDirectiveKeepOrder 32 set -l keeporder (math (math --scale 0 $directive / $shellCompDirectiveKeepOrder) % 2) __chezmoi_debug "Keeporder is: $keeporder" if test $keeporder -ne 0 __chezmoi_debug "This does require order preservation" return 0 end __chezmoi_debug "This doesn't require order preservation" return 1 end # This function does two things: # - Obtain the completions and store them in the global __chezmoi_comp_results # - Return false if file completion should be performed function __chezmoi_prepare_completions __chezmoi_debug "" __chezmoi_debug "========= starting completion logic ==========" # Start fresh set --erase __chezmoi_comp_results __chezmoi_perform_completion_once __chezmoi_debug "Completion results: $__chezmoi_perform_completion_once_result" if test -z "$__chezmoi_perform_completion_once_result" __chezmoi_debug "No completion, probably due to a failure" # Might as well do file completion, in case it helps return 1 end set -l directive (string sub --start 2 $__chezmoi_perform_completion_once_result[-1]) set --global __chezmoi_comp_results $__chezmoi_perform_completion_once_result[1..-2] __chezmoi_debug "Completions are: $__chezmoi_comp_results" __chezmoi_debug "Directive is: $directive" set -l shellCompDirectiveError 1 set -l shellCompDirectiveNoSpace 2 set -l shellCompDirectiveNoFileComp 4 set -l shellCompDirectiveFilterFileExt 8 set -l shellCompDirectiveFilterDirs 16 if test -z "$directive" set directive 0 end set -l compErr (math (math --scale 0 $directive / $shellCompDirectiveError) % 2) if test $compErr -eq 1 __chezmoi_debug "Received error directive: aborting." # Might as well do file completion, in case it helps return 1 end set -l filefilter (math (math --scale 0 $directive / $shellCompDirectiveFilterFileExt) % 2) set -l dirfilter (math (math --scale 0 $directive / $shellCompDirectiveFilterDirs) % 2) if test $filefilter -eq 1; or test $dirfilter -eq 1 __chezmoi_debug "File extension filtering or directory filtering not supported" # Do full file completion instead return 1 end set -l nospace (math (math --scale 0 $directive / $shellCompDirectiveNoSpace) % 2) set -l nofiles (math (math --scale 0 $directive / $shellCompDirectiveNoFileComp) % 2) __chezmoi_debug "nospace: $nospace, nofiles: $nofiles" # If we want to prevent a space, or if file completion is NOT disabled, # we need to count the number of valid completions. # To do so, we will filter on prefix as the completions we have received # may not already be filtered so as to allow fish to match on different # criteria than the prefix. if test $nospace -ne 0; or test $nofiles -eq 0 set -l prefix (commandline -t | string escape --style=regex) __chezmoi_debug "prefix: $prefix" set -l completions (string match -r -- "^$prefix.*" $__chezmoi_comp_results) set --global __chezmoi_comp_results $completions __chezmoi_debug "Filtered completions are: $__chezmoi_comp_results" # Important not to quote the variable for count to work set -l numComps (count $__chezmoi_comp_results) __chezmoi_debug "numComps: $numComps" if test $numComps -eq 1; and test $nospace -ne 0 # We must first split on \t to get rid of the descriptions to be # able to check what the actual completion will be. # We don't need descriptions anyway since there is only a single # real completion which the shell will expand immediately. set -l split (string split --max 1 \t $__chezmoi_comp_results[1]) # Fish won't add a space if the completion ends with any # of the following characters: @=/:., set -l lastChar (string sub -s -1 -- $split) if not string match -r -q "[@=/:.,]" -- "$lastChar" # In other cases, to support the "nospace" directive we trick the shell # by outputting an extra, longer completion. __chezmoi_debug "Adding second completion to perform nospace directive" set --global __chezmoi_comp_results $split[1] $split[1]. __chezmoi_debug "Completions are now: $__chezmoi_comp_results" end end if test $numComps -eq 0; and test $nofiles -eq 0 # To be consistent with bash and zsh, we only trigger file # completion when there are no other completions __chezmoi_debug "Requesting file completion" return 1 end end return 0 end # Since Fish completions are only loaded once the user triggers them, we trigger them ourselves # so we can properly delete any completions provided by another script. # Only do this if the program can be found, or else fish may print some errors; besides, # the existing completions will only be loaded if the program can be found. if type -q "chezmoi" # The space after the program name is essential to trigger completion for the program # and not completion of the program name itself. # Also, we use '> /dev/null 2>&1' since '&>' is not supported in older versions of fish. complete --do-complete "chezmoi " > /dev/null 2>&1 end # Remove any pre-existing completions for the program since we will be handling all of them. complete -c chezmoi -e # this will get called after the two calls below and clear the $__chezmoi_perform_completion_once_result global complete -c chezmoi -n '__chezmoi_clear_perform_completion_once_result' # The call to __chezmoi_prepare_completions will setup __chezmoi_comp_results # which provides the program's completion choices. # If this doesn't require order preservation, we don't use the -k flag complete -c chezmoi -n 'not __chezmoi_requires_order_preservation && __chezmoi_prepare_completions' -f -a '$__chezmoi_comp_results' # otherwise we use the -k flag complete -k -c chezmoi -n '__chezmoi_requires_order_preservation && __chezmoi_prepare_completions' -f -a '$__chezmoi_comp_results' ================================================ FILE: completions/chezmoi.ps1 ================================================ # powershell completion for chezmoi -*- shell-script -*- function __chezmoi_debug { if ($env:BASH_COMP_DEBUG_FILE) { "$args" | Out-File -Append -FilePath "$env:BASH_COMP_DEBUG_FILE" } } filter __chezmoi_escapeStringWithSpecialChars { $_ -replace '\s|#|@|\$|;|,|''|\{|\}|\(|\)|"|`|\||<|>|&','`$&' } [scriptblock]${__chezmoiCompleterBlock} = { param( $WordToComplete, $CommandAst, $CursorPosition ) # Get the current command line and convert into a string $Command = $CommandAst.CommandElements $Command = "$Command" __chezmoi_debug "" __chezmoi_debug "========= starting completion logic ==========" __chezmoi_debug "WordToComplete: $WordToComplete Command: $Command CursorPosition: $CursorPosition" # The user could have moved the cursor backwards on the command-line. # We need to trigger completion from the $CursorPosition location, so we need # to truncate the command-line ($Command) up to the $CursorPosition location. # Make sure the $Command is longer then the $CursorPosition before we truncate. # This happens because the $Command does not include the last space. if ($Command.Length -gt $CursorPosition) { $Command=$Command.Substring(0,$CursorPosition) } __chezmoi_debug "Truncated command: $Command" $ShellCompDirectiveError=1 $ShellCompDirectiveNoSpace=2 $ShellCompDirectiveNoFileComp=4 $ShellCompDirectiveFilterFileExt=8 $ShellCompDirectiveFilterDirs=16 $ShellCompDirectiveKeepOrder=32 # Prepare the command to request completions for the program. # Split the command at the first space to separate the program and arguments. $Program,$Arguments = $Command.Split(" ",2) $RequestComp="$Program __complete $Arguments" __chezmoi_debug "RequestComp: $RequestComp" # we cannot use $WordToComplete because it # has the wrong values if the cursor was moved # so use the last argument if ($WordToComplete -ne "" ) { $WordToComplete = $Arguments.Split(" ")[-1] } __chezmoi_debug "New WordToComplete: $WordToComplete" # Check for flag with equal sign $IsEqualFlag = ($WordToComplete -Like "--*=*" ) if ( $IsEqualFlag ) { __chezmoi_debug "Completing equal sign flag" # Remove the flag part $Flag,$WordToComplete = $WordToComplete.Split("=",2) } if ( $WordToComplete -eq "" -And ( -Not $IsEqualFlag )) { # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go method. __chezmoi_debug "Adding extra empty parameter" # PowerShell 7.2+ changed the way how the arguments are passed to executables, # so for pre-7.2 or when Legacy argument passing is enabled we need to use # `"`" to pass an empty argument, a "" or '' does not work!!! if ($PSVersionTable.PsVersion -lt [version]'7.2.0' -or ($PSVersionTable.PsVersion -lt [version]'7.3.0' -and -not [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -or (($PSVersionTable.PsVersion -ge [version]'7.3.0' -or [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -and $PSNativeCommandArgumentPassing -eq 'Legacy')) { $RequestComp="$RequestComp" + ' `"`"' } else { $RequestComp="$RequestComp" + ' ""' } } __chezmoi_debug "Calling $RequestComp" # First disable ActiveHelp which is not supported for Powershell ${env:CHEZMOI_ACTIVE_HELP}=0 #call the command store the output in $out and redirect stderr and stdout to null # $Out is an array contains each line per element Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null # get directive from last line [int]$Directive = $Out[-1].TrimStart(':') if ($Directive -eq "") { # There is no directive specified $Directive = 0 } __chezmoi_debug "The completion directive is: $Directive" # remove directive (last element) from out $Out = $Out | Where-Object { $_ -ne $Out[-1] } __chezmoi_debug "The completions are: $Out" if (($Directive -band $ShellCompDirectiveError) -ne 0 ) { # Error code. No completion. __chezmoi_debug "Received error from custom completion go code" return } $Longest = 0 [Array]$Values = $Out | ForEach-Object { #Split the output in name and description $Name, $Description = $_.Split("`t",2) __chezmoi_debug "Name: $Name Description: $Description" # Look for the longest completion so that we can format things nicely if ($Longest -lt $Name.Length) { $Longest = $Name.Length } # Set the description to a one space string if there is none set. # This is needed because the CompletionResult does not accept an empty string as argument if (-Not $Description) { $Description = " " } New-Object -TypeName PSCustomObject -Property @{ Name = "$Name" Description = "$Description" } } $Space = " " if (($Directive -band $ShellCompDirectiveNoSpace) -ne 0 ) { # remove the space here __chezmoi_debug "ShellCompDirectiveNoSpace is called" $Space = "" } if ((($Directive -band $ShellCompDirectiveFilterFileExt) -ne 0 ) -or (($Directive -band $ShellCompDirectiveFilterDirs) -ne 0 )) { __chezmoi_debug "ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs are not supported" # return here to prevent the completion of the extensions return } $Values = $Values | Where-Object { # filter the result $_.Name -like "$WordToComplete*" # Join the flag back if we have an equal sign flag if ( $IsEqualFlag ) { __chezmoi_debug "Join the equal sign flag back to the completion value" $_.Name = $Flag + "=" + $_.Name } } # we sort the values in ascending order by name if keep order isn't passed if (($Directive -band $ShellCompDirectiveKeepOrder) -eq 0 ) { $Values = $Values | Sort-Object -Property Name } if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) { __chezmoi_debug "ShellCompDirectiveNoFileComp is called" if ($Values.Length -eq 0) { # Just print an empty string here so the # shell does not start to complete paths. # We cannot use CompletionResult here because # it does not accept an empty string as argument. "" return } } # Get the current mode $Mode = (Get-PSReadLineKeyHandler | Where-Object {$_.Key -eq "Tab" }).Function __chezmoi_debug "Mode: $Mode" $Values | ForEach-Object { # store temporary because switch will overwrite $_ $comp = $_ # PowerShell supports three different completion modes # - TabCompleteNext (default windows style - on each key press the next option is displayed) # - Complete (works like bash) # - MenuComplete (works like zsh) # You set the mode with Set-PSReadLineKeyHandler -Key Tab -Function # CompletionResult Arguments: # 1) CompletionText text to be used as the auto completion result # 2) ListItemText text to be displayed in the suggestion list # 3) ResultType type of completion result # 4) ToolTip text for the tooltip with details about the object switch ($Mode) { # bash like "Complete" { if ($Values.Length -eq 1) { __chezmoi_debug "Only one completion left" # insert space after value $CompletionText = $($comp.Name | __chezmoi_escapeStringWithSpecialChars) + $Space if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") } else { $CompletionText } } else { # Add the proper number of spaces to align the descriptions while($comp.Name.Length -lt $Longest) { $comp.Name = $comp.Name + " " } # Check for empty description and only add parentheses if needed if ($($comp.Description) -eq " " ) { $Description = "" } else { $Description = " ($($comp.Description))" } $CompletionText = "$($comp.Name)$Description" if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)") } else { $CompletionText } } } # zsh like "MenuComplete" { # insert space after value # MenuComplete will automatically show the ToolTip of # the highlighted value at the bottom of the suggestions. $CompletionText = $($comp.Name | __chezmoi_escapeStringWithSpecialChars) + $Space if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") } else { $CompletionText } } # TabCompleteNext and in case we get something unknown Default { # Like MenuComplete but we don't want to add a space here because # the user need to press space anyway to get the completion. # Description will not be shown because that's not possible with TabCompleteNext $CompletionText = $($comp.Name | __chezmoi_escapeStringWithSpecialChars) if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") } else { $CompletionText } } } } } Register-ArgumentCompleter -CommandName 'chezmoi' -ScriptBlock ${__chezmoiCompleterBlock} ================================================ FILE: completions/chezmoi.zsh ================================================ #compdef chezmoi compdef _chezmoi chezmoi # zsh completion for chezmoi -*- shell-script -*- __chezmoi_debug() { local file="$BASH_COMP_DEBUG_FILE" if [[ -n ${file} ]]; then echo "$*" >> "${file}" fi } _chezmoi() { local shellCompDirectiveError=1 local shellCompDirectiveNoSpace=2 local shellCompDirectiveNoFileComp=4 local shellCompDirectiveFilterFileExt=8 local shellCompDirectiveFilterDirs=16 local shellCompDirectiveKeepOrder=32 local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace keepOrder local -a completions __chezmoi_debug "\n========= starting completion logic ==========" __chezmoi_debug "CURRENT: ${CURRENT}, words[*]: ${words[*]}" # The user could have moved the cursor backwards on the command-line. # We need to trigger completion from the $CURRENT location, so we need # to truncate the command-line ($words) up to the $CURRENT location. # (We cannot use $CURSOR as its value does not work when a command is an alias.) words=("${=words[1,CURRENT]}") __chezmoi_debug "Truncated words[*]: ${words[*]}," lastParam=${words[-1]} lastChar=${lastParam[-1]} __chezmoi_debug "lastParam: ${lastParam}, lastChar: ${lastChar}" # For zsh, when completing a flag with an = (e.g., chezmoi -n=) # completions must be prefixed with the flag setopt local_options BASH_REMATCH if [[ "${lastParam}" =~ '-.*=' ]]; then # We are dealing with a flag with an = flagPrefix="-P ${BASH_REMATCH}" fi # Prepare the command to obtain completions requestComp="${words[1]} __complete ${words[2,-1]}" if [ "${lastChar}" = "" ]; then # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go completion code. __chezmoi_debug "Adding extra empty parameter" requestComp="${requestComp} \"\"" fi __chezmoi_debug "About to call: eval ${requestComp}" # Use eval to handle any environment variables and such out=$(eval ${requestComp} 2>/dev/null) __chezmoi_debug "completion output: ${out}" # Extract the directive integer following a : from the last line local lastLine while IFS='\n' read -r line; do lastLine=${line} done < <(printf "%s\n" "${out[@]}") __chezmoi_debug "last line: ${lastLine}" if [ "${lastLine[1]}" = : ]; then directive=${lastLine[2,-1]} # Remove the directive including the : and the newline local suffix (( suffix=${#lastLine}+2)) out=${out[1,-$suffix]} else # There is no directive specified. Leave $out as is. __chezmoi_debug "No directive found. Setting do default" directive=0 fi __chezmoi_debug "directive: ${directive}" __chezmoi_debug "completions: ${out}" __chezmoi_debug "flagPrefix: ${flagPrefix}" if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then __chezmoi_debug "Completion received error. Ignoring completions." return fi local activeHelpMarker="_activeHelp_ " local endIndex=${#activeHelpMarker} local startIndex=$((${#activeHelpMarker}+1)) local hasActiveHelp=0 while IFS='\n' read -r comp; do # Check if this is an activeHelp statement (i.e., prefixed with $activeHelpMarker) if [ "${comp[1,$endIndex]}" = "$activeHelpMarker" ];then __chezmoi_debug "ActiveHelp found: $comp" comp="${comp[$startIndex,-1]}" if [ -n "$comp" ]; then compadd -x "${comp}" __chezmoi_debug "ActiveHelp will need delimiter" hasActiveHelp=1 fi continue fi if [ -n "$comp" ]; then # If requested, completions are returned with a description. # The description is preceded by a TAB character. # For zsh's _describe, we need to use a : instead of a TAB. # We first need to escape any : as part of the completion itself. comp=${comp//:/\\:} local tab="$(printf '\t')" comp=${comp//$tab/:} __chezmoi_debug "Adding completion: ${comp}" completions+=${comp} lastComp=$comp fi done < <(printf "%s\n" "${out[@]}") # Add a delimiter after the activeHelp statements, but only if: # - there are completions following the activeHelp statements, or # - file completion will be performed (so there will be choices after the activeHelp) if [ $hasActiveHelp -eq 1 ]; then if [ ${#completions} -ne 0 ] || [ $((directive & shellCompDirectiveNoFileComp)) -eq 0 ]; then __chezmoi_debug "Adding activeHelp delimiter" compadd -x "--" hasActiveHelp=0 fi fi if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then __chezmoi_debug "Activating nospace." noSpace="-S ''" fi if [ $((directive & shellCompDirectiveKeepOrder)) -ne 0 ]; then __chezmoi_debug "Activating keep order." keepOrder="-V" fi if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then # File extension filtering local filteringCmd filteringCmd='_files' for filter in ${completions[@]}; do if [ ${filter[1]} != '*' ]; then # zsh requires a glob pattern to do file filtering filter="\*.$filter" fi filteringCmd+=" -g $filter" done filteringCmd+=" ${flagPrefix}" __chezmoi_debug "File filtering command: $filteringCmd" _arguments '*:filename:'"$filteringCmd" elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then # File completion for directories only local subdir subdir="${completions[1]}" if [ -n "$subdir" ]; then __chezmoi_debug "Listing directories in $subdir" pushd "${subdir}" >/dev/null 2>&1 else __chezmoi_debug "Listing directories in ." fi local result _arguments '*:dirname:_files -/'" ${flagPrefix}" result=$? if [ -n "$subdir" ]; then popd >/dev/null 2>&1 fi return $result else __chezmoi_debug "Calling _describe" if eval _describe $keepOrder "completions" completions $flagPrefix $noSpace; then __chezmoi_debug "_describe found some completions" # Return the success of having called _describe return 0 else __chezmoi_debug "_describe did not find completions." __chezmoi_debug "Checking if we should do file completion." if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then __chezmoi_debug "deactivating file completion" # We must return an error code here to let zsh know that there were no # completions found by _describe; this is what will trigger other # matching algorithms to attempt to find completions. # For example zsh can match letters in the middle of words. return 1 else # Perform file completion __chezmoi_debug "Activating file completion" # We must return the result of this command, so it must be the # last command, or else we must store its result to return it. _arguments '*:filename:_files'" ${flagPrefix}" fi fi fi } # don't run the completion function when being source-ed or eval-ed if [ "$funcstack[1]" = "_chezmoi" ]; then _chezmoi fi ================================================ FILE: go.mod ================================================ module chezmoi.io/chezmoi go 1.25.7 tool ( chezmoi.io/chezmoi chezmoi.io/chezmoi/internal/cmds/execute-template chezmoi.io/chezmoi/internal/cmds/generate-commit chezmoi.io/chezmoi/internal/cmds/generate-helps chezmoi.io/chezmoi/internal/cmds/generate-install.sh chezmoi.io/chezmoi/internal/cmds/generate-license chezmoi.io/chezmoi/internal/cmds/hexencode chezmoi.io/chezmoi/internal/cmds/lint-commit-messages chezmoi.io/chezmoi/internal/cmds/lint-txtar chezmoi.io/chezmoi/internal/cmds/lint-whitespace github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker github.com/google/capslock/cmd/capslock github.com/josephspurrier/goversioninfo/cmd/goversioninfo github.com/rhysd/actionlint/cmd/actionlint github.com/twpayne/find-typos github.com/twpayne/go-jsonstruct/v3/cmd/gojsonstruct ) require ( filippo.io/age v1.3.1 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0 github.com/BurntSushi/toml v1.6.0 github.com/Masterminds/sprig/v3 v3.3.0 github.com/Shopify/ejson v1.5.4 github.com/alecthomas/assert/v2 v2.11.0 github.com/aws/aws-sdk-go-v2 v1.41.4 github.com/aws/aws-sdk-go-v2/config v1.32.12 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.41.4 github.com/bmatcuk/doublestar/v4 v4.10.0 github.com/bradenhilton/mozillainstallhash v1.0.1 github.com/charmbracelet/bubbles v0.20.0 github.com/charmbracelet/bubbletea v1.3.10 github.com/charmbracelet/glamour v1.0.0 github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 github.com/coreos/go-semver v0.3.1 github.com/fsnotify/fsnotify v1.9.0 github.com/go-git/go-git/v5 v5.17.0 github.com/go-sprout/sprout v1.0.3 github.com/go-viper/mapstructure/v2 v2.5.0 github.com/goccy/go-yaml v1.19.2 github.com/google/go-github/v61 v61.0.0 github.com/google/renameio/v2 v2.0.2 github.com/gopasspw/gopass v1.16.1 github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 github.com/itchyny/gojq v0.12.18 github.com/klauspost/compress v1.18.4 github.com/mattn/go-runewidth v0.0.21 github.com/mitchellh/copystructure v1.2.0 github.com/muesli/combinator v0.3.0 github.com/muesli/termenv v0.16.0 github.com/nwaples/rardecode/v2 v2.2.2 github.com/pelletier/go-toml/v2 v2.2.4 github.com/rogpeppe/go-internal v1.14.1 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd github.com/tobischo/gokeepasslib/v3 v3.6.2 github.com/twpayne/go-expect v0.0.2-0.20241130000624-916db2914efd github.com/twpayne/go-pinentry/v4 v4.0.1 github.com/twpayne/go-shell v0.5.0 github.com/twpayne/go-vfs/v5 v5.0.5 github.com/twpayne/go-xdg/v6 v6.1.3 github.com/ulikunitz/xz v0.5.15 github.com/zalando/go-keyring v0.2.6 github.com/zricethezav/gitleaks/v8 v8.30.1 go.etcd.io/bbolt v1.4.3 golang.org/x/crypto v0.49.0 golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.20.0 golang.org/x/sys v0.42.0 golang.org/x/term v0.41.0 golang.org/x/text v0.35.0 gopkg.in/ini.v1 v1.67.1 howett.net/plist v1.0.1 mvdan.cc/sh/v3 v3.13.0 znkr.io/diff v1.0.0-beta.4 ) require ( al.essio.dev/pkg/shellescape v1.6.0 // indirect dario.cat/mergo v1.0.2 // indirect filippo.io/edwards25519 v1.2.0 // indirect filippo.io/hpke v0.4.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.7.0 // indirect github.com/BobuSumisu/aho-corasick v1.0.3 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.4.0 // indirect github.com/STARRY-S/zip v0.2.3 // indirect github.com/akavel/rsrc v0.10.2 // indirect github.com/alecthomas/chroma/v2 v2.23.1 // indirect github.com/alecthomas/repr v0.5.2 // indirect github.com/andybalholm/brotli v1.2.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect github.com/aws/smithy-go v1.24.2 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/bodgit/plumbing v1.3.0 // indirect github.com/bodgit/sevenzip v1.6.1 // indirect github.com/bodgit/windows v1.0.1 // indirect github.com/bradenhilton/cityhash v1.0.0 // indirect github.com/caspr-io/yamlpath v0.0.0-20200722075116-502e8d113a9b // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/charmbracelet/colorprofile v0.4.3 // indirect github.com/charmbracelet/harmonica v0.2.0 // indirect github.com/charmbracelet/x/ansi v0.11.6 // indirect github.com/charmbracelet/x/cellbuf v0.0.15 // indirect github.com/charmbracelet/x/exp/slice v0.0.0-20260311145557-c83711a11ffa // indirect github.com/charmbracelet/x/term v0.2.2 // indirect github.com/clipperhouse/displaywidth v0.11.0 // indirect github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/cloudflare/circl v1.6.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/creack/pty/v2 v2.0.0-20231209135443-03db72c7b76c // indirect github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/danieljoos/wincred v1.2.3 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad // indirect github.com/editorconfig-checker/editorconfig-checker/v3 v3.6.1 // indirect github.com/editorconfig/editorconfig-core-go/v2 v2.6.4 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/fatih/camelcase v1.0.0 // indirect github.com/fatih/color v1.18.0 // indirect github.com/fatih/semgroup v1.3.0 // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect github.com/gitleaks/go-gitdiff v0.9.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.8.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/godbus/dbus/v5 v5.2.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/capslock v0.3.1 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-querystring v1.2.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gopasspw/gitconfig v0.0.4 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/h2non/filetype v1.1.3 // indirect github.com/hashicorp/go-version v1.8.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/itchyny/timefmt-go v0.1.7 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/josephspurrier/goversioninfo v1.5.0 // indirect github.com/kevinburke/ssh_config v1.6.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.3.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect github.com/mholt/archives v0.1.5 // indirect github.com/microcosm-cc/bluemonday v1.0.27 // indirect github.com/mikelolasagasti/xz v1.0.1 // indirect github.com/minio/minlz v1.1.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pierrec/lz4/v4 v4.1.26 // indirect github.com/pjbgf/sha1cd v0.5.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/rhysd/actionlint v1.7.11 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rs/zerolog v1.34.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/sergi/go-diff v1.4.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/skeema/knownhosts v1.3.2 // indirect github.com/sorairolake/lzip-go v0.3.8 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/viper v1.21.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tetratelabs/wazero v1.11.0 // indirect github.com/tobischo/argon2 v0.1.0 // indirect github.com/twpayne/find-typos v0.0.3 // indirect github.com/twpayne/go-jsonstruct/v3 v3.3.0 // indirect github.com/urfave/cli/v2 v2.27.7 // indirect github.com/wasilibs/go-re2 v1.10.0 // indirect github.com/wasilibs/wazero-helpers v0.0.0-20250123031827-cd30c44769bb // indirect github.com/wlynxg/chardet v1.0.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect github.com/yuin/goldmark v1.7.16 // indirect github.com/yuin/goldmark-emoji v1.0.6 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect go.yaml.in/yaml/v4 v4.0.0-rc.3 // indirect go4.org v0.0.0-20260112195520-a5071408f32f // indirect golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect golang.org/x/mod v0.34.0 // indirect golang.org/x/net v0.52.0 // indirect golang.org/x/tools v0.43.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) exclude ( github.com/charmbracelet/bubbles v0.21.0 // https://github.com/twpayne/chezmoi/issues/4405 github.com/charmbracelet/bubbles v0.21.1 // https://github.com/twpayne/chezmoi/issues/4405 github.com/charmbracelet/bubbles v1.0.0 // https://github.com/twpayne/chezmoi/issues/4405 ) ================================================ FILE: go.sum ================================================ al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeXHA= al.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= c2sp.org/CCTV/age v0.0.0-20251208015420-e9274a7bdbfd h1:ZLsPO6WdZ5zatV4UfVpr7oAwLGRZ+sebTUruuM4Ra3M= c2sp.org/CCTV/age v0.0.0-20251208015420-e9274a7bdbfd/go.mod h1:SrHC2C7r5GkDk8R+NFVzYy/sdj0Ypg9htaPXQq5Cqeo= cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c= cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI= cloud.google.com/go/auth v0.18.0 h1:wnqy5hrv7p3k7cShwAU/Br3nzod7fxoqG+k0VZ+/Pk0= cloud.google.com/go/auth v0.18.0/go.mod h1:wwkPM1AgE1f2u6dG443MiWoD8C3BtOywNsUMcUTVDRo= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE= cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI= cloud.google.com/go/storage v1.56.0 h1:iixmq2Fse2tqxMbWhLWC9HfBj1qdxqAmiK8/eqtsLxI= cloud.google.com/go/storage v1.56.0/go.mod h1:Tpuj6t4NweCLzlNbw9Z9iwxEkrSem20AetIeH/shgVU= codeberg.org/tslocum/cbind v0.1.6 h1:RhnKC7tmrCf0ZJBTQ6b1voAFcGqIEjDsKzqlqFWwkV8= codeberg.org/tslocum/cbind v0.1.6/go.mod h1:gfR4e1lfYqC4xlR0N//omQc1JbHx+e1Mk5F8UfotYYc= cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8= cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= filippo.io/age v1.3.1 h1:hbzdQOJkuaMEpRCLSN1/C5DX74RPcNCk6oqhKMXmZi0= filippo.io/age v1.3.1/go.mod h1:EZorDTYUxt836i3zdori5IJX/v2Lj6kWFU0cfh6C0D4= filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= filippo.io/hpke v0.4.0 h1:p575VVQ6ted4pL+it6M00V/f2qTZITO0zgmdKCkd5+A= filippo.io/hpke v0.4.0/go.mod h1:EmAN849/P3qdeK+PCMkDpDm83vRHM5cDipBJ8xbQLVY= filippo.io/nistec v0.0.4 h1:F14ZHT5htWlMnQVPndX9ro9arf56cBhQxq4LnDI491s= filippo.io/nistec v0.0.4/go.mod h1:PK/lw8I1gQT4hUML4QGaqljwdDaFcMyFKSXN7kjrtKI= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0 h1:/g8S6wk65vfC6m3FIxJ+i5QDyN9JWwXI8Hb0Img10hU= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0/go.mod h1:gpl+q95AzZlKVI3xSoseF9QPrypk0hQqBiJYeB/cR/I= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 h1:nCYfgcSyHZXJI8J0IWE5MsCGlb2xp9fJiXyxWgmOFg4= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0/go.mod h1:ucUjca2JtSZboY8IoUqyQyuuXvwbMBVwFOm0vdQPNhA= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.7.0 h1:4iB+IesclUXdP0ICgAabvq2FYLXrJWKx1fJQ+GxSo3Y= github.com/AzureAD/microsoft-authentication-library-for-go v1.7.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BobuSumisu/aho-corasick v1.0.3 h1:uuf+JHwU9CHP2Vx+wAy6jcksJThhJS9ehR8a+4nPE9g= github.com/BobuSumisu/aho-corasick v1.0.3/go.mod h1:hm4jLcvZKI2vRF2WDU1N4p/jpWtpOzp3nLmi9AzX/XE= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 h1:Ron4zCA/yk6U7WOBXhTJcDpsUBG9npumK6xw2auFltQ= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProtonMail/go-crypto v1.4.0 h1:Zq/pbM3F5DFgJiMouxEdSVY44MVoQNEKp5d5QxIQceQ= github.com/ProtonMail/go-crypto v1.4.0/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4= github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk= github.com/Shopify/ejson v1.5.4 h1:rE3THgxBjdSUcJTNTn1SYaAzaGyxvjkEssAZEJ+zD+s= github.com/Shopify/ejson v1.5.4/go.mod h1:GZg88n4LpYqp92+tzWjvj+1aaiDJn7F1uWebQb4HbeQ= github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw= github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY= github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o= github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.41.4 h1:9aZbO86sraeCIHHCpZhxwN9tnVy9POkSKzi4/TpT54A= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.41.4/go.mod h1:cxiXDhEzIq7Xx1BtmC4lGBK3SwAZ79+EUWiKawYHo14= github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE= github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs= github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= github.com/bodgit/sevenzip v1.6.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4= github.com/bodgit/sevenzip v1.6.1/go.mod h1:GVoYQbEVbOGT8n2pfqCIMRUaRjQ8F9oSqoBEqZh5fQ8= github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo= github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bradenhilton/cityhash v1.0.0 h1:1QauDCwfxwIGwO2jBTJdEBqXgfCusAgQOSgdl4RsTMI= github.com/bradenhilton/cityhash v1.0.0/go.mod h1:Wmb8yW1egA9ulrsRX4mxfYx5zq4nBWOCZ+j63oK6uz8= github.com/bradenhilton/mozillainstallhash v1.0.1 h1:JVAVsItiWlLoudJX4L+tIuml+hoxjlzCwkhlENi9yS4= github.com/bradenhilton/mozillainstallhash v1.0.1/go.mod h1:J6cA36kUZrgaTkDl2bHRqI+4i2UKO1ImDB1P1x1PyOA= github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/caspr-io/yamlpath v0.0.0-20200722075116-502e8d113a9b h1:2K3B6Xm7/lnhOugeGB3nIk50bZ9zhuJvXCEfUuL68ik= github.com/caspr-io/yamlpath v0.0.0-20200722075116-502e8d113a9b/go.mod h1:4rP9T6iHCuPAIDKdNaZfTuuqSIoQQvFctNWIAUI1rlg= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q= github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q= github.com/charmbracelet/glamour v1.0.0 h1:AWMLOVFHTsysl4WV8T8QgkQ0s/ZNZo7CiE4WKhk8l08= github.com/charmbracelet/glamour v1.0.0/go.mod h1:DSdohgOBkMr2ZQNhw4LZxSGpx3SvpeujNoXrQyH2hxo= github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE= github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA= github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8= github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ= github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI= github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q= github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q= github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/exp/slice v0.0.0-20260311145557-c83711a11ffa h1:bmNUSF4m+fwrzZAOhluMSZxdM4bk+SWN0Ni2DimCZm8= github.com/charmbracelet/x/exp/slice v0.0.0-20260311145557-c83711a11ffa/go.mod h1:vqEfX6xzqW1pKKZUUiFOKg0OQ7bCh54Q2vR/tserrRA= github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk= github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI= github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8= github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0= github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0= github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/creack/pty/v2 v2.0.0-20231209135443-03db72c7b76c h1:5l8y/PgjeX1aUyZxXabtAf2ahCYQaqWzlFzQgU16o0U= github.com/creack/pty/v2 v2.0.0-20231209135443-03db72c7b76c/go.mod h1:1gZ4PfMDNcYx8FxDdnF/6HYP327cTeB/ru6UdoWVQvw= github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE= github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ= github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4= github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780 h1:tFh1tRc4CA31yP6qDcu+Trax5wW5GuMxvkIba07qVLY= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad h1:Qk76DOWdOp+GlyDKBAG3Klr9cn7N+LcYc82AZ2S7+cA= github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad/go.mod h1:mPKfmRa823oBIgl2r20LeMSpTAteW5j7FLkc0vjmzyQ= github.com/editorconfig-checker/editorconfig-checker/v3 v3.6.1 h1:jdfYul8o1YpAb0tjNBcfJsVFwTn1s4qis4ec5Feuhhs= github.com/editorconfig-checker/editorconfig-checker/v3 v3.6.1/go.mod h1:3M/pJVyyr63yWjRWOFpPqKNAzj6JaE/I/+dNDfgN+3I= github.com/editorconfig/editorconfig-core-go/v2 v2.6.4 h1:CHwUbBVVyKWRX9kt5A/OtwhYUJB32DrFp9xzmjR6cac= github.com/editorconfig/editorconfig-core-go/v2 v2.6.4/go.mod h1:JWRVKHdVW+dkv6F8p+xGCa6a+TyMrqsFbFkSs/aQkrQ= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo= github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs= github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/ergochat/readline v0.1.3 h1:/DytGTmwdUJcLAe3k3VJgowh5vNnsdifYT6uVaf4pSo= github.com/ergochat/readline v0.1.3/go.mod h1:o3ux9QLHLm77bq7hDB21UTm6HlV2++IPDMfIfKDuOgY= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/semgroup v1.3.0 h1:pTEnmcEze/BUf4UmVn9f1ZT1OckkBTNRV9w9k/I2/y4= github.com/fatih/semgroup v1.3.0/go.mod h1:thVp+PGZMO9KJ+k96oNGJo06hWgsKOWxTfYfx5R2VaE= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw= github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo= github.com/gdamore/tcell/v2 v2.9.0 h1:N6t+eqK7/xwtRPwxzs1PXeRWnm0H9l02CrgJ7DLn1ys= github.com/gdamore/tcell/v2 v2.9.0/go.mod h1:8/ZoqM9rxzYphT9tH/9LnunhV9oPBqwS8WHGYm5nrmo= github.com/gen2brain/shm v0.1.1 h1:1cTVA5qcsUFixnDHl14TmRoxgfWEEZlTezpUj1vm5uQ= github.com/gen2brain/shm v0.1.1/go.mod h1:UgIcVtvmOu+aCJpqJX7GOtiN7X2ct+TKLg4RTxwPIUA= github.com/gitleaks/go-gitdiff v0.9.1 h1:ni6z6/3i9ODT685OLCTf+s/ERlWUNWQF4x1pvoNICw0= github.com/gitleaks/go-gitdiff v0.9.1/go.mod h1:pKz0X4YzCKZs30BL+weqBIG7mx0jl4tF1uXV9ZyNvrA= github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-snaps v0.5.19 h1:hUJlCQOpTt1M+kSisMwioDWZDWpDtdAvUhvWCx1YGW0= github.com/gkampitakis/go-snaps v0.5.19/go.mod h1:gC3YqxQTPyIXvQrw/Vpt3a8VqR1MO8sVpZFWN4DGwNs= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0= github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.17.0 h1:AbyI4xf+7DsjINHMu35quAh4wJygKBKBuXVjV/pxesM= github.com/go-git/go-git/v5 v5.17.0/go.mod h1:f82C4YiLx+Lhi8eHxltLeGC5uBTXSFa6PC5WW9o4SjI= github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-sprout/sprout v1.0.3 h1:LLuz0D3aYazgbVTOwCVuMor3LOUVYinipXRIdjA/D+I= github.com/go-sprout/sprout v1.0.3/go.mod h1:cFFzpnyGGry3cmN0UNCAM1f7AGok6vPVabeYQzBMBZY= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= github.com/gokyle/twofactor v1.0.1 h1:uRhvx0S4Hb82RPIDALnf7QxbmPL49LyyaCkJDpWx+Ek= github.com/gokyle/twofactor v1.0.1/go.mod h1:4gxzH1eaE/F3Pct/sCDNOylP0ClofUO5j4XZN9tKtLE= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/capslock v0.3.1 h1:MdYuvca7uAH2iSgkDtNRm9+Qb7/4xRJgzHoL/LAOhVA= github.com/google/capslock v0.3.1/go.mod h1:7K6wQ78xV0QcIXsk0O1wSEExGgCmquxwNAleZT4GrA4= github.com/google/go-cmdtest v0.4.0 h1:ToXh6W5spLp3npJV92tk6d5hIpUPYEzHLkD+rncbyhI= github.com/google/go-cmdtest v0.4.0/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github/v61 v61.0.0 h1:VwQCBwhyE9JclCI+22/7mLB1PuU9eowCXKY5pNlu1go= github.com/google/go-github/v61 v61.0.0/go.mod h1:0WR+KmsWX75G2EbpyGsGmradjo3IiciuI4BmdVCobQY= github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio/v2 v2.0.2 h1:qKZs+tfn+arruZZhQ7TKC/ergJunuJicWS6gLDt/dGw= github.com/google/renameio/v2 v2.0.2/go.mod h1:OX+G6WHHpHq3NVj7cAOleLOwJfcQ1s3uUJQCrr78SWo= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ= github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y= github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14= github.com/gopasspw/clipboard v0.0.4 h1:v3HUlVHfBXPx9woIQnsBIbs9ZM3i77OCtVKRMLhmR+c= github.com/gopasspw/clipboard v0.0.4/go.mod h1:i0cShr7JEbOXZ/iKM5RyfBLbu1FPzouO8BTCJy0uHy8= github.com/gopasspw/gitconfig v0.0.4 h1:7JE0iTm92OdXCtkS33CnbqcAEqQXYWTUriYFf3sRTBk= github.com/gopasspw/gitconfig v0.0.4/go.mod h1:W5AHsZgCbBRsc8TnElO82GYflOz/l2dIndncymoCv+A= github.com/gopasspw/gopass v1.16.1 h1:eqlW8zkWVzcJsEstDYDuvaPnEYZ0HALc7+n/o1/JZNM= github.com/gopasspw/gopass v1.16.1/go.mod h1:Dsxqt+adcFszXvA+yMvPMfx8dteUtZKrSQ2qgaKFZgI= github.com/gopasspw/gopass-hibp v1.15.18 h1:OoMr1ugY0aso5Qjn3MRPaVJoYGuwdhaebUxKP611Cj0= github.com/gopasspw/gopass-hibp v1.15.18/go.mod h1:5JOaWMoQbOFTRUdiCOw3JTNPtjbUPWKpw7OEFjQwAdo= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/itchyny/go-yaml v0.0.0-20251001235044-fca9a0999f15 h1:m4jKsIK0QS9ihQzOxUN2zJcPdrACwqIWCwvdzv9skMQ= github.com/itchyny/go-yaml v0.0.0-20251001235044-fca9a0999f15/go.mod h1:Tmbz8uw5I/I6NvVpEGuhzlElCGS5hPoXJkt7l+ul6LE= github.com/itchyny/gojq v0.12.18 h1:gFGHyt/MLbG9n6dqnvlliiya2TaMMh6FFaR2b1H6Drc= github.com/itchyny/gojq v0.12.18/go.mod h1:4hPoZ/3lN9fDL1D+aK7DY1f39XZpY9+1Xpjz8atrEkg= github.com/itchyny/timefmt-go v0.1.7 h1:xyftit9Tbw+Dc/huSSPJaEmX1TVL8lw5vxjJLK4GMMA= github.com/itchyny/timefmt-go v0.1.7/go.mod h1:5E46Q+zj7vbTgWY8o5YkMeYb4I6GeWLFnetPy5oBrAI= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= github.com/josephspurrier/goversioninfo v1.5.0 h1:9TJtORoyf4YMoWSOo/cXFN9A/lB3PniJ91OxIH6e7Zg= github.com/josephspurrier/goversioninfo v1.5.0/go.mod h1:6MoTvFZ6GKJkzcdLnU5T/RGYUbHQbKpYeNP0AgQLd2o= github.com/jsimonetti/pwscheme v0.0.0-20220922140336-67a4d090f150 h1:ta6N7DaOQEACq28cLa0iRqXIbchByN9Lfll08CT2GBc= github.com/jsimonetti/pwscheme v0.0.0-20220922140336-67a4d090f150/go.mod h1:SiNTKDgjKQORnazFVHXhpny7UtU0iJOqtxd7R7sCfDI= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jwalton/gchalk v1.3.0 h1:uTfAaNexN8r0I9bioRTksuT8VGjrPs9YIXR1PQbtX/Q= github.com/jwalton/gchalk v1.3.0/go.mod h1:ytRlj60R9f7r53IAElbpq4lVuPOPNg2J4tJcCxtFqr8= github.com/jwalton/go-supportscolor v1.2.0 h1:g6Ha4u7Vm3LIsQ5wmeBpS4gazu0UP1DRDE8y6bre4H8= github.com/jwalton/go-supportscolor v1.2.0/go.mod h1:hFVUAZV2cWg+WFFC4v8pT2X/S2qUUBYMioBD9AINXGs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kbinani/screenshot v0.0.0-20250624051815-089614a94018 h1:NQYgMY188uWrS+E/7xMVpydsI48PMHcc7SfR4OxkDF4= github.com/kbinani/screenshot v0.0.0-20250624051815-089614a94018/go.mod h1:Pmpz2BLf55auQZ67u3rvyI2vAQvNetkK/4zYUmpauZQ= github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY= github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= github.com/kjk/lzmadec v0.0.0-20210713164611-19ac3ee91a71 h1:TYp9Fj0apeZMWentXRaFM6B0ixdFefrlgY8n8XYEz1s= github.com/kjk/lzmadec v0.0.0-20210713164611-19ac3ee91a71/go.mod h1:2zRkQCuw/eK6cqkYAeNqyBU7JKa2Gcq40BZ9GSJbmfE= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb h1:w1g9wNDIE/pHSTmAaUhv4TZQuPBS6GV3mMz5hkgziIU= github.com/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb/go.mod h1:5ELEyG+X8f+meRWHuqUOewBOhvHkl7M76pdGEansxW4= github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc= github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/makiuchi-d/gozxing v0.1.1 h1:xxqijhoedi+/lZlhINteGbywIrewVdVv2wl9r5O9S1I= github.com/makiuchi-d/gozxing v0.1.1/go.mod h1:eRIHbOjX7QWxLIDJoQuMLhuXg9LAuw6znsUtRkNw9DU= github.com/martinhoefling/goxkcdpwgen v0.1.2-0.20231122080842-e51aa57005ca h1:jV6vw7U2RoS1sI7f6f12/wsCwMjADZ/xUxi/lhUqjV8= github.com/martinhoefling/goxkcdpwgen v0.1.2-0.20231122080842-e51aa57005ca/go.mod h1:IKRlPM0t4ZmK9YZ33QZ2hB1DcSY8WnQedKRDyYeNRp4= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w= github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-tty v0.0.7 h1:KJ486B6qI8+wBO7kQxYgmmEFDaFEE96JMBQ7h400N8Q= github.com/mattn/go-tty v0.0.7/go.mod h1:f2i5ZOvXBU/tCABmLmOfzLz9azMo5wdAaElRNnJKr+k= github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ= github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0= github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc= github.com/minio/minlz v1.1.0 h1:rUOGu3EP4EqJC5k3qCsIwEnZiJULKqtRyDdqbhlvMmQ= github.com/minio/minlz v1.1.0/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/montanaflynn/stats v0.7.0 h1:r3y12KyNxj/Sb/iOE46ws+3mS1+MZca1wlHQFPsY/JU= github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/combinator v0.3.0 h1:SZDuRzzwmVPLkbOzbhGzBTwd5+Y6aFN4UusOW2azrNA= github.com/muesli/combinator v0.3.0/go.mod h1:ttPegJX0DPQaGDtJKMInIP6Vfp5pN8RX7QntFCcpy18= github.com/muesli/crunchy v0.4.0 h1:qdiml8gywULHBsztiSAf6rrE6EyuNasNKZ104mAaahM= github.com/muesli/crunchy v0.4.0/go.mod h1:9k4x6xdSbb7WwtAVy0iDjaiDjIk6Wa5AgUIqp+HqOpU= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/noborus/guesswidth v0.4.0 h1:+PPh+Z+GM4mKmVrhYR4lpjeyBuLMSVo2arM+VErdHIc= github.com/noborus/guesswidth v0.4.0/go.mod h1:ghA6uh9RcK+uSmaDDmBMj/tRZ3BSpspDP6DMF5Xk3bc= github.com/noborus/ov v0.45.1 h1:wlyehWHzsn/9QcS1Y2E52q+PbcufI98Vn+pJgcF5BQs= github.com/noborus/ov v0.45.1/go.mod h1:3XW/dJo/FtcMJz6amDMaYR0LmUj/W+uSSjm5NRtsWd4= github.com/noborus/tcellansi v0.2.0 h1:GoSLIya57T7IiojDyNLps+9SjjCaCuLmvnGqHr3ldYU= github.com/noborus/tcellansi v0.2.0/go.mod h1:hZhcjTUr0iNe3XmyErkIMW3kBQmKZTngSARCWF+DxT0= github.com/nwaples/rardecode/v2 v2.2.2 h1:/5oL8dzYivRM/tqX9VcTSWfbpwcbwKG1QtSJr3b3KcU= github.com/nwaples/rardecode/v2 v2.2.2/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4/v4 v4.1.26 h1:GrpZw1gZttORinvzBdXPUXATeqlJjqUG/D87TKMnhjY= github.com/pierrec/lz4/v4 v4.1.26/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs= github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/rhysd/actionlint v1.7.11 h1:m+aSuCpCIClS8X02xMG4Z8s87fCHPsAtYkAoWGQZgEE= github.com/rhysd/actionlint v1.7.11/go.mod h1:8n50YougV9+50niD7oxgDTZ1KbN/ZnKiQ2xpLFeVhsI= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/schollz/closestmatch v0.0.0-20190308193919-1fbe626be92e h1:HFUDYOpUVZ0oTXeZy2A59Lkf69SsOF03Lg1GsI3Xh9o= github.com/schollz/closestmatch v0.0.0-20190308193919-1fbe626be92e/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg= github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik= github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd h1:Rf9uhF1+VJ7ZHqxrG8pJ6YacmHvVCmByDmGbAWCc/gA= github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo= github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA= github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tobischo/argon2 v0.1.0 h1:mwAx/9DK/4rP0xzNifb/XMAf43dU3eG1B3aeF88qu4Y= github.com/tobischo/argon2 v0.1.0/go.mod h1:4NLmLFwhWPbT66nRZNgcktV/mibJ6fESoeEp43h9GRw= github.com/tobischo/gokeepasslib/v3 v3.6.2 h1:SJzzllmNe7iZLudLJ3Lzdm3pDb++AJqZlmqG+SR8bVc= github.com/tobischo/gokeepasslib/v3 v3.6.2/go.mod h1:ga7HFqG0TZSLNao/QOnV2+yngkrf5186saPxSQ1Xp7o= github.com/twpayne/find-typos v0.0.3 h1:ht/lBr6E7llyqBlRIwWXcWG1nhPemW1Db/QE6vK43L8= github.com/twpayne/find-typos v0.0.3/go.mod h1:+6q93VenFlU95gXd6qPbTyqh4vYvIuqDk4msgBeW36s= github.com/twpayne/go-expect v0.0.2-0.20241130000624-916db2914efd h1:/ekqcdG89euFEeFfuFYGF2An2AwsaJHnYxVwW8hqvcw= github.com/twpayne/go-expect v0.0.2-0.20241130000624-916db2914efd/go.mod h1:Z1PlEHxKw23bid0pq2RhO4NMScxckEhBXZLebwJpjrk= github.com/twpayne/go-jsonstruct/v3 v3.3.0 h1:LaxH/CPO2mUnvnsIe+0E/WS2RN/PiUWEjoHlGI31ogY= github.com/twpayne/go-jsonstruct/v3 v3.3.0/go.mod h1:Ep6oLjT0GADi6TmsIDASia0cfw3Nja3rBz+GIfYlC/A= github.com/twpayne/go-pinentry/v4 v4.0.1 h1:2mIMlMRzNwzQtTe96NnjY46xFiqsR+EasvXt4NkNKIY= github.com/twpayne/go-pinentry/v4 v4.0.1/go.mod h1:z5f+OFtNOUILmMdKBGVbZTcO7sDjg7HHvcrnGxr/cq0= github.com/twpayne/go-shell v0.5.0 h1:Mgd2GReIHdsCRT9OqYCh/ywf7x9r9cJhsZMIqKSVjIU= github.com/twpayne/go-shell v0.5.0/go.mod h1:MP3aUA0TQ3IGoJc15ahjb+7A7wZH4NeGrvLZ/aFQsHc= github.com/twpayne/go-vfs/v5 v5.0.5 h1:s+Rb66vj0Y8waQV3xndzjh/qdZNiOWEpnCctM2cHWaA= github.com/twpayne/go-vfs/v5 v5.0.5/go.mod h1:AF7wvxTGEE0XnSdtHXKwHY8vcanqhFga27BhJYHUvMo= github.com/twpayne/go-xdg/v6 v6.1.3 h1:viM0S9v4KAc0IRW2xI3Zp8ZkqOCoCxmCmVZ7GTnG0y0= github.com/twpayne/go-xdg/v6 v6.1.3/go.mod h1:kVT9oShzQ0Cb5r4gzwziZUfluW2sTR72slQC/4N1AXI= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= github.com/wasilibs/go-re2 v1.10.0 h1:vQZEBYZOCA9jdBMmrO4+CvqyCj0x4OomXTJ4a5/urQ0= github.com/wasilibs/go-re2 v1.10.0/go.mod h1:k+5XqO2bCJS+QpGOnqugyfwC04nw0jaglmjrrkG8U6o= github.com/wasilibs/wazero-helpers v0.0.0-20250123031827-cd30c44769bb h1:gQ+ZV4wJke/EBKYciZ2MshEouEHFuinB85dY3f5s1q8= github.com/wasilibs/wazero-helpers v0.0.0-20250123031827-cd30c44769bb/go.mod h1:jMeV4Vpbi8osrE/pKUxRZkVaA0EX7NZN0A9/oRzgpgY= github.com/wlynxg/chardet v1.0.4 h1:hkI71Dx8v3RiAz3XKV5lJEh9QfKo7xXKUmYJQeIMlpo= github.com/wlynxg/chardet v1.0.4/go.mod h1:HLQMNsa0w4MkH2e7waQaFD+Yh85riFFTLhFtP8fsdbQ= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE= github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs= github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA= github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s= github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI= github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= github.com/zricethezav/gitleaks/v8 v8.30.1 h1:PmEvCfVI7ti9dV3s5aMZUY7sS2GxRvG3yzih7E+cS3w= github.com/zricethezav/gitleaks/v8 v8.30.1/go.mod h1:rTDwxRjufMKAkhTI/Mijd07nday1yOhf9qywjwz5Irw= go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= go.etcd.io/gofail v0.2.0 h1:p19drv16FKK345a09a1iubchlw/vmRuksmRzgBIGjcA= go.etcd.io/gofail v0.2.0/go.mod h1:nL3ILMGfkXTekKI3clMBNazKnjUZjYLKmBHzsVAnC1o= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/detectors/gcp v1.38.0 h1:ZoYbqX7OaA/TAikspPl3ozPI6iY6LiIY9I8cUfm+pJs= go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go.yaml.in/yaml/v4 v4.0.0-rc.3 h1:3h1fjsh1CTAPjW7q/EMe+C8shx5d8ctzZTrLcs/j8Go= go.yaml.in/yaml/v4 v4.0.0-rc.3/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= go4.org v0.0.0-20260112195520-a5071408f32f h1:ziUVAjmTPwQMBmYR1tbdRFJPtTcQUI12fH9QQjfb0Sw= go4.org v0.0.0-20260112195520-a5071408f32f/go.mod h1:ZRJnO5ZI4zAwMFp+dS1+V6J6MSyAowhRqAE+DPa1Xp0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c h1:6a8FdnNk6bTXBjR4AGKFgUKuo+7GnR3FX5L7CbveeZc= golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c/go.mod h1:TpUTTEp9frx7rTdLpC9gFG9kdI7zVLFTFFlqaH2Cncw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/api v0.259.0 h1:90TaGVIxScrh1Vn/XI2426kRpBqHwWIzVBzJsVZ5XrQ= google.golang.org/api v0.259.0/go.mod h1:LC2ISWGWbRoyQVpxGntWwLWN/vLNxxKBK9KuJRI8Te4= google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217 h1:GvESR9BIyHUahIb0NcTum6itIWtdoglGX+rnGxm2934= google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:yJ2HH4EHEDTd3JiLmhds6NkJ17ITVYOdV3m3VKOnws0= google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU= google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k= gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 h1:POO/ycCATvegFmVuPpQzZFJ+pGZeX22Ufu6fibxDVjU= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= mvdan.cc/editorconfig v0.3.0 h1:D1D2wLYEYGpawWT5SpM5pRivgEgXjtEXwC9MWhEY0gQ= mvdan.cc/editorconfig v0.3.0/go.mod h1:NcJHuDtNOTEJ6251indKiWuzK6+VcrMuLzGMLKBFupQ= mvdan.cc/sh/v3 v3.13.0 h1:dSfq/MVsY4w0Vsi6Lbs0IcQquMVqLdKLESAOZjuHdLg= mvdan.cc/sh/v3 v3.13.0/go.mod h1:KV1GByGPc/Ho0X1E6Uz9euhsIQEj4hwyKnodLlFLoDM= rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs= znkr.io/diff v1.0.0-beta.4 h1:KjiBi1xYkI5cPIu6cRbD14vO082guKyD+45uzNRQDXE= znkr.io/diff v1.0.0-beta.4/go.mod h1:q5GjOY1oOrUy1kHHw1DIJSvNMdmxpL5rL9cTz14MF/0= ================================================ FILE: internal/archivetest/archivetest.go ================================================ // Package archivetest provides useful functions for testing archives. package archivetest import ( "io/fs" ) // A Dir represents a directory. type Dir struct { Perm fs.FileMode Entries map[string]any } // A File represents a file. type File struct { Perm fs.FileMode Contents []byte } // A Symlink represents a symlink. type Symlink struct { Target string } ================================================ FILE: internal/archivetest/tar.go ================================================ package archivetest import ( "archive/tar" "bytes" "fmt" "io/fs" "maps" "slices" ) // NewTar returns the bytes of a new tar archive containing root. func NewTar(root map[string]any) ([]byte, error) { buffer := &bytes.Buffer{} tarWriter := tar.NewWriter(buffer) for _, key := range slices.Sorted(maps.Keys(root)) { if err := tarAddEntry(tarWriter, key, root[key]); err != nil { return nil, err } } if err := tarWriter.Close(); err != nil { return nil, err } return buffer.Bytes(), nil } func tarAddEntry(w *tar.Writer, name string, entry any) error { switch entry := entry.(type) { case []byte: if err := w.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, Name: name, Size: int64(len(entry)), Mode: 0o666, }); err != nil { return err } if _, err := w.Write(entry); err != nil { return err } case map[string]any: if err := w.WriteHeader(&tar.Header{ Typeflag: tar.TypeDir, Name: name + "/", Mode: int64(fs.ModePerm), }); err != nil { return err } for _, key := range slices.Sorted(maps.Keys(entry)) { if err := tarAddEntry(w, name+"/"+key, entry[key]); err != nil { return err } } case nil: return nil case string: if err := w.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, Name: name, Size: int64(len(entry)), Mode: 0o666, }); err != nil { return err } if _, err := w.Write([]byte(entry)); err != nil { return err } case *Dir: if err := w.WriteHeader(&tar.Header{ Typeflag: tar.TypeDir, Name: name + "/", Mode: int64(entry.Perm), }); err != nil { return err } for _, key := range slices.Sorted(maps.Keys(entry.Entries)) { if err := tarAddEntry(w, name+"/"+key, entry.Entries[key]); err != nil { return err } } case *File: if err := w.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, Name: name, Size: int64(len(entry.Contents)), Mode: int64(entry.Perm), }); err != nil { return err } if _, err := w.Write(entry.Contents); err != nil { return err } case *Symlink: if err := w.WriteHeader(&tar.Header{ Typeflag: tar.TypeSymlink, Name: name, Linkname: entry.Target, }); err != nil { return err } default: return fmt.Errorf("%s: unsupported type: %T", name, entry) } return nil } ================================================ FILE: internal/archivetest/tar_test.go ================================================ package archivetest import ( "archive/tar" "bytes" "io/fs" "testing" "github.com/alecthomas/assert/v2" ) func TestNewTar(t *testing.T) { data, err := NewTar(map[string]any{ "dir": map[string]any{ "file1": "# contents of dir/file1\n", "file2": []byte("# contents of dir/file2\n"), "subdir": &Dir{ Perm: 0o700, Entries: map[string]any{ "file": &File{ Perm: fs.ModePerm, Contents: []byte("# contents of dir/subdir/file\n"), }, "symlink": &Symlink{ Target: "file", }, }, }, }, }) assert.NoError(t, err) tarReader := tar.NewReader(bytes.NewBuffer(data)) header, err := tarReader.Next() assert.NoError(t, err) assert.Equal(t, byte(tar.TypeDir), header.Typeflag) assert.Equal(t, "dir/", header.Name) assert.Equal(t, int64(fs.ModePerm), header.Mode) header, err = tarReader.Next() assert.NoError(t, err) assert.Equal(t, byte(tar.TypeReg), header.Typeflag) assert.Equal(t, "dir/file1", header.Name) assert.Equal(t, int64(len("# contents of dir/file1\n")), header.Size) assert.Equal(t, int64(0o666), header.Mode) header, err = tarReader.Next() assert.NoError(t, err) assert.Equal(t, byte(tar.TypeReg), header.Typeflag) assert.Equal(t, "dir/file2", header.Name) assert.Equal(t, int64(len("# contents of dir/file2\n")), header.Size) assert.Equal(t, int64(0o666), header.Mode) header, err = tarReader.Next() assert.NoError(t, err) assert.Equal(t, byte(tar.TypeDir), header.Typeflag) assert.Equal(t, "dir/subdir/", header.Name) assert.Equal(t, int64(0o700), header.Mode) header, err = tarReader.Next() assert.NoError(t, err) assert.Equal(t, byte(tar.TypeReg), header.Typeflag) assert.Equal(t, "dir/subdir/file", header.Name) assert.Equal(t, int64(len("# contents of dir/subdir/file\n")), header.Size) assert.Equal(t, int64(fs.ModePerm), header.Mode) header, err = tarReader.Next() assert.NoError(t, err) assert.Equal(t, byte(tar.TypeSymlink), header.Typeflag) assert.Equal(t, "dir/subdir/symlink", header.Name) assert.Equal(t, "file", header.Linkname) } ================================================ FILE: internal/archivetest/zip.go ================================================ package archivetest import ( "bytes" "fmt" "io/fs" "maps" "slices" "github.com/klauspost/compress/zip" ) func NewZip(root map[string]any) ([]byte, error) { buffer := &bytes.Buffer{} zipWriter := zip.NewWriter(buffer) for _, key := range slices.Sorted(maps.Keys(root)) { if err := zipAddEntry(zipWriter, key, root[key]); err != nil { return nil, err } } if err := zipWriter.Close(); err != nil { return nil, err } return buffer.Bytes(), nil } func zipAddEntry(w *zip.Writer, name string, entry any) error { switch entry := entry.(type) { case []byte: return zipAddEntryFile(w, name, entry, 0o666) case map[string]any: return zipAddEntryDir(w, name, fs.ModePerm, entry) case string: return zipAddEntryFile(w, name, []byte(entry), 0o666) case *Dir: return zipAddEntryDir(w, name, entry.Perm, entry.Entries) case *File: return zipAddEntryFile(w, name, entry.Contents, entry.Perm) case *Symlink: return zipAddEntrySymlink(w, name, []byte(entry.Target)) default: return fmt.Errorf("%s: unsupported type: %T", name, entry) } } func zipAddEntryDir(w *zip.Writer, name string, perm fs.FileMode, entries map[string]any) error { fileHeader := zip.FileHeader{ Name: name, } fileHeader.SetMode(fs.ModeDir | perm) if _, err := w.CreateHeader(&fileHeader); err != nil { return err } for _, key := range slices.Sorted(maps.Keys(entries)) { if err := zipAddEntry(w, name+"/"+key, entries[key]); err != nil { return err } } return nil } func zipAddEntryFile(w *zip.Writer, name string, data []byte, perm fs.FileMode) error { fileHeader := zip.FileHeader{ Name: name, Method: zip.Deflate, UncompressedSize64: uint64(len(data)), } fileHeader.SetMode(perm) fileWriter, err := w.CreateHeader(&fileHeader) if err != nil { return err } _, err = fileWriter.Write(data) return err } func zipAddEntrySymlink(w *zip.Writer, name string, target []byte) error { fileHeader := zip.FileHeader{ Name: name, UncompressedSize64: uint64(len(target)), } fileHeader.SetMode(fs.ModeSymlink) fileWriter, err := w.CreateHeader(&fileHeader) if err != nil { return err } _, err = fileWriter.Write(target) return err } ================================================ FILE: internal/archivetest/zip_test.go ================================================ package archivetest import ( "bytes" "io/fs" "testing" "github.com/alecthomas/assert/v2" "github.com/klauspost/compress/zip" ) func TestNewZip(t *testing.T) { data, err := NewZip(map[string]any{ "dir": map[string]any{ "file1": "# contents of dir/file1\n", "file2": []byte("# contents of dir/file2\n"), "subdir": &Dir{ Perm: 0o700, Entries: map[string]any{ "file": &File{ Perm: fs.ModePerm, Contents: []byte("# contents of dir/subdir/file\n"), }, "symlink": &Symlink{ Target: "file", }, }, }, }, }) assert.NoError(t, err) zipReader, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) assert.NoError(t, err) fileIndex := 0 nextFile := func() *zip.File { assert.True(t, fileIndex <= len(zipReader.File)) zipFile := zipReader.File[fileIndex] fileIndex++ return zipFile } zipFile := nextFile() assert.Equal(t, "dir", zipFile.Name) assert.Equal(t, fs.ModeDir, zipFile.FileInfo().Mode().Type()) assert.Equal(t, fs.ModePerm, zipFile.FileInfo().Mode().Perm()) zipFile = nextFile() assert.Equal(t, "dir/file1", zipFile.Name) assert.Equal(t, fs.FileMode(0), zipFile.FileInfo().Mode().Type()) assert.Equal(t, fs.FileMode(0o666), zipFile.FileInfo().Mode().Perm()) assert.Equal(t, uint64(len("# contents of dir/file1\n")), zipFile.UncompressedSize64) zipFile = nextFile() assert.Equal(t, "dir/file2", zipFile.Name) assert.Equal(t, fs.FileMode(0), zipFile.FileInfo().Mode().Type()) assert.Equal(t, fs.FileMode(0o666), zipFile.FileInfo().Mode().Perm()) assert.Equal(t, uint64(len("# contents of dir/file2\n")), zipFile.UncompressedSize64) zipFile = nextFile() assert.Equal(t, "dir/subdir", zipFile.Name) assert.Equal(t, fs.ModeDir, zipFile.FileInfo().Mode().Type()) assert.Equal(t, fs.FileMode(0o700), zipFile.FileInfo().Mode().Perm()) zipFile = nextFile() assert.Equal(t, "dir/subdir/file", zipFile.Name) assert.Equal(t, fs.FileMode(0), zipFile.FileInfo().Mode().Type()) assert.Equal(t, fs.ModePerm, zipFile.FileInfo().Mode().Perm()) assert.Equal(t, uint64(len("# contents of dir/subdir/file\n")), zipFile.UncompressedSize64) zipFile = nextFile() assert.Equal(t, "dir/subdir/symlink", zipFile.Name) assert.Equal(t, fs.ModeSymlink, zipFile.FileInfo().Mode().Type()) assert.Equal(t, uint64(len("file")), zipFile.UncompressedSize64) assert.Equal(t, fileIndex, len(zipReader.File)) } ================================================ FILE: internal/chezmoi/abspath.go ================================================ package chezmoi import ( "fmt" "path" "path/filepath" "reflect" "strings" "github.com/go-viper/mapstructure/v2" ) var ( DotAbsPath = NewAbsPath(".") EmptyAbsPath = NewAbsPath("") RootAbsPath = NewAbsPath("/") ) // An AbsPath is an absolute path. type AbsPath string // NewAbsPath returns a new AbsPath. func NewAbsPath(absPath string) AbsPath { return AbsPath(filepath.ToSlash(absPath)) } // Append appends s to p. func (p AbsPath) Append(s string) AbsPath { return NewAbsPath(string(p) + s) } // Base returns p's basename. func (p AbsPath) Base() string { return path.Base(string(p)) } // Bytes returns p as a []byte. func (p AbsPath) Bytes() []byte { return []byte(p) } // Dir returns p's directory. func (p AbsPath) Dir() AbsPath { return NewAbsPath(filepath.Dir(string(p))).ToSlash() } // IsEmpty returns if p is empty. func (p AbsPath) IsEmpty() bool { return p == "" } // Ext returns p's extension. func (p AbsPath) Ext() string { return path.Ext(string(p)) } // HasDirPrefix returns if p has the given prefix. func (p AbsPath) HasDirPrefix(prefix AbsPath) bool { pWithTrailingSlash := p.WithTrailingSlash() prefixWithTrailingSlash := prefix.WithTrailingSlash() if pWithTrailingSlash == prefixWithTrailingSlash { return true } return strings.HasPrefix(string(pWithTrailingSlash), string(prefixWithTrailingSlash)) } // Join returns a new AbsPath with relPaths appended. func (p AbsPath) Join(relPaths ...RelPath) AbsPath { relPathStrs := make([]string, len(relPaths)+1) relPathStrs[0] = string(p) for i, relPath := range relPaths { relPathStrs[i+1] = relPath.String() } return NewAbsPath(filepath.ToSlash(filepath.Join(relPathStrs...))) } // JoinString returns a new AbsPath with ss appended. func (p AbsPath) JoinString(ss ...string) AbsPath { strs := make([]string, len(ss)+1) strs[0] = string(p) copy(strs[1:len(ss)+1], ss) return NewAbsPath(filepath.ToSlash(filepath.Join(strs...))) } // Len returns the length of p. func (p AbsPath) Len() int { return len(p) } // MustTrimDirPrefix is like TrimPrefix but panics on any error. func (p AbsPath) MustTrimDirPrefix(dirPrefix AbsPath) RelPath { relPath, err := p.TrimDirPrefix(dirPrefix) if err != nil { panic(err) } return relPath } // Set implements github.com/spf13/pflag.Value.Set. func (p *AbsPath) Set(s string) error { if s == "" { *p = "" return nil } homeDirAbsPath, err := HomeDirAbsPath() if err != nil { return err } absPath, err := NewAbsPathFromExtPath(s, homeDirAbsPath) if err != nil { return err } *p = absPath return nil } // Split returns p's directory and file. func (p AbsPath) Split() (AbsPath, RelPath) { dir, file := path.Split(p.String()) return NewAbsPath(strings.TrimSuffix(dir, "/")), NewRelPath(file) } func (p AbsPath) String() string { return string(p) } // ToSlash calls [filepath.ToSlash] on p. func (p AbsPath) ToSlash() AbsPath { return NewAbsPath(filepath.ToSlash(string(p))) } // TrimDirPrefix trims prefix from p. func (p AbsPath) TrimDirPrefix(dirPrefixAbsPath AbsPath) (RelPath, error) { if p == dirPrefixAbsPath { return EmptyRelPath, nil } dirAbsPath := dirPrefixAbsPath.WithTrailingSlash() if !strings.HasPrefix(string(p), string(dirAbsPath)) { return EmptyRelPath, &NotInAbsDirError{ pathAbsPath: p, dirAbsPath: dirPrefixAbsPath, } } return NewRelPath(string(p[len(dirAbsPath):])), nil } // TrimSuffix returns p with the optional suffix removed. func (p AbsPath) TrimSuffix(suffix string) AbsPath { return NewAbsPath(strings.TrimSuffix(string(p), suffix)) } // Type implements github.com/spf13/pflag.Value.Type. func (p AbsPath) Type() string { return "path" } // WithTrailingSlash returns p with a trailing slash. func (p AbsPath) WithTrailingSlash() AbsPath { if strings.HasSuffix(string(p), "/") { return p } return p + "/" } // HomeDirAbsPath returns the user's home directory as an AbsPath. func HomeDirAbsPath() (AbsPath, error) { userHomeDir, err := UserHomeDir() if err != nil { return EmptyAbsPath, err } absPath, err := NormalizePath(userHomeDir) if err != nil { return EmptyAbsPath, err } return absPath, nil } // StringToAbsPathHookFunc is a // github.com/go-viper/mapstructure/v2.DecodeHookFunc that parses an AbsPath // from a string. func StringToAbsPathHookFunc() mapstructure.DecodeHookFunc { return func(from, to reflect.Type, data any) (any, error) { if to != reflect.TypeFor[AbsPath]() { return data, nil } s, ok := data.(string) if !ok { return nil, fmt.Errorf("expected a string, got a %T", data) } var absPath AbsPath if err := absPath.Set(s); err != nil { return nil, err } return absPath, nil } } ================================================ FILE: internal/chezmoi/abspath_test.go ================================================ package chezmoi import ( "os" "runtime" "strconv" "testing" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestNewAbsPathFromExtPath(t *testing.T) { wd, err := os.Getwd() assert.NoError(t, err) wdAbsPath := NewAbsPath(wd) assert.NoError(t, err) homeDirAbsPath, err := NormalizePath(chezmoitest.HomeDir()) assert.NoError(t, err) for _, tc := range []struct { name string extPath string expected AbsPath }{ { name: "empty", expected: wdAbsPath, }, { name: "file", extPath: "file", expected: wdAbsPath.JoinString("file"), }, { name: "tilde", extPath: "~", expected: homeDirAbsPath, }, { name: "tilde_home_file", extPath: "~/file", expected: homeDirAbsPath.JoinString("file"), }, { name: "tilde_home_file_windows", extPath: `~\file`, expected: homeDirAbsPath.JoinString("file"), }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.SkipUnlessGOOS(t, tc.name) actual, err := NewAbsPathFromExtPath(tc.extPath, homeDirAbsPath) assert.NoError(t, err) normalizedActual, err := NormalizePath(actual.String()) assert.NoError(t, err) expected, err := NormalizePath(tc.expected.String()) assert.NoError(t, err) assert.Equal(t, expected, normalizedActual) }) } } func TestAbsPathJoin(t *testing.T) { for i, tc := range []struct { skip bool absPath AbsPath relPath RelPath expected AbsPath }{ { skip: runtime.GOOS != "windows", absPath: NewAbsPath("//WSL.LOCALHOST/UBUNTU/home/user"), relPath: NewRelPath(".local/share/chezmoi"), expected: NewAbsPath("//WSL.LOCALHOST/UBUNTU/home/user/.local/share/chezmoi"), }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { if tc.skip { t.Skip() } assert.Equal(t, tc.expected, tc.absPath.Join(tc.relPath)) }) } } ================================================ FILE: internal/chezmoi/actualstateentry.go ================================================ package chezmoi import ( "crypto/sha256" "errors" "io/fs" "sync" ) // An ActualStateEntry represents the actual state of an entry in the // filesystem. type ActualStateEntry interface { EntryState() (*EntryState, error) IsExternal() bool Path() AbsPath Remove(system System) error OriginString() string } // A ActualStateAbsent represents the absence of an entry in the filesystem. type ActualStateAbsent struct { absPath AbsPath } // A ActualStateDir represents the state of a directory in the filesystem. type ActualStateDir struct { absPath AbsPath perm fs.FileMode } // A ActualStateFile represents the state of a file in the filesystem. type ActualStateFile struct { absPath AbsPath perm fs.FileMode contentsFunc func() ([]byte, error) } // A ActualStateSymlink represents the state of a symlink in the filesystem. type ActualStateSymlink struct { absPath AbsPath linknameFunc func() (string, error) } // NewActualStateEntry returns a new ActualStateEntry populated with absPath // from system. func NewActualStateEntry(system System, absPath AbsPath, fileInfo fs.FileInfo, err error) (ActualStateEntry, error) { if fileInfo == nil { fileInfo, err = system.Lstat(absPath) } switch { case errors.Is(err, fs.ErrNotExist): return &ActualStateAbsent{ absPath: absPath, }, nil case err != nil: return nil, err } switch fileInfo.Mode().Type() { case 0: return &ActualStateFile{ absPath: absPath, perm: fileInfo.Mode().Perm(), contentsFunc: sync.OnceValues(func() ([]byte, error) { return system.ReadFile(absPath) }), }, nil case fs.ModeDir: return &ActualStateDir{ absPath: absPath, perm: fileInfo.Mode().Perm(), }, nil case fs.ModeSymlink: return &ActualStateSymlink{ absPath: absPath, linknameFunc: sync.OnceValues(func() (string, error) { linkname, err := system.Readlink(absPath) if err != nil { return "", err } return normalizeLinkname(linkname), nil }), }, nil default: return nil, &UnsupportedFileTypeError{ absPath: absPath, mode: fileInfo.Mode(), } } } // EntryState returns s's entry state. func (s *ActualStateAbsent) EntryState() (*EntryState, error) { return &EntryState{ Type: EntryStateTypeRemove, }, nil } // IsExternal returns if s is an external. func (s *ActualStateAbsent) IsExternal() bool { return false } // Path returns s's path. func (s *ActualStateAbsent) Path() AbsPath { return s.absPath } // Remove removes s. func (s *ActualStateAbsent) Remove(system System) error { return nil } // OriginString returns s's origin. func (s *ActualStateAbsent) OriginString() string { return s.absPath.String() } // EntryState returns s's entry state. func (s *ActualStateDir) EntryState() (*EntryState, error) { return &EntryState{ Type: EntryStateTypeDir, Mode: fs.ModeDir | s.perm, }, nil } // IsExternal returns if s is an external. func (s *ActualStateDir) IsExternal() bool { return false } // Path returns s's path. func (s *ActualStateDir) Path() AbsPath { return s.absPath } // Remove removes s. func (s *ActualStateDir) Remove(system System) error { return system.RemoveAll(s.absPath) } // OriginString returns s's origin. func (s *ActualStateDir) OriginString() string { return s.absPath.String() } // Contents returns s's contents. func (s *ActualStateFile) Contents() ([]byte, error) { return s.contentsFunc() } // EntryState returns s's entry state. func (s *ActualStateFile) EntryState() (*EntryState, error) { contents, err := s.Contents() if err != nil { return nil, err } contentsSHA256 := sha256.Sum256(contents) return &EntryState{ Type: EntryStateTypeFile, Mode: s.perm, ContentsSHA256: HexBytes(contentsSHA256[:]), contents: contents, }, nil } // IsExternal returns if s is an external. func (s *ActualStateFile) IsExternal() bool { return false } // Path returns s's path. func (s *ActualStateFile) Path() AbsPath { return s.absPath } // Perm returns s's perm. func (s *ActualStateFile) Perm() fs.FileMode { return s.perm } // Remove removes s. func (s *ActualStateFile) Remove(system System) error { return system.RemoveAll(s.absPath) } // OriginString returns s's origin. func (s *ActualStateFile) OriginString() string { return s.absPath.String() } // EntryState returns s's entry state. func (s *ActualStateSymlink) EntryState() (*EntryState, error) { linkname, err := s.Linkname() if err != nil { return nil, err } linknameSHA256 := sha256.Sum256([]byte(linkname)) return &EntryState{ Type: EntryStateTypeSymlink, ContentsSHA256: HexBytes(linknameSHA256[:]), contents: []byte(linkname), }, nil } // IsExternal returns if s is an external. func (s *ActualStateSymlink) IsExternal() bool { return false } // Linkname returns s's linkname. func (s *ActualStateSymlink) Linkname() (string, error) { return s.linknameFunc() } // Path returns s's path. func (s *ActualStateSymlink) Path() AbsPath { return s.absPath } // Remove removes s. func (s *ActualStateSymlink) Remove(system System) error { return system.RemoveAll(s.absPath) } // OriginString returns s's origin. func (s *ActualStateSymlink) OriginString() string { return s.absPath.String() } ================================================ FILE: internal/chezmoi/ageencryption.go ================================================ package chezmoi import ( "bytes" "io" "log/slog" "os" "os/exec" "filippo.io/age" "filippo.io/age/armor" "chezmoi.io/chezmoi/internal/chezmoierrors" "chezmoi.io/chezmoi/internal/chezmoilog" ) // An AgeEncryption uses age for encryption and decryption. See // https://age-encryption.org. type AgeEncryption struct { UseBuiltin bool `json:"useBuiltin" mapstructure:"useBuiltin" yaml:"useBuiltin"` Command string `json:"command" mapstructure:"command" yaml:"command"` Args []string `json:"args" mapstructure:"args" yaml:"args"` Identity AbsPath `json:"identity" mapstructure:"identity" yaml:"identity"` Identities []AbsPath `json:"identities" mapstructure:"identities" yaml:"identities"` Passphrase bool `json:"passphrase" mapstructure:"passphrase" yaml:"passphrase"` Recipient string `json:"recipient" mapstructure:"recipient" yaml:"recipient"` Recipients []string `json:"recipients" mapstructure:"recipients" yaml:"recipients"` RecipientsFile AbsPath `json:"recipientsFile" mapstructure:"recipientsFile" yaml:"recipientsFile"` RecipientsFiles []AbsPath `json:"recipientsFiles" mapstructure:"recipientsFiles" yaml:"recipientsFiles"` Suffix string `json:"suffix" mapstructure:"suffix" yaml:"suffix"` Symmetric bool `json:"symmetric" mapstructure:"symmetric" yaml:"symmetric"` } // Decrypt implements Encryption.Decrypt. func (e *AgeEncryption) Decrypt(ciphertext []byte) ([]byte, error) { if e.UseBuiltin { return e.builtinDecrypt(ciphertext) } cmd := exec.Command(e.Command, append(e.decryptArgs(), e.Args...)...) cmd.Stdin = bytes.NewReader(ciphertext) cmd.Stderr = os.Stderr return chezmoilog.LogCmdOutput(slog.Default(), cmd) } // DecryptToFile implements Encryption.DecryptToFile. func (e *AgeEncryption) DecryptToFile(plaintextAbsPath AbsPath, ciphertext []byte) error { if e.UseBuiltin { plaintext, err := e.builtinDecrypt(ciphertext) if err != nil { return err } return os.WriteFile(plaintextAbsPath.String(), plaintext, 0o644) } args := append(append(e.decryptArgs(), "--output", plaintextAbsPath.String()), e.Args...) cmd := exec.Command(e.Command, args...) cmd.Stdin = bytes.NewReader(ciphertext) cmd.Stderr = os.Stderr return chezmoilog.LogCmdRun(slog.Default(), cmd) } // Encrypt implements Encryption.Encrypt. func (e *AgeEncryption) Encrypt(plaintext []byte) ([]byte, error) { if e.UseBuiltin { return e.builtinEncrypt(plaintext) } cmd := exec.Command(e.Command, append(e.encryptArgs(), e.Args...)...) cmd.Stdin = bytes.NewReader(plaintext) cmd.Stderr = os.Stderr return chezmoilog.LogCmdOutput(slog.Default(), cmd) } // EncryptFile implements Encryption.EncryptFile. func (e *AgeEncryption) EncryptFile(plaintextAbsPath AbsPath) ([]byte, error) { if e.UseBuiltin { plaintext, err := os.ReadFile(plaintextAbsPath.String()) if err != nil { return nil, err } return e.builtinEncrypt(plaintext) } args := append(append(e.encryptArgs(), e.Args...), plaintextAbsPath.String()) cmd := exec.Command(e.Command, args...) cmd.Stderr = os.Stderr return chezmoilog.LogCmdOutput(slog.Default(), cmd) } // EncryptedSuffix implements Encryption.EncryptedSuffix. func (e *AgeEncryption) EncryptedSuffix() string { return e.Suffix } // builtinDecrypt decrypts ciphertext using the builtin age. func (e *AgeEncryption) builtinDecrypt(ciphertext []byte) ([]byte, error) { identities, err := e.builtinIdentities() if err != nil { return nil, err } var ciphertextReader io.Reader = bytes.NewReader(ciphertext) if bytes.HasPrefix(ciphertext, []byte(armor.Header)) { ciphertextReader = armor.NewReader(ciphertextReader) } plaintextReader, err := age.Decrypt(ciphertextReader, identities...) if err != nil { return nil, err } plaintextBuffer := &bytes.Buffer{} if _, err := io.Copy(plaintextBuffer, plaintextReader); err != nil { return nil, err } return plaintextBuffer.Bytes(), nil } // builtinEncrypt encrypts ciphertext using the builtin age. func (e *AgeEncryption) builtinEncrypt(plaintext []byte) ([]byte, error) { recipients, err := e.builtinRecipients() if err != nil { return nil, err } ciphertextBuffer := &bytes.Buffer{} armoredCiphertextWriter := armor.NewWriter(ciphertextBuffer) ciphertextWriteCloser, err := age.Encrypt(armoredCiphertextWriter, recipients...) if err != nil { return nil, err } if _, err := io.Copy(ciphertextWriteCloser, bytes.NewReader(plaintext)); err != nil { return nil, err } if err := ciphertextWriteCloser.Close(); err != nil { return nil, err } if err := armoredCiphertextWriter.Close(); err != nil { return nil, err } return ciphertextBuffer.Bytes(), nil } // builtinIdentities returns the identities for decryption using the builtin // age. func (e *AgeEncryption) builtinIdentities() ([]age.Identity, error) { var identities []age.Identity if !e.Identity.IsEmpty() { parsedIdentities, err := parseIdentityFile(e.Identity) if err != nil { return nil, err } identities = append(identities, parsedIdentities...) } for _, identityAbsPath := range e.Identities { parsedIdentities, err := parseIdentityFile(identityAbsPath) if err != nil { return nil, err } identities = append(identities, parsedIdentities...) } return identities, nil } // builtinRecipients returns the recipients for encryption using the builtin // age. func (e *AgeEncryption) builtinRecipients() ([]age.Recipient, error) { recipients := make([]age.Recipient, 0, 1+len(e.Recipients)) if e.Recipient != "" { parsedRecipient, err := age.ParseX25519Recipient(e.Recipient) if err != nil { return nil, err } recipients = append(recipients, parsedRecipient) } for _, recipient := range e.Recipients { parsedRecipient, err := age.ParseX25519Recipient(recipient) if err != nil { return nil, err } recipients = append(recipients, parsedRecipient) } if !e.RecipientsFile.IsEmpty() { parsedRecipients, err := parseRecipientsFile(e.RecipientsFile) if err != nil { return nil, err } recipients = append(recipients, parsedRecipients...) } for _, recipientsFile := range e.RecipientsFiles { parsedRecipients, err := parseRecipientsFile(recipientsFile) if err != nil { return nil, err } recipients = append(recipients, parsedRecipients...) } return recipients, nil } // decryptArgs returns the arguments for decryption. func (e *AgeEncryption) decryptArgs() []string { var args []string args = append(args, "--decrypt") if !e.Passphrase { args = append(args, e.identityArgs()...) } return args } // encryptArgs returns the arguments for encryption. func (e *AgeEncryption) encryptArgs() []string { var args []string args = append(args, "--armor", "--encrypt", ) switch { case e.Passphrase: args = append(args, "--passphrase") case e.Symmetric: args = append(args, e.identityArgs()...) default: if e.Recipient != "" { args = append(args, "--recipient", e.Recipient) } for _, recipient := range e.Recipients { args = append(args, "--recipient", recipient) } if !e.RecipientsFile.IsEmpty() { args = append(args, "--recipients-file", e.RecipientsFile.String()) } for _, recipientsFile := range e.RecipientsFiles { args = append(args, "--recipients-file", recipientsFile.String()) } } return args } // identityArgs returns the arguments for identity. func (e *AgeEncryption) identityArgs() []string { args := make([]string, 0, 2+2*len(e.Identities)) if !e.Identity.IsEmpty() { args = append(args, "--identity", e.Identity.String()) } for _, identity := range e.Identities { args = append(args, "--identity", identity.String()) } return args } // parseIdentityFile parses the identities from identityFile using the builtin // age. func parseIdentityFile(identityFile AbsPath) (identities []age.Identity, err error) { var file *os.File if file, err = os.Open(identityFile.String()); err != nil { return nil, err } defer chezmoierrors.CombineFunc(&err, file.Close) return age.ParseIdentities(file) } // parseRecipientsFile parses the recipients from recipientsFile using the // builtin age. func parseRecipientsFile(recipientsFile AbsPath) (recipients []age.Recipient, err error) { var file *os.File if file, err = os.Open(recipientsFile.String()); err != nil { return nil, err } defer chezmoierrors.CombineFunc(&err, file.Close) return age.ParseRecipients(file) } ================================================ FILE: internal/chezmoi/ageencryption_test.go ================================================ package chezmoi import ( "os" "path/filepath" "testing" "filippo.io/age" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/chezmoitest" ) var ageCommands = []string{ "age", "rage", } func TestAgeEncryption(t *testing.T) { forEachAgeCommand(t, func(t *testing.T, command string) { t.Helper() identityFile := filepath.Join(t.TempDir(), "chezmoi-test-age-key.txt") recipient, err := chezmoitest.AgeGenerateKey(command, identityFile) assert.NoError(t, err) testEncryption(t, &AgeEncryption{ Command: command, Identity: NewAbsPath(identityFile), Recipient: recipient.String(), }) }) } func TestAgeEncryptionMarshalUnmarshal(t *testing.T) { for _, format := range []Format{ FormatJSON, FormatYAML, } { t.Run(format.Name(), func(t *testing.T) { expected := AgeEncryption{ UseBuiltin: true, Command: "command", Args: []string{ "arg1", "arg2", }, Identity: NewAbsPath("/identity"), Identities: []AbsPath{ NewAbsPath("/identity1"), NewAbsPath("/identity2"), }, Passphrase: true, Recipient: "recipient", RecipientsFile: NewAbsPath("/recipients-file"), RecipientsFiles: []AbsPath{ NewAbsPath("/recipients-file1"), NewAbsPath("/recipients-file2"), }, Suffix: "suffix", Symmetric: true, } data, err := format.Marshal(expected) assert.NoError(t, err) var actual AgeEncryption assert.NoError(t, format.Unmarshal(data, &actual)) assert.Equal(t, expected, actual) }) } } func TestAgeEncryptionMarshalUnmarshalField(t *testing.T) { type ConfigFile struct { Age AgeEncryption `json:"age" yaml:"age"` } for _, format := range []Format{ FormatJSON, FormatYAML, } { t.Run(format.Name(), func(t *testing.T) { expected := ConfigFile{ Age: AgeEncryption{ UseBuiltin: true, Command: "command", Args: []string{ "arg1", "arg2", }, Identity: NewAbsPath("/identity"), Identities: []AbsPath{ NewAbsPath("/identity1"), NewAbsPath("/identity2"), }, Passphrase: true, Recipient: "recipient", RecipientsFile: NewAbsPath("/recipients-file"), RecipientsFiles: []AbsPath{ NewAbsPath("/recipients-file1"), NewAbsPath("/recipients-file2"), }, Suffix: "suffix", Symmetric: true, }, } data, err := format.Marshal(expected) assert.NoError(t, err) var actual ConfigFile assert.NoError(t, format.Unmarshal(data, &actual)) assert.Equal(t, expected, actual) }) } } func TestAgeEncryptionMarshalUnmarshalFieldEmbedded(t *testing.T) { type ConfigFile struct { Age AgeEncryption `json:"age" yaml:"age"` } type Config struct { ConfigFile } for _, format := range []Format{ FormatJSON, FormatYAML, } { t.Run(format.Name(), func(t *testing.T) { expected := Config{ ConfigFile: ConfigFile{ Age: AgeEncryption{ UseBuiltin: true, Command: "command", Args: []string{ "arg1", "arg2", }, Identity: NewAbsPath("/identity"), Identities: []AbsPath{ NewAbsPath("/identity1"), NewAbsPath("/identity2"), }, Passphrase: true, Recipient: "recipient", RecipientsFile: NewAbsPath("/recipients-file"), RecipientsFiles: []AbsPath{ NewAbsPath("/recipients-file1"), NewAbsPath("/recipients-file2"), }, Suffix: "suffix", Symmetric: true, }, }, } data, err := format.Marshal(expected) assert.NoError(t, err) var actual Config assert.NoError(t, format.Unmarshal(data, &actual)) assert.Equal(t, expected, actual) }) } } func TestAgeMultipleIdentitiesAndMultipleRecipients(t *testing.T) { forEachAgeCommand(t, func(t *testing.T, command string) { t.Helper() tempDir := t.TempDir() identityFile1 := filepath.Join(tempDir, "chezmoi-test-age-key1.txt") recipient1, err := chezmoitest.AgeGenerateKey(command, identityFile1) assert.NoError(t, err) identityFile2 := filepath.Join(tempDir, "chezmoi-test-age-key2.txt") recipient2, err := chezmoitest.AgeGenerateKey(command, identityFile2) assert.NoError(t, err) testEncryption(t, &AgeEncryption{ Command: command, Identities: []AbsPath{ NewAbsPath(identityFile1), NewAbsPath(identityFile2), }, Recipients: []string{ recipient1.String(), recipient2.String(), }, }) }) } func TestAgeRecipientsFile(t *testing.T) { t.Helper() forEachAgeCommand(t, func(t *testing.T, command string) { t.Helper() tempDir := t.TempDir() identityFile := filepath.Join(tempDir, "chezmoi-test-age-key.txt") recipient, err := chezmoitest.AgeGenerateKey(command, identityFile) assert.NoError(t, err) recipientsFile := filepath.Join(t.TempDir(), "chezmoi-test-age-recipients.txt") assert.NoError(t, os.WriteFile(recipientsFile, []byte(recipient.String()), 0o666)) testEncryption(t, &AgeEncryption{ Command: command, Identity: NewAbsPath(identityFile), RecipientsFile: NewAbsPath(recipientsFile), }) testEncryption(t, &AgeEncryption{ Command: command, Identity: NewAbsPath(identityFile), RecipientsFiles: []AbsPath{ NewAbsPath(recipientsFile), }, }) }) } func TestBuiltinAgeEncryption(t *testing.T) { recipientStringer, identityAbsPath := builtinAgeGenerateKey(t) testEncryption(t, &AgeEncryption{ UseBuiltin: true, Identity: identityAbsPath, Recipient: recipientStringer.String(), }) } func TestBuiltinAgeMultipleIdentitiesAndMultipleRecipients(t *testing.T) { recipient1, identityAbsPath1 := builtinAgeGenerateKey(t) recipient2, identityAbsPath2 := builtinAgeGenerateKey(t) testEncryption(t, &AgeEncryption{ UseBuiltin: true, Identities: []AbsPath{ identityAbsPath1, identityAbsPath2, }, Recipients: []string{ recipient1.String(), recipient2.String(), }, }) } func TestBuiltinAgeRecipientsFile(t *testing.T) { recipient, identityAbsPath := builtinAgeGenerateKey(t) recipientsFile := filepath.Join(t.TempDir(), "chezmoi-builtin-age-recipients.txt") assert.NoError(t, os.WriteFile(recipientsFile, []byte(recipient.String()), 0o666)) testEncryption(t, &AgeEncryption{ UseBuiltin: true, Identity: identityAbsPath, RecipientsFile: NewAbsPath(recipientsFile), }) testEncryption(t, &AgeEncryption{ UseBuiltin: true, Identity: identityAbsPath, RecipientsFiles: []AbsPath{ NewAbsPath(recipientsFile), }, }) } func builtinAgeGenerateKey(t *testing.T) (*age.X25519Recipient, AbsPath) { t.Helper() identity, err := age.GenerateX25519Identity() assert.NoError(t, err) identityFile := filepath.Join(t.TempDir(), "chezmoi-test-builtin-age-key.txt") assert.NoError(t, os.WriteFile(identityFile, []byte(identity.String()), 0o600)) return identity.Recipient(), NewAbsPath(identityFile) } func forEachAgeCommand(t *testing.T, f func(*testing.T, string)) { t.Helper() for _, command := range ageCommands { t.Run(command, func(t *testing.T) { f(t, lookPathOrSkip(t, command)) }) } } ================================================ FILE: internal/chezmoi/archive.go ================================================ package chezmoi import ( "archive/tar" "bytes" "compress/bzip2" "errors" "fmt" "io" "io/fs" "path" "strings" "time" "github.com/klauspost/compress/gzip" "github.com/klauspost/compress/zip" "github.com/klauspost/compress/zstd" "github.com/nwaples/rardecode/v2" "github.com/ulikunitz/xz" "chezmoi.io/chezmoi/internal/chezmoiset" ) // An ArchiveFormat is an archive format and implements the // github.com/spf13/pflag.Value interface. type ArchiveFormat string // Archive formats. const ( ArchiveFormatUnknown ArchiveFormat = "" ArchiveFormatRar ArchiveFormat = "rar" ArchiveFormatTar ArchiveFormat = "tar" ArchiveFormatTarBz2 ArchiveFormat = "tar.bz2" ArchiveFormatTarGz ArchiveFormat = "tar.gz" ArchiveFormatTarXz ArchiveFormat = "tar.xz" ArchiveFormatTarZst ArchiveFormat = "tar.zst" ArchiveFormatZip ArchiveFormat = "zip" ) // An WalkArchiveFunc is called once for each entry in an archive. type WalkArchiveFunc func(name string, info fs.FileInfo, r io.Reader, linkname string) error // GuessArchiveFormat guesses the archive format from the name and data. func GuessArchiveFormat(name string, data []byte) ArchiveFormat { switch nameLower := strings.ToLower(name); { case strings.HasSuffix(nameLower, ".rar"): return ArchiveFormatRar case strings.HasSuffix(nameLower, ".tar"): return ArchiveFormatTar case strings.HasSuffix(nameLower, ".tar.bz2") || strings.HasSuffix(nameLower, ".tbz2"): return ArchiveFormatTarBz2 case strings.HasSuffix(nameLower, ".tar.gz") || strings.HasSuffix(nameLower, ".tgz"): return ArchiveFormatTarGz case strings.HasSuffix(nameLower, ".tar.xz") || strings.HasSuffix(nameLower, ".txz"): return ArchiveFormatTarXz case strings.HasSuffix(nameLower, ".tar.zst"): return ArchiveFormatTarZst case strings.HasSuffix(nameLower, ".zip"): return ArchiveFormatZip } switch { case len(data) >= 6 && bytes.Equal(data[:6], []byte{'R', 'a', 'r', '!', 0x1a, 0x07}): return ArchiveFormatRar case len(data) >= 3 && bytes.Equal(data[:3], []byte{0x1f, 0x8b, 0x08}): return ArchiveFormatTarGz case len(data) >= 4 && bytes.Equal(data[:4], []byte{'P', 'K', 0x03, 0x04}): return ArchiveFormatZip case len(data) >= xz.HeaderLen && xz.ValidHeader(data): return ArchiveFormatTarXz case (&zstd.Header{}).Decode(data) == nil: return ArchiveFormatTarZst case isTarArchive(bytes.NewReader(data)): return ArchiveFormatTar case isTarArchive(bzip2.NewReader(bytes.NewReader(data))): return ArchiveFormatTarBz2 } return ArchiveFormatUnknown } // WalkArchive walks over all the entries in an archive. func WalkArchive(data []byte, format ArchiveFormat, f WalkArchiveFunc) error { switch format { case ArchiveFormatRar: return walkArchiveRar(bytes.NewReader(data), f) case ArchiveFormatZip: return walkArchiveZip(bytes.NewReader(data), int64(len(data)), f) } // r will read bytes in tar format. var r io.Reader = bytes.NewReader(data) switch format { case ArchiveFormatTar: // Already in tar format, do nothing. case ArchiveFormatTarBz2: // Decompress with bzip2. r = bzip2.NewReader(r) case ArchiveFormatTarGz: // Decompress with gzip. var err error r, err = gzip.NewReader(r) if err != nil { return err } case ArchiveFormatTarXz: // Decompress with xz. var err error r, err = xz.NewReader(r) if err != nil { return err } case ArchiveFormatTarZst: // Decompress with zstd. var err error r, err = zstd.NewReader(r) if err != nil { return err } default: return fmt.Errorf("%s: unknown archive format", format) } return walkArchiveTar(r, f) } // isTarArchive returns if r looks like a tar archive. func isTarArchive(r io.Reader) bool { tarReader := tar.NewReader(r) _, err := tarReader.Next() return err == nil } func implicitTarDirHeader(dir string, modTime time.Time) *tar.Header { return &tar.Header{ Typeflag: tar.TypeDir, Name: dir, Mode: 0o777, Size: 0, ModTime: modTime, } } // A RARFileInfo wraps a *rardecode.FileHeader so that it implements // [fs.FileInfo]. type RARFileInfo struct { *rardecode.FileHeader } func (i RARFileInfo) Name() string { return i.FileHeader.Name } func (i RARFileInfo) Size() int64 { return i.UnPackedSize } func (i RARFileInfo) Mode() fs.FileMode { return i.FileHeader.Mode() } func (i RARFileInfo) ModTime() time.Time { return i.ModificationTime } func (i RARFileInfo) IsDir() bool { return i.FileHeader.IsDir } func (i RARFileInfo) Sys() any { return nil } // walkArchiveRar walks over all the entries in a rar archive. func walkArchiveRar(r io.Reader, f WalkArchiveFunc) error { rarReader, err := rardecode.NewReader(r) if err != nil { return err } var skippedDirPrefixes []string seenDirs := chezmoiset.New[string]() processHeader := func(header *rardecode.FileHeader, dir string) error { for _, skippedDirPrefix := range skippedDirPrefixes { if strings.HasPrefix(header.Name, skippedDirPrefix) { return fs.SkipDir } } if seenDirs.Contains(dir) { return nil } seenDirs.Add(dir) name := strings.TrimSuffix(header.Name, "/") switch err := f(name, RARFileInfo{FileHeader: header}, rarReader, ""); { case errors.Is(err, fs.SkipDir): skippedDirPrefixes = append(skippedDirPrefixes, header.Name) case err != nil: return err } return nil } HEADER: for { fileHeader, err := rarReader.Next() switch { case errors.Is(err, io.EOF): return nil case err != nil: return err } switch err := processHeader(fileHeader, fileHeader.Name); { case errors.Is(err, fs.SkipDir): continue HEADER case errors.Is(err, fs.SkipAll): return nil case err != nil: return err } } } // walkArchiveTar walks over all the entries in a tar archive. func walkArchiveTar(r io.Reader, f WalkArchiveFunc) error { tarReader := tar.NewReader(r) var skippedDirPrefixes []string seenDirs := chezmoiset.New[string]() processHeader := func(header *tar.Header, dir string) error { for _, skippedDirPrefix := range skippedDirPrefixes { if strings.HasPrefix(header.Name, skippedDirPrefix) { return fs.SkipDir } } if seenDirs.Contains(dir) { return nil } seenDirs.Add(dir) name := strings.TrimSuffix(header.Name, "/") switch err := f(name, header.FileInfo(), tarReader, header.Linkname); { case errors.Is(err, fs.SkipDir): skippedDirPrefixes = append(skippedDirPrefixes, header.Name) case err != nil: return err } return nil } HEADER: for { header, err := tarReader.Next() switch { case errors.Is(err, io.EOF): return nil case err != nil: return err } switch header.Typeflag { case tar.TypeReg, tar.TypeDir, tar.TypeSymlink: if header.Typeflag == tar.TypeReg { dirs, _ := path.Split(header.Name) dirComponents := strings.Split(strings.TrimSuffix(dirs, "/"), "/") for i := range dirComponents { if dir := strings.Join(dirComponents[0:i+1], "/"); dir != "" { switch err := processHeader(implicitTarDirHeader(dir+"/", header.ModTime), dir+"/"); { case errors.Is(err, fs.SkipDir): continue HEADER case errors.Is(err, fs.SkipAll): return nil case err != nil: return err } } } } switch err := processHeader(header, header.Name); { case errors.Is(err, fs.SkipDir): continue HEADER case errors.Is(err, fs.SkipAll): return nil case err != nil: return err } case tar.TypeXGlobalHeader: // Do nothing. default: return fmt.Errorf("%s: unsupported typeflag '%c'", header.Name, header.Typeflag) } } } // walkArchiveZip walks over all the entries in a zip archive. func walkArchiveZip(r io.ReaderAt, size int64, f WalkArchiveFunc) error { zipReader, err := zip.NewReader(r, size) if err != nil { return err } var skippedDirPrefixes []string seenDirs := chezmoiset.New[string]() processHeader := func(fileInfo fs.FileInfo, dir string) error { for _, skippedDirPrefix := range skippedDirPrefixes { if strings.HasPrefix(dir, skippedDirPrefix) { return fs.SkipDir } } if seenDirs.Contains(dir) { return nil } seenDirs.Add(dir) name := strings.TrimSuffix(dir, "/") dirFileInfo := implicitTarDirHeader(dir, fileInfo.ModTime()).FileInfo() switch err := f(name, dirFileInfo, nil, ""); { case errors.Is(err, fs.SkipDir): skippedDirPrefixes = append(skippedDirPrefixes, dir) return err case err != nil: return err } return nil } FILE: for _, zipFile := range zipReader.File { zipFileReader, err := zipFile.Open() if err != nil { return err } name := path.Clean(zipFile.Name) if strings.HasPrefix(name, "../") || strings.Contains(name, "/../") { return fmt.Errorf("%s: invalid filename", zipFile.Name) } for _, skippedDirPrefix := range skippedDirPrefixes { if strings.HasPrefix(zipFile.Name, skippedDirPrefix) { continue FILE } } switch fileInfo := zipFile.FileInfo(); fileInfo.Mode() & fs.ModeType { case 0: dirs, _ := path.Split(name) dirComponents := strings.Split(strings.TrimSuffix(dirs, "/"), "/") for i := range dirComponents { if dir := strings.Join(dirComponents[0:i+1], "/"); dir != "" { switch err := processHeader(fileInfo, dir+"/"); { case errors.Is(err, fs.SkipDir): continue FILE case errors.Is(err, fs.SkipAll): return nil case err != nil: return err } } } err = f(name, fileInfo, zipFileReader, "") case fs.ModeDir: err = processHeader(fileInfo, name+"/") case fs.ModeSymlink: var linknameBytes []byte linknameBytes, err = io.ReadAll(zipFileReader) if err != nil { return err } err = f(name, fileInfo, nil, string(linknameBytes)) } err2 := zipFileReader.Close() switch { case errors.Is(err, fs.SkipDir): skippedDirPrefixes = append(skippedDirPrefixes, zipFile.Name+"/") case errors.Is(err, fs.SkipAll): return nil case err != nil: return err } if err2 != nil { return err2 } } return nil } ================================================ FILE: internal/chezmoi/archive_test.go ================================================ package chezmoi import ( "io" "io/fs" "testing" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/archivetest" ) func TestWalkArchive(t *testing.T) { nestedRoot := map[string]any{ "dir1": map[string]any{ "subdir1": map[string]any{ "file1": "", "file2": "", }, "subdir2": map[string]any{ "file1": "", "file2": "", }, }, "dir2": map[string]any{ "subdir1": map[string]any{ "file1": "", "file2": "", }, "subdir2": map[string]any{ "file1": "", "file2": "", }, }, "file1": "", "file2": "", "symlink1": &archivetest.Symlink{Target: "file1"}, "symlink2": &archivetest.Symlink{Target: "file2"}, } flatRoot := map[string]any{ "dir1/subdir1/file1": "", "dir1/subdir1/file2": "", "dir1/subdir2/file1": "", "dir1/subdir2/file2": "", "dir2/subdir1/file1": "", "dir2/subdir1/file2": "", "dir2/subdir2/file1": "", "dir2/subdir2/file2": "", "file1": "", "file2": "", "symlink1": &archivetest.Symlink{Target: "file1"}, "symlink2": &archivetest.Symlink{Target: "file2"}, } for _, tc := range []struct { name string root map[string]any dataFunc func(map[string]any) ([]byte, error) archiveFormat ArchiveFormat }{ { name: "tar", root: nestedRoot, dataFunc: archivetest.NewTar, archiveFormat: ArchiveFormatTar, }, { name: "zip", root: nestedRoot, dataFunc: archivetest.NewZip, archiveFormat: ArchiveFormatZip, }, { name: "zip-flat", root: flatRoot, dataFunc: archivetest.NewZip, archiveFormat: ArchiveFormatZip, }, { name: "tar-flat", root: flatRoot, dataFunc: archivetest.NewTar, archiveFormat: ArchiveFormatTar, }, } { t.Run(tc.name, func(t *testing.T) { data, err := tc.dataFunc(tc.root) assert.NoError(t, err) expectedNames := []string{ "dir1", "dir1/subdir1", "dir1/subdir1/file1", "dir1/subdir1/file2", "dir1/subdir2", "dir2", "file1", "file2", "symlink1", } var actualNames []string walkArchiveFunc := func(name string, info fs.FileInfo, r io.Reader, linkname string) error { actualNames = append(actualNames, name) switch name { case "dir1/subdir2": return fs.SkipDir case "dir2": return fs.SkipDir case "symlink1": return fs.SkipAll default: return nil } } assert.NoError(t, WalkArchive(data, tc.archiveFormat, walkArchiveFunc)) assert.Equal(t, expectedNames, actualNames) }) } } ================================================ FILE: internal/chezmoi/archivereadersystem.go ================================================ package chezmoi import ( "fmt" "io" "io/fs" "path" "strings" ) // A ArchiveReaderSystem a system constructed from reading an archive. type ArchiveReaderSystem struct { emptySystemMixin noUpdateSystemMixin fileInfos map[AbsPath]fs.FileInfo contents map[AbsPath][]byte linkname map[AbsPath]string } // ArchiveReaderSystemOptions are options to NewArchiveReaderSystem. type ArchiveReaderSystemOptions struct { RootAbsPath AbsPath StripComponents int } // NewArchiveReaderSystem returns a new ArchiveReaderSystem reading from data // and using archivePath as a hint for the archive format. func NewArchiveReaderSystem( archivePath string, data []byte, format ArchiveFormat, options ArchiveReaderSystemOptions, ) (*ArchiveReaderSystem, error) { s := &ArchiveReaderSystem{ fileInfos: make(map[AbsPath]fs.FileInfo), contents: make(map[AbsPath][]byte), linkname: make(map[AbsPath]string), } if format == ArchiveFormatUnknown { format = GuessArchiveFormat(archivePath, data) } if err := WalkArchive(data, format, func(name string, fileInfo fs.FileInfo, r io.Reader, linkname string) error { if options.StripComponents > 0 { components := strings.Split(name, "/") if len(components) <= options.StripComponents { return nil } name = path.Join(components[options.StripComponents:]...) } if name == "" { return nil } nameAbsPath := options.RootAbsPath.JoinString(name) s.fileInfos[nameAbsPath] = fileInfo switch { case fileInfo.IsDir(): // Do nothing. case fileInfo.Mode()&fs.ModeType == 0: contents, err := io.ReadAll(r) if err != nil { return fmt.Errorf("%s: %w", name, err) } s.contents[nameAbsPath] = contents case fileInfo.Mode()&fs.ModeType == fs.ModeSymlink: s.linkname[nameAbsPath] = linkname default: return fmt.Errorf("%s: unsupported mode %o", name, fileInfo.Mode()&fs.ModeType) } return nil }); err != nil { return nil, err } return s, nil } // FileInfos returns s's fs.FileInfos. func (s *ArchiveReaderSystem) FileInfos() map[AbsPath]fs.FileInfo { return s.fileInfos } // Lstat implements System.Lstat. func (s *ArchiveReaderSystem) Lstat(filename AbsPath) (fs.FileInfo, error) { fileInfo, ok := s.fileInfos[filename] if !ok { return nil, fs.ErrNotExist } return fileInfo, nil } // ReadFile implements System.ReadFile. func (s *ArchiveReaderSystem) ReadFile(name AbsPath) ([]byte, error) { if contents, ok := s.contents[name]; ok { return contents, nil } if _, ok := s.fileInfos[name]; ok { return nil, fs.ErrInvalid } return nil, fs.ErrNotExist } // Readlink implements System.Readlink. func (s *ArchiveReaderSystem) Readlink(name AbsPath) (string, error) { if linkname, ok := s.linkname[name]; ok { return linkname, nil } if _, ok := s.fileInfos[name]; ok { return "", fs.ErrInvalid } return "", fs.ErrNotExist } ================================================ FILE: internal/chezmoi/archivereadersystem_test.go ================================================ package chezmoi import ( "errors" "io/fs" "testing" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/archivetest" ) func TestArchiveReaderSystemTar(t *testing.T) { data, err := archivetest.NewTar(map[string]any{ "dir": map[string]any{ "file": "# contents of dir/file\n", "symlink": &archivetest.Symlink{ Target: "file", }, }, }) assert.NoError(t, err) options := ArchiveReaderSystemOptions{ RootAbsPath: NewAbsPath("/home/user"), StripComponents: 1, } archiveReaderSystem, err := NewArchiveReaderSystem("archive.tar", data, ArchiveFormatTar, options) assert.NoError(t, err) for _, tc := range []struct { absPath AbsPath lstatErr error readlink string readlinkErr error readFileData []byte readFileErr error }{ { absPath: NewAbsPath("/home/user/file"), readlinkErr: fs.ErrInvalid, readFileData: []byte("# contents of dir/file\n"), }, { absPath: NewAbsPath("/home/user/not-exist"), readlinkErr: fs.ErrNotExist, lstatErr: fs.ErrNotExist, readFileErr: fs.ErrNotExist, }, { absPath: NewAbsPath("/home/user/symlink"), readlink: "file", readFileErr: fs.ErrInvalid, }, } { _, err = archiveReaderSystem.Lstat(tc.absPath) if tc.lstatErr != nil { assert.True(t, errors.Is(err, tc.lstatErr)) } else { assert.NoError(t, err) } actualLinkname, err := archiveReaderSystem.Readlink(tc.absPath) if tc.readlinkErr != nil { assert.True(t, errors.Is(err, tc.readlinkErr)) } else { assert.NoError(t, err) assert.Equal(t, tc.readlink, actualLinkname) } actualReadFileData, err := archiveReaderSystem.ReadFile(tc.absPath) if tc.readFileErr != nil { assert.True(t, errors.Is(err, tc.readFileErr)) } else { assert.NoError(t, err) assert.Equal(t, tc.readFileData, actualReadFileData) } } } ================================================ FILE: internal/chezmoi/attr.go ================================================ package chezmoi import ( "errors" "io/fs" "log/slog" "strings" ) var ( errEmptyDirName = errors.New("empty directory name") errEmptyFilename = errors.New("empty filename") ) // A SourceFileTargetType is a the type of a target represented by a file in the // source state. A file in the source state can represent a file, script, or // symlink in the target state. type SourceFileTargetType int // Source file types. const ( SourceFileTypeCreate SourceFileTargetType = iota SourceFileTypeFile SourceFileTypeModify SourceFileTypeRemove SourceFileTypeScript SourceFileTypeSymlink ) var sourceFileTypeStrs = map[SourceFileTargetType]string{ SourceFileTypeCreate: "create", SourceFileTypeFile: "file", SourceFileTypeModify: "modify", SourceFileTypeRemove: "remove", SourceFileTypeScript: "script", SourceFileTypeSymlink: "symlink", } // A ScriptOrder defines when a script should be executed. type ScriptOrder int // Script orders. const ( ScriptOrderBefore ScriptOrder = -1 ScriptOrderDuring ScriptOrder = 0 ScriptOrderAfter ScriptOrder = 1 ) // A ScriptCondition defines under what conditions a script should be executed. type ScriptCondition string // Script conditions. const ( ScriptConditionNone ScriptCondition = "" ScriptConditionAlways ScriptCondition = "always" ScriptConditionOnce ScriptCondition = "once" ScriptConditionOnChange ScriptCondition = "onchange" ) // DirAttr holds attributes parsed from a source directory name. type DirAttr struct { TargetName string Exact bool External bool Private bool ReadOnly bool Remove bool } // A FileAttr holds attributes parsed from a source file name. type FileAttr struct { TargetName string Type SourceFileTargetType Condition ScriptCondition Empty bool Encrypted bool Executable bool Order ScriptOrder Private bool ReadOnly bool Template bool } type invalidDirNameError string func (e invalidDirNameError) Error() string { return string(e) + ": invalid directory name" } type invalidFileNameError string func (e invalidFileNameError) Error() string { return string(e) + ": invalid filename" } // parseDirAttr parses a single directory name in the source state. func parseDirAttr(name string) (DirAttr, error) { if name == "" { return DirAttr{}, errEmptyDirName } originalName := name name, remove := strings.CutPrefix(name, removePrefix) name, external := strings.CutPrefix(name, externalPrefix) name, exact := strings.CutPrefix(name, exactPrefix) name, private := strings.CutPrefix(name, privatePrefix) name, readOnly := strings.CutPrefix(name, readOnlyPrefix) var namePrefix string switch { case strings.HasPrefix(name, dotPrefix): namePrefix = "." name = name[len(dotPrefix):] case strings.HasPrefix(name, literalPrefix): name = name[len(literalPrefix):] } if name == "" { return DirAttr{}, invalidDirNameError(originalName) } return DirAttr{ TargetName: namePrefix + name, Exact: exact, External: external, Private: private, ReadOnly: readOnly, Remove: remove, }, nil } // LogValue implements log/slog.LogValuer.LogValue. func (da DirAttr) LogValue() slog.Value { return slog.GroupValue( slog.String("TargetName", da.TargetName), slog.Bool("Exact", da.Exact), slog.Bool("External", da.External), slog.Bool("Private", da.Private), slog.Bool("ReadOnly", da.ReadOnly), slog.Bool("Remove", da.Remove), ) } // SourceName returns da's source name. func (da DirAttr) SourceName() string { sourceName := "" if da.Remove { sourceName += removePrefix } if da.External { sourceName += externalPrefix } if da.Exact { sourceName += exactPrefix } if da.Private { sourceName += privatePrefix } if da.ReadOnly { sourceName += readOnlyPrefix } switch { case strings.HasPrefix(da.TargetName, "."): sourceName += dotPrefix + da.TargetName[len("."):] case dirPrefixRx.MatchString(da.TargetName): sourceName += literalPrefix + da.TargetName default: sourceName += da.TargetName } return sourceName } // perm returns da's file mode. func (da DirAttr) perm() fs.FileMode { perm := fs.ModePerm if da.Private { perm &^= 0o77 } if da.ReadOnly { perm &^= 0o222 } return perm } // parseFileAttr parses a source file name in the source state. func parseFileAttr(name, encryptedSuffix string) (FileAttr, error) { if name == "" { return FileAttr{}, errEmptyFilename } originalName := name var ( sourceFileType = SourceFileTypeFile condition = ScriptConditionNone empty = false encrypted = false executable = false order = ScriptOrderDuring private = false readOnly = false template = false ) switch { case strings.HasPrefix(name, createPrefix): sourceFileType = SourceFileTypeCreate name = name[len(createPrefix):] name, encrypted = strings.CutPrefix(name, encryptedPrefix) name, private = strings.CutPrefix(name, privatePrefix) name, readOnly = strings.CutPrefix(name, readOnlyPrefix) name, empty = strings.CutPrefix(name, emptyPrefix) name, executable = strings.CutPrefix(name, executablePrefix) case strings.HasPrefix(name, removePrefix): sourceFileType = SourceFileTypeRemove name = name[len(removePrefix):] case strings.HasPrefix(name, runPrefix): sourceFileType = SourceFileTypeScript name = name[len(runPrefix):] switch { case strings.HasPrefix(name, oncePrefix): name = name[len(oncePrefix):] condition = ScriptConditionOnce case strings.HasPrefix(name, onChangePrefix): name = name[len(onChangePrefix):] condition = ScriptConditionOnChange default: condition = ScriptConditionAlways } switch { case strings.HasPrefix(name, beforePrefix): name = name[len(beforePrefix):] order = ScriptOrderBefore case strings.HasPrefix(name, afterPrefix): name = name[len(afterPrefix):] order = ScriptOrderAfter } case strings.HasPrefix(name, symlinkPrefix): sourceFileType = SourceFileTypeSymlink name = name[len(symlinkPrefix):] case strings.HasPrefix(name, modifyPrefix): sourceFileType = SourceFileTypeModify name = name[len(modifyPrefix):] name, encrypted = strings.CutPrefix(name, encryptedPrefix) name, private = strings.CutPrefix(name, privatePrefix) name, readOnly = strings.CutPrefix(name, readOnlyPrefix) name, executable = strings.CutPrefix(name, executablePrefix) default: name, encrypted = strings.CutPrefix(name, encryptedPrefix) name, private = strings.CutPrefix(name, privatePrefix) name, readOnly = strings.CutPrefix(name, readOnlyPrefix) name, empty = strings.CutPrefix(name, emptyPrefix) name, executable = strings.CutPrefix(name, executablePrefix) } var namePrefix string switch { case strings.HasPrefix(name, dotPrefix): namePrefix = "." name = name[len(dotPrefix):] case strings.HasPrefix(name, literalPrefix): name = name[len(literalPrefix):] } if encrypted { name, _ = strings.CutSuffix(name, encryptedSuffix) } switch { case strings.HasSuffix(name, literalSuffix): name = name[:len(name)-len(literalSuffix)] case strings.HasSuffix(name, TemplateSuffix): name = name[:len(name)-len(TemplateSuffix)] template = true name, _ = strings.CutSuffix(name, literalSuffix) } if name == "" { return FileAttr{}, invalidFileNameError(originalName) } return FileAttr{ TargetName: namePrefix + name, Type: sourceFileType, Condition: condition, Empty: empty, Encrypted: encrypted, Executable: executable, Order: order, Private: private, ReadOnly: readOnly, Template: template, }, nil } // LogValue implements log/slog.LogValuer.LogValue. func (fa FileAttr) LogValue() slog.Value { return slog.GroupValue( slog.String("TargetName", fa.TargetName), slog.String("Type", sourceFileTypeStrs[fa.Type]), slog.String("Condition", string(fa.Condition)), slog.Bool("Empty", fa.Empty), slog.Bool("Encrypted", fa.Encrypted), slog.Bool("Executable", fa.Executable), slog.Int("Order", int(fa.Order)), slog.Bool("Private", fa.Private), slog.Bool("ReadOnly", fa.ReadOnly), slog.Bool("Template", fa.Template), ) } // SourceName returns fa's source name. func (fa FileAttr) SourceName(encryptedSuffix string) string { sourceName := "" switch fa.Type { case SourceFileTypeCreate: sourceName = createPrefix if fa.Encrypted { sourceName += encryptedPrefix } if fa.Private { sourceName += privatePrefix } if fa.ReadOnly { sourceName += readOnlyPrefix } if fa.Empty { sourceName += emptyPrefix } if fa.Executable { sourceName += executablePrefix } case SourceFileTypeFile: if fa.Encrypted { sourceName += encryptedPrefix } if fa.Private { sourceName += privatePrefix } if fa.ReadOnly { sourceName += readOnlyPrefix } if fa.Empty { sourceName += emptyPrefix } if fa.Executable { sourceName += executablePrefix } case SourceFileTypeModify: sourceName = modifyPrefix if fa.Encrypted { sourceName += encryptedPrefix } if fa.Private { sourceName += privatePrefix } if fa.ReadOnly { sourceName += readOnlyPrefix } if fa.Executable { sourceName += executablePrefix } case SourceFileTypeRemove: sourceName = removePrefix case SourceFileTypeScript: sourceName = runPrefix switch fa.Condition { case ScriptConditionOnce: sourceName += oncePrefix case ScriptConditionOnChange: sourceName += onChangePrefix } switch fa.Order { case ScriptOrderBefore: sourceName += beforePrefix case ScriptOrderAfter: sourceName += afterPrefix } case SourceFileTypeSymlink: sourceName = symlinkPrefix } switch { case strings.HasPrefix(fa.TargetName, "."): sourceName += dotPrefix + fa.TargetName[len("."):] case filePrefixRx.MatchString(fa.TargetName): sourceName += literalPrefix + fa.TargetName default: sourceName += fa.TargetName } if fileSuffixRx.MatchString(fa.TargetName) { sourceName += literalSuffix } if fa.Template { sourceName += TemplateSuffix } if fa.Encrypted { sourceName += encryptedSuffix } return sourceName } // perm returns fa's permissions. func (fa FileAttr) perm() fs.FileMode { perm := fs.FileMode(0o666) if fa.Executable { perm |= 0o111 } if fa.Private { perm &^= 0o77 } if fa.ReadOnly { perm &^= 0o222 } return perm } ================================================ FILE: internal/chezmoi/attr_test.go ================================================ package chezmoi import ( "io/fs" "testing" "github.com/alecthomas/assert/v2" "github.com/muesli/combinator" ) func TestDirAttr(t *testing.T) { var dirAttrs []DirAttr targetNames := []string{ ".dir", "dir.tmpl", "dir", "exact_dir", "empty_dir", "encrypted_dir", "executable_dir", "once_dir", "run_dir", "run_once_dir", "symlink_dir", } assert.NoError(t, combinator.Generate(&dirAttrs, struct { TargetName []string Exact []bool External []bool Private []bool ReadOnly []bool Remove []bool }{ TargetName: targetNames, Exact: []bool{false, true}, External: []bool{false, true}, Private: []bool{false, true}, ReadOnly: []bool{false, true}, Remove: []bool{false, true}, })) for _, dirAttr := range dirAttrs { actualSourceName := dirAttr.SourceName() actualDirAttr, err := parseDirAttr(actualSourceName) assert.NoError(t, err) assert.Equal(t, dirAttr, actualDirAttr) assert.Equal(t, actualSourceName, actualDirAttr.SourceName()) } } func TestDirAttrLiteral(t *testing.T) { for _, tc := range []struct { sourceName string dirAttr DirAttr }{ { sourceName: "exact_dir", dirAttr: DirAttr{ TargetName: "dir", Exact: true, }, }, { sourceName: "literal_exact_dir", dirAttr: DirAttr{ TargetName: "exact_dir", }, }, { sourceName: "literal_literal_dir", dirAttr: DirAttr{ TargetName: "literal_dir", }, }, } { t.Run(tc.sourceName, func(t *testing.T) { assert.Equal(t, tc.sourceName, tc.dirAttr.SourceName()) actualDirAttr, err := parseDirAttr(tc.sourceName) assert.NoError(t, err) assert.Equal(t, tc.dirAttr, actualDirAttr) }) } } func TestFileAttr(t *testing.T) { var fileAttrs []FileAttr targetNames := []string{ ".name", "create_name", "dot_name", "exact_name", "literal_name", "literal_name", "modify_name", "name.literal", "name", "remove_", "run_name", "symlink_name", "template.tmpl", } assert.NoError(t, combinator.Generate(&fileAttrs, struct { Type SourceFileTargetType TargetName []string Empty []bool Encrypted []bool Executable []bool Private []bool ReadOnly []bool Template []bool }{ Type: SourceFileTypeCreate, TargetName: targetNames, Empty: []bool{false, true}, Encrypted: []bool{false, true}, Executable: []bool{false, true}, Private: []bool{false, true}, ReadOnly: []bool{false, true}, Template: []bool{false, true}, })) assert.NoError(t, combinator.Generate(&fileAttrs, struct { Type SourceFileTargetType TargetName []string Empty []bool Encrypted []bool Executable []bool Private []bool ReadOnly []bool Template []bool }{ Type: SourceFileTypeFile, TargetName: targetNames, Empty: []bool{false, true}, Encrypted: []bool{false, true}, Executable: []bool{false, true}, Private: []bool{false, true}, ReadOnly: []bool{false, true}, Template: []bool{false, true}, })) assert.NoError(t, combinator.Generate(&fileAttrs, struct { Type SourceFileTargetType TargetName []string Encrypted []bool Executable []bool Private []bool ReadOnly []bool Template []bool }{ Type: SourceFileTypeModify, TargetName: targetNames, Encrypted: []bool{false, true}, Executable: []bool{false, true}, Private: []bool{false, true}, ReadOnly: []bool{false, true}, Template: []bool{false, true}, })) assert.NoError(t, combinator.Generate(&fileAttrs, struct { Type SourceFileTargetType TargetName []string }{ Type: SourceFileTypeRemove, TargetName: targetNames, })) assert.NoError(t, combinator.Generate(&fileAttrs, struct { Type SourceFileTargetType Condition []ScriptCondition TargetName []string Order []ScriptOrder }{ Type: SourceFileTypeScript, Condition: []ScriptCondition{ ScriptConditionAlways, ScriptConditionOnce, ScriptConditionOnChange, }, TargetName: targetNames, Order: []ScriptOrder{ScriptOrderBefore, ScriptOrderDuring, ScriptOrderAfter}, })) assert.NoError(t, combinator.Generate(&fileAttrs, struct { Type SourceFileTargetType TargetName []string }{ Type: SourceFileTypeSymlink, TargetName: targetNames, })) for _, fileAttr := range fileAttrs { actualSourceName := fileAttr.SourceName("") t.Run(actualSourceName, func(t *testing.T) { actualFileAttr, err := parseFileAttr(actualSourceName, "") assert.NoError(t, err) assert.Equal(t, fileAttr, actualFileAttr) assert.Equal(t, actualSourceName, actualFileAttr.SourceName("")) }) } } func TestFileAttrEncryptedSuffix(t *testing.T) { for _, tc := range []struct { sourceName string expectedTargetName string }{ { sourceName: "encrypted_file", expectedTargetName: "file", }, { sourceName: "encrypted_file.asc", expectedTargetName: "file", }, { sourceName: "file.asc", expectedTargetName: "file.asc", }, } { actualFileAttr, err := parseFileAttr(tc.sourceName, ".asc") assert.NoError(t, err) assert.Equal(t, tc.expectedTargetName, actualFileAttr.TargetName) } } func TestFileAttrLiteral(t *testing.T) { for _, tc := range []struct { sourceName string encryptedSuffix string fileAttr FileAttr nonCanonical bool }{ { sourceName: "dot_file", fileAttr: FileAttr{ TargetName: ".file", Type: SourceFileTypeFile, }, }, { sourceName: "literal_dot_file", fileAttr: FileAttr{ TargetName: "dot_file", Type: SourceFileTypeFile, }, }, { sourceName: "literal_literal_file", fileAttr: FileAttr{ TargetName: "literal_file", Type: SourceFileTypeFile, }, }, { sourceName: "run_once_script", fileAttr: FileAttr{ TargetName: "script", Condition: ScriptConditionOnce, Type: SourceFileTypeScript, }, }, { sourceName: "run_literal_once_script", fileAttr: FileAttr{ TargetName: "once_script", Condition: ScriptConditionAlways, Type: SourceFileTypeScript, }, }, { sourceName: "file.literal", fileAttr: FileAttr{ TargetName: "file", Type: SourceFileTypeFile, }, nonCanonical: true, }, { sourceName: "file.literal.literal", fileAttr: FileAttr{ TargetName: "file.literal", Type: SourceFileTypeFile, }, }, { sourceName: "file.tmpl", fileAttr: FileAttr{ TargetName: "file", Type: SourceFileTypeFile, Template: true, }, }, { sourceName: "file.tmpl.literal", fileAttr: FileAttr{ TargetName: "file.tmpl", Type: SourceFileTypeFile, }, }, { sourceName: "file.tmpl.literal.tmpl", fileAttr: FileAttr{ TargetName: "file.tmpl", Type: SourceFileTypeFile, Template: true, }, }, } { t.Run(tc.sourceName, func(t *testing.T) { actualFileAttr, err := parseFileAttr(tc.sourceName, tc.encryptedSuffix) assert.NoError(t, err) assert.Equal(t, tc.fileAttr, actualFileAttr) if !tc.nonCanonical { assert.Equal(t, tc.sourceName, tc.fileAttr.SourceName(tc.encryptedSuffix)) } }) } } func TestFileAttrPerm(t *testing.T) { for _, tc := range []struct { fileAttr FileAttr expected fs.FileMode }{ { fileAttr: FileAttr{}, expected: 0o666, }, { fileAttr: FileAttr{ Executable: true, }, expected: fs.ModePerm, }, { fileAttr: FileAttr{ Private: true, }, expected: 0o600, }, { fileAttr: FileAttr{ Executable: true, Private: true, }, expected: 0o700, }, { fileAttr: FileAttr{ ReadOnly: true, }, expected: 0o444, }, { fileAttr: FileAttr{ Executable: true, ReadOnly: true, }, expected: 0o555, }, { fileAttr: FileAttr{ Private: true, ReadOnly: true, }, expected: 0o400, }, { fileAttr: FileAttr{ Executable: true, Private: true, ReadOnly: true, }, expected: 0o500, }, } { assert.Equal(t, tc.expected, tc.fileAttr.perm()) } } func TestInvalidDirAttr(t *testing.T) { for _, tc := range []string{ "dot_", "literal_", } { t.Run(tc, func(t *testing.T) { _, err := parseDirAttr(tc) assert.IsError(t, err, invalidDirNameError(tc)) }) } } func TestInvalidFileAttr(t *testing.T) { for _, tc := range []string{ "dot_", "literal_", ".tmpl", "encrypted_.age", } { t.Run(tc, func(t *testing.T) { _, err := parseFileAttr(tc, ".age") assert.IsError(t, err, invalidFileNameError(tc)) }) } } ================================================ FILE: internal/chezmoi/autotemplate.go ================================================ package chezmoi import ( "cmp" "regexp" "slices" "strings" ) // A TemplateVariable is a template variable. It is used instead of a // map[string]string so that we can control order. type TemplateVariable struct { Components []string Value string } var templateMarkerRx = regexp.MustCompile(`\{{2,}|\}{2,}`) // autoTemplate converts contents into a template by escaping template markers // and replacing values in data with their keys. It returns the template and if // any replacements were made. func autoTemplate(contents []byte, data map[string]any) ([]byte, bool) { contentsStr := string(contents) replacements := false // Replace template markers. replacedTemplateMarkersStr := templateMarkerRx.ReplaceAllString(contentsStr, `{{ "$0" }}`) if replacedTemplateMarkersStr != contentsStr { contentsStr = replacedTemplateMarkersStr replacements = true } // Determine the priority order of replacements. // // Replace longest values first. If there are multiple matches for the same // length of value, then choose the shallowest first so that .variable is // preferred over .chezmoi.config.data.variable. If there are multiple // matches at the same depth, chose the variable that comes first // alphabetically. variables := extractVariables(data) slices.SortFunc(variables, func(a, b TemplateVariable) int { // First sort by value length, longest first. if compare := -cmp.Compare(len(a.Value), len(b.Value)); compare != 0 { return compare } // Second sort by value name depth, shallowest first. if compare := cmp.Compare(len(a.Components), len(b.Components)); compare != 0 { return compare } // Thirdly, sort by component names in alphabetical order. return slices.Compare(a.Components, b.Components) }) // Replace variables in order. // // This naive approach will generate incorrect templates if the variable // names match variable values. The algorithm here is probably O(N^2), we // can do better. for _, variable := range variables { if variable.Value == "" { continue } index := strings.Index(contentsStr, variable.Value) for index != -1 && index != len(contentsStr) { if !inWord(contentsStr, index) && !inWord(contentsStr, index+len(variable.Value)) { // Replace variable.value which is on word boundaries at both // ends. replacement := "{{ ." + strings.Join(variable.Components, ".") + " }}" contentsStr = contentsStr[:index] + replacement + contentsStr[index+len(variable.Value):] index += len(replacement) replacements = true } else { // Otherwise, keep looking. Consume at least one byte so we make // progress. index++ } // Look for the next occurrence of variable.value. j := strings.Index(contentsStr[index:], variable.Value) if j == -1 { // No more occurrences found, so terminate the loop. break } // Advance to the next occurrence. index += j } } return []byte(contentsStr), replacements } // appendVariables appends all template variables in data to variables // and returns variables. data is assumed to be rooted at parent. func appendVariables(variables []TemplateVariable, parent []string, data map[string]any) []TemplateVariable { for name, value := range data { switch value := value.(type) { case string: variable := TemplateVariable{ Components: append(slices.Clone(parent), name), Value: value, } variables = append(variables, variable) case map[string]any: variables = appendVariables(variables, append(parent, name), value) } } return variables } // extractVariables extracts all template variables from data. func extractVariables(data map[string]any) []TemplateVariable { return appendVariables(nil, nil, data) } // inWord returns true if splitting s at position i would split a word. func inWord(s string, i int) bool { return i > 0 && i < len(s) && isWord(s[i-1]) && isWord(s[i]) } // isWord returns true if b is a word byte. func isWord(b byte) bool { return '0' <= b && b <= '9' || 'A' <= b && b <= 'Z' || 'a' <= b && b <= 'z' } ================================================ FILE: internal/chezmoi/autotemplate_test.go ================================================ package chezmoi import ( "testing" "github.com/alecthomas/assert/v2" ) func TestAutoTemplate(t *testing.T) { for _, tc := range []struct { name string contentsStr string data map[string]any expected string expectedReplacements bool }{ { name: "simple", contentsStr: "email = you@example.com\n", data: map[string]any{ "email": "you@example.com", }, expected: "email = {{ .email }}\n", expectedReplacements: true, }, { name: "longest_first", contentsStr: "name = John Smith\nfirstName = John\n", data: map[string]any{ "name": "John Smith", "firstName": "John", }, expected: "" + "name = {{ .name }}\n" + "firstName = {{ .firstName }}\n", expectedReplacements: true, }, { name: "alphabetical_first", contentsStr: "name = John Smith\n", data: map[string]any{ "alpha": "John Smith", "beta": "John Smith", "gamma": "John Smith", }, expected: "name = {{ .alpha }}\n", expectedReplacements: true, }, { name: "nested_values", contentsStr: "email = you@example.com\n", data: map[string]any{ "personal": map[string]any{ "email": "you@example.com", }, }, expected: "email = {{ .personal.email }}\n", expectedReplacements: true, }, { name: "only_replace_words", contentsStr: "darwinian evolution", data: map[string]any{ "os": "darwin", }, expected: "darwinian evolution", // not "{{ .os }}ian evolution" }, { name: "longest_match_first", contentsStr: "/home/user", data: map[string]any{ "homeDir": "/home/user", }, expected: "{{ .homeDir }}", expectedReplacements: true, }, { name: "longest_match_first_prefix", contentsStr: "HOME=/home/user", data: map[string]any{ "homeDir": "/home/user", }, expected: "HOME={{ .homeDir }}", expectedReplacements: true, }, { name: "longest_match_first_suffix", contentsStr: "/home/user/something", data: map[string]any{ "homeDir": "/home/user", }, expected: "{{ .homeDir }}/something", expectedReplacements: true, }, { name: "longest_match_first_prefix_and_suffix", contentsStr: "HOME=/home/user/something", data: map[string]any{ "homeDir": "/home/user", }, expected: "HOME={{ .homeDir }}/something", expectedReplacements: true, }, { name: "depth_first", contentsStr: "a", data: map[string]any{ "deep": map[string]any{ "deeper": "a", }, "shallow": "a", }, expected: "{{ .shallow }}", expectedReplacements: true, }, { name: "alphabetical_first", contentsStr: "a", data: map[string]any{ "parent": map[string]any{ "alpha": "a", "beta": "a", }, }, expected: "{{ .parent.alpha }}", expectedReplacements: true, }, { name: "words_only", contentsStr: "aaa aa a aa aaa aa a aa aaa", data: map[string]any{ "alpha": "a", }, expected: "aaa aa {{ .alpha }} aa aaa aa {{ .alpha }} aa aaa", expectedReplacements: true, }, { name: "words_only_2", contentsStr: "aaa aa a aa aaa aa a aa aaa", data: map[string]any{ "alpha": "aa", }, expected: "aaa {{ .alpha }} a {{ .alpha }} aaa {{ .alpha }} a {{ .alpha }} aaa", expectedReplacements: true, }, { name: "words_only_3", contentsStr: "aaa aa a aa aaa aa a aa aaa", data: map[string]any{ "alpha": "aaa", }, expected: "{{ .alpha }} aa a aa {{ .alpha }} aa a aa {{ .alpha }}", expectedReplacements: true, }, { name: "skip_empty", contentsStr: "a", data: map[string]any{ "empty": "", }, expected: "a", }, { name: "markers", contentsStr: "{{}}", expected: `{{ "{{" }}{{ "}}" }}`, expectedReplacements: true, }, } { t.Run(tc.name, func(t *testing.T) { actualTemplate, actualReplacements := autoTemplate([]byte(tc.contentsStr), tc.data) assert.Equal(t, tc.expected, string(actualTemplate)) assert.Equal(t, tc.expectedReplacements, actualReplacements) }) } } func TestInWord(t *testing.T) { for _, tc := range []struct { s string i int expected bool }{ {s: "", i: 0, expected: false}, {s: "a", i: 0, expected: false}, {s: "a", i: 1, expected: false}, {s: "ab", i: 0, expected: false}, {s: "ab", i: 1, expected: true}, {s: "ab", i: 2, expected: false}, {s: "abc", i: 0, expected: false}, {s: "abc", i: 1, expected: true}, {s: "abc", i: 2, expected: true}, {s: "abc", i: 3, expected: false}, {s: " abc ", i: 0, expected: false}, {s: " abc ", i: 1, expected: false}, {s: " abc ", i: 2, expected: true}, {s: " abc ", i: 3, expected: true}, {s: " abc ", i: 4, expected: false}, {s: " abc ", i: 5, expected: false}, {s: "/home/user", i: 0, expected: false}, {s: "/home/user", i: 1, expected: false}, {s: "/home/user", i: 2, expected: true}, {s: "/home/user", i: 3, expected: true}, {s: "/home/user", i: 4, expected: true}, {s: "/home/user", i: 5, expected: false}, {s: "/home/user", i: 6, expected: false}, {s: "/home/user", i: 7, expected: true}, {s: "/home/user", i: 8, expected: true}, {s: "/home/user", i: 9, expected: true}, {s: "/home/user", i: 10, expected: false}, } { assert.Equal(t, tc.expected, inWord(tc.s, tc.i)) } } ================================================ FILE: internal/chezmoi/boltpersistentstate.go ================================================ package chezmoi import ( "errors" "fmt" "io/fs" "os" "path/filepath" "slices" "syscall" "time" "go.etcd.io/bbolt" ) // A BoltPersistentStateMode is a mode for opening a PersistentState. type BoltPersistentStateMode int // Persistent state modes. const ( BoltPersistentStateReadOnly BoltPersistentStateMode = iota BoltPersistentStateReadWrite ) // A BoltPersistentState is a state persisted with bolt. type BoltPersistentState struct { system System empty bool path AbsPath options bbolt.Options db *bbolt.DB } // NewBoltPersistentState returns a new BoltPersistentState. func NewBoltPersistentState(system System, path AbsPath, mode BoltPersistentStateMode) (*BoltPersistentState, error) { empty := false switch _, err := system.Stat(path); { case errors.Is(err, fs.ErrNotExist): // We need to simulate an empty persistent state because Bolt's // read-only mode is only supported for databases that already exist. // // If the database does not already exist, then Bolt will open it with // O_RDONLY and then attempt to initialize it, which leads to EBADF // errors on Linux. See also https://github.com/etcd-io/bbolt/issues/98. empty = true case err != nil: return nil, err } options := bbolt.Options{ OpenFile: func(name string, flag int, perm fs.FileMode) (*os.File, error) { rawPath, err := system.RawPath(NewAbsPath(name)) if err != nil { return nil, err } dir, _ := filepath.Split(rawPath.String()) if err := os.MkdirAll(dir, 0o777); err != nil { return nil, err } return os.OpenFile(rawPath.String(), flag, perm) }, ReadOnly: mode == BoltPersistentStateReadOnly, Timeout: time.Second, } return &BoltPersistentState{ system: system, empty: empty, path: path, options: options, }, nil } // Close closes b. func (b *BoltPersistentState) Close() error { if b.db != nil { if err := b.db.Close(); err != nil { return err } b.db = nil } return nil } // CopyTo copies b to p. func (b *BoltPersistentState) CopyTo(p PersistentState) error { if b.empty { return nil } if err := b.open(); err != nil { return err } return b.db.View(func(tx *bbolt.Tx) error { return tx.ForEach(func(bucket []byte, b *bbolt.Bucket) error { return b.ForEach(func(key, value []byte) error { return p.Set(slices.Clone(bucket), slices.Clone(key), slices.Clone(value)) }) }) }) } // Delete deletes the value associate with key in bucket. If bucket or key does // not exist then Delete does nothing. func (b *BoltPersistentState) Delete(bucket, key []byte) error { if b.empty { return nil } if err := b.open(); err != nil { return err } return b.db.Update(func(tx *bbolt.Tx) error { b := tx.Bucket(bucket) if b == nil { return nil } return b.Delete(key) }) } // DeleteBucket deletes the bucket. func (b *BoltPersistentState) DeleteBucket(bucket []byte) error { if b.empty { return nil } if err := b.open(); err != nil { return err } return b.db.Update(func(tx *bbolt.Tx) error { return tx.DeleteBucket(bucket) }) } // Data returns all the data in b. func (b *BoltPersistentState) Data() (map[string]map[string]string, error) { if b.empty { return nil, nil } if err := b.open(); err != nil { return nil, err } data := make(map[string]map[string]string) err := b.db.View(func(tx *bbolt.Tx) error { return tx.ForEach(func(name []byte, b *bbolt.Bucket) error { bucketName := string(name) bucket, ok := data[bucketName] if !ok { bucket = make(map[string]string) data[bucketName] = bucket } return b.ForEach(func(k, v []byte) error { bucket[string(k)] = string(v) return nil }) }) }) if err != nil { return nil, err } return data, nil } // ForEach calls fn for each key, value pair in bucket. func (b *BoltPersistentState) ForEach(bucket []byte, fn func(k, v []byte) error) error { if b.empty { return nil } if err := b.open(); err != nil { return err } return b.db.View(func(tx *bbolt.Tx) error { b := tx.Bucket(bucket) if b == nil { return nil } return b.ForEach(func(k, v []byte) error { return fn(slices.Clone(k), slices.Clone(v)) }) }) } // Get returns the value associated with key in bucket. func (b *BoltPersistentState) Get(bucket, key []byte) ([]byte, error) { if b.empty { return nil, nil } if err := b.open(); err != nil { return nil, err } var value []byte if err := b.db.View(func(tx *bbolt.Tx) error { b := tx.Bucket(bucket) if b == nil { return nil } value = slices.Clone(b.Get(key)) return nil }); err != nil { return nil, err } return value, nil } // Set sets the value associated with key in bucket. bucket will be created if // it does not already exist. func (b *BoltPersistentState) Set(bucket, key, value []byte) error { if err := b.open(); err != nil { return err } return b.db.Update(func(tx *bbolt.Tx) error { b, err := tx.CreateBucketIfNotExists(bucket) if err != nil { return err } return b.Put(key, value) }) } // open opens b's database if it is not already open, creating it if needed. func (b *BoltPersistentState) open() error { if b.db != nil { return nil } if err := MkdirAll(b.system, b.path.Dir(), fs.ModePerm); err != nil { return err } switch db, err := bbolt.Open(b.path.String(), 0o600, &b.options); { case errors.Is(err, syscall.EINVAL): // Assume that any EINVAL error is because flock(2) failed. return fmt.Errorf("open %s: failed to acquire lock: %w", b.path, err) case err != nil: return fmt.Errorf("open %s: %w", b.path, err) default: b.empty = false b.db = db return nil } } ================================================ FILE: internal/chezmoi/boltpersistentstate_test.go ================================================ package chezmoi import ( "os" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" "chezmoi.io/chezmoi/internal/chezmoitest" ) var _ PersistentState = &BoltPersistentState{} func TestBoltPersistentState(t *testing.T) { chezmoitest.WithTestFS(t, nil, func(fileSystem vfs.FS) { var ( system = NewRealSystem(fileSystem) path = NewAbsPath("/home/user/.config/chezmoi/chezmoistate.boltdb") bucket = []byte("bucket") key = []byte("key") value = []byte("value") ) b1, err := NewBoltPersistentState(system, path, BoltPersistentStateReadWrite) assert.NoError(t, err) // Test that getting a key from an non-existent state does not create // the state. actualValue, err := b1.Get(bucket, key) assert.NoError(t, err) vfst.RunTests(t, fileSystem, "", vfst.TestPath(path.String(), vfst.TestDoesNotExist(), ), ) assert.Equal(t, []byte(nil), actualValue) // Test that deleting a key from a non-existent state does not create // the state. assert.NoError(t, b1.Delete(bucket, key)) vfst.RunTests(t, fileSystem, "", vfst.TestPath(path.String(), vfst.TestDoesNotExist(), ), ) // Test that setting a key creates the state. assert.NoError(t, b1.Set(bucket, key, value)) vfst.RunTests(t, fileSystem, "", vfst.TestPath(path.String(), vfst.TestModeIsRegular(), ), ) actualValue, err = b1.Get(bucket, key) assert.NoError(t, err) assert.Equal(t, value, actualValue) visited := false assert.NoError(t, b1.ForEach(bucket, func(k, v []byte) error { visited = true assert.Equal(t, key, k) assert.Equal(t, value, v) return nil })) assert.True(t, visited) data, err := b1.Data() assert.NoError(t, err) assert.Equal(t, map[string]map[string]string{ string(bucket): { string(key): string(value), }, }, data) assert.NoError(t, b1.Close()) b2, err := NewBoltPersistentState(system, path, BoltPersistentStateReadWrite) assert.NoError(t, err) assert.NoError(t, b2.Delete(bucket, key)) actualValue, err = b2.Get(bucket, key) assert.NoError(t, err) assert.Equal(t, []byte(nil), actualValue) }) } func TestBoltPersistentStateMock(t *testing.T) { chezmoitest.WithTestFS(t, nil, func(fileSystem vfs.FS) { var ( system = NewRealSystem(fileSystem) path = NewAbsPath("/home/user/.config/chezmoi/chezmoistate.boltdb") bucket = []byte("bucket") key = []byte("key") value1 = []byte("value1") value2 = []byte("value2") ) b, err := NewBoltPersistentState(system, path, BoltPersistentStateReadWrite) assert.NoError(t, err) assert.NoError(t, b.Set(bucket, key, value1)) m := NewMockPersistentState() assert.NoError(t, b.CopyTo(m), err) actualValue, err := m.Get(bucket, key) assert.NoError(t, err) assert.Equal(t, value1, actualValue) assert.NoError(t, m.Set(bucket, key, value2)) actualValue, err = m.Get(bucket, key) assert.NoError(t, err) assert.Equal(t, value2, actualValue) actualValue, err = b.Get(bucket, key) assert.NoError(t, err) assert.Equal(t, value1, actualValue) assert.NoError(t, m.Delete(bucket, key)) actualValue, err = m.Get(bucket, key) assert.NoError(t, err) assert.Zero(t, actualValue) actualValue, err = b.Get(bucket, key) assert.NoError(t, err) assert.Equal(t, value1, actualValue) assert.NoError(t, b.Close()) }) } func TestBoltPersistentStateGeneric(t *testing.T) { system := NewRealSystem(vfs.OSFS) var tempDirs []string defer func() { for _, tempDir := range tempDirs { assert.NoError(t, os.RemoveAll(tempDir)) } }() testPersistentState(t, func() PersistentState { tempDir := t.TempDir() absPath := NewAbsPath(tempDir).JoinString("chezmoistate.boltdb") b, err := NewBoltPersistentState(system, absPath, BoltPersistentStateReadWrite) assert.NoError(t, err) return b }) } func TestBoltPersistentStateReadOnly(t *testing.T) { chezmoitest.WithTestFS(t, nil, func(fileSystem vfs.FS) { var ( system = NewRealSystem(fileSystem) path = NewAbsPath("/home/user/.config/chezmoi/chezmoistate.boltdb") bucket = []byte("bucket") key = []byte("key") value = []byte("value") ) b1, err := NewBoltPersistentState(system, path, BoltPersistentStateReadWrite) assert.NoError(t, err) assert.NoError(t, b1.Set(bucket, key, value)) assert.NoError(t, b1.Close()) b2, err := NewBoltPersistentState(system, path, BoltPersistentStateReadOnly) assert.NoError(t, err) defer func() { assert.NoError(t, b2.Close()) }() b3, err := NewBoltPersistentState(system, path, BoltPersistentStateReadOnly) assert.NoError(t, err) defer func() { assert.NoError(t, b3.Close()) }() actualValueB, err := b2.Get(bucket, key) assert.NoError(t, err) assert.Equal(t, value, actualValueB) actualValueC, err := b3.Get(bucket, key) assert.NoError(t, err) assert.Equal(t, value, actualValueC) assert.Error(t, b2.Set(bucket, key, value)) assert.Error(t, b3.Set(bucket, key, value)) assert.NoError(t, b2.Close()) assert.NoError(t, b3.Close()) }) } ================================================ FILE: internal/chezmoi/byteordermarks.go ================================================ package chezmoi type byteOrderMark struct { prefix []byte name string } // Byte order marks. See https://en.wikipedia.org/wiki/Byte_order_mark. var byteOrderMarks = []byteOrderMark{ { prefix: []byte{0xef, 0xbb, 0xbf}, name: "UTF-8", }, { prefix: []byte{0xfe, 0xff}, name: "UTF-16 (BE)", }, { prefix: []byte{0xff, 0xfe}, name: "UTF-16 (LE)", }, { prefix: []byte{0x00, 0x00, 0xfe, 0xff}, name: "UTF-32 (BE)", }, { prefix: []byte{0xff, 0xfe, 0x00, 0x00}, name: "UTF-32 (LE)", }, { prefix: []byte{0x2b, 0x2f, 0x76}, name: "UTF-7", }, { prefix: []byte{0xf7, 0x64, 0x4c}, name: "UTF-1", }, { prefix: []byte{0xdd, 0x73, 0x66, 0x73}, name: "UTF-EBCDIC", }, { prefix: []byte{0x0e, 0xfe, 0xff}, name: "SCSU", }, { prefix: []byte{0xfb, 0xee, 0x28}, name: "BOCU-1", }, { prefix: []byte{0x84, 0x31, 0x95, 0x33}, name: "GB18030", }, } ================================================ FILE: internal/chezmoi/chezmoi.go ================================================ // Package chezmoi contains chezmoi's core logic. package chezmoi import ( "bytes" "crypto/md5" "crypto/sha1" "crypto/sha256" "crypto/sha512" "fmt" "io/fs" "net" "os" "regexp" "runtime" "strconv" "strings" "sync" "github.com/spf13/cobra" vfs "github.com/twpayne/go-vfs/v5" "golang.org/x/crypto/ripemd160" //nolint:staticcheck "chezmoi.io/chezmoi/internal/chezmoiset" ) var ( // DefaultTemplateOptions are the default template options. DefaultTemplateOptions = []string{"missingkey=error"} // Umask is the process's umask. Umask = fs.FileMode(0) ) // Prefixes and suffixes. const ( ignorePrefix = "." afterPrefix = "after_" beforePrefix = "before_" createPrefix = "create_" dotPrefix = "dot_" emptyPrefix = "empty_" encryptedPrefix = "encrypted_" exactPrefix = "exact_" executablePrefix = "executable_" externalPrefix = "external_" literalPrefix = "literal_" modifyPrefix = "modify_" oncePrefix = "once_" onChangePrefix = "onchange_" privatePrefix = "private_" readOnlyPrefix = "readonly_" removePrefix = "remove_" runPrefix = "run_" symlinkPrefix = "symlink_" literalSuffix = ".literal" TemplateSuffix = ".tmpl" ) // Special file names. const ( Prefix = ".chezmoi" RootName = Prefix + "root" TemplatesDirName = Prefix + "templates" VersionName = Prefix + "version" dataName = Prefix + "data" externalName = Prefix + "external" externalsDirName = Prefix + "externals" ignoreName = Prefix + "ignore" removeName = Prefix + "remove" scriptsDirName = Prefix + "scripts" ) var ( dirPrefixRx = regexp.MustCompile(`\A(dot|exact|literal|readonly|private)_`) filePrefixRx = regexp.MustCompile( `\A(after|before|create|dot|empty|encrypted|executable|literal|modify|once|private|readonly|remove|run|symlink)_`, ) fileSuffixRx = regexp.MustCompile(`\.(literal|tmpl)\z`) whitespaceRx = regexp.MustCompile(`\s+`) ) // knownPrefixedFiles is a set of known filenames with the .chezmoi prefix. var knownPrefixedFiles = chezmoiset.New( Prefix+".json"+TemplateSuffix, Prefix+".toml"+TemplateSuffix, Prefix+".yaml"+TemplateSuffix, RootName, VersionName, dataName+".json", dataName+".toml", dataName+".yaml", externalName+".json"+TemplateSuffix, externalName+".json", externalName+".toml"+TemplateSuffix, externalName+".toml", externalName+".yaml"+TemplateSuffix, externalName+".yaml", ignoreName+TemplateSuffix, ignoreName, removeName+TemplateSuffix, removeName, ) // knownPrefixedDirs is a set of known dirnames with the .chezmoi prefix. var knownPrefixedDirs = chezmoiset.New( TemplatesDirName, dataName, externalsDirName, scriptsDirName, ) // knownTargetFiles is a set of known target files that should not be managed // directly. var knownTargetFiles = chezmoiset.New( "chezmoi.json", "chezmoi.toml", "chezmoi.yaml", "chezmoistate.boltdb", ) // ignoredHostnameSuffixes is a list of suffixes that are ignored when // determining the hostname from /etc/hosts. See // https://en.wikipedia.org/wiki/Special-use_domain_name. var ignoredHostnameSuffixes = []string{ ".alt", ".example", ".invalid", ".internal", ".local", ".localhost", ".onion", ".test", } var FileModeTypeNames = map[fs.FileMode]string{ 0: "file", fs.ModeDir: "dir", fs.ModeSymlink: "symlink", fs.ModeNamedPipe: "named pipe", fs.ModeSocket: "socket", fs.ModeDevice: "device", fs.ModeCharDevice: "char device", } // A TextConvFunc converts the contents of a file into a more human-readable // form. It returns the converted data, whether any conversion occurred, and any // error. type TextConvFunc func(string, []byte) ([]byte, bool, error) // FQDNHostname returns the FQDN hostname. func FQDNHostname(fileSystem vfs.FS) (string, error) { // First, try os.Hostname. If it returns something that looks like a FQDN // hostname, or we're on Windows, return it. osHostname, err := os.Hostname() if runtime.GOOS == "windows" || (err == nil && strings.Contains(osHostname, ".")) { return osHostname, err } // Otherwise, if we're on OpenBSD, try /etc/myname. if runtime.GOOS == "openbsd" { if fqdnHostname, err := etcMynameFQDNHostname(fileSystem); err == nil && fqdnHostname != "" { return fqdnHostname, nil } } // Otherwise, try /etc/hosts. if fqdnHostname, err := etcHostsFQDNHostname(fileSystem); err == nil && fqdnHostname != "" { return fqdnHostname, nil } // Otherwise, try /etc/hostname. if fqdnHostname, err := etcHostnameFQDNHostname(fileSystem); err == nil && fqdnHostname != "" { return fqdnHostname, nil } // Finally, fall back to whatever os.Hostname returned. return osHostname, err } // FlagCompletionFunc returns a flag completion function. func FlagCompletionFunc(allCompletions []string) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var completions []string for _, completion := range allCompletions { if strings.HasPrefix(completion, toComplete) { completions = append(completions, completion) } } return completions, cobra.ShellCompDirectiveNoFileComp } } // ParseBool is like [strconv.ParseBool] but also accepts on, ON, y, Y, yes, // YES, n, N, no, NO, off, and OFF. func ParseBool(str string) (bool, error) { switch strings.ToLower(strings.TrimSpace(str)) { case "n", "no", "off": return false, nil case "on", "y", "yes": return true, nil default: return strconv.ParseBool(str) } } // IsSuspiciousSourceDirEntry returns true if base is a suspicious dir entry. func IsSuspiciousSourceDirEntry(base string, fileInfo fs.FileInfo, encryptedSuffixes []string) bool { switch fileInfo.Mode().Type() { case 0: if strings.HasPrefix(base, Prefix) && !knownPrefixedFiles.Contains(base) { return true } for _, encryptedSuffix := range encryptedSuffixes { fileAttr, err := parseFileAttr(fileInfo.Name(), encryptedSuffix) if err != nil { return true } if knownTargetFiles.Contains(fileAttr.TargetName) { return true } } return false case fs.ModeDir: return strings.HasPrefix(base, Prefix) && !knownPrefixedDirs.Contains(base) case fs.ModeSymlink: return strings.HasPrefix(base, Prefix) default: return true } } // UniqueAbbreviations returns a map of unique abbreviations of values to // values. Values always map to themselves. func UniqueAbbreviations(values []string) map[string]string { abbreviations := make(map[string][]string) for _, value := range values { for i := 1; i <= len(value); i++ { abbreviation := value[:i] abbreviations[abbreviation] = append(abbreviations[abbreviation], value) } } uniqueAbbreviations := make(map[string]string) for abbreviation, values := range abbreviations { if len(values) == 1 { uniqueAbbreviations[abbreviation] = values[0] } } for _, value := range values { uniqueAbbreviations[value] = value } return uniqueAbbreviations } // eagerNoErr returns a function that returns an eagerly-evaluated value and no // error. func eagerNoErr[T any](value T) func() (T, error) { return func() (T, error) { return value, nil } } // eagerZeroNoErr returns a function that returns a zero value and no error. func eagerZeroNoErr[T any]() func() (T, error) { var zero T return func() (T, error) { return zero, nil } } // etcHostnameFQDNHostname returns the FQDN hostname from parsing /etc/hostname. func etcHostnameFQDNHostname(fileSystem vfs.FS) (string, error) { contents, err := fileSystem.ReadFile("/etc/hostname") if err != nil { return "", err } for line := range bytes.Lines(contents) { line, _, _ = bytes.Cut(line, []byte{'#'}) if hostname := bytes.TrimSpace(line); len(hostname) != 0 { return string(hostname), nil } } return "", nil } // etcMynameFQDNHostname returns the FQDN hostname from parsing /etc/myname. // See OpenBSD's myname(5) for details on this file. func etcMynameFQDNHostname(fileSystem vfs.FS) (string, error) { contents, err := fileSystem.ReadFile("/etc/myname") if err != nil { return "", err } for line := range bytes.Lines(contents) { if bytes.HasPrefix(line, []byte{'#'}) { continue } if hostname := bytes.TrimSpace(line); len(hostname) != 0 { return string(hostname), nil } } return "", nil } // etcHostsFQDNHostname returns the FQDN hostname from parsing /etc/hosts. func etcHostsFQDNHostname(fileSystem vfs.FS) (string, error) { contents, err := fileSystem.ReadFile("/etc/hosts") if err != nil { return "", err } LINE: for line := range bytes.Lines(contents) { line, _, _ = bytes.Cut(bytes.TrimSpace(line), []byte{'#'}) fields := whitespaceRx.Split(string(line), -1) if len(fields) < 2 { continue } ipAddress, canonicalHostname := fields[0], fields[1] if !net.ParseIP(ipAddress).IsLoopback() { continue } if hostname, _, found := strings.Cut(canonicalHostname, "."); !found || hostname == "localhost" { continue } for _, ignoredHostnameSuffix := range ignoredHostnameSuffixes { if strings.HasSuffix(canonicalHostname, ignoredHostnameSuffix) { continue LINE } } return canonicalHostname, nil } return "", nil } // isEmpty returns true if data is empty after trimming whitespace from both // ends. func isEmpty(data []byte) bool { return len(bytes.TrimSpace(data)) == 0 } // isPrivate returns if fileInfo is private. func isPrivate(fileInfo fs.FileInfo) bool { return fileInfo.Mode().Perm()&0o77 == 0 } // isReadOnly returns if fileInfo is read-only. func isReadOnly(fileInfo fs.FileInfo) bool { return fileInfo.Mode().Perm()&0o222 == 0 } // md5Sum returns the MD5 sum of data. func md5Sum(data []byte) []byte { md5SumArr := md5.Sum(data) return md5SumArr[:] } // lazySHA256 returns a function that returns a SHA256 computed lazily. func lazySHA256(contentsFunc func() ([]byte, error)) func() ([32]byte, error) { return sync.OnceValues(func() ([32]byte, error) { contents, err := contentsFunc() if err != nil { return [32]byte{}, err } return sha256.Sum256(contents), nil }) } // modeTypeName returns a string representation of mode. func modeTypeName(mode fs.FileMode) string { if name, ok := FileModeTypeNames[mode.Type()]; ok { return name } return fmt.Sprintf("0o%o: unknown type", mode.Type()) } // ripemd160Sum returns the RIPEMD-160 sum of data. func ripemd160Sum(data []byte) []byte { return ripemd160.New().Sum(data) } // sha1Sum returns the SHA1 sum of data. func sha1Sum(data []byte) []byte { sha1SumArr := sha1.Sum(data) return sha1SumArr[:] } // sha384Sum returns the SHA384 sum of data. func sha384Sum(data []byte) []byte { sha384SumArr := sha512.Sum384(data) return sha384SumArr[:] } // sha512Sum returns the SHA512 sum of data. func sha512Sum(data []byte) []byte { sha512SumArr := sha512.Sum512(data) return sha512SumArr[:] } // ensureSuffix adds suffix to s if s is not suffixed by suffix. func ensureSuffix(s, suffix string) string { if strings.HasSuffix(s, suffix) { return s } return s + suffix } ================================================ FILE: internal/chezmoi/chezmoi_test.go ================================================ package chezmoi import ( "strings" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestEtcHostsFQDNHostname(t *testing.T) { for _, tc := range []struct { name string root any f func(vfs.FS) (string, error) expected string }{ { name: "etc_hosts", root: map[string]any{ "/etc/hosts": chezmoitest.JoinLines( `# The following lines are desirable for IPv4 capable hosts`, `127.0.0.1 localhost`, ``, `# 127.0.1.1 is often used for the FQDN of the machine`, `127.0.1.1 host.example.com host`, `192.168.1.10 foo.example.com foo`, `192.168.1.13 bar.example.com bar`, `146.82.138.7 master.debian.org master`, `209.237.226.90 www.example.org`, ``, `# The following lines are desirable for IPv6 capable hosts`, `::1 localhost ip6-localhost ip6-loopback`, `ff02::1 ip6-allnodes`, `ff02::2 ip6-allrouters`, ), }, f: etcHostsFQDNHostname, expected: "host.example.com", }, { name: "etc_hosts_loopback_ipv4", root: map[string]any{ "/etc/hosts": chezmoitest.JoinLines( `invalid localhost`, `127.0.0.1 localhost`, `::1 localhost`, `127.0.0.2 host.example.com host`, ), }, f: etcHostsFQDNHostname, expected: "host.example.com", }, { name: "etc_hosts_loopback_ipv4_localhost_dot_localdomain", root: map[string]any{ "/etc/hosts": chezmoitest.JoinLines( `127.0.0.1 localhost.localdomain`, `127.0.0.2 service.local`, `127.0.0.4 multi.part.domain.example`, `127.0.0.4 host.example.com host`, ), }, f: etcHostsFQDNHostname, expected: "host.example.com", }, { name: "etc_hosts_loopback_ipv6", root: map[string]any{ "/etc/hosts": chezmoitest.JoinLines( `127.0.0.1 localhost`, `::1 localhost`, `::1 host.example.com host`, ), }, f: etcHostsFQDNHostname, expected: "host.example.com", }, { name: "etc_hosts_whitespace_and_comments", root: map[string]any{ "/etc/hosts": chezmoitest.JoinLines( " \t127.0.1.1 \thost.example.com# comment", ), }, f: etcHostsFQDNHostname, expected: "host.example.com", }, { name: "etc_hosts_missing_canonical_hostname", root: map[string]any{ "/etc/hosts": chezmoitest.JoinLines( `127.0.1.1`, `127.0.1.1 host.example.com`, ), }, f: etcHostsFQDNHostname, expected: "host.example.com", }, { name: "etc_hosts_kubernetes_docker_internal", root: map[string]any{ "/etc/hosts": chezmoitest.JoinLines( `##`, `# Host Database`, `#`, `# localhost is used to configure the loopback interface`, `# when the system is booting. Do not change this entry.`, `##`, `127.0.0.1 localhost`, `255.255.255.255 broadcasthost`, `::1 localhost`, `# Added by Docker Desktop`, `# To allow the same kube context to work on the host and the container:`, `127.0.0.1 kubernetes.docker.internal`, `# End of section`, `127.0.1.1`, `127.0.1.1 host.example.com`, ), }, f: etcHostsFQDNHostname, expected: "host.example.com", }, { name: "etc_hostname", root: map[string]any{ "/etc/hostname": chezmoitest.JoinLines( `# comment`, ` hostname.example.com # comment`, ), }, f: etcHostnameFQDNHostname, expected: "hostname.example.com", }, { name: "etc_myname", root: map[string]any{ "/etc/myname": chezmoitest.JoinLines( "# comment", "", "hostname.example.com", ), }, f: etcMynameFQDNHostname, expected: "hostname.example.com", }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { fqdnHostname, err := tc.f(fileSystem) assert.NoError(t, err) assert.Equal(t, tc.expected, fqdnHostname) }) }) } } func TestUniqueAbbreviations(t *testing.T) { for _, tc := range []struct { values []string expected map[string]string }{ { values: nil, expected: map[string]string{}, }, { values: []string{ "yes", "no", "all", "quit", }, expected: map[string]string{ "y": "yes", "ye": "yes", "yes": "yes", "n": "no", "no": "no", "a": "all", "al": "all", "all": "all", "q": "quit", "qu": "quit", "qui": "quit", "quit": "quit", }, }, { values: []string{ "ale", "all", "abort", }, expected: map[string]string{ "ale": "ale", "all": "all", "ab": "abort", "abo": "abort", "abor": "abort", "abort": "abort", }, }, { values: []string{ "no", "now", "nope", }, expected: map[string]string{ "no": "no", "now": "now", "nop": "nope", "nope": "nope", }, }, } { t.Run(strings.Join(tc.values, "_"), func(t *testing.T) { actual := UniqueAbbreviations(tc.values) assert.Equal(t, tc.expected, actual) }) } } ================================================ FILE: internal/chezmoi/chezmoi_unix.go ================================================ //go:build unix package chezmoi import ( "io/fs" "os" "golang.org/x/sys/unix" ) const nativeLineEnding = "\n" func init() { Umask = fs.FileMode(unix.Umask(0)) unix.Umask(int(Umask)) } // findExecutableExtensions returns valid OS executable extensions, on unix it // can be anything. func findExecutableExtensions(path string) []string { return []string{path} } // IsExecutable returns if fileInfo is executable. func IsExecutable(fileInfo fs.FileInfo) bool { return fileInfo.Mode().Perm()&0o111 != 0 } // UserHomeDir on UNIX returns the value of [os.UserHomeDir]. func UserHomeDir() (string, error) { return os.UserHomeDir() } ================================================ FILE: internal/chezmoi/chezmoi_unix_test.go ================================================ //go:build unix package chezmoi import ( "testing" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestUmask(t *testing.T) { assert.Equal(t, chezmoitest.Umask, Umask) } ================================================ FILE: internal/chezmoi/chezmoi_windows.go ================================================ package chezmoi import ( "io/fs" "os" "path/filepath" "slices" "strings" ) const nativeLineEnding = "\r\n" var pathExts = strings.Split(os.Getenv("PATHEXT"), string(filepath.ListSeparator)) // findExecutableExtensions returns valid OS executable extensions for the // provided file if it does not already have an extension. The executable // extensions are derived from %PathExt%. func findExecutableExtensions(path string) []string { cmdExt := filepath.Ext(path) if cmdExt != "" { return []string{path} } result := make([]string, len(pathExts)) withoutSuffix := strings.TrimSuffix(path, cmdExt) for i, ext := range pathExts { result[i] = withoutSuffix + ext } return result } // IsExecutable checks if the file is a regular file and has an // extension listed in the PATHEXT environment variable as per // https://www.nextofwindows.com/what-is-pathext-environment-variable-in-windows. func IsExecutable(fileInfo fs.FileInfo) bool { if fileInfo.Mode().Perm()&0o111 != 0 { return true } if !fileInfo.Mode().IsRegular() { return false } ext := filepath.Ext(fileInfo.Name()) if ext == "" { return false } return slices.ContainsFunc(pathExts, func(pathExt string) bool { return strings.EqualFold(pathExt, ext) }) } // UserHomeDir on Windows returns the value of $HOME if it is set and either // Cygwin or msys2 is detected, otherwise it falls back to os.UserHomeDir. func UserHomeDir() (string, error) { if os.Getenv("CYGWIN") != "" || os.Getenv("MSYSTEM") != "" { if userHomeDir := os.Getenv("HOME"); userHomeDir != "" { return userHomeDir, nil } } return os.UserHomeDir() } // isSlash returns if c is a slash character. func isSlash(c byte) bool { return c == '\\' || c == '/' } ================================================ FILE: internal/chezmoi/compression.go ================================================ package chezmoi import ( "bytes" "compress/bzip2" "fmt" "io" "github.com/klauspost/compress/gzip" "github.com/klauspost/compress/zstd" "github.com/ulikunitz/xz" ) // A CompressionFormat is a compression format. type CompressionFormat string // Compression formats. const ( CompressionFormatNone CompressionFormat = "" CompressionFormatBzip2 CompressionFormat = "bzip2" CompressionFormatGzip CompressionFormat = "gzip" CompressionFormatXz CompressionFormat = "xz" CompressionFormatZstd CompressionFormat = "zstd" ) func decompress(compressionFormat CompressionFormat, data []byte) ([]byte, error) { switch compressionFormat { case CompressionFormatNone: return data, nil case CompressionFormatBzip2: return io.ReadAll(bzip2.NewReader(bytes.NewReader(data))) case CompressionFormatGzip: gzipReader, err := gzip.NewReader(bytes.NewReader(data)) if err != nil { return nil, err } return io.ReadAll(gzipReader) case CompressionFormatXz: xzReader, err := xz.NewReader(bytes.NewReader(data)) if err != nil { return nil, err } return io.ReadAll(xzReader) case CompressionFormatZstd: zstdReader, err := zstd.NewReader(bytes.NewReader(data)) if err != nil { return nil, err } return io.ReadAll(zstdReader) default: return nil, fmt.Errorf("%s: unknown compression format", compressionFormat) } } ================================================ FILE: internal/chezmoi/data.go ================================================ package chezmoi import ( "bytes" "errors" "fmt" "io/fs" "path/filepath" "strconv" "github.com/twpayne/go-vfs/v5" ) // Kernel returns the kernel information parsed from /proc/sys/kernel. func Kernel(fileSystem vfs.FS) (map[string]any, error) { const procSysKernel = "/proc/sys/kernel" switch fileInfo, err := fileSystem.Stat(procSysKernel); { case errors.Is(err, fs.ErrNotExist): return nil, nil case errors.Is(err, fs.ErrPermission): return nil, nil case err != nil: return nil, err case !fileInfo.Mode().IsDir(): return nil, nil } kernel := make(map[string]any) for _, filename := range []string{ "osrelease", "ostype", "version", } { switch data, err := fileSystem.ReadFile(filepath.Join(procSysKernel, filename)); { case err == nil: kernel[filename] = string(bytes.TrimSpace(data)) case errors.Is(err, fs.ErrNotExist): continue case errors.Is(err, fs.ErrPermission): continue default: return nil, err } } return kernel, nil } // OSRelease returns the operating system identification data as defined by the // os-release specification. func OSRelease(fileSystem vfs.FS) (map[string]any, error) { for _, filename := range []string{ "/etc/os-release", "/usr/lib/os-release", } { data, err := fileSystem.ReadFile(filename) if errors.Is(err, fs.ErrNotExist) { continue } else if err != nil { return nil, err } m, err := parseOSRelease(data) if err != nil { return nil, err } return m, nil } return nil, fs.ErrNotExist } // maybeUnquote removes quotation marks around s. func maybeUnquote(s string) string { // Try to unquote. if s, err := strconv.Unquote(s); err == nil { return s } // Otherwise return s, unchanged. return s } // parseOSRelease parses operating system identification data from r as defined // by the os-release specification. func parseOSRelease(data []byte) (map[string]any, error) { result := make(map[string]any) for line := range bytes.Lines(data) { token := bytes.TrimSpace(line) if len(token) == 0 || token[0] == '#' { continue } key, value, ok := bytes.Cut(token, []byte{'='}) if !ok { return nil, fmt.Errorf("%s: parse error", token) } result[string(key)] = maybeUnquote(string(value)) } return result, nil } ================================================ FILE: internal/chezmoi/data_test.go ================================================ package chezmoi import ( "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestKernel(t *testing.T) { for _, tc := range []struct { name string root any expectedKernel map[string]any }{ { name: "windows_services_for_linux", root: map[string]any{ "/proc/sys/kernel": map[string]any{ "osrelease": "4.19.81-microsoft-standard\n", "ostype": "Linux\n", "version": "#1 SMP Debian 5.2.9-2 (2019-08-21)\n", }, }, expectedKernel: map[string]any{ "osrelease": "4.19.81-microsoft-standard", "ostype": "Linux", "version": "#1 SMP Debian 5.2.9-2 (2019-08-21)", }, }, { name: "debian_version_only", root: map[string]any{ "/proc/sys/kernel": map[string]any{ "version": "#1 SMP Debian 5.2.9-2 (2019-08-21)\n", }, }, expectedKernel: map[string]any{ "version": "#1 SMP Debian 5.2.9-2 (2019-08-21)", }, }, { name: "proc_sys_kernel_missing", root: map[string]any{ "/proc/sys": &vfst.Dir{Perm: 0o755}, }, expectedKernel: nil, }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { actual, err := Kernel(fileSystem) assert.NoError(t, err) assert.Equal(t, tc.expectedKernel, actual) }) }) } } func TestOSRelease(t *testing.T) { for _, tc := range []struct { name string root map[string]any expected map[string]any }{ { name: "archlinux", root: map[string]any{ "/usr/lib/os-release": chezmoitest.JoinLines( `NAME="Arch Linux"`, `PRETTY_NAME="Arch Linux"`, `ID=arch`, `BUILD_ID=rolling`, `ANSI_COLOR="38;2;23;147;209"`, `HOME_URL="https://archlinux.org/"`, `DOCUMENTATION_URL="https://wiki.archlinux.org/"`, `SUPPORT_URL="https://bbs.archlinux.org/"`, `BUG_REPORT_URL="https://bugs.archlinux.org/"`, `LOGO=archlinux`, ), }, expected: map[string]any{ "NAME": "Arch Linux", "PRETTY_NAME": "Arch Linux", "ID": "arch", "BUILD_ID": "rolling", "ANSI_COLOR": "38;2;23;147;209", "HOME_URL": "https://archlinux.org/", "DOCUMENTATION_URL": "https://wiki.archlinux.org/", "SUPPORT_URL": "https://bbs.archlinux.org/", "BUG_REPORT_URL": "https://bugs.archlinux.org/", "LOGO": "archlinux", }, }, { name: "fedora", root: map[string]any{ "/etc/os-release": chezmoitest.JoinLines( `NAME=Fedora`, `VERSION="17 (Beefy Miracle)"`, `ID=fedora`, `VERSION_ID=17`, `PRETTY_NAME="Fedora 17 (Beefy Miracle)"`, `ANSI_COLOR="0;34"`, `CPE_NAME="cpe:/o:fedoraproject:fedora:17"`, `HOME_URL="https://fedoraproject.org/"`, `BUG_REPORT_URL="https://bugzilla.redhat.com/"`, ), }, expected: map[string]any{ "NAME": "Fedora", "VERSION": "17 (Beefy Miracle)", "ID": "fedora", "VERSION_ID": "17", "PRETTY_NAME": "Fedora 17 (Beefy Miracle)", "ANSI_COLOR": "0;34", "CPE_NAME": "cpe:/o:fedoraproject:fedora:17", "HOME_URL": "https://fedoraproject.org/", "BUG_REPORT_URL": "https://bugzilla.redhat.com/", }, }, { name: "ubuntu", root: map[string]any{ "/usr/lib/os-release": chezmoitest.JoinLines( `NAME="Ubuntu"`, `VERSION="18.04.1 LTS (Bionic Beaver)"`, `ID=ubuntu`, `ID_LIKE=debian`, `PRETTY_NAME="Ubuntu 18.04.1 LTS"`, `VERSION_ID="18.04"`, `HOME_URL="https://www.ubuntu.com/"`, `SUPPORT_URL="https://help.ubuntu.com/"`, `BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"`, `PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"`, `VERSION_CODENAME=bionic`, `UBUNTU_CODENAME=bionic`, ), }, expected: map[string]any{ "NAME": "Ubuntu", "VERSION": "18.04.1 LTS (Bionic Beaver)", "ID": "ubuntu", "ID_LIKE": "debian", "PRETTY_NAME": "Ubuntu 18.04.1 LTS", "VERSION_ID": "18.04", "HOME_URL": "https://www.ubuntu.com/", "SUPPORT_URL": "https://help.ubuntu.com/", "BUG_REPORT_URL": "https://bugs.launchpad.net/ubuntu/", "PRIVACY_POLICY_URL": "https://www.ubuntu.com/legal/terms-and-policies/privacy-policy", "VERSION_CODENAME": "bionic", "UBUNTU_CODENAME": "bionic", }, }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { actual, err := OSRelease(fileSystem) assert.NoError(t, err) assert.Equal(t, tc.expected, actual) }) }) } } func TestParseOSRelease(t *testing.T) { for _, tc := range []struct { name string s string expected map[string]any }{ { name: "fedora", s: chezmoitest.JoinLines( `NAME=Fedora`, `VERSION="17 (Beefy Miracle)"`, `ID=fedora`, `VERSION_ID=17`, `PRETTY_NAME="Fedora 17 (Beefy Miracle)"`, `ANSI_COLOR="0;34"`, `CPE_NAME="cpe:/o:fedoraproject:fedora:17"`, `HOME_URL="https://fedoraproject.org/"`, `BUG_REPORT_URL="https://bugzilla.redhat.com/"`, ), expected: map[string]any{ "NAME": "Fedora", "VERSION": "17 (Beefy Miracle)", "ID": "fedora", "VERSION_ID": "17", "PRETTY_NAME": "Fedora 17 (Beefy Miracle)", "ANSI_COLOR": "0;34", "CPE_NAME": "cpe:/o:fedoraproject:fedora:17", "HOME_URL": "https://fedoraproject.org/", "BUG_REPORT_URL": "https://bugzilla.redhat.com/", }, }, { name: "ubuntu_with_comments", s: chezmoitest.JoinLines( `NAME="Ubuntu"`, `VERSION="18.04.1 LTS (Bionic Beaver)"`, `ID=ubuntu`, `ID_LIKE=debian`, `PRETTY_NAME="Ubuntu 18.04.1 LTS"`, `VERSION_ID="18.04"`, `HOME_URL="https://www.ubuntu.com/"`, `SUPPORT_URL="https://help.ubuntu.com/"`, `BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"`, `PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"`, `# comment`, ``, ` # comment`, `VERSION_CODENAME=bionic`, `UBUNTU_CODENAME=bionic`, ), expected: map[string]any{ "NAME": "Ubuntu", "VERSION": "18.04.1 LTS (Bionic Beaver)", "ID": "ubuntu", "ID_LIKE": "debian", "PRETTY_NAME": "Ubuntu 18.04.1 LTS", "VERSION_ID": "18.04", "HOME_URL": "https://www.ubuntu.com/", "SUPPORT_URL": "https://help.ubuntu.com/", "BUG_REPORT_URL": "https://bugs.launchpad.net/ubuntu/", "PRIVACY_POLICY_URL": "https://www.ubuntu.com/legal/terms-and-policies/privacy-policy", "VERSION_CODENAME": "bionic", "UBUNTU_CODENAME": "bionic", }, }, } { t.Run(tc.name, func(t *testing.T) { actual, err := parseOSRelease([]byte(tc.s)) assert.NoError(t, err) assert.Equal(t, tc.expected, actual) }) } } ================================================ FILE: internal/chezmoi/debugencryption.go ================================================ package chezmoi import ( "log/slog" "chezmoi.io/chezmoi/internal/chezmoilog" ) // A DebugEncryption logs all calls to an Encryption. type DebugEncryption struct { logger *slog.Logger encryption Encryption } // NewDebugEncryption returns a new DebugEncryption that logs methods on // encryption to logger. func NewDebugEncryption(encryption Encryption, logger *slog.Logger) *DebugEncryption { return &DebugEncryption{ logger: logger, encryption: encryption, } } // Decrypt implements Encryption.Decrypt. func (e *DebugEncryption) Decrypt(ciphertext []byte) ([]byte, error) { plaintext, err := e.encryption.Decrypt(ciphertext) chezmoilog.InfoOrError(e.logger, "Decrypt", err, chezmoilog.FirstFewBytes("ciphertext", ciphertext), chezmoilog.FirstFewBytes("plaintext", plaintext), ) return plaintext, err } // DecryptToFile implements Encryption.DecryptToFile. func (e *DebugEncryption) DecryptToFile(plaintextAbsPath AbsPath, ciphertext []byte) error { err := e.encryption.DecryptToFile(plaintextAbsPath, ciphertext) chezmoilog.InfoOrError(e.logger, "DecryptToFile", err, chezmoilog.Stringer("plaintextAbsPath", plaintextAbsPath), chezmoilog.FirstFewBytes("ciphertext", ciphertext), ) return err } // Encrypt implements Encryption.Encrypt. func (e *DebugEncryption) Encrypt(plaintext []byte) ([]byte, error) { ciphertext, err := e.encryption.Encrypt(plaintext) chezmoilog.InfoOrError(e.logger, "Encrypt", err, chezmoilog.FirstFewBytes("plaintext", plaintext), chezmoilog.FirstFewBytes("ciphertext", ciphertext), ) return ciphertext, err } // EncryptFile implements Encryption.EncryptFile. func (e *DebugEncryption) EncryptFile(plaintextAbsPath AbsPath) ([]byte, error) { ciphertext, err := e.encryption.EncryptFile(plaintextAbsPath) chezmoilog.InfoOrError(e.logger, "EncryptFile", err, chezmoilog.Stringer("plaintextAbsPath", plaintextAbsPath), chezmoilog.FirstFewBytes("ciphertext", ciphertext), ) return ciphertext, err } // EncryptedSuffix implements Encryption.EncryptedSuffix. func (e *DebugEncryption) EncryptedSuffix() string { return e.encryption.EncryptedSuffix() } ================================================ FILE: internal/chezmoi/debugpersistentstate.go ================================================ package chezmoi import ( "log/slog" "chezmoi.io/chezmoi/internal/chezmoilog" ) // A DebugPersistentState logs calls to a PersistentState. type DebugPersistentState struct { logger *slog.Logger persistentState PersistentState } // NewDebugPersistentState returns a new debugPersistentState that logs methods // on persistentState to logger. func NewDebugPersistentState(persistentState PersistentState, logger *slog.Logger) *DebugPersistentState { return &DebugPersistentState{ logger: logger, persistentState: persistentState, } } // Close implements PersistentState.Close. func (s *DebugPersistentState) Close() error { err := s.persistentState.Close() chezmoilog.InfoOrError(s.logger, "Close", err) return err } // CopyTo implements PersistentState.CopyTo. func (s *DebugPersistentState) CopyTo(p PersistentState) error { err := s.persistentState.CopyTo(p) chezmoilog.InfoOrError(s.logger, "CopyTo", err) return err } // Data implements PersistentState.Data. func (s *DebugPersistentState) Data() (map[string]map[string]string, error) { data, err := s.persistentState.Data() chezmoilog.InfoOrError(s.logger, "Data", err, slog.Any("data", data), ) return data, err } // Delete implements PersistentState.Delete. func (s *DebugPersistentState) Delete(bucket, key []byte) error { err := s.persistentState.Delete(bucket, key) chezmoilog.InfoOrError(s.logger, "Delete", err, chezmoilog.Bytes("bucket", bucket), chezmoilog.Bytes("key", key), ) return err } // DeleteBucket implements PersistentState.DeleteBucket. func (s *DebugPersistentState) DeleteBucket(bucket []byte) error { err := s.persistentState.DeleteBucket(bucket) chezmoilog.InfoOrError(s.logger, "DeleteBucket", err, chezmoilog.Bytes("bucket", bucket), ) return err } // ForEach implements PersistentState.ForEach. func (s *DebugPersistentState) ForEach(bucket []byte, fn func(k, v []byte) error) error { err := s.persistentState.ForEach(bucket, func(k, v []byte) error { err := fn(k, v) chezmoilog.InfoOrError(s.logger, "ForEach", err, chezmoilog.Bytes("bucket", bucket), chezmoilog.Bytes("key", k), chezmoilog.Bytes("value", v), ) return err }) chezmoilog.InfoOrError(s.logger, "ForEach", err, chezmoilog.Bytes("bucket", bucket), ) return err } // Get implements PersistentState.Get. func (s *DebugPersistentState) Get(bucket, key []byte) ([]byte, error) { value, err := s.persistentState.Get(bucket, key) chezmoilog.InfoOrError(s.logger, "Get", err, chezmoilog.Bytes("bucket", bucket), chezmoilog.Bytes("key", key), chezmoilog.Bytes("value", value), ) return value, err } // Set implements PersistentState.Set. func (s *DebugPersistentState) Set(bucket, key, value []byte) error { err := s.persistentState.Set(bucket, key, value) chezmoilog.InfoOrError(s.logger, "Set", err, chezmoilog.Bytes("bucket", bucket), chezmoilog.Bytes("key", key), chezmoilog.Bytes("value", value), ) return err } ================================================ FILE: internal/chezmoi/debugsystem.go ================================================ package chezmoi import ( "io/fs" "log/slog" "os/exec" "time" "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoilog" ) // A DebugSystem logs all calls to a System. type DebugSystem struct { logger *slog.Logger system System } // NewDebugSystem returns a new DebugSystem that logs methods on system to // logger. func NewDebugSystem(system System, logger *slog.Logger) *DebugSystem { return &DebugSystem{ logger: logger, system: system, } } // Chtimes implements System.Chtimes. func (s *DebugSystem) Chtimes(name AbsPath, atime, mtime time.Time) error { err := s.system.Chtimes(name, atime, mtime) chezmoilog.InfoOrError(s.logger, "Chtimes", err, chezmoilog.Stringer("name", name), slog.Time("atime", atime), slog.Time("mtime", mtime), ) return err } // Chmod implements System.Chmod. func (s *DebugSystem) Chmod(name AbsPath, mode fs.FileMode) error { err := s.system.Chmod(name, mode) chezmoilog.InfoOrError(s.logger, "Chmod", err, chezmoilog.Stringer("name", name), slog.Int("mode", int(mode)), ) return err } // Glob implements System.Glob. func (s *DebugSystem) Glob(name string) ([]string, error) { matches, err := s.system.Glob(name) chezmoilog.InfoOrError(s.logger, "Glob", err, slog.String("name", name), slog.Any("matches", matches), ) return matches, err } // Link implements System.Link. func (s *DebugSystem) Link(oldPath, newPath AbsPath) error { err := s.system.Link(oldPath, newPath) chezmoilog.InfoOrError(s.logger, "Link", err, chezmoilog.Stringer("oldPath", oldPath), chezmoilog.Stringer("newPath", newPath), ) return err } // Lstat implements System.Lstat. func (s *DebugSystem) Lstat(name AbsPath) (fs.FileInfo, error) { fileInfo, err := s.system.Lstat(name) chezmoilog.InfoOrError(s.logger, "Lstat", err, chezmoilog.Stringer("name", name), ) return fileInfo, err } // Mkdir implements System.Mkdir. func (s *DebugSystem) Mkdir(name AbsPath, perm fs.FileMode) error { err := s.system.Mkdir(name, perm) chezmoilog.InfoOrError(s.logger, "Mkdir", err, chezmoilog.Stringer("name", name), slog.Int("perm", int(perm)), ) return err } // RawPath implements System.RawPath. func (s *DebugSystem) RawPath(path AbsPath) (AbsPath, error) { return s.system.RawPath(path) } // ReadDir implements System.ReadDir. func (s *DebugSystem) ReadDir(name AbsPath) ([]fs.DirEntry, error) { dirEntries, err := s.system.ReadDir(name) chezmoilog.InfoOrError(s.logger, "ReadDir", err, chezmoilog.Stringer("name", name), ) return dirEntries, err } // ReadFile implements System.ReadFile. func (s *DebugSystem) ReadFile(name AbsPath) ([]byte, error) { data, err := s.system.ReadFile(name) chezmoilog.InfoOrError(s.logger, "ReadFile", err, chezmoilog.Stringer("name", name), slog.Int("size", len(data)), chezmoilog.FirstFewBytes("data", data), ) return data, err } // Readlink implements System.Readlink. func (s *DebugSystem) Readlink(name AbsPath) (string, error) { linkname, err := s.system.Readlink(name) chezmoilog.InfoOrError(s.logger, "ReadLink", err, slog.String("linkname", linkname), ) return linkname, err } // Remove implements System.Remove. func (s *DebugSystem) Remove(name AbsPath) error { err := s.system.Remove(name) chezmoilog.InfoOrError(s.logger, "Remove", err, chezmoilog.Stringer("name", name), ) return err } // RemoveAll implements System.RemoveAll. func (s *DebugSystem) RemoveAll(name AbsPath) error { err := s.system.RemoveAll(name) chezmoilog.InfoOrError(s.logger, "RemoveAll", err, chezmoilog.Stringer("name", name), ) return err } // Rename implements System.Rename. func (s *DebugSystem) Rename(oldPath, newPath AbsPath) error { err := s.system.Rename(oldPath, newPath) chezmoilog.InfoOrError(s.logger, "RemoveAll", err, chezmoilog.Stringer("oldPath", oldPath), chezmoilog.Stringer("newPath", newPath), ) return err } // RunCmd implements System.RunCmd. func (s *DebugSystem) RunCmd(cmd *exec.Cmd) error { start := time.Now() err := s.system.RunCmd(cmd) attrs := []slog.Attr{ slog.Any("cmd", chezmoilog.OSExecCmdLogValuer{Cmd: cmd}), slog.Duration("duration", time.Since(start)), } attrs = chezmoilog.AppendExitErrorAttrs(attrs, err) chezmoilog.InfoOrError(s.logger, "RunCmd", err, attrs...) return err } // RunScript implements System.RunScript. func (s *DebugSystem) RunScript(scriptName RelPath, dir AbsPath, data []byte, options RunScriptOptions) error { err := s.system.RunScript(scriptName, dir, data, options) attrs := []slog.Attr{ chezmoilog.Stringer("scriptName", scriptName), chezmoilog.Stringer("dir", dir), chezmoilog.FirstFewBytes("data", data), slog.Any("interpreter", options.Interpreter), slog.String("condition", string(options.Condition)), } attrs = chezmoilog.AppendExitErrorAttrs(attrs, err) chezmoilog.InfoOrError(s.logger, "RunScript", err, attrs...) return err } // Stat implements System.Stat. func (s *DebugSystem) Stat(name AbsPath) (fs.FileInfo, error) { fileInfo, err := s.system.Stat(name) chezmoilog.InfoOrError(s.logger, "Stat", err, chezmoilog.Stringer("name", name), ) return fileInfo, err } // UnderlyingFS implements System.UnderlyingFS. func (s *DebugSystem) UnderlyingFS() vfs.FS { return s.system.UnderlyingFS() } // WriteFile implements System.WriteFile. func (s *DebugSystem) WriteFile(name AbsPath, data []byte, perm fs.FileMode) error { err := s.system.WriteFile(name, data, perm) chezmoilog.InfoOrError(s.logger, "WriteFile", err, chezmoilog.Stringer("name", name), slog.Int("size", len(data)), chezmoilog.FirstFewBytes("data", data), slog.Int("perm", int(perm)), ) return err } // WriteSymlink implements System.WriteSymlink. func (s *DebugSystem) WriteSymlink(oldName string, newName AbsPath) error { err := s.system.WriteSymlink(oldName, newName) chezmoilog.InfoOrError(s.logger, "WriteSymlink", err, slog.String("oldName", oldName), chezmoilog.Stringer("newName", newName), ) return err } ================================================ FILE: internal/chezmoi/debugsystem_test.go ================================================ package chezmoi var _ System = &DebugSystem{} ================================================ FILE: internal/chezmoi/diff.go ================================================ package chezmoi import ( "io/fs" "net/http" "strings" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/filemode" "github.com/go-git/go-git/v5/plumbing/format/diff" znkrdiff "znkr.io/diff" znkrtextdiff "znkr.io/diff/textdiff" ) var gitDiffOperation = [...]diff.Operation{ znkrdiff.Delete: diff.Delete, znkrdiff.Match: diff.Equal, znkrdiff.Insert: diff.Add, } // A gitDiffChunk implements the // github.com/go-git/go-git/v5/plumbing/format/diff.Chunk interface. type gitDiffChunk struct { content string operation diff.Operation } func (c *gitDiffChunk) Content() string { return c.content } func (c *gitDiffChunk) Type() diff.Operation { return c.operation } // A gitDiffFile implements the // github.com/go-git/go-git/v5/plumbing/format/diff.File interface. type gitDiffFile struct { hash plumbing.Hash fileMode filemode.FileMode relPath RelPath } func (f *gitDiffFile) Hash() plumbing.Hash { return f.hash } func (f *gitDiffFile) Mode() filemode.FileMode { return f.fileMode } func (f *gitDiffFile) Path() string { return f.relPath.String() } // A gitDiffFilePatch implements the // github.com/go-git/go-git/v5/plumbing/format/diff.FilePatch interface. type gitDiffFilePatch struct { binary bool from, to diff.File chunks []diff.Chunk } func (fp *gitDiffFilePatch) IsBinary() bool { return fp.binary } func (fp *gitDiffFilePatch) Files() (from, to diff.File) { return fp.from, fp.to } func (fp *gitDiffFilePatch) Chunks() []diff.Chunk { return fp.chunks } // A gitDiffPatch implements the // github.com/go-git/go-git/v5/plumbing/format/diff.Patch interface. type gitDiffPatch struct { filePatches []diff.FilePatch message string } func (p *gitDiffPatch) FilePatches() []diff.FilePatch { return p.filePatches } func (p *gitDiffPatch) Message() string { return p.message } // DiffPatch returns a github.com/go-git/go-git/plumbing/format/diff.Patch for // path from the given data and mode to the given data and mode. func DiffPatch(path RelPath, fromData []byte, fromMode fs.FileMode, toData []byte, toMode fs.FileMode) (diff.Patch, error) { isBinary := isBinary(fromData) || isBinary(toData) var from diff.File if fromData != nil || fromMode != 0 { fromFileMode, err := diffFileMode(fromMode) if err != nil { return nil, err } from = &gitDiffFile{ fileMode: fromFileMode, relPath: path, hash: plumbing.ComputeHash(plumbing.BlobObject, fromData), } } var to diff.File if toData != nil || toMode != 0 { toFileMode, err := diffFileMode(toMode) if err != nil { return nil, err } to = &gitDiffFile{ fileMode: toFileMode, relPath: path, hash: plumbing.ComputeHash(plumbing.BlobObject, toData), } } var chunks []diff.Chunk if !isBinary { chunks = diffChunks(string(fromData), string(toData)) } return &gitDiffPatch{ filePatches: []diff.FilePatch{ &gitDiffFilePatch{ binary: isBinary, from: from, to: to, chunks: chunks, }, }, }, nil } // diffChunks returns the // github.com/go-git/go-git/v5/plumbing/format/diff.Chunks required to transform // from into to. // // Nothing documents this, but go-git seems to depend on never encountering // consecutive edits of the same operation. This forces us to join the // individual edits into chunks. func diffChunks(from, to string) []diff.Chunk { edits := znkrtextdiff.Edits(from, to, znkrdiff.Minimal()) if len(edits) == 0 { return nil } var chunks []diff.Chunk lastOp := edits[0].Op var sb strings.Builder for _, edit := range edits { if edit.Op != lastOp { if content := sb.String(); content != "" { chunks = append(chunks, &gitDiffChunk{ content: content, operation: gitDiffOperation[lastOp], }) } lastOp = edit.Op sb.Reset() } sb.WriteString(edit.Line) } if content := sb.String(); content != "" { chunks = append(chunks, &gitDiffChunk{ content: content, operation: gitDiffOperation[lastOp], }) } return chunks } // diffFileMode converts an [io/fs.FileMode] into a // github.com/go-git/go-git/v5/plumbing/format/diff.FileMode. func diffFileMode(mode fs.FileMode) (filemode.FileMode, error) { fileMode, err := filemode.NewFromOSFileMode(mode) if err != nil { return 0, err } return (fileMode &^ filemode.FileMode(fs.ModePerm)) | filemode.FileMode(mode.Perm()), nil } // isBinary returns true if data contains binary (non-human-readable) data. func isBinary(data []byte) bool { return len(data) != 0 && !strings.HasPrefix(http.DetectContentType(data), "text/") } ================================================ FILE: internal/chezmoi/dryrunsystem.go ================================================ package chezmoi import ( "io/fs" "os/exec" "time" vfs "github.com/twpayne/go-vfs/v5" ) // DryRunSystem is an System that reads from, but does not write to, to // a wrapped System. type DryRunSystem struct { system System modified bool } // NewDryRunSystem returns a new DryRunSystem that wraps fs. func NewDryRunSystem(system System) *DryRunSystem { return &DryRunSystem{ system: system, } } // Chmod implements System.Chmod. func (s *DryRunSystem) Chmod(name AbsPath, mode fs.FileMode) error { s.setModified() return nil } // Chtimes implements System.Chtimes. func (s *DryRunSystem) Chtimes(name AbsPath, atime, mtime time.Time) error { s.setModified() return nil } // Glob implements System.Glob. func (s *DryRunSystem) Glob(pattern string) ([]string, error) { return s.system.Glob(pattern) } // Link implements System.Link. func (s *DryRunSystem) Link(oldName, newName AbsPath) error { s.setModified() return nil } // Lstat implements System.Lstat. func (s *DryRunSystem) Lstat(name AbsPath) (fs.FileInfo, error) { return s.system.Lstat(name) } // Mkdir implements System.Mkdir. func (s *DryRunSystem) Mkdir(name AbsPath, perm fs.FileMode) error { s.setModified() return nil } // IsModified returns true if a method that would have modified the wrapped // system has been called. func (s *DryRunSystem) IsModified() bool { return s.modified } // RawPath implements System.RawPath. func (s *DryRunSystem) RawPath(path AbsPath) (AbsPath, error) { return s.system.RawPath(path) } // ReadDir implements System.ReadDir. func (s *DryRunSystem) ReadDir(name AbsPath) ([]fs.DirEntry, error) { return s.system.ReadDir(name) } // ReadFile implements System.ReadFile. func (s *DryRunSystem) ReadFile(name AbsPath) ([]byte, error) { return s.system.ReadFile(name) } // Readlink implements System.Readlink. func (s *DryRunSystem) Readlink(name AbsPath) (string, error) { return s.system.Readlink(name) } // Remove implements System.Remove. func (s *DryRunSystem) Remove(AbsPath) error { s.setModified() return nil } // RemoveAll implements System.RemoveAll. func (s *DryRunSystem) RemoveAll(AbsPath) error { s.setModified() return nil } // Rename implements System.Rename. func (s *DryRunSystem) Rename(oldPath, newPath AbsPath) error { s.setModified() return nil } // RunCmd implements System.RunCmd. func (s *DryRunSystem) RunCmd(cmd *exec.Cmd) error { s.setModified() return nil } // RunScript implements System.RunScript. func (s *DryRunSystem) RunScript(scriptName RelPath, dir AbsPath, data []byte, options RunScriptOptions) error { s.setModified() return nil } // Stat implements System.Stat. func (s *DryRunSystem) Stat(name AbsPath) (fs.FileInfo, error) { return s.system.Stat(name) } // UnderlyingFS implements System.UnderlyingFS. func (s *DryRunSystem) UnderlyingFS() vfs.FS { return s.system.UnderlyingFS() } // WriteFile implements System.WriteFile. func (s *DryRunSystem) WriteFile(AbsPath, []byte, fs.FileMode) error { s.setModified() return nil } // WriteSymlink implements System.WriteSymlink. func (s *DryRunSystem) WriteSymlink(string, AbsPath) error { s.setModified() return nil } // setModified sets the modified flag to true. It is a separate function so that // it can act as a convenient breakpoint for detecting modifications to the // underlying system. func (s *DryRunSystem) setModified() { s.modified = true } ================================================ FILE: internal/chezmoi/dryrunsystem_test.go ================================================ package chezmoi var _ System = &DryRunSystem{} ================================================ FILE: internal/chezmoi/dumpsystem.go ================================================ package chezmoi import ( "io/fs" "os/exec" vfs "github.com/twpayne/go-vfs/v5" ) // A DumpSystemDataType is a data type in a dump system. type DumpSystemDataType string // Dump system data types. const ( DumpSystemDataTypeCommand DumpSystemDataType = "command" DumpSystemDataTypeDir DumpSystemDataType = "dir" DumpSystemDataTypeFile DumpSystemDataType = "file" DumpSystemDataTypeScript DumpSystemDataType = "script" DumpSystemDataTypeSymlink DumpSystemDataType = "symlink" ) // A DumpSystem is a System that writes to a data file. type DumpSystem struct { emptySystemMixin noUpdateSystemMixin data map[string]any } // A DumpSystemCommandData contains data about a command. type DumpSystemCommandData struct { Type DumpSystemDataType `json:"type" yaml:"type"` Path string `json:"path" yaml:"path"` Args []string `json:"args" yaml:"args"` } // A DumpSystemDirData contains data about a directory. type DumpSystemDirData struct { Type DumpSystemDataType `json:"type" yaml:"type"` Name AbsPath `json:"name" yaml:"name"` Perm fs.FileMode `json:"perm" yaml:"perm"` } // A DumpSystemFileData contains data about a file. type DumpSystemFileData struct { Type DumpSystemDataType `json:"type" yaml:"type"` Name AbsPath `json:"name" yaml:"name"` Contents string `json:"contents" yaml:"contents"` Perm fs.FileMode `json:"perm" yaml:"perm"` } // A DumpSystemScriptData contains data about a script. type DumpSystemScriptData struct { Type DumpSystemDataType `json:"type" yaml:"type"` Name AbsPath `json:"name" yaml:"name"` Contents string `json:"contents" yaml:"contents"` Condition string `json:"condition" yaml:"condition"` Interpreter *Interpreter `json:"interpreter,omitempty" yaml:"interpreter,omitempty"` } // A DumpSystemSymlinkData contains data about a symlink. type DumpSystemSymlinkData struct { Type DumpSystemDataType `json:"type" yaml:"type"` Name AbsPath `json:"name" yaml:"name"` Linkname string `json:"linkname" yaml:"linkname"` } // NewDumpSystem returns a new DumpSystem that accumulates data. func NewDumpSystem() *DumpSystem { return &DumpSystem{ data: make(map[string]any), } } // Data returns s's data. func (s *DumpSystem) Data() map[string]any { return s.data } // Mkdir implements System.Mkdir. func (s *DumpSystem) Mkdir(dirname AbsPath, perm fs.FileMode) error { return s.setData(dirname.String(), &DumpSystemDirData{ Type: DumpSystemDataTypeDir, Name: dirname, Perm: perm, }) } // RunCmd implements System.RunCmd. func (s *DumpSystem) RunCmd(cmd *exec.Cmd) error { if cmd.Dir == "" { return nil } return s.setData(cmd.Dir, &DumpSystemCommandData{ Type: DumpSystemDataTypeCommand, Path: cmd.Path, Args: cmd.Args, }) } // RunScript implements System.RunScript. func (s *DumpSystem) RunScript(scriptName RelPath, dir AbsPath, data []byte, options RunScriptOptions) error { scriptNameStr := scriptName.String() scriptData := &DumpSystemScriptData{ Type: DumpSystemDataTypeScript, Name: NewAbsPath(scriptNameStr), Contents: string(data), } if options.Condition != ScriptConditionNone { scriptData.Condition = string(options.Condition) } if !options.Interpreter.None() { scriptData.Interpreter = options.Interpreter } return s.setData(scriptNameStr, scriptData) } // UnderlyingFS implements System.UnderlyingFS. func (s *DumpSystem) UnderlyingFS() vfs.FS { return nil } // WriteFile implements System.WriteFile. func (s *DumpSystem) WriteFile(filename AbsPath, data []byte, perm fs.FileMode) error { return s.setData(filename.String(), &DumpSystemFileData{ Type: DumpSystemDataTypeFile, Name: filename, Contents: string(data), Perm: perm, }) } // WriteSymlink implements System.WriteSymlink. func (s *DumpSystem) WriteSymlink(oldName string, newName AbsPath) error { return s.setData(newName.String(), &DumpSystemSymlinkData{ Type: DumpSystemDataTypeSymlink, Name: newName, Linkname: oldName, }) } func (s *DumpSystem) setData(key string, value any) error { if _, ok := s.data[key]; ok { return fs.ErrExist } s.data[key] = value return nil } ================================================ FILE: internal/chezmoi/dumpsystem_test.go ================================================ package chezmoi import ( "io/fs" "testing" "github.com/alecthomas/assert/v2" "github.com/coreos/go-semver/semver" vfs "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoitest" ) var _ System = &DumpSystem{} func TestDumpSystem(t *testing.T) { chezmoitest.WithTestFS(t, map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiignore": "README.md\n", ".chezmoiremove": "*.txt\n", ".chezmoiversion": "1.2.3\n", ".chezmoitemplates": map[string]any{ "template": "# contents of .chezmoitemplates/template\n", }, "README.md": "", "dot_dir": map[string]any{ "file": "# contents of .dir/file\n", }, "run_script": "# contents of script\n", "symlink_symlink": ".dir/subdir/file\n", }, }, func(fileSystem vfs.FS) { ctx := t.Context() system := NewRealSystem(fileSystem) s := NewSourceState( WithBaseSystem(system), WithDestDir(NewAbsPath("/home/user")), WithSourceDir(NewAbsPath("/home/user/.local/share/chezmoi")), WithSystem(system), WithVersion(semver.Version{ Major: 1, Minor: 2, Patch: 3, }), ) assert.NoError(t, s.Read(ctx, nil)) requireEvaluateAll(t, s, system) dumpSystem := NewDumpSystem() persistentState := NewMockPersistentState() err := s.applyAll(dumpSystem, system, persistentState, EmptyAbsPath, ApplyOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }) assert.NoError(t, err) expectedData := map[string]any{ ".dir": &DumpSystemDirData{ Type: DumpSystemDataTypeDir, Name: NewAbsPath(".dir"), Perm: fs.ModePerm &^ chezmoitest.Umask, }, ".dir/file": &DumpSystemFileData{ Type: DumpSystemDataTypeFile, Name: NewAbsPath(".dir/file"), Contents: "# contents of .dir/file\n", Perm: 0o666 &^ chezmoitest.Umask, }, "script": &DumpSystemScriptData{ Type: DumpSystemDataTypeScript, Name: NewAbsPath("script"), Contents: "# contents of script\n", Condition: "always", }, "symlink": &DumpSystemSymlinkData{ Type: DumpSystemDataTypeSymlink, Name: NewAbsPath("symlink"), Linkname: ".dir/subdir/file", }, } assert.Equal(t, expectedData, dumpSystem.Data()) }) } ================================================ FILE: internal/chezmoi/duration.go ================================================ package chezmoi import "time" // A Duration is a [time.Duration] that implements [encoding.TextUnmarshaler]. type Duration time.Duration func (d *Duration) UnmarshalText(data []byte) error { timeDuration, err := time.ParseDuration(string(data)) if err != nil { return err } *d = Duration(timeDuration) return nil } ================================================ FILE: internal/chezmoi/encryption.go ================================================ package chezmoi // An Encryption encrypts and decrypts files and data. type Encryption interface { Decrypt(ciphertext []byte) ([]byte, error) DecryptToFile(plaintextAbsPath AbsPath, ciphertext []byte) error Encrypt(plaintext []byte) ([]byte, error) EncryptFile(plaintextAbsPath AbsPath) ([]byte, error) EncryptedSuffix() string } ================================================ FILE: internal/chezmoi/encryption_test.go ================================================ package chezmoi import ( "errors" "math/rand/v2" "os" "os/exec" "testing" "github.com/alecthomas/assert/v2" ) type xorEncryption struct { key byte } var _ Encryption = &xorEncryption{} func (e *xorEncryption) Decrypt(ciphertext []byte) ([]byte, error) { return e.xorWithKey(ciphertext), nil } func (e *xorEncryption) DecryptToFile(plaintextAbsPath AbsPath, ciphertext []byte) error { return os.WriteFile(plaintextAbsPath.String(), e.xorWithKey(ciphertext), 0o666) } func (e *xorEncryption) Encrypt(plaintext []byte) ([]byte, error) { return e.xorWithKey(plaintext), nil } func (e *xorEncryption) EncryptFile(plaintextAbsPath AbsPath) ([]byte, error) { plaintext, err := os.ReadFile(plaintextAbsPath.String()) if err != nil { return nil, err } return e.xorWithKey(plaintext), nil } func (e *xorEncryption) EncryptedSuffix() string { return ".xor" } func (e *xorEncryption) xorWithKey(input []byte) []byte { output := make([]byte, len(input)) for i, b := range input { output[i] = b ^ e.key } return output } func lookPathOrSkip(t *testing.T, file string) string { t.Helper() command, err := LookPath(file) if errors.Is(err, exec.ErrNotFound) { t.Skipf("%s not found in $PATH", file) } assert.NoError(t, err) return command } func testEncryptionDecryptToFile(t *testing.T, encryption Encryption) { t.Helper() t.Run("DecryptToFile", func(t *testing.T) { expectedPlaintext := []byte("plaintext\n") actualCiphertext, err := encryption.Encrypt(expectedPlaintext) assert.NoError(t, err) assert.NotZero(t, actualCiphertext) assert.NotEqual(t, expectedPlaintext, actualCiphertext) plaintextAbsPath := NewAbsPath(t.TempDir()).JoinString("plaintext") assert.NoError(t, encryption.DecryptToFile(plaintextAbsPath, actualCiphertext)) actualPlaintext, err := os.ReadFile(plaintextAbsPath.String()) assert.NoError(t, err) assert.NotZero(t, actualPlaintext) assert.Equal(t, expectedPlaintext, actualPlaintext) }) } func testEncryptionEncryptDecrypt(t *testing.T, encryption Encryption) { t.Helper() t.Run("EncryptDecrypt", func(t *testing.T) { expectedPlaintext := []byte("plaintext\n") actualCiphertext, err := encryption.Encrypt(expectedPlaintext) assert.NoError(t, err) assert.NotZero(t, actualCiphertext) assert.NotEqual(t, expectedPlaintext, actualCiphertext) actualPlaintext, err := encryption.Decrypt(actualCiphertext) assert.NoError(t, err) assert.NotZero(t, actualPlaintext) assert.Equal(t, expectedPlaintext, actualPlaintext) }) } func testEncryptionEncryptFile(t *testing.T, encryption Encryption) { t.Helper() t.Run("EncryptFile", func(t *testing.T) { expectedPlaintext := []byte("plaintext\n") plaintextAbsPath := NewAbsPath(t.TempDir()).JoinString("plaintext") assert.NoError(t, os.WriteFile(plaintextAbsPath.String(), expectedPlaintext, 0o666)) actualCiphertext, err := encryption.EncryptFile(plaintextAbsPath) assert.NoError(t, err) assert.NotZero(t, actualCiphertext) assert.NotEqual(t, expectedPlaintext, actualCiphertext) actualPlaintext, err := encryption.Decrypt(actualCiphertext) assert.NoError(t, err) assert.NotZero(t, actualPlaintext) assert.Equal(t, expectedPlaintext, actualPlaintext) }) } func TestXOREncryption(t *testing.T) { testEncryption(t, &xorEncryption{ key: byte(rand.N(255) + 1), }) } func testEncryption(t *testing.T, encryption Encryption) { t.Helper() testEncryptionDecryptToFile(t, encryption) testEncryptionEncryptDecrypt(t, encryption) testEncryptionEncryptFile(t, encryption) } ================================================ FILE: internal/chezmoi/entrystate.go ================================================ package chezmoi import ( "bytes" "io/fs" "log/slog" "runtime" "chezmoi.io/chezmoi/internal/chezmoilog" ) // An EntryStateType is an entry state type. type EntryStateType string // Entry state types. const ( EntryStateTypeDir EntryStateType = "dir" EntryStateTypeFile EntryStateType = "file" EntryStateTypeSymlink EntryStateType = "symlink" EntryStateTypeRemove EntryStateType = "remove" EntryStateTypeScript EntryStateType = "script" ) // An EntryState represents the state of an entry. A nil EntryState is // equivalent to EntryStateTypeAbsent. type EntryState struct { Type EntryStateType `json:"type" yaml:"type"` Mode fs.FileMode `json:"mode,omitempty" yaml:"mode,omitempty"` ContentsSHA256 HexBytes `json:"contentsSHA256,omitempty" yaml:"contentsSHA256,omitempty"` //nolint:tagliatelle contents []byte overwrite bool } // Contents returns s's contents, if available. func (s *EntryState) Contents() []byte { return s.contents } // Equal returns true if s is equal to other. func (s *EntryState) Equal(other *EntryState) bool { if s.Type != other.Type { return false } if runtime.GOOS != "windows" && s.Mode.Perm() != other.Mode.Perm() { return false } return bytes.Equal(s.ContentsSHA256, other.ContentsSHA256) } // Equivalent returns true if s is equivalent to other. func (s *EntryState) Equivalent(other *EntryState) bool { switch { case s == nil: return other == nil || other.Type == EntryStateTypeRemove case other == nil: return s.Type == EntryStateTypeRemove default: return s.Equal(other) } } // LogValue implements log/slog.LogValuer.LogValue. func (s *EntryState) LogValue() slog.Value { if s == nil { return slog.Value{} } attrs := []slog.Attr{ slog.String("Type", string(s.Type)), slog.Int("Mode", int(s.Mode)), chezmoilog.Stringer("ContentsSHA256", s.ContentsSHA256), } if len(s.contents) != 0 { attrs = append(attrs, chezmoilog.FirstFewBytes("contents", s.contents)) } if s.overwrite { attrs = append(attrs, slog.Bool("overwrite", s.overwrite)) } return slog.GroupValue(attrs...) } // Overwrite returns true if s should be overwritten by default. func (s *EntryState) Overwrite() bool { return s.overwrite } ================================================ FILE: internal/chezmoi/entrystate_test.go ================================================ package chezmoi import ( "fmt" "io/fs" "maps" "runtime" "slices" "testing" "github.com/alecthomas/assert/v2" "github.com/muesli/combinator" ) func TestEntryStateEquivalent(t *testing.T) { entryStates := map[string]*EntryState{ "dir1": { Type: EntryStateTypeDir, Mode: fs.ModeDir | fs.ModePerm, }, "dir1_copy": { Type: EntryStateTypeDir, Mode: fs.ModeDir | fs.ModePerm, }, "dir_private": { Type: EntryStateTypeDir, Mode: fs.ModeDir | 0o700, }, "file1": { Type: EntryStateTypeFile, Mode: 0o666, ContentsSHA256: []byte{1}, }, "file1_copy": { Type: EntryStateTypeFile, Mode: 0o666, ContentsSHA256: []byte{1}, }, "file2": { Type: EntryStateTypeFile, Mode: 0o666, ContentsSHA256: []byte{2}, }, "nil1": nil, "nil2": nil, "remove": { Type: EntryStateTypeRemove, }, "script": { Type: EntryStateTypeScript, ContentsSHA256: []byte{4}, }, "symlink": { Type: EntryStateTypeSymlink, ContentsSHA256: []byte{5}, }, "symlink_copy": { Type: EntryStateTypeSymlink, ContentsSHA256: []byte{5}, }, } expectedEquivalents := map[string]bool{ "dir_private_dir1_copy": runtime.GOOS == "windows", "dir_private_dir1": runtime.GOOS == "windows", "dir1_copy_dir_private": runtime.GOOS == "windows", "dir1_copy_dir1": true, "dir1_dir_private": runtime.GOOS == "windows", "dir1_dir1_copy": true, "file1_copy_file1": true, "file1_create": true, "file1_file1_copy": true, "nil1_remove": true, "nil2_remove": true, "remove_nil1": true, "remove_nil2": true, "symlink_copy_symlink": true, "symlink_symlink_copy": true, } entryStateKeys := slices.Sorted(maps.Keys(entryStates)) testData := struct { EntryState1Key []string EntryState2Key []string }{ EntryState1Key: entryStateKeys, EntryState2Key: entryStateKeys, } var testCases []struct { EntryState1Key string EntryState2Key string } assert.NoError(t, combinator.Generate(&testCases, testData)) for _, tc := range testCases { name := fmt.Sprintf("%s_%s", tc.EntryState1Key, tc.EntryState2Key) t.Run(name, func(t *testing.T) { entryState1 := entryStates[tc.EntryState1Key] entryState2 := entryStates[tc.EntryState2Key] expectedEquivalent := entryState1 == entryState2 || expectedEquivalents[name] assert.Equal(t, expectedEquivalent, entryState1.Equivalent(entryState2)) }) } } ================================================ FILE: internal/chezmoi/entrytypefilter.go ================================================ package chezmoi import "io/fs" // An EntryTypeFilter filters entries by type and source attributes. Any entry // in the include set is included, otherwise if the entry is in the exclude set // then it is excluded, otherwise it is included. type EntryTypeFilter struct { Include *EntryTypeSet Exclude *EntryTypeSet } // NewEntryTypeFilter returns a new EntryTypeFilter with the given entry type // bits. func NewEntryTypeFilter(includeEntryTypeBits, excludeEntryTypeBits EntryTypeBits) *EntryTypeFilter { return &EntryTypeFilter{ Include: NewEntryTypeSet(includeEntryTypeBits), Exclude: NewEntryTypeSet(excludeEntryTypeBits), } } // IncludeEntryTypeBits returns if entryTypeBits is included. func (f *EntryTypeFilter) IncludeEntryTypeBits(entryTypeBits EntryTypeBits) bool { return f.Include.ContainsEntryTypeBits(entryTypeBits) && !f.Exclude.ContainsEntryTypeBits(entryTypeBits) } // IncludeFileInfo returns if fileInfo is included. func (f *EntryTypeFilter) IncludeFileInfo(fileInfo fs.FileInfo) bool { return f.Include.ContainsFileInfo(fileInfo) && !f.Exclude.ContainsFileInfo(fileInfo) } // IncludeSourceStateEntry returns if sourceStateEntry is included. func (f *EntryTypeFilter) IncludeSourceStateEntry(sourceStateEntry SourceStateEntry) bool { return f.Include.ContainsSourceStateEntry(sourceStateEntry) && !f.Exclude.ContainsSourceStateEntry(sourceStateEntry) } // IncludeTargetStateEntry returns if targetStateEntry is included. func (f *EntryTypeFilter) IncludeTargetStateEntry(targetStateEntry TargetStateEntry) bool { return f.Include.ContainsTargetStateEntry(targetStateEntry) && !f.Exclude.ContainsTargetStateEntry(targetStateEntry) } ================================================ FILE: internal/chezmoi/entrytypeset.go ================================================ package chezmoi import ( "fmt" "io/fs" "maps" "reflect" "slices" "strings" "github.com/go-viper/mapstructure/v2" "github.com/spf13/cobra" ) // An EntryTypeSet is a set of entry types. It parses and prints as a // comma-separated list of strings, but is internally represented as a bitmask. // *EntryTypeSet implements the github.com/spf13/pflag.Value interface. type EntryTypeSet struct { bits EntryTypeBits } // An EntryTypeBits is a bitmask of entry types. type EntryTypeBits int // Entry type bits. const ( EntryTypeDirs EntryTypeBits = 1 << iota EntryTypeFiles EntryTypeRemove EntryTypeScripts EntryTypeSymlinks EntryTypeEncrypted EntryTypeExternals EntryTypeTemplates EntryTypeAlways // EntryTypesAll is all entry types. EntryTypesAll EntryTypeBits = EntryTypeDirs | EntryTypeFiles | EntryTypeRemove | EntryTypeScripts | EntryTypeSymlinks | EntryTypeEncrypted | EntryTypeExternals | EntryTypeTemplates | EntryTypeAlways // EntryTypesNone is no entry types. EntryTypesNone EntryTypeBits = 0 ) var ( // entryTypeBits is a map from human-readable strings to EntryTypeBits. entryTypeBits = map[string]EntryTypeBits{ "all": EntryTypesAll, "always": EntryTypeAlways, "dirs": EntryTypeDirs, "files": EntryTypeFiles, "remove": EntryTypeRemove, "scripts": EntryTypeScripts, "symlinks": EntryTypeSymlinks, "encrypted": EntryTypeEncrypted, "externals": EntryTypeExternals, "templates": EntryTypeTemplates, } entryTypeStrings = slices.Sorted(maps.Keys(entryTypeBits)) entryTypeCompletions = []string{ "all", "always", "dirs", "encrypted", "externals", "files", "noalways", "nodirs", "noencrypted", "noexternals", "nofiles", "none", "noremove", "noscripts", "nosymlinks", "notemplates", "remove", "scripts", "symlinks", "templates", } ) // NewEntryTypeSet returns a new IncludeSet. func NewEntryTypeSet(bits EntryTypeBits) *EntryTypeSet { return &EntryTypeSet{ bits: bits, } } // Bits returns s's bits. func (s *EntryTypeSet) Bits() EntryTypeBits { return s.bits } // ContainsEntryTypeBits returns if s includes b. func (s *EntryTypeSet) ContainsEntryTypeBits(b EntryTypeBits) bool { return s.bits&b != 0 } // ContainsFileInfo returns true if fileInfo is a member. func (s *EntryTypeSet) ContainsFileInfo(fileInfo fs.FileInfo) bool { switch { case fileInfo.IsDir(): return s.bits&EntryTypeDirs != 0 case fileInfo.Mode().IsRegular(): return s.bits&EntryTypeFiles != 0 case fileInfo.Mode().Type() == fs.ModeSymlink: return s.bits&EntryTypeSymlinks != 0 default: return false } } // ContainsSourceStateEntry returns true if sourceStateEntry is a member. func (s *EntryTypeSet) ContainsSourceStateEntry(sourceStateEntry SourceStateEntry) bool { isExternal := sourceStateEntry.Origin().IsExternal() switch sourceStateEntry := sourceStateEntry.(type) { case *SourceStateCommand: switch { case s.bits&EntryTypeExternals != 0 && isExternal: return true case s.bits&EntryTypeDirs != 0: return true default: return false } case *SourceStateDir, *SourceStateImplicitDir: switch { case s.bits&EntryTypeExternals != 0 && isExternal: return true case s.bits&EntryTypeDirs != 0: return true default: return false } case *SourceStateFile: switch sourceAttr := sourceStateEntry.Attr(); { case s.bits&EntryTypeExternals != 0 && isExternal: return true case s.bits&EntryTypeEncrypted != 0 && sourceAttr.Encrypted: return true case s.bits&EntryTypeTemplates != 0 && sourceAttr.Template: return true case s.bits&EntryTypeFiles != 0 && sourceAttr.Type == SourceFileTypeCreate: return true case s.bits&EntryTypeFiles != 0 && sourceAttr.Type == SourceFileTypeFile: return true case s.bits&EntryTypeFiles != 0 && sourceAttr.Type == SourceFileTypeModify: return true case s.bits&EntryTypeRemove != 0 && sourceAttr.Type == SourceFileTypeRemove: return true case s.bits&EntryTypeScripts != 0 && sourceAttr.Type == SourceFileTypeScript: return true case s.bits&EntryTypeSymlinks != 0 && sourceAttr.Type == SourceFileTypeSymlink: return true case s.bits&EntryTypeAlways != 0 && sourceAttr.Condition == ScriptConditionAlways: return true default: return false } case *SourceStateRemove: switch { case s.bits&EntryTypeExternals != 0 && isExternal: return true case s.bits&EntryTypeRemove != 0: return true default: return false } default: panic(fmt.Sprintf("%T: unsupported type", sourceStateEntry)) } } // ContainsTargetStateEntry returns true if targetStateEntry is a member. func (s *EntryTypeSet) ContainsTargetStateEntry(targetStateEntry TargetStateEntry) bool { sourceAttr := targetStateEntry.SourceAttr() switch targetStateEntry.(type) { case *TargetStateDir: switch { case s.bits&EntryTypeExternals != 0 && sourceAttr.External: return true case s.bits&EntryTypeDirs != 0: return true default: return false } case *TargetStateFile: switch { case s.bits&EntryTypeEncrypted != 0 && sourceAttr.Encrypted: return true case s.bits&EntryTypeExternals != 0 && sourceAttr.External: return true case s.bits&EntryTypeTemplates != 0 && sourceAttr.Template: return true case s.bits&EntryTypeFiles != 0: return true default: return false } case *TargetStateModifyDirWithCmd: switch { case s.bits&EntryTypeExternals != 0 && sourceAttr.External: return true case s.bits&EntryTypeDirs != 0: return true default: return false } case *TargetStateRemove: return s.bits&EntryTypeRemove != 0 case *TargetStateScript: switch { case s.bits&EntryTypeEncrypted != 0 && sourceAttr.Encrypted: return true case s.bits&EntryTypeTemplates != 0 && sourceAttr.Template: return true case s.bits&EntryTypeAlways != 0 && sourceAttr.Condition == ScriptConditionAlways: return true case s.bits&EntryTypeScripts != 0: return true default: return false } case *TargetStateSymlink: switch { case s.bits&EntryTypeEncrypted != 0 && sourceAttr.Encrypted: return true case s.bits&EntryTypeExternals != 0 && sourceAttr.External: return true case s.bits&EntryTypeTemplates != 0 && sourceAttr.Template: return true case s.bits&EntryTypeSymlinks != 0: return true default: return false } default: panic(fmt.Sprintf("%T: unsupported type", targetStateEntry)) } } // MarshalJSON implements encoding/json.Marshaler.MarshalJSON. func (s *EntryTypeSet) MarshalJSON() ([]byte, error) { switch s.bits { case EntryTypesAll: return []byte(`["all"]`), nil case EntryTypesNone: return []byte("[]"), nil default: var elements []string for _, key := range entryTypeStrings { if bit := entryTypeBits[key]; s.bits&bit == bit { elements = append(elements, `"`+key+`"`) } } return []byte("[" + strings.Join(elements, ",") + "]"), nil } } // MarshalYAML implements github.com/goccy/go-yaml.Marshaler. func (s *EntryTypeSet) MarshalYAML() (any, error) { if s.bits == EntryTypesAll { return []string{"all"}, nil } var result []string for _, key := range entryTypeStrings { if bit := entryTypeBits[key]; s.bits&bit == bit { result = append(result, key) } } return result, nil } // Set implements github.com/spf13/pflag.Value.Set. func (s *EntryTypeSet) Set(str string) error { if str == "none" { s.bits = EntryTypesNone return nil } return s.SetSlice(strings.Split(str, ",")) } // SetSlice sets s from a []string. func (s *EntryTypeSet) SetSlice(ss []string) error { bits := EntryTypesNone for i, element := range ss { if element == "" { continue } element, exclude := strings.CutPrefix(element, "no") bit, ok := entryTypeBits[element] if !ok { return fmt.Errorf("%s: unknown entry type", element) } if i == 0 && exclude { bits = EntryTypesAll } if exclude { bits &^= bit } else { bits |= bit } } s.bits = bits return nil } // String implements github.com/spf13/pflag.Value.String. func (s *EntryTypeSet) String() string { if s == nil { return "none" } switch s.bits { case EntryTypesAll: return "all" case EntryTypesNone: return "none" } var entryTypeStrs []string for _, entryTypeStr := range entryTypeStrings { bits := entryTypeBits[entryTypeStr] if s.bits&bits == bits { entryTypeStrs = append(entryTypeStrs, entryTypeStr) } } return strings.Join(entryTypeStrs, ",") } // Type implements github.com/spf13/pflag.Value.Type. func (s *EntryTypeSet) Type() string { return "types" } // StringSliceToEntryTypeSetHookFunc is a // github.com/go-viper/mapstructure/v2.DecodeHookFunc that parses an // EntryTypeSet from a []string. func StringSliceToEntryTypeSetHookFunc() mapstructure.DecodeHookFunc { return func(from, to reflect.Type, data any) (any, error) { if to != reflect.TypeFor[EntryTypeSet]() { return data, nil } elemsAny, ok := data.([]any) if !ok { return nil, fmt.Errorf("expected a []string, got a %T", data) } elemStrs := make([]string, len(elemsAny)) for i, elemAny := range elemsAny { elemStr, ok := elemAny.(string) if !ok { return nil, fmt.Errorf("expected a []string, got a %T element", elemAny) } elemStrs[i] = elemStr } s := NewEntryTypeSet(EntryTypesNone) if err := s.SetSlice(elemStrs); err != nil { return nil, err } return s, nil } } // EntryTypeSetFlagCompletionFunc completes EntryTypeSet flags. func EntryTypeSetFlagCompletionFunc(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var completions []string entryTypes := strings.Split(toComplete, ",") lastEntryType := entryTypes[len(entryTypes)-1] var prefix string if len(entryTypes) > 0 { prefix = toComplete[:len(toComplete)-len(lastEntryType)] } for _, completion := range entryTypeCompletions { if strings.HasPrefix(completion, lastEntryType) { completions = append(completions, prefix+completion) } } return completions, cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp } ================================================ FILE: internal/chezmoi/entrytypeset_test.go ================================================ package chezmoi import ( "testing" "github.com/alecthomas/assert/v2" "github.com/spf13/cobra" ) func TestIncludeMaskSet(t *testing.T) { for _, tc := range []struct { s string expected *EntryTypeSet expectedErr bool }{ { s: "", expected: NewEntryTypeSet(EntryTypesNone), }, { s: "none", expected: NewEntryTypeSet(EntryTypesNone), }, { s: "dirs,files", expected: NewEntryTypeSet(EntryTypeDirs | EntryTypeFiles), }, { s: "all", expected: NewEntryTypeSet(EntryTypesAll), }, { s: "all,noscripts", expected: NewEntryTypeSet(EntryTypesAll &^ EntryTypeScripts), }, { s: "noscripts", expected: NewEntryTypeSet(EntryTypesAll &^ EntryTypeScripts), }, { s: "noscripts,nosymlinks", expected: NewEntryTypeSet(EntryTypesAll &^ (EntryTypeScripts | EntryTypeSymlinks)), }, { s: "symlinks,,", expected: NewEntryTypeSet(EntryTypeSymlinks), }, { s: "devices", expectedErr: true, }, } { t.Run(tc.s, func(t *testing.T) { actual := NewEntryTypeSet(EntryTypesNone) err := actual.Set(tc.s) if tc.expectedErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, actual) } }) } } func TestIncludeMaskStringSlice(t *testing.T) { for _, tc := range []struct { bits EntryTypeBits expected string }{ { bits: EntryTypesAll, expected: "all", }, { bits: EntryTypeDirs, expected: "dirs", }, { bits: EntryTypeFiles, expected: "files", }, { bits: EntryTypeRemove, expected: "remove", }, { bits: EntryTypeScripts, expected: "scripts", }, { bits: EntryTypeSymlinks, expected: "symlinks", }, { bits: EntryTypeEncrypted, expected: "encrypted", }, { bits: EntryTypeExternals, expected: "externals", }, { bits: EntryTypesNone, expected: "none", }, { bits: EntryTypeDirs | EntryTypeFiles, expected: "dirs,files", }, } { t.Run(tc.expected, func(t *testing.T) { assert.Equal(t, tc.expected, NewEntryTypeSet(tc.bits).String()) }) } } func TestEntryTypeSetFlagCompletionFunc(t *testing.T) { for _, tc := range []struct { toComplete string expectedCompletions []string }{ { toComplete: "a", expectedCompletions: []string{ "all", "always", }, }, { toComplete: "e", expectedCompletions: []string{ "encrypted", "externals", }, }, { toComplete: "t", expectedCompletions: []string{ "templates", }, }, { toComplete: "all,nos", expectedCompletions: []string{ "all,noscripts", "all,nosymlinks", }, }, } { t.Run(tc.toComplete, func(t *testing.T) { completions, shellCompDirective := EntryTypeSetFlagCompletionFunc(nil, nil, tc.toComplete) assert.Equal(t, tc.expectedCompletions, completions) expectedShellCompDirective := cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp assert.Equal(t, expectedShellCompDirective, shellCompDirective) }) } } ================================================ FILE: internal/chezmoi/erroronwritesystem.go ================================================ package chezmoi import ( "io/fs" "os/exec" "time" vfs "github.com/twpayne/go-vfs/v5" ) // An ErrorOnWriteSystem is an System that passes reads to the wrapped System // and returns an error if it is written to. type ErrorOnWriteSystem struct { system System err error } // NewErrorOnWriteSystem returns a new ErrorOnWriteSystem that wraps fs and // returns err on any write operation. func NewErrorOnWriteSystem(system System, err error) *ErrorOnWriteSystem { return &ErrorOnWriteSystem{ system: system, err: err, } } // Chmod implements System.Chmod. func (s *ErrorOnWriteSystem) Chmod(name AbsPath, mode fs.FileMode) error { return s.err } // Chtimes implements System.Chtimes. func (s *ErrorOnWriteSystem) Chtimes(name AbsPath, atime, mtime time.Time) error { return s.err } // Glob implements System.Glob. func (s *ErrorOnWriteSystem) Glob(pattern string) ([]string, error) { return s.system.Glob(pattern) } // Link implements System.Link. func (s *ErrorOnWriteSystem) Link(oldName, newName AbsPath) error { return s.err } // Lstat implements System.Lstat. func (s *ErrorOnWriteSystem) Lstat(name AbsPath) (fs.FileInfo, error) { return s.system.Lstat(name) } // Mkdir implements System.Mkdir. func (s *ErrorOnWriteSystem) Mkdir(name AbsPath, perm fs.FileMode) error { return s.err } // RawPath implements System.RawPath. func (s *ErrorOnWriteSystem) RawPath(path AbsPath) (AbsPath, error) { return s.system.RawPath(path) } // ReadDir implements System.ReadDir. func (s *ErrorOnWriteSystem) ReadDir(name AbsPath) ([]fs.DirEntry, error) { return s.system.ReadDir(name) } // ReadFile implements System.ReadFile. func (s *ErrorOnWriteSystem) ReadFile(name AbsPath) ([]byte, error) { return s.system.ReadFile(name) } // Readlink implements System.Readlink. func (s *ErrorOnWriteSystem) Readlink(name AbsPath) (string, error) { return s.system.Readlink(name) } // Remove implements System.Remove. func (s *ErrorOnWriteSystem) Remove(AbsPath) error { return s.err } // RemoveAll implements System.RemoveAll. func (s *ErrorOnWriteSystem) RemoveAll(AbsPath) error { return s.err } // Rename implements System.Rename. func (s *ErrorOnWriteSystem) Rename(oldPath, newPath AbsPath) error { return s.err } // RunCmd implements System.RunCmd. func (s *ErrorOnWriteSystem) RunCmd(cmd *exec.Cmd) error { return s.err } // RunScript implements System.RunScript. func (s *ErrorOnWriteSystem) RunScript(scriptName RelPath, dir AbsPath, data []byte, options RunScriptOptions) error { return s.err } // Stat implements System.Stat. func (s *ErrorOnWriteSystem) Stat(name AbsPath) (fs.FileInfo, error) { return s.system.Stat(name) } // UnderlyingFS implements System.UnderlyingFS. func (s *ErrorOnWriteSystem) UnderlyingFS() vfs.FS { return s.system.UnderlyingFS() } // WriteFile implements System.WriteFile. func (s *ErrorOnWriteSystem) WriteFile(AbsPath, []byte, fs.FileMode) error { return s.err } // WriteSymlink implements System.WriteSymlink. func (s *ErrorOnWriteSystem) WriteSymlink(string, AbsPath) error { return s.err } ================================================ FILE: internal/chezmoi/erroronwritesystem_test.go ================================================ package chezmoi var _ System = &ErrorOnWriteSystem{} ================================================ FILE: internal/chezmoi/errors.go ================================================ package chezmoi import ( "fmt" "io/fs" "strings" "github.com/coreos/go-semver/semver" ) // An ExitCodeError indicates the main program should exit with the given // code. type ExitCodeError int func (e ExitCodeError) Error() string { return fmt.Sprintf("exit status %d", int(e)) } // A TooOldError is returned when the source state requires a newer version of // chezmoi. type TooOldError struct { Have semver.Version Need semver.Version } func (e *TooOldError) Error() string { format := "source state requires chezmoi version %s or later, chezmoi is version %s" return fmt.Sprintf(format, e.Need, e.Have) } type InconsistentStateError struct { targetRelPath RelPath origins []string } func (e *InconsistentStateError) Error() string { format := "%s: inconsistent state (%s)" return fmt.Sprintf(format, e.targetRelPath, strings.Join(e.origins, ", ")) } type NotInAbsDirError struct { pathAbsPath AbsPath dirAbsPath AbsPath } func (e *NotInAbsDirError) Error() string { return fmt.Sprintf("%s: not in %s", e.pathAbsPath, e.dirAbsPath) } type NotInRelDirError struct { pathRelPath RelPath dirRelPath RelPath } func (e *NotInRelDirError) Error() string { return fmt.Sprintf("%s: not in %s", e.pathRelPath, e.dirRelPath) } type UnsupportedFileTypeError struct { absPath AbsPath mode fs.FileMode } func (e *UnsupportedFileTypeError) Error() string { return fmt.Sprintf("%s: unsupported file type %s", e.absPath, modeTypeName(e.mode)) } ================================================ FILE: internal/chezmoi/externaldiffsystem.go ================================================ package chezmoi import ( "bytes" "errors" "io/fs" "log/slog" "os" "os/exec" "strconv" "time" vfs "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoilog" ) // An ExternalDiffSystem is a DiffSystem that uses an external diff tool. type ExternalDiffSystem struct { system System command string args []string destDirAbsPath AbsPath tempDirAbsPath AbsPath filter *EntryTypeFilter pagerCmdFunc func() (*exec.Cmd, error) reverse bool scriptContents bool templateOptions TemplateOptions textConvFunc TextConvFunc } // ExternalDiffSystemOptions are options for NewExternalDiffSystem. type ExternalDiffSystemOptions struct { Filter *EntryTypeFilter Reverse bool ScriptContents bool TextConvFunc TextConvFunc TemplateOptions TemplateOptions } // NewExternalDiffSystem creates a new ExternalDiffSystem. func NewExternalDiffSystem( system System, command string, args []string, destDirAbsPath AbsPath, pagerCmdFunc func() (*exec.Cmd, error), options *ExternalDiffSystemOptions, ) *ExternalDiffSystem { return &ExternalDiffSystem{ system: system, command: command, args: args, destDirAbsPath: destDirAbsPath, filter: options.Filter, pagerCmdFunc: pagerCmdFunc, reverse: options.Reverse, scriptContents: options.ScriptContents, templateOptions: options.TemplateOptions, textConvFunc: options.TextConvFunc, } } // Close frees all resources held by s. func (s *ExternalDiffSystem) Close() error { if !s.tempDirAbsPath.IsEmpty() { if err := os.RemoveAll(s.tempDirAbsPath.String()); err != nil && !errors.Is(err, fs.ErrNotExist) { return err } s.tempDirAbsPath = EmptyAbsPath } return nil } // Chmod implements System.Chmod. func (s *ExternalDiffSystem) Chmod(name AbsPath, mode fs.FileMode) error { // FIXME generate suitable inputs for s.command return s.system.Chmod(name, mode) } // Chtimes implements System.Chtimes. func (s *ExternalDiffSystem) Chtimes(name AbsPath, atime, mtime time.Time) error { return s.system.Chtimes(name, atime, mtime) } // Glob implements System.Glob. func (s *ExternalDiffSystem) Glob(pattern string) ([]string, error) { return s.system.Glob(pattern) } // Link implements System.Link. func (s *ExternalDiffSystem) Link(oldName, newName AbsPath) error { // FIXME generate suitable inputs for s.command return s.system.Link(oldName, newName) } // Lstat implements System.Lstat. func (s *ExternalDiffSystem) Lstat(name AbsPath) (fs.FileInfo, error) { return s.system.Lstat(name) } // Mkdir implements System.Mkdir. func (s *ExternalDiffSystem) Mkdir(name AbsPath, perm fs.FileMode) error { if s.filter.IncludeEntryTypeBits(EntryTypeDirs) { targetRelPath, err := name.TrimDirPrefix(s.destDirAbsPath) if err != nil { return err } tempDirAbsPath, err := s.tempDir() if err != nil { return err } targetAbsPath := tempDirAbsPath.Join(targetRelPath) if err := os.MkdirAll(targetAbsPath.String(), perm); err != nil { return err } if err := s.RunDiffCommand(devNullAbsPath, targetAbsPath); err != nil { return err } } return s.system.Mkdir(name, perm) } // RawPath implements System.RawPath. func (s *ExternalDiffSystem) RawPath(path AbsPath) (AbsPath, error) { return s.system.RawPath(path) } // ReadDir implements System.ReadDir. func (s *ExternalDiffSystem) ReadDir(name AbsPath) ([]fs.DirEntry, error) { return s.system.ReadDir(name) } // ReadFile implements System.ReadFile. func (s *ExternalDiffSystem) ReadFile(name AbsPath) ([]byte, error) { return s.system.ReadFile(name) } // Readlink implements System.Readlink. func (s *ExternalDiffSystem) Readlink(name AbsPath) (string, error) { return s.system.Readlink(name) } // Remove implements System.Remove. func (s *ExternalDiffSystem) Remove(name AbsPath) error { if s.filter.IncludeEntryTypeBits(EntryTypeRemove) { switch fileInfo, err := s.system.Lstat(name); { case errors.Is(err, fs.ErrNotExist): // Do nothing. case err != nil: return err case s.filter.IncludeFileInfo(fileInfo): if err := s.RunDiffCommand(name, devNullAbsPath); err != nil { return err } } } return s.system.Remove(name) } // RemoveAll implements System.RemoveAll. func (s *ExternalDiffSystem) RemoveAll(name AbsPath) error { if s.filter.IncludeEntryTypeBits(EntryTypeRemove) { switch fileInfo, err := s.system.Lstat(name); { case errors.Is(err, fs.ErrNotExist): // Do nothing. case err != nil: return err case s.filter.IncludeFileInfo(fileInfo): if err := s.RunDiffCommand(name, devNullAbsPath); err != nil { return err } } } return s.system.RemoveAll(name) } // Rename implements System.Rename. func (s *ExternalDiffSystem) Rename(oldPath, newPath AbsPath) error { // FIXME generate suitable inputs for s.command return s.system.Rename(oldPath, newPath) } // RunCmd implements System.RunCmd. func (s *ExternalDiffSystem) RunCmd(cmd *exec.Cmd) error { return s.system.RunCmd(cmd) } // RunDiffCommand runs the external diff command. func (s *ExternalDiffSystem) RunDiffCommand(destAbsPath, targetAbsPath AbsPath) error { templateData := struct { Destination string Target string }{ Destination: destAbsPath.String(), Target: targetAbsPath.String(), } if s.reverse { templateData.Destination, templateData.Target = templateData.Target, templateData.Destination } args := make([]string, 0, len(s.args)) // Work around a regression introduced in 2.1.5 // (https://github.com/twpayne/chezmoi/pull/1328) in a user-friendly way. // // Prior to #1328, the diff.args config option was prepended to the default // order of files to the diff command. Post #1328, the diff.args config // option replaced all arguments to the diff command. // // Work around this by looking for any templates in diff.args. An arg is // considered a template if, after execution as a template, it is not equal // to the original arg. anyTemplateArgs := false for i, arg := range s.args { tmpl, err := ParseTemplate("diff.args["+strconv.Itoa(i)+"]", []byte(arg), s.templateOptions) if err != nil { return err } newArg, err := tmpl.ExecuteString(templateData) if err != nil { return err } args = append(args, newArg) // Detect template arguments. if arg != newArg { anyTemplateArgs = true } } // If there are no template arguments, then append the destination and // target paths as prior to #1328. if !anyTemplateArgs { args = append(args, templateData.Destination, templateData.Target) } diffCmd := exec.Command(s.command, args...) diffCmd.Stdin = os.Stdin diffCmd.Stderr = os.Stderr var diffCmdErr error switch diffPagerCmd, err := s.pagerCmdFunc(); { case err != nil: return err case diffPagerCmd == nil: diffCmd.Stdout = os.Stdout diffCmdErr = chezmoilog.LogCmdRun(slog.Default(), diffCmd) default: reader, writer, err := os.Pipe() if err != nil { return err } defer writer.Close() diffPagerCmd.Stdin = reader if err := chezmoilog.LogCmdStart(slog.Default(), diffPagerCmd); err != nil { return err } diffCmd.Stdout = writer diffCmdErr = chezmoilog.LogCmdRun(slog.Default(), diffCmd) if err := writer.Close(); err != nil { return err } if err := diffPagerCmd.Wait(); err != nil { return err } } // Swallow exit status 1 errors if the entries differ and there are actual // differences between the entries as diff commands traditionally exit with // code 1 in this case. if exitError := (&exec.ExitError{}); errors.As(diffCmdErr, &exitError) && exitError.ExitCode() == 1 { switch entriesDiffer, err := s.entriesDiffer(destAbsPath, targetAbsPath); { case err != nil: return err case entriesDiffer: return nil } } return diffCmdErr } // RunScript implements System.RunScript. func (s *ExternalDiffSystem) RunScript(scriptName RelPath, dir AbsPath, data []byte, options RunScriptOptions) error { bits := EntryTypeScripts if options.Condition == ScriptConditionAlways { bits |= EntryTypeAlways } if s.filter.IncludeEntryTypeBits(bits) { tempDirAbsPath, err := s.tempDir() if err != nil { return err } targetAbsPath := tempDirAbsPath.Join(scriptName) if err := os.MkdirAll(targetAbsPath.Dir().String(), 0o700); err != nil { return err } toData := data if !s.scriptContents { toData = nil } if err := os.WriteFile(targetAbsPath.String(), toData, 0o700); err != nil { return err } if err := s.RunDiffCommand(devNullAbsPath, targetAbsPath); err != nil { return err } } return s.system.RunScript(scriptName, dir, data, options) } // Stat implements System.Stat. func (s *ExternalDiffSystem) Stat(name AbsPath) (fs.FileInfo, error) { return s.system.Stat(name) } // UnderlyingFS implements System.UnderlyingFS. func (s *ExternalDiffSystem) UnderlyingFS() vfs.FS { return s.system.UnderlyingFS() } // WriteFile implements System.WriteFile. func (s *ExternalDiffSystem) WriteFile(filename AbsPath, data []byte, perm fs.FileMode) error { if s.filter.IncludeEntryTypeBits(EntryTypeFiles) { targetRelPath, err := filename.TrimDirPrefix(s.destDirAbsPath) if err != nil { return err } tempDirAbsPath, err := s.tempDir() if err != nil { return err } // If filename does not exist, replace it with /dev/null to avoid // passing the name of a non-existent file to the external diff command. // Otherwise, if the file exists and a textconv filter is configured, // run the filter and update fromAbsPath to point to the converted data. fromAbsPath := filename switch fileInfo, err := os.Lstat(fromAbsPath.String()); { case errors.Is(err, fs.ErrNotExist): fromAbsPath = devNullAbsPath case err != nil: return err case s.textConvFunc != nil: // Maybe convert the from data with textconv. fromData, err := os.ReadFile(fromAbsPath.String()) if err != nil { return err } switch convertedFromData, converted, err := s.textConvFunc(fromAbsPath.String(), fromData); { case err != nil: return err case converted: tempFromAbsPath := tempDirAbsPath.Join(NewRelPath("a"), targetRelPath) if err := os.MkdirAll(tempFromAbsPath.Dir().String(), 0o700); err != nil { return err } if err := os.WriteFile(tempFromAbsPath.String(), convertedFromData, fileInfo.Mode().Perm()); err != nil { return err } fromAbsPath = tempFromAbsPath } } // Write the target contents to a file in a temporary directory. toAbsPath := tempDirAbsPath.Join(targetRelPath) toData := data if s.textConvFunc != nil { // Maybe convert the to data with textconv. switch convertedToData, converted, err := s.textConvFunc(filename.String(), toData); { case err != nil: return err case converted: toAbsPath = tempDirAbsPath.Join(NewRelPath("b"), targetRelPath) toData = convertedToData } } if err := os.MkdirAll(toAbsPath.Dir().String(), 0o700); err != nil { return err } if err := os.WriteFile(toAbsPath.String(), toData, perm); err != nil { return err } // Run the external diff command. if err := s.RunDiffCommand(fromAbsPath, toAbsPath); err != nil { return err } } return s.system.WriteFile(filename, data, perm) } // WriteSymlink implements System.WriteSymlink. func (s *ExternalDiffSystem) WriteSymlink(oldName string, newName AbsPath) error { // FIXME generate suitable inputs for s.command return s.system.WriteSymlink(oldName, newName) } // tempDir creates a temporary directory for s if it does not already exist and // returns its path. func (s *ExternalDiffSystem) tempDir() (AbsPath, error) { if s.tempDirAbsPath.IsEmpty() { tempDir, err := os.MkdirTemp("", "chezmoi-diff") if err != nil { return EmptyAbsPath, err } s.tempDirAbsPath = NewAbsPath(tempDir) } return s.tempDirAbsPath, nil } // entriesDiffer returns whether the two given entries differ. // // This function employs negative logic, i.e. that the default is that the // function returns that the entries DO differ unless it can prove otherwise. func (s *ExternalDiffSystem) entriesDiffer(absPath1, absPath2 AbsPath) (bool, error) { fileInfo1, err1 := s.Lstat(absPath1) fileInfo2, err2 := s.Lstat(absPath2) switch { case errors.Is(err1, fs.ErrNotExist) && errors.Is(err2, fs.ErrNotExist): // If neither entry exists, then they do not differ. return false, nil case err1 != nil || err2 != nil: // If lstating either entry returned an error, then combine both errors. return true, errors.Join(err1, err2) case fileInfo1.Mode() != fileInfo2.Mode(): // If the modes are not equal, then the entries are not equal. This // covers the case where the entries are of different types, or that // their permissions differ. return true, nil case fileInfo1.Mode()&fs.ModeType == 0: // If both entries are files, then their contents must be equal. contents1, err1 := s.ReadFile(absPath1) if err1 != nil { return true, err1 } contents2, err2 := s.ReadFile(absPath2) if err2 != nil { return true, err2 } return !bytes.Equal(contents1, contents2), nil case fileInfo1.Mode()&fs.ModeType == fs.ModeSymlink: // If both entries are symlinks, then their targets must be equal. linkname1, err1 := s.Readlink(absPath1) if err1 != nil { return true, err1 } linkname2, err2 := s.Readlink(absPath2) if err2 != nil { return true, err2 } return linkname1 != linkname2, nil default: // Otherwise return that the entries DO differ. This code should not be // reached. This does not cover the case where both entries are special // files (e.g. device nodes with the same device numbers or UNIX domain // sockets) but chezmoi does not manage these types of entries. return true, nil } } ================================================ FILE: internal/chezmoi/externaldiffsystem_test.go ================================================ package chezmoi var _ System = &ExternalDiffSystem{} ================================================ FILE: internal/chezmoi/findexecutable.go ================================================ package chezmoi import ( "os" "path/filepath" "strings" "sync" ) var ( foundExecutableCacheMutex sync.Mutex foundExecutableCache = make(map[string]string) ) // FindExecutable is like LookPath except that: // // - You can specify the needle as `string`, `[]string`, or `[]any` // (that converts to `[]string`). // - You specify the haystack instead of relying on `$PATH`/`%PATH%`. // // This makes it useful for the resulting path of shell configurations // managed by chezmoi. func FindExecutable(files, paths []string) (string, error) { foundExecutableCacheMutex.Lock() defer foundExecutableCacheMutex.Unlock() key := strings.Join(files, "\x00") + "\x01" + strings.Join(paths, "\x00") if path, ok := foundExecutableCache[key]; ok { return path, nil } var candidates []string for _, file := range files { candidates = append(candidates, findExecutableExtensions(file)...) } // based on /usr/lib/go-1.20/src/os/exec/lp_unix.go:52 for _, candidatePath := range paths { if candidatePath == "" { continue } for _, candidate := range candidates { path := filepath.Join(candidatePath, candidate) info, err := os.Stat(path) if err != nil { continue } // isExecutable doesn't care if it's a directory if info.Mode().IsDir() { continue } if IsExecutable(info) { foundExecutableCache[key] = path return path, nil } } } return "", nil } ================================================ FILE: internal/chezmoi/findexecutable_darwin_test.go ================================================ package chezmoi import ( "fmt" "testing" "github.com/alecthomas/assert/v2" ) func TestFindExecutable(t *testing.T) { tests := []struct { files []string paths []string expected string }{ { files: []string{"sh"}, paths: []string{"/usr/bin", "/bin"}, expected: "/bin/sh", }, { files: []string{"sh"}, paths: []string{"/bin", "/usr/bin"}, expected: "/bin/sh", }, { files: []string{"chezmoish"}, paths: []string{"/bin", "/usr/bin"}, expected: "", }, { files: []string{"chezmoish", "sh"}, paths: []string{"/usr/bin", "/bin"}, expected: "/bin/sh", }, { files: []string{"chezmoish", "sh"}, paths: []string{"/bin", "/usr/bin"}, expected: "/bin/sh", }, { files: []string{"chezmoish", "chezvoush"}, paths: []string{"/bin", "/usr/bin"}, expected: "", }, } for _, test := range tests { format := "FindExecutable %#v in %#v as %#v" name := fmt.Sprintf(format, test.files, test.paths, test.expected) t.Run(name, func(t *testing.T) { actual, err := FindExecutable(test.files, test.paths) assert.NoError(t, err) assert.Equal(t, test.expected, actual) }) } } ================================================ FILE: internal/chezmoi/findexecutable_unix_test.go ================================================ //go:build !windows && !darwin package chezmoi import ( "fmt" "testing" "github.com/alecthomas/assert/v2" ) func TestFindExecutable(t *testing.T) { tests := []struct { files []string paths []string expected string }{ { files: []string{"yes"}, paths: []string{"/usr/bin", "/bin"}, expected: "/usr/bin/yes", }, { files: []string{"sh"}, paths: []string{"/bin", "/usr/bin"}, expected: "/bin/sh", }, { files: []string{"chezmoish"}, paths: []string{"/bin", "/usr/bin"}, expected: "", }, { files: []string{"chezmoish", "yes"}, paths: []string{"/usr/bin", "/bin"}, expected: "/usr/bin/yes", }, { files: []string{"chezmoish", "sh"}, paths: []string{"/bin", "/usr/bin"}, expected: "/bin/sh", }, { files: []string{"chezmoish", "chezvoush"}, paths: []string{"/bin", "/usr/bin"}, expected: "", }, } for _, test := range tests { format := "FindExecutable %#v in %#v as %#v" name := fmt.Sprintf(format, test.files, test.paths, test.expected) t.Run(name, func(t *testing.T) { actual, err := FindExecutable(test.files, test.paths) assert.NoError(t, err) assert.Equal(t, test.expected, actual) }) } } ================================================ FILE: internal/chezmoi/findexecutable_windows_test.go ================================================ //go:build windows package chezmoi import ( "fmt" "strings" "testing" "github.com/alecthomas/assert/v2" ) func TestFindExecutable(t *testing.T) { tests := []struct { files []string paths []string expected string }{ { files: []string{"powershell.exe"}, paths: []string{ "c:\\windows\\system32", "c:\\windows\\system64", "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0", }, expected: "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", }, { files: []string{"powershell"}, paths: []string{ "c:\\windows\\system32", "c:\\windows\\system64", "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0", }, expected: "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", }, { files: []string{"weakshell.exe"}, paths: []string{ "c:\\windows\\system32", "c:\\windows\\system64", "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0", }, expected: "", }, { files: []string{"weakshell"}, paths: []string{ "c:\\windows\\system32", "c:\\windows\\system64", "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0", }, expected: "", }, { files: []string{"weakshell.exe", "powershell.exe"}, paths: []string{ "c:\\windows\\system32", "c:\\windows\\system64", "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0", }, expected: "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", }, { files: []string{"weakshell", "powershell"}, paths: []string{ "c:\\windows\\system32", "c:\\windows\\system64", "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0", }, expected: "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", }, { files: []string{"weakshell.exe", "chezmoishell.exe"}, paths: []string{ "c:\\windows\\system32", "c:\\windows\\system64", "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0", }, expected: "", }, { files: []string{"weakshell", "chezmoishell"}, paths: []string{ "c:\\windows\\system32", "c:\\windows\\system64", "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0", }, expected: "", }, } for _, test := range tests { name := fmt.Sprintf("FindExecutable %v in %#v as %v", test.files, test.paths, test.expected) t.Run(name, func(t *testing.T) { actual, err := FindExecutable(test.files, test.paths) assert.NoError(t, err) assert.Equal(t, strings.ToLower(test.expected), strings.ToLower(actual)) }) } } ================================================ FILE: internal/chezmoi/format.go ================================================ package chezmoi import ( "bytes" "encoding/json" "errors" "fmt" "io" "maps" "slices" "strings" "github.com/goccy/go-yaml" "github.com/pelletier/go-toml/v2" "github.com/tailscale/hujson" ) // Formats. var ( FormatJSON Format = formatJSON{} FormatJSONC Format = formatJSONC{} FormatTOML Format = formatTOML{} FormatYAML Format = formatYAML{} ) var errExpectedEOF = errors.New("expected EOF") // A Format is a serialization format. type Format interface { Marshal(value any) ([]byte, error) Name() string Unmarshal(data []byte, value any) error } // A formatJSON implements the JSON serialization format. type formatJSON struct{} // A formatJSONC implements the JSONC serialization format. type formatJSONC struct{} // A formatTOML implements the TOML serialization format. type formatTOML struct{} // A formatYAML implements the YAML serialization format. type formatYAML struct{} var ( // FormatsByName is a map of all FormatsByName by name. FormatsByName = map[string]Format{ "jsonc": FormatJSONC, "json": FormatJSON, "toml": FormatTOML, "yaml": FormatYAML, } // FormatsByExtension is a map of all Formats by extension. FormatsByExtension = map[string]Format{ "jsonc": FormatJSONC, "json": FormatJSON, "toml": FormatTOML, "yaml": FormatYAML, "yml": FormatYAML, } FormatExtensions = slices.Sorted(maps.Keys(FormatsByExtension)) ) // UnmarshalFileData unmarshals data in the format guessed from filenameAbsPath. func UnmarshalFileData(filenameAbsPath AbsPath, data []byte, value any) error { extension := strings.TrimPrefix(filenameAbsPath.Ext(), ".") format, ok := FormatsByExtension[extension] if !ok { return fmt.Errorf("%s: unknown format", filenameAbsPath) } return format.Unmarshal(data, value) } // Marshal implements Format.Marshal. func (formatJSONC) Marshal(value any) ([]byte, error) { var builder strings.Builder encoder := json.NewEncoder(&builder) encoder.SetEscapeHTML(false) if err := encoder.Encode(value); err != nil { return nil, err } return hujson.Format([]byte(builder.String())) } // Name implements Format.Name. func (formatJSONC) Name() string { return "jsonc" } // Unmarshal implements Format.Unmarshal. func (formatJSONC) Unmarshal(data []byte, value any) error { data, err := hujson.Standardize(data) if err != nil { return err } return FormatJSON.Unmarshal(data, value) } // Marshal implements Format.Marshal. func (formatJSON) Marshal(value any) ([]byte, error) { var builder strings.Builder encoder := json.NewEncoder(&builder) encoder.SetEscapeHTML(false) encoder.SetIndent("", " ") if err := encoder.Encode(value); err != nil { return nil, err } return []byte(builder.String()), nil } // Name implements Format.Name. func (formatJSON) Name() string { return "json" } // Unmarshal implements Format.Unmarshal. func (formatJSON) Unmarshal(data []byte, value any) error { decoder := json.NewDecoder(bytes.NewReader(data)) decoder.DisallowUnknownFields() // When unmarshaling into a generic type, use json.Number to preserve as // much type information as possible. switch value.(type) { case *any, *[]any, *map[string]any: decoder.UseNumber() } if err := decoder.Decode(value); err != nil { return err } if _, err := decoder.Token(); !errors.Is(err, io.EOF) { return errExpectedEOF } switch value := value.(type) { case *any: *value = replaceJSONNumbersWithNumericValues(*value) case *[]any: *value = replaceJSONNumbersWithNumericValuesSlice(*value) case *map[string]any: *value = replaceJSONNumbersWithNumericValuesMap(*value) } return nil } // Marshal implements Format.Marshal. func (formatTOML) Marshal(value any) ([]byte, error) { return toml.Marshal(value) } // Name implements Format.Name. func (formatTOML) Name() string { return "toml" } // Unmarshal implements Format.Unmarshal. func (formatTOML) Unmarshal(data []byte, value any) error { decoder := toml.NewDecoder(bytes.NewReader(data)) decoder.DisallowUnknownFields() return decoder.Decode(value) } // Name implements Format.Name. func (formatYAML) Name() string { return "yaml" } // Marshal implements Format.Marshal. func (formatYAML) Marshal(value any) ([]byte, error) { return yaml.Marshal(value) } // Unmarshal implements Format.Unmarshal. func (formatYAML) Unmarshal(data []byte, value any) error { decoder := yaml.NewDecoder( bytes.NewReader(data), yaml.DisallowUnknownField(), ) switch err := decoder.Decode(value); { case errors.Is(err, io.EOF): return nil // Empty input is OK. case err != nil: return err default: return nil } } // FormatFromAbsPath returns the expected format of absPath. func FormatFromAbsPath(absPath AbsPath) (Format, error) { format, err := formatFromExtension(absPath.Ext()) if err != nil { return nil, fmt.Errorf("%s: %w", absPath, err) } return format, nil } // formatFromExtension returns the expected format of absPath. func formatFromExtension(extension string) (Format, error) { format, ok := FormatsByExtension[strings.TrimPrefix(extension, ".")] if !ok { return nil, fmt.Errorf("%s: unknown format", extension) } return format, nil } func isPrefixDotFormat(name, prefix string) bool { for extension := range FormatsByExtension { if name == prefix+"."+extension { return true } } return false } func isPrefixDotFormatDotTmpl(name, prefix string) bool { for extension := range FormatsByExtension { if name == prefix+"."+extension+TemplateSuffix { return true } } return false } // replaceJSONNumbersWithNumericValues replaces any json.Numbers in value with // int64s or float64s if possible and returns the new value. If value is a slice // or a map then it is mutated in place. func replaceJSONNumbersWithNumericValues(value any) any { switch value := value.(type) { case json.Number: if int64Value, err := value.Int64(); err == nil { return int64Value } if float64Value, err := value.Float64(); err == nil { return float64Value } // If value cannot be represented as an int64 or a float64 then return // it as a string to preserve its value. Such values are valid JSON but // are unlikely to occur in practice. See // https://www.rfc-editor.org/rfc/rfc7159#section-6. return value.String() case []any: return replaceJSONNumbersWithNumericValuesSlice(value) case map[string]any: return replaceJSONNumbersWithNumericValuesMap(value) default: return value } } func replaceJSONNumbersWithNumericValuesMap(value map[string]any) map[string]any { for k, v := range value { value[k] = replaceJSONNumbersWithNumericValues(v) } return value } func replaceJSONNumbersWithNumericValuesSlice(value []any) []any { for i, e := range value { value[i] = replaceJSONNumbersWithNumericValues(e) } return value } ================================================ FILE: internal/chezmoi/format_test.go ================================================ package chezmoi import ( "testing" "github.com/alecthomas/assert/v2" ) func TestFormatJSONSingleValue(t *testing.T) { var value any assert.NoError(t, FormatJSON.Unmarshal([]byte(`{}`), &value)) assert.NoError(t, FormatJSON.Unmarshal([]byte(`{} `), &value)) assert.Error(t, FormatJSON.Unmarshal([]byte(`{} 1`), &value)) } func TestFormats(t *testing.T) { assert.NotZero(t, FormatsByName["json"]) assert.NotZero(t, FormatsByName["jsonc"]) assert.NotZero(t, FormatsByName["toml"]) assert.NotZero(t, FormatsByName["yaml"]) assert.Zero(t, FormatsByName["yml"]) } func TestFormatRoundTrip(t *testing.T) { type value struct { Bool bool Bytes []byte Int int Object map[string]any String string } for _, format := range []Format{ formatJSONC{}, formatJSON{}, formatTOML{}, formatYAML{}, } { t.Run(format.Name(), func(t *testing.T) { v := value{ Bool: true, Bytes: []byte("bytes"), Int: 1, Object: map[string]any{ "key": "value", }, String: "string", } data, err := format.Marshal(v) assert.NoError(t, err) var actualValue value assert.NoError(t, format.Unmarshal(data, &actualValue)) assert.Equal(t, v, actualValue) }) } } func TestFormatEdgeCases(t *testing.T) { type T struct { Key string `json:"key" yaml:"key"` } for _, tc := range []struct { name string format Format data string expected T expectedErr string }{ { name: "jsonc", format: FormatJSONC, data: `{"key":"value"} // comment` + "\n", expected: T{Key: "value"}, }, { name: "jsonc_empty", format: FormatJSONC, expectedErr: "parsing value: unexpected EOF", }, { name: "jsonc_simple", format: FormatJSONC, data: `{"key":"value"}`, expected: T{Key: "value"}, }, { name: "jsonc_trailing_value", format: FormatJSONC, data: `{"key":"value"}1`, expectedErr: "invalid character '1' after top-level value", }, { name: "jsonc_unknown_field", format: FormatJSONC, data: `{"unknown":"value"}`, expectedErr: `json: unknown field "unknown"`, }, { name: "jsonc_unexpected_eof", format: FormatJSONC, data: `{`, expectedErr: "parsing value: unexpected EOF", }, { name: "jsonc_whitespace", format: FormatJSONC, data: "\n", expectedErr: "parsing value: unexpected EOF", }, { name: "json", format: FormatJSON, data: `{"key":"value"}`, expected: T{Key: "value"}, }, { name: "json_empty", format: FormatJSON, expectedErr: "EOF", }, { name: "json_simple", format: FormatJSON, data: `{"key":"value"}`, expected: T{Key: "value"}, }, { name: "json_unknown_field", format: FormatJSON, data: `{"unknown":"value"}`, expectedErr: `json: unknown field "unknown"`, }, { name: "json_unexpected_eof", format: FormatJSON, data: `{`, expectedErr: "unexpected EOF", }, { name: "json_whitespace", format: FormatJSON, data: "\n", expectedErr: "EOF", }, { name: "toml", format: FormatTOML, data: `key = "value"`, expected: T{Key: "value"}, }, { name: "toml_empty", format: FormatTOML, }, { name: "toml_simple", format: FormatTOML, data: `key = "value"`, expected: T{Key: "value"}, }, { name: "toml_unknown_field", format: FormatTOML, data: `unknown = "value"`, expectedErr: "strict mode: fields in the document are missing in the target struct", }, { name: "toml_unexpected_eof", format: FormatTOML, data: `[`, expectedErr: "expected key but found none", }, { name: "toml_whitespace", format: FormatTOML, data: "\n", }, { name: "yaml", format: FormatYAML, data: `key: value`, expected: T{Key: "value"}, }, { name: "yaml_empty", format: FormatYAML, }, { name: "yaml_simple", format: FormatYAML, data: "key: value", expected: T{Key: "value"}, }, { name: "yaml_unknown_field", format: FormatYAML, data: "unknown: value", expectedErr: "unknown: value", }, { name: "yaml_unexpected_eof", format: FormatYAML, data: `{`, expectedErr: "could not find flow mapping end token '}'", }, { name: "yaml_whitespace", format: FormatYAML, data: "\n", }, } { t.Run(tc.name, func(t *testing.T) { var actual T err := tc.format.Unmarshal([]byte(tc.data), &actual) if tc.expectedErr != "" { assert.Error(t, err) assert.Contains(t, err.Error(), tc.expectedErr) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, actual) } }) } } ================================================ FILE: internal/chezmoi/gitdiffsystem.go ================================================ package chezmoi import ( "errors" "io" "io/fs" "os/exec" "runtime" "time" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/filemode" "github.com/go-git/go-git/v5/plumbing/format/diff" vfs "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoiset" ) // A GitDiffSystem wraps a System and logs all of the actions executed as a git // diff. type GitDiffSystem struct { system System dirAbsPath AbsPath filter *EntryTypeFilter removedEntries chezmoiset.Set[AbsPath] reverse bool scriptContents bool textConvFunc TextConvFunc unifiedEncoder *diff.UnifiedEncoder } // GitDiffSystemOptions are options for NewGitDiffSystem. type GitDiffSystemOptions struct { Color bool Filter *EntryTypeFilter Reverse bool ScriptContents bool TextConvFunc TextConvFunc } // NewGitDiffSystem returns a new GitDiffSystem. Output is written to w, the // dirAbsPath is stripped from paths, and color controls whether the output // contains ANSI color escape sequences. func NewGitDiffSystem(system System, w io.Writer, dirAbsPath AbsPath, options *GitDiffSystemOptions) *GitDiffSystem { unifiedEncoder := diff.NewUnifiedEncoder(w, diff.DefaultContextLines) if options.Color { unifiedEncoder.SetColor(diff.NewColorConfig()) } return &GitDiffSystem{ system: system, dirAbsPath: dirAbsPath, filter: options.Filter, removedEntries: chezmoiset.New[AbsPath](), reverse: options.Reverse, scriptContents: options.ScriptContents, textConvFunc: options.TextConvFunc, unifiedEncoder: unifiedEncoder, } } // Chmod implements System.Chmod. func (s *GitDiffSystem) Chmod(name AbsPath, mode fs.FileMode) error { fromInfo, err := s.system.Stat(name) if err != nil { return err } if s.filter.IncludeFileInfo(fromInfo) { toMode := fromInfo.Mode().Type() | mode var toData []byte if fromInfo.Mode().IsRegular() { toData, err = s.ReadFile(name) if err != nil { return err } } if err := s.encodeDiff(name, toData, toMode); err != nil { return err } } return s.system.Chmod(name, mode) } // Chtimes implements system.Chtimes. func (s *GitDiffSystem) Chtimes(name AbsPath, atime, mtime time.Time) error { if s.isRemoved(name) { return fs.ErrNotExist } return s.system.Chtimes(name, atime, mtime) } // Glob implements System.Glob. func (s *GitDiffSystem) Glob(pattern string) ([]string, error) { matches, err := s.system.Glob(pattern) if err != nil { return nil, err } n := 0 for _, match := range matches { if s.isRemoved(NewAbsPath(match)) { continue } matches[n] = match n++ } return matches[:n], nil } // Link implements System.Link. func (s *GitDiffSystem) Link(oldName, newName AbsPath) error { // LATER generate a diff return s.system.Link(oldName, newName) } // Lstat implements System.Lstat. func (s *GitDiffSystem) Lstat(name AbsPath) (fs.FileInfo, error) { if s.isRemoved(name) { return nil, fs.ErrNotExist } return s.system.Lstat(name) } // Mkdir implements System.Mkdir. func (s *GitDiffSystem) Mkdir(name AbsPath, perm fs.FileMode) error { if s.filter.IncludeEntryTypeBits(EntryTypeDirs) { if err := s.encodeDiff(name, nil, fs.ModeDir|perm); err != nil { return err } } return s.system.Mkdir(name, perm) } // RawPath implements System.RawPath. func (s *GitDiffSystem) RawPath(path AbsPath) (AbsPath, error) { return s.system.RawPath(path) } // ReadDir implements System.ReadDir. func (s *GitDiffSystem) ReadDir(name AbsPath) ([]fs.DirEntry, error) { if s.isRemoved(name) { return nil, fs.ErrNotExist } dirEntries, err := s.system.ReadDir(name) if err != nil { return nil, err } n := 0 for _, dirEntry := range dirEntries { if s.isRemoved(name.JoinString(dirEntry.Name())) { continue } dirEntries[n] = dirEntry n++ } return dirEntries[:n], nil } // ReadFile implements System.ReadFile. func (s *GitDiffSystem) ReadFile(name AbsPath) ([]byte, error) { return s.system.ReadFile(name) } // Readlink implements System.Readlink. func (s *GitDiffSystem) Readlink(name AbsPath) (string, error) { return s.system.Readlink(name) } // Remove implements System.Remove. func (s *GitDiffSystem) Remove(name AbsPath) error { // Only emit diffs for removing directories if the underlying directory is // empty. Keep track of removed entries to handled nested removes. switch fileInfo, err := s.system.Stat(name); { case err != nil: return err case fileInfo.IsDir(): switch dirEntries, err := s.ReadDir(name); { case err != nil: return err case len(dirEntries) != 0: return fs.ErrExist } } if s.filter.IncludeEntryTypeBits(EntryTypeRemove) { if err := s.encodeDiff(name, nil, 0); err != nil { return err } } if err := s.system.Remove(name); err != nil { return err } s.removedEntries.Add(name) return nil } // RemoveAll implements System.RemoveAll. func (s *GitDiffSystem) RemoveAll(name AbsPath) error { if s.filter.IncludeEntryTypeBits(EntryTypeRemove) { if err := s.encodeDiff(name, nil, 0); err != nil { return err } } if err := s.system.RemoveAll(name); err != nil { return err } s.removedEntries.Add(name) return nil } // Rename implements System.Rename. func (s *GitDiffSystem) Rename(oldPath, newPath AbsPath) error { fromFileInfo, err := s.Stat(oldPath) if err != nil { return err } if s.filter.IncludeFileInfo(fromFileInfo) { var fileMode filemode.FileMode var hash plumbing.Hash switch { case fromFileInfo.Mode().IsDir(): hash = plumbing.ZeroHash // LATER be more intelligent here case fromFileInfo.Mode().IsRegular(): data, err := s.system.ReadFile(oldPath) if err != nil { return err } hash = plumbing.ComputeHash(plumbing.BlobObject, data) default: fileMode = filemode.FileMode(fromFileInfo.Mode()) } fromPath, toPath := s.trimPrefix(oldPath), s.trimPrefix(newPath) if s.reverse { fromPath, toPath = toPath, fromPath } if err := s.unifiedEncoder.Encode(&gitDiffPatch{ filePatches: []diff.FilePatch{ &gitDiffFilePatch{ from: &gitDiffFile{ fileMode: fileMode, relPath: fromPath, hash: hash, }, to: &gitDiffFile{ fileMode: fileMode, relPath: toPath, hash: hash, }, }, }, }); err != nil { return err } } if err := s.system.Rename(oldPath, newPath); err != nil { return err } s.removedEntries.Add(oldPath) return nil } // RunCmd implements System.RunCmd. func (s *GitDiffSystem) RunCmd(cmd *exec.Cmd) error { return s.system.RunCmd(cmd) } // RunScript implements System.RunScript. func (s *GitDiffSystem) RunScript(scriptName RelPath, dir AbsPath, data []byte, options RunScriptOptions) error { bits := EntryTypeScripts if options.Condition == ScriptConditionAlways { bits |= EntryTypeAlways } if s.filter.IncludeEntryTypeBits(bits) { fromData, toData := []byte(nil), data fromMode, toMode := fs.FileMode(0), fs.FileMode(filemode.Executable) if !s.scriptContents { toData = nil } if s.reverse { fromData, toData = toData, fromData fromMode, toMode = toMode, fromMode } diffPatch, err := DiffPatch(scriptName, fromData, fromMode, toData, toMode) if err != nil { return err } if err := s.unifiedEncoder.Encode(diffPatch); err != nil { return err } } return s.system.RunScript(scriptName, dir, data, options) } // Stat implements System.Stat. func (s *GitDiffSystem) Stat(name AbsPath) (fs.FileInfo, error) { if s.isRemoved(name) { return nil, fs.ErrNotExist } return s.system.Stat(name) } // UnderlyingFS implements System.UnderlyingFS. func (s *GitDiffSystem) UnderlyingFS() vfs.FS { return s.system.UnderlyingFS() } // WriteFile implements System.WriteFile. func (s *GitDiffSystem) WriteFile(filename AbsPath, data []byte, perm fs.FileMode) error { if s.filter.IncludeEntryTypeBits(EntryTypeFiles) { if err := s.encodeDiff(filename, data, perm); err != nil { return err } } return s.system.WriteFile(filename, data, perm) } // WriteSymlink implements System.WriteSymlink. func (s *GitDiffSystem) WriteSymlink(oldName string, newName AbsPath) error { if s.filter.IncludeEntryTypeBits(EntryTypeSymlinks) { toData := append([]byte(normalizeLinkname(oldName)), '\n') toMode := fs.ModeSymlink if runtime.GOOS == "windows" { toMode |= 0o666 } if err := s.encodeDiff(newName, toData, toMode); err != nil { return err } } return s.system.WriteSymlink(oldName, newName) } // encodeDiff encodes the diff between the actual state of absPath and the // target state of toData and toMode. func (s *GitDiffSystem) encodeDiff(absPath AbsPath, toData []byte, toMode fs.FileMode) error { var fromData []byte var fromMode fs.FileMode switch fromInfo, err := s.system.Lstat(absPath); { case errors.Is(err, fs.ErrNotExist): // Leave fromData and fromMode at their zero values. case err != nil: return err case fromInfo.Mode().IsRegular(): fromData, err = s.system.ReadFile(absPath) if err != nil { return err } if s.textConvFunc != nil { fromData, _, err = s.textConvFunc(absPath.String(), fromData) if err != nil { return err } } fromMode = fromInfo.Mode() case fromInfo.Mode().Type() == fs.ModeSymlink: fromDataStr, err := s.system.Readlink(absPath) if err != nil { return err } fromData = append([]byte(fromDataStr), '\n') fromMode = fromInfo.Mode() default: fromMode = fromInfo.Mode() } if s.textConvFunc != nil { var err error toData, _, err = s.textConvFunc(absPath.String(), toData) if err != nil { return err } } if s.reverse { fromData, toData = toData, fromData fromMode, toMode = toMode, fromMode } diffPatch, err := DiffPatch(s.trimPrefix(absPath), fromData, fromMode, toData, toMode) if err != nil { return err } return s.unifiedEncoder.Encode(diffPatch) } func (s *GitDiffSystem) isRemoved(absPath AbsPath) bool { if s.removedEntries.IsEmpty() { return false } if s.removedEntries.Contains(absPath) { return true } var lastPrefixAbsPath AbsPath prefixAbsPath, _ := absPath.Split() for prefixAbsPath != lastPrefixAbsPath { if s.removedEntries.Contains(prefixAbsPath) { return true } lastPrefixAbsPath = prefixAbsPath prefixAbsPath, _ = prefixAbsPath.Split() } return false } // trimPrefix removes s's directory prefix from absPath. func (s *GitDiffSystem) trimPrefix(absPath AbsPath) RelPath { return absPath.MustTrimDirPrefix(s.dirAbsPath) } ================================================ FILE: internal/chezmoi/gitdiffsystem_test.go ================================================ package chezmoi import ( "github.com/go-git/go-git/v5/plumbing/format/diff" ) var ( _ System = &GitDiffSystem{} _ diff.Chunk = &gitDiffChunk{} _ diff.File = &gitDiffFile{} _ diff.FilePatch = &gitDiffFilePatch{} _ diff.Patch = &gitDiffPatch{} ) ================================================ FILE: internal/chezmoi/github.go ================================================ package chezmoi import ( "context" "net/http" "os" "github.com/google/go-github/v61/github" "golang.org/x/oauth2" ) // NewGitHubClient returns a new github.Client configured with an access token // and a http client, if available. func NewGitHubClient(ctx context.Context, httpClient *http.Client) *github.Client { for _, key := range []string{ "CHEZMOI_GITHUB_ACCESS_TOKEN", "CHEZMOI_GITHUB_TOKEN", "GITHUB_ACCESS_TOKEN", "GITHUB_TOKEN", } { if accessToken := os.Getenv(key); accessToken != "" { httpClient = oauth2.NewClient( context.WithValue(ctx, oauth2.HTTPClient, httpClient), oauth2.StaticTokenSource(&oauth2.Token{ AccessToken: accessToken, })) break } } return github.NewClient(httpClient) } ================================================ FILE: internal/chezmoi/glob.go ================================================ package chezmoi import ( "io/fs" "github.com/bmatcuk/doublestar/v4" vfs "github.com/twpayne/go-vfs/v5" ) // A LstatFS implements [io/fs.StatFS] but uses Lstat instead of Stat. type LstatFS struct { Wrapped interface { fs.FS Lstat(name string) (fs.FileInfo, error) } } // Open implements io/fs.StatFS.Open. func (s LstatFS) Open(name string) (fs.File, error) { return s.Wrapped.Open(name) } // Stat implements io/fs.StatFS.Stat. func (s LstatFS) Stat(name string) (fs.FileInfo, error) { return s.Wrapped.Lstat(name) } // Glob is like github.com/bmatcuk/doublestar/v4.Glob except that it does not // follow symlinks. func Glob(fileSystem vfs.FS, prefix string) ([]string, error) { fsys := LstatFS{Wrapped: fileSystem} opts := []doublestar.GlobOption{ doublestar.WithFailOnIOErrors(), doublestar.WithNoFollow(), } return doublestar.Glob(fsys, prefix, opts...) } ================================================ FILE: internal/chezmoi/gpgencryption.go ================================================ package chezmoi import ( "log/slog" "os" "os/exec" "runtime" "chezmoi.io/chezmoi/internal/chezmoierrors" "chezmoi.io/chezmoi/internal/chezmoilog" ) // A GPGEncryption uses gpg for encryption and decryption. See // https://gnupg.org/. type GPGEncryption struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Args []string `json:"args" mapstructure:"args" yaml:"args"` Recipient string `json:"recipient" mapstructure:"recipient" yaml:"recipient"` Recipients []string `json:"recipients" mapstructure:"recipients" yaml:"recipients"` Symmetric bool `json:"symmetric" mapstructure:"symmetric" yaml:"symmetric"` Suffix string `json:"suffix" mapstructure:"suffix" yaml:"suffix"` } // Decrypt implements Encryption.Decrypt. func (e *GPGEncryption) Decrypt(ciphertext []byte) ([]byte, error) { var plaintext []byte if err := withPrivateTempDir(func(tempDirAbsPath AbsPath) error { ciphertextAbsPath := tempDirAbsPath.JoinString("ciphertext" + e.EncryptedSuffix()) if err := os.WriteFile(ciphertextAbsPath.String(), ciphertext, 0o600); err != nil { return err } plaintextAbsPath := tempDirAbsPath.JoinString("plaintext") args := e.decryptArgs(plaintextAbsPath, ciphertextAbsPath) if err := e.run(args); err != nil { return err } var err error plaintext, err = os.ReadFile(plaintextAbsPath.String()) return err }); err != nil { return nil, err } return plaintext, nil } // DecryptToFile implements Encryption.DecryptToFile. func (e *GPGEncryption) DecryptToFile(plaintextAbsPath AbsPath, ciphertext []byte) error { return withPrivateTempDir(func(tempDirAbsPath AbsPath) error { ciphertextAbsPath := tempDirAbsPath.JoinString("ciphertext" + e.EncryptedSuffix()) if err := os.WriteFile(ciphertextAbsPath.String(), ciphertext, 0o600); err != nil { return err } args := e.decryptArgs(plaintextAbsPath, ciphertextAbsPath) return e.run(args) }) } // Encrypt implements Encryption.Encrypt. func (e *GPGEncryption) Encrypt(plaintext []byte) ([]byte, error) { var ciphertext []byte if err := withPrivateTempDir(func(tempDirAbsPath AbsPath) error { plaintextAbsPath := tempDirAbsPath.JoinString("plaintext") if err := os.WriteFile(plaintextAbsPath.String(), plaintext, 0o600); err != nil { return err } ciphertextAbsPath := tempDirAbsPath.JoinString("ciphertext" + e.EncryptedSuffix()) args := e.encryptArgs(plaintextAbsPath, ciphertextAbsPath) if err := e.run(args); err != nil { return err } var err error ciphertext, err = os.ReadFile(ciphertextAbsPath.String()) return err }); err != nil { return nil, err } return ciphertext, nil } // EncryptFile implements Encryption.EncryptFile. func (e *GPGEncryption) EncryptFile(plaintextAbsPath AbsPath) ([]byte, error) { var ciphertext []byte if err := withPrivateTempDir(func(tempDirAbsPath AbsPath) error { ciphertextAbsPath := tempDirAbsPath.JoinString("ciphertext" + e.EncryptedSuffix()) args := e.encryptArgs(plaintextAbsPath, ciphertextAbsPath) if err := e.run(args); err != nil { return err } var err error ciphertext, err = os.ReadFile(ciphertextAbsPath.String()) return err }); err != nil { return nil, err } return ciphertext, nil } // EncryptedSuffix implements Encryption.EncryptedSuffix. func (e *GPGEncryption) EncryptedSuffix() string { return e.Suffix } // decryptArgs returns the arguments for decryption. func (e *GPGEncryption) decryptArgs(plaintextAbsPath, ciphertextAbsPath AbsPath) []string { args := []string{"--output", plaintextAbsPath.String()} args = append(args, e.Args...) args = append(args, "--decrypt", ciphertextAbsPath.String()) return args } // encryptArgs returns the arguments for encryption. func (e *GPGEncryption) encryptArgs(plaintextAbsPath, ciphertextAbsPath AbsPath) []string { args := []string{ "--armor", "--output", ciphertextAbsPath.String(), } if e.Symmetric { args = append(args, "--symmetric") } else { if e.Recipient != "" { args = append(args, "--recipient", e.Recipient) } for _, recipient := range e.Recipients { args = append(args, "--recipient", recipient) } } args = append(args, e.Args...) if !e.Symmetric { args = append(args, "--encrypt") } args = append(args, plaintextAbsPath.String()) return args } // run runs the command with args. func (e *GPGEncryption) run(args []string) error { cmd := exec.Command(e.Command, args...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return chezmoilog.LogCmdRun(slog.Default(), cmd) } // withPrivateTempDir creates a private temporary and calls f. func withPrivateTempDir(f func(tempDirAbsPath AbsPath) error) (err error) { var tempDir string if tempDir, err = os.MkdirTemp("", "chezmoi-encryption"); err != nil { return err } defer chezmoierrors.CombineFunc(&err, func() error { return os.RemoveAll(tempDir) }) if runtime.GOOS != "windows" { if err := os.Chmod(tempDir, 0o700); err != nil { return err } } return f(NewAbsPath(tempDir)) } ================================================ FILE: internal/chezmoi/gpgencryption_test.go ================================================ package chezmoi import ( "runtime" "testing" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestGPGEncryption(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("skipping gpg tests on Windows") } command := lookPathOrSkip(t, "gpg") tempDir := t.TempDir() key, passphrase, err := chezmoitest.GPGGenerateKey(command, tempDir) assert.NoError(t, err) for _, tc := range []struct { name string symmetric bool }{ { name: "asymmetric", symmetric: false, }, { name: "symmetric", symmetric: true, }, } { t.Run(tc.name, func(t *testing.T) { testEncryption(t, &GPGEncryption{ Command: command, Args: []string{ "--homedir", tempDir, "--no-tty", "--passphrase", passphrase, "--pinentry-mode", "loopback", }, Recipient: key, Symmetric: tc.symmetric, }) }) } } ================================================ FILE: internal/chezmoi/hexbytes.go ================================================ package chezmoi import ( "encoding/hex" "strconv" ) // A HexBytes is a []byte which is marshaled as a hex string. type HexBytes []byte // Bytes returns h as a []byte. func (h HexBytes) Bytes() []byte { return []byte(h) } // MarshalText implements encoding.TextMarshaler.MarshalText. func (h HexBytes) MarshalText() ([]byte, error) { if len(h) == 0 { return nil, nil } result := make([]byte, hex.EncodedLen(len(h))) hex.Encode(result, h) return result, nil } // MarshalYAML implements github.com/goccy/go-yaml.BytesMarshaler.MarshalYAML. func (h HexBytes) MarshalYAML() ([]byte, error) { data := make([]byte, 2+2*len(h)) data[0] = '"' hex.Encode(data[1:len(data)-1], []byte(h)) data[len(data)-1] = '"' return data, nil } // UnmarshalText implements encoding.TextUnmarshaler.UnmarshalText. func (h *HexBytes) UnmarshalText(text []byte) error { if len(text) == 0 { *h = nil return nil } result := make([]byte, hex.DecodedLen(len(text))) if _, err := hex.Decode(result, text); err != nil { return err } *h = result return nil } // UnmarshalYAML implements // github.com/goccy/go-yaml.BytesUnmarshaler.UnmarshalYAML. func (h *HexBytes) UnmarshalYAML(data []byte) error { s, err := strconv.Unquote(string(data)) if err != nil { return err } if s == "" { *h = nil return nil } hexBytes, err := hex.DecodeString(s) if err != nil { return err } *h = hexBytes return nil } func (h HexBytes) String() string { return hex.EncodeToString(h) } ================================================ FILE: internal/chezmoi/hexbytes_test.go ================================================ package chezmoi import ( "strconv" "testing" "github.com/alecthomas/assert/v2" ) func TestHexBytes(t *testing.T) { for i, tc := range []struct { b HexBytes expectedStr string }{ { b: nil, expectedStr: "\"\"\n", }, { b: []byte{0}, expectedStr: "\"00\"\n", }, { b: []byte{0, 1, 2, 3}, expectedStr: "\"00010203\"\n", }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { for _, format := range []Format{ formatJSON{}, formatYAML{}, } { t.Run(format.Name(), func(t *testing.T) { actual, err := format.Marshal(tc.b) assert.NoError(t, err) assert.Equal(t, []byte(tc.expectedStr), actual) var actualHexBytes HexBytes assert.NoError(t, format.Unmarshal(actual, &actualHexBytes)) assert.Equal(t, tc.b, actualHexBytes) }) } }) } } ================================================ FILE: internal/chezmoi/interpreter.go ================================================ package chezmoi import ( "log/slog" "os/exec" ) // An Interpreter interprets scripts. type Interpreter struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Args []string `json:"args" mapstructure:"args" yaml:"args"` } // ExecCommand returns the [*exec.Cmd] to interpret name. func (i *Interpreter) ExecCommand(name string) *exec.Cmd { if i.None() { return exec.Command(name) } return exec.Command(i.Command, append(i.Args, name)...) } // None returns if i represents no interpreter. func (i *Interpreter) None() bool { return i == nil || i.Command == "" } // LogValue implements log/slog.LogValuer.LogValue. func (i *Interpreter) LogValue() slog.Value { var attrs []slog.Attr if i.Command != "" { attrs = append(attrs, slog.String("command", i.Command)) } if i.Args != nil { attrs = append(attrs, slog.Any("args", i.Args)) } return slog.GroupValue(attrs...) } ================================================ FILE: internal/chezmoi/lookpath.go ================================================ package chezmoi import ( "os/exec" "sync" ) var ( lookPathCacheMutex sync.Mutex lookPathCache = make(map[string]string) ) // LookPath is like [os/exec.LookPath] except that the first positive result is // cached. func LookPath(file string) (string, error) { lookPathCacheMutex.Lock() defer lookPathCacheMutex.Unlock() if path, ok := lookPathCache[file]; ok { return path, nil } path, err := exec.LookPath(file) if err == nil { lookPathCache[file] = path } return path, err } ================================================ FILE: internal/chezmoi/mockpersistentstate.go ================================================ package chezmoi // A MockPersistentState is a mock persistent state. type MockPersistentState struct { buckets map[string]map[string][]byte } // NewMockPersistentState returns a new PersistentState. func NewMockPersistentState() *MockPersistentState { return &MockPersistentState{ buckets: make(map[string]map[string][]byte), } } // Close closes s. func (s *MockPersistentState) Close() error { return nil } // CopyTo implements PersistentState.CopyTo. func (s *MockPersistentState) CopyTo(p PersistentState) error { for bucket, bucketMap := range s.buckets { for key, value := range bucketMap { if err := p.Set([]byte(bucket), []byte(key), value); err != nil { return err } } } return nil } // Data implements PersistentState.Data. func (s *MockPersistentState) Data() (map[string]map[string]string, error) { data := make(map[string]map[string]string) for bucket, bucketMap := range s.buckets { dataBucketMap := make(map[string]string, len(bucketMap)) for key, value := range bucketMap { dataBucketMap[key] = string(value) } data[bucket] = dataBucketMap } return data, nil } // Delete implements PersistentState.Delete. func (s *MockPersistentState) Delete(bucket, key []byte) error { bucketMap, ok := s.buckets[string(bucket)] if !ok { return nil } delete(bucketMap, string(key)) return nil } // DeleteBucket implements PersistentState.DeleteBucket. func (s *MockPersistentState) DeleteBucket(bucket []byte) error { delete(s.buckets, string(bucket)) return nil } // ForEach implements PersistentState.ForEach. func (s *MockPersistentState) ForEach(bucket []byte, fn func(k, v []byte) error) error { for k, v := range s.buckets[string(bucket)] { if err := fn([]byte(k), v); err != nil { return err } } return nil } // Get implements PersistentState.Get. func (s *MockPersistentState) Get(bucket, key []byte) ([]byte, error) { bucketMap, ok := s.buckets[string(bucket)] if !ok { return nil, nil } return bucketMap[string(key)], nil } // Set implements PersistentState.Set. func (s *MockPersistentState) Set(bucket, key, value []byte) error { bucketMap, ok := s.buckets[string(bucket)] if !ok { bucketMap = make(map[string][]byte) s.buckets[string(bucket)] = bucketMap } bucketMap[string(key)] = value return nil } ================================================ FILE: internal/chezmoi/mockpersistentstate_test.go ================================================ package chezmoi import ( "testing" ) func TestMockPersistentState(t *testing.T) { testPersistentState(t, func() PersistentState { return NewMockPersistentState() }) } ================================================ FILE: internal/chezmoi/mode.go ================================================ package chezmoi // A Mode is a mode of operation. It implements the github.com/spf13/flag.Value // interface. type Mode string // Modes. const ( ModeFile Mode = "file" ModeSymlink Mode = "symlink" ) type InvalidModeError string func (e InvalidModeError) Error() string { return "invalid mode: " + string(e) } // ModeFlagCompletionFunc is a function that completes the value of mode flags. var ModeFlagCompletionFunc = FlagCompletionFunc([]string{ string(ModeFile), string(ModeSymlink), }) // Set implements github.com/spf13/flag.Value.Set. func (m *Mode) Set(s string) error { switch Mode(s) { case ModeFile: *m = ModeFile return nil case ModeSymlink: *m = ModeSymlink return nil default: return InvalidModeError(Mode(s)) } } // String implements github.com/spf13/flag.Value.String. func (m Mode) String() string { return string(m) } // Type implements github.com/spf13/flag.Value.Type. func (m Mode) Type() string { return "file|symlink" } ================================================ FILE: internal/chezmoi/noencryption.go ================================================ package chezmoi import "errors" var errEncryptionNotConfigured = errors.New("encryption not configured") // NoEncryption returns an error when any method is called. type NoEncryption struct{} // Decrypt implements Encryption.Decrypt. func (NoEncryption) Decrypt([]byte) ([]byte, error) { return nil, errEncryptionNotConfigured } // DecryptToFile implements Encryption.DecryptToFile. func (NoEncryption) DecryptToFile(AbsPath, []byte) error { return errEncryptionNotConfigured } // Encrypt implements Encryption.Encrypt. func (NoEncryption) Encrypt([]byte) ([]byte, error) { return nil, errEncryptionNotConfigured } // EncryptFile implements Encryption.EncryptFile. func (NoEncryption) EncryptFile(AbsPath) ([]byte, error) { return nil, errEncryptionNotConfigured } // EncryptedSuffix implements Encryption.EncryptedSuffix. func (NoEncryption) EncryptedSuffix() string { return "" } ================================================ FILE: internal/chezmoi/noencryption_test.go ================================================ package chezmoi var _ Encryption = NoEncryption{} ================================================ FILE: internal/chezmoi/nullpersistentstate.go ================================================ package chezmoi // A NullPersistentState is an empty PersistentState that returns the zero value // for all reads and silently consumes all writes. type NullPersistentState struct{} // Close does nothing. func (NullPersistentState) Close() error { return nil } // CopyTo does nothing. func (NullPersistentState) CopyTo(s PersistentState) error { return nil } // Data does nothing. func (NullPersistentState) Data() (map[string]map[string]string, error) { return nil, nil } // Delete does nothing. func (NullPersistentState) Delete(bucket, key []byte) error { return nil } // DeleteBucket does nothing. func (NullPersistentState) DeleteBucket(bucket []byte) error { return nil } // ForEach does nothing. func (NullPersistentState) ForEach(bucket []byte, fn func(k, v []byte) error) error { return nil } // Get does nothing. func (NullPersistentState) Get(bucket, key []byte) ([]byte, error) { return nil, nil } // Set does nothing. func (NullPersistentState) Set(bucket, key, value []byte) error { return nil } ================================================ FILE: internal/chezmoi/nullsystem.go ================================================ package chezmoi type NullSystem struct { emptySystemMixin noUpdateSystemMixin } ================================================ FILE: internal/chezmoi/nullsystem_test.go ================================================ package chezmoi var _ System = &NullSystem{} ================================================ FILE: internal/chezmoi/path_unix.go ================================================ //go:build unix package chezmoi import ( "path/filepath" "strings" ) var devNullAbsPath = NewAbsPath("/dev/null") // NewAbsPathFromExtPath returns a new AbsPath by converting extPath to use // slashes, performing tilde expansion, and making the path absolute. func NewAbsPathFromExtPath(extPath string, homeDirAbsPath AbsPath) (AbsPath, error) { extPath = filepath.Clean(extPath) switch { case extPath == "~": return homeDirAbsPath, nil case strings.HasPrefix(extPath, "~/"): return homeDirAbsPath.JoinString(extPath[2:]), nil case filepath.IsAbs(extPath): return NewAbsPath(extPath), nil default: absPath, err := filepath.Abs(extPath) if err != nil { return EmptyAbsPath, err } return NewAbsPath(absPath), nil } } // NormalizePath returns path normalized. On non-Windows systems, normalized // paths are absolute paths. func NormalizePath(path string) (AbsPath, error) { absPath, err := filepath.Abs(path) if err != nil { return EmptyAbsPath, err } return NewAbsPath(absPath), nil } // normalizeLinkname returns linkname normalized. On non-Windows systems, it // returns linkname unchanged. func normalizeLinkname(linkname string) string { return linkname } ================================================ FILE: internal/chezmoi/path_windows.go ================================================ package chezmoi import ( "path/filepath" "strings" ) var devNullAbsPath = NewAbsPath("NUL:") // NewAbsPathFromExtPath returns a new AbsPath by converting extPath to use // slashes, performing tilde expansion, making the path absolute, and converting // the volume name to uppercase. func NewAbsPathFromExtPath(extPath string, homeDirAbsPath AbsPath) (AbsPath, error) { extPath = filepath.Clean(extPath) switch { case extPath == "~": return homeDirAbsPath, nil case len(extPath) >= 2 && extPath[0] == '~' && isSlash(extPath[1]): return homeDirAbsPath.JoinString(filepath.ToSlash(extPath[2:])), nil case filepath.IsAbs(extPath): return NewAbsPath(volumeNameToUpper(extPath)).ToSlash(), nil default: extPath, err := filepath.Abs(extPath) if err != nil { return EmptyAbsPath, err } return NewAbsPath(volumeNameToUpper(extPath)).ToSlash(), nil } } // NormalizePath returns path normalized. On Windows, normalized paths are // absolute paths with a uppercase volume name and forward slashes. func NormalizePath(path string) (AbsPath, error) { var err error path, err = filepath.Abs(path) if err != nil { return EmptyAbsPath, err } if n := volumeNameLen(path); n > 0 { path = strings.ToUpper(path[:n]) + path[n:] } return NewAbsPath(path).ToSlash(), nil } // normalizeLinkname returns linkname normalized. On Windows, backslashes are // converted to forward slashes and if linkname is an absolute path then the // volume name is converted to uppercase. func normalizeLinkname(linkname string) string { if filepath.IsAbs(linkname) { return filepath.ToSlash(volumeNameToUpper(linkname)) } return filepath.ToSlash(linkname) } // volumeNameLen returns length of the leading volume name on Windows. It // returns 0 elsewhere. func volumeNameLen(path string) int { if len(path) < 2 { return 0 } // with drive letter c := path[0] if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') { return 2 } // is it UNC? https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) && !isSlash(path[2]) && path[2] != '.' { // first, leading `\\` and next shouldn't be `\`. its server name. for n := 3; n < l-1; n++ { // second, next '\' shouldn't be repeated. if isSlash(path[n]) { n++ // third, following something characters. its share name. if !isSlash(path[n]) { if path[n] == '.' { break } for ; n < l; n++ { if isSlash(path[n]) { break } } return n } break } } } return 0 } // volumeNameToUpper returns path with the volume name converted to uppercase. func volumeNameToUpper(path string) string { if n := volumeNameLen(path); n > 0 { return strings.ToUpper(path[:n]) + path[n:] } return path } ================================================ FILE: internal/chezmoi/path_windows_test.go ================================================ package chezmoi import ( "strconv" "testing" "github.com/alecthomas/assert/v2" ) func TestAbsPathTrimDirPrefix(t *testing.T) { for i, tc := range []struct { absPath AbsPath dirPrefixAbsPath AbsPath expected RelPath }{ { absPath: NewAbsPath("/home/user/.config"), dirPrefixAbsPath: NewAbsPath("/home/user"), expected: NewRelPath(".config"), }, { absPath: NewAbsPath("H:/.config"), dirPrefixAbsPath: NewAbsPath("H:"), expected: NewRelPath(".config"), }, { absPath: NewAbsPath("H:/.config"), dirPrefixAbsPath: NewAbsPath("H:/"), expected: NewRelPath(".config"), }, { absPath: NewAbsPath("H:/home/user/.config"), dirPrefixAbsPath: NewAbsPath("H:/home/user"), expected: NewRelPath(".config"), }, { absPath: NewAbsPath(`//server/user/.config`), dirPrefixAbsPath: NewAbsPath(`//server/user`), expected: NewRelPath(".config"), }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { actual, err := tc.absPath.TrimDirPrefix(tc.dirPrefixAbsPath) assert.NoError(t, err) assert.Equal(t, tc.expected, actual) }) } } func TestNormalizeLinkname(t *testing.T) { for _, tc := range []struct { linkname string expected string }{ { linkname: "rel", expected: "rel", }, { linkname: "rel/forward", expected: "rel/forward", }, { linkname: "rel\\" + "backward", expected: "rel/backward", }, { linkname: "rel/forward\\" + "backward", expected: "rel/forward/backward", }, { linkname: "/abs/forward", expected: "/abs/forward", }, { linkname: "\\abs\\" + "backward", expected: "/abs/backward", }, { linkname: "/abs/forward\\" + "backward", expected: "/abs/forward/backward", }, { linkname: "c:/abs/forward", expected: "C:/abs/forward", }, { linkname: "c:\\abs\\" + "backward", expected: "C:/abs/backward", }, { linkname: "c:/abs/forward\\" + "backward", expected: "C:/abs/forward/backward", }, } { t.Run(tc.linkname, func(t *testing.T) { assert.Equal(t, tc.expected, normalizeLinkname(tc.linkname)) }) } } ================================================ FILE: internal/chezmoi/patternset.go ================================================ package chezmoi import ( "fmt" "log/slog" "path" "path/filepath" "slices" "github.com/bmatcuk/doublestar/v4" "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoiset" ) type PatternSetIncludeType bool const ( PatternSetInclude PatternSetIncludeType = true PatternSetExclude PatternSetIncludeType = false ) type PatternSetMatchType int const ( PatternSetMatchInclude PatternSetMatchType = 1 PatternSetMatchUnknown PatternSetMatchType = 0 PatternSetMatchExclude PatternSetMatchType = -1 ) // An PatternSet is a set of patterns. type PatternSet struct { IncludePatterns chezmoiset.Set[string] ExcludePatterns chezmoiset.Set[string] } // NewPatternSet returns a new patternSet. func NewPatternSet() *PatternSet { return &PatternSet{ IncludePatterns: chezmoiset.New[string](), ExcludePatterns: chezmoiset.New[string](), } } // LogValue implements log/slog.LogValuer.LogValue. func (ps *PatternSet) LogValue() slog.Value { if ps == nil { return slog.Value{} } return slog.GroupValue( slog.Any("includePatterns", slices.Sorted(ps.IncludePatterns.Elements())), slog.Any("excludePatterns", slices.Sorted(ps.ExcludePatterns.Elements())), ) } // Add adds a pattern to ps. func (ps *PatternSet) Add(pattern string, include PatternSetIncludeType) error { if ok := doublestar.ValidatePattern(pattern); !ok { return fmt.Errorf("%s: invalid pattern", pattern) } switch include { case PatternSetInclude: ps.IncludePatterns.Add(pattern) case PatternSetExclude: ps.ExcludePatterns.Add(pattern) } return nil } // Glob returns all matches in fileSystem. func (ps *PatternSet) Glob(fileSystem vfs.FS, prefix string) ([]string, error) { allMatches := chezmoiset.New[string]() for includePattern := range ps.IncludePatterns { matches, err := Glob(fileSystem, filepath.ToSlash(prefix+includePattern)) if err != nil { return nil, err } allMatches.Add(matches...) } for match := range allMatches { for excludePattern := range ps.ExcludePatterns { exclude, err := doublestar.Match(path.Clean(prefix+excludePattern), match) if err != nil { return nil, err } if exclude { allMatches.Remove(match) } } } sortedMatches := slices.Sorted(allMatches.Elements()) for i, match := range sortedMatches { sortedMatches[i] = filepath.ToSlash(match)[len(prefix):] } return sortedMatches, nil } // Match returns if name matches ps. func (ps *PatternSet) Match(name string) PatternSetMatchType { // If name is explicitly excluded, then return exclude. for pattern := range ps.ExcludePatterns { if ok, _ := doublestar.Match(pattern, name); ok { return PatternSetMatchExclude } } // If name is explicitly included, then return include. for pattern := range ps.IncludePatterns { if ok, _ := doublestar.Match(pattern, name); ok { return PatternSetMatchInclude } } // If name did not match any include or exclude patterns... switch { case len(ps.IncludePatterns) > 0 && len(ps.ExcludePatterns) == 0: // ...only include patterns were specified, so exclude by default. return PatternSetMatchExclude case len(ps.IncludePatterns) == 0 && len(ps.ExcludePatterns) > 0: // ...only exclude patterns were specified, so include by default. return PatternSetMatchInclude default: // ...both include and exclude were specified, so return unknown. return PatternSetMatchUnknown } } ================================================ FILE: internal/chezmoi/patternset_test.go ================================================ package chezmoi import ( "testing" "github.com/alecthomas/assert/v2" vfs "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestPatternSet(t *testing.T) { for _, tc := range []struct { name string ps *PatternSet expectMatches map[string]PatternSetMatchType }{ { name: "empty", ps: NewPatternSet(), expectMatches: map[string]PatternSetMatchType{ "foo": PatternSetMatchUnknown, }, }, { name: "exact", ps: mustNewPatternSet(t, map[string]PatternSetIncludeType{ "foo": PatternSetInclude, }), expectMatches: map[string]PatternSetMatchType{ "foo": PatternSetMatchInclude, "bar": PatternSetMatchExclude, }, }, { name: "wildcard", ps: mustNewPatternSet(t, map[string]PatternSetIncludeType{ "b*": PatternSetInclude, }), expectMatches: map[string]PatternSetMatchType{ "foo": PatternSetMatchExclude, "bar": PatternSetMatchInclude, "baz": PatternSetMatchInclude, }, }, { name: "exclude", ps: mustNewPatternSet(t, map[string]PatternSetIncludeType{ "b*": PatternSetInclude, "baz": PatternSetExclude, }), expectMatches: map[string]PatternSetMatchType{ "foo": PatternSetMatchUnknown, "bar": PatternSetMatchInclude, "baz": PatternSetMatchExclude, }, }, { name: "doublestar", ps: mustNewPatternSet(t, map[string]PatternSetIncludeType{ "**/foo": PatternSetInclude, }), expectMatches: map[string]PatternSetMatchType{ "foo": PatternSetMatchInclude, "bar/foo": PatternSetMatchInclude, "baz/bar/foo": PatternSetMatchInclude, }, }, } { t.Run(tc.name, func(t *testing.T) { for s, expectMatch := range tc.expectMatches { assert.Equal(t, expectMatch, tc.ps.Match(s)) } }) } } func TestPatternSetGlob(t *testing.T) { for _, tc := range []struct { name string ps *PatternSet root any expectedMatches []string }{ { name: "empty", ps: NewPatternSet(), }, { name: "simple", ps: mustNewPatternSet(t, map[string]PatternSetIncludeType{ "/f*": PatternSetInclude, }), root: map[string]any{ "foo": "", }, expectedMatches: []string{ "foo", }, }, { name: "include_exclude", ps: mustNewPatternSet(t, map[string]PatternSetIncludeType{ "/b*": PatternSetInclude, "/*z": PatternSetExclude, }), root: map[string]any{ "bar": "", "baz": "", }, expectedMatches: []string{ "bar", }, }, { name: "doublestar", ps: mustNewPatternSet(t, map[string]PatternSetIncludeType{ "/**/f*": PatternSetInclude, }), root: map[string]any{ "dir1/dir2/foo": "", }, expectedMatches: []string{ "dir1/dir2/foo", }, }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { actualMatches, err := tc.ps.Glob(fileSystem, "/") assert.NoError(t, err) assert.Equal(t, tc.expectedMatches, actualMatches) }) }) } } func mustNewPatternSet(t *testing.T, patterns map[string]PatternSetIncludeType) *PatternSet { t.Helper() ps := NewPatternSet() for pattern, include := range patterns { assert.NoError(t, ps.Add(pattern, include)) } return ps } ================================================ FILE: internal/chezmoi/persistentstate.go ================================================ package chezmoi var ( // ConfigStateBucket is the bucket for recording the config state. ConfigStateBucket = []byte("configState") // EntryStateBucket is the bucket for recording the entry states. EntryStateBucket = []byte("entryState") // GitRepoExternalStateBucket is the bucket for recording the state of commands // that modify directories. GitRepoExternalStateBucket = []byte("gitRepoExternalState") // ScriptStateBucket is the bucket for recording the state of run once // scripts. ScriptStateBucket = []byte("scriptState") stateFormat = formatJSON{} ) // A PersistentState is a persistent state. type PersistentState interface { Close() error CopyTo(s PersistentState) error Data() (map[string]map[string]string, error) Delete(bucket, key []byte) error DeleteBucket(bucket []byte) error ForEach(bucket []byte, fn func(k, v []byte) error) error Get(bucket, key []byte) ([]byte, error) Set(bucket, key, value []byte) error } // PersistentStateBucketData returns the state data in bucket in s. func PersistentStateBucketData(s PersistentState, bucket []byte) (map[string]any, error) { result := make(map[string]any) if err := s.ForEach(bucket, func(k, v []byte) error { var value map[string]any if err := stateFormat.Unmarshal(v, &value); err != nil { return err } result[string(k)] = value return nil }); err != nil { return nil, err } return result, nil } // PersistentStateData returns the structured data in s. func PersistentStateData(s PersistentState, buckets map[string][]byte) (map[string]any, error) { result := make(map[string]any) for bucketName, bucketKey := range buckets { stateData, err := PersistentStateBucketData(s, bucketKey) if err != nil { return nil, err } result[bucketName] = stateData } return result, nil } // PersistentStateGet gets the value associated with key in bucket in s, if it // exists. func PersistentStateGet(s PersistentState, bucket, key []byte, value any) (bool, error) { data, err := s.Get(bucket, key) if err != nil { return false, err } if data == nil { return false, nil } if err := stateFormat.Unmarshal(data, value); err != nil { return false, err } return true, nil } // PersistentStateSet sets the value associated with key in bucket in s. func PersistentStateSet(s PersistentState, bucket, key []byte, value any) error { data, err := stateFormat.Marshal(value) if err != nil { return err } return s.Set(bucket, key, data) } ================================================ FILE: internal/chezmoi/persistentstate_test.go ================================================ package chezmoi import ( "io" "testing" "github.com/alecthomas/assert/v2" ) func testPersistentState(t *testing.T, constructor func() PersistentState) { t.Helper() var ( bucket1 = []byte("bucket1") bucket2 = []byte("bucket2") key = []byte("key1") value = []byte("value") ) s1 := constructor() defer s1.Close() assert.NoError(t, s1.Delete(bucket1, value)) actualValue, err := s1.Get(bucket1, key) assert.NoError(t, err) assert.Zero(t, actualValue) assert.NoError(t, s1.Set(bucket1, key, value)) actualValue, err = s1.Get(bucket1, key) assert.NoError(t, err) assert.Equal(t, value, actualValue) assert.NoError(t, s1.ForEach(bucket1, func(k, v []byte) error { assert.Equal(t, key, k) assert.Equal(t, value, v) return nil })) assert.Equal(t, io.EOF, s1.ForEach(bucket1, func(k, v []byte) error { return io.EOF })) s2 := constructor() defer s2.Close() assert.NoError(t, s1.CopyTo(s2)) actualValue, err = s2.Get(bucket1, key) assert.NoError(t, err) assert.Equal(t, value, actualValue) assert.NoError(t, s2.Close()) actualValue, err = s1.Get(bucket1, key) assert.NoError(t, err) assert.Equal(t, value, actualValue) assert.NoError(t, s1.Delete(bucket1, key)) actualValue, err = s1.Get(bucket1, key) assert.NoError(t, err) assert.Zero(t, actualValue) assert.NoError(t, s1.Set(bucket2, key, value)) actualValue, err = s1.Get(bucket2, key) assert.NoError(t, err) assert.Equal(t, value, actualValue) assert.NoError(t, s1.DeleteBucket(bucket2)) actualValue, err = s1.Get(bucket2, key) assert.NoError(t, err) assert.Zero(t, actualValue) } ================================================ FILE: internal/chezmoi/readonlysystem.go ================================================ package chezmoi import ( "io/fs" vfs "github.com/twpayne/go-vfs/v5" ) // A ReadOnlySystem is a system that may only be read from. type ReadOnlySystem struct { noUpdateSystemMixin system System } // NewReadOnlySystem returns a new ReadOnlySystem that wraps system. func NewReadOnlySystem(system System) *ReadOnlySystem { return &ReadOnlySystem{ system: system, } } // Glob implements System.Glob. func (s *ReadOnlySystem) Glob(pattern string) ([]string, error) { return s.system.Glob(pattern) } // Lstat implements System.Lstat. func (s *ReadOnlySystem) Lstat(filename AbsPath) (fs.FileInfo, error) { return s.system.Lstat(filename) } // RawPath implements System.RawPath. func (s *ReadOnlySystem) RawPath(path AbsPath) (AbsPath, error) { return s.system.RawPath(path) } // ReadDir implements System.ReadDir. func (s *ReadOnlySystem) ReadDir(name AbsPath) ([]fs.DirEntry, error) { return s.system.ReadDir(name) } // ReadFile implements System.ReadFile. func (s *ReadOnlySystem) ReadFile(name AbsPath) ([]byte, error) { return s.system.ReadFile(name) } // Readlink implements System.Readlink. func (s *ReadOnlySystem) Readlink(name AbsPath) (string, error) { return s.system.Readlink(name) } // Stat implements System.Stat. func (s *ReadOnlySystem) Stat(name AbsPath) (fs.FileInfo, error) { return s.system.Stat(name) } // UnderlyingFS implements System.UnderlyingFS. func (s *ReadOnlySystem) UnderlyingFS() vfs.FS { return s.system.UnderlyingFS() } ================================================ FILE: internal/chezmoi/readonlysystem_test.go ================================================ package chezmoi var _ System = &ReadOnlySystem{} ================================================ FILE: internal/chezmoi/realsystem.go ================================================ package chezmoi import ( "errors" "io/fs" "log/slog" "os" "os/exec" "path/filepath" "runtime" "time" vfs "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoierrors" "chezmoi.io/chezmoi/internal/chezmoilog" ) // A RealSystemOption sets an option on a RealSystem. type RealSystemOption func(*RealSystem) // Chtimes implements System.Chtimes. func (s *RealSystem) Chtimes(name AbsPath, atime, mtime time.Time) error { return s.fileSystem.Chtimes(name.String(), atime, mtime) } // Glob implements System.Glob. func (s *RealSystem) Glob(pattern string) ([]string, error) { return Glob(s.UnderlyingFS(), filepath.ToSlash(pattern)) } // Link implements System.Link. func (s *RealSystem) Link(oldName, newName AbsPath) error { return s.fileSystem.Link(oldName.String(), newName.String()) } // Lstat implements System.Lstat. func (s *RealSystem) Lstat(filename AbsPath) (fs.FileInfo, error) { return s.fileSystem.Lstat(filename.String()) } // Mkdir implements System.Mkdir. func (s *RealSystem) Mkdir(name AbsPath, perm fs.FileMode) error { return s.fileSystem.Mkdir(name.String(), perm) } // RawPath implements System.RawPath. func (s *RealSystem) RawPath(absPath AbsPath) (AbsPath, error) { rawAbsPath, err := s.fileSystem.RawPath(absPath.String()) if err != nil { return EmptyAbsPath, err } return NewAbsPath(rawAbsPath), nil } // ReadDir implements System.ReadDir. func (s *RealSystem) ReadDir(name AbsPath) ([]fs.DirEntry, error) { return s.fileSystem.ReadDir(name.String()) } // ReadFile implements System.ReadFile. func (s *RealSystem) ReadFile(name AbsPath) ([]byte, error) { return s.fileSystem.ReadFile(name.String()) } // Remove implements System.Remove. func (s *RealSystem) Remove(name AbsPath) error { return s.fileSystem.Remove(name.String()) } // RemoveAll implements System.RemoveAll. func (s *RealSystem) RemoveAll(name AbsPath) error { return s.fileSystem.RemoveAll(name.String()) } // Rename implements System.Rename. func (s *RealSystem) Rename(oldPath, newPath AbsPath) error { return s.fileSystem.Rename(oldPath.String(), newPath.String()) } // RunCmd implements System.RunCmd. func (s *RealSystem) RunCmd(cmd *exec.Cmd) error { return chezmoilog.LogCmdRun(slog.Default(), cmd) } // RunScript implements System.RunScript. func (s *RealSystem) RunScript(scriptName RelPath, dir AbsPath, data []byte, options RunScriptOptions) (err error) { // Create the script temporary directory, if needed. s.createScriptTempDirOnce.Do(func() { if !s.scriptTempDir.IsEmpty() { err = os.MkdirAll(s.scriptTempDir.String(), 0o700) } }) if err != nil { return err } // Write the temporary script file. Put the randomness at the front of the // filename to preserve any file extension for Windows scripts. var f *os.File f, err = os.CreateTemp(s.scriptTempDir.String(), "*."+scriptName.Base()) if err != nil { return err } defer chezmoierrors.CombineFunc(&err, func() error { return os.RemoveAll(f.Name()) }) // Make the script private before writing it in case it contains any // secrets. if runtime.GOOS != "windows" { if err := f.Chmod(0o700); err != nil { return err } } _, err = f.Write(data) err = chezmoierrors.Combine(err, f.Close()) if err != nil { return err } cmd := options.Interpreter.ExecCommand(f.Name()) cmd.Dir, err = s.getScriptWorkingDir(dir) if err != nil { return err } cmd.Env = append(os.Environ(), "CHEZMOI_SOURCE_FILE="+options.SourceRelPath.String(), ) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return s.RunCmd(cmd) } // Stat implements System.Stat. func (s *RealSystem) Stat(name AbsPath) (fs.FileInfo, error) { return s.fileSystem.Stat(name.String()) } // UnderlyingFS implements System.UnderlyingFS. func (s *RealSystem) UnderlyingFS() vfs.FS { return s.fileSystem } // getScriptWorkingDir returns the script's working directory. // // If this is a before_ script then the requested working directory may not // actually exist yet, so search through the parent directory hierarchy until // we find a suitable working directory. func (s *RealSystem) getScriptWorkingDir(dir AbsPath) (string, error) { // This should always terminate because dir will eventually become ".", i.e. // the current directory. for { switch fileInfo, err := s.Stat(dir); { case err == nil && fileInfo.IsDir(): // dir exists and is a directory. Use it. dirRawAbsPath, err := s.RawPath(dir) if err != nil { return "", err } return dirRawAbsPath.String(), nil case err == nil || errors.Is(err, fs.ErrNotExist): // Either dir does not exist, or it exists and is not a directory. dir = dir.Dir() default: // Some other error occurred. return "", err } } } ================================================ FILE: internal/chezmoi/realsystem_test.go ================================================ package chezmoi import ( "path/filepath" "slices" "testing" "github.com/alecthomas/assert/v2" vfs "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoitest" ) var _ System = &RealSystem{} func TestRealSystemGlob(t *testing.T) { chezmoitest.WithTestFS(t, map[string]any{ "/home/user": map[string]any{ "bar": "", "baz": "", "foo": "", "dir/bar": "", "dir/foo": "", "dir/subdir/foo": "", }, }, func(fileSystem vfs.FS) { system := NewRealSystem(fileSystem) for _, tc := range []struct { pattern string expectedMatches []string }{ { pattern: "/home/user/foo", expectedMatches: []string{ "/home/user/foo", }, }, { pattern: "/home/user/**/foo", expectedMatches: []string{ "/home/user/dir/foo", "/home/user/dir/subdir/foo", "/home/user/foo", }, }, { pattern: "/home/user/**/ba*", expectedMatches: []string{ "/home/user/bar", "/home/user/baz", "/home/user/dir/bar", }, }, } { t.Run(tc.pattern, func(t *testing.T) { actualMatches, err := system.Glob(tc.pattern) assert.NoError(t, err) slices.Sort(actualMatches) assert.Equal(t, tc.expectedMatches, pathsToSlashes(actualMatches)) }) } }) } func pathsToSlashes(paths []string) []string { result := make([]string, 0, len(paths)) for _, path := range paths { result = append(result, filepath.ToSlash(path)) } return result } ================================================ FILE: internal/chezmoi/realsystem_unix.go ================================================ //go:build unix package chezmoi import ( "errors" "io/fs" "os" "sync" "syscall" "github.com/google/renameio/v2" vfs "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoierrors" ) // An RealSystem is a System that writes to a filesystem and executes scripts. type RealSystem struct { fileSystem vfs.FS safe bool createScriptTempDirOnce sync.Once scriptTempDir AbsPath devCache map[AbsPath]uint // devCache maps directories to device numbers. tempDirCache map[uint]string // tempDirCache maps device numbers to renameio temporary directories. } // RealSystemWithSafe sets the safe flag of the RealSystem. func RealSystemWithSafe(safe bool) RealSystemOption { return func(s *RealSystem) { s.safe = safe } } // RealSystemWithScriptTempDir sets the script temporary directory of the // RealSystem. func RealSystemWithScriptTempDir(scriptTempDir AbsPath) RealSystemOption { return func(s *RealSystem) { s.scriptTempDir = scriptTempDir } } // NewRealSystem returns a System that acts on fileSystem. func NewRealSystem(fileSystem vfs.FS, options ...RealSystemOption) *RealSystem { s := &RealSystem{ fileSystem: fileSystem, safe: true, devCache: make(map[AbsPath]uint), tempDirCache: make(map[uint]string), } for _, option := range options { option(s) } return s } // Chmod implements System.Chmod. func (s *RealSystem) Chmod(name AbsPath, mode fs.FileMode) error { return s.fileSystem.Chmod(name.String(), mode) } // Readlink implements System.Readlink. func (s *RealSystem) Readlink(name AbsPath) (string, error) { return s.fileSystem.Readlink(name.String()) } // WriteFile implements System.WriteFile. func (s *RealSystem) WriteFile(filename AbsPath, data []byte, perm fs.FileMode) (err error) { // Special case: if writing to the real filesystem in safe mode, use // github.com/google/renameio. if s.safe && s.fileSystem == vfs.OSFS { dir := filename.Dir() dev, ok := s.devCache[dir] if !ok { var fileInfo fs.FileInfo fileInfo, err = s.Stat(dir) if err != nil { return err } statT, ok := fileInfo.Sys().(*syscall.Stat_t) if !ok { return errors.New("fs.FileInfo.Sys() cannot be converted to a *syscall.Stat_t") } dev = uint(statT.Dev) s.devCache[dir] = dev } tempDir, ok := s.tempDirCache[dev] if !ok { tempDir = renameio.TempDir(dir.String()) s.tempDirCache[dev] = tempDir } var t *renameio.PendingFile if t, err = renameio.TempFile(tempDir, filename.String()); err != nil { return err } defer chezmoierrors.CombineFunc(&err, t.Cleanup) if err := t.Chmod(perm); err != nil { return err } if _, err := t.Write(data); err != nil { return err } return t.CloseAtomicallyReplace() } return writeFile(s.fileSystem, filename, data, perm) } // WriteSymlink implements System.WriteSymlink. func (s *RealSystem) WriteSymlink(oldName string, newName AbsPath) error { // Special case: if writing to the real filesystem in safe mode, use // github.com/google/renameio. if s.safe && s.fileSystem == vfs.OSFS { return renameio.Symlink(oldName, newName.String()) } if err := s.fileSystem.RemoveAll(newName.String()); err != nil && !errors.Is(err, fs.ErrNotExist) { return err } return s.fileSystem.Symlink(oldName, newName.String()) } // writeFile is like [os.WriteFile] but always sets perm before writing data. // [os.WriteFile] only sets the permissions when creating a new file. We need to // ensure permissions, so we use our own implementation. func writeFile(fileSystem vfs.FS, filename AbsPath, data []byte, perm fs.FileMode) (err error) { // Create a new file, or truncate any existing one. var f *os.File if f, err = fileSystem.OpenFile(filename.String(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm); err != nil { return err } defer chezmoierrors.CombineFunc(&err, f.Close) // Set permissions after truncation but before writing any data, in case the // file contained private data before, but before writing the new contents, // in case the new contents contain private data after. if err := f.Chmod(perm); err != nil { return err } _, err = f.Write(data) return err } ================================================ FILE: internal/chezmoi/realsystem_windows.go ================================================ package chezmoi import ( "errors" "io/fs" "path/filepath" "sync" vfs "github.com/twpayne/go-vfs/v5" ) // An RealSystem is a System that writes to a filesystem and executes scripts. type RealSystem struct { fileSystem vfs.FS createScriptTempDirOnce sync.Once scriptTempDir AbsPath } // RealSystemWithSafe sets the safe flag of the RealSystem. On Windows it does // nothing as Windows does not support atomic file or symlink updates. See // https://github.com/google/renameio/issues/1 and // https://github.com/golang/go/issues/22397#issuecomment-498856679. func RealSystemWithSafe(safe bool) RealSystemOption { return func(s *RealSystem) {} } // RealSystemWithScriptTempDir sets the script temporary directory of the // RealSystem. func RealSystemWithScriptTempDir(scriptTempDir AbsPath) RealSystemOption { return func(s *RealSystem) {} } // NewRealSystem returns a System that acts on fs. func NewRealSystem(fileSystem vfs.FS, options ...RealSystemOption) *RealSystem { s := &RealSystem{ fileSystem: fileSystem, } for _, option := range options { option(s) } return s } // Chmod implements System.Chmod. func (s *RealSystem) Chmod(name AbsPath, mode fs.FileMode) error { return nil } // Readlink implements System.Readlink. func (s *RealSystem) Readlink(name AbsPath) (string, error) { linkname, err := s.fileSystem.Readlink(name.String()) if err != nil { return "", err } return normalizeLinkname(linkname), nil } // WriteFile implements System.WriteFile. func (s *RealSystem) WriteFile(filename AbsPath, data []byte, perm fs.FileMode) error { return s.fileSystem.WriteFile(filename.String(), data, perm) } // WriteSymlink implements System.WriteSymlink. func (s *RealSystem) WriteSymlink(oldName string, newName AbsPath) error { if err := s.fileSystem.RemoveAll(newName.String()); err != nil && !errors.Is(err, fs.ErrNotExist) { return err } return s.fileSystem.Symlink(filepath.FromSlash(oldName), newName.String()) } ================================================ FILE: internal/chezmoi/recursivemerge.go ================================================ package chezmoi // recursiveCopy returns a recursive copy of v. func recursiveCopy(v any) any { m, ok := v.(map[string]any) if !ok { return v } c := make(map[string]any) for key, value := range m { if mapValue, ok := value.(map[string]any); ok { c[key] = recursiveCopy(mapValue) } else { c[key] = value } } return c } // RecursiveMerge recursively merges maps in source into dest. func RecursiveMerge(dest, source map[string]any) { for key, sourceValue := range source { destValue, ok := dest[key] if !ok { dest[key] = recursiveCopy(sourceValue) continue } destMap, ok := destValue.(map[string]any) if !ok || destMap == nil { dest[key] = recursiveCopy(sourceValue) continue } sourceMap, ok := sourceValue.(map[string]any) if !ok { dest[key] = recursiveCopy(sourceValue) continue } RecursiveMerge(destMap, sourceMap) } } ================================================ FILE: internal/chezmoi/recursivemerge_test.go ================================================ package chezmoi import ( "testing" "github.com/alecthomas/assert/v2" ) func TestRecursiveMerge(t *testing.T) { for _, tc := range []struct { dest map[string]any source map[string]any expectedDest map[string]any }{ { dest: map[string]any{}, source: nil, expectedDest: map[string]any{}, }, { dest: map[string]any{ "a": 1, "b": 2, "c": map[string]any{ "d": 4, "e": 5, }, "f": map[string]any{ "g": 6, }, }, source: map[string]any{ "b": 20, "c": map[string]any{ "e": 50, "f": 60, }, "f": 60, }, expectedDest: map[string]any{ "a": 1, "b": 20, "c": map[string]any{ "d": 4, "e": 50, "f": 60, }, "f": 60, }, }, } { RecursiveMerge(tc.dest, tc.source) assert.Equal(t, tc.expectedDest, tc.dest) } } func TestRecursiveMergeCopies(t *testing.T) { original := map[string]any{ "key": "initialValue", } dest := make(map[string]any) RecursiveMerge(dest, original) RecursiveMerge(dest, map[string]any{ "key": "mergedValue", }) assert.Equal(t, "mergedValue", dest["key"]) assert.Equal(t, "initialValue", original["key"]) } ================================================ FILE: internal/chezmoi/refreshexternals.go ================================================ package chezmoi import ( "fmt" "maps" "slices" "strings" ) type RefreshExternals int const ( RefreshExternalsAuto RefreshExternals = iota RefreshExternalsAlways RefreshExternalsNever ) var ( refreshExternalsWellKnownStrings = map[string]RefreshExternals{ "always": RefreshExternalsAlways, "auto": RefreshExternalsAuto, "never": RefreshExternalsNever, } RefreshExternalsFlagCompletionFunc = FlagCompletionFunc(slices.Sorted(maps.Keys(refreshExternalsWellKnownStrings))) ) func (re *RefreshExternals) Set(s string) error { if value, ok := refreshExternalsWellKnownStrings[strings.ToLower(s)]; ok { *re = value return nil } switch value, err := ParseBool(s); { case err != nil: return err case value: *re = RefreshExternalsAlways return nil default: *re = RefreshExternalsNever return nil } } func (re RefreshExternals) String() string { switch re { case RefreshExternalsAlways: return "always" case RefreshExternalsAuto: return "auto" case RefreshExternalsNever: return "never" default: panic(fmt.Sprintf("%d: invalid RefreshExternals value", re)) } } func (re RefreshExternals) Type() string { return "always|auto|never" } ================================================ FILE: internal/chezmoi/relpath.go ================================================ package chezmoi import ( "cmp" "path" "strings" ) var ( DotRelPath = NewRelPath(".") EmptyRelPath = NewRelPath("") ) // A RelPath is a relative path. type RelPath struct { relPath string } // NewRelPath returns a new RelPath. func NewRelPath(relPath string) RelPath { return RelPath{ relPath: relPath, } } // AppendString appends s to p. func (p RelPath) AppendString(s string) RelPath { return NewRelPath(p.relPath + s) } // Base returns p's base name. func (p RelPath) Base() string { return path.Base(p.relPath) } // Dir returns p's directory. func (p RelPath) Dir() RelPath { return NewRelPath(path.Dir(p.relPath)) } // IsEmpty returns if p is empty. func (p RelPath) IsEmpty() bool { return p.relPath == "" } // Ext returns p's extension. func (p RelPath) Ext() string { return path.Ext(p.relPath) } // HasDirPrefix returns true if p has dir prefix dirPrefix. func (p RelPath) HasDirPrefix(dirPrefix RelPath) bool { return strings.HasPrefix(p.relPath, dirPrefix.String()+"/") } // Join appends relPaths to p. func (p RelPath) Join(relPaths ...RelPath) RelPath { relPathStrs := make([]string, 0, len(relPaths)+1) if p.relPath != "" { relPathStrs = append(relPathStrs, p.relPath) } for _, relPath := range relPaths { relPathStrs = append(relPathStrs, relPath.String()) } return NewRelPath(path.Join(relPathStrs...)) } // JoinString returns a new RelPath with ss appended. func (p RelPath) JoinString(ss ...string) RelPath { strs := make([]string, len(ss)+1) strs[0] = p.relPath copy(strs[1:len(ss)+1], ss) return NewRelPath(path.Join(strs...)) } // Len returns the length of p. func (p RelPath) Len() int { return len(p.relPath) } // MarshalText implements encoding.TextMarshaler.MarshalText. func (p RelPath) MarshalText() ([]byte, error) { return []byte(p.relPath), nil } // Slice returns a part of p. func (p RelPath) Slice(begin, end int) RelPath { return NewRelPath(p.relPath[begin:end]) } // SourceRelPath returns p as a SourceRelPath. func (p RelPath) SourceRelPath() SourceRelPath { return NewSourceRelPath(p.relPath) } // SourceRelDirPath returns p as a directory SourceRelPath. func (p RelPath) SourceRelDirPath() SourceRelPath { return NewSourceRelDirPath(p.relPath) } // Split returns p's directory and path. func (p RelPath) Split() (dirRelPath, fileRelPath RelPath) { dir, file := path.Split(p.relPath) return NewRelPath(dir), NewRelPath(file) } // SplitAll returns p's components. func (p RelPath) SplitAll() []RelPath { components := strings.Split(p.relPath, "/") relPaths := make([]RelPath, len(components)) for i, component := range components { relPaths[i] = NewRelPath(component) } return relPaths } func (p RelPath) String() string { return p.relPath } // TrimDirPrefix trims prefix from p. func (p RelPath) TrimDirPrefix(dirPrefix RelPath) (RelPath, error) { if !p.HasDirPrefix(dirPrefix) { return EmptyRelPath, &NotInRelDirError{ pathRelPath: p, dirRelPath: dirPrefix, } } return p.Slice(dirPrefix.Len()+1, p.Len()), nil } // CompareRelPaths compares a and b. func CompareRelPaths(a, b RelPath) int { return cmp.Compare(a.relPath, b.relPath) } ================================================ FILE: internal/chezmoi/sourcerelpath.go ================================================ package chezmoi import ( "path" "strings" ) var emptySourceRelPath SourceRelPath // A SourceRelPath is a relative path to an entry in the source state. type SourceRelPath struct { relPath RelPath isDir bool } // NewSourceRelDirPath returns a new SourceRelPath for a directory. func NewSourceRelDirPath(relPath string) SourceRelPath { return SourceRelPath{ relPath: NewRelPath(relPath), isDir: true, } } // NewSourceRelPath returns a new SourceRelPath. func NewSourceRelPath(relPath string) SourceRelPath { return SourceRelPath{ relPath: NewRelPath(relPath), } } // Dir returns p's directory. func (p SourceRelPath) Dir() SourceRelPath { return SourceRelPath{ relPath: p.relPath.Dir(), isDir: true, } } // IsEmpty returns true if p is empty. func (p SourceRelPath) IsEmpty() bool { return p == SourceRelPath{} } // Join appends sourceRelPaths to p. func (p SourceRelPath) Join(sourceRelPaths ...SourceRelPath) SourceRelPath { if len(sourceRelPaths) == 0 { return p } relPaths := make([]RelPath, len(sourceRelPaths)) for i, sourceRelPath := range sourceRelPaths { relPaths[i] = sourceRelPath.relPath } return SourceRelPath{ relPath: p.relPath.Join(relPaths...), isDir: sourceRelPaths[len(sourceRelPaths)-1].isDir, } } // MarshalText implements encoding.TextMarshaler.MarshalText. func (p SourceRelPath) MarshalText() ([]byte, error) { return []byte(p.relPath.String()), nil } // RelPath returns p as a relative path. func (p SourceRelPath) RelPath() RelPath { return p.relPath } // Split returns the p's file and directory. func (p SourceRelPath) Split() (dirSourceRelPath, fileSourceRelPath SourceRelPath) { dir, file := p.relPath.Split() return NewSourceRelDirPath(dir.String()), NewSourceRelPath(file.String()) } func (p SourceRelPath) String() string { return p.relPath.String() } // TargetRelPath returns the relative path of p's target. func (p SourceRelPath) TargetRelPath(encryptedSuffix string) (RelPath, error) { sourceNames := strings.Split(p.relPath.String(), "/") relPathStrs := make([]string, 0, len(sourceNames)) if p.isDir { for i, sourceName := range sourceNames { if i == 0 && sourceName == "" { // Special case: we allow the first element to be empty. relPathStrs = append(relPathStrs, "") continue } dirAttr, err := parseDirAttr(sourceName) if err != nil { return RelPath{}, err } relPathStrs = append(relPathStrs, dirAttr.TargetName) } } else { for _, sourceName := range sourceNames[:len(sourceNames)-1] { dirAttr, err := parseDirAttr(sourceName) if err != nil { return RelPath{}, err } relPathStrs = append(relPathStrs, dirAttr.TargetName) } fileAttr, err := parseFileAttr(sourceNames[len(sourceNames)-1], encryptedSuffix) if err != nil { return RelPath{}, err } relPathStrs = append(relPathStrs, fileAttr.TargetName) } return NewRelPath(path.Join(relPathStrs...)), nil } ================================================ FILE: internal/chezmoi/sourcerelpath_test.go ================================================ package chezmoi import ( "testing" "github.com/alecthomas/assert/v2" ) func TestSourceRelPath(t *testing.T) { for _, tc := range []struct { name string sourceStatePath SourceRelPath expectedDirPath SourceRelPath expectedTargetRelPath RelPath }{ { name: "dir", sourceStatePath: NewSourceRelDirPath("dir"), expectedDirPath: NewSourceRelDirPath("."), expectedTargetRelPath: NewRelPath("dir"), }, { name: "exact_dir", sourceStatePath: NewSourceRelDirPath("exact_dir"), expectedDirPath: NewSourceRelDirPath("."), expectedTargetRelPath: NewRelPath("dir"), }, { name: "exact_dir_private_dir", sourceStatePath: NewSourceRelDirPath("exact_dir/private_dir"), expectedDirPath: NewSourceRelDirPath("exact_dir"), expectedTargetRelPath: NewRelPath("dir/dir"), }, { name: "file", sourceStatePath: NewSourceRelPath("file"), expectedDirPath: NewSourceRelDirPath("."), expectedTargetRelPath: NewRelPath("file"), }, { name: "dot_file", sourceStatePath: NewSourceRelPath("dot_file"), expectedDirPath: NewSourceRelDirPath("."), expectedTargetRelPath: NewRelPath(".file"), }, { name: "exact_dir_executable_file", sourceStatePath: NewSourceRelPath("exact_dir/executable_file"), expectedDirPath: NewSourceRelDirPath("exact_dir"), expectedTargetRelPath: NewRelPath("dir/file"), }, } { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expectedDirPath, tc.sourceStatePath.Dir()) targetRelPath, err := tc.sourceStatePath.TargetRelPath("") assert.NoError(t, err) assert.Equal(t, tc.expectedTargetRelPath, targetRelPath) }) } } ================================================ FILE: internal/chezmoi/sourcestate.go ================================================ package chezmoi // FIXME implement externals in chezmoi source state format import ( "bytes" "cmp" "context" "crypto/sha256" "encoding/hex" "errors" "fmt" "io" "io/fs" "log/slog" "maps" "net/http" "net/url" "os" "os/exec" "path" "path/filepath" "regexp" "runtime" "slices" "strings" "sync" "syscall" "text/template" "time" "unicode/utf8" "github.com/coreos/go-semver/semver" "github.com/mitchellh/copystructure" "chezmoi.io/chezmoi/internal/chezmoierrors" "chezmoi.io/chezmoi/internal/chezmoilog" "chezmoi.io/chezmoi/internal/chezmoiset" ) // An ExternalType is a type of external source. type ExternalType string // ExternalTypes. const ( ExternalTypeArchive ExternalType = "archive" ExternalTypeArchiveFile ExternalType = "archive-file" ExternalTypeFile ExternalType = "file" ExternalTypeGitRepo ExternalType = "git-repo" ) var ( commentRx = regexp.MustCompile(`(?:\A|\s+)#.*(?:\r?\n)?$`) lineEndingRx = regexp.MustCompile(`(?m)(?:\r\n|\r|\n)`) modifyTemplateRx = regexp.MustCompile(`(?m)^.*chezmoi:modify-template.*$(?:\r?\n)?`) templateDirectiveRx = regexp.MustCompile(`(?m)^.*?chezmoi:template:(.*)$(?:\r?\n)?`) templateDirectiveKeyValuePairRx = regexp.MustCompile(`\s*(\S+)=("(?:[^"]|\\")*"|\S+)`) // AppleDouble constants. appleDoubleNamePrefix = "._" appleDoubleContentsPrefix = []byte{0x00, 0x05, 0x16, 0x07, 0x00, 0x02, 0x00, 0x00} ) type ExternalArchive struct { ExtractAppleDoubleFiles bool `json:"extractAppleDoubleFiles" toml:"extractAppleDoubleFiles" yaml:"extractAppleDoubleFiles"` } type ExternalChecksum struct { MD5 HexBytes `json:"md5" toml:"md5" yaml:"md5"` RIPEMD160 HexBytes `json:"ripemd160" toml:"ripemd160" yaml:"ripemd160"` SHA1 HexBytes `json:"sha1" toml:"sha1" yaml:"sha1"` SHA256 HexBytes `json:"sha256" toml:"sha256" yaml:"sha256"` SHA384 HexBytes `json:"sha384" toml:"sha384" yaml:"sha384"` SHA512 HexBytes `json:"sha512" toml:"sha512" yaml:"sha512"` Size int `json:"size" toml:"size" yaml:"size"` } type ExternalClone struct { Args []string `json:"args" toml:"args" yaml:"args"` } type ExternalFilter struct { Command string `json:"command" toml:"command" yaml:"command"` Args []string `json:"args" toml:"args" yaml:"args"` } type ExternalPull struct { Args []string `json:"args" toml:"args" yaml:"args"` } // A WarnFunc is a function that warns the user. type WarnFunc func(string, ...any) // An External is an external source. type External struct { Type ExternalType `json:"type" toml:"type" yaml:"type"` Encrypted bool `json:"encrypted" toml:"encrypted" yaml:"encrypted"` Exact bool `json:"exact" toml:"exact" yaml:"exact"` Executable bool `json:"executable" toml:"executable" yaml:"executable"` Private bool `json:"private" toml:"private" yaml:"private"` ReadOnly bool `json:"readonly" toml:"readonly" yaml:"readonly"` Checksum ExternalChecksum `json:"checksum" toml:"checksum" yaml:"checksum"` Clone ExternalClone `json:"clone" toml:"clone" yaml:"clone"` Decompress CompressionFormat `json:"decompress" toml:"decompress" yaml:"decompress"` Exclude []string `json:"exclude" toml:"exclude" yaml:"exclude"` Filter ExternalFilter `json:"filter" toml:"filter" yaml:"filter"` Format ArchiveFormat `json:"format" toml:"format" yaml:"format"` Archive ExternalArchive `json:"archive" toml:"archive" yaml:"archive"` Include []string `json:"include" toml:"include" yaml:"include"` ArchivePath string `json:"path" toml:"path" yaml:"path"` Pull ExternalPull `json:"pull" toml:"pull" yaml:"pull"` RefreshPeriod Duration `json:"refreshPeriod" toml:"refreshPeriod" yaml:"refreshPeriod"` StripComponents int `json:"stripComponents" toml:"stripComponents" yaml:"stripComponents"` URL string `json:"url" toml:"url" yaml:"url"` URLs []string `json:"urls" toml:"urls" yaml:"urls"` TargetPath string `json:"targetPath" toml:"targetPath" yaml:"targetPath"` sourceAbsPath AbsPath } // A SourceState is a source state. type SourceState struct { mutex sync.Mutex root SourceStateEntryTreeNode removeDirs chezmoiset.Set[RelPath] baseSystem System system System sourceDirAbsPath AbsPath destDirAbsPath AbsPath cacheDirAbsPath AbsPath createScriptTempDirOnce sync.Once scriptTempDirAbsPath AbsPath umask fs.FileMode encryption Encryption ignore *PatternSet remove *PatternSet interpreters map[string]Interpreter httpClient *http.Client logger *slog.Logger version semver.Version mode Mode defaultTemplateDataFunc func() map[string]any templateDataOnly bool readTemplateData bool readTemplates bool defaultTemplateData map[string]any userTemplateData map[string]any priorityTemplateData map[string]any templateData map[string]any templateFuncs template.FuncMap templateOptions []string templates map[string]*Template externals map[RelPath][]*External ignoredRelPaths chezmoiset.Set[RelPath] warnFunc WarnFunc } // A SourceStateOption sets an option on a source state. type SourceStateOption func(*SourceState) // WithBaseSystem sets the base system. func WithBaseSystem(baseSystem System) SourceStateOption { return func(s *SourceState) { s.baseSystem = baseSystem } } // WithCacheDir sets the cache directory. func WithCacheDir(cacheDirAbsPath AbsPath) SourceStateOption { return func(s *SourceState) { s.cacheDirAbsPath = cacheDirAbsPath } } // WithDefaultTemplateDataFunc sets the default template data function. func WithDefaultTemplateDataFunc(defaultTemplateDataFunc func() map[string]any) SourceStateOption { return func(s *SourceState) { s.defaultTemplateDataFunc = defaultTemplateDataFunc } } // WithDestDir sets the destination directory. func WithDestDir(destDirAbsPath AbsPath) SourceStateOption { return func(s *SourceState) { s.destDirAbsPath = destDirAbsPath } } // WithEncryption sets the encryption. func WithEncryption(encryption Encryption) SourceStateOption { return func(s *SourceState) { s.encryption = encryption } } // WithHTTPClient sets the HTTP client. func WithHTTPClient(httpClient *http.Client) SourceStateOption { return func(s *SourceState) { s.httpClient = httpClient } } // WithInterpreters sets the interpreters. func WithInterpreters(interpreters map[string]Interpreter) SourceStateOption { return func(s *SourceState) { s.interpreters = interpreters } } // WithLogger sets the logger. func WithLogger(logger *slog.Logger) SourceStateOption { return func(s *SourceState) { s.logger = logger } } // WithMode sets the mode. func WithMode(mode Mode) SourceStateOption { return func(s *SourceState) { s.mode = mode } } // WithPriorityTemplateData adds priority template data. func WithPriorityTemplateData(priorityTemplateData map[string]any) SourceStateOption { return func(s *SourceState) { RecursiveMerge(s.priorityTemplateData, priorityTemplateData) } } // WithReadTemplateData sets whether to read .chezmoidata. files. func WithReadTemplateData(readTemplateData bool) SourceStateOption { return func(s *SourceState) { s.readTemplateData = readTemplateData } } // WithReadTemplates sets whether to read .chezmoitemplates directories. func WithReadTemplates(readTemplates bool) SourceStateOption { return func(s *SourceState) { s.readTemplates = readTemplates } } // WithScriptTempDir sets the source directory. func WithScriptTempDir(scriptDirAbsPath AbsPath) SourceStateOption { return func(s *SourceState) { s.scriptTempDirAbsPath = scriptDirAbsPath } } // WithSourceDir sets the source directory. func WithSourceDir(sourceDirAbsPath AbsPath) SourceStateOption { return func(s *SourceState) { s.sourceDirAbsPath = sourceDirAbsPath } } // WithSystem sets the system. func WithSystem(system System) SourceStateOption { return func(s *SourceState) { s.system = system } } // WithTemplateDataOnly sets whether only template data should be read. func WithTemplateDataOnly(templateDataOnly bool) SourceStateOption { return func(s *SourceState) { s.templateDataOnly = templateDataOnly } } // WithTemplateFuncs sets the template functions. func WithTemplateFuncs(templateFuncs template.FuncMap) SourceStateOption { return func(s *SourceState) { s.templateFuncs = templateFuncs } } // WithTemplateOptions sets the template options. func WithTemplateOptions(templateOptions []string) SourceStateOption { return func(s *SourceState) { s.templateOptions = templateOptions } } // WithUmask sets the umask. func WithUmask(umask fs.FileMode) SourceStateOption { return func(s *SourceState) { s.umask = umask } } // WithVersion sets the version. func WithVersion(version semver.Version) SourceStateOption { return func(s *SourceState) { s.version = version } } // WithWarnFunc sets the warning function. func WithWarnFunc(warnFunc WarnFunc) SourceStateOption { return func(s *SourceState) { s.warnFunc = warnFunc } } // A TargetStateEntryFunc returns a TargetStateEntry based on reading an AbsPath // on a System. type TargetStateEntryFunc func(System, AbsPath) (TargetStateEntry, error) // NewSourceState creates a new source state with the given options. func NewSourceState(options ...SourceStateOption) *SourceState { s := &SourceState{ removeDirs: chezmoiset.New[RelPath](), umask: Umask, encryption: NoEncryption{}, ignore: NewPatternSet(), remove: NewPatternSet(), httpClient: http.DefaultClient, logger: slog.Default(), readTemplateData: true, readTemplates: true, priorityTemplateData: make(map[string]any), userTemplateData: make(map[string]any), templateOptions: DefaultTemplateOptions, templates: make(map[string]*Template), externals: make(map[RelPath][]*External), ignoredRelPaths: chezmoiset.New[RelPath](), } for _, option := range options { option(s) } return s } // A PreAddFunc is called before a new source state entry is added. type PreAddFunc func(targetRelPath RelPath, fileInfo fs.FileInfo) error // A ReplaceFunc is called before a source state entry is replaced. type ReplaceFunc func(targetRelPath RelPath, newSourceStateEntry, oldSourceStateEntry SourceStateEntry) error // AddOptions are options to SourceState.Add. type AddOptions struct { AutoTemplate bool // Automatically create templates, if possible. Create bool // Add create_ entries instead of normal entries. Encrypt bool // Encrypt files. EncryptedSuffix string // Suffix for encrypted files. Errorf func(string, ...any) // Function to print errors. Exact bool // Add the exact_ attribute to added directories. ExactTargetRelPaths chezmoiset.Set[RelPath] // Paths that should be marked exact (if nil and Exact is true, all dirs are exact). Filter *EntryTypeFilter // Entry type filter. OnIgnoreFunc func(RelPath) // Function to call when a target is ignored. PreAddFunc PreAddFunc // Function to be called before a source entry is added. ConfigFileAbsPath AbsPath // Path to config file. ProtectedAbsPaths []AbsPath // Paths that must not be added. RemoveDir RelPath // Directory to remove before adding. ReplaceFunc ReplaceFunc // Function to be called before a source entry is replaced. Template bool // Add the .tmpl attribute to added files. TemplateSymlinks bool // Add symlinks with targets in the source or home directories as templates. } // shouldBeExact returns true if targetRelPath should have the exact attribute. // A path is exact if Exact is true AND either ExactTargetRelPaths is nil // (legacy behavior) or the path equals or is a descendant of an explicit // exact target. func (o *AddOptions) shouldBeExact(targetRelPath RelPath) bool { if !o.Exact { return false } // If ExactTargetRelPaths is nil, use legacy behavior (all dirs are exact) if o.ExactTargetRelPaths == nil { return true } // Check if targetRelPath equals or is a descendant of any exact target for exactTarget := range o.ExactTargetRelPaths { if targetRelPath == exactTarget || targetRelPath.HasDirPrefix(exactTarget) { return true } } return false } // Add adds destAbsPathInfos to s. func (s *SourceState) Add( sourceSystem System, persistentState PersistentState, destSystem System, destAbsPathInfos map[AbsPath]fs.FileInfo, options *AddOptions, ) error { // Filter out excluded and ignored paths. destAbsPaths := slices.Sorted(maps.Keys(destAbsPathInfos)) n := 0 for _, destAbsPath := range destAbsPaths { switch destAbsPathInfo := destAbsPathInfos[destAbsPath]; { case destAbsPathInfo == nil: // If the destination does not exist then create a new, empty file, // unless files are excluded. if !options.Filter.IncludeEntryTypeBits(EntryTypeFiles) { continue } case !options.Filter.IncludeFileInfo(destAbsPathInfo): continue } targetRelPath := destAbsPath.MustTrimDirPrefix(s.destDirAbsPath) if s.Ignore(targetRelPath) { if options.OnIgnoreFunc != nil { options.OnIgnoreFunc(targetRelPath) } continue } destAbsPaths[n] = destAbsPath n++ } destAbsPaths = destAbsPaths[:n] // Check for protected paths. for _, destAbsPath := range destAbsPaths { if destAbsPath == options.ConfigFileAbsPath { format := "%s: cannot add chezmoi's config file to chezmoi, use a config file template instead" return fmt.Errorf(format, destAbsPath) } } for _, destAbsPath := range destAbsPaths { for _, protectedAbsPath := range options.ProtectedAbsPaths { if protectedAbsPath.IsEmpty() { continue } if destAbsPath.HasDirPrefix(protectedAbsPath) { format := "%s: cannot add chezmoi file to chezmoi (%s is protected)" return fmt.Errorf(format, destAbsPath, protectedAbsPath) } } } type sourceUpdate struct { destAbsPath AbsPath entryState *EntryState sourceRelPaths []SourceRelPath } sourceUpdates := make([]sourceUpdate, 0, len(destAbsPaths)) newSourceStateEntries := make(map[SourceRelPath]SourceStateEntry) newSourceStateEntriesByTargetRelPath := make(map[RelPath]SourceStateEntry) nonEmptyDirs := chezmoiset.New[SourceRelPath]() externalDirRelPaths := chezmoiset.New[RelPath]() dirRenames := make(map[AbsPath]AbsPath) DEST_ABS_PATH: for _, destAbsPath := range destAbsPaths { targetRelPath := destAbsPath.MustTrimDirPrefix(s.destDirAbsPath) // Skip any entries in known external dirs. for externalDir := range externalDirRelPaths { if targetRelPath.HasDirPrefix(externalDir) { continue DEST_ABS_PATH } } // Find the target's parent directory in the source state. var parentSourceRelPath SourceRelPath if targetParentRelPath := targetRelPath.Dir(); targetParentRelPath == DotRelPath { parentSourceRelPath = SourceRelPath{} } else if parentEntry, ok := newSourceStateEntriesByTargetRelPath[targetParentRelPath]; ok { parentSourceRelPath = parentEntry.SourceRelPath() } else if nodes := s.root.GetNodes(targetParentRelPath); nodes != nil { for i, node := range nodes { if i == 0 { // nodes[0].sourceStateEntry should always be nil because it // refers to the destination directory, which is not managed. // chezmoi manages the destination directory's contents, not // the destination directory itself. For example, chezmoi // does not set the name or permissions of the user's home // directory. if node.SourceStateEntry != nil { panic(fmt.Errorf("nodes[0]: expected nil, got %+v", node.SourceStateEntry)) } continue } switch sourceStateDir, ok := node.SourceStateEntry.(*SourceStateDir); { case i != len(nodes)-1 && !ok: panic(fmt.Errorf("nodes[%d]: unexpected non-terminal source state entry, got %T", i, node.SourceStateEntry)) case ok && sourceStateDir.attr.External: targetRelPathComponents := targetRelPath.SplitAll() externalDirRelPath := EmptyRelPath.Join(targetRelPathComponents[:i]...) externalDirRelPaths.Add(externalDirRelPath) if options.Errorf != nil { options.Errorf("%s: skipping entries in external_ directory\n", externalDirRelPath) } continue DEST_ABS_PATH } } parentSourceRelPath = nodes[len(nodes)-1].SourceStateEntry.SourceRelPath() } else { return fmt.Errorf("%s: parent directory not in source state", destAbsPath) } nonEmptyDirs.Add(parentSourceRelPath) destAbsPathInfo := destAbsPathInfos[destAbsPath] var actualStateEntry ActualStateEntry var newSourceStateEntry SourceStateEntry if destAbsPathInfo == nil { // If the destination does not exist then create a new, empty file. _, relPath := destAbsPath.Split() actualStateEntry = &ActualStateAbsent{ absPath: destAbsPath, } fileAttr := FileAttr{ TargetName: relPath.String(), Type: SourceFileTypeFile, Encrypted: options.Encrypt, Empty: true, Template: options.Template, } sourceRelPath := parentSourceRelPath.Join(NewSourceRelPath(fileAttr.SourceName(s.encryption.EncryptedSuffix()))) newSourceStateEntry = &SourceStateFile{ attr: fileAttr, origin: actualStateEntry, sourceRelPath: sourceRelPath, targetStateEntry: &TargetStateFile{ contentsFunc: eagerZeroNoErr[[]byte](), contentsSHA256Func: eagerNoErr(sha256.Sum256(nil)), empty: true, perm: 0o666 &^ s.umask, }, } } else { var err error actualStateEntry, err = NewActualStateEntry(destSystem, destAbsPath, destAbsPathInfo, nil) if err != nil { return err } newSourceStateEntry, err = s.sourceStateEntry(actualStateEntry, destAbsPath, destAbsPathInfo, parentSourceRelPath, targetRelPath, options) if err != nil { return err } if newSourceStateEntry == nil { continue } } if options.PreAddFunc != nil && destAbsPathInfo != nil { switch err := options.PreAddFunc(targetRelPath, destAbsPathInfo); { case errors.Is(err, fs.SkipDir): continue DEST_ABS_PATH case err != nil: return err } } sourceEntryRelPath := newSourceStateEntry.SourceRelPath() entryState, err := actualStateEntry.EntryState() if err != nil { return err } update := sourceUpdate{ destAbsPath: destAbsPath, entryState: entryState, sourceRelPaths: []SourceRelPath{sourceEntryRelPath}, } if oldSourceStateEntry := s.root.Get(targetRelPath); oldSourceStateEntry != nil { oldSourceEntryRelPath := oldSourceStateEntry.SourceRelPath() if !oldSourceEntryRelPath.IsEmpty() && oldSourceEntryRelPath != sourceEntryRelPath { if options.ReplaceFunc != nil { switch err := options.ReplaceFunc(targetRelPath, newSourceStateEntry, oldSourceStateEntry); { case errors.Is(err, fs.SkipDir): continue DEST_ABS_PATH case err != nil: return err } } // If both the new and old source state entries are directories // but the name has changed, rename to avoid losing the // directory's contents. _, newIsDir := newSourceStateEntry.(*SourceStateDir) _, oldIsDir := oldSourceStateEntry.(*SourceStateDir) if newIsDir && oldIsDir { oldSourceAbsPath := s.sourceDirAbsPath.Join(oldSourceEntryRelPath.RelPath()) newSourceAbsPath := s.sourceDirAbsPath.Join(sourceEntryRelPath.RelPath()) dirRenames[oldSourceAbsPath] = newSourceAbsPath continue DEST_ABS_PATH } // Otherwise, remove the old entry. newSourceStateEntries[oldSourceEntryRelPath] = &SourceStateRemove{ origin: SourceStateOriginRemove{}, } update.sourceRelPaths = append(update.sourceRelPaths, oldSourceEntryRelPath) } } newSourceStateEntries[sourceEntryRelPath] = newSourceStateEntry newSourceStateEntriesByTargetRelPath[targetRelPath] = newSourceStateEntry sourceUpdates = append(sourceUpdates, update) } // Create .keep files in empty added directories. for sourceEntryRelPath, sourceStateEntry := range newSourceStateEntries { if _, ok := sourceStateEntry.(*SourceStateDir); !ok { continue } if nonEmptyDirs.Contains(sourceEntryRelPath) { continue } dotKeepFileRelPath := sourceEntryRelPath.Join(NewSourceRelPath(".keep")) dotKeepFileSourceUpdate := sourceUpdate{ entryState: &EntryState{ Type: EntryStateTypeFile, Mode: 0o666 &^ s.umask, }, sourceRelPaths: []SourceRelPath{dotKeepFileRelPath}, } sourceUpdates = append(sourceUpdates, dotKeepFileSourceUpdate) newSourceStateEntries[dotKeepFileRelPath] = &SourceStateFile{ origin: SourceStateOriginAbsPath(s.sourceDirAbsPath.Join(sourceEntryRelPath.RelPath())), targetStateEntry: &TargetStateFile{ contentsFunc: eagerNoErr[[]byte](nil), contentsSHA256Func: eagerNoErr(sha256.Sum256(nil)), empty: true, perm: 0o666 &^ s.umask, }, } } var sourceRoot SourceStateEntryTreeNode for sourceRelPath, sourceStateEntry := range newSourceStateEntries { sourceRoot.Set(sourceRelPath.RelPath(), sourceStateEntry) } // Simulate removing a directory by creating SourceStateRemove entries for // all existing source state entries that are in options.RemoveDir and not // in the new source state. if options.RemoveDir != EmptyRelPath { _ = s.root.ForEach(EmptyRelPath, func(targetRelPath RelPath, sourceStateEntry SourceStateEntry) error { if !targetRelPath.HasDirPrefix(options.RemoveDir) { return nil } if _, ok := newSourceStateEntriesByTargetRelPath[targetRelPath]; ok { return nil } sourceRelPath := sourceStateEntry.SourceRelPath() sourceRoot.Set(sourceRelPath.RelPath(), &SourceStateRemove{ origin: SourceStateOriginRemove{}, sourceRelPath: sourceRelPath, targetRelPath: targetRelPath, }) update := sourceUpdate{ destAbsPath: s.destDirAbsPath.Join(targetRelPath), entryState: &EntryState{ Type: EntryStateTypeRemove, }, sourceRelPaths: []SourceRelPath{sourceRelPath}, } sourceUpdates = append(sourceUpdates, update) return nil }) } targetSourceState := &SourceState{ root: sourceRoot, } for _, sourceUpdate := range sourceUpdates { for _, sourceRelPath := range sourceUpdate.sourceRelPaths { err := targetSourceState.Apply( sourceSystem, sourceSystem, NullPersistentState{}, s.sourceDirAbsPath, sourceRelPath.RelPath(), ApplyOptions{ Filter: options.Filter, Umask: s.umask, }, ) if err != nil { return err } } if !sourceUpdate.destAbsPath.IsEmpty() { if err := PersistentStateSet( persistentState, EntryStateBucket, sourceUpdate.destAbsPath.Bytes(), sourceUpdate.entryState, ); err != nil { return err } } } // Rename directories last because updates assume that directory names have // not changed. Rename directories in reverse order so children are renamed // before their parents. oldDirAbsPaths := slices.Sorted(maps.Keys(dirRenames)) for _, oldDirAbsPath := range slices.Backward(oldDirAbsPaths) { newDirAbsPath := dirRenames[oldDirAbsPath] if err := sourceSystem.Rename(oldDirAbsPath, newDirAbsPath); err != nil { return err } } return nil } // AddDestAbsPathInfos adds an [fs.FileInfo] to destAbsPathInfos for destAbsPath // and any of its parents which are not already known. func (s *SourceState) AddDestAbsPathInfos( destAbsPathInfos map[AbsPath]fs.FileInfo, system System, destAbsPath AbsPath, fileInfo fs.FileInfo, ) error { for { if _, err := destAbsPath.TrimDirPrefix(s.destDirAbsPath); err != nil { return err } if _, ok := destAbsPathInfos[destAbsPath]; ok { return nil } if fileInfo == nil { var err error fileInfo, err = system.Lstat(destAbsPath) if err != nil { return err } } destAbsPathInfos[destAbsPath] = fileInfo parentAbsPath := destAbsPath.Dir() if parentAbsPath == s.destDirAbsPath { return nil } parentRelPath := parentAbsPath.MustTrimDirPrefix(s.destDirAbsPath) if _, ok := s.root.Get(parentRelPath).(*SourceStateDir); ok { return nil } destAbsPath = parentAbsPath fileInfo = nil } } // A PreApplyFunc is called before a target is applied. type PreApplyFunc func(targetRelPath RelPath, targetEntryState, lastWrittenEntryState, actualEntryState *EntryState) error // ApplyOptions are options to SourceState.ApplyAll and SourceState.ApplyOne. type ApplyOptions struct { Filter *EntryTypeFilter PreApplyFunc PreApplyFunc Umask fs.FileMode } // Apply updates targetRelPath in targetDirAbsPath in destSystem to match s. func (s *SourceState) Apply( targetSystem, destSystem System, persistentState PersistentState, targetDirAbsPath AbsPath, targetRelPath RelPath, options ApplyOptions, ) error { sourceStateEntry := s.root.Get(targetRelPath) if sourceStateEntry == nil { return nil } if !options.Filter.IncludeSourceStateEntry(sourceStateEntry) { return nil } destAbsPath := s.destDirAbsPath.Join(targetRelPath) targetStateEntry, err := sourceStateEntry.TargetStateEntry(destSystem, destAbsPath) if err != nil { return err } if !options.Filter.IncludeTargetStateEntry(targetStateEntry) { return nil } targetAbsPath := targetDirAbsPath.Join(targetRelPath) targetEntryState, err := targetStateEntry.EntryState(options.Umask) if err != nil { return err } switch skip, err := targetStateEntry.SkipApply(persistentState, targetAbsPath); { case err != nil: return err case skip: return nil } actualStateEntry, err := NewActualStateEntry(targetSystem, targetAbsPath, nil, nil) if err != nil { return err } if options.PreApplyFunc != nil { var lastWrittenEntryState *EntryState var entryState EntryState ok, err := PersistentStateGet(persistentState, EntryStateBucket, targetAbsPath.Bytes(), &entryState) if err != nil { return err } if ok { lastWrittenEntryState = &entryState } actualEntryState, err := actualStateEntry.EntryState() if err != nil { return err } // If the target entry state matches the actual entry state, but not the // last written entry state then silently update the last written entry // state. This handles the case where the user makes identical edits to // the source and target states: instead of reporting a diff with // respect to the last written state, we record the effect of the last // apply as the last written state. if targetEntryState.Equivalent(actualEntryState) && !lastWrittenEntryState.Equivalent(actualEntryState) { err := PersistentStateSet(persistentState, EntryStateBucket, targetAbsPath.Bytes(), targetEntryState) if err != nil { return err } lastWrittenEntryState = targetEntryState } err = options.PreApplyFunc(targetRelPath, targetEntryState, lastWrittenEntryState, actualEntryState) if err != nil { return err } } if changed, err := targetStateEntry.Apply(targetSystem, persistentState, actualStateEntry); err != nil { return err } else if !changed { return nil } return PersistentStateSet(persistentState, EntryStateBucket, targetAbsPath.Bytes(), targetEntryState) } // Encryption returns s's encryption. func (s *SourceState) Encryption() Encryption { return s.encryption } // ExecuteTemplateDataOptions are options to SourceState.ExecuteTemplateData. type ExecuteTemplateDataOptions struct { NameRelPath RelPath DestAbsPath AbsPath Data []byte TemplateOptions TemplateOptions ExtraData map[string]any } // ExecuteTemplateData returns the result of executing template data. func (s *SourceState) ExecuteTemplateData(options ExecuteTemplateDataOptions) ([]byte, error) { templateOptions := options.TemplateOptions templateOptions.Funcs = s.templateFuncs templateOptions.Options = slices.Clone(s.templateOptions) tmpl, err := ParseTemplate(options.NameRelPath.String(), options.Data, templateOptions) if err != nil { return nil, err } for _, t := range s.templates { tmpl, err = tmpl.AddParseTree(t) if err != nil { return nil, err } } // Set .chezmoi.sourceFile to the name of the template. templateData := s.TemplateData() if chezmoiTemplateData, ok := templateData["chezmoi"].(map[string]any); ok { chezmoiTemplateData["sourceFile"] = options.NameRelPath.String() chezmoiTemplateData["targetFile"] = options.DestAbsPath.String() } RecursiveMerge(templateData, options.ExtraData) result, err := tmpl.Execute(templateData) if errors.Is(err, errReturnEmpty) { return nil, nil } return result, err } // ForEach calls f for each source state entry. func (s *SourceState) ForEach(f func(RelPath, SourceStateEntry) error) error { return s.root.ForEach(EmptyRelPath, func(targetRelPath RelPath, entry SourceStateEntry) error { return f(targetRelPath, entry) }) } // Get returns the source state entry for targetRelPath. func (s *SourceState) Get(targetRelPath RelPath) SourceStateEntry { return s.root.Get(targetRelPath) } // Ignore returns if targetRelPath should be ignored. func (s *SourceState) Ignore(targetRelPath RelPath) bool { s.mutex.Lock() defer s.mutex.Unlock() ignore := s.ignore.Match(targetRelPath.String()) == PatternSetMatchInclude if ignore { s.ignoredRelPaths.Add(targetRelPath) } return ignore } // Ignored returns all ignored RelPaths. func (s *SourceState) Ignored() []RelPath { return slices.SortedFunc(s.ignoredRelPaths.Elements(), CompareRelPaths) } // MustEntry returns the source state entry associated with targetRelPath, and // panics if it does not exist. func (s *SourceState) MustEntry(targetRelPath RelPath) SourceStateEntry { sourceStateEntry := s.root.Get(targetRelPath) if sourceStateEntry == nil { panic(fmt.Sprintf("%s: not in source state", targetRelPath)) } return sourceStateEntry } // PostApply performs all updates required after s.Apply. func (s *SourceState) PostApply( targetSystem System, persistentState PersistentState, targetDirAbsPath AbsPath, targetRelPaths []RelPath, ) error { // Remove empty directories with the remove_ attribute. This assumes that // targetRelPaths is already sorted and iterates in reverse order so that // children are removed before their parents. TARGET: for i := len(targetRelPaths) - 1; i >= 0; i-- { targetRelPath := targetRelPaths[i] if !s.removeDirs.Contains(targetRelPath) { continue } // Ensure that we are attempting to remove a directory, not any other entry type. targetAbsPath := targetDirAbsPath.Join(targetRelPath) switch fileInfo, err := targetSystem.Stat(targetAbsPath); { case errors.Is(err, fs.ErrNotExist): continue TARGET case err != nil: return err case !fileInfo.IsDir(): return fmt.Errorf("%s: not a directory", targetAbsPath) } // Attempt to remove the directory, but ignore any "not exist" or "not // empty" errors. switch err := targetSystem.Remove(targetAbsPath); { case err == nil: // Do nothing. case errors.Is(err, fs.ErrExist): continue TARGET case errors.Is(err, fs.ErrNotExist): continue TARGET default: return err } entryState := &EntryState{ Type: EntryStateTypeRemove, } if err := PersistentStateSet(persistentState, EntryStateBucket, targetAbsPath.Bytes(), entryState); err != nil { return err } } return nil } // ReadOptions are options to SourceState.Read. type ReadOptions struct { ReadHTTPResponse func(string, *http.Response) ([]byte, error) RefreshExternals RefreshExternals TimeNow func() time.Time } // Read reads the source state from the source directory. func (s *SourceState) Read(ctx context.Context, options *ReadOptions) error { switch fileInfo, err := s.system.Stat(s.sourceDirAbsPath); { case errors.Is(err, fs.ErrNotExist): return nil case err != nil: return err case !fileInfo.IsDir(): return fmt.Errorf("%s: not a directory", s.sourceDirAbsPath) } // Read all source entries. var allSourceStateEntriesMu sync.Mutex allSourceStateEntries := make(map[RelPath][]SourceStateEntry) addSourceStateEntries := func(relPath RelPath, sourceStateEntries ...SourceStateEntry) { allSourceStateEntriesMu.Lock() defer allSourceStateEntriesMu.Unlock() allSourceStateEntries[relPath] = append(allSourceStateEntries[relPath], sourceStateEntries...) } walkFunc := func(sourceAbsPath AbsPath, fileInfo fs.FileInfo, err error) error { if err != nil { return err } if sourceAbsPath == s.sourceDirAbsPath { return nil } // Follow symlinks in the source directory. if fileInfo.Mode().Type() == fs.ModeSymlink { // Some programs (notably emacs) use invalid symlinks as lockfiles. // To avoid following them and getting an ENOENT error, check first // if this is an entry that we will ignore anyway. if strings.HasPrefix(fileInfo.Name(), ignorePrefix) && !strings.HasPrefix(fileInfo.Name(), Prefix) { return nil } fileInfo, err = s.system.Stat(sourceAbsPath) if err != nil { return err } } sourceRelPath := SourceRelPath{ relPath: sourceAbsPath.MustTrimDirPrefix(s.sourceDirAbsPath), isDir: fileInfo.IsDir(), } parentSourceRelPath, sourceName := sourceRelPath.Split() switch { case fileInfo.Name() == dataName: if !s.readTemplateData { return nil } if err := s.addTemplateDataDir(sourceAbsPath, fileInfo); err != nil { return err } return fs.SkipDir case isPrefixDotFormat(fileInfo.Name(), dataName): if !s.readTemplateData { return nil } return s.addTemplateData(sourceAbsPath) case fileInfo.Name() == TemplatesDirName: if s.readTemplates { if err := s.addTemplatesDir(ctx, sourceAbsPath); err != nil { return err } } return fs.SkipDir case s.templateDataOnly: return nil case isPrefixDotFormat(fileInfo.Name(), externalName) || isPrefixDotFormatDotTmpl(fileInfo.Name(), externalName): parentAbsPath, _ := sourceAbsPath.Split() return s.addExternal(sourceAbsPath, parentAbsPath) case fileInfo.Name() == externalsDirName: if err := s.addExternalDir(ctx, sourceAbsPath); err != nil { return err } return fs.SkipDir case fileInfo.Name() == ignoreName || fileInfo.Name() == ignoreName+TemplateSuffix: return s.addPatterns(s.ignore, sourceAbsPath, parentSourceRelPath) case fileInfo.Name() == removeName || fileInfo.Name() == removeName+TemplateSuffix: return s.addPatterns(s.remove, sourceAbsPath, parentSourceRelPath) case fileInfo.Name() == scriptsDirName: scriptsDirSourceStateEntries, err := s.readScriptsDir(ctx, sourceAbsPath) if err != nil { return err } for relPath, scriptSourceStateEntries := range scriptsDirSourceStateEntries { addSourceStateEntries(relPath, scriptSourceStateEntries...) } return fs.SkipDir case fileInfo.Name() == VersionName: return s.readVersionFile(sourceAbsPath) case strings.HasPrefix(fileInfo.Name(), Prefix): fallthrough case strings.HasPrefix(fileInfo.Name(), ignorePrefix): if fileInfo.IsDir() { return fs.SkipDir } return nil case fileInfo.IsDir(): da, err := parseDirAttr(sourceName.String()) if err != nil { return err } targetRelPath, err := parentSourceRelPath.Dir().TargetRelPath(s.encryption.EncryptedSuffix()) if err != nil { return err } targetRelPath = targetRelPath.JoinString(da.TargetName) if s.Ignore(targetRelPath) { return fs.SkipDir } sourceStateDir := s.newSourceStateDir(sourceAbsPath, sourceRelPath, da) addSourceStateEntries(targetRelPath, sourceStateDir) if da.External { sourceStateEntries, err := s.readExternalDir(sourceAbsPath, sourceRelPath, targetRelPath) if err != nil { return err } allSourceStateEntriesMu.Lock() for relPath, entries := range sourceStateEntries { allSourceStateEntries[relPath] = append(allSourceStateEntries[relPath], entries...) } allSourceStateEntriesMu.Unlock() return fs.SkipDir } if sourceStateDir.attr.Remove { s.mutex.Lock() s.removeDirs.Add(targetRelPath) s.mutex.Unlock() } return nil case fileInfo.Mode().IsRegular(): fa, err := parseFileAttr(sourceName.String(), s.encryption.EncryptedSuffix()) if err != nil { return err } targetRelPath, err := parentSourceRelPath.Dir().TargetRelPath(s.encryption.EncryptedSuffix()) if err != nil { return err } targetRelPath = targetRelPath.JoinString(fa.TargetName) if s.Ignore(targetRelPath) { return nil } var sourceStateEntry SourceStateEntry targetRelPath, sourceStateEntry = s.newSourceStateFile(sourceAbsPath, sourceRelPath, fa, targetRelPath) addSourceStateEntries(targetRelPath, sourceStateEntry) return nil default: return &UnsupportedFileTypeError{ absPath: sourceAbsPath, mode: fileInfo.Mode(), } } } if err := WalkSourceDir(s.system, s.sourceDirAbsPath, walkFunc); err != nil { return err } if s.templateDataOnly { return nil } // Read externals. externalRelPaths := make([]RelPath, 0, len(s.externals)) for externalRelPath := range s.externals { externalRelPaths = append(externalRelPaths, externalRelPath) } slices.SortFunc(externalRelPaths, CompareRelPaths) for _, externalRelPath := range externalRelPaths { if s.Ignore(externalRelPath) { continue } for _, external := range s.externals[externalRelPath] { parentRelPath, _ := externalRelPath.Split() var parentSourceRelPath SourceRelPath switch parentSourceStateEntry, err := s.root.MkdirAll(parentRelPath, external, s.umask); { case err != nil: return err case parentSourceStateEntry != nil: parentSourceRelPath = parentSourceStateEntry.SourceRelPath() } externalSourceStateEntries, err := s.readExternal(ctx, externalRelPath, parentSourceRelPath, external, options) if err != nil { return err } for targetRelPath, sourceStateEntries := range externalSourceStateEntries { if s.Ignore(targetRelPath) { continue } allSourceStateEntries[targetRelPath] = append(allSourceStateEntries[targetRelPath], sourceStateEntries...) } } } // Remove all ignored targets. for targetRelPath := range allSourceStateEntries { if s.Ignore(targetRelPath) { delete(allSourceStateEntries, targetRelPath) } } // Generate SourceStateRemoves for existing targets. matches, err := s.remove.Glob(s.system.UnderlyingFS(), ensureSuffix(s.destDirAbsPath.String(), "/")) if err != nil { return err } for _, match := range matches { targetRelPath := NewRelPath(match) if s.Ignore(targetRelPath) { continue } sourceStateEntry := &SourceStateRemove{ origin: SourceStateOriginRemove{}, sourceRelPath: NewSourceRelPath(".chezmoiremove"), targetRelPath: targetRelPath, } allSourceStateEntries[targetRelPath] = append(allSourceStateEntries[targetRelPath], sourceStateEntry) } // Where there are multiple SourceStateEntries for a single target, replace // them with a single canonical SourceStateEntry if possible. for targetRelPath, sourceStateEntries := range allSourceStateEntries { if sourceStateEntry, ok := canonicalSourceStateEntry(sourceStateEntries); ok { allSourceStateEntries[targetRelPath] = []SourceStateEntry{sourceStateEntry} } } // Generate SourceStateRemoves for exact directories. for targetRelPath, sourceStateEntries := range allSourceStateEntries { if len(sourceStateEntries) != 1 { continue } sourceStateDir, ok := sourceStateEntries[0].(*SourceStateDir) switch { case !ok: continue case !sourceStateDir.attr.Exact: continue } switch fileInfos, err := s.system.ReadDir(s.destDirAbsPath.Join(targetRelPath)); { case err == nil: for _, fileInfo := range fileInfos { name := fileInfo.Name() if name == "." || name == ".." { continue } destEntryRelPath := targetRelPath.JoinString(name) if _, ok := allSourceStateEntries[destEntryRelPath]; ok { continue } if s.Ignore(destEntryRelPath) { continue } sourceStateRemove := &SourceStateRemove{ origin: sourceStateDir.Origin(), sourceRelPath: sourceStateDir.sourceRelPath, targetRelPath: destEntryRelPath, } allSourceStateEntries[destEntryRelPath] = append(allSourceStateEntries[destEntryRelPath], sourceStateRemove) } case errors.Is(err, fs.ErrNotExist): // Do nothing. case errors.Is(err, syscall.ENOTDIR): // Do nothing. default: return err } } // Generate SourceStateCommands for git-repo externals. var gitRepoExternalRelPaths []RelPath for externalRelPath, externals := range s.externals { if s.Ignore(externalRelPath) { continue } for _, external := range externals { if external.Type == ExternalTypeGitRepo { gitRepoExternalRelPaths = append(gitRepoExternalRelPaths, externalRelPath) } } } slices.SortFunc(gitRepoExternalRelPaths, CompareRelPaths) for _, externalRelPath := range gitRepoExternalRelPaths { for _, external := range s.externals[externalRelPath] { destAbsPath := s.destDirAbsPath.Join(externalRelPath) switch _, err := s.system.Lstat(destAbsPath); { case errors.Is(err, fs.ErrNotExist): // FIXME add support for using builtin git sourceStateCommand := &SourceStateCommand{ // Use a sync.OnceValue to defer the call to // os/exec.Command because os/exec.Command calls // os/exec.LookupPath and therefore depends on the state of // $PATH when os/exec.Command is called, not the state of // $PATH when os/exec.Cmd.{Run,Start} is called. cmdFunc: sync.OnceValue(func() *exec.Cmd { args := []string{"clone"} args = append(args, external.Clone.Args...) args = append(args, external.URL, destAbsPath.String()) cmd := exec.Command("git", args...) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd }), origin: external, forceRefresh: options.RefreshExternals == RefreshExternalsAlways, refreshPeriod: external.RefreshPeriod, sourceAttr: SourceAttr{ External: true, }, } allSourceStateEntries[externalRelPath] = append(allSourceStateEntries[externalRelPath], sourceStateCommand) case err != nil: return err default: // FIXME add support for using builtin git sourceStateCommand := &SourceStateCommand{ // Use a sync.OnceValue to defer the call to // os/exec.Command because os/exec.Command calls // os/exec.LookupPath and therefore depends on the state of // $PATH when os/exec.Command is called, not the state of // $PATH when os/exec.Cmd.{Run,Start} is called. cmdFunc: sync.OnceValue(func() *exec.Cmd { args := []string{"pull"} args = append(args, external.Pull.Args...) cmd := exec.Command("git", args...) cmd.Dir = destAbsPath.String() cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd }), origin: external, forceRefresh: options.RefreshExternals == RefreshExternalsAlways, refreshPeriod: external.RefreshPeriod, sourceAttr: SourceAttr{ External: true, }, } allSourceStateEntries[externalRelPath] = append(allSourceStateEntries[externalRelPath], sourceStateCommand) } } } // Check for inconsistent source entries. Iterate over the target names in // order so that any error is deterministic. targetRelPaths := make([]RelPath, 0, len(allSourceStateEntries)) for targetRelPath := range allSourceStateEntries { targetRelPaths = append(targetRelPaths, targetRelPath) } slices.SortFunc(targetRelPaths, CompareRelPaths) errs := make([]error, 0, len(targetRelPaths)) for _, targetRelPath := range targetRelPaths { sourceStateEntries := allSourceStateEntries[targetRelPath] if len(sourceStateEntries) == 1 { continue } origins := make([]string, len(sourceStateEntries)) for i, sourceStateEntry := range sourceStateEntries { origins[i] = sourceStateEntry.Origin().OriginString() } slices.Sort(origins) errs = append(errs, &InconsistentStateError{ targetRelPath: targetRelPath, origins: origins, }) } if len(errs) != 0 { return errors.Join(errs...) } // Populate s.Entries with the unique source entry for each target. for targetRelPath, sourceEntries := range allSourceStateEntries { s.root.Set(targetRelPath, sourceEntries[0]) } return nil } // TargetRelPaths returns all of s's target relative paths in order. func (s *SourceState) TargetRelPaths() []RelPath { entries := s.root.GetMap() targetRelPaths := make([]RelPath, 0, len(entries)) for targetRelPath := range entries { targetRelPaths = append(targetRelPaths, targetRelPath) } slices.SortFunc(targetRelPaths, func(a, b RelPath) int { if compare := cmp.Compare(entries[a].Order(), entries[b].Order()); compare != 0 { return compare } return CompareRelPaths(a, b) }) return targetRelPaths } // TemplateData returns a copy of s's template data. func (s *SourceState) TemplateData() map[string]any { s.mutex.Lock() defer s.mutex.Unlock() if s.templateData == nil { s.templateData = make(map[string]any) if s.defaultTemplateDataFunc != nil { s.defaultTemplateData = s.defaultTemplateDataFunc() s.defaultTemplateDataFunc = nil } RecursiveMerge(s.templateData, s.defaultTemplateData) RecursiveMerge(s.templateData, s.userTemplateData) RecursiveMerge(s.templateData, s.priorityTemplateData) } templateData, err := copystructure.Copy(s.templateData) if err != nil { panic(err) } return templateData.(map[string]any) //nolint:forcetypeassert,revive } // addExternal adds external source entries to s. func (s *SourceState) addExternal(sourceAbsPath, parentAbsPath AbsPath) error { parentRelPath, err := parentAbsPath.TrimDirPrefix(s.sourceDirAbsPath) if err != nil { return err } parentSourceRelPath := NewSourceRelDirPath(parentRelPath.String()) parentTargetSourceRelPath, err := parentSourceRelPath.TargetRelPath(s.encryption.EncryptedSuffix()) if err != nil { return err } format, err := FormatFromAbsPath(sourceAbsPath.TrimSuffix(TemplateSuffix)) if err != nil { return err } data, err := s.executeTemplate(sourceAbsPath) if err != nil { return fmt.Errorf("%s: %w", sourceAbsPath, err) } externals := make(map[string]External) if err := format.Unmarshal(data, &externals); err != nil { return fmt.Errorf("%s: %w", sourceAbsPath, err) } s.mutex.Lock() defer s.mutex.Unlock() for targetPath, external := range externals { if external.TargetPath != "" { targetPath = external.TargetPath } if targetPath == "" { return fmt.Errorf("%s: empty path", sourceAbsPath) } externalPath := path.Clean(targetPath) if strings.HasPrefix(externalPath, "/") || filepath.IsAbs(externalPath) { return fmt.Errorf("%s: %s: path is not relative", sourceAbsPath, targetPath) } switch relPath, err := filepath.Rel(".", externalPath); { case err != nil: return fmt.Errorf("%s: %s: %w", sourceAbsPath, targetPath, err) case relPath == ".": return fmt.Errorf("%s: %s: empty relative path", sourceAbsPath, targetPath) case relPath == "..", strings.HasPrefix(relPath, "../"): return fmt.Errorf("%s: %s: relative path in parent", sourceAbsPath, targetPath) } targetRelPath := parentTargetSourceRelPath.JoinString(externalPath) external.sourceAbsPath = sourceAbsPath s.externals[targetRelPath] = append(s.externals[targetRelPath], &external) } return nil } // addExternalDir adds all externals in externalsDirAbsPath to s. func (s *SourceState) addExternalDir(ctx context.Context, externalsDirAbsPath AbsPath) error { walkFunc := func(ctx context.Context, externalAbsPath AbsPath, fileInfo fs.FileInfo, err error) error { if externalAbsPath == externalsDirAbsPath { return nil } if err == nil && fileInfo.Mode().Type() == fs.ModeSymlink { fileInfo, err = s.system.Stat(externalAbsPath) } switch { case err != nil: return err case strings.HasPrefix(fileInfo.Name(), Prefix): return fmt.Errorf("%s: not allowed in %s directory", externalAbsPath, externalsDirName) case strings.HasPrefix(fileInfo.Name(), ignorePrefix): if fileInfo.IsDir() { return fs.SkipDir } return nil case fileInfo.Mode().IsRegular(): parentAbsPath, _ := externalAbsPath.Split() return s.addExternal(externalAbsPath, parentAbsPath.Dir()) case fileInfo.IsDir(): return nil default: return &UnsupportedFileTypeError{ absPath: externalAbsPath, mode: fileInfo.Mode(), } } } return concurrentWalkSourceDir(ctx, s.system, externalsDirAbsPath, walkFunc) } // addPatterns executes the template at sourceAbsPath, interprets the result as // a list of patterns, and adds all patterns found to patternSet. func (s *SourceState) addPatterns(patternSet *PatternSet, sourceAbsPath AbsPath, sourceRelPath SourceRelPath) error { data, err := s.executeTemplate(sourceAbsPath) if err != nil { return err } s.mutex.Lock() defer s.mutex.Unlock() dir, err := sourceRelPath.Dir().TargetRelPath("") if err != nil { return err } lineNumber := 0 for line := range bytes.Lines(data) { lineNumber++ line = commentRx.ReplaceAll(line, nil) line = bytes.TrimSpace(line) if len(line) == 0 { continue } include := PatternSetInclude line, ok := bytes.CutPrefix(line, []byte{'!'}) if ok { include = PatternSetExclude } pattern := dir.JoinString(string(line)).String() if err := patternSet.Add(pattern, include); err != nil { return fmt.Errorf("%s:%d: %w", sourceAbsPath, lineNumber, err) } } return nil } // addTemplateData adds all template data in sourceAbsPath to s. func (s *SourceState) addTemplateData(sourceAbsPath AbsPath) error { format, err := FormatFromAbsPath(sourceAbsPath) if err != nil { return err } data, err := s.system.ReadFile(sourceAbsPath) if err != nil { return fmt.Errorf("%s: %w", sourceAbsPath, err) } var templateData map[string]any if err := format.Unmarshal(data, &templateData); err != nil { return fmt.Errorf("%s: %w", sourceAbsPath, err) } s.mutex.Lock() RecursiveMerge(s.userTemplateData, templateData) // Clear the cached template data, as the change to the user template data // means that the cached value is now invalid. s.templateData = nil s.mutex.Unlock() return nil } // addTemplateDataDir adds all template data in the directory sourceAbsPath to // s. func (s *SourceState) addTemplateDataDir(sourceAbsPath AbsPath, fileInfo fs.FileInfo) error { walkFunc := func(dataAbsPath AbsPath, fileInfo fs.FileInfo, err error) error { if dataAbsPath == sourceAbsPath { return nil } if err == nil && fileInfo.Mode().Type() == fs.ModeSymlink { fileInfo, err = s.system.Stat(dataAbsPath) } switch { case err != nil: return err case strings.HasPrefix(fileInfo.Name(), Prefix): return fmt.Errorf("%s: not allowed in %s directory", dataAbsPath, dataName) case strings.HasPrefix(fileInfo.Name(), ignorePrefix): if fileInfo.IsDir() { return fs.SkipDir } return nil case fileInfo.Mode().IsRegular(): return s.addTemplateData(dataAbsPath) case fileInfo.IsDir(): return nil default: return &UnsupportedFileTypeError{ absPath: dataAbsPath, mode: fileInfo.Mode(), } } } return walkSourceDirHelper(s.system, sourceAbsPath, fileInfo, walkFunc) } // addTemplatesDir adds all templates in templatesDirAbsPath to s. func (s *SourceState) addTemplatesDir(ctx context.Context, templatesDirAbsPath AbsPath) error { walkFunc := func(ctx context.Context, templateAbsPath AbsPath, fileInfo fs.FileInfo, err error) error { if templateAbsPath == templatesDirAbsPath { return nil } if err == nil && fileInfo.Mode().Type() == fs.ModeSymlink { fileInfo, err = s.system.Stat(templateAbsPath) } switch { case err != nil: return err case strings.HasPrefix(fileInfo.Name(), Prefix): return fmt.Errorf("%s: not allowed in %s directory", templateAbsPath, TemplatesDirName) case strings.HasPrefix(fileInfo.Name(), ignorePrefix): if fileInfo.IsDir() { return fs.SkipDir } return nil case fileInfo.Mode().IsRegular(): contents, err := s.system.ReadFile(templateAbsPath) if err != nil { return err } templateRelPath := templateAbsPath.MustTrimDirPrefix(templatesDirAbsPath) name := templateRelPath.String() tmpl, err := ParseTemplate(name, contents, TemplateOptions{ Funcs: s.templateFuncs, Options: slices.Clone(s.templateOptions), }) if err != nil { return err } s.mutex.Lock() s.templates[name] = tmpl s.mutex.Unlock() return nil case fileInfo.IsDir(): return nil default: return &UnsupportedFileTypeError{ absPath: templateAbsPath, mode: fileInfo.Mode(), } } } return concurrentWalkSourceDir(ctx, s.system, templatesDirAbsPath, walkFunc) } // executeTemplate executes the template at path and returns the result. func (s *SourceState) executeTemplate(templateAbsPath AbsPath) ([]byte, error) { data, err := s.system.ReadFile(templateAbsPath) if err != nil { return nil, err } return s.ExecuteTemplateData(ExecuteTemplateDataOptions{ NameRelPath: templateAbsPath.MustTrimDirPrefix(s.sourceDirAbsPath), Data: data, }) } // getExternalDataRaw returns the raw data for external at externalRelPath, // possibly from the external cache. func (s *SourceState) getExternalDataRaw( ctx context.Context, externalRelPath RelPath, urlStr string, refreshPeriod Duration, options *ReadOptions, ) ([]byte, error) { // Handle file:// URLs by always reading from disk. switch urlStruct, err := url.Parse(urlStr); { case err != nil: return nil, err case urlStruct.Scheme == "file": data, err := s.system.ReadFile(NewAbsPath(urlStruct.Path)) if err != nil { return nil, err } return data, nil } var now time.Time if options != nil && options.TimeNow != nil { now = options.TimeNow() } else { now = time.Now() } now = now.UTC() refreshExternals := RefreshExternalsAuto if options != nil { refreshExternals = options.RefreshExternals } urlSHA256 := sha256.Sum256([]byte(urlStr)) cacheKey := hex.EncodeToString(urlSHA256[:]) cachedDataAbsPath := s.cacheDirAbsPath.JoinString("external", cacheKey) switch refreshExternals { case RefreshExternalsAlways: // Never use the cache. case RefreshExternalsAuto: // Use the cache, if available and within the refresh period. if fileInfo, err := s.baseSystem.Stat(cachedDataAbsPath); err == nil { if refreshPeriod == 0 || fileInfo.ModTime().Add(time.Duration(refreshPeriod)).After(now) { if data, err := s.baseSystem.ReadFile(cachedDataAbsPath); err == nil { return data, nil } } } case RefreshExternalsNever: // Always use the cache, if available, irrespective of the refresh // period. if data, err := s.baseSystem.ReadFile(cachedDataAbsPath); err == nil { return data, nil } } req, err := http.NewRequestWithContext(ctx, http.MethodGet, urlStr, http.NoBody) if err != nil { return nil, err } resp, err := chezmoilog.LogHTTPRequest(ctx, s.logger, s.httpClient, req) if err != nil { return nil, err } var data []byte if options == nil || options.ReadHTTPResponse == nil { data, err = io.ReadAll(resp.Body) } else { data, err = options.ReadHTTPResponse(urlStr, resp) } resp.Body.Close() if err != nil { return nil, err } if resp.StatusCode < http.StatusOK || http.StatusMultipleChoices <= resp.StatusCode { return nil, fmt.Errorf("%s: %s: %s", externalRelPath, urlStr, resp.Status) } if err := MkdirAll(s.baseSystem, cachedDataAbsPath.Dir(), 0o700); err != nil { return nil, err } if err := s.baseSystem.WriteFile(cachedDataAbsPath, data, 0o600); err != nil { return nil, err } if err := s.baseSystem.Chtimes(cachedDataAbsPath, now, now); err != nil { return nil, err } return data, nil } // getExternalDataAndURL iterates over external.URL and external.URLs, returning // the first data that is downloaded successfully and the URL it was downloaded // from. func (s *SourceState) getExternalDataAndURL( ctx context.Context, externalRelPath RelPath, external *External, options *ReadOptions, ) ([]byte, string, error) { var firstURLStr string var firstErr error for _, urlStr := range append([]string{external.URL}, external.URLs...) { if urlStr == "" { continue } data, err := s.getExternalDataRaw(ctx, externalRelPath, urlStr, external.RefreshPeriod, options) if err == nil { return data, urlStr, nil } if firstURLStr == "" { firstURLStr = urlStr firstErr = err } } if firstURLStr == "" { return nil, "", fmt.Errorf("%s: no URL", externalRelPath) } return nil, firstURLStr, firstErr } // getExternalData reads the external data for externalRelPath from // external.URL or external.URLs, returning the data and URL. func (s *SourceState) getExternalData( ctx context.Context, externalRelPath RelPath, external *External, options *ReadOptions, ) ([]byte, string, error) { data, urlStr, err := s.getExternalDataAndURL(ctx, externalRelPath, external, options) if err != nil { return nil, "", err } var errs []error if external.Checksum.Size != 0 { if external.Checksum.SHA256 == nil && external.Checksum.SHA384 == nil && external.Checksum.SHA512 == nil { s.warnFunc("%s: warning: insecure size check without secure hash will be removed\n", externalRelPath) } if len(data) != external.Checksum.Size { err := fmt.Errorf("size mismatch: expected %d, got %d", external.Checksum.Size, len(data)) errs = append(errs, err) } } if external.Checksum.MD5 != nil { s.warnFunc( "%s: warning: insecure MD5 checksum will be removed, use a secure hash like SHA256 instead\n", externalRelPath, ) if gotMD5Sum := md5Sum(data); !bytes.Equal(gotMD5Sum, external.Checksum.MD5) { err := fmt.Errorf("MD5 mismatch: expected %s, got %s", external.Checksum.MD5, hex.EncodeToString(gotMD5Sum)) errs = append(errs, err) } } if external.Checksum.RIPEMD160 != nil { s.warnFunc( "%s: warning: insecure RIPEMD-160 checksum will be removed, use a secure hash like SHA256 instead\n", externalRelPath, ) if gotRIPEMD160Sum := ripemd160Sum(data); !bytes.Equal(gotRIPEMD160Sum, external.Checksum.RIPEMD160) { format := "RIPEMD-160 mismatch: expected %s, got %s" err := fmt.Errorf(format, external.Checksum.RIPEMD160, hex.EncodeToString(gotRIPEMD160Sum)) errs = append(errs, err) } } if external.Checksum.SHA1 != nil { s.warnFunc( "%s: warning: insecure SHA1 checksum will be removed, use a secure hash like SHA256 instead\n", externalRelPath, ) if gotSHA1Sum := sha1Sum(data); !bytes.Equal(gotSHA1Sum, external.Checksum.SHA1) { err := fmt.Errorf("SHA1 mismatch: expected %s, got %s", external.Checksum.SHA1, hex.EncodeToString(gotSHA1Sum)) errs = append(errs, err) } } if external.Checksum.SHA256 != nil { if gotSHA256Sum := sha256.Sum256(data); !bytes.Equal(gotSHA256Sum[:], external.Checksum.SHA256) { format := "SHA256 mismatch: expected %s, got %s" err := fmt.Errorf(format, external.Checksum.SHA256, hex.EncodeToString(gotSHA256Sum[:])) errs = append(errs, err) } } if external.Checksum.SHA384 != nil { if gotSHA384Sum := sha384Sum(data); !bytes.Equal(gotSHA384Sum, external.Checksum.SHA384) { errs = append(errs, fmt.Errorf("SHA384 mismatch: expected %s, got %s", external.Checksum.SHA384, hex.EncodeToString(gotSHA384Sum))) } } if external.Checksum.SHA512 != nil { if gotSHA512Sum := sha512Sum(data); !bytes.Equal(gotSHA512Sum, external.Checksum.SHA512) { errs = append(errs, fmt.Errorf("SHA512 mismatch: expected %s, got %s", external.Checksum.SHA512, hex.EncodeToString(gotSHA512Sum))) } } if len(errs) != 0 { return nil, urlStr, fmt.Errorf("%s: %w", externalRelPath, errors.Join(errs...)) } if external.Encrypted { data, err = s.encryption.Decrypt(data) if err != nil { return nil, urlStr, fmt.Errorf("%s: %s: %w", externalRelPath, urlStr, err) } } data, err = decompress(external.Decompress, data) if err != nil { return nil, urlStr, fmt.Errorf("%s: %w", externalRelPath, err) } if external.Filter.Command != "" { cmd := exec.Command(external.Filter.Command, external.Filter.Args...) cmd.Stdin = bytes.NewReader(data) cmd.Stderr = os.Stderr data, err = chezmoilog.LogCmdOutput(s.logger, cmd) if err != nil { return nil, urlStr, fmt.Errorf("%s: %s: %w", externalRelPath, urlStr, err) } } return data, urlStr, nil } // newSourceStateDir returns a new SourceStateDir. func (s *SourceState) newSourceStateDir(absPath AbsPath, sourceRelPath SourceRelPath, dirAttr DirAttr) *SourceStateDir { targetStateDir := &TargetStateDir{ perm: dirAttr.perm() &^ s.umask, } return &SourceStateDir{ origin: SourceStateOriginAbsPath(absPath), sourceRelPath: sourceRelPath, attr: dirAttr, targetStateEntry: targetStateDir, } } // newCreateTargetStateEntryFunc returns a targetStateEntryFunc that returns a // file with the value of sourceContentsFunc if the file does not already exist, // or returns the actual file's contents unchanged if the file already exists. func (s *SourceState) newCreateTargetStateEntryFunc( sourceRelPath SourceRelPath, fileAttr FileAttr, sourceContentsFunc func() ([]byte, error), ) TargetStateEntryFunc { return func(destSystem System, destAbsPath AbsPath) (TargetStateEntry, error) { var contentsFunc func() ([]byte, error) switch contents, err := destSystem.ReadFile(destAbsPath); { case err == nil: contentsFunc = eagerNoErr(contents) case errors.Is(err, fs.ErrNotExist): contentsFunc = sync.OnceValues(func() ([]byte, error) { contents, err = sourceContentsFunc() if err != nil { return nil, err } if fileAttr.Template { contents, err = s.ExecuteTemplateData(ExecuteTemplateDataOptions{ NameRelPath: sourceRelPath.RelPath(), Data: contents, DestAbsPath: destAbsPath, }) if err != nil { return nil, err } } return contents, nil }) default: return nil, err } return &TargetStateFile{ contentsFunc: contentsFunc, contentsSHA256Func: lazySHA256(contentsFunc), empty: fileAttr.Empty, perm: fileAttr.perm() &^ s.umask, sourceAttr: SourceAttr{ Encrypted: fileAttr.Encrypted, Template: fileAttr.Template, }, }, nil } } // newFileTargetStateEntryFunc returns a targetStateEntryFunc that returns a // file with the contents of the value of sourceContentsFunc. func (s *SourceState) newFileTargetStateEntryFunc( sourceRelPath SourceRelPath, fileAttr FileAttr, sourceContentsFunc func() ([]byte, error), ) TargetStateEntryFunc { return func(destSystem System, destAbsPath AbsPath) (TargetStateEntry, error) { if s.mode == ModeSymlink && !fileAttr.Encrypted && !fileAttr.Executable && !fileAttr.Private && !fileAttr.Template { switch contents, err := sourceContentsFunc(); { case err != nil: return nil, err case isEmpty(contents) && !fileAttr.Empty: return &TargetStateRemove{}, nil default: linkname := normalizeLinkname(s.sourceDirAbsPath.Join(sourceRelPath.RelPath()).String()) return &TargetStateSymlink{ linknameFunc: eagerNoErr(linkname), sourceAttr: SourceAttr{ Template: fileAttr.Template, }, }, nil } } executedContentsFunc := sync.OnceValues(func() ([]byte, error) { contents, err := sourceContentsFunc() if err != nil { return nil, err } if fileAttr.Template { contents, err = s.ExecuteTemplateData(ExecuteTemplateDataOptions{ NameRelPath: sourceRelPath.RelPath(), Data: contents, DestAbsPath: destAbsPath, }) if err != nil { return nil, err } } return contents, nil }) return &TargetStateFile{ contentsFunc: executedContentsFunc, contentsSHA256Func: lazySHA256(executedContentsFunc), empty: fileAttr.Empty, perm: fileAttr.perm() &^ s.umask, sourceAttr: SourceAttr{ Encrypted: fileAttr.Encrypted, Template: fileAttr.Template, }, }, nil } } // newModifyTargetStateEntryFunc returns a targetStateEntryFunc that returns a // file with the contents modified by running the sourceLazyContents script. func (s *SourceState) newModifyTargetStateEntryFunc( sourceRelPath SourceRelPath, fileAttr FileAttr, contentsFunc func() ([]byte, error), interpreter *Interpreter, ) TargetStateEntryFunc { return func(destSystem System, destAbsPath AbsPath) (TargetStateEntry, error) { contentsFunc := sync.OnceValues(func() (contents []byte, err error) { // FIXME this should share code with RealSystem.RunScript // Read the current contents of the target. var currentContents []byte currentContents, err = destSystem.ReadFile(destAbsPath) if err != nil && !errors.Is(err, fs.ErrNotExist) { return nil, err } // Compute the contents of the modifier. var modifierContents []byte modifierContents, err = contentsFunc() if err != nil { return nil, err } if fileAttr.Template { modifierContents, err = s.ExecuteTemplateData(ExecuteTemplateDataOptions{ NameRelPath: sourceRelPath.RelPath(), Data: modifierContents, DestAbsPath: destAbsPath, }) if err != nil { return nil, err } } // If the modifier is empty then return the current contents unchanged. if isEmpty(modifierContents) { return currentContents, nil } // If the modifier contains chezmoi:modify-template then execute it // as a template. if matches := modifyTemplateRx.FindAllSubmatchIndex(modifierContents, -1); matches != nil { sourceFile := sourceRelPath.String() templateContents := removeMatches(modifierContents, matches) var tmpl *Template tmpl, err = ParseTemplate(sourceFile, templateContents, TemplateOptions{ Funcs: s.templateFuncs, Options: slices.Clone(s.templateOptions), }) if err != nil { return nil, err } // Temporarily set .chezmoi.stdin to the current contents and // .chezmoi.sourceFile to the name of the template. templateData := s.TemplateData() if chezmoiTemplateData, ok := templateData["chezmoi"].(map[string]any); ok { chezmoiTemplateData["stdin"] = string(currentContents) chezmoiTemplateData["sourceFile"] = sourceRelPath.RelPath().String() } return tmpl.Execute(templateData) } // Create the script temporary directory, if needed. s.createScriptTempDirOnce.Do(func() { if !s.scriptTempDirAbsPath.IsEmpty() { err = os.MkdirAll(s.scriptTempDirAbsPath.String(), 0o700) } }) if err != nil { return nil, err } // Write the modifier to a temporary file. var tempFile *os.File if tempFile, err = os.CreateTemp(s.scriptTempDirAbsPath.String(), "*."+fileAttr.TargetName); err != nil { return nil, err } defer chezmoierrors.CombineFunc(&err, func() error { return os.RemoveAll(tempFile.Name()) }) if runtime.GOOS != "windows" { if err := tempFile.Chmod(0o700); err != nil { return nil, err } } _, err = tempFile.Write(modifierContents) err = chezmoierrors.Combine(err, tempFile.Close()) if err != nil { return nil, err } // Run the modifier on the current contents. cmd := interpreter.ExecCommand(tempFile.Name()) cmd.Env = append(os.Environ(), "CHEZMOI_SOURCE_FILE="+sourceRelPath.String(), ) cmd.Stdin = bytes.NewReader(currentContents) cmd.Stderr = os.Stderr return chezmoilog.LogCmdOutput(s.logger, cmd) }) return &TargetStateFile{ contentsFunc: contentsFunc, contentsSHA256Func: lazySHA256(contentsFunc), overwrite: true, perm: fileAttr.perm() &^ s.umask, }, nil } } // newRemoveTargetStateEntryFunc returns a targetStateEntryFunc that removes a // target. func (s *SourceState) newRemoveTargetStateEntryFunc() TargetStateEntryFunc { return func(destSystem System, destAbsPath AbsPath) (TargetStateEntry, error) { return &TargetStateRemove{}, nil } } // newScriptTargetStateEntryFunc returns a targetStateEntryFunc that returns a // script with sourceLazyContents. func (s *SourceState) newScriptTargetStateEntryFunc( sourceRelPath SourceRelPath, fileAttr FileAttr, targetRelPath RelPath, sourceContentsFunc func() ([]byte, error), interpreter *Interpreter, ) TargetStateEntryFunc { return func(destSystem System, destAbsPath AbsPath) (TargetStateEntry, error) { contentsFunc := sync.OnceValues(func() ([]byte, error) { contents, err := sourceContentsFunc() if err != nil { return nil, err } if fileAttr.Template { contents, err = s.ExecuteTemplateData(ExecuteTemplateDataOptions{ NameRelPath: sourceRelPath.RelPath(), Data: contents, DestAbsPath: destAbsPath, }) if err != nil { return nil, err } } return contents, nil }) return &TargetStateScript{ name: targetRelPath, contentsFunc: contentsFunc, contentsSHA256Func: lazySHA256(contentsFunc), condition: fileAttr.Condition, interpreter: interpreter, sourceAttr: SourceAttr{ Condition: fileAttr.Condition, }, sourceRelPath: sourceRelPath, }, nil } } // newSymlinkTargetStateEntryFunc returns a targetStateEntryFunc that returns a // symlink with the linkname sourceLazyContents. func (s *SourceState) newSymlinkTargetStateEntryFunc( sourceRelPath SourceRelPath, fileAttr FileAttr, contentsFunc func() ([]byte, error), ) TargetStateEntryFunc { return func(destSystem System, destAbsPath AbsPath) (TargetStateEntry, error) { linknameFunc := func() (string, error) { linknameBytes, err := contentsFunc() if err != nil { return "", err } if fileAttr.Template { linknameBytes, err = s.ExecuteTemplateData(ExecuteTemplateDataOptions{ NameRelPath: sourceRelPath.RelPath(), Data: linknameBytes, DestAbsPath: destAbsPath, }) if err != nil { return "", err } } linkname := normalizeLinkname(string(bytes.TrimSpace(linknameBytes))) return linkname, nil } return &TargetStateSymlink{ linknameFunc: linknameFunc, }, nil } } // newSourceStateFile returns a possibly new target RalPath and a new // SourceStateFile. func (s *SourceState) newSourceStateFile( absPath AbsPath, sourceRelPath SourceRelPath, fileAttr FileAttr, targetRelPath RelPath, ) (RelPath, *SourceStateFile) { contentsFunc := sync.OnceValues(func() ([]byte, error) { contents, err := s.system.ReadFile(s.sourceDirAbsPath.Join(sourceRelPath.RelPath())) if err != nil { return nil, err } if fileAttr.Encrypted { contents, err = s.encryption.Decrypt(contents) if err != nil { return nil, err } } return contents, nil }) var targetStateEntryFunc TargetStateEntryFunc switch fileAttr.Type { case SourceFileTypeCreate: targetStateEntryFunc = s.newCreateTargetStateEntryFunc(sourceRelPath, fileAttr, contentsFunc) case SourceFileTypeFile: targetStateEntryFunc = s.newFileTargetStateEntryFunc(sourceRelPath, fileAttr, contentsFunc) case SourceFileTypeModify: // If the target has an extension, determine if it indicates an // interpreter to use. extension := strings.ToLower(strings.TrimPrefix(targetRelPath.Ext(), ".")) if interpreter, ok := s.interpreters[extension]; ok { // For modify scripts, the script extension is not considered part // of the target name, so remove it. targetRelPath = targetRelPath.Slice(0, targetRelPath.Len()-len(extension)-1) targetStateEntryFunc = s.newModifyTargetStateEntryFunc(sourceRelPath, fileAttr, contentsFunc, &interpreter) } else { targetStateEntryFunc = s.newModifyTargetStateEntryFunc(sourceRelPath, fileAttr, contentsFunc, nil) } case SourceFileTypeRemove: targetStateEntryFunc = s.newRemoveTargetStateEntryFunc() case SourceFileTypeScript: // If the script has an extension, determine if it indicates an // interpreter to use. extension := strings.ToLower(strings.TrimPrefix(targetRelPath.Ext(), ".")) if interpreter, ok := s.interpreters[extension]; ok { targetStateEntryFunc = s.newScriptTargetStateEntryFunc( sourceRelPath, fileAttr, targetRelPath, contentsFunc, &interpreter, ) } else { targetStateEntryFunc = s.newScriptTargetStateEntryFunc(sourceRelPath, fileAttr, targetRelPath, contentsFunc, nil) } case SourceFileTypeSymlink: targetStateEntryFunc = s.newSymlinkTargetStateEntryFunc(sourceRelPath, fileAttr, contentsFunc) default: panic(fmt.Sprintf("%d: unsupported type", fileAttr.Type)) } return targetRelPath, &SourceStateFile{ origin: SourceStateOriginAbsPath(absPath), sourceRelPath: sourceRelPath, attr: fileAttr, contentsFunc: contentsFunc, contentsSHA256Func: lazySHA256(contentsFunc), targetStateEntryFunc: targetStateEntryFunc, } } // newSourceStateDirEntry returns a SourceStateEntry constructed from a // directory in s. func (s *SourceState) newSourceStateDirEntry( actualStateDir *ActualStateDir, fileInfo fs.FileInfo, parentSourceRelPath SourceRelPath, targetRelPath RelPath, options *AddOptions, ) *SourceStateDir { dirAttr := DirAttr{ TargetName: fileInfo.Name(), Exact: options.shouldBeExact(targetRelPath), Private: isPrivate(fileInfo), ReadOnly: isReadOnly(fileInfo), } sourceRelPath := parentSourceRelPath.Join(NewSourceRelDirPath(dirAttr.SourceName())) return &SourceStateDir{ attr: dirAttr, origin: actualStateDir, sourceRelPath: sourceRelPath, targetStateEntry: &TargetStateDir{ perm: fs.ModePerm &^ s.umask, }, } } // newSourceStateFileEntryFromFile returns a SourceStateEntry constructed from a // file in s. func (s *SourceState) newSourceStateFileEntryFromFile( actualStateFile *ActualStateFile, fileInfo fs.FileInfo, parentSourceRelPath SourceRelPath, options *AddOptions, ) (*SourceStateFile, error) { fileAttr := FileAttr{ TargetName: fileInfo.Name(), Encrypted: options.Encrypt, Executable: IsExecutable(fileInfo), Private: isPrivate(fileInfo), ReadOnly: isReadOnly(fileInfo), Template: options.Template, } if options.Create { fileAttr.Type = SourceFileTypeCreate } else { fileAttr.Type = SourceFileTypeFile } contents, err := actualStateFile.Contents() if err != nil { return nil, err } if options.Template { if !utf8.Valid(contents) { s.warnFunc("%s: invalid UTF-8\n", fileInfo.Name()) } for _, byteOrderMark := range byteOrderMarks { if bytes.HasPrefix(contents, byteOrderMark.prefix) && byteOrderMark.name != "UTF-8" { s.warnFunc( "%s: detected %s byte order mark, ensure that template is in UTF-8\n", fileInfo.Name(), byteOrderMark.name, ) } } } if options.AutoTemplate { var replacements bool contents, replacements = autoTemplate(contents, s.TemplateData()) if replacements { fileAttr.Template = true } } if len(contents) == 0 { fileAttr.Empty = true } if options.Encrypt { contents, err = s.encryption.Encrypt(contents) if err != nil { return nil, err } } contentsFunc := eagerNoErr(contents) contentsSHA256Func := lazySHA256(contentsFunc) sourceRelPath := parentSourceRelPath.Join(NewSourceRelPath(fileAttr.SourceName(s.encryption.EncryptedSuffix()))) return &SourceStateFile{ attr: fileAttr, origin: actualStateFile, sourceRelPath: sourceRelPath, contentsFunc: contentsFunc, contentsSHA256Func: contentsSHA256Func, targetStateEntry: &TargetStateFile{ contentsFunc: contentsFunc, contentsSHA256Func: contentsSHA256Func, empty: len(contents) == 0, perm: 0o666 &^ s.umask, }, }, nil } // newSourceStateFileEntryFromSymlink returns a SourceStateEntry constructed // from a symlink in s. func (s *SourceState) newSourceStateFileEntryFromSymlink( actualStateSymlink *ActualStateSymlink, fileInfo fs.FileInfo, parentSourceRelPath SourceRelPath, options *AddOptions, ) (*SourceStateFile, error) { linkname, err := actualStateSymlink.Linkname() if err != nil { return nil, err } contents := []byte(linkname) isTemplate := false switch { case options.AutoTemplate: contents, isTemplate = autoTemplate(contents, s.TemplateData()) case options.Template: isTemplate = true case !options.Template && options.TemplateSymlinks: switch { case strings.HasPrefix(linkname, s.sourceDirAbsPath.String()+"/"): contents = []byte("{{ .chezmoi.sourceDir }}/" + linkname[s.sourceDirAbsPath.Len()+1:]) isTemplate = true case strings.HasPrefix(linkname, s.destDirAbsPath.String()+"/"): contents = []byte("{{ .chezmoi.homeDir }}/" + linkname[s.destDirAbsPath.Len()+1:]) isTemplate = true } } contents = append(contents, '\n') contentsFunc := eagerNoErr(contents) contentsSHA256Func := lazySHA256(contentsFunc) fileAttr := FileAttr{ TargetName: fileInfo.Name(), Type: SourceFileTypeSymlink, Template: isTemplate, } sourceRelPath := parentSourceRelPath.Join(NewSourceRelPath(fileAttr.SourceName(s.encryption.EncryptedSuffix()))) return &SourceStateFile{ attr: fileAttr, sourceRelPath: sourceRelPath, contentsFunc: contentsFunc, contentsSHA256Func: contentsSHA256Func, origin: SourceStateOriginAbsPath(s.sourceDirAbsPath.Join(sourceRelPath.RelPath())), targetStateEntry: &TargetStateFile{ contentsFunc: contentsFunc, contentsSHA256Func: contentsSHA256Func, perm: 0o666 &^ s.umask, }, }, nil } // populateImplicitParentDirs creates implicit parent directories for // externalRelPath. func (s *SourceState) populateImplicitParentDirs( externalRelPath RelPath, external *External, sourceStateEntries map[RelPath][]SourceStateEntry, ) map[RelPath][]SourceStateEntry { for relPath := externalRelPath.Dir(); relPath != DotRelPath; relPath = relPath.Dir() { sourceStateEntries[relPath] = append(sourceStateEntries[relPath], &SourceStateImplicitDir{ origin: external, targetStateEntry: &TargetStateDir{ perm: fs.ModePerm &^ s.umask, }, }, ) } return sourceStateEntries } // readExternal reads an external and returns its SourceStateEntries. func (s *SourceState) readExternal( ctx context.Context, externalRelPath RelPath, parentSourceRelPath SourceRelPath, external *External, options *ReadOptions, ) (map[RelPath][]SourceStateEntry, error) { switch external.Type { case ExternalTypeArchive: return s.readExternalArchive(ctx, externalRelPath, parentSourceRelPath, external, options) case ExternalTypeArchiveFile: return s.readExternalArchiveFile(ctx, externalRelPath, parentSourceRelPath, external, options) case ExternalTypeFile: return s.readExternalFile(ctx, externalRelPath, parentSourceRelPath, external, options) case ExternalTypeGitRepo: return nil, nil case "": return nil, fmt.Errorf("%s: missing external type", externalRelPath) default: return nil, fmt.Errorf("%s: unknown external type: %s", externalRelPath, external.Type) } } // readExternalArchive reads an external archive and returns its // SourceStateEntries. func (s *SourceState) readExternalArchive( ctx context.Context, externalRelPath RelPath, parentSourceRelPath SourceRelPath, external *External, options *ReadOptions, ) (map[RelPath][]SourceStateEntry, error) { data, urlStr, format, err := s.readExternalArchiveData(ctx, externalRelPath, external, options) if err != nil { return nil, err } dirAttr := DirAttr{ TargetName: externalRelPath.Base(), Exact: external.Exact, } sourceStateDir := &SourceStateDir{ attr: dirAttr, origin: external, sourceRelPath: parentSourceRelPath.Join(NewSourceRelPath(dirAttr.SourceName())), targetStateEntry: &TargetStateDir{ perm: fs.ModePerm &^ s.umask, sourceAttr: SourceAttr{ External: true, }, }, } sourceStateEntries := map[RelPath][]SourceStateEntry{ externalRelPath: {sourceStateDir}, } patternSet := NewPatternSet() for _, includePattern := range external.Include { if err := patternSet.Add(includePattern, PatternSetInclude); err != nil { return nil, err } } for _, excludePattern := range external.Exclude { if err := patternSet.Add(excludePattern, PatternSetExclude); err != nil { return nil, err } } sourceRelPaths := make(map[RelPath]SourceRelPath) if err := WalkArchive(data, format, func(name string, fileInfo fs.FileInfo, r io.Reader, linkname string) error { // Perform matching against the name before stripping any components, // otherwise it is not possible to differentiate between // identically-named files at the same level. if patternSet.Match(name) == PatternSetMatchExclude { // In case that `name` is a directory which matched an explicit // exclude pattern, return fs.SkipDir to exclude not just the // directory itself but also everything it contains (recursively). if fileInfo.IsDir() && len(patternSet.ExcludePatterns) > 0 { return fs.SkipDir } return nil } if external.StripComponents > 0 { components := strings.Split(name, "/") if len(components) <= external.StripComponents { return nil } name = path.Join(components[external.StripComponents:]...) } if name == "" { return nil } targetRelPath := externalRelPath.JoinString(name) if s.Ignore(targetRelPath) { if fileInfo.IsDir() { return fs.SkipDir } return nil } dirTargetRelPath, _ := targetRelPath.Split() dirSourceRelPath := sourceRelPaths[dirTargetRelPath] var sourceStateEntry SourceStateEntry switch { case fileInfo.IsDir(): targetStateEntry := &TargetStateDir{ perm: fileInfo.Mode().Perm() &^ s.umask, sourceAttr: SourceAttr{ External: true, }, } dirAttr := DirAttr{ TargetName: fileInfo.Name(), Exact: external.Exact, Private: isPrivate(fileInfo), ReadOnly: isReadOnly(fileInfo), } sourceStateEntry = &SourceStateDir{ attr: dirAttr, origin: external, sourceRelPath: parentSourceRelPath.Join(dirSourceRelPath, NewSourceRelPath(dirAttr.SourceName())), targetStateEntry: targetStateEntry, } case fileInfo.Mode()&fs.ModeType == 0: contents, err := io.ReadAll(r) if err != nil { return fmt.Errorf("%s: %w", name, err) } if !external.Archive.ExtractAppleDoubleFiles && isAppleDoubleFile(name, contents) { return nil } contentsFunc := eagerNoErr(contents) contentsSHA256Func := lazySHA256(contentsFunc) fileAttr := FileAttr{ TargetName: fileInfo.Name(), Type: SourceFileTypeFile, Empty: fileInfo.Size() == 0, Executable: IsExecutable(fileInfo), Private: isPrivate(fileInfo), ReadOnly: isReadOnly(fileInfo), } sourceRelPath := NewSourceRelPath(fileAttr.SourceName(s.encryption.EncryptedSuffix())) targetStateEntry := &TargetStateFile{ contentsFunc: contentsFunc, contentsSHA256Func: contentsSHA256Func, empty: fileAttr.Empty, perm: fileAttr.perm() &^ s.umask, sourceAttr: SourceAttr{ External: true, }, } sourceStateEntry = &SourceStateFile{ attr: fileAttr, contentsFunc: contentsFunc, contentsSHA256Func: contentsSHA256Func, origin: external, sourceRelPath: parentSourceRelPath.Join(dirSourceRelPath, sourceRelPath), targetStateEntry: targetStateEntry, } case fileInfo.Mode()&fs.ModeType == fs.ModeSymlink: fileAttr := FileAttr{ TargetName: fileInfo.Name(), Type: SourceFileTypeSymlink, } sourceRelPath := NewSourceRelPath(fileAttr.SourceName(s.encryption.EncryptedSuffix())) targetStateEntry := &TargetStateSymlink{ linknameFunc: sync.OnceValues(func() (string, error) { return linkname, nil }), sourceAttr: SourceAttr{ External: true, }, } sourceStateEntry = &SourceStateFile{ attr: fileAttr, origin: external, sourceRelPath: parentSourceRelPath.Join(dirSourceRelPath, sourceRelPath), targetStateEntry: targetStateEntry, } default: return fmt.Errorf("%s: unsupported mode %o", name, fileInfo.Mode()&fs.ModeType) } sourceStateEntries[targetRelPath] = append(sourceStateEntries[targetRelPath], sourceStateEntry) return nil }); err != nil { return nil, fmt.Errorf("%s: %s: %w", externalRelPath, urlStr, err) } return s.populateImplicitParentDirs(externalRelPath, external, sourceStateEntries), nil } // readExternalArchiveData reads an external archive's data and returns its data // and format. func (s *SourceState) readExternalArchiveData( ctx context.Context, externalRelPath RelPath, external *External, options *ReadOptions, ) ([]byte, string, ArchiveFormat, error) { data, urlStr, err := s.getExternalData(ctx, externalRelPath, external, options) if err != nil { return nil, "", ArchiveFormatUnknown, err } externalURL, err := url.Parse(urlStr) if err != nil { err := fmt.Errorf("%s: %s: %w", externalRelPath, urlStr, err) return nil, urlStr, ArchiveFormatUnknown, err } urlPath := externalURL.Path if external.Encrypted { urlPath = strings.TrimSuffix(urlPath, s.encryption.EncryptedSuffix()) } format := external.Format if format == ArchiveFormatUnknown { format = GuessArchiveFormat(urlPath, data) } return data, urlStr, format, nil } // readExternalArchiveFile reads a file from an external archive and returns its // SourceStateEntries. func (s *SourceState) readExternalArchiveFile( ctx context.Context, externalRelPath RelPath, parentSourceRelPath SourceRelPath, external *External, options *ReadOptions, ) (map[RelPath][]SourceStateEntry, error) { if external.ArchivePath == "" { return nil, fmt.Errorf("%s: missing path", externalRelPath) } data, urlStr, format, err := s.readExternalArchiveData(ctx, externalRelPath, external, options) if err != nil { return nil, err } var sourceStateEntry SourceStateEntry if err := WalkArchive(data, format, func(name string, fileInfo fs.FileInfo, r io.Reader, linkname string) error { if external.StripComponents > 0 { components := strings.Split(name, "/") if len(components) <= external.StripComponents { return nil } name = path.Join(components[external.StripComponents:]...) } switch { case name == "": return nil case name != external.ArchivePath: // If this entry is a directory and it cannot contain the file we // are looking for then skip this directory. if fileInfo.IsDir() && !strings.HasPrefix(external.ArchivePath, name) { return fs.SkipDir } return nil case fileInfo.Mode()&fs.ModeType == 0: contents, err := io.ReadAll(r) if err != nil { return fmt.Errorf("%s: %w", name, err) } if !external.Archive.ExtractAppleDoubleFiles && isAppleDoubleFile(name, contents) { return nil } contentsFunc := eagerNoErr(contents) contentsSHA256Func := lazySHA256(contentsFunc) fileAttr := FileAttr{ TargetName: fileInfo.Name(), Type: SourceFileTypeFile, Empty: fileInfo.Size() == 0, Executable: IsExecutable(fileInfo) || external.Executable, Private: isPrivate(fileInfo) || external.Private, ReadOnly: isReadOnly(fileInfo) || external.ReadOnly, } sourceRelPath := parentSourceRelPath.Join(NewSourceRelPath(fileAttr.SourceName(s.encryption.EncryptedSuffix()))) targetStateEntry := &TargetStateFile{ contentsFunc: contentsFunc, contentsSHA256Func: contentsSHA256Func, empty: fileAttr.Empty, perm: fileAttr.perm() &^ s.umask, sourceAttr: SourceAttr{ External: true, }, } sourceStateEntry = &SourceStateFile{ attr: fileAttr, contentsFunc: contentsFunc, contentsSHA256Func: contentsSHA256Func, origin: external, sourceRelPath: sourceRelPath, targetStateEntry: targetStateEntry, } return fs.SkipAll case fileInfo.Mode()&fs.ModeType == fs.ModeSymlink: fileAttr := FileAttr{ TargetName: fileInfo.Name(), Type: SourceFileTypeSymlink, } sourceRelPath := parentSourceRelPath.Join(NewSourceRelPath(fileAttr.SourceName(s.encryption.EncryptedSuffix()))) targetStateEntry := &TargetStateSymlink{ linknameFunc: sync.OnceValues(func() (string, error) { return linkname, nil }), sourceAttr: SourceAttr{ External: true, }, } sourceStateEntry = &SourceStateFile{ attr: fileAttr, origin: external, sourceRelPath: sourceRelPath, targetStateEntry: targetStateEntry, } return fs.SkipAll default: return fmt.Errorf("%s: unsupported mode %o", name, fileInfo.Mode()&fs.ModeType) } }); err != nil { return nil, err } if sourceStateEntry == nil { return nil, fmt.Errorf("%s: path not found in %s", external.ArchivePath, urlStr) } return s.populateImplicitParentDirs(externalRelPath, external, map[RelPath][]SourceStateEntry{ externalRelPath: {sourceStateEntry}, }), nil } // readExternalDir returns all source state entries in an external_ dir. func (s *SourceState) readExternalDir( rootSourceAbsPath AbsPath, rootSourceRelPath SourceRelPath, rootTargetRelPath RelPath, ) (map[RelPath][]SourceStateEntry, error) { sourceStateEntries := make(map[RelPath][]SourceStateEntry) walkFunc := func(absPath AbsPath, fileInfo fs.FileInfo, err error) error { switch { case err != nil: return err case absPath == rootSourceAbsPath: return nil } relPath := absPath.MustTrimDirPrefix(rootSourceAbsPath) targetRelPath := rootTargetRelPath.Join(relPath) if s.Ignore(targetRelPath) { if fileInfo.IsDir() { return fs.SkipDir } return nil } var sourceStateEntry SourceStateEntry switch fileInfo.Mode().Type() { case 0: fileAttr := FileAttr{ TargetName: fileInfo.Name(), Type: SourceFileTypeFile, Empty: true, Executable: IsExecutable(fileInfo), Private: isPrivate(fileInfo), ReadOnly: isReadOnly(fileInfo), } contentsFunc := sync.OnceValues(func() ([]byte, error) { return s.system.ReadFile(absPath) }) contentsSHA256Func := lazySHA256(contentsFunc) sourceStateEntry = &SourceStateFile{ origin: SourceStateOriginAbsPath(absPath), attr: fileAttr, contentsFunc: contentsFunc, contentsSHA256Func: contentsSHA256Func, sourceRelPath: rootSourceRelPath.Join(relPath.SourceRelPath()), targetStateEntry: &TargetStateFile{ contentsFunc: contentsFunc, contentsSHA256Func: contentsSHA256Func, empty: true, perm: fileAttr.perm() &^ s.umask, }, } case fs.ModeDir: dirAttr := DirAttr{ TargetName: fileInfo.Name(), Exact: true, Private: isPrivate(fileInfo), ReadOnly: isReadOnly(fileInfo), } sourceStateEntry = &SourceStateDir{ origin: SourceStateOriginAbsPath(absPath), sourceRelPath: rootSourceRelPath.Join(relPath.SourceRelDirPath()), attr: dirAttr, targetStateEntry: &TargetStateDir{ perm: dirAttr.perm() &^ s.umask, }, } case fs.ModeSymlink: fileAttr := FileAttr{ TargetName: fileInfo.Name(), Type: SourceFileTypeFile, } linknameFunc := sync.OnceValues(func() (string, error) { return s.system.Readlink(absPath) }) contentsFunc := sync.OnceValues(func() ([]byte, error) { linkname, err := linknameFunc() if err != nil { return nil, err } return []byte(linkname), nil }) sourceStateEntry = &SourceStateFile{ origin: SourceStateOriginAbsPath(absPath), attr: fileAttr, contentsFunc: contentsFunc, contentsSHA256Func: lazySHA256(contentsFunc), sourceRelPath: rootSourceRelPath.Join(relPath.SourceRelPath()), targetStateEntry: &TargetStateSymlink{ linknameFunc: linknameFunc, }, } } sourceStateEntries[targetRelPath] = append(sourceStateEntries[targetRelPath], sourceStateEntry) return nil } if err := Walk(s.system, rootSourceAbsPath, walkFunc); err != nil { return nil, err } return sourceStateEntries, nil } // readExternalFile reads an external file and returns its SourceStateEntries. func (s *SourceState) readExternalFile( ctx context.Context, externalRelPath RelPath, parentSourceRelPath SourceRelPath, external *External, options *ReadOptions, ) (map[RelPath][]SourceStateEntry, error) { contentsFunc := sync.OnceValues(func() ([]byte, error) { data, _, err := s.getExternalData(ctx, externalRelPath, external, options) return data, err }) fileAttr := FileAttr{ Empty: true, Executable: external.Executable, Private: external.Private, ReadOnly: external.ReadOnly, } targetStateEntry := &TargetStateFile{ contentsFunc: contentsFunc, contentsSHA256Func: lazySHA256(contentsFunc), empty: fileAttr.Empty, perm: fileAttr.perm() &^ s.umask, sourceAttr: SourceAttr{ External: true, }, } sourceStateEntry := &SourceStateFile{ origin: external, sourceRelPath: parentSourceRelPath.Join( NewSourceRelPath(fileAttr.SourceName(s.encryption.EncryptedSuffix())), ), targetStateEntry: targetStateEntry, } return s.populateImplicitParentDirs(externalRelPath, external, map[RelPath][]SourceStateEntry{ externalRelPath: {sourceStateEntry}, }), nil } // readScriptsDir reads all scripts in scriptsDirAbsPath. func (s *SourceState) readScriptsDir(ctx context.Context, scriptsDirAbsPath AbsPath) (map[RelPath][]SourceStateEntry, error) { var allSourceStateEntriesMu sync.Mutex allSourceStateEntries := make(map[RelPath][]SourceStateEntry) addSourceStateEntry := func(relPath RelPath, sourceStateEntry SourceStateEntry) { allSourceStateEntriesMu.Lock() allSourceStateEntries[relPath] = append(allSourceStateEntries[relPath], sourceStateEntry) allSourceStateEntriesMu.Unlock() } walkFunc := func(ctx context.Context, sourceAbsPath AbsPath, fileInfo fs.FileInfo, err error) error { if err != nil { return err } if sourceAbsPath == scriptsDirAbsPath { return nil } // Follow symlinks in the source directory. if fileInfo.Mode().Type() == fs.ModeSymlink { // Some programs (notably emacs) use invalid symlinks as lockfiles. // To avoid following them and getting an ENOENT error, check first // if this is an entry that we will ignore anyway. if strings.HasPrefix(fileInfo.Name(), ignorePrefix) && !strings.HasPrefix(fileInfo.Name(), Prefix) { return nil } fileInfo, err = s.system.Stat(sourceAbsPath) if err != nil { return err } } sourceRelPath := SourceRelPath{ relPath: sourceAbsPath.MustTrimDirPrefix(s.sourceDirAbsPath), isDir: fileInfo.IsDir(), } parentSourceRelPath, sourceName := sourceRelPath.Split() switch { case err != nil: return err case strings.HasPrefix(fileInfo.Name(), Prefix): return fmt.Errorf("%s: not allowed in %s directory", sourceAbsPath, scriptsDirName) case strings.HasPrefix(fileInfo.Name(), ignorePrefix): if fileInfo.IsDir() { return fs.SkipDir } return nil case fileInfo.IsDir(): return nil case fileInfo.Mode().IsRegular(): fa, err := parseFileAttr(sourceName.String(), s.encryption.EncryptedSuffix()) if err != nil { return err } if fa.Type != SourceFileTypeScript { return fmt.Errorf("%s: not a script", sourceAbsPath) } targetRelPath, err := parentSourceRelPath.Dir().TargetRelPath(s.encryption.EncryptedSuffix()) if err != nil { return err } targetRelPath = targetRelPath.JoinString(fa.TargetName) if s.Ignore(targetRelPath) { return nil } var sourceStateEntry SourceStateEntry targetRelPath, sourceStateEntry = s.newSourceStateFile(sourceAbsPath, sourceRelPath, fa, targetRelPath) addSourceStateEntry(targetRelPath, sourceStateEntry) return nil default: return &UnsupportedFileTypeError{ absPath: sourceAbsPath, mode: fileInfo.Mode(), } } } if err := concurrentWalkSourceDir(ctx, s.system, scriptsDirAbsPath, walkFunc); err != nil { return nil, err } return allSourceStateEntries, nil } // readVersionFile reads a .chezmoiversion file from sourceAbsPath and returns // an error if the version is newer that s's version. func (s *SourceState) readVersionFile(sourceAbsPath AbsPath) error { data, err := s.system.ReadFile(sourceAbsPath) if err != nil { return err } version, err := semver.NewVersion(strings.TrimSpace(string(data))) if err != nil { return fmt.Errorf("%s: %q: %w", sourceAbsPath, data, err) } var zeroVersion semver.Version if s.version != zeroVersion && s.version.LessThan(*version) { return &TooOldError{ Have: s.version, Need: *version, } } return nil } // sourceStateEntry returns a new SourceStateEntry based on actualStateEntry. func (s *SourceState) sourceStateEntry( actualStateEntry ActualStateEntry, destAbsPath AbsPath, fileInfo fs.FileInfo, parentSourceRelPath SourceRelPath, targetRelPath RelPath, options *AddOptions, ) (SourceStateEntry, error) { switch actualStateEntry := actualStateEntry.(type) { case *ActualStateAbsent: return nil, fmt.Errorf("%s: not found", destAbsPath) case *ActualStateDir: return s.newSourceStateDirEntry(actualStateEntry, fileInfo, parentSourceRelPath, targetRelPath, options), nil case *ActualStateFile: return s.newSourceStateFileEntryFromFile(actualStateEntry, fileInfo, parentSourceRelPath, options) case *ActualStateSymlink: return s.newSourceStateFileEntryFromSymlink(actualStateEntry, fileInfo, parentSourceRelPath, options) default: panic(fmt.Sprintf("%T: unsupported type", actualStateEntry)) } } func (e *External) IsExternal() bool { return true } func (e *External) Path() AbsPath { return e.sourceAbsPath } func (e *External) OriginString() string { urlStr := cmp.Or(append([]string{e.URL}, e.URLs...)...) return urlStr + " defined in " + e.sourceAbsPath.String() } // canonicalSourceStateEntry returns the canonical SourceStateEntry for the // given sourceStateEntries. // // This only applies to directories, where SourceStateImplicitDirs are // considered equivalent to all SourceStateDirs. func canonicalSourceStateEntry(sourceStateEntries []SourceStateEntry) (SourceStateEntry, bool) { // Find all directories to check for equivalence. var firstSourceStateDir *SourceStateDir sourceStateDirs := make([]SourceStateEntry, len(sourceStateEntries)) for i, sourceStateEntry := range sourceStateEntries { switch sourceStateEntry := sourceStateEntry.(type) { case *SourceStateDir: firstSourceStateDir = sourceStateEntry sourceStateDirs[i] = sourceStateEntry case *SourceStateImplicitDir: sourceStateDirs[i] = sourceStateEntry default: return nil, false } } switch len(sourceStateDirs) { case 0: // If there are no SourceStateDirs then there are no equivalent directories. return nil, false case 1: return sourceStateDirs[0], true default: // Check for equivalence. for _, sourceStateDir := range sourceStateDirs { switch sourceStateDir := sourceStateDir.(type) { case *SourceStateDir: if sourceStateDir.attr != firstSourceStateDir.attr { return nil, false } case *SourceStateImplicitDir: // SourceStateImplicitDirs are considered equivalent to all other // directories. } } // If all directories are equivalent then return the first real // *SourceStateDir, if it exists. if firstSourceStateDir != nil { return firstSourceStateDir, true } // Otherwise, return the first entry which is a *SourceStateImplicitDir. return sourceStateDirs[0], true } } // isAppleDoubleFile returns true if the file looks like and has the // expected signature of an AppleDouble file. func isAppleDoubleFile(name string, contents []byte) bool { return strings.HasPrefix(path.Base(name), appleDoubleNamePrefix) && bytes.HasPrefix(contents, appleDoubleContentsPrefix) } ================================================ FILE: internal/chezmoi/sourcestate_test.go ================================================ package chezmoi import ( "archive/tar" "bytes" "crypto/sha256" "errors" "fmt" "io/fs" "net/http" "net/http/httptest" "path/filepath" "testing" "text/template" "time" "github.com/alecthomas/assert/v2" "github.com/coreos/go-semver/semver" vfs "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" "chezmoi.io/chezmoi/internal/chezmoiset" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestSourceStateAdd(t *testing.T) { for _, tc := range []struct { name string destAbsPaths []AbsPath addOptions AddOptions extraRoot any tests []any }{ { name: "dir", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.dir"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/file", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/subdir", vfst.TestDoesNotExist(), ), }, }, { name: "dir_change_attributes", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.dir"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, extraRoot: map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi/exact_dot_dir/file": "# contents of .dir/file\n", }, }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/exact_dot_dir", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .dir/file\n"), ), }, }, { name: "dir_file", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.dir/file"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .dir/file\n"), ), }, }, { name: "dir_file_existing_dir", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.dir/file"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, extraRoot: map[string]any{ "/home/user/.local/share/chezmoi/dot_dir": &vfst.Dir{Perm: fs.ModePerm}, }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/file", vfst.TestModeIsRegular(), vfst.TestContentsString("# contents of .dir/file\n"), ), }, }, { name: "dir_subdir", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.dir/subdir"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/subdir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/subdir/file", vfst.TestDoesNotExist(), ), }, }, { name: "dir_subdir_file", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.dir/subdir/file"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/file", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/subdir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/subdir/file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .dir/subdir/file\n"), ), }, }, { name: "dir_subdir_file_existing_dir_subdir", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.dir/subdir/file"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, extraRoot: map[string]any{ "/home/user/.local/share/chezmoi/dot_dir/subdir": &vfst.Dir{Perm: fs.ModePerm}, }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/subdir/file", vfst.TestModeIsRegular(), vfst.TestContentsString("# contents of .dir/subdir/file\n"), ), }, }, { name: "dir_readonly_unix", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.readonly_dir"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, extraRoot: map[string]any{ "/home/user/.readonly_dir": &vfst.Dir{Perm: 0o555}, }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/readonly_dot_readonly_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), }, }, { name: "empty", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.empty"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_empty", vfst.TestDoesNotExist(), ), }, }, { name: "empty_with_empty", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.empty"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/empty_dot_empty", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContents(nil), ), }, }, { name: "executable_unix", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.executable"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/executable_dot_executable", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .executable\n"), ), }, }, { name: "executable_windows", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.executable"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_executable", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .executable\n"), ), }, }, { name: "create", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.create"), }, addOptions: AddOptions{ Create: true, Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/create_dot_create", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .create\n"), ), }, }, { name: "file", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.file"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .file\n"), ), }, }, { name: "file_change_attributes", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.file"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, extraRoot: map[string]any{ "/home/user/.local/share/chezmoi/executable_dot_file": "# contents of .file\n", }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .file\n"), ), vfst.TestPath("/home/user/.local/share/chezmoi/executable_dot_file", vfst.TestDoesNotExist(), ), }, }, { name: "file_replace_contents", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.file"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, extraRoot: map[string]any{ "/home/user/.local/share/chezmoi/dot_file": "# old contents of .file\n", }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .file\n"), ), }, }, { name: "private_unix", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.private"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/private_dot_private", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .private\n"), ), }, }, { name: "private_windows", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.private"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_private", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .private\n"), ), }, }, { name: "file_readonly_unix", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.readonly"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, extraRoot: map[string]any{ "/home/user/.readonly": &vfst.File{ Perm: 0o444, Contents: []byte("# contents of .readonly\n"), }, }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/readonly_dot_readonly", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .readonly\n"), ), }, }, { name: "symlink", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.symlink"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/symlink_dot_symlink", vfst.TestModeIsRegular(), vfst.TestContentsString(".dir/subdir/file\n"), ), }, }, { name: "symlink_backslash_windows", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.symlink_windows"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, extraRoot: map[string]any{ "/home/user": map[string]any{ ".symlink_windows": &vfst.Symlink{Target: ".dir\\subdir\\file"}, }, }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/symlink_dot_symlink_windows", vfst.TestModeIsRegular(), vfst.TestContentsString(".dir/subdir/file\n"), ), }, }, { name: "template", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.template"), }, addOptions: AddOptions{ AutoTemplate: true, Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_template.tmpl", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("key = {{ .variable }}\n"), ), }, }, { name: "dir_and_dir_file", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.dir"), NewAbsPath("/home/user/.dir/file"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .dir/file\n"), ), }, }, { name: "file_in_dir_exact_subdir", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.dir/subdir/file"), }, addOptions: AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, extraRoot: map[string]any{ "/home/user/.local/share/chezmoi/dot_dir/exact_subdir": &vfst.Dir{Perm: fs.ModePerm}, }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/exact_subdir/file", vfst.TestModeIsRegular(), vfst.TestContentsString("# contents of .dir/subdir/file\n"), ), }, }, { name: "exact_subdir_not_exact_parent", destAbsPaths: []AbsPath{ NewAbsPath("/home/user/.dir/subdir"), }, addOptions: AddOptions{ Exact: true, ExactTargetRelPaths: chezmoiset.New(NewRelPath(".dir/subdir")), Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }, tests: []any{ // Parent directory should NOT have exact_ prefix vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), // Target directory should have exact_ prefix vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/exact_subdir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), // Verify that exact_dot_dir does NOT exist (parent should not be exact) vfst.TestPath("/home/user/.local/share/chezmoi/exact_dot_dir", vfst.TestDoesNotExist(), ), }, }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.SkipUnlessGOOS(t, tc.name) chezmoitest.WithTestFS(t, map[string]any{ "/home/user": map[string]any{ ".create": "# contents of .create\n", ".dir": map[string]any{ "file": "# contents of .dir/file\n", "subdir": map[string]any{ "file": "# contents of .dir/subdir/file\n", }, }, ".empty": "", ".executable": &vfst.File{ Perm: fs.ModePerm, Contents: []byte("# contents of .executable\n"), }, ".file": "# contents of .file\n", ".local": map[string]any{ "share": map[string]any{ "chezmoi": &vfst.Dir{Perm: fs.ModePerm}, }, }, ".private": &vfst.File{ Perm: 0o600, Contents: []byte("# contents of .private\n"), }, ".symlink": &vfst.Symlink{Target: ".dir/subdir/file"}, ".template": "key = value\n", }, }, func(fileSystem vfs.FS) { ctx := t.Context() system := NewRealSystem(fileSystem) persistentState := NewMockPersistentState() if tc.extraRoot != nil { assert.NoError(t, vfst.NewBuilder().Build(system.UnderlyingFS(), tc.extraRoot)) } s := NewSourceState( WithBaseSystem(system), WithDestDir(NewAbsPath("/home/user")), WithSourceDir(NewAbsPath("/home/user/.local/share/chezmoi")), WithSystem(system), withUserTemplateData(map[string]any{ "variable": "value", }), ) assert.NoError(t, s.Read(ctx, nil)) requireEvaluateAll(t, s, system) destAbsPathInfos := make(map[AbsPath]fs.FileInfo) for _, destAbsPath := range tc.destAbsPaths { assert.NoError(t, s.AddDestAbsPathInfos(destAbsPathInfos, system, destAbsPath, nil)) } assert.NoError(t, s.Add(system, persistentState, system, destAbsPathInfos, &tc.addOptions)) vfst.RunTests(t, fileSystem, "", tc.tests...) }) }) } } func TestSourceStateAddInExternal(t *testing.T) { buffer := &bytes.Buffer{} tarWriterSystem := NewTarWriterSystem(buffer, tar.Header{}) assert.NoError(t, tarWriterSystem.Mkdir(NewAbsPath("dir"), fs.ModePerm)) assert.NoError(t, tarWriterSystem.WriteFile(NewAbsPath("dir/file"), []byte("# contents of dir/file\n"), 0o666)) assert.NoError(t, tarWriterSystem.Close()) archiveData := buffer.Bytes() httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, err := w.Write(archiveData) assert.NoError(t, err) })) defer httpServer.Close() root := map[string]any{ "/home/user": map[string]any{ ".dir/file2": "# contents of .dir/file2\n", ".local/share/chezmoi": map[string]any{ ".chezmoiexternal.toml": chezmoitest.JoinLines( `[".dir"]`, ` type = "archive"`, ` url = "`+httpServer.URL+`/archive.tar"`, ` stripComponents = 1`, ), "dot_dir": &vfst.Dir{Perm: fs.ModePerm}, }, }, } chezmoitest.WithTestFS(t, root, func(fileSystem vfs.FS) { ctx := t.Context() system := NewRealSystem(fileSystem) persistentState := NewMockPersistentState() s := NewSourceState( WithBaseSystem(system), WithCacheDir(NewAbsPath("/home/user/.cache/chezmoi")), WithDestDir(NewAbsPath("/home/user")), WithSourceDir(NewAbsPath("/home/user/.local/share/chezmoi")), WithSystem(system), ) assert.NoError(t, s.Read(ctx, nil)) destAbsPath := NewAbsPath("/home/user/.dir/file2") fileInfo, err := system.Stat(destAbsPath) assert.NoError(t, err) destAbsPathInfos := map[AbsPath]fs.FileInfo{ destAbsPath: fileInfo, } assert.NoError(t, s.Add(system, persistentState, system, destAbsPathInfos, &AddOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), })) vfst.RunTests(t, fileSystem, "", vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/file2", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .dir/file2\n"), ), ) }) } func TestSourceStateApplyAll(t *testing.T) { for _, tc := range []struct { name string root any sourceStateOptions []SourceStateOption tests []any }{ { name: "empty", root: map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi": &vfst.Dir{Perm: fs.ModePerm}, }, }, }, { name: "dir", root: map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi": map[string]any{ "dot_dir": &vfst.Dir{Perm: fs.ModePerm}, }, }, }, tests: []any{ vfst.TestPath("/home/user/.dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), }, }, { name: "dir_exact", root: map[string]any{ "/home/user": map[string]any{ ".dir": map[string]any{ "file": "# contents of .dir/file\n", }, ".local/share/chezmoi": map[string]any{ "exact_dot_dir": &vfst.Dir{Perm: fs.ModePerm}, }, }, }, tests: []any{ vfst.TestPath("/home/user/.dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.dir/file", vfst.TestDoesNotExist(), ), }, }, { name: "file", root: map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi": map[string]any{ "dot_file": "# contents of .file\n", }, }, }, tests: []any{ vfst.TestPath("/home/user/.file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .file\n"), ), }, }, { name: "file_remove_empty", root: map[string]any{ "/home/user": map[string]any{ ".empty": "# contents of .empty\n", ".local/share/chezmoi": map[string]any{ "dot_empty": "", }, }, }, tests: []any{ vfst.TestPath("/home/user/.empty", vfst.TestDoesNotExist(), ), }, }, { name: "file_create_empty", root: map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi": map[string]any{ "empty_dot_empty": "", }, }, }, tests: []any{ vfst.TestPath("/home/user/.empty", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContents(nil), ), }, }, { name: "file_template", root: map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi": map[string]any{ "dot_template.tmpl": "key = {{ .variable }}\n", }, }, }, sourceStateOptions: []SourceStateOption{ withUserTemplateData(map[string]any{ "variable": "value", }), }, tests: []any{ vfst.TestPath("/home/user/.template", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("key = value\n"), ), }, }, { name: "create", root: map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi": map[string]any{ "create_dot_create": "# contents of .create\n", }, }, }, tests: []any{ vfst.TestPath("/home/user/.create", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .create\n"), ), }, }, { name: "create_no_replace", root: map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi": map[string]any{ "create_dot_create": "# contents of .create\n", }, ".create": "# existing contents of .create\n", }, }, tests: []any{ vfst.TestPath("/home/user/.create", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# existing contents of .create\n"), ), }, }, { name: "symlink", root: map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi": map[string]any{ "symlink_dot_symlink": ".dir/subdir/file\n", }, }, }, tests: []any{ vfst.TestPath("/home/user/.symlink", vfst.TestModeType(fs.ModeSymlink), vfst.TestSymlinkTarget(filepath.FromSlash(".dir/subdir/file")), ), }, }, { name: "symlink_template", root: map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi": map[string]any{ "symlink_dot_symlink.tmpl": `{{ ".dir/subdir/file" }}` + "\n", }, }, }, tests: []any{ vfst.TestPath("/home/user/.symlink", vfst.TestModeType(fs.ModeSymlink), vfst.TestSymlinkTarget(filepath.FromSlash(".dir/subdir/file")), ), }, }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { ctx := t.Context() system := NewRealSystem(fileSystem) persistentState := NewMockPersistentState() sourceStateOptions := []SourceStateOption{ WithBaseSystem(system), WithDestDir(NewAbsPath("/home/user")), WithSourceDir(NewAbsPath("/home/user/.local/share/chezmoi")), WithSystem(system), } sourceStateOptions = append(sourceStateOptions, tc.sourceStateOptions...) s := NewSourceState(sourceStateOptions...) assert.NoError(t, s.Read(ctx, nil)) requireEvaluateAll(t, s, system) err := s.applyAll(system, system, persistentState, NewAbsPath("/home/user"), ApplyOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), Umask: chezmoitest.Umask, }) assert.NoError(t, err) vfst.RunTests(t, fileSystem, "", tc.tests...) }) }) } } func TestSourceStateExecuteTemplateData(t *testing.T) { for _, tc := range []struct { name string dataStr string expectedStr string }{ { name: "line_ending_lf", dataStr: "" + "unix\n" + "\n" + "windows\r\n" + "\r\n" + "# chezmoi:template:line-ending=lf\n", expectedStr: chezmoitest.JoinLines( "unix", "", "windows", "", ), }, { name: "line_endings_lf", dataStr: "" + "unix\n" + "\n" + "windows\r\n" + "\r\n" + "# chezmoi:template:line-endings=lf\n", expectedStr: chezmoitest.JoinLines( "unix", "", "windows", "", ), }, } { t.Run(tc.name, func(t *testing.T) { s := NewSourceState() actual, err := s.ExecuteTemplateData(ExecuteTemplateDataOptions{ NameRelPath: NewRelPath(tc.name), Data: []byte(tc.dataStr), }) assert.NoError(t, err) assert.Equal(t, tc.expectedStr, string(actual)) }) } } func TestSourceStateRead(t *testing.T) { for _, tc := range []struct { name string root any expectedError string expectedSourceState *SourceState }{ { name: "empty", root: map[string]any{ "/home/user/.local/share/chezmoi": &vfst.Dir{Perm: fs.ModePerm}, }, expectedSourceState: NewSourceState(), }, { name: "dir", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ "dir": &vfst.Dir{ Perm: fs.ModePerm &^ chezmoitest.Umask, }, }, }, expectedSourceState: NewSourceState( withEntries(map[RelPath]SourceStateEntry{ NewRelPath("dir"): &SourceStateDir{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/dir")), sourceRelPath: NewSourceRelDirPath("dir"), attr: DirAttr{ TargetName: "dir", }, targetStateEntry: &TargetStateDir{ perm: fs.ModePerm &^ chezmoitest.Umask, }, }, }), ), }, { name: "file", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ "dot_file": "# contents of .file\n", }, }, expectedSourceState: NewSourceState( withEntries(map[RelPath]SourceStateEntry{ NewRelPath(".file"): &SourceStateFile{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/dot_file")), sourceRelPath: NewSourceRelPath("dot_file"), attr: FileAttr{ TargetName: ".file", Type: SourceFileTypeFile, }, contentsFunc: eagerNoErr([]byte("# contents of .file\n")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("# contents of .file\n"))), targetStateEntry: &TargetStateFile{ contentsFunc: eagerNoErr([]byte("# contents of .file\n")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("# contents of .file\n"))), perm: 0o666 &^ chezmoitest.Umask, }, }, }), ), }, { name: "duplicate_target_file", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ "dot_file": "# contents of .file\n", "dot_file.tmpl": "# contents of .file\n", }, }, expectedError: ".file: inconsistent state (/home/user/.local/share/chezmoi/dot_file, /home/user/.local/share/chezmoi/dot_file.tmpl)", }, { name: "duplicate_target_dir", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ "dir": &vfst.Dir{ Perm: fs.ModePerm &^ chezmoitest.Umask, }, "exact_dir": &vfst.Dir{ Perm: fs.ModePerm &^ chezmoitest.Umask, }, }, }, expectedError: "dir: inconsistent state (/home/user/.local/share/chezmoi/dir, /home/user/.local/share/chezmoi/exact_dir)", }, { name: "duplicate_target_script", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ "run_script": "#!/bin/sh\n", "run_once_script": "#!/bin/sh\n", }, }, expectedError: "script: inconsistent state (/home/user/.local/share/chezmoi/run_once_script, /home/user/.local/share/chezmoi/run_script)", }, { name: "symlink_with_attr", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".file": "# contents of .file\n", "executable_dot_file": &vfst.Symlink{Target: ".file"}, }, }, expectedSourceState: NewSourceState( withEntries(map[RelPath]SourceStateEntry{ NewRelPath(".file"): &SourceStateFile{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/executable_dot_file")), sourceRelPath: NewSourceRelPath("executable_dot_file"), attr: FileAttr{ TargetName: ".file", Type: SourceFileTypeFile, Executable: true, }, contentsFunc: eagerNoErr([]byte("# contents of .file\n")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("# contents of .file\n"))), targetStateEntry: &TargetStateFile{ contentsFunc: eagerNoErr([]byte("# contents of .file\n")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("# contents of .file\n"))), perm: fs.ModePerm &^ chezmoitest.Umask, }, }, }), ), }, { name: "symlink_script", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".script": "# contents of .script\n", "run_script": &vfst.Symlink{Target: ".script"}, }, }, expectedSourceState: NewSourceState( withEntries(map[RelPath]SourceStateEntry{ NewRelPath("script"): &SourceStateFile{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/run_script")), sourceRelPath: NewSourceRelPath("run_script"), attr: FileAttr{ TargetName: "script", Type: SourceFileTypeScript, Condition: ScriptConditionAlways, }, contentsFunc: eagerNoErr([]byte("# contents of .script\n")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("# contents of .script\n"))), targetStateEntry: &TargetStateScript{ name: NewRelPath("script"), contentsFunc: eagerNoErr([]byte("# contents of .script\n")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("# contents of .script\n"))), condition: ScriptConditionAlways, sourceAttr: SourceAttr{ Condition: ScriptConditionAlways, }, sourceRelPath: NewSourceRelPath("run_script"), }, }, }), ), }, { name: "script", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ "run_script": "# contents of script\n", }, }, expectedSourceState: NewSourceState( withEntries(map[RelPath]SourceStateEntry{ NewRelPath("script"): &SourceStateFile{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/run_script")), sourceRelPath: NewSourceRelPath("run_script"), attr: FileAttr{ TargetName: "script", Type: SourceFileTypeScript, Condition: ScriptConditionAlways, }, contentsFunc: eagerNoErr([]byte("# contents of script\n")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("# contents of script\n"))), targetStateEntry: &TargetStateScript{ name: NewRelPath("script"), contentsFunc: eagerNoErr([]byte("# contents of script\n")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("# contents of script\n"))), condition: ScriptConditionAlways, sourceAttr: SourceAttr{ Condition: ScriptConditionAlways, }, sourceRelPath: NewSourceRelPath("run_script"), }, }, }), ), }, { name: "symlink", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ "symlink_dot_symlink": ".dir/subdir/file", }, }, expectedSourceState: NewSourceState( withEntries(map[RelPath]SourceStateEntry{ NewRelPath(".symlink"): &SourceStateFile{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/symlink_dot_symlink")), sourceRelPath: NewSourceRelPath("symlink_dot_symlink"), attr: FileAttr{ TargetName: ".symlink", Type: SourceFileTypeSymlink, }, contentsFunc: eagerNoErr([]byte(".dir/subdir/file")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte(".dir/subdir/file"))), targetStateEntry: &TargetStateSymlink{ linknameFunc: eagerNoErr(".dir/subdir/file"), }, }, }), ), }, { name: "file_in_dir", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ "dir": map[string]any{ "file": "# contents of .dir/file\n", }, }, }, expectedSourceState: NewSourceState( withEntries(map[RelPath]SourceStateEntry{ NewRelPath("dir"): &SourceStateDir{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/dir")), sourceRelPath: NewSourceRelDirPath("dir"), attr: DirAttr{ TargetName: "dir", }, targetStateEntry: &TargetStateDir{ perm: fs.ModePerm &^ chezmoitest.Umask, }, }, NewRelPath("dir/file"): &SourceStateFile{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/dir/file")), sourceRelPath: NewSourceRelPath("dir/file"), attr: FileAttr{ TargetName: "file", Type: SourceFileTypeFile, }, contentsFunc: eagerNoErr([]byte("# contents of .dir/file\n")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("# contents of .dir/file\n"))), targetStateEntry: &TargetStateFile{ contentsFunc: eagerNoErr([]byte("# contents of .dir/file\n")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("# contents of .dir/file\n"))), perm: 0o666 &^ chezmoitest.Umask, }, }, }), ), }, { name: "chezmoiignore", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiignore": "README.md\n", }, }, expectedSourceState: NewSourceState( withIgnore( mustNewPatternSet(t, map[string]PatternSetIncludeType{ "README.md": PatternSetInclude, }), ), ), }, { name: "chezmoiignore_ignore_file", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiignore": "README.md\n", "README.md": "", }, }, expectedSourceState: NewSourceState( withIgnore( mustNewPatternSet(t, map[string]PatternSetIncludeType{ "README.md": PatternSetInclude, }), ), withIgnoredRelPathStrs( "README.md", ), ), }, { name: "chezmoiignore_ignore_file_with_hash", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiignore": "README.md#\n", "README.md#": "", }, }, expectedSourceState: NewSourceState( withIgnore( mustNewPatternSet(t, map[string]PatternSetIncludeType{ "README.md#": PatternSetInclude, }), ), withIgnoredRelPathStrs( "README.md#", ), ), }, { name: "chezmoiignore_exact_dir", root: map[string]any{ "/home/user/dir": map[string]any{ "file1": "# contents of dir/file1\n", "file2": "# contents of dir/file2\n", "file3": "# contents of dir/file3\n", }, "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiignore": "dir/file3\n", "exact_dir": map[string]any{ "file1": "# contents of dir/file1\n", }, }, }, expectedSourceState: NewSourceState( withEntries(map[RelPath]SourceStateEntry{ NewRelPath("dir"): &SourceStateDir{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/exact_dir")), sourceRelPath: NewSourceRelDirPath("exact_dir"), attr: DirAttr{ TargetName: "dir", Exact: true, }, targetStateEntry: &TargetStateDir{ perm: fs.ModePerm &^ chezmoitest.Umask, }, }, NewRelPath("dir/file1"): &SourceStateFile{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/exact_dir/file1")), sourceRelPath: NewSourceRelPath("exact_dir/file1"), attr: FileAttr{ TargetName: "file1", Type: SourceFileTypeFile, }, contentsFunc: eagerNoErr([]byte("# contents of dir/file1\n")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("# contents of dir/file1\n"))), targetStateEntry: &TargetStateFile{ contentsFunc: eagerNoErr([]byte("# contents of dir/file1\n")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("# contents of dir/file1\n"))), perm: 0o666 &^ chezmoitest.Umask, }, }, NewRelPath("dir/file2"): &SourceStateRemove{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/exact_dir")), sourceRelPath: NewSourceRelDirPath("exact_dir"), targetRelPath: NewRelPath("dir/file2"), }, }), withIgnore( mustNewPatternSet(t, map[string]PatternSetIncludeType{ "dir/file3": PatternSetInclude, }), ), withIgnoredRelPathStrs( "dir/file3", ), ), }, { name: "chezmoiremove", root: map[string]any{ "/home/user/file": "", "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiremove": "file\n", }, }, expectedSourceState: NewSourceState( withEntries(map[RelPath]SourceStateEntry{ NewRelPath("file"): &SourceStateRemove{ origin: SourceStateOriginRemove{}, sourceRelPath: NewSourceRelPath(".chezmoiremove"), targetRelPath: NewRelPath("file"), }, }), withRemove( mustNewPatternSet(t, map[string]PatternSetIncludeType{ "file": PatternSetInclude, }), ), ), }, { name: "chezmoiremove_and_ignore", root: map[string]any{ "/home/user": map[string]any{ "file1": "", "file2": "", }, "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiignore": "file2\n", ".chezmoiremove": "file*\n", }, }, expectedSourceState: NewSourceState( withEntries(map[RelPath]SourceStateEntry{ NewRelPath("file1"): &SourceStateRemove{ origin: SourceStateOriginRemove{}, sourceRelPath: NewSourceRelPath(".chezmoiremove"), targetRelPath: NewRelPath("file1"), }, }), withIgnore( mustNewPatternSet(t, map[string]PatternSetIncludeType{ "file2": PatternSetInclude, }), ), withIgnoredRelPathStrs( "file2", ), withRemove( mustNewPatternSet(t, map[string]PatternSetIncludeType{ "file*": PatternSetInclude, }), ), ), }, { name: "chezmoiremove_and_ignore_in_subdir", root: map[string]any{ "/home/user": map[string]any{ "dir": map[string]any{ "file1": "", "file2": "", }, }, "/home/user/.local/share/chezmoi": map[string]any{ "dir/.chezmoiignore": "file2\n", "dir/.chezmoiremove": "file*\n", }, }, expectedSourceState: NewSourceState( withEntries(map[RelPath]SourceStateEntry{ NewRelPath("dir"): &SourceStateDir{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/dir")), sourceRelPath: NewSourceRelDirPath("dir"), attr: DirAttr{ TargetName: "dir", }, targetStateEntry: &TargetStateDir{ perm: fs.ModePerm &^ chezmoitest.Umask, }, }, NewRelPath("dir/file1"): &SourceStateRemove{ origin: SourceStateOriginRemove{}, sourceRelPath: NewSourceRelPath(".chezmoiremove"), targetRelPath: NewRelPath("dir/file1"), }, }), withIgnore( mustNewPatternSet(t, map[string]PatternSetIncludeType{ "dir/file2": PatternSetInclude, }), ), withIgnoredRelPathStrs( "dir/file2", ), withRemove( mustNewPatternSet(t, map[string]PatternSetIncludeType{ "dir/file*": PatternSetInclude, }), ), ), }, { name: "external", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ "external_dir": map[string]any{ "dot_file": "# contents of dir/dot_file\n", "subdir": map[string]any{ "empty_file": "", }, "symlink": &vfst.Symlink{Target: "dot_file"}, }, }, }, expectedSourceState: NewSourceState( withEntries(map[RelPath]SourceStateEntry{ NewRelPath("dir"): &SourceStateDir{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/external_dir")), sourceRelPath: NewSourceRelDirPath("external_dir"), attr: DirAttr{ TargetName: "dir", External: true, }, targetStateEntry: &TargetStateDir{ perm: fs.ModePerm &^ chezmoitest.Umask, }, }, NewRelPath("dir/dot_file"): &SourceStateFile{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/external_dir/dot_file")), sourceRelPath: NewSourceRelPath("external_dir/dot_file"), attr: FileAttr{ TargetName: "dot_file", Type: SourceFileTypeFile, Empty: true, }, contentsFunc: eagerNoErr([]byte("# contents of dir/dot_file\n")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("# contents of dir/dot_file\n"))), targetStateEntry: &TargetStateFile{ contentsFunc: eagerNoErr([]byte("# contents of dir/dot_file\n")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("# contents of dir/dot_file\n"))), empty: true, perm: 0o666 &^ chezmoitest.Umask, }, }, NewRelPath("dir/subdir"): &SourceStateDir{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/external_dir/subdir")), sourceRelPath: NewSourceRelDirPath("external_dir/subdir"), attr: DirAttr{ TargetName: "subdir", Exact: true, }, targetStateEntry: &TargetStateDir{ perm: fs.ModePerm &^ chezmoitest.Umask, }, }, NewRelPath("dir/subdir/empty_file"): &SourceStateFile{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/external_dir/subdir/empty_file")), sourceRelPath: NewSourceRelPath("external_dir/subdir/empty_file"), attr: FileAttr{ TargetName: "empty_file", Type: SourceFileTypeFile, Empty: true, }, contentsFunc: eagerNoErr([]byte{}), contentsSHA256Func: eagerNoErr(sha256.Sum256(nil)), targetStateEntry: &TargetStateFile{ empty: true, perm: 0o666 &^ chezmoitest.Umask, contentsFunc: eagerZeroNoErr[[]byte](), contentsSHA256Func: eagerNoErr(sha256.Sum256(nil)), }, }, NewRelPath("dir/symlink"): &SourceStateFile{ origin: SourceStateOriginAbsPath(NewAbsPath("/home/user/.local/share/chezmoi/external_dir/symlink")), sourceRelPath: NewSourceRelPath("external_dir/symlink"), attr: FileAttr{ TargetName: "symlink", Type: SourceFileTypeFile, }, contentsFunc: eagerNoErr([]byte("dot_file")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("dot_file"))), targetStateEntry: &TargetStateSymlink{ linknameFunc: eagerNoErr("dot_file"), }, }, }), ), }, { name: "chezmoitemplates", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoitemplates": map[string]any{ "template": "# contents of .chezmoitemplates/template\n", }, }, }, expectedSourceState: NewSourceState( withTemplates( map[string]*Template{ "template": { name: "template", template: template.Must( template.New("template"). Option("missingkey=error"). Parse("# contents of .chezmoitemplates/template\n"), ), options: TemplateOptions{ Options: []string{"missingkey=error"}, }, }, }, ), ), }, { name: "chezmoiversion", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiversion": "1.2.3\n", }, }, expectedSourceState: NewSourceState(), }, { name: "chezmoiversion_multiple", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiversion": "1.2.3\n", "dir": map[string]any{ ".chezmoiversion": "2.3.4\n", }, }, }, expectedError: "source state requires chezmoi version 2.3.4 or later, chezmoi is version 1.2.3", }, { name: "ignore_dir", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".dir": map[string]any{ "file": "# contents of .dir/file\n", }, }, }, expectedSourceState: NewSourceState(), }, { name: "ignore_file", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".file": "# contents of .file\n", }, }, expectedSourceState: NewSourceState(), }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { ctx := t.Context() system := NewRealSystem(fileSystem) s := NewSourceState( WithBaseSystem(system), WithDestDir(NewAbsPath("/home/user")), WithSourceDir(NewAbsPath("/home/user/.local/share/chezmoi")), WithSystem(system), WithVersion(semver.Version{ Major: 1, Minor: 2, Patch: 3, }), ) err := s.Read(ctx, nil) if tc.expectedError != "" { assert.Error(t, err) assert.Equal(t, tc.expectedError, err.Error()) return } assert.NoError(t, err) requireEvaluateAll(t, s, system) tc.expectedSourceState.destDirAbsPath = NewAbsPath("/home/user") tc.expectedSourceState.sourceDirAbsPath = NewAbsPath( "/home/user/.local/share/chezmoi", ) requireEvaluateAll(t, tc.expectedSourceState, system) s.templateData = nil s.version = semver.Version{} assert.Equal(t, tc.expectedSourceState, s, assert.Exclude[System]()) }) }) } } func TestSourceStateReadExternal(t *testing.T) { httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, err := w.Write([]byte("data")) assert.NoError(t, err) })) defer httpServer.Close() for _, tc := range []struct { name string root any expectedExternals map[RelPath][]*External }{ { name: "external_yaml", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiexternal.yaml": chezmoitest.JoinLines( `file:`, ` type: "file"`, ` url: "`+httpServer.URL+`/file"`, ), }, }, expectedExternals: map[RelPath][]*External{ NewRelPath("file"): { { Type: "file", URL: httpServer.URL + "/file", sourceAbsPath: NewAbsPath("/home/user/.local/share/chezmoi/.chezmoiexternal.yaml"), }, }, }, }, { name: "external_toml", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiexternal.toml": chezmoitest.JoinLines( `[file]`, ` type = "file"`, ` url = "`+httpServer.URL+`/file"`, ), }, }, expectedExternals: map[RelPath][]*External{ NewRelPath("file"): { { Type: "file", URL: httpServer.URL + "/file", sourceAbsPath: NewAbsPath("/home/user/.local/share/chezmoi/.chezmoiexternal.toml"), }, }, }, }, { name: "external_in_subdir", root: map[string]any{ "/home/user/.local/share/chezmoi/dot_dir": map[string]any{ ".chezmoiexternal.yaml": chezmoitest.JoinLines( `file:`, ` type: "file"`, ` url: "`+httpServer.URL+`/file"`, ), }, }, expectedExternals: map[RelPath][]*External{ NewRelPath(".dir/file"): { { Type: "file", URL: httpServer.URL + "/file", sourceAbsPath: NewAbsPath("/home/user/.local/share/chezmoi/dot_dir/.chezmoiexternal.yaml"), }, }, }, }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { ctx := t.Context() system := NewRealSystem(fileSystem) s := NewSourceState( WithBaseSystem(system), WithCacheDir(NewAbsPath("/home/user/.cache/chezmoi")), WithDestDir(NewAbsPath("/home/user")), WithSourceDir(NewAbsPath("/home/user/.local/share/chezmoi")), WithSystem(system), ) assert.NoError(t, s.Read(ctx, nil)) assert.Equal(t, tc.expectedExternals, s.externals) }) }) } } func TestSourceStateReadScriptsConcurrent(t *testing.T) { for _, tc := range []struct { name string root any }{ { name: "with_ignore", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiignore": ".chezmoiscripts/linux/**\n", ".chezmoiscripts": map[string]any{ "linux": manyScripts(1000), "darwin": manyScripts(1000), }, }, }, }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { ctx := t.Context() system := NewRealSystem(fileSystem) s := NewSourceState( WithBaseSystem(system), WithCacheDir(NewAbsPath("/home/user/.cache/chezmoi")), WithDestDir(NewAbsPath("/home/user")), WithSourceDir(NewAbsPath("/home/user/.local/share/chezmoi")), WithSystem(system), ) assert.NoError(t, s.Read(ctx, nil)) }) }) } } func TestSourceStateReadExternalCache(t *testing.T) { buffer := &bytes.Buffer{} tarWriterSystem := NewTarWriterSystem(buffer, tar.Header{}) assert.NoError(t, tarWriterSystem.WriteFile(NewAbsPath("file"), []byte("# contents of file\n"), 0o666)) assert.NoError(t, tarWriterSystem.Close()) archiveData := buffer.Bytes() httpRequests := 0 httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { httpRequests++ _, err := w.Write(archiveData) assert.NoError(t, err) })) defer httpServer.Close() now := time.Now() chezmoitest.WithTestFS(t, map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiexternal.yaml": chezmoitest.JoinLines( `.dir:`, ` type: "archive"`, ` url: "`+httpServer.URL+`/archive.tar"`, ` refreshPeriod: "1m"`, ), }, }, func(fileSystem vfs.FS) { ctx := t.Context() system := NewRealSystem(fileSystem) readSourceState := func(refreshExternals RefreshExternals) { s := NewSourceState( WithBaseSystem(system), WithCacheDir(NewAbsPath("/home/user/.cache/chezmoi")), WithDestDir(NewAbsPath("/home/user")), WithSourceDir(NewAbsPath("/home/user/.local/share/chezmoi")), WithSystem(system), ) assert.NoError(t, s.Read(ctx, &ReadOptions{ RefreshExternals: refreshExternals, TimeNow: func() time.Time { return now }, })) assert.Equal(t, map[RelPath][]*External{ NewRelPath(".dir"): { { Type: "archive", URL: httpServer.URL + "/archive.tar", RefreshPeriod: Duration(1 * time.Minute), sourceAbsPath: NewAbsPath("/home/user/.local/share/chezmoi/.chezmoiexternal.yaml"), }, }, }, s.externals) } readSourceState(RefreshExternalsAuto) assert.Equal(t, 1, httpRequests) now = now.Add(10 * time.Second) readSourceState(RefreshExternalsAuto) assert.Equal(t, 1, httpRequests) now = now.Add(1 * time.Minute) readSourceState(RefreshExternalsAuto) assert.Equal(t, 2, httpRequests) now = now.Add(10 * time.Second) readSourceState(RefreshExternalsAlways) assert.Equal(t, 3, httpRequests) now = now.Add(5 * time.Minute) readSourceState(RefreshExternalsNever) assert.Equal(t, 3, httpRequests) }) } func TestSourceStateTargetRelPaths(t *testing.T) { for _, tc := range []struct { name string root any expectedTargetRelPaths []RelPath }{ { name: "empty", root: nil, expectedTargetRelPaths: []RelPath{}, }, { name: "scripts", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ "run_before_1before": "", "run_before_2before": "", "run_before_3before": "", "run_1": "", "run_2": "", "run_3": "", "run_after_1after": "", "run_after_2after": "", "run_after_3after": "", }, }, expectedTargetRelPaths: []RelPath{ NewRelPath("1before"), NewRelPath("2before"), NewRelPath("3before"), NewRelPath("1"), NewRelPath("2"), NewRelPath("3"), NewRelPath("1after"), NewRelPath("2after"), NewRelPath("3after"), }, }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { ctx := t.Context() system := NewRealSystem(fileSystem) s := NewSourceState( WithBaseSystem(system), WithSourceDir(NewAbsPath("/home/user/.local/share/chezmoi")), WithSystem(system), ) assert.NoError(t, s.Read(ctx, nil)) assert.Equal(t, tc.expectedTargetRelPaths, s.TargetRelPaths()) }) }) } } func TestTemplateOptionsParseDirectives(t *testing.T) { for _, tc := range []struct { name string dataStr string expected TemplateOptions expectedErr string expectedDataStr string }{ { name: "empty", }, { name: "unquoted", dataStr: "chezmoi:template:left-delimiter=[[ right-delimiter=]]", expected: TemplateOptions{ LeftDelimiter: "[[", RightDelimiter: "]]", }, }, { name: "quoted", dataStr: `chezmoi:template:left-delimiter="# {{" right-delimiter="}}"`, expected: TemplateOptions{ LeftDelimiter: "# {{", RightDelimiter: "}}", }, }, { name: "left_only", dataStr: "chezmoi:template:left-delimiter=[[", expected: TemplateOptions{ LeftDelimiter: "[[", }, }, { name: "left_quoted_only", dataStr: `chezmoi:template:left-delimiter="# [["`, expected: TemplateOptions{ LeftDelimiter: "# [[", }, }, { name: "right_quoted_only", dataStr: `chezmoi:template:right-delimiter="]]"`, expected: TemplateOptions{ RightDelimiter: "]]", }, }, { name: "line_with_leading_data", dataStr: "# chezmoi:template:left-delimiter=[[ right-delimiter=]]", expected: TemplateOptions{ LeftDelimiter: "[[", RightDelimiter: "]]", }, }, { name: "line_before", dataStr: chezmoitest.JoinLines( "# before", "# chezmoi:template:left-delimiter=[[ right-delimiter=]]", ), expected: TemplateOptions{ LeftDelimiter: "[[", RightDelimiter: "]]", }, expectedDataStr: chezmoitest.JoinLines( "# before", ), }, { name: "line_after", dataStr: chezmoitest.JoinLines( "# chezmoi:template:left-delimiter=[[ right-delimiter=]]", "# after", ), expected: TemplateOptions{ LeftDelimiter: "[[", RightDelimiter: "]]", }, expectedDataStr: chezmoitest.JoinLines( "# after", ), }, { name: "line_before_and_after", dataStr: chezmoitest.JoinLines( "# before", "# chezmoi:template:left-delimiter=[[ right-delimiter=]]", "# after", ), expected: TemplateOptions{ LeftDelimiter: "[[", RightDelimiter: "]]", }, expectedDataStr: chezmoitest.JoinLines( "# before", "# after", ), }, { name: "multiple_lines", dataStr: chezmoitest.JoinLines( "# before", "# chezmoi:template:left-delimiter=<<", "# during", "# chezmoi:template:left-delimiter=[[", "# chezmoi:template:right-delimiter=]]", "# after", ), expected: TemplateOptions{ LeftDelimiter: "[[", RightDelimiter: "]]", }, expectedDataStr: chezmoitest.JoinLines( "# before", "# during", "# after", ), }, { name: "duplicate_directives", dataStr: chezmoitest.JoinLines( "# chezmoi:template:left-delimiter=<<", "# chezmoi:template:left-delimiter=[[", ), expected: TemplateOptions{ LeftDelimiter: "[[", }, }, { name: "missing_key", dataStr: "chezmoi:template:missing-key=zero", expected: TemplateOptions{ Options: []string{"missingkey=zero"}, }, }, { name: "line_ending_crlf", dataStr: "chezmoi:template:line-ending=crlf", expected: TemplateOptions{ LineEnding: "\r\n", }, }, { name: "line_endings_crlf", dataStr: "chezmoi:template:line-endings=crlf", expected: TemplateOptions{ LineEnding: "\r\n", }, }, { name: "line_ending_quoted", dataStr: `chezmoi:template:line-ending="\n"`, expected: TemplateOptions{ LineEnding: "\n", }, }, { name: "line_endings_quoted", dataStr: `chezmoi:template:line-endings="\n"`, expected: TemplateOptions{ LineEnding: "\n", }, }, { name: "format_indent_string", dataStr: `chezmoi:template:format-indent="\t"`, expected: TemplateOptions{ FormatIndent: "\t", }, }, { name: "format_indent_width_number", dataStr: `chezmoi:template:format-indent-width=2`, expected: TemplateOptions{ FormatIndent: " ", }, }, { name: "format_indent_width_number_error", dataStr: `chezmoi:template:format-indent-width=x`, expectedErr: `strconv.Atoi: parsing "x": invalid syntax`, }, } { t.Run(tc.name, func(t *testing.T) { var actual TemplateOptions actualData, err := actual.parseAndRemoveDirectives([]byte(tc.dataStr)) if tc.expectedErr != "" { assert.EqualError(t, err, tc.expectedErr) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, actual) assert.Equal(t, tc.expectedDataStr, string(actualData)) } }) } } func TestSourceStateExternalErrors(t *testing.T) { for _, tc := range []struct { name string shareDir map[string]any expectedErr string }{ { name: "missing_type", shareDir: map[string]any{ ".chezmoiexternal.toml": chezmoitest.JoinLines( `["dir"]`, ` url = "http://example.com/"`, ), }, expectedErr: "dir: missing external type", }, { name: "empty_rel_path", shareDir: map[string]any{ ".chezmoiexternal.toml": chezmoitest.JoinLines( `[""]`, ` type = "file"`, ` url = "http://example.com/"`, ), }, expectedErr: "/home/user/.local/share/chezmoi/.chezmoiexternal.toml: empty path", }, { name: "relative_empty_rel_path", shareDir: map[string]any{ ".chezmoiexternal.toml": chezmoitest.JoinLines( `["."]`, ` type = "file"`, ` url = "http://example.com/"`, ), }, expectedErr: "/home/user/.local/share/chezmoi/.chezmoiexternal.toml: .: empty relative path", }, { name: "parent_root_rel_path", shareDir: map[string]any{ ".chezmoiexternal.toml": chezmoitest.JoinLines( `[".."]`, ` type = "file"`, ` url = "http://example.com/"`, ), }, expectedErr: "/home/user/.local/share/chezmoi/.chezmoiexternal.toml: ..: relative path in parent", }, { name: "relative_parent_root_rel_path", shareDir: map[string]any{ ".chezmoiexternal.toml": chezmoitest.JoinLines( `["./.."]`, ` type = "file"`, ` url = "http://example.com/"`, ), }, expectedErr: "/home/user/.local/share/chezmoi/.chezmoiexternal.toml: ./..: relative path in parent", }, { name: "relative_empty", shareDir: map[string]any{ ".chezmoiexternal.toml": chezmoitest.JoinLines( `["a/../b/.."]`, ` type = "file"`, ` url = "http://example.com/"`, ), }, expectedErr: "/home/user/.local/share/chezmoi/.chezmoiexternal.toml: a/../b/..: empty relative path", }, { name: "relative_parent", shareDir: map[string]any{ ".chezmoiexternal.toml": chezmoitest.JoinLines( `["a/../b/../.."]`, ` type = "file"`, ` url = "http://example.com/"`, ), }, expectedErr: "/home/user/.local/share/chezmoi/.chezmoiexternal.toml: a/../b/../..: relative path in parent", }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, map[string]any{ "/home/user/.local/share/chezmoi": tc.shareDir, }, func(fileSystem vfs.FS) { ctx := t.Context() system := NewRealSystem(fileSystem) s := NewSourceState( WithBaseSystem(system), WithCacheDir(NewAbsPath("/home/user/.cache/chezmoi")), WithDestDir(NewAbsPath("/home/user")), WithSourceDir(NewAbsPath("/home/user/.local/share/chezmoi")), WithSystem(system), ) err := s.Read(ctx, nil) assert.Error(t, err) assert.Equal(t, err.Error(), tc.expectedErr) }) }) } } // applyAll updates targetDirAbsPath in targetSystem to match s. func (s *SourceState) applyAll( targetSystem, destSystem System, persistentState PersistentState, targetDirAbsPath AbsPath, options ApplyOptions, ) error { for _, targetRelPath := range s.TargetRelPaths() { switch err := s.Apply(targetSystem, destSystem, persistentState, targetDirAbsPath, targetRelPath, options); { case errors.Is(err, fs.SkipDir): continue case err != nil: return err } } return nil } // requireEvaluateAll requires that every target state entry in s evaluates // without error. func requireEvaluateAll(t *testing.T, s *SourceState, destSystem System) { t.Helper() err := s.root.ForEach(EmptyRelPath, func(targetRelPath RelPath, sourceStateEntry SourceStateEntry) error { assert.NoError(t, sourceStateEntry.Evaluate()) if sourceStateFile, ok := sourceStateEntry.(*SourceStateFile); ok { contents, err := sourceStateFile.Contents() assert.NoError(t, err) contentsSHA256, err := sourceStateFile.ContentsSHA256() assert.NoError(t, err) assert.Equal(t, sha256.Sum256(contents), contentsSHA256) } destAbsPath := s.destDirAbsPath.Join(targetRelPath) targetStateEntry, err := sourceStateEntry.TargetStateEntry(destSystem, destAbsPath) assert.NoError(t, err) assert.NoError(t, targetStateEntry.Evaluate()) switch targetStateEntry := targetStateEntry.(type) { case *TargetStateFile: contents, err := targetStateEntry.Contents() assert.NoError(t, err) contentsSHA256, err := targetStateEntry.ContentsSHA256() assert.NoError(t, err) assert.Equal(t, sha256.Sum256(contents), contentsSHA256) case *TargetStateScript: contents, err := targetStateEntry.Contents() assert.NoError(t, err) contentsSHA256, err := targetStateEntry.ContentsSHA256() assert.NoError(t, err) assert.Equal(t, sha256.Sum256(contents), contentsSHA256) } return nil }) assert.NoError(t, err) } func withEntries(sourceEntries map[RelPath]SourceStateEntry) SourceStateOption { return func(s *SourceState) { s.root = SourceStateEntryTreeNode{} for targetRelPath, sourceStateEntry := range sourceEntries { s.root.Set(targetRelPath, sourceStateEntry) } } } func withIgnore(ignore *PatternSet) SourceStateOption { return func(s *SourceState) { s.ignore = ignore } } func withIgnoredRelPathStrs(relPathStrs ...string) SourceStateOption { return func(s *SourceState) { for _, relPathStr := range relPathStrs { s.ignoredRelPaths.Add(NewRelPath(relPathStr)) } } } func withRemove(remove *PatternSet) SourceStateOption { return func(s *SourceState) { s.remove = remove } } // withUserTemplateData adds template data. func withUserTemplateData(templateData map[string]any) SourceStateOption { return func(s *SourceState) { RecursiveMerge(s.userTemplateData, templateData) } } func withTemplates(templates map[string]*Template) SourceStateOption { return func(s *SourceState) { s.templates = templates } } func manyScripts(amount int) map[string]any { scripts := map[string]any{} for i := range amount { scripts[fmt.Sprintf("run_onchange_before_%d.sh", i)] = "" } return scripts } ================================================ FILE: internal/chezmoi/sourcestateentry.go ================================================ package chezmoi import ( "encoding/hex" "log/slog" "os/exec" "chezmoi.io/chezmoi/internal/chezmoilog" ) // A SourceAttr contains attributes of the source. type SourceAttr struct { Condition ScriptCondition Encrypted bool External bool Template bool } // A SourceStateOrigin represents the origin of a source state. type SourceStateOrigin interface { IsExternal() bool Path() AbsPath OriginString() string } // A SourceStateOriginAbsPath is an absolute path. type SourceStateOriginAbsPath AbsPath // A SourceStateEntry represents the state of an entry in the source state. type SourceStateEntry interface { slog.LogValuer Evaluate() error Order() ScriptOrder Origin() SourceStateOrigin SourceRelPath() SourceRelPath TargetStateEntry(destSystem System, destDirAbsPath AbsPath) (TargetStateEntry, error) } // A SourceStateCommand represents a command that should be run. type SourceStateCommand struct { cmdFunc func() *exec.Cmd origin SourceStateOrigin forceRefresh bool refreshPeriod Duration sourceAttr SourceAttr } // A SourceStateDir represents the state of a directory in the source state. type SourceStateDir struct { attr DirAttr origin SourceStateOrigin sourceRelPath SourceRelPath targetStateEntry TargetStateEntry } // A SourceStateFile represents the state of a file in the source state. type SourceStateFile struct { attr FileAttr contentsFunc func() ([]byte, error) contentsSHA256Func func() ([32]byte, error) origin SourceStateOrigin sourceRelPath SourceRelPath targetStateEntryFunc TargetStateEntryFunc targetStateEntry TargetStateEntry targetStateEntryErr error } // A SourceStateImplicitDir represents the state of a directory that is implicit // in the source state, typically because it is a parent directory of an // external. Implicit directories have no attributes and are considered // equivalent to any other directory. type SourceStateImplicitDir struct { origin SourceStateOrigin targetStateEntry TargetStateEntry } // A SourceStateRemove represents that an entry should be removed. type SourceStateRemove struct { origin SourceStateOrigin sourceRelPath SourceRelPath targetRelPath RelPath } // A SourceStateOriginRemove is used for removes. The source of the remove is // not currently tracked. The remove could come from an exact_ directory, a // non-empty_ file with empty contents, or one of many patterns in many // .chezmoiignore files. // // FIXME remove this when the sources of all removes are tracked. type SourceStateOriginRemove struct{} // Evaluate evaluates s and returns any error. func (s *SourceStateCommand) Evaluate() error { return nil } // LogValue implements log/slog.LogValuer.LogValue. func (s *SourceStateCommand) LogValue() slog.Value { return slog.GroupValue( slog.Any("cmd", chezmoilog.OSExecCmdLogValuer{Cmd: s.cmdFunc()}), slog.String("origin", s.origin.OriginString()), ) } // Order returns s's order. func (s *SourceStateCommand) Order() ScriptOrder { return ScriptOrderDuring } // Origin returns s's origin. func (s *SourceStateCommand) Origin() SourceStateOrigin { return s.origin } // SourceRelPath returns s's source relative path. func (s *SourceStateCommand) SourceRelPath() SourceRelPath { return emptySourceRelPath } // TargetStateEntry returns s's target state entry. func (s *SourceStateCommand) TargetStateEntry(destSystem System, destDirAbsPath AbsPath) (TargetStateEntry, error) { return &TargetStateModifyDirWithCmd{ cmdFunc: s.cmdFunc, forceRefresh: s.forceRefresh, refreshPeriod: s.refreshPeriod, sourceAttr: s.sourceAttr, }, nil } // Attr returns s's attributes. func (s *SourceStateDir) Attr() DirAttr { return s.attr } // Evaluate evaluates s and returns any error. func (s *SourceStateDir) Evaluate() error { return nil } // LogValue implements log/slog.LogValuer.LogValue. func (s *SourceStateDir) LogValue() slog.Value { return slog.GroupValue( chezmoilog.Stringer("sourceRelPath", s.sourceRelPath), slog.Any("attr", s.attr), ) } // Order returns s's order. func (s *SourceStateDir) Order() ScriptOrder { return ScriptOrderDuring } // Origin returns s's origin. func (s *SourceStateDir) Origin() SourceStateOrigin { return s.origin } // SourceRelPath returns s's source relative path. func (s *SourceStateDir) SourceRelPath() SourceRelPath { return s.sourceRelPath } // TargetStateEntry returns s's target state entry. func (s *SourceStateDir) TargetStateEntry(destSystem System, destDirAbsPath AbsPath) (TargetStateEntry, error) { return s.targetStateEntry, nil } // Attr returns s's attributes. func (s *SourceStateFile) Attr() FileAttr { return s.attr } // Contents returns s's contents. func (s *SourceStateFile) Contents() ([]byte, error) { return s.contentsFunc() } // ContentsSHA256 returns the SHA256 sum of s's contents. func (s *SourceStateFile) ContentsSHA256() ([32]byte, error) { return s.contentsSHA256Func() } // Evaluate evaluates s and returns any error. func (s *SourceStateFile) Evaluate() error { if _, err := s.Contents(); err != nil { return err } if _, err := s.ContentsSHA256(); err != nil { return err } return nil } // LogValue implements log/slog.LogValuer.LogValue. func (s *SourceStateFile) LogValue() slog.Value { attrs := []slog.Attr{ chezmoilog.Stringer("sourceRelPath", s.sourceRelPath), slog.Any("attr", s.attr), } contents, contentsErr := s.Contents() attrs = append(attrs, chezmoilog.FirstFewBytes("contents", contents)) if contentsErr != nil { attrs = append(attrs, slog.Any("contentsErr", contentsErr)) } contentsSHA256, contentsSHA256Err := s.ContentsSHA256() attrs = append(attrs, slog.String("contentsSHA256", hex.EncodeToString(contentsSHA256[:]))) if contentsSHA256Err != nil { attrs = append(attrs, slog.Any("contentsSHA256Err", contentsSHA256Err)) } return slog.GroupValue(attrs...) } // Order returns s's order. func (s *SourceStateFile) Order() ScriptOrder { return s.attr.Order } // Origin returns s's origin. func (s *SourceStateFile) Origin() SourceStateOrigin { return s.origin } // SourceRelPath returns s's source relative path. func (s *SourceStateFile) SourceRelPath() SourceRelPath { return s.sourceRelPath } // TargetStateEntry returns s's target state entry. func (s *SourceStateFile) TargetStateEntry(destSystem System, destDirAbsPath AbsPath) (TargetStateEntry, error) { if s.targetStateEntryFunc != nil { s.targetStateEntry, s.targetStateEntryErr = s.targetStateEntryFunc(destSystem, destDirAbsPath) s.targetStateEntryFunc = nil } return s.targetStateEntry, s.targetStateEntryErr } // Evaluate evaluates s and returns any error. func (s *SourceStateImplicitDir) Evaluate() error { return nil } // LogValue implements log/slog.LogValuer.LogValue. func (s *SourceStateImplicitDir) LogValue() slog.Value { return slog.GroupValue() } // Order returns s's order. func (s *SourceStateImplicitDir) Order() ScriptOrder { return ScriptOrderDuring } // Origin returns s's origin. func (s *SourceStateImplicitDir) Origin() SourceStateOrigin { return s.origin } // SourceRelPath returns s's source relative path. func (s *SourceStateImplicitDir) SourceRelPath() SourceRelPath { return emptySourceRelPath } // TargetStateEntry returns s's target state entry. func (s *SourceStateImplicitDir) TargetStateEntry(destSystem System, destDirAbsPath AbsPath) (TargetStateEntry, error) { return s.targetStateEntry, nil } // Evaluate evaluates s and returns any error. func (s *SourceStateRemove) Evaluate() error { return nil } // LogValue implements log/slog.LogValuer.LogValue. func (s *SourceStateRemove) LogValue() slog.Value { return slog.GroupValue( chezmoilog.Stringer("targetRelPath", s.targetRelPath), ) } // Order returns s's order. func (s *SourceStateRemove) Order() ScriptOrder { return ScriptOrderDuring } // Origin returns s's origin. func (s *SourceStateRemove) Origin() SourceStateOrigin { return s.origin } // SourceRelPath returns s's source relative path. func (s *SourceStateRemove) SourceRelPath() SourceRelPath { return SourceRelPath{} } // TargetStateEntry returns s's target state entry. func (s *SourceStateRemove) TargetStateEntry(destSystem System, destDirAbsPath AbsPath) (TargetStateEntry, error) { return &TargetStateRemove{}, nil } // IsExternal returns if s is an external. func (SourceStateOriginAbsPath) IsExternal() bool { return false } // Path returns s's path. func (s SourceStateOriginAbsPath) Path() AbsPath { return AbsPath(s) } // OriginString returns s's origin. func (s SourceStateOriginAbsPath) OriginString() string { return AbsPath(s).String() } // Path returns s's path. func (s SourceStateOriginRemove) Path() AbsPath { return EmptyAbsPath } // OriginString returns s's origin. func (s SourceStateOriginRemove) OriginString() string { return "remove" } // IsExternal returns if s is an external. func (SourceStateOriginRemove) IsExternal() bool { return false } ================================================ FILE: internal/chezmoi/sourcestatetreenode.go ================================================ package chezmoi import ( "errors" "fmt" "io/fs" "maps" "slices" ) // A SourceStateEntryTreeNode is a node in a tree of SourceStateEntries. type SourceStateEntryTreeNode struct { SourceStateEntry SourceStateEntry Children map[RelPath]*SourceStateEntryTreeNode } // NewSourceStateEntryTreeNode returns a new sourceStateEntryTreeNode. func NewSourceStateEntryTreeNode() *SourceStateEntryTreeNode { return &SourceStateEntryTreeNode{} } // Get returns the SourceStateEntry at relPath. func (n *SourceStateEntryTreeNode) Get(relPath RelPath) SourceStateEntry { nodes := n.GetNodes(relPath) if nodes == nil { return nil } return nodes[len(nodes)-1].SourceStateEntry } // GetNodes returns the sourceStateEntryTreeNodes to reach targetRelPath. func (n *SourceStateEntryTreeNode) GetNodes(targetRelPath RelPath) []*SourceStateEntryTreeNode { if targetRelPath.IsEmpty() { return []*SourceStateEntryTreeNode{n} } targetRelPathComponents := targetRelPath.SplitAll() nodes := make([]*SourceStateEntryTreeNode, 0, len(targetRelPathComponents)) nodes = append(nodes, n) for _, childRelPath := range targetRelPathComponents { childNode, ok := nodes[len(nodes)-1].Children[childRelPath] if !ok { return nil } nodes = append(nodes, childNode) } return nodes } // ForEach calls f for each SourceStateEntry in the tree. func (n *SourceStateEntryTreeNode) ForEach(targetRelPath RelPath, f func(RelPath, SourceStateEntry) error) error { return n.ForEachNode(targetRelPath, func(targetRelPath RelPath, node *SourceStateEntryTreeNode) error { if node.SourceStateEntry == nil { return nil } return f(targetRelPath, node.SourceStateEntry) }) } // ForEachNode calls f for each node in the tree. func (n *SourceStateEntryTreeNode) ForEachNode(targetRelPath RelPath, f func(RelPath, *SourceStateEntryTreeNode) error) error { switch err := f(targetRelPath, n); { case errors.Is(err, fs.SkipDir): return nil case err != nil: return err } childrenByRelPath := slices.Collect(maps.Keys(n.Children)) slices.SortFunc(childrenByRelPath, CompareRelPaths) for _, childRelPath := range childrenByRelPath { child := n.Children[childRelPath] if err := child.ForEachNode(targetRelPath.Join(childRelPath), f); err != nil { return err } } return nil } // GetMap returns a map of relPaths to SourceStateEntries. func (n *SourceStateEntryTreeNode) GetMap() map[RelPath]SourceStateEntry { m := make(map[RelPath]SourceStateEntry) _ = n.ForEach(EmptyRelPath, func(relPath RelPath, sourceStateEntry SourceStateEntry) error { m[relPath] = sourceStateEntry return nil }) return m } // MkdirAll creates SourceStateDirs for all components of targetRelPath if they // do not already exist and returns the SourceStateDir of relPath. func (n *SourceStateEntryTreeNode) MkdirAll( targetRelPath RelPath, origin SourceStateOrigin, umask fs.FileMode, ) (*SourceStateDir, error) { if targetRelPath == EmptyRelPath { return nil, nil } node := n var sourceRelPath SourceRelPath componentRelPaths := targetRelPath.SplitAll() var sourceStateDir *SourceStateDir for i, componentRelPath := range componentRelPaths { if node.Children == nil { node.Children = make(map[RelPath]*SourceStateEntryTreeNode) } if child, ok := node.Children[componentRelPath]; ok { node = child } else { child = NewSourceStateEntryTreeNode() node.Children[componentRelPath] = child node = child } if node.SourceStateEntry == nil { dirAttr := DirAttr{ TargetName: componentRelPath.String(), } targetStateDir := &TargetStateDir{ perm: dirAttr.perm() &^ umask, } sourceRelPath = sourceRelPath.Join(NewSourceRelPath(dirAttr.SourceName())) sourceStateDir = &SourceStateDir{ attr: dirAttr, origin: origin, sourceRelPath: sourceRelPath, targetStateEntry: targetStateDir, } node.SourceStateEntry = sourceStateDir } else { var ok bool sourceStateDir, ok = node.SourceStateEntry.(*SourceStateDir) if !ok { return nil, fmt.Errorf("%s: not a directory", componentRelPaths[0].Join(componentRelPaths[1:i+1]...)) } sourceRelPath = sourceRelPath.Join(NewSourceRelPath(sourceStateDir.attr.SourceName())) } } return sourceStateDir, nil } // Set sets the SourceStateEntry at relPath to sourceStateEntry. func (n *SourceStateEntryTreeNode) Set(targetRelPath RelPath, sourceStateEntry SourceStateEntry) { if targetRelPath.IsEmpty() { n.SourceStateEntry = sourceStateEntry return } node := n for _, childRelPath := range targetRelPath.SplitAll() { if node.Children == nil { node.Children = make(map[RelPath]*SourceStateEntryTreeNode) } if child, ok := node.Children[childRelPath]; ok { node = child } else { child = NewSourceStateEntryTreeNode() node.Children[childRelPath] = child node = child } } node.SourceStateEntry = sourceStateEntry } ================================================ FILE: internal/chezmoi/sourcestatetreenode_test.go ================================================ package chezmoi import ( "errors" "testing" "github.com/alecthomas/assert/v2" ) func TestSourceStateEntryTreeNodeEmpty(t *testing.T) { n := NewSourceStateEntryTreeNode() assert.Equal(t, nil, n.Get(EmptyRelPath)) assert.Equal(t, []*SourceStateEntryTreeNode{n}, n.GetNodes(EmptyRelPath)) assert.NoError(t, n.ForEach(EmptyRelPath, func(RelPath, SourceStateEntry) error { return errors.New("should not be called") })) } func TestSourceStateEntryTreeNodeSingle(t *testing.T) { n := NewSourceStateEntryTreeNode() sourceStateFile := &SourceStateFile{} n.Set(NewRelPath("file"), sourceStateFile) assert.Equal(t, sourceStateFile, n.Get(NewRelPath("file")).(*SourceStateFile)) err := n.ForEach(EmptyRelPath, func(targetRelPath RelPath, sourceStateEntry SourceStateEntry) error { assert.Equal(t, NewRelPath("file"), targetRelPath) assert.Equal(t, sourceStateFile, sourceStateEntry.(*SourceStateFile)) return nil }) assert.NoError(t, err) } func TestSourceStateEntryTreeNodeMultiple(t *testing.T) { entries := map[RelPath]SourceStateEntry{ NewRelPath("a_file"): &SourceStateFile{}, NewRelPath("b_file"): &SourceStateFile{}, NewRelPath("c_file"): &SourceStateFile{}, NewRelPath("dir"): &SourceStateDir{}, NewRelPath("dir/a_file"): &SourceStateFile{}, NewRelPath("dir/b_file"): &SourceStateFile{}, } n := NewSourceStateEntryTreeNode() for targetRelPath, sourceStateEntry := range entries { n.Set(targetRelPath, sourceStateEntry) } var targetRelPaths []RelPath err := n.ForEach(EmptyRelPath, func(targetRelPath RelPath, sourceStateEntry SourceStateEntry) error { assert.Equal(t, entries[targetRelPath], sourceStateEntry) targetRelPaths = append(targetRelPaths, targetRelPath) return nil }) assert.NoError(t, err) assert.Equal(t, []RelPath{ NewRelPath("a_file"), NewRelPath("b_file"), NewRelPath("c_file"), NewRelPath("dir"), NewRelPath("dir/a_file"), NewRelPath("dir/b_file"), }, targetRelPaths) assert.Equal(t, entries, n.GetMap()) } ================================================ FILE: internal/chezmoi/system.go ================================================ package chezmoi import ( "cmp" "context" "errors" "io/fs" "os/exec" "slices" "strings" "time" vfs "github.com/twpayne/go-vfs/v5" "golang.org/x/sync/errgroup" ) type RunScriptOptions struct { Interpreter *Interpreter Condition ScriptCondition SourceRelPath SourceRelPath } // A System reads from and writes to a filesystem, runs scripts, and persists // state. type System interface { //nolint:interfacebloat Chmod(name AbsPath, mode fs.FileMode) error Chtimes(name AbsPath, atime, mtime time.Time) error Glob(pattern string) ([]string, error) Link(oldName, newName AbsPath) error Lstat(filename AbsPath) (fs.FileInfo, error) Mkdir(name AbsPath, perm fs.FileMode) error RawPath(absPath AbsPath) (AbsPath, error) ReadDir(name AbsPath) ([]fs.DirEntry, error) ReadFile(name AbsPath) ([]byte, error) Readlink(name AbsPath) (string, error) Remove(name AbsPath) error RemoveAll(name AbsPath) error Rename(oldPath, newPath AbsPath) error RunCmd(cmd *exec.Cmd) error RunScript(scriptName RelPath, dir AbsPath, data []byte, options RunScriptOptions) error Stat(name AbsPath) (fs.FileInfo, error) UnderlyingFS() vfs.FS WriteFile(filename AbsPath, data []byte, perm fs.FileMode) error WriteSymlink(oldName string, newName AbsPath) error } // A emptySystemMixin simulates an empty system. type emptySystemMixin struct{} func (emptySystemMixin) Glob(pattern string) ([]string, error) { return nil, nil } func (emptySystemMixin) Lstat(name AbsPath) (fs.FileInfo, error) { return nil, fs.ErrNotExist } func (emptySystemMixin) RawPath(path AbsPath) (AbsPath, error) { return path, nil } func (emptySystemMixin) ReadDir(name AbsPath) ([]fs.DirEntry, error) { return nil, fs.ErrNotExist } func (emptySystemMixin) ReadFile(name AbsPath) ([]byte, error) { return nil, fs.ErrNotExist } func (emptySystemMixin) Readlink(name AbsPath) (string, error) { return "", fs.ErrNotExist } func (emptySystemMixin) Stat(name AbsPath) (fs.FileInfo, error) { return nil, fs.ErrNotExist } func (emptySystemMixin) UnderlyingFS() vfs.FS { return nil } // A noUpdateSystemMixin panics on any update. type noUpdateSystemMixin struct{} func (noUpdateSystemMixin) Chmod(name AbsPath, perm fs.FileMode) error { panic("update to no update system") } func (noUpdateSystemMixin) Chtimes(name AbsPath, atime, mtime time.Time) error { panic("update to no update system") } func (noUpdateSystemMixin) Link(oldName, newName AbsPath) error { panic("update to no update system") } func (noUpdateSystemMixin) Mkdir(name AbsPath, perm fs.FileMode) error { panic("update to no update system") } func (noUpdateSystemMixin) Remove(name AbsPath) error { panic("update to no update system") } func (noUpdateSystemMixin) RemoveAll(name AbsPath) error { panic("update to no update system") } func (noUpdateSystemMixin) Rename(oldPath, newPath AbsPath) error { panic("update to no update system") } func (noUpdateSystemMixin) RunCmd(cmd *exec.Cmd) error { panic("update to no update system") } func (noUpdateSystemMixin) RunScript(scriptName RelPath, dir AbsPath, data []byte, options RunScriptOptions) error { panic("update to no update system") } func (noUpdateSystemMixin) WriteFile(filename AbsPath, data []byte, perm fs.FileMode) error { panic("update to no update system") } func (noUpdateSystemMixin) WriteSymlink(oldName string, newName AbsPath) error { panic("update to no update system") } // MkdirAll is the equivalent of [os.MkdirAll] but operates on system. func MkdirAll(system System, absPath AbsPath, perm fs.FileMode) error { switch err := system.Mkdir(absPath, perm); { case err == nil: // Mkdir was successful. return nil case errors.Is(err, fs.ErrExist): // path already exists, but we don't know whether it's a directory or // something else. We get this error if we try to create a subdirectory // of a non-directory, for example if the parent directory of path is a // file. There's a race condition here between the call to Mkdir and the // call to Stat but we can't avoid it because there's not enough // information in the returned error from Mkdir. We need to distinguish // between "path already exists and is already a directory" and "path // already exists and is not a directory". Between the call to Mkdir and // the call to Stat path might have changed. fileInfo, statErr := system.Stat(absPath) if statErr != nil { return statErr } if !fileInfo.IsDir() { return err } return nil case errors.Is(err, fs.ErrNotExist): // Parent directory does not exist. Create the parent directory // recursively, then try again. parentDir := absPath.Dir() if parentDir == RootAbsPath || parentDir == DotAbsPath { // We cannot create the root directory or the current directory, so // return the original error. return err } if err := MkdirAll(system, parentDir, perm); err != nil { return err } return system.Mkdir(absPath, perm) default: // Some other error. return err } } // A WalkFunc is called for every entry in a directory. type WalkFunc func(absPath AbsPath, fileInfo fs.FileInfo, err error) error // Walk walks rootAbsPath in system, calling walkFunc for each file or directory // in the tree, including rootAbsPath. // // Walk does not follow symlinks. func Walk(system System, rootAbsPath AbsPath, walkFunc WalkFunc) error { outerWalkFunc := func(absPath string, fileInfo fs.FileInfo, err error) error { return walkFunc(NewAbsPath(absPath).ToSlash(), fileInfo, err) } return vfs.Walk(system.UnderlyingFS(), rootAbsPath.String(), outerWalkFunc) } // A ConcurrentWalkSourceDirFunc is a function called concurrently for every // entry in a source directory. type ConcurrentWalkSourceDirFunc func(ctx context.Context, absPath AbsPath, fileInfo fs.FileInfo, err error) error // WalkSourceDir walks the source directory rooted at sourceDirAbsPath in // system, calling walkFunc for each file or directory in the tree, including // sourceDirAbsPath. // // WalkSourceDir does not follow symbolic links found in directories, but if // sourceDirAbsPath itself is a symbolic link, its target will be walked. // // Directory entries .chezmoidata. and .chezmoitemplates are visited // before all other entries. All other entries are visited in alphabetical // order. func WalkSourceDir(system System, sourceDirAbsPath AbsPath, walkFunc WalkFunc) error { fileInfo, err := system.Stat(sourceDirAbsPath) if err != nil { err = walkFunc(sourceDirAbsPath, nil, err) } else { err = walkSourceDirHelper(system, sourceDirAbsPath, fileInfo, walkFunc) if errors.Is(err, fs.SkipDir) { err = nil } } return err } // sourceDirEntryOrder defines the order in which entries are visited in the // source directory. More negative values are visited first. Entries with the // same order are visited alphabetically. The default order is zero. var sourceDirEntryOrder = map[string]int{ VersionName: -3, dataName + ".json": -2, dataName + ".toml": -2, dataName + ".yaml": -2, TemplatesDirName: -1, } // walkSourceDirHelper is a helper function for WalkSourceDir. func walkSourceDirHelper(system System, name AbsPath, fileInfo fs.FileInfo, walkFunc WalkFunc) error { switch err := walkFunc(name, fileInfo, nil); { case fileInfo.IsDir() && errors.Is(err, fs.SkipDir): return nil case err != nil: return err case !fileInfo.IsDir(): return nil } dirEntries, err := system.ReadDir(name) if err != nil { err = walkFunc(name, fileInfo, err) if err != nil { return err } } slices.SortFunc(dirEntries, compareDirEntries) for _, dirEntry := range dirEntries { fileInfo, err := dirEntry.Info() if err != nil { err = walkFunc(name, nil, err) if err != nil { return err } } if err := walkSourceDirHelper(system, name.JoinString(dirEntry.Name()), fileInfo, walkFunc); err != nil { if !errors.Is(err, fs.SkipDir) { return err } } } return nil } func concurrentWalkSourceDir( ctx context.Context, system System, dirAbsPath AbsPath, walkFunc ConcurrentWalkSourceDirFunc, ) error { dirEntries, err := system.ReadDir(dirAbsPath) if err != nil { return walkFunc(ctx, dirAbsPath, nil, err) } slices.SortFunc(dirEntries, compareDirEntries) // Walk all control plane entries in order. visitDirEntry := func(dirEntry fs.DirEntry) error { absPath := dirAbsPath.Join(NewRelPath(dirEntry.Name())) fileInfo, err := dirEntry.Info() if err != nil { return walkFunc(ctx, absPath, nil, err) } switch err := walkFunc(ctx, absPath, fileInfo, nil); { case fileInfo.IsDir() && errors.Is(err, fs.SkipDir): return nil case err != nil: return err case fileInfo.IsDir(): return concurrentWalkSourceDir(ctx, system, absPath, walkFunc) default: return nil } } i := 0 for ; i < len(dirEntries); i++ { dirEntry := dirEntries[i] if !strings.HasPrefix(dirEntry.Name(), ".") { break } if err := visitDirEntry(dirEntry); err != nil { return err } } // Walk all remaining entries concurrently. visitDirEntryFunc := func(dirEntry fs.DirEntry) func() error { return func() error { return visitDirEntry(dirEntry) } } group, ctx := errgroup.WithContext(ctx) for _, dirEntry := range dirEntries[i:] { group.Go(visitDirEntryFunc(dirEntry)) } return group.Wait() } func compareDirEntries(a, b fs.DirEntry) int { aName, bName := a.Name(), b.Name() if compare := cmp.Compare(sourceDirEntryOrder[aName], sourceDirEntryOrder[bName]); compare != 0 { return compare } return cmp.Compare(aName, bName) } ================================================ FILE: internal/chezmoi/system_test.go ================================================ package chezmoi import ( "context" "io/fs" "slices" "sync" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestConcurrentWalkSourceDir(t *testing.T) { sourceDirAbsPath := NewAbsPath("/home/user/.local/share/chezmoi") root := map[string]any{ sourceDirAbsPath.String(): map[string]any{ ".chezmoiversion": "# contents of .chezmoiversion\n", "dot_dir/file": "# contents of .dir/file\n", }, } expectedSourceAbsPaths := []AbsPath{ sourceDirAbsPath.JoinString(".chezmoiversion"), sourceDirAbsPath.JoinString("dot_dir"), sourceDirAbsPath.JoinString("dot_dir/file"), } var actualSourceAbsPaths []AbsPath chezmoitest.WithTestFS(t, root, func(fileSystem vfs.FS) { ctx := t.Context() system := NewRealSystem(fileSystem) var mutex sync.Mutex walkFunc := func(ctx context.Context, sourceAbsPath AbsPath, fileInfo fs.FileInfo, err error) error { mutex.Lock() actualSourceAbsPaths = append(actualSourceAbsPaths, sourceAbsPath) mutex.Unlock() return nil } assert.NoError(t, concurrentWalkSourceDir(ctx, system, sourceDirAbsPath, walkFunc)) }) slices.Sort(actualSourceAbsPaths) assert.Equal(t, expectedSourceAbsPaths, actualSourceAbsPaths) } func TestWalkSourceDir(t *testing.T) { sourceDirAbsPath := NewAbsPath("/home/user/.local/share/chezmoi") root := map[string]any{ sourceDirAbsPath.String(): map[string]any{ ".chezmoi.toml.tmpl": "", ".chezmoidata.json": "", ".chezmoidata.toml": "", ".chezmoidata.yaml": "", ".chezmoiexternal.yaml": "", ".chezmoiignore": "", ".chezmoiremove": "", ".chezmoitemplates": &vfst.Dir{Perm: fs.ModePerm}, ".chezmoiversion": "", "dot_file": "", }, } expectedSourceDirAbsPaths := []AbsPath{ sourceDirAbsPath, sourceDirAbsPath.JoinString(".chezmoiversion"), sourceDirAbsPath.JoinString(".chezmoidata.json"), sourceDirAbsPath.JoinString(".chezmoidata.toml"), sourceDirAbsPath.JoinString(".chezmoidata.yaml"), sourceDirAbsPath.JoinString(".chezmoitemplates"), sourceDirAbsPath.JoinString(".chezmoi.toml.tmpl"), sourceDirAbsPath.JoinString(".chezmoiexternal.yaml"), sourceDirAbsPath.JoinString(".chezmoiignore"), sourceDirAbsPath.JoinString(".chezmoiremove"), sourceDirAbsPath.JoinString("dot_file"), } var actualSourceDirAbsPaths []AbsPath chezmoitest.WithTestFS(t, root, func(fileSystem vfs.FS) { system := NewRealSystem(fileSystem) err := WalkSourceDir(system, sourceDirAbsPath, func(absPath AbsPath, fileInfo fs.FileInfo, err error) error { if err != nil { return err } actualSourceDirAbsPaths = append(actualSourceDirAbsPaths, absPath) return nil }) assert.NoError(t, err) }) assert.Equal(t, expectedSourceDirAbsPaths, actualSourceDirAbsPaths) } ================================================ FILE: internal/chezmoi/targetstateentry.go ================================================ package chezmoi import ( "bytes" "crypto/sha256" "encoding/hex" "fmt" "io/fs" "os/exec" "runtime" "time" ) // A TargetStateEntry represents the state of an entry in the target state. type TargetStateEntry interface { Apply( system System, persistentState PersistentState, actualStateEntry ActualStateEntry, ) (bool, error) EntryState(umask fs.FileMode) (*EntryState, error) Evaluate() error SkipApply(persistentState PersistentState, targetAbsPath AbsPath) (bool, error) SourceAttr() SourceAttr } // A TargetStateModifyDirWithCmd represents running a command that modifies // a directory. type TargetStateModifyDirWithCmd struct { cmdFunc func() *exec.Cmd forceRefresh bool refreshPeriod Duration sourceAttr SourceAttr } // A TargetStateDir represents the state of a directory in the target state. type TargetStateDir struct { perm fs.FileMode sourceAttr SourceAttr } // A TargetStateFile represents the state of a file in the target state. type TargetStateFile struct { contentsFunc func() ([]byte, error) contentsSHA256Func func() ([32]byte, error) empty bool overwrite bool perm fs.FileMode sourceAttr SourceAttr } // A TargetStateRemove represents the absence of an entry in the target state. type TargetStateRemove struct{} // A TargetStateScript represents the state of a script. type TargetStateScript struct { name RelPath contentsFunc func() ([]byte, error) contentsSHA256Func func() ([32]byte, error) interpreter *Interpreter condition ScriptCondition sourceAttr SourceAttr sourceRelPath SourceRelPath } // A TargetStateSymlink represents the state of a symlink in the target state. type TargetStateSymlink struct { linknameFunc func() (string, error) sourceAttr SourceAttr } // A ModifyDirWithCmdState records the state of a directory modified by a // command. type ModifyDirWithCmdState struct { Name AbsPath `json:"name" yaml:"name"` RunAt time.Time `json:"runAt" yaml:"runAt"` } // A ScriptState records the state of a script that has been run. type ScriptState struct { Name RelPath `json:"name" yaml:"name"` RunAt time.Time `json:"runAt" yaml:"runAt"` } // Apply updates actualStateEntry to match t. func (t *TargetStateModifyDirWithCmd) Apply( system System, persistentState PersistentState, actualStateEntry ActualStateEntry, ) (bool, error) { if _, ok := actualStateEntry.(*ActualStateDir); !ok { if err := actualStateEntry.Remove(system); err != nil { return false, err } } runAt := time.Now().UTC() if err := system.RunCmd(t.cmdFunc()); err != nil { return false, fmt.Errorf("%s: %w", actualStateEntry.Path(), err) } modifyDirWithCmdStateKey := []byte(actualStateEntry.Path().String()) if err := PersistentStateSet( persistentState, GitRepoExternalStateBucket, modifyDirWithCmdStateKey, &ModifyDirWithCmdState{ Name: actualStateEntry.Path(), RunAt: runAt, }); err != nil { return false, err } return true, nil } // EntryState returns t's entry state. func (t *TargetStateModifyDirWithCmd) EntryState(umask fs.FileMode) (*EntryState, error) { return &EntryState{ Type: EntryStateTypeDir, Mode: fs.ModeDir | fs.ModePerm&^umask, }, nil } // Evaluate evaluates t. func (t *TargetStateModifyDirWithCmd) Evaluate() error { return nil } // SkipApply implements TargetStateEntry.SkipApply. func (t *TargetStateModifyDirWithCmd) SkipApply(persistentState PersistentState, targetAbsPath AbsPath) (bool, error) { if t.forceRefresh { return false, nil } modifyDirWithCmdKey := []byte(targetAbsPath.String()) switch modifyDirWithCmdStateBytes, err := persistentState.Get(GitRepoExternalStateBucket, modifyDirWithCmdKey); { case err != nil: return false, err case modifyDirWithCmdStateBytes == nil: return false, nil default: var modifyDirWithCmdState ModifyDirWithCmdState if err := stateFormat.Unmarshal(modifyDirWithCmdStateBytes, &modifyDirWithCmdState); err != nil { return false, err } if t.refreshPeriod == 0 { return true, nil } return time.Since(modifyDirWithCmdState.RunAt) < time.Duration(t.refreshPeriod), nil } } // SourceAttr implements TargetStateEntry.SourceAttr. func (t *TargetStateModifyDirWithCmd) SourceAttr() SourceAttr { return t.sourceAttr } // Apply updates actualStateEntry to match t. It does not recurse. func (t *TargetStateDir) Apply( system System, persistentState PersistentState, actualStateEntry ActualStateEntry, ) (bool, error) { if actualStateDir, ok := actualStateEntry.(*ActualStateDir); ok { if runtime.GOOS == "windows" || actualStateDir.perm == t.perm { return false, nil } return true, system.Chmod(actualStateDir.Path(), t.perm) } if err := actualStateEntry.Remove(system); err != nil { return false, err } return true, system.Mkdir(actualStateEntry.Path(), t.perm) } // EntryState returns t's entry state. func (t *TargetStateDir) EntryState(umask fs.FileMode) (*EntryState, error) { return &EntryState{ Type: EntryStateTypeDir, Mode: fs.ModeDir | t.perm&^umask, }, nil } // Evaluate evaluates t. func (t *TargetStateDir) Evaluate() error { return nil } // SkipApply implements TargetState.SkipApply. func (t *TargetStateDir) SkipApply(persistentState PersistentState, targetAbsPath AbsPath) (bool, error) { return false, nil } // SourceAttr implements TargetStateEntry.SourceAttr. func (t *TargetStateDir) SourceAttr() SourceAttr { return t.sourceAttr } // Apply updates actualStateEntry to match t. func (t *TargetStateFile) Apply( system System, persistentState PersistentState, actualStateEntry ActualStateEntry, ) (bool, error) { contents, err := t.Contents() if err != nil { return false, err } if !t.sourceAttr.External && !t.empty && isEmpty(contents) { if _, ok := actualStateEntry.(*ActualStateAbsent); ok { return false, nil } return true, system.RemoveAll(actualStateEntry.Path()) } if actualStateFile, ok := actualStateEntry.(*ActualStateFile); ok { // Compare file contents using only their SHA256 sums. This is so that // we can compare last-written states without storing the full contents // of each file written. actualContents, err := actualStateFile.Contents() if err != nil { return false, err } actualContentsSHA256 := sha256.Sum256(actualContents) contentsSHA256, err := t.ContentsSHA256() if err != nil { return false, err } if actualContentsSHA256 == contentsSHA256 { if runtime.GOOS == "windows" || actualStateFile.perm == t.perm { return false, nil } return true, system.Chmod(actualStateFile.Path(), t.perm) } } else if err := actualStateEntry.Remove(system); err != nil { return false, err } return true, system.WriteFile(actualStateEntry.Path(), contents, t.perm) } // Contents returns t's contents. func (t *TargetStateFile) Contents() ([]byte, error) { return t.contentsFunc() } // ContentsSHA256 returns the SHA256 sum of t's contents. func (t *TargetStateFile) ContentsSHA256() ([32]byte, error) { return t.contentsSHA256Func() } // EntryState returns t's entry state. func (t *TargetStateFile) EntryState(umask fs.FileMode) (*EntryState, error) { contents, err := t.Contents() if err != nil { return nil, err } if !t.sourceAttr.External && !t.empty && isEmpty(contents) { return &EntryState{ Type: EntryStateTypeRemove, }, nil } contentsSHA256, err := t.ContentsSHA256() if err != nil { return nil, err } return &EntryState{ Type: EntryStateTypeFile, Mode: t.perm &^ umask, ContentsSHA256: HexBytes(contentsSHA256[:]), contents: contents, overwrite: t.overwrite, }, nil } // Evaluate evaluates t. func (t *TargetStateFile) Evaluate() error { if _, err := t.Contents(); err != nil { return err } if _, err := t.ContentsSHA256(); err != nil { return err } return nil } // Perm returns t's perm. func (t *TargetStateFile) Perm(umask fs.FileMode) fs.FileMode { return t.perm &^ umask } // SkipApply implements TargetStateEntry.SkipApply. func (t *TargetStateFile) SkipApply(persistentState PersistentState, targetAbsPath AbsPath) (bool, error) { return false, nil } // SourceAttr implements TargetStateEntry.SourceAttr. func (t *TargetStateFile) SourceAttr() SourceAttr { return t.sourceAttr } // Apply updates actualStateEntry to match t. func (t *TargetStateRemove) Apply( system System, persistentState PersistentState, actualStateEntry ActualStateEntry, ) (bool, error) { if _, ok := actualStateEntry.(*ActualStateAbsent); ok { return false, nil } return true, system.RemoveAll(actualStateEntry.Path()) } // EntryState returns t's entry state. func (t *TargetStateRemove) EntryState(umask fs.FileMode) (*EntryState, error) { return &EntryState{ Type: EntryStateTypeRemove, }, nil } // Evaluate evaluates t. func (t *TargetStateRemove) Evaluate() error { return nil } // SkipApply implements TargetStateEntry.SkipApply. func (t *TargetStateRemove) SkipApply(persistentState PersistentState, targetAbsPath AbsPath) (bool, error) { return false, nil } // SourceAttr implements TargetStateEntry.SourceAttr. func (t *TargetStateRemove) SourceAttr() SourceAttr { return SourceAttr{} } // Apply runs t. func (t *TargetStateScript) Apply( system System, persistentState PersistentState, actualStateEntry ActualStateEntry, ) (bool, error) { skipApply, err := t.SkipApply(persistentState, actualStateEntry.Path()) if err != nil { return false, err } if skipApply { return false, nil } contentsSHA256, err := t.ContentsSHA256() if err != nil { return false, err } contents, err := t.Contents() if err != nil { return false, err } runAt := time.Now().UTC() if !isEmpty(contents) { if err := system.RunScript(t.name, actualStateEntry.Path().Dir(), contents, RunScriptOptions{ Condition: t.condition, Interpreter: t.interpreter, SourceRelPath: t.sourceRelPath, }); err != nil { return false, err } } scriptStateKey := []byte(hex.EncodeToString(contentsSHA256[:])) if err := PersistentStateSet(persistentState, ScriptStateBucket, scriptStateKey, &ScriptState{ Name: t.name, RunAt: runAt, }); err != nil { return false, err } entryStateKey := actualStateEntry.Path().Bytes() if err := PersistentStateSet(persistentState, EntryStateBucket, entryStateKey, &EntryState{ Type: EntryStateTypeScript, ContentsSHA256: HexBytes(contentsSHA256[:]), }); err != nil { return false, err } return true, nil } // Contents returns t's contents. func (t *TargetStateScript) Contents() ([]byte, error) { return t.contentsFunc() } // ContentsSHA256 returns the SHA256 sum of t's contents. func (t *TargetStateScript) ContentsSHA256() ([32]byte, error) { return t.contentsSHA256Func() } // EntryState returns t's entry state. func (t *TargetStateScript) EntryState(umask fs.FileMode) (*EntryState, error) { contentsSHA256, err := t.ContentsSHA256() if err != nil { return nil, err } return &EntryState{ Type: EntryStateTypeScript, ContentsSHA256: HexBytes(contentsSHA256[:]), }, nil } // Evaluate evaluates t. func (t *TargetStateScript) Evaluate() error { _, err := t.ContentsSHA256() return err } // SkipApply implements TargetStateEntry.SkipApply. func (t *TargetStateScript) SkipApply(persistentState PersistentState, targetAbsPath AbsPath) (bool, error) { switch contents, err := t.Contents(); { case err != nil: return false, err case len(contents) == 0: return true, nil } switch t.condition { case ScriptConditionAlways: return false, nil case ScriptConditionOnce: contentsSHA256, err := t.ContentsSHA256() if err != nil { return false, err } scriptStateKey := []byte(hex.EncodeToString(contentsSHA256[:])) switch scriptState, err := persistentState.Get(ScriptStateBucket, scriptStateKey); { case err != nil: return false, err case scriptState != nil: return true, nil } case ScriptConditionOnChange: entryStateKey := []byte(targetAbsPath.String()) switch entryStateBytes, err := persistentState.Get(EntryStateBucket, entryStateKey); { case err != nil: return false, err case entryStateBytes != nil: var entryState EntryState if err := stateFormat.Unmarshal(entryStateBytes, &entryState); err != nil { return false, err } contentsSHA256, err := t.ContentsSHA256() if err != nil { return false, err } if bytes.Equal(entryState.ContentsSHA256.Bytes(), contentsSHA256[:]) { return true, nil } } } return false, nil } // SourceAttr implements TargetStateEntry.SourceAttr. func (t *TargetStateScript) SourceAttr() SourceAttr { return t.sourceAttr } // Apply updates actualStateEntry to match t. func (t *TargetStateSymlink) Apply( system System, persistentState PersistentState, actualStateEntry ActualStateEntry, ) (bool, error) { linkname, err := t.Linkname() if err != nil { return false, err } if linkname == "" { if _, ok := actualStateEntry.(*ActualStateAbsent); ok { return false, nil } return true, system.RemoveAll(actualStateEntry.Path()) } if actualStateSymlink, ok := actualStateEntry.(*ActualStateSymlink); ok { actualLinkname, err := actualStateSymlink.Linkname() if err != nil { return false, err } linkname, err := t.Linkname() if err != nil { return false, err } if normalizeLinkname(actualLinkname) == normalizeLinkname(linkname) { return false, nil } } if err := actualStateEntry.Remove(system); err != nil { return false, err } return true, system.WriteSymlink(linkname, actualStateEntry.Path()) } // EntryState returns t's entry state. func (t *TargetStateSymlink) EntryState(umask fs.FileMode) (*EntryState, error) { linkname, err := t.Linkname() if err != nil { return nil, err } if linkname == "" { return &EntryState{ Type: EntryStateTypeRemove, }, nil } linknameSHA256 := sha256.Sum256([]byte(linkname)) return &EntryState{ Type: EntryStateTypeSymlink, ContentsSHA256: linknameSHA256[:], contents: []byte(linkname), }, nil } // Evaluate evaluates t. func (t *TargetStateSymlink) Evaluate() error { _, err := t.Linkname() return err } func (t *TargetStateSymlink) Linkname() (string, error) { return t.linknameFunc() } // SkipApply implements TargetStateEntry.SkipApply. func (t *TargetStateSymlink) SkipApply(persistentState PersistentState, targetAbsPath AbsPath) (bool, error) { return false, nil } // SourceAttr implements TargetStateEntry.SourceAttr. func (t *TargetStateSymlink) SourceAttr() SourceAttr { return t.sourceAttr } ================================================ FILE: internal/chezmoi/targetstateentry_test.go ================================================ package chezmoi import ( "crypto/sha256" "fmt" "io/fs" "maps" "slices" "testing" "github.com/alecthomas/assert/v2" "github.com/muesli/combinator" vfs "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestTargetStateEntryApply(t *testing.T) { targetStates := map[string]TargetStateEntry{ "dir": &TargetStateDir{ perm: fs.ModePerm &^ chezmoitest.Umask, }, "file": &TargetStateFile{ contentsFunc: eagerNoErr([]byte("# contents of file")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("# contents of file"))), perm: 0o666 &^ chezmoitest.Umask, }, "file_empty": &TargetStateFile{ contentsFunc: eagerZeroNoErr[[]byte](), contentsSHA256Func: eagerNoErr(sha256.Sum256(nil)), perm: 0o666 &^ chezmoitest.Umask, empty: true, }, "file_executable": &TargetStateFile{ contentsFunc: eagerNoErr([]byte("#!/bin/sh\n")), contentsSHA256Func: eagerNoErr(sha256.Sum256([]byte("#!/bin/sh\n"))), perm: fs.ModePerm &^ chezmoitest.Umask, }, "remove": &TargetStateRemove{}, "symlink": &TargetStateSymlink{ linknameFunc: eagerNoErr("target"), }, } actualStates := map[string]map[string]any{ "dir": { "/home/user/target": &vfst.Dir{Perm: fs.ModePerm}, }, "file": { "/home/user/target": "# contents of file", }, "file_empty": { "/home/user/target": "", }, "file_executable": { "/home/user/target": &vfst.File{ Perm: fs.ModePerm, Contents: []byte("!/bin/sh\n"), }, }, "remove": { "/home/user": &vfst.Dir{Perm: fs.ModePerm}, }, "symlink": { "/home/user": map[string]any{ "symlink-target": "", "target": &vfst.Symlink{Target: "symlink-target"}, }, }, "symlink_broken": { "/home/user/target": &vfst.Symlink{Target: "symlink-target"}, }, } testData := struct { TargetStateKey []string ActualDestDirStateKey []string }{ TargetStateKey: slices.Sorted(maps.Keys(targetStates)), ActualDestDirStateKey: slices.Sorted(maps.Keys(actualStates)), } var testCases []struct { TargetStateKey string ActualDestDirStateKey string } assert.NoError(t, combinator.Generate(&testCases, testData)) for _, tc := range testCases { name := fmt.Sprintf("target_%s_actual_%s", tc.TargetStateKey, tc.ActualDestDirStateKey) t.Run(name, func(t *testing.T) { targetState := targetStates[tc.TargetStateKey] actualState := actualStates[tc.ActualDestDirStateKey] chezmoitest.WithTestFS(t, actualState, func(fileSystem vfs.FS) { system := NewRealSystem(fileSystem) // Read the initial destination state entry from fileSystem. actualStateEntry, err := NewActualStateEntry(system, NewAbsPath("/home/user/target"), nil, nil) assert.NoError(t, err) // Apply the target state entry. _, err = targetState.Apply(system, nil, actualStateEntry) assert.NoError(t, err) // Verify that the actual state entry matches the desired // state. tests := vfst.TestPath("/home/user/target", targetStateTest(t, targetState)...) vfst.RunTests(t, fileSystem, "", tests) }) }) } } func targetStateTest(t *testing.T, ts TargetStateEntry) []vfst.PathTest { t.Helper() switch ts := ts.(type) { case *TargetStateRemove: return []vfst.PathTest{ vfst.TestDoesNotExist(), } case *TargetStateDir: return []vfst.PathTest{ vfst.TestIsDir(), vfst.TestModePerm(ts.perm &^ chezmoitest.Umask), } case *TargetStateFile: expectedContents, err := ts.Contents() assert.NoError(t, err) return []vfst.PathTest{ vfst.TestModeIsRegular(), vfst.TestContents(expectedContents), vfst.TestModePerm(ts.perm &^ chezmoitest.Umask), } case *TargetStateScript: return nil case *TargetStateSymlink: expectedLinkname, err := ts.Linkname() assert.NoError(t, err) return []vfst.PathTest{ vfst.TestModeType(fs.ModeSymlink), vfst.TestSymlinkTarget(expectedLinkname), } default: return nil } } ================================================ FILE: internal/chezmoi/tarwritersystem.go ================================================ package chezmoi import ( "archive/tar" "io" "io/fs" "os/exec" ) // A TarWriterSystem is a System that writes to a tar archive. type TarWriterSystem struct { emptySystemMixin noUpdateSystemMixin tarWriter *tar.Writer headerTemplate tar.Header } // NewTarWriterSystem returns a new TarWriterSystem that writes a tar file to w. func NewTarWriterSystem(w io.Writer, headerTemplate tar.Header) *TarWriterSystem { return &TarWriterSystem{ tarWriter: tar.NewWriter(w), headerTemplate: headerTemplate, } } // Close closes m. func (s *TarWriterSystem) Close() error { return s.tarWriter.Close() } // Mkdir implements System.Mkdir. func (s *TarWriterSystem) Mkdir(name AbsPath, perm fs.FileMode) error { header := s.headerTemplate header.Typeflag = tar.TypeDir header.Name = name.String() + "/" header.Mode = int64(perm) return s.tarWriter.WriteHeader(&header) } // RunCmd implements System.RunCmd. func (s *TarWriterSystem) RunCmd(cmd *exec.Cmd) error { return nil } // RunScript implements System.RunScript. func (s *TarWriterSystem) RunScript(scriptName RelPath, dir AbsPath, data []byte, options RunScriptOptions) error { return s.WriteFile(NewAbsPath(scriptName.String()), data, 0o700) } // WriteFile implements System.WriteFile. func (s *TarWriterSystem) WriteFile(filename AbsPath, data []byte, perm fs.FileMode) error { header := s.headerTemplate header.Typeflag = tar.TypeReg header.Name = filename.String() header.Size = int64(len(data)) header.Mode = int64(perm) if err := s.tarWriter.WriteHeader(&header); err != nil { return err } _, err := s.tarWriter.Write(data) return err } // WriteSymlink implements System.WriteSymlink. func (s *TarWriterSystem) WriteSymlink(oldName string, newName AbsPath) error { header := s.headerTemplate header.Typeflag = tar.TypeSymlink header.Name = newName.String() header.Linkname = oldName return s.tarWriter.WriteHeader(&header) } ================================================ FILE: internal/chezmoi/tarwritersystem_test.go ================================================ package chezmoi import ( "archive/tar" "bytes" "io" "io/fs" "testing" "github.com/alecthomas/assert/v2" "github.com/coreos/go-semver/semver" vfs "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoitest" ) var _ System = &TarWriterSystem{} func TestTarWriterSystem(t *testing.T) { chezmoitest.WithTestFS(t, map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiignore": "README.md\n", ".chezmoiremove": "*.txt\n", ".chezmoiversion": "1.2.3\n", ".chezmoitemplates": map[string]any{ "template": "# contents of .chezmoitemplates/template\n", }, "README.md": "", "dot_dir": map[string]any{ "file": "# contents of .dir/file\n", }, "run_script": "# contents of script\n", "symlink_symlink": ".dir/subdir/file\n", }, }, func(fileSystem vfs.FS) { ctx := t.Context() system := NewRealSystem(fileSystem) s := NewSourceState( WithBaseSystem(system), WithDestDir(NewAbsPath("/home/user")), WithSourceDir(NewAbsPath("/home/user/.local/share/chezmoi")), WithSystem(system), WithVersion(semver.Version{ Major: 1, Minor: 2, Patch: 3, }), ) assert.NoError(t, s.Read(ctx, nil)) requireEvaluateAll(t, s, system) b := &bytes.Buffer{} tarWriterSystem := NewTarWriterSystem(b, tar.Header{}) persistentState := NewMockPersistentState() err := s.applyAll(tarWriterSystem, system, persistentState, EmptyAbsPath, ApplyOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }) assert.NoError(t, err) assert.NoError(t, tarWriterSystem.Close()) r := tar.NewReader(b) for _, tc := range []struct { expectedTypeflag byte expectedName string expectedMode int64 expectedLinkname string expectedContents []byte }{ { expectedTypeflag: tar.TypeDir, expectedName: ".dir/", expectedMode: int64(fs.ModePerm &^ chezmoitest.Umask), }, { expectedTypeflag: tar.TypeReg, expectedName: ".dir/file", expectedContents: []byte("# contents of .dir/file\n"), expectedMode: 0o666 &^ int64(chezmoitest.Umask), }, { expectedTypeflag: tar.TypeReg, expectedName: "script", expectedContents: []byte("# contents of script\n"), expectedMode: 0o700, }, { expectedTypeflag: tar.TypeSymlink, expectedName: "symlink", expectedLinkname: ".dir/subdir/file", }, } { t.Run(tc.expectedName, func(t *testing.T) { header, err := r.Next() assert.NoError(t, err) assert.Equal(t, tc.expectedTypeflag, header.Typeflag) assert.Equal(t, tc.expectedName, header.Name) assert.Equal(t, tc.expectedMode, header.Mode) assert.Equal(t, tc.expectedLinkname, header.Linkname) assert.Equal(t, int64(len(tc.expectedContents)), header.Size) if tc.expectedContents != nil { actualContents, err := io.ReadAll(r) assert.NoError(t, err) assert.Equal(t, tc.expectedContents, actualContents) } }) } _, err = r.Next() assert.Equal(t, io.EOF, err) }) } ================================================ FILE: internal/chezmoi/template.go ================================================ package chezmoi import ( "bytes" "encoding/json" "fmt" "maps" "strconv" "strings" "text/template" "github.com/BurntSushi/toml" "github.com/goccy/go-yaml" "github.com/mattn/go-runewidth" "github.com/mitchellh/copystructure" "golang.org/x/text/encoding" "golang.org/x/text/encoding/unicode" ) // A Template extends [text/template.Template] with support for directives. type Template struct { name string template *template.Template options TemplateOptions } // TemplateOptions are template options that can be set with directives. type TemplateOptions struct { Encoding encoding.Encoding Funcs template.FuncMap FormatIndent string LeftDelimiter string LineEnding string RightDelimiter string Options []string } // ParseTemplate parses a template named name from data with the given funcs and // templateOptions. func ParseTemplate(name string, data []byte, options TemplateOptions) (*Template, error) { contents, err := options.parseAndRemoveDirectives(data) if err != nil { return nil, err } funcs := options.Funcs if options.FormatIndent != "" { funcs = maps.Clone(funcs) funcs["toJson"] = func(data any) string { var builder strings.Builder encoder := json.NewEncoder(&builder) encoder.SetIndent("", options.FormatIndent) if err := encoder.Encode(data); err != nil { panic(err) } return builder.String() } funcs["toToml"] = func(data any) string { var builder strings.Builder encoder := toml.NewEncoder(&builder) encoder.Indent = options.FormatIndent if err := encoder.Encode(data); err != nil { panic(err) } return builder.String() } funcs["toYaml"] = func(data any) string { var builder strings.Builder encoder := yaml.NewEncoder(&builder, yaml.Indent(runewidth.StringWidth(options.FormatIndent)), ) if err := encoder.Encode(data); err != nil { panic(err) } return builder.String() } } tmpl, err := template.New(name). Option(options.Options...). Delims(options.LeftDelimiter, options.RightDelimiter). Funcs(funcs). Parse(string(contents)) if err != nil { return nil, err } return &Template{ name: name, template: tmpl, options: options, }, nil } // AddParseTree adds tmpl's parse tree to t. func (t *Template) AddParseTree(tmpl *Template) (*Template, error) { var err error t.template, err = t.template.AddParseTree(tmpl.name, tmpl.template.Tree) return t, err } // Execute executes t with data. func (t *Template) Execute(data any) ([]byte, error) { if data != nil { // Make a deep copy of data, in case any template functions modify it. var err error data, err = copystructure.Copy(data) if err != nil { return nil, err } } var builder strings.Builder if err := t.template.ExecuteTemplate(&builder, t.name, data); err != nil { return nil, err } result := []byte(replaceLineEndings(builder.String(), t.options.LineEnding)) if t.options.Encoding != nil { return t.options.Encoding.NewEncoder().Bytes(result) } return result, nil } // ExecuteString executes t with s. func (t *Template) ExecuteString(data any) (string, error) { resultBytes, err := t.Execute(data) if err != nil { return "", err } return string(resultBytes), err } // parseAndRemoveDirectives updates o by parsing all template directives in data // and returns data with the lines containing directives removed. The lines are // removed so that any delimiters do not break template parsing. func (o *TemplateOptions) parseAndRemoveDirectives(data []byte) ([]byte, error) { directiveMatches := templateDirectiveRx.FindAllSubmatchIndex(data, -1) if directiveMatches == nil { return data, nil } // Parse options from directives. for _, directiveMatch := range directiveMatches { keyValuePairMatches := templateDirectiveKeyValuePairRx.FindAllSubmatch(data[directiveMatch[2]:directiveMatch[3]], -1) for _, keyValuePairMatch := range keyValuePairMatches { key := string(keyValuePairMatch[1]) value := maybeUnquote(string(keyValuePairMatch[2])) switch key { case "encoding": switch value { case "utf-8": o.Encoding = unicode.UTF8 case "utf-8-bom": o.Encoding = unicode.UTF8BOM case "utf-16-be": o.Encoding = unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM) case "utf-16-be-bom": o.Encoding = unicode.UTF16(unicode.BigEndian, unicode.UseBOM) case "utf-16-le": o.Encoding = unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) case "utf-16-le-bom": o.Encoding = unicode.UTF16(unicode.LittleEndian, unicode.UseBOM) default: return nil, fmt.Errorf("%s: unknown encoding", value) } case "format-indent": o.FormatIndent = value case "format-indent-width": width, err := strconv.Atoi(value) if err != nil { return nil, err } o.FormatIndent = strings.Repeat(" ", width) case "left-delimiter": o.LeftDelimiter = value case "line-ending", "line-endings": switch string(keyValuePairMatch[2]) { case "crlf": o.LineEnding = "\r\n" case "lf": o.LineEnding = "\n" case "native": o.LineEnding = nativeLineEnding default: o.LineEnding = value } case "right-delimiter": o.RightDelimiter = value case "missing-key": o.Options = append(o.Options, "missingkey="+value) } } } return removeMatches(data, directiveMatches), nil } // removeMatches returns data with matchesIndexes removed. func removeMatches(data []byte, matchesIndexes [][]int) []byte { slices := make([][]byte, len(matchesIndexes)+1) slices[0] = data[:matchesIndexes[0][0]] for i, matchIndexes := range matchesIndexes[1:] { slices[i+1] = data[matchesIndexes[i][1]:matchIndexes[0]] } slices[len(matchesIndexes)] = data[matchesIndexes[len(matchesIndexes)-1][1]:] return bytes.Join(slices, nil) } // replaceLineEndings replaces all line endings in s with lineEnding. If // lineEnding is empty it returns s unchanged. func replaceLineEndings(s, lineEnding string) string { if lineEnding == "" { return s } return lineEndingRx.ReplaceAllString(s, lineEnding) } ================================================ FILE: internal/chezmoi/template_test.go ================================================ package chezmoi import ( "testing" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestTemplateParseAndExecute(t *testing.T) { for _, tc := range []struct { name string dataStr string expectedStr string }{ { name: "missing_key", dataStr: chezmoitest.JoinLines( "# chezmoi:template:missing-key=invalid", "{{ .missing }}", ), expectedStr: chezmoitest.JoinLines( "", ), }, { name: "delimiters", dataStr: chezmoitest.JoinLines( "# chezmoi:template:left-delimiter=[[ right-delimiter=]]", "[[ 0 ]]", ), expectedStr: chezmoitest.JoinLines( "0", ), }, { name: "line_ending_crlf", dataStr: "" + "unix\n" + "\n" + "windows\r\n" + "\r\n" + "# chezmoi:template:line-ending=crlf\n", expectedStr: "" + "unix\r\n" + "\r\n" + "windows\r\n" + "\r\n", }, { name: "line_ending_lf", dataStr: "" + "unix\n" + "\n" + "windows\r\n" + "\r\n" + "# chezmoi:template:line-ending=lf\n", expectedStr: chezmoitest.JoinLines( "unix", "", "windows", "", ), }, } { t.Run(tc.name, func(t *testing.T) { tmpl, err := ParseTemplate(tc.name, []byte(tc.dataStr), TemplateOptions{}) assert.NoError(t, err) actual, err := tmpl.Execute(nil) assert.NoError(t, err) assert.Equal(t, tc.expectedStr, string(actual)) }) } } ================================================ FILE: internal/chezmoi/templatefuncs.go ================================================ package chezmoi import "errors" var errReturnEmpty = errors.New("return empty") func AbortEmptyTemplateFunc() string { panic(errReturnEmpty) } ================================================ FILE: internal/chezmoi/transparentencryption.go ================================================ package chezmoi import ( "os" "github.com/google/renameio/v2/maybe" ) // A TransparentEncryption assumes that encryption is handled transparently. // Encrypted files still get the encrypted_ attribute, but no encryption occurs // in chezmoi. type TransparentEncryption struct{} // Decrypt implements Encryption.Decrypt. func (t TransparentEncryption) Decrypt(ciphertext []byte) ([]byte, error) { return ciphertext, nil } // DecryptToFile implements Encryption.DecryptToFile. func (t TransparentEncryption) DecryptToFile(plaintextAbsPath AbsPath, ciphertext []byte) error { return maybe.WriteFile(plaintextAbsPath.String(), ciphertext, 0o600) } // Encrypt implements Encryption.Encrypt. func (t TransparentEncryption) Encrypt(plaintext []byte) ([]byte, error) { return plaintext, nil } // EncryptFile implements Encryption.EncryptFile. func (t TransparentEncryption) EncryptFile(plaintextAbsPath AbsPath) ([]byte, error) { return os.ReadFile(plaintextAbsPath.String()) } // EncryptedSuffix implements Encryption.EncryptedSuffix. func (t TransparentEncryption) EncryptedSuffix() string { return "" } ================================================ FILE: internal/chezmoi/transparentencryption_test.go ================================================ package chezmoi var _ Encryption = TransparentEncryption{} ================================================ FILE: internal/chezmoi/zipwritersystem.go ================================================ package chezmoi import ( "io" "io/fs" "os/exec" "time" "github.com/klauspost/compress/zip" ) // A ZIPWriterSystem is a System that writes to a ZIP archive. type ZIPWriterSystem struct { emptySystemMixin noUpdateSystemMixin zipWriter *zip.Writer modified time.Time } // NewZIPWriterSystem returns a new ZIPWriterSystem that writes a ZIP archive to // w. func NewZIPWriterSystem(w io.Writer, modified time.Time) *ZIPWriterSystem { return &ZIPWriterSystem{ zipWriter: zip.NewWriter(w), modified: modified, } } // Close closes m. func (s *ZIPWriterSystem) Close() error { return s.zipWriter.Close() } // Mkdir implements System.Mkdir. func (s *ZIPWriterSystem) Mkdir(name AbsPath, perm fs.FileMode) error { fileHeader := zip.FileHeader{ Name: name.String(), Modified: s.modified, } fileHeader.SetMode(fs.ModeDir | perm) _, err := s.zipWriter.CreateHeader(&fileHeader) return err } // RunCmd implements System.RunCmd. func (s *ZIPWriterSystem) RunCmd(cmd *exec.Cmd) error { return nil } // RunScript implements System.RunScript. func (s *ZIPWriterSystem) RunScript(scriptName RelPath, dir AbsPath, data []byte, options RunScriptOptions) error { return s.WriteFile(NewAbsPath(scriptName.String()), data, 0o700) } // WriteFile implements System.WriteFile. func (s *ZIPWriterSystem) WriteFile(filename AbsPath, data []byte, perm fs.FileMode) error { fileHeader := zip.FileHeader{ Name: filename.String(), Method: zip.Deflate, Modified: s.modified, UncompressedSize64: uint64(len(data)), } fileHeader.SetMode(perm) fileWriter, err := s.zipWriter.CreateHeader(&fileHeader) if err != nil { return err } _, err = fileWriter.Write(data) return err } // WriteSymlink implements System.WriteSymlink. func (s *ZIPWriterSystem) WriteSymlink(oldName string, newName AbsPath) error { data := []byte(oldName) fileHeader := zip.FileHeader{ Name: newName.String(), Modified: s.modified, UncompressedSize64: uint64(len(data)), } fileHeader.SetMode(fs.ModeSymlink) fileWriter, err := s.zipWriter.CreateHeader(&fileHeader) if err != nil { return err } _, err = fileWriter.Write(data) return err } ================================================ FILE: internal/chezmoi/zipwritersystem_test.go ================================================ package chezmoi import ( "bytes" "io" "io/fs" "testing" "time" "github.com/alecthomas/assert/v2" "github.com/coreos/go-semver/semver" "github.com/klauspost/compress/zip" vfs "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoitest" ) var _ System = &ZIPWriterSystem{} func TestZIPWriterSystem(t *testing.T) { chezmoitest.WithTestFS(t, map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiignore": "README.md\n", ".chezmoiremove": "*.txt\n", ".chezmoiversion": "1.2.3\n", ".chezmoitemplates": map[string]any{ "template": "# contents of .chezmoitemplates/template\n", }, "README.md": "", "dot_dir": map[string]any{ "file": "# contents of .dir/file\n", }, "run_script": "# contents of script\n", "symlink_symlink": ".dir/subdir/file\n", }, }, func(fileSystem vfs.FS) { ctx := t.Context() system := NewRealSystem(fileSystem) s := NewSourceState( WithBaseSystem(system), WithDestDir(NewAbsPath("/home/user")), WithSourceDir(NewAbsPath("/home/user/.local/share/chezmoi")), WithSystem(system), WithVersion(semver.Version{ Major: 1, Minor: 2, Patch: 3, }), ) assert.NoError(t, s.Read(ctx, nil)) requireEvaluateAll(t, s, system) b := &bytes.Buffer{} zipWriterSystem := NewZIPWriterSystem(b, time.Now().UTC()) persistentState := NewMockPersistentState() err := s.applyAll(zipWriterSystem, system, persistentState, EmptyAbsPath, ApplyOptions{ Filter: NewEntryTypeFilter(EntryTypesAll, EntryTypesNone), }) assert.NoError(t, err) assert.NoError(t, zipWriterSystem.Close()) r, err := zip.NewReader(bytes.NewReader(b.Bytes()), int64(b.Len())) assert.NoError(t, err) expectedFiles := []struct { name string method uint16 mode fs.FileMode contents []byte }{ { name: ".dir", mode: (fs.ModeDir | fs.ModePerm) &^ chezmoitest.Umask, }, { name: ".dir/file", method: zip.Deflate, mode: 0o666 &^ chezmoitest.Umask, contents: []byte("# contents of .dir/file\n"), }, { name: "script", method: zip.Deflate, mode: 0o700 &^ chezmoitest.Umask, contents: []byte("# contents of script\n"), }, { name: "symlink", mode: fs.ModeSymlink, contents: []byte(".dir/subdir/file"), }, } assert.Equal(t, len(expectedFiles), len(r.File)) for i, expectedFile := range expectedFiles { t.Run(expectedFile.name, func(t *testing.T) { actualFile := r.File[i] assert.Equal(t, expectedFile.name, actualFile.Name) assert.Equal(t, expectedFile.method, actualFile.Method) assert.Equal(t, expectedFile.mode, actualFile.Mode()) if expectedFile.contents != nil { rc, err := actualFile.Open() assert.NoError(t, err) actualContents, err := io.ReadAll(rc) assert.NoError(t, err) assert.Equal(t, expectedFile.contents, actualContents) } }) } }) } ================================================ FILE: internal/chezmoiassert/chezmoiassert.go ================================================ // Package chezmoiassert implements testing assertions not implemented by // github.com/alecthomas/assert/v2. package chezmoiassert import ( "fmt" "testing" "github.com/alecthomas/assert/v2" ) func PanicsWithError(tb testing.TB, expected error, fn func(), msgAndArgs ...any) { tb.Helper() defer func() { if value, ok := recover().(error); ok { assert.Equal(tb, expected, value, msgAndArgs...) } else { msg := formatMsgAndArgs("Expected function to panic with error", msgAndArgs...) tb.Fatal(msg) } }() fn() } func PanicsWithErrorString(tb testing.TB, errString string, fn func(), msgAndArgs ...any) { tb.Helper() defer func() { if value, ok := recover().(error); ok { assert.EqualError(tb, value, errString, msgAndArgs...) } else { msg := formatMsgAndArgs("Expected function to panic with error string", msgAndArgs...) tb.Fatal(msg) } }() fn() } func formatMsgAndArgs(dflt string, msgAndArgs ...any) string { if len(msgAndArgs) == 0 { return dflt } return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) //nolint:forcetypeassert } ================================================ FILE: internal/chezmoibubbles/boolinputmodel.go ================================================ package chezmoibubbles import ( "strconv" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "chezmoi.io/chezmoi/internal/chezmoi" ) type BoolInputModel struct { textInput textinput.Model defaultValue *bool canceled bool } func NewBoolInputModel(prompt string, defaultValue *bool) BoolInputModel { textInput := textinput.New() textInput.Prompt = prompt textInput.Placeholder = "bool" if defaultValue != nil { textInput.Placeholder += ", default " + strconv.FormatBool(*defaultValue) } textInput.Validate = func(value string) error { if value == "" && defaultValue != nil { return nil } _, err := chezmoi.ParseBool(value) return err } textInput.Focus() return BoolInputModel{ textInput: textInput, defaultValue: defaultValue, } } func (m BoolInputModel) Canceled() bool { return m.canceled } func (m BoolInputModel) Init() tea.Cmd { return textinput.Blink } func (m BoolInputModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if keyMsg, ok := msg.(tea.KeyMsg); ok { switch keyMsg.Type { case tea.KeyCtrlC, tea.KeyEsc: m.canceled = true return m, tea.Quit case tea.KeyEnter: if m.defaultValue != nil { m.textInput.SetValue(strconv.FormatBool(*m.defaultValue)) return m, tea.Quit } } } var cmd tea.Cmd m.textInput, cmd = m.textInput.Update(msg) if _, err := chezmoi.ParseBool(m.textInput.Value()); err == nil { return m, tea.Quit } return m, cmd } func (m BoolInputModel) Value() bool { valueStr := m.textInput.Value() if valueStr == "" && m.defaultValue != nil { return *m.defaultValue } value, _ := chezmoi.ParseBool(valueStr) return value } func (m BoolInputModel) View() string { return m.textInput.View() } ================================================ FILE: internal/chezmoibubbles/boolinputmodel_test.go ================================================ package chezmoibubbles import ( "testing" "github.com/alecthomas/assert/v2" ) func TestBoolInputModel(t *testing.T) { for _, tc := range []struct { name string defaultValue *bool input string expectedCanceled bool expectedValue bool }{ { name: "empty_with_default", defaultValue: newValue(true), input: "\r", expectedValue: true, }, { name: "cancel_ctrlc", input: "\x03", expectedCanceled: true, }, { name: "cancel_esc", input: "\x1b", expectedCanceled: true, }, { name: "true", input: "t", expectedValue: true, }, { name: "false", input: "f", expectedValue: false, }, { name: "yes", input: "y", expectedValue: true, }, { name: "no", input: "n", expectedValue: false, }, { name: "one", input: "1", expectedValue: true, }, { name: "zero", input: "0", expectedValue: false, }, } { t.Run(tc.name, func(t *testing.T) { actualModel := testRunModelWithInput(t, NewBoolInputModel("prompt", tc.defaultValue), tc.input) assert.Equal(t, tc.expectedCanceled, actualModel.Canceled()) assert.Equal(t, tc.expectedValue, actualModel.Value()) }) } } ================================================ FILE: internal/chezmoibubbles/chezmoibubbles.go ================================================ // Package chezmoibubbles provides text user interface components for chezmoi // using github.com/charmbracelet/bubbletea. package chezmoibubbles ================================================ FILE: internal/chezmoibubbles/chezmoibubbles_test.go ================================================ package chezmoibubbles import ( "testing" "github.com/alecthomas/assert/v2" tea "github.com/charmbracelet/bubbletea" "chezmoi.io/chezmoi/internal/chezmoiset" ) var keyTypes = chezmoiset.New( tea.KeyCtrlC, tea.KeyEnter, tea.KeyEsc, ) func makeKeyMsg(r rune) tea.Msg { key := tea.Key{ Type: tea.KeyRunes, Runes: []rune{r}, } if keyTypes.Contains(tea.KeyType(r)) { key = tea.Key{ Type: tea.KeyType(r), } } return tea.KeyMsg(key) } func makeKeyMsgs(s string) []tea.Msg { msgs := make([]tea.Msg, len(s)) for i, r := range s { msgs[i] = makeKeyMsg(r) } return msgs } func testRunModelWithInput[M tea.Model](t *testing.T, model M, input string) M { t.Helper() for _, msg := range makeKeyMsgs(input) { m, _ := model.Update(msg) var ok bool model, ok = m.(M) assert.True(t, ok) } return model } func newValue[T any](value T) *T { return &value } ================================================ FILE: internal/chezmoibubbles/choiceinputmodel.go ================================================ package chezmoibubbles import ( "errors" "strings" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoiset" ) type ChoiceInputModel struct { textInput textinput.Model uniqueAbbreviations map[string]string defaultValue *string canceled bool } func NewChoiceInputModel(prompt string, choices []string, defaultValue *string) ChoiceInputModel { textInput := textinput.New() textInput.Prompt = prompt textInput.Placeholder = strings.Join(choices, "/") if defaultValue != nil { textInput.Placeholder += ", default " + *defaultValue } allAbbreviations := chezmoiset.New[string]() for _, choice := range choices { for i := range choice { allAbbreviations.Add(choice[:i+1]) } } textInput.Validate = func(s string) error { if s == "" && defaultValue != nil { return nil } if allAbbreviations.Contains(s) { return nil } return errors.New("unknown or ambiguous choice") } textInput.Focus() return ChoiceInputModel{ textInput: textInput, uniqueAbbreviations: chezmoi.UniqueAbbreviations(choices), defaultValue: defaultValue, } } func (m ChoiceInputModel) Canceled() bool { return m.canceled } func (m ChoiceInputModel) Init() tea.Cmd { return textinput.Blink } func (m ChoiceInputModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if keyMsg, ok := msg.(tea.KeyMsg); ok { switch keyMsg.Type { case tea.KeyCtrlC, tea.KeyEsc: m.canceled = true return m, tea.Quit case tea.KeyEnter: value := m.textInput.Value() if value == "" && m.defaultValue != nil { m.textInput.SetValue(*m.defaultValue) return m, tea.Quit } else if value, ok := m.uniqueAbbreviations[value]; ok { m.textInput.SetValue(value) return m, tea.Quit } } } var cmd tea.Cmd m.textInput, cmd = m.textInput.Update(msg) if _, ok := m.uniqueAbbreviations[m.textInput.Value()]; ok { return m, tea.Quit } return m, cmd } func (m ChoiceInputModel) Value() string { value := m.textInput.Value() if value == "" && m.defaultValue != nil { return *m.defaultValue } return m.uniqueAbbreviations[value] } func (m ChoiceInputModel) View() string { return m.textInput.View() } ================================================ FILE: internal/chezmoibubbles/choiceinputmodel_test.go ================================================ package chezmoibubbles import ( "testing" "github.com/alecthomas/assert/v2" ) func TestChoiceInputModel(t *testing.T) { choicesYesNoAll := []string{"yes", "no", "all"} for _, tc := range []struct { name string choices []string defaultValue *string input string expectedCanceled bool expectedValue string }{ { name: "empty_with_default", choices: choicesYesNoAll, defaultValue: newValue("all"), input: "\r", expectedValue: "all", }, { name: "cancel_ctrlc", input: "\x03", expectedCanceled: true, }, { name: "cancel_esc", input: "\x1b", expectedCanceled: true, }, { name: "y", choices: choicesYesNoAll, input: "y", expectedValue: "yes", }, { name: "n", choices: choicesYesNoAll, input: "n", expectedValue: "no", }, { name: "a", choices: choicesYesNoAll, input: "a", expectedValue: "all", }, { name: "ambiguous_a", choices: []string{"aaa", "abb", "bbb"}, input: "a", }, { name: "unambiguous_b", choices: []string{"aaa", "abb", "bbb"}, input: "b", expectedValue: "bbb", }, { name: "ambiguous_resolved", choices: []string{"aaa", "abb", "bbb"}, input: "aa", expectedValue: "aaa", }, } { t.Run(tc.name, func(t *testing.T) { actualModel := testRunModelWithInput(t, NewChoiceInputModel("prompt", tc.choices, tc.defaultValue), tc.input) assert.Equal(t, tc.expectedCanceled, actualModel.Canceled()) assert.Equal(t, tc.expectedValue, actualModel.Value()) }) } } ================================================ FILE: internal/chezmoibubbles/intinputmodel.go ================================================ package chezmoibubbles import ( "strconv" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" ) type IntInputModel struct { textInput textinput.Model defaultValue *int64 canceled bool } func NewIntInputModel(prompt string, defaultValue *int64) IntInputModel { textInput := textinput.New() textInput.Prompt = prompt textInput.Placeholder = "int" if defaultValue != nil { textInput.Placeholder += ", default " + strconv.FormatInt(*defaultValue, 10) } textInput.Validate = func(value string) error { if value == "" && defaultValue != nil { return nil } if value == "-" { return nil } _, err := strconv.ParseInt(value, 10, 64) return err } textInput.Focus() return IntInputModel{ textInput: textInput, defaultValue: defaultValue, } } func (m IntInputModel) Canceled() bool { return m.canceled } func (m IntInputModel) Init() tea.Cmd { return textinput.Blink } func (m IntInputModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if keyMsg, ok := msg.(tea.KeyMsg); ok { switch keyMsg.Type { case tea.KeyCtrlC, tea.KeyEsc: m.canceled = true return m, tea.Quit case tea.KeyEnter: if m.textInput.Value() == "" && m.defaultValue != nil { m.textInput.SetValue(strconv.FormatInt(*m.defaultValue, 10)) } return m, tea.Quit } } var cmd tea.Cmd m.textInput, cmd = m.textInput.Update(msg) return m, cmd } func (m IntInputModel) Value() int64 { valueStr := m.textInput.Value() if valueStr == "" && m.defaultValue != nil { return *m.defaultValue } value, _ := strconv.ParseInt(valueStr, 10, 64) return value } func (m IntInputModel) View() string { return m.textInput.View() } ================================================ FILE: internal/chezmoibubbles/intinputmodel_test.go ================================================ package chezmoibubbles import ( "testing" "github.com/alecthomas/assert/v2" ) func TestIntInputModel(t *testing.T) { for _, tc := range []struct { name string defaultValue *int64 input string expectedCanceled bool expectedValue int64 }{ { name: "empty_with_default", defaultValue: newValue(int64(1)), input: "\r", expectedValue: 1, }, { name: "cancel_ctrlc", input: "\x03", expectedCanceled: true, }, { name: "cancel_esc", input: "\x1b", expectedCanceled: true, }, { name: "one_enter", input: "1\r", expectedValue: 1, }, { name: "minus_one_enter", input: "-1\r", expectedValue: -1, }, { name: "minus_enter", input: "-\r", expectedValue: 0, }, { name: "one_invalid_enter", input: "1a\r", expectedValue: 0, }, } { t.Run(tc.name, func(t *testing.T) { actualModel := testRunModelWithInput(t, NewIntInputModel("prompt", tc.defaultValue), tc.input) assert.Equal(t, tc.expectedCanceled, actualModel.Canceled()) assert.Equal(t, tc.expectedValue, actualModel.Value()) }) } } ================================================ FILE: internal/chezmoibubbles/multichoiceinputmodel.go ================================================ package chezmoibubbles import ( "slices" "strings" "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/paginator" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) // This is adapted from github.com/charmbracelet/gum/blob/main/choose/... for // chezmoi, as of gum 0.15.2. Many things which are configurable in gum are not // configurable in this module. // // Specific configuration options: // - Provided options (`choices`) are also the labels. // - No support for automatic select with one entry in the choice list. // - Any number of options may be selected. // - Default values are pre-selected. There is no support for wildcard // selection. // - Options are shown in the order provided, selections are preserved in the // order selected. type item struct { text string selected bool } type keymap struct { Down, Up, Right, Left, Home, End, ToggleAll, Toggle, Abort, Quit, Submit key.Binding } type MultichoiceInputModel struct { items []item defaultValue *[]string header string quitting bool submitted bool index int numSelected int paginator paginator.Model help help.Model keymap keymap } const ( height = 10 cursor = "> " selectedPrefix = "✓ " unselectedPrefix = "• " cursorPrefix = "• " ) var ( cursorStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("212")) headerStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("99")) itemStyle = lipgloss.NewStyle() selectedItemStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("212")) ) func NewMultichoiceInputModel(prompt string, choices []string, defaultValue *[]string) MultichoiceInputModel { var ( subduedStyle = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#847A85", Dark: "#979797"}) verySubduedStyle = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#DDDADA", Dark: "#3C3C3C"}) ) currentSelected := 0 hasSelectedItems := defaultValue != nil && len(*defaultValue) > 0 startingIndex := 0 items := make([]item, len(choices)) for i, option := range choices { // Check if the option should be selected isSelected := hasSelectedItems && slices.Contains(*defaultValue, option) if isSelected { currentSelected++ } items[i] = item{text: option, selected: isSelected} } // Use the pagination model to display the current and total number of // pages. pager := paginator.New() pager.SetTotalPages((len(items) + height - 1) / height) pager.PerPage = height pager.Type = paginator.Dots pager.ActiveDot = subduedStyle.Render("•") pager.InactiveDot = verySubduedStyle.Render("•") pager.KeyMap = paginator.KeyMap{} pager.Page = startingIndex / height km := keymap{ Down: key.NewBinding(key.WithKeys("down", "j", "ctrl+j", "ctrl+n")), Up: key.NewBinding(key.WithKeys("up", "k", "ctrl+k", "ctrl+p")), Right: key.NewBinding(key.WithKeys("right", "l", "ctrl+f")), Left: key.NewBinding(key.WithKeys("left", "h", "ctrl+b")), Home: key.NewBinding(key.WithKeys("g", "home")), End: key.NewBinding(key.WithKeys("G", "end")), ToggleAll: key.NewBinding( key.WithKeys("a", "A", "ctrl+a"), key.WithHelp("ctrl+a", "select all"), ), Toggle: key.NewBinding( key.WithKeys(" ", "tab", "x", "ctrl+@"), key.WithHelp("x", "toggle"), ), Abort: key.NewBinding( key.WithKeys("ctrl+c"), key.WithHelp("ctrl+c", "abort"), ), Quit: key.NewBinding( key.WithKeys("esc"), key.WithHelp("esc", "quit"), ), Submit: key.NewBinding( key.WithKeys("enter", "ctrl+q"), key.WithHelp("enter", "submit"), ), } m := MultichoiceInputModel{ items: items, defaultValue: defaultValue, header: prompt, index: startingIndex, paginator: pager, numSelected: currentSelected, help: help.New(), keymap: km, } return m } func (m MultichoiceInputModel) Init() tea.Cmd { return nil } func (m MultichoiceInputModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: return m, nil case tea.KeyMsg: start, end := m.paginator.GetSliceBounds(len(m.items)) km := m.keymap switch { case key.Matches(msg, km.Down): m.index++ if m.index >= len(m.items) { m.index = 0 m.paginator.Page = 0 } if m.index >= end { m.paginator.NextPage() } case key.Matches(msg, km.Up): m.index-- if m.index < 0 { m.index = len(m.items) - 1 m.paginator.Page = m.paginator.TotalPages - 1 } if m.index < start { m.paginator.PrevPage() } case key.Matches(msg, km.Right): m.index = clamp(m.index+height, 0, len(m.items)-1) m.paginator.NextPage() case key.Matches(msg, km.Left): m.index = clamp(m.index-height, 0, len(m.items)-1) m.paginator.PrevPage() case key.Matches(msg, km.End): m.index = len(m.items) - 1 m.paginator.Page = m.paginator.TotalPages - 1 case key.Matches(msg, km.Home): m.index = 0 m.paginator.Page = 0 case key.Matches(msg, km.ToggleAll): if m.numSelected < len(m.items) { m = m.selectAll() } else { m = m.deselectAll() } case key.Matches(msg, km.Quit): if m.numSelected < 1 && m.defaultValue != nil { for i := range m.items { if slices.Contains(*m.defaultValue, m.items[i].text) { m.items[i].selected = true m.numSelected++ } } } m.quitting = true return m, tea.Quit case key.Matches(msg, km.Abort): m.quitting = true return m, tea.Interrupt case key.Matches(msg, km.Toggle): if m.items[m.index].selected { m.items[m.index].selected = false m.numSelected-- } else { m.items[m.index].selected = true m.numSelected++ } case key.Matches(msg, km.Submit): m.quitting = true m.submitted = true return m, tea.Quit } } var cmd tea.Cmd m.paginator, cmd = m.paginator.Update(msg) return m, cmd } func (m MultichoiceInputModel) View() string { if m.quitting { return "" } var s strings.Builder start, end := m.paginator.GetSliceBounds(len(m.items)) for i, item := range m.items[start:end] { if i == m.index%height { s.WriteString(cursorStyle.Render(cursor)) } else { s.WriteString(strings.Repeat(" ", lipgloss.Width(cursor))) } switch { case item.selected: s.WriteString(selectedItemStyle.Render(selectedPrefix + item.text)) case i == m.index%height: s.WriteString(cursorStyle.Render(cursorPrefix + item.text)) default: s.WriteString(itemStyle.Render(unselectedPrefix + item.text)) } if i != height { s.WriteRune('\n') } } if m.paginator.TotalPages > 1 { s.WriteString(strings.Repeat("\n", height-m.paginator.ItemsOnPage(len(m.items))+1)) s.WriteString(" " + m.paginator.View()) } var parts []string if m.header != "" { parts = append(parts, headerStyle.Render(m.header)) } parts = append(parts, s.String(), m.help.View(m.keymap)) return lipgloss.JoinVertical(lipgloss.Left, parts...) } func (m MultichoiceInputModel) Value() []string { if (m.numSelected == 0 || !m.submitted) && m.defaultValue != nil { return *m.defaultValue } var out []string for _, item := range m.items { if item.selected { out = append(out, item.text) } } return out } func (m MultichoiceInputModel) deselectAll() MultichoiceInputModel { for i := range m.items { m.items[i].selected = false } m.numSelected = 0 return m } func (m MultichoiceInputModel) selectAll() MultichoiceInputModel { for i := range m.items { if m.items[i].selected { continue } m.items[i].selected = true m.numSelected++ } return m } func clamp(x, low, high int) int { if x < low { return low } if x > high { return high } return x } // FullHelp implements help.KeyMap. func (k keymap) FullHelp() [][]key.Binding { return nil } // ShortHelp implements help.KeyMap. func (k keymap) ShortHelp() []key.Binding { return []key.Binding{ k.Toggle, key.NewBinding( key.WithKeys("up", "down", "right", "left"), key.WithHelp("←↓↑→", "navigate"), ), k.Submit, k.ToggleAll, } } ================================================ FILE: internal/chezmoibubbles/passwordinputmodel.go ================================================ package chezmoibubbles import ( "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" ) type PasswordInputModel struct { textInput textinput.Model canceled bool } func NewPasswordInputModel(prompt, placeholder string) PasswordInputModel { textInput := textinput.New() textInput.Prompt = prompt textInput.Placeholder = placeholder textInput.EchoMode = textinput.EchoNone textInput.Focus() return PasswordInputModel{ textInput: textInput, } } func (m PasswordInputModel) Canceled() bool { return m.canceled } func (m PasswordInputModel) Init() tea.Cmd { return textinput.Blink } func (m PasswordInputModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if keyMsg, ok := msg.(tea.KeyMsg); ok { switch keyMsg.Type { case tea.KeyCtrlC, tea.KeyEsc: m.canceled = true return m, tea.Quit case tea.KeyEnter: return m, tea.Quit } } var cmd tea.Cmd m.textInput, cmd = m.textInput.Update(msg) return m, cmd } func (m PasswordInputModel) Value() string { return m.textInput.Value() } func (m PasswordInputModel) View() string { return m.textInput.View() } ================================================ FILE: internal/chezmoibubbles/passwordinputmodel_test.go ================================================ package chezmoibubbles import ( "testing" "github.com/alecthomas/assert/v2" ) func TestPasswordInputModel(t *testing.T) { for _, tc := range []struct { name string input string expectedCanceled bool expectedValue string }{ { name: "empty", input: "\r", }, { name: "cancel_ctrlc", input: "\x03", expectedCanceled: true, }, { name: "cancel_esc", input: "\x1b", expectedCanceled: true, }, { name: "password_enter", input: "password\r", expectedValue: "password", }, { name: "password_ctrlc", input: "password\x03", expectedCanceled: true, expectedValue: "password", }, } { t.Run(tc.name, func(t *testing.T) { actualModel := testRunModelWithInput(t, NewPasswordInputModel("prompt", "placeholder"), tc.input) assert.Equal(t, tc.expectedCanceled, actualModel.Canceled()) assert.Equal(t, tc.expectedValue, actualModel.Value()) }) } } ================================================ FILE: internal/chezmoibubbles/stringinputmodel.go ================================================ package chezmoibubbles import ( "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" ) type StringInputModel struct { textInput textinput.Model defaultValue *string canceled bool } func NewStringInputModel(prompt string, defaultValue *string) StringInputModel { textInput := textinput.New() textInput.Prompt = prompt textInput.Placeholder = "string" if defaultValue != nil { textInput.Placeholder += ", default " + *defaultValue } textInput.Focus() return StringInputModel{ textInput: textInput, defaultValue: defaultValue, } } func (m StringInputModel) Canceled() bool { return m.canceled } func (m StringInputModel) Init() tea.Cmd { return textinput.Blink } func (m StringInputModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if keyMsg, ok := msg.(tea.KeyMsg); ok { switch keyMsg.Type { case tea.KeyCtrlC, tea.KeyEsc: m.canceled = true return m, tea.Quit case tea.KeyEnter: return m, tea.Quit } } var cmd tea.Cmd m.textInput, cmd = m.textInput.Update(msg) return m, cmd } func (m StringInputModel) Value() string { value := m.textInput.Value() if value == "" && m.defaultValue != nil { return *m.defaultValue } return value } func (m StringInputModel) View() string { return m.textInput.View() } ================================================ FILE: internal/chezmoibubbles/stringinputmodel_test.go ================================================ package chezmoibubbles import ( "testing" "github.com/alecthomas/assert/v2" ) func TestStringInputModel(t *testing.T) { for _, tc := range []struct { name string defaultValue *string input string expectedCanceled bool expectedValue string }{ { name: "empty", input: "\r", }, { name: "empty_with_default", defaultValue: newValue("default"), input: "\r", expectedValue: "default", }, { name: "cancel_ctrlc", input: "\x03", expectedCanceled: true, }, { name: "cancel_esc", input: "\x1b", expectedCanceled: true, }, { name: "value_enter", input: "value\r", expectedValue: "value", }, { name: "value_ctrlc", input: "value\x03", expectedCanceled: true, expectedValue: "value", }, } { t.Run(tc.name, func(t *testing.T) { actualModel := testRunModelWithInput(t, NewStringInputModel("prompt", tc.defaultValue), tc.input) assert.Equal(t, tc.expectedCanceled, actualModel.Canceled()) assert.Equal(t, tc.expectedValue, actualModel.Value()) }) } } ================================================ FILE: internal/chezmoibubbles/test-all.sh ================================================ #!/bin/bash set -eufo pipefail go tool chezmoi internal-test prompt-bool "yes or no" go tool chezmoi internal-test prompt-bool overwrite false go tool chezmoi internal-test prompt-choice color red,green,blue go tool chezmoi internal-test prompt-choice season spring,summer,fall,winter summer go tool chezmoi internal-test prompt-int count go tool chezmoi internal-test prompt-int retries 3 go tool chezmoi internal-test prompt-multichoice directions north,east,south,west go tool chezmoi internal-test prompt-multichoice days mon,tue,wed,thu,fri,sat,sun sat,sun go tool chezmoi internal-test prompt-string name go tool chezmoi internal-test prompt-string username root go tool chezmoi internal-test read-password ================================================ FILE: internal/chezmoierrors/chezmoierrors.go ================================================ // Package chezmoierrors contains convenience functions for combining multiple // errors. package chezmoierrors import "errors" // Combine combines all non-nil errors in errs into one. If there are no non-nil // errors, it returns nil. If there is exactly one non-nil error then it returns // that error. Otherwise, it returns the non-nil errors combined with // [errors.Join]. func Combine(errs ...error) error { nonNilErrs := make([]error, 0, len(errs)) for _, err := range errs { if err != nil { nonNilErrs = append(nonNilErrs, err) } } switch len(nonNilErrs) { case 0: return nil case 1: return nonNilErrs[0] default: return errors.Join(nonNilErrs...) } } // CombineFunc combines the error pointed to by errp with the result of calling // f. func CombineFunc(errp *error, f func() error) { //nolint:gocritic if err := f(); err != nil { *errp = Combine(*errp, err) } } ================================================ FILE: internal/chezmoigit/chezmoigit.go ================================================ // Package chezmoigit contains functions for interacting with git. package chezmoigit ================================================ FILE: internal/chezmoigit/status.go ================================================ package chezmoigit import ( "regexp" "strconv" "strings" ) // A ParseError is a parse error. type ParseError string // An OrdinaryStatus is a status of a modified file. type OrdinaryStatus struct { X byte Y byte Sub string MH int64 MI int64 MW int64 HH string HI string Path string } // A RenamedOrCopiedStatus is a status of a renamed or copied file. type RenamedOrCopiedStatus struct { X byte Y byte Sub string MH int64 MI int64 MW int64 HH string HI string RC byte Score int64 Path string OrigPath string } // An UnmergedStatus is the status of an unmerged file. type UnmergedStatus struct { X byte Y byte Sub string M1 int64 M2 int64 M3 int64 MW int64 H1 string H2 string H3 string Path string } // An UntrackedStatus is a status of an untracked file. type UntrackedStatus struct { Path string } // An IgnoredStatus is a status of an ignored file. type IgnoredStatus struct { Path string } // A Status is a status. type Status struct { Ordinary []OrdinaryStatus RenamedOrCopied []RenamedOrCopiedStatus Unmerged []UnmergedStatus Untracked []UntrackedStatus Ignored []IgnoredStatus } var ( statusPorcelainV2ZOrdinaryRx = regexp.MustCompile(`` + `^1 ` + `([!\.\?ACDMRU])([!\.\?ACDMRU]) ` + `(N\.\.\.|S[\.C][\.M][\.U]) ` + `([0-7]+) ` + `([0-7]+) ` + `([0-7]+) ` + `([0-9a-f]+) ` + `([0-9a-f]+) ` + `(.*)` + `$`, ) statusPorcelainV2ZRenamedOrCopiedRx = regexp.MustCompile(`` + `^2 ` + `([!\.\?ACDMRU])([!\.\?ACDMRU]) ` + `(N\.\.\.|S[\.C][\.M][\.U]) ` + `([0-7]+) ` + `([0-7]+) ` + `([0-7]+) ` + `([0-9a-f]+) ` + `([0-9a-f]+) ` + `([CR])([0-9]+) ` + `(.*?)\t(.*)` + `$`, ) statusPorcelainV2ZUnmergedRx = regexp.MustCompile(`` + `^u ` + `([!\.\?ACDMRU])([!\.\?ACDMRU]) ` + `(N\.\.\.|S[\.C][\.M][\.U]) ` + `([0-7]+) ` + `([0-7]+) ` + `([0-7]+) ` + `([0-7]+) ` + `([0-9a-f]+) ` + `([0-9a-f]+) ` + `([0-9a-f]+) ` + `(.*)` + `$`, ) statusPorcelainV2ZUntrackedRx = regexp.MustCompile(`` + `^\? ` + `(.*)` + `$`, ) statusPorcelainV2ZIgnoredRx = regexp.MustCompile(`` + `^! ` + `(.*)` + `$`, ) ) func (e ParseError) Error() string { return string(e) + ": parse error" } // ParseStatusPorcelainV2 parses the output of // // git status --ignored --porcelain=v2 // // See https://git-scm.com/docs/git-status. func ParseStatusPorcelainV2(output []byte) (*Status, error) { var status Status for line := range strings.Lines(string(output)) { line = strings.TrimSpace(line) if line == "" { continue } switch line[0] { case '1': m := statusPorcelainV2ZOrdinaryRx.FindStringSubmatchIndex(line) if m == nil { return nil, ParseError(line) } mH, err := strconv.ParseInt(line[m[8]:m[9]], 8, 64) if err != nil { return nil, err } mI, err := strconv.ParseInt(line[m[10]:m[11]], 8, 64) if err != nil { return nil, err } mW, err := strconv.ParseInt(line[m[12]:m[13]], 8, 64) if err != nil { return nil, err } os := OrdinaryStatus{ X: line[m[2]], Y: line[m[4]], Sub: line[m[6]:m[7]], MH: mH, MI: mI, MW: mW, HH: line[m[14]:m[15]], HI: line[m[16]:m[17]], Path: line[m[18]:m[19]], } status.Ordinary = append(status.Ordinary, os) case '2': m := statusPorcelainV2ZRenamedOrCopiedRx.FindStringSubmatchIndex(line) if m == nil { return nil, ParseError(line) } mH, err := strconv.ParseInt(line[m[8]:m[9]], 8, 64) if err != nil { return nil, err } mI, err := strconv.ParseInt(line[m[10]:m[11]], 8, 64) if err != nil { return nil, err } mW, err := strconv.ParseInt(line[m[12]:m[13]], 8, 64) if err != nil { return nil, err } score, err := strconv.ParseInt(line[m[20]:m[21]], 10, 64) if err != nil { return nil, err } rocs := RenamedOrCopiedStatus{ X: line[m[2]], Y: line[m[4]], Sub: line[m[6]:m[7]], MH: mH, MI: mI, MW: mW, HH: line[m[14]:m[15]], HI: line[m[16]:m[17]], RC: line[m[18]], Score: score, Path: line[m[22]:m[23]], OrigPath: line[m[24]:m[25]], } status.RenamedOrCopied = append(status.RenamedOrCopied, rocs) case 'u': m := statusPorcelainV2ZUnmergedRx.FindStringSubmatchIndex(line) if m == nil { return nil, ParseError(line) } m1, err := strconv.ParseInt(line[m[8]:m[9]], 8, 64) if err != nil { return nil, err } m2, err := strconv.ParseInt(line[m[10]:m[11]], 8, 64) if err != nil { return nil, err } m3, err := strconv.ParseInt(line[m[12]:m[13]], 8, 64) if err != nil { return nil, err } mW, err := strconv.ParseInt(line[m[14]:m[15]], 8, 64) if err != nil { return nil, err } us := UnmergedStatus{ X: line[m[2]], Y: line[m[4]], Sub: line[m[6]:m[7]], M1: m1, M2: m2, M3: m3, MW: mW, H1: line[m[16]:m[17]], H2: line[m[18]:m[19]], H3: line[m[20]:m[21]], Path: line[m[22]:m[23]], } status.Unmerged = append(status.Unmerged, us) case '?': m := statusPorcelainV2ZUntrackedRx.FindStringSubmatchIndex(line) if m == nil { return nil, ParseError(line) } us := UntrackedStatus{ Path: line[m[2]:m[3]], } status.Untracked = append(status.Untracked, us) case '!': m := statusPorcelainV2ZIgnoredRx.FindStringSubmatchIndex(line) if m == nil { return nil, ParseError(line) } us := IgnoredStatus{ Path: line[m[2]:m[3]], } status.Ignored = append(status.Ignored, us) case '#': continue default: return nil, ParseError(line) } } return &status, nil } // IsEmpty returns true if s is empty. func (s *Status) IsEmpty() bool { switch { case s == nil: return true case len(s.Ignored) != 0: return false case len(s.Ordinary) != 0: return false case len(s.RenamedOrCopied) != 0: return false case len(s.Unmerged) != 0: return false case len(s.Untracked) != 0: return false default: return true } } ================================================ FILE: internal/chezmoigit/status_test.go ================================================ package chezmoigit import ( "testing" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestParseStatusPorcelainV2(t *testing.T) { for _, tc := range []struct { name string outputStr string expectedEmpty bool expectedStatus *Status }{ { name: "empty", outputStr: "", expectedStatus: &Status{}, }, { name: "added", outputStr: "1 A. N... 000000 100644 100644 0000000000000000000000000000000000000000 cea5c3500651a923bacd80f960dd20f04f71d509 main.go\n", expectedStatus: &Status{ Ordinary: []OrdinaryStatus{ { X: 'A', Y: '.', Sub: "N...", MH: 0, MI: 0o100644, MW: 0o100644, HH: "0000000000000000000000000000000000000000", HI: "cea5c3500651a923bacd80f960dd20f04f71d509", Path: "main.go", }, }, }, }, { name: "removed", outputStr: "1 D. N... 100644 000000 000000 cea5c3500651a923bacd80f960dd20f04f71d509 0000000000000000000000000000000000000000 main.go\n", expectedStatus: &Status{ Ordinary: []OrdinaryStatus{ { X: 'D', Y: '.', Sub: "N...", MH: 0o100644, MI: 0, MW: 0, HH: "cea5c3500651a923bacd80f960dd20f04f71d509", HI: "0000000000000000000000000000000000000000", Path: "main.go", }, }, }, }, { name: "copied", outputStr: chezmoitest.JoinLines( "2 C. N... 100644 100644 100644 4a58007052a65fbc2fc3f910f2855f45a4058e74 4a58007052a65fbc2fc3f910f2855f45a4058e74 C100 c\tb", "2 R. N... 100644 100644 100644 4a58007052a65fbc2fc3f910f2855f45a4058e74 4a58007052a65fbc2fc3f910f2855f45a4058e74 R100 d\tb", ), expectedStatus: &Status{ RenamedOrCopied: []RenamedOrCopiedStatus{ { X: 'C', Y: '.', Sub: "N...", MH: 0o100644, MI: 0o100644, MW: 0o100644, HH: "4a58007052a65fbc2fc3f910f2855f45a4058e74", HI: "4a58007052a65fbc2fc3f910f2855f45a4058e74", RC: 'C', Score: 100, Path: "c", OrigPath: "b", }, { X: 'R', Y: '.', Sub: "N...", MH: 0o100644, MI: 0o100644, MW: 0o100644, HH: "4a58007052a65fbc2fc3f910f2855f45a4058e74", HI: "4a58007052a65fbc2fc3f910f2855f45a4058e74", RC: 'R', Score: 100, Path: "d", OrigPath: "b", }, }, }, }, { name: "update", outputStr: "1 .M N... 100644 100644 100644 353dbbb3c29a80fb44d4e26dac111739d25294db 353dbbb3c29a80fb44d4e26dac111739d25294db cmd/git.go\n", expectedStatus: &Status{ Ordinary: []OrdinaryStatus{ { X: '.', Y: 'M', Sub: "N...", MH: 0o100644, MI: 0o100644, MW: 0o100644, HH: "353dbbb3c29a80fb44d4e26dac111739d25294db", HI: "353dbbb3c29a80fb44d4e26dac111739d25294db", Path: "cmd/git.go", }, }, }, }, { name: "renamed", outputStr: "2 R. N... 100644 100644 100644 9d06c86ecba40e1c695e69b55a40843df6a79cef 9d06c86ecba40e1c695e69b55a40843df6a79cef R100 chezmoi_rename.go\tchezmoi.go\n", expectedStatus: &Status{ RenamedOrCopied: []RenamedOrCopiedStatus{ { X: 'R', Y: '.', Sub: "N...", MH: 0o100644, MI: 0o100644, MW: 0o100644, HH: "9d06c86ecba40e1c695e69b55a40843df6a79cef", HI: "9d06c86ecba40e1c695e69b55a40843df6a79cef", RC: 'R', Score: 100, Path: "chezmoi_rename.go", OrigPath: "chezmoi.go", }, }, }, }, { name: "renamed_2", outputStr: "2 R. N... 100644 100644 100644 ddbd961d7e4db2bb6615a9e8ce86364fa65e732d ddbd961d7e4db2bb6615a9e8ce86364fa65e732d R100 dot_config/chezmoi/private_chezmoi.toml\tdot_config/chezmoi/chezmoi.toml", //nolint:dupword expectedStatus: &Status{ RenamedOrCopied: []RenamedOrCopiedStatus{ { X: 82, Y: 46, Sub: "N...", MH: 0o100644, MI: 0o100644, MW: 0o100644, HH: "ddbd961d7e4db2bb6615a9e8ce86364fa65e732d", HI: "ddbd961d7e4db2bb6615a9e8ce86364fa65e732d", RC: 'R', Score: 100, Path: "dot_config/chezmoi/private_chezmoi.toml", OrigPath: "dot_config/chezmoi/chezmoi.toml", }, }, }, }, { name: "modified_2", outputStr: "1 .M N... 100644 100644 100644 5716ca5987cbf97d6bb54920bea6adde242d87e6 5716ca5987cbf97d6bb54920bea6adde242d87e6 foo\n", expectedStatus: &Status{ Ordinary: []OrdinaryStatus{ { X: '.', Y: 'M', Sub: "N...", MH: 0o100644, MI: 0o100644, MW: 0o100644, HH: "5716ca5987cbf97d6bb54920bea6adde242d87e6", HI: "5716ca5987cbf97d6bb54920bea6adde242d87e6", Path: "foo", }, }, }, }, { name: "unmerged", outputStr: "u UU N... 100644 100644 100644 100644 78981922613b2afb6025042ff6bd878ac1994e85 0f7bc766052a5a0ee28a393d51d2370f96d8ceb8 422c2b7ab3b3c668038da977e4e93a5fc623169c README.md\n", expectedStatus: &Status{ Unmerged: []UnmergedStatus{ { X: 'U', Y: 'U', Sub: "N...", M1: 0o100644, M2: 0o100644, M3: 0o100644, MW: 0o100644, H1: "78981922613b2afb6025042ff6bd878ac1994e85", H2: "0f7bc766052a5a0ee28a393d51d2370f96d8ceb8", H3: "422c2b7ab3b3c668038da977e4e93a5fc623169c", Path: "README.md", }, }, }, }, { name: "ordinary", outputStr: "1 .M N... 100644 100644 100644 54276930d8aaf52fffff3c90e4aaae21beb3c7d5 54276930d8aaf52fffff3c90e4aaae21beb3c7d5 .chezmoi.toml.work.tmpl", expectedStatus: &Status{ Ordinary: []OrdinaryStatus{ { X: '.', Y: 'M', Sub: "N...", MH: 0o100644, MI: 0o100644, MW: 0o100644, HH: "54276930d8aaf52fffff3c90e4aaae21beb3c7d5", HI: "54276930d8aaf52fffff3c90e4aaae21beb3c7d5", Path: ".chezmoi.toml.work.tmpl", }, }, }, }, { name: "untracked", outputStr: "? chezmoi.go\n", expectedStatus: &Status{ Untracked: []UntrackedStatus{ { Path: "chezmoi.go", }, }, }, }, { name: "ignored", outputStr: "! chezmoi.go\n", expectedStatus: &Status{ Ignored: []IgnoredStatus{ { Path: "chezmoi.go", }, }, }, }, } { t.Run(tc.name, func(t *testing.T) { actualStatus, err := ParseStatusPorcelainV2([]byte(tc.outputStr)) assert.NoError(t, err) assert.Equal(t, tc.expectedStatus, actualStatus) }) } } ================================================ FILE: internal/chezmoilog/chezmoilog.go ================================================ // Package chezmoilog contains support for chezmoi logging. package chezmoilog import ( "context" "errors" "fmt" "log/slog" "net/http" "os" "os/exec" "slices" "time" ) const few = 64 // An OSExecCmdLogValuer wraps an [*os/exec.Cmd] and adds [log/slog.LogValuer] // functionality. type OSExecCmdLogValuer struct { *exec.Cmd } // An OSExecExitLogValuerError wraps an [*os/exec.ExitError] and adds // [log/slog.LogValuer]. type OSExecExitLogValuerError struct { *exec.ExitError } // An OSProcessStateLogValuer wraps an [*os.ProcessState] and adds // [log/slog.LogValuer] functionality. type OSProcessStateLogValuer struct { *os.ProcessState } // LogValuer implements log/slog.LogValuer.LogValue. func (cmd OSExecCmdLogValuer) LogValuer() slog.Value { var attrs []slog.Attr if cmd.Path != "" { attrs = append(attrs, slog.String("path", cmd.Path)) } if len(cmd.Args) != 0 { attrs = append(attrs, slog.Any("args", cmd.Args)) } if cmd.Dir != "" { attrs = append(attrs, slog.String("dir", cmd.Dir)) } if len(cmd.Env) != 0 { attrs = append(attrs, slog.Any("env", cmd.Env)) } return slog.GroupValue(attrs...) } // LogValuer implements log/slog.LogValuer.LogValue. func (err OSExecExitLogValuerError) LogValuer() slog.Value { attrs := []slog.Attr{ slog.Any("processState", OSProcessStateLogValuer{err.ProcessState}), } if osExecExitError := (&exec.ExitError{}); errors.As(err, &osExecExitError) { attrs = append(attrs, Bytes("stderr", err.Stderr)) } return slog.GroupValue(attrs...) } // LogValue implements log/slog.LogValuer.LogValue. func (p OSProcessStateLogValuer) LogValue() slog.Value { var attrs []slog.Attr if p.ProcessState != nil { if p.Exited() { if !p.Success() { attrs = append(attrs, slog.Int("exitCode", p.ExitCode())) } } else { attrs = append(attrs, slog.Int("pid", p.Pid())) } if userTime := p.UserTime(); userTime != 0 { attrs = append(attrs, slog.Duration("userTime", userTime)) } if systemTime := p.SystemTime(); systemTime != 0 { attrs = append(attrs, slog.Duration("systemTime", systemTime)) } } return slog.GroupValue(attrs...) } func AppendExitErrorAttrs(attrs []slog.Attr, err error) []slog.Attr { var execExitError *exec.ExitError if !errors.As(err, &execExitError) { return append(attrs, slog.Any("err", err)) } if execExitError.ProcessState != nil { if execExitError.Exited() { attrs = append(attrs, slog.Int("exitCode", execExitError.ExitCode())) } else { attrs = append(attrs, slog.Int("pid", execExitError.Pid())) } if userTime := execExitError.UserTime(); userTime != 0 { attrs = append(attrs, slog.Duration("userTime", userTime)) } if systemTime := execExitError.SystemTime(); systemTime != 0 { attrs = append(attrs, slog.Duration("systemTime", systemTime)) } } return attrs } // Bytes returns an [slog.Attr] with the value data. func Bytes(key string, data []byte) slog.Attr { return slog.String(key, string(data)) } // FirstFewBytes returns an [slog.Attr] with the value of the first few bytes of // data. func FirstFewBytes(key string, data []byte) slog.Attr { return slog.String(key, string(firstFewBytesHelper(data))) } // LogHTTPRequest calls httpClient.Do, logs the result to logger, and returns // the result. func LogHTTPRequest(ctx context.Context, logger *slog.Logger, client *http.Client, req *http.Request) (*http.Response, error) { start := time.Now() resp, err := client.Do(req) attrs := []slog.Attr{ slog.Duration("duration", time.Since(start)), slog.String("method", req.Method), Stringer("url", req.URL), } if resp != nil { attrs = append(attrs, slog.Int("statusCode", resp.StatusCode), slog.String("status", resp.Status), slog.Int("contentLength", int(resp.ContentLength)), ) } InfoOrErrorContext(ctx, logger, "HTTPRequest", err, attrs...) return resp, err } // LogCmdCombinedOutput calls cmd.CombinedOutput, logs the result, and returns // the result. func LogCmdCombinedOutput(logger *slog.Logger, cmd *exec.Cmd) ([]byte, error) { start := time.Now() combinedOutput, err := cmd.CombinedOutput() attrs := []slog.Attr{ slog.Any("cmd", OSExecCmdLogValuer{Cmd: cmd}), slog.Duration("duration", time.Since(start)), slog.Int("size", len(combinedOutput)), slog.Any("combinedOutput", firstFewBytesHelper(combinedOutput)), } attrs = AppendExitErrorAttrs(attrs, err) InfoOrErrorContext(context.Background(), logger, "Output", err, attrs...) return combinedOutput, err } // LogCmdOutput calls cmd.Output, logs the result, and returns the result. func LogCmdOutput(logger *slog.Logger, cmd *exec.Cmd) ([]byte, error) { start := time.Now() output, err := cmd.Output() attrs := []slog.Attr{ slog.Any("cmd", OSExecCmdLogValuer{Cmd: cmd}), slog.Duration("duration", time.Since(start)), slog.Int("size", len(output)), slog.Any("output", firstFewBytesHelper(output)), } attrs = AppendExitErrorAttrs(attrs, err) InfoOrErrorContext(context.Background(), logger, "Output", err, attrs...) return output, err } // LogCmdRun calls cmd.Run, logs the result, and returns the result. func LogCmdRun(logger *slog.Logger, cmd *exec.Cmd) error { start := time.Now() err := cmd.Run() attrs := []slog.Attr{ slog.Any("cmd", OSExecCmdLogValuer{Cmd: cmd}), slog.Duration("duration", time.Since(start)), } attrs = AppendExitErrorAttrs(attrs, err) InfoOrErrorContext(context.Background(), logger, "Run", err, attrs...) return err } // LogCmdStart calls cmd.Start, logs the result, and returns the result. func LogCmdStart(logger *slog.Logger, cmd *exec.Cmd) error { start := time.Now() err := cmd.Start() attrs := []slog.Attr{ slog.Any("cmd", OSExecCmdLogValuer{Cmd: cmd}), slog.Time("start", start), } attrs = AppendExitErrorAttrs(attrs, err) InfoOrErrorContext(context.Background(), logger, "Start", err, attrs...) return err } // LogCmdWait calls cmd.Wait, logs the result, and returns the result. func LogCmdWait(logger *slog.Logger, cmd *exec.Cmd) error { err := cmd.Wait() end := time.Now() attrs := []slog.Attr{ slog.Any("cmd", OSExecCmdLogValuer{Cmd: cmd}), slog.Time("end", end), } attrs = AppendExitErrorAttrs(attrs, err) InfoOrError(logger, "Wait", err, attrs...) return err } func InfoOrError(logger *slog.Logger, msg string, err error, attrs ...slog.Attr) { InfoOrErrorContext(context.Background(), logger, msg, err, attrs...) } func InfoOrErrorContext(ctx context.Context, logger *slog.Logger, msg string, err error, attrs ...slog.Attr) { if logger == nil { return } args := make([]any, 0, len(attrs)+1) if err != nil { args = append(args, slog.Any("err", err)) } for _, attr := range attrs { args = append(args, attr) } level := slog.LevelInfo if err != nil { level = slog.LevelError } logger.Log(ctx, level, msg, args...) } // Stringer returns an [slog.Attr] with value. func Stringer(key string, value fmt.Stringer) slog.Attr { return slog.String(key, value.String()) } // firstFewBytesHelper returns the first few bytes of data. func firstFewBytesHelper(data []byte) []byte { if len(data) > few { data = slices.Clone(data[:few]) data = append(data, '.', '.', '.') } return data } ================================================ FILE: internal/chezmoilog/nullhandler.go ================================================ package chezmoilog import ( "context" "log/slog" ) // A NullHandler implements [log/slog.Handler] and drops all output. type NullHandler struct{} func (NullHandler) Enabled(context.Context, slog.Level) bool { return false } func (NullHandler) Handle(context.Context, slog.Record) error { return nil } func (h NullHandler) WithAttrs([]slog.Attr) slog.Handler { return h } func (h NullHandler) WithGroup(string) slog.Handler { return h } ================================================ FILE: internal/chezmoilog/nullhandler_test.go ================================================ package chezmoilog import "log/slog" var _ slog.Handler = NullHandler{} ================================================ FILE: internal/chezmoiset/chezmoiset.go ================================================ // Package chezmoiset implements a generic set type. package chezmoiset import ( "iter" "maps" ) // A Set is a set of elements. type Set[T comparable] map[T]struct{} // New returns a new set containing elements. func New[T comparable](elements ...T) Set[T] { s := make(Set[T], len(elements)) s.Add(elements...) return s } // NewWithCapacity returns a new empty set with the given capacity. func NewWithCapacity[T comparable](capacity int) Set[T] { return make(Set[T], capacity) } // Add adds elements to s. func (s Set[T]) Add(elements ...T) { for _, element := range elements { s[element] = struct{}{} } } // AddSet adds all elements from other to s. func (s Set[T]) AddSet(other Set[T]) { for element := range other { s[element] = struct{}{} } } // AnyElement returns an arbitrary element from s. It is typically used when s // is known to contain exactly one element. func (s Set[T]) AnyElement() T { for element := range s { return element } var zero T return zero } // Contains returns true if s contains element. func (s Set[T]) Contains(element T) bool { _, ok := s[element] return ok } // Elements returns all the elements of s. func (s Set[T]) Elements() iter.Seq[T] { return maps.Keys(s) } // IsEmpty returns if s is empty. func (s Set[T]) IsEmpty() bool { return len(s) == 0 } // Remove removes elements from s. func (s Set[T]) Remove(elements ...T) { for _, element := range elements { delete(s, element) } } ================================================ FILE: internal/chezmoitest/chezmoitest.go ================================================ // Package chezmoitest contains test helper functions for chezmoi. package chezmoitest import ( "io/fs" "log/slog" "os" "os/exec" "runtime" "strconv" "strings" "testing" "filippo.io/age" "github.com/alecthomas/assert/v2" "github.com/google/renameio/v2/maybe" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" "chezmoi.io/chezmoi/internal/chezmoilog" ) // AgeGenerateKey generates an identity in identityFile and returns the // recipient. func AgeGenerateKey(command, identityFile string) (*age.X25519Recipient, error) { identity, err := age.GenerateX25519Identity() if err != nil { return nil, err } if err := maybe.WriteFile(identityFile, []byte(identity.String()), 0o600); err != nil { return nil, err } return identity.Recipient(), nil } // GPGGenerateKey generates GPG key in homeDir and returns the key and the // passphrase. func GPGGenerateKey(command, homeDir string) (key, passphrase string, err error) { key = "chezmoi-test-gpg-key" passphrase = "chezmoi-test-gpg-passphrase" cmd := exec.Command( command, "--batch", "--homedir", homeDir, "--no-tty", "--passphrase", passphrase, "--pinentry-mode", "loopback", "--quick-generate-key", key, ) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = chezmoilog.LogCmdRun(slog.Default(), cmd) return key, passphrase, err } // HomeDir returns the home directory. func HomeDir() string { switch runtime.GOOS { case "windows": return "C:/home/user" default: return "/home/user" } } // JoinLines joins lines with newlines. func JoinLines(lines ...string) string { if len(lines) == 0 { return "" } return strings.Join(lines, "\n") + "\n" } // SkipUnlessGOOS calls t.Skip() if name does not match [runtime.GOOS]. func SkipUnlessGOOS(t *testing.T, name string) { t.Helper() switch { case strings.HasSuffix(name, "_windows") && runtime.GOOS != "windows": t.Skip("skipping Windows test on UNIX") case strings.HasSuffix(name, "_unix") && runtime.GOOS == "windows": t.Skip("skipping UNIX test on Windows") } } // WithTestFS calls f with a test filesystem populated with root. func WithTestFS(t *testing.T, root any, f func(vfs.FS)) { t.Helper() fileSystem, cleanup, err := vfst.NewTestFS(root, vfst.BuilderUmask(Umask)) assert.NoError(t, err) t.Cleanup(cleanup) f(fileSystem) } // mustParseFileMode parses s as a [fs.FileMode] and panics on any error. func mustParseFileMode(s string) fs.FileMode { u, err := strconv.ParseUint(s, 0, 32) if err != nil { panic(err) } return fs.FileMode(uint32(u)) } ================================================ FILE: internal/chezmoitest/chezmoitest_test.go ================================================ package chezmoitest import ( "strconv" "testing" "github.com/alecthomas/assert/v2" ) func TestJoinLines(t *testing.T) { for i, tc := range []struct { lines []string expected string }{ { lines: nil, expected: "", }, { lines: []string{""}, expected: "\n", }, { lines: []string{"a"}, expected: "a\n", }, { lines: []string{"a", "b"}, expected: "a\nb\n", }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { assert.Equal(t, tc.expected, JoinLines(tc.lines...)) }) } } ================================================ FILE: internal/chezmoitest/chezmoitest_unix.go ================================================ //go:build unix package chezmoitest import ( "golang.org/x/sys/unix" ) var ( // umaskStr is the umask used in tests represented as a string so it can be // set with the // -ldflags="-X chezmoi.io/chezmoi/internal/chezmoitest.umaskStr=..." // option to go build and go test. umaskStr = "0o022" // Umask is the umask used in tests. // // If you change this then you will need to update the testscripts in // testdata/scripts where permissions after applying umask are hardcoded as // strings. Pure Go tests should use this value to ensure that they pass, // irrespective of what it is set to. Be aware that the process's umask is a // process-level property and cannot be locally changed within individual // tests. Umask = mustParseFileMode(umaskStr) ) func init() { unix.Umask(int(Umask)) } ================================================ FILE: internal/chezmoitest/chezmoitest_windows.go ================================================ package chezmoitest var ( // umaskStr is the umask used in tests represented as a string so it can be // set with the // -ldflags="-X chezmoi.io/chezmoi/internal/chezmoitest.umaskStr=..." // option to go build and go test. umaskStr = "0" // Umask is the umask used in tests. // // On Windows, Umask is zero as Windows does not use POSIX-style permissions. Umask = mustParseFileMode(umaskStr) ) ================================================ FILE: internal/cmd/addcmd.go ================================================ package cmd import ( "errors" "fmt" "io/fs" "maps" "os" "slices" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoiset" ) const ( severityIgnore = "ignore" severityWarning = "warning" severityError = "error" ) var severityValues = []string{ severityIgnore, severityWarning, severityError, } type addCmdConfig struct { Encrypt bool `json:"encrypt" mapstructure:"encrypt" yaml:"encrypt"` Secrets *choiceFlag `json:"secrets" mapstructure:"secrets" yaml:"secrets"` TemplateSymlinks bool `json:"templateSymlinks" mapstructure:"templateSymlinks" yaml:"templateSymlinks"` autoTemplate bool create bool exact bool filter *chezmoi.EntryTypeFilter follow bool new bool prompt bool quiet bool recursive bool template bool } func (c *Config) newAddCmd() *cobra.Command { addCmd := &cobra.Command{ GroupID: groupIDDaily, Use: "add targets...", Aliases: []string{"manage"}, Short: "Add an existing file, directory, or symlink to the source state", Long: mustLongHelp("add"), Example: example("add"), Args: cobra.MinimumNArgs(1), RunE: c.makeRunEWithSourceState(c.runAddCmd), Annotations: newAnnotations( createSourceDirectoryIfNeeded, modifiesSourceDirectory, persistentStateModeReadWrite, requiresWorkingTree, ), } addCmd.Flags(). BoolVarP(&c.Add.autoTemplate, "autotemplate", "a", c.Add.autoTemplate, "Generate the template when adding files as templates") addCmd.Flags().BoolVar(&c.Add.create, "create", c.Add.create, "Add files that should exist, irrespective of their contents") addCmd.Flags().BoolVar(&c.Add.Encrypt, "encrypt", c.Add.Encrypt, "Encrypt files") addCmd.Flags().BoolVar(&c.Add.exact, "exact", c.Add.exact, "Add directories exactly") addCmd.Flags().VarP(c.Add.filter.Exclude, "exclude", "x", "Exclude entry types") addCmd.Flags().BoolVarP(&c.Add.follow, "follow", "f", c.Add.follow, "Add symlink targets instead of symlinks") addCmd.Flags().VarP(c.Add.filter.Include, "include", "i", "Include entry types") addCmd.Flags().BoolVar(&c.Add.new, "new", c.Add.new, "Create new file if target does not exist") addCmd.Flags().BoolVarP(&c.Add.prompt, "prompt", "p", c.Add.prompt, "Prompt before adding each entry") addCmd.Flags().BoolVarP(&c.Add.quiet, "quiet", "q", c.Add.quiet, "Suppress warnings") addCmd.Flags().BoolVarP(&c.Add.recursive, "recursive", "r", c.Add.recursive, "Recurse into subdirectories") addCmd.Flags().Var(c.Add.Secrets, "secrets", "Scan for secrets when adding unencrypted files") must(addCmd.RegisterFlagCompletionFunc("secrets", c.Add.Secrets.FlagCompletionFunc())) addCmd.Flags().BoolVarP(&c.Add.template, "template", "T", c.Add.template, "Add files as templates") addCmd.Flags(). BoolVar(&c.Add.TemplateSymlinks, "template-symlinks", c.Add.TemplateSymlinks, "Add symlinks with target in source or home dirs as templates") return addCmd } func (c *Config) defaultOnIgnoreFunc(targetRelPath chezmoi.RelPath) { if !c.Add.quiet { c.errorf("warning: ignoring %s\n", targetRelPath) } } func (c *Config) defaultPreAddFunc(targetRelPath chezmoi.RelPath, fileInfo fs.FileInfo) error { // Scan unencrypted files for secrets, if configured. if c.Add.Secrets.String() != severityIgnore && fileInfo.Mode().Type() == 0 && !c.Add.Encrypt { absPath := c.DestDirAbsPath.Join(targetRelPath) content, err := c.destSystem.ReadFile(absPath) if err != nil { return err } gitleaksDetector, err := c.getGitleaksDetector() if err != nil { return err } findings := gitleaksDetector.DetectBytes(content) for _, finding := range findings { c.errorf("%s:%d: %s\n", absPath, finding.StartLine+1, finding.Description) } if !c.force && c.Add.Secrets.String() == severityError && len(findings) > 0 { return chezmoi.ExitCodeError(1) } } if !c.Add.prompt { return nil } prompt := fmt.Sprintf("add %s", c.SourceDirAbsPath.Join(targetRelPath)) for { switch choice, err := c.promptChoice(prompt, choicesYesNoAllQuit); { case err != nil: return err case choice == "all": c.Add.prompt = false return nil case choice == "no": return fs.SkipDir case choice == "quit": return chezmoi.ExitCodeError(0) case choice == "yes": return nil default: panic(choice + ": unexpected choice") } } } // defaultReplaceFunc prompts the user for confirmation if the adding the entry // would remove any of the encrypted, private, or template attributes. func (c *Config) defaultReplaceFunc( targetRelPath chezmoi.RelPath, newSourceStateEntry, oldSourceStateEntry chezmoi.SourceStateEntry, ) error { if c.force { return nil } newFile, newIsFile := newSourceStateEntry.(*chezmoi.SourceStateFile) oldFile, oldIsFile := oldSourceStateEntry.(*chezmoi.SourceStateFile) if !newIsFile || !oldIsFile { return nil } var removedAttributes []string if !newFile.Attr().Encrypted && oldFile.Attr().Encrypted { removedAttributes = append(removedAttributes, "encrypted") } if !newFile.Attr().Private && oldFile.Attr().Private { removedAttributes = append(removedAttributes, "private") } if !newFile.Attr().Template && oldFile.Attr().Template { removedAttributes = append(removedAttributes, "template") } if len(removedAttributes) == 0 { return nil } removedAttributesStr := englishListWithNoun(removedAttributes, "attribute", "") prompt := fmt.Sprintf("adding %s would remove %s, continue", targetRelPath, removedAttributesStr) for { switch choice, err := c.promptChoice(prompt, choicesYesNoAllQuit); { case err != nil: return err case choice == "all": c.force = true return nil case choice == "no": return fs.SkipDir case choice == "quit": return chezmoi.ExitCodeError(0) case choice == "yes": return nil default: panic(choice + ": unexpected choice") } } } func (c *Config) runAddCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { switch severity := c.Add.Secrets.String(); severity { case severityIgnore: case severityWarning: case severityError: default: return fmt.Errorf("%s: invalid severity", severity) } onNotExist := onNotExistError if c.Add.new { onNotExist = onNotExistAdd } destAbsPathInfos, err := c.destAbsPathInfos(sourceState, args, destAbsPathInfosOptions{ follow: c.Mode == chezmoi.ModeSymlink || c.Add.follow, onIgnoreFunc: c.defaultOnIgnoreFunc, onNotExist: onNotExist, recursive: c.Add.recursive, }) if err != nil { return err } if c.Add.follow && c.Add.recursive { for _, absPath := range slices.Sorted(maps.Keys(destAbsPathInfos)) { if destAbsPathInfo := destAbsPathInfos[absPath]; destAbsPathInfo.IsDir() { return errors.New(absPath.String() + ": follow and recursive are mutually exclusive for directories") } } } persistentStateFileAbsPath, err := c.persistentStateFile() if err != nil { return err } executable, err := os.Executable() if err != nil { return err } configFileAbsPath, err := c.getConfigFileAbsPath() if err != nil { return err } // Build exact target paths set from the original args when --exact is used. // This ensures only explicitly specified paths (and their descendants) get // the exact_ prefix, not implicitly-added parent directories. var exactTargetRelPaths chezmoiset.Set[chezmoi.RelPath] if c.Add.exact { exactTargetRelPaths = chezmoiset.New[chezmoi.RelPath]() for _, arg := range args { destAbsPath, err := chezmoi.NormalizePath(arg) if err != nil { return err } targetRelPath, err := destAbsPath.TrimDirPrefix(c.DestDirAbsPath) if err != nil { return err } exactTargetRelPaths.Add(targetRelPath) } } return sourceState.Add( c.sourceSystem, c.persistentState, c.destSystem, destAbsPathInfos, &chezmoi.AddOptions{ AutoTemplate: c.Add.autoTemplate, Create: c.Add.create, Encrypt: c.Add.Encrypt, EncryptedSuffix: c.encryption.EncryptedSuffix(), Exact: c.Add.exact, ExactTargetRelPaths: exactTargetRelPaths, Errorf: c.errorf, Filter: c.Add.filter, OnIgnoreFunc: c.defaultOnIgnoreFunc, PreAddFunc: c.defaultPreAddFunc, ConfigFileAbsPath: configFileAbsPath, ProtectedAbsPaths: []chezmoi.AbsPath{ c.CacheDirAbsPath, c.WorkingTreeAbsPath, configFileAbsPath.Dir(), persistentStateFileAbsPath, c.sourceDirAbsPath, chezmoi.NewAbsPath(executable), }, ReplaceFunc: c.defaultReplaceFunc, Template: c.Add.template, TemplateSymlinks: c.Add.TemplateSymlinks, }, ) } ================================================ FILE: internal/cmd/addcmd_test.go ================================================ package cmd import ( "io/fs" "os" "runtime" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestAddCmd(t *testing.T) { for _, tc := range []struct { name string root any args []string tests []any }{ { name: "dir", root: map[string]any{ "/home/user": map[string]any{ ".dir": &vfst.Dir{Perm: fs.ModePerm}, }, }, args: []string{"~/.dir"}, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/.keep", vfst.TestContents(nil), vfst.TestModePerm(0o666&^chezmoitest.Umask), ), }, }, { name: "dir_with_file", root: map[string]any{ "/home/user": map[string]any{ ".dir": &vfst.Dir{ Perm: fs.ModePerm, Entries: map[string]any{ "file": "# contents of .dir/file\n", }, }, }, }, args: []string{"~/.dir"}, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/.keep", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .dir/file\n"), ), }, }, { name: "dir_with_file_with_--recursive=false", root: map[string]any{ "/home/user": map[string]any{ ".dir": &vfst.Dir{ Perm: fs.ModePerm, Entries: map[string]any{ "file": "# contents of .dir/file\n", }, }, }, }, args: []string{"~/.dir", "--recursive=false"}, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/.keep", vfst.TestContents(nil), vfst.TestModePerm(0o666&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/file", vfst.TestDoesNotExist(), ), }, }, { name: "dir_private_unix", root: map[string]any{ "/home/user": map[string]any{ ".dir": &vfst.Dir{ Perm: 0o700, Entries: map[string]any{ "file": "# contents of .dir/file\n", }, }, }, }, args: []string{"~/.dir"}, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/private_dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/private_dot_dir/.keep", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.local/share/chezmoi/private_dot_dir/file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .dir/file\n"), ), }, }, { name: "dir_file_private_unix", root: map[string]any{ "/home/user": map[string]any{ ".dir": &vfst.Dir{ Perm: 0o700, Entries: map[string]any{ "file": "# contents of .dir/file\n", }, }, }, }, args: []string{"~/.dir/file"}, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/private_dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/private_dot_dir/.keep", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.local/share/chezmoi/private_dot_dir/file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .dir/file\n"), ), }, }, { name: "empty", root: map[string]any{ "/home/user": map[string]any{ ".empty": "", }, }, args: []string{"~/.empty"}, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/empty_dot_empty", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContents(nil), ), }, }, { name: "executable_unix", root: map[string]any{ "/home/user": map[string]any{ ".executable": &vfst.File{ Perm: fs.ModePerm, Contents: []byte("#!/bin/sh\n"), }, }, }, args: []string{"~/.executable"}, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/executable_dot_executable", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("#!/bin/sh\n"), ), }, }, { name: "file", root: map[string]any{ "/home/user": map[string]any{ ".file": "# contents of .file\n", }, }, args: []string{"~/.file"}, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .file\n"), ), }, }, { name: "symlink", root: map[string]any{ "/home/user": map[string]any{ ".symlink": &vfst.Symlink{ Target: ".dir/subdir/file", }, }, }, args: []string{"~/.symlink"}, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/symlink_dot_symlink", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString(".dir/subdir/file\n"), ), }, }, { name: "symlink_with_--follow", root: map[string]any{ "/home/user": map[string]any{ ".file": "# contents of .file\n", ".symlink": &vfst.Symlink{ Target: ".file", }, }, }, args: []string{"--follow", "~/.symlink"}, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_symlink", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .file\n"), ), }, }, { name: "issue_3666", root: map[string]any{ "/home/user": map[string]any{ ".config/helix/themes/ayu_custom.toml": "# contents of ayu_custom.toml\n", ".local/share/chezmoi": map[string]any{ "dot_config/exact_helix": &vfst.Dir{Perm: 0o777 &^ chezmoitest.Umask}, }, }, }, args: []string{"~/.config/helix/themes/ayu_custom.toml"}, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_config/exact_helix/themes/ayu_custom.toml", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of ayu_custom.toml\n"), ), }, }, { name: "new", root: map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi": &vfst.Dir{Perm: 0o777 &^ chezmoitest.Umask}, }, }, args: []string{"--new", "~/.file"}, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/empty_dot_file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContents(nil), ), }, }, { name: "new_--recursive=false", root: map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi": &vfst.Dir{Perm: 0o777 &^ chezmoitest.Umask}, }, }, args: []string{"--new", "--recursive=false", "~/.file"}, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/empty_dot_file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContents(nil), ), }, }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.SkipUnlessGOOS(t, tc.name) chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { assert.NoError(t, newTestConfig(t, fileSystem).execute(append([]string{"add"}, tc.args...))) vfst.RunTests(t, fileSystem, "", tc.tests...) }) }) } } func TestAddCmdChmod(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("skipping UNIX test on Windows") } chezmoitest.WithTestFS(t, map[string]any{ "/home/user": map[string]any{ ".dir/subdir/file": "# contents of .dir/subdir/file\n", }, }, func(fileSystem vfs.FS) { assert.NoError(t, newTestConfig(t, fileSystem).execute([]string{"add", "/home/user/.dir"})) assert.NoError(t, fileSystem.Chmod("/home/user/.dir/subdir", 0o700)) assert.NoError(t, newTestConfig(t, fileSystem).execute([]string{"add", "--force", "/home/user/.dir"})) }) } func TestAddCmdSecretsError(t *testing.T) { chezmoitest.WithTestFS(t, map[string]any{ "/home/user": map[string]any{ ".secret": "AWS_ACCESS_KEY_ID=AKIALALEMEL33243OLIA\n", }, }, func(fileSystem vfs.FS) { assert.Error(t, newTestConfig(t, fileSystem).execute([]string{"add", "--secrets=error", "/home/user/.secret"})) }) } func TestIssue4107(t *testing.T) { if runtime.GOOS == "windows" && os.Getenv("GITHUB_RUN_ID") != "" { // FIXME fix this test. It may be failing because the home directory on // GitHub Actions is on the D: drive t.Skip("skipping failing test on Windows on GitHub Actions") } chezmoitest.WithTestFS(t, map[string]any{ "/home/user": map[string]any{ ".secret": "AWS_ACCESS_KEY_ID=AKIALALEMEL33243OLIA\n", ".config/chezmoi": map[string]any{ "chezmoi.toml": chezmoitest.JoinLines( `[add]`, ` secrets = "error"`, ), }, }, }, func(fileSystem vfs.FS) { assert.NoError(t, newTestConfig(t, fileSystem).execute([]string{"add", "--secrets=ignore", "/home/user/.secret"})) }) } ================================================ FILE: internal/cmd/agecmd.go ================================================ package cmd import ( "bytes" "errors" "io" "filippo.io/age" "filippo.io/age/armor" "github.com/spf13/cobra" ) type ageDecryptCmdConfig struct { passphrase bool } type ageEncryptCmdConfig struct { passphrase bool } type ageCmdConfig struct { decrypt ageDecryptCmdConfig encrypt ageEncryptCmdConfig } func (c *Config) newAgeCmd() *cobra.Command { ageCmd := &cobra.Command{ GroupID: groupIDEncryption, Use: "age", Args: cobra.NoArgs, Short: "Interact with age", Annotations: newAnnotations( persistentStateModeReadOnly, ), } ageDecryptCmd := &cobra.Command{ Use: "decrypt [file...]", Short: "Decrypt file or standard input", RunE: c.runAgeDecryptCmd, Annotations: newAnnotations( persistentStateModeReadOnly, ), } ageDecryptCmd.Flags(). BoolVarP(&c.age.decrypt.passphrase, "passphrase", "p", c.age.decrypt.passphrase, "Decrypt with a passphrase") ageCmd.AddCommand(ageDecryptCmd) ageEncryptCmd := &cobra.Command{ Use: "encrypt [file...]", Short: "Encrypt file or standard input", RunE: c.runAgeEncryptCmd, Annotations: newAnnotations( persistentStateModeReadOnly, ), } ageEncryptCmd.Flags(). BoolVarP(&c.age.encrypt.passphrase, "passphrase", "p", c.age.encrypt.passphrase, "Encrypt with a passphrase") ageCmd.AddCommand(ageEncryptCmd) return ageCmd } func (c *Config) runAgeDecryptCmd(cmd *cobra.Command, args []string) error { if !c.age.decrypt.passphrase { return errors.New("only passphrase encryption is supported") } decrypt := func(ciphertext []byte) ([]byte, error) { var ciphertextReader io.Reader = bytes.NewReader(ciphertext) if bytes.HasPrefix(ciphertext, []byte(armor.Header)) { ciphertextReader = armor.NewReader(ciphertextReader) } identity := &LazyScryptIdentity{ Passphrase: func() (string, error) { return c.readPassword("Enter passphrase: ", "passphrase") }, } plaintextReader, err := age.Decrypt(ciphertextReader, identity) if err != nil { return nil, err } plaintextBuffer := &bytes.Buffer{} if _, err := io.Copy(plaintextBuffer, plaintextReader); err != nil { return nil, err } return plaintextBuffer.Bytes(), nil } return c.filterInput(args, decrypt) } func (c *Config) runAgeEncryptCmd(cmd *cobra.Command, args []string) error { if !c.age.encrypt.passphrase { return errors.New("only passphrase encryption is supported") } passphrase, err := c.readPassword("Enter passphrase: ", "passphrase") if err != nil { return err } confirmPassphrase, err := c.readPassword("Confirm passphrase: ", "passphrase") if err != nil { return err } if passphrase != confirmPassphrase { return errors.New("passphrases didn't match") } recipient, err := age.NewScryptRecipient(passphrase) if err != nil { return err } encrypt := func(plaintext []byte) ([]byte, error) { ciphertextBuffer := &bytes.Buffer{} armoredCiphertextWriter := armor.NewWriter(ciphertextBuffer) ciphertextWriteCloser, err := age.Encrypt(armoredCiphertextWriter, recipient) if err != nil { return nil, err } if _, err := io.Copy(ciphertextWriteCloser, bytes.NewReader(plaintext)); err != nil { return nil, err } if err := ciphertextWriteCloser.Close(); err != nil { return nil, err } if err := armoredCiphertextWriter.Close(); err != nil { return nil, err } return ciphertextBuffer.Bytes(), nil } return c.filterInput(args, encrypt) } ================================================ FILE: internal/cmd/agekeygencmd.go ================================================ package cmd import ( "errors" "fmt" "io/fs" "os" "strings" "time" "filippo.io/age" "github.com/spf13/cobra" "golang.org/x/term" "chezmoi.io/chezmoi/internal/chezmoi" ) type ageKeygenCmdConfig struct { convert bool postQuantum bool } func (c *Config) newAgeKeygenCmd() *cobra.Command { ageKeygenCommand := &cobra.Command{ GroupID: groupIDEncryption, Use: "age-keygen", Args: cobra.MaximumNArgs(1), Short: "Generate an age identity or convert an age identity to an age recipient", Long: mustLongHelp("age-keygen"), Example: example("age-keygen"), RunE: c.runAgeKeygenCmd, Annotations: newAnnotations( persistentStateModeNone, ), } ageKeygenCommand.Flags(). BoolVarP(&c.ageKeygen.convert, "convert", "y", c.ageKeygen.convert, "convert identities to recipients") ageKeygenCommand.Flags(). BoolVar(&c.ageKeygen.postQuantum, "pq", c.ageKeygen.postQuantum, "generate a post-quantum key pair") return ageKeygenCommand } func (c *Config) runAgeKeygenCmd(cmd *cobra.Command, args []string) error { switch { case c.ageKeygen.convert && c.ageKeygen.postQuantum: return errors.New("--pq cannot be used with --convert") case c.ageKeygen.convert: return c.runAgeKeygenConvertCmd(args) default: return c.runAgeKeygenGenerateCmd(cmd, args) } } func (c *Config) runAgeKeygenConvertCmd(args []string) error { input := c.stdin if len(args) > 0 { inputFile, err := os.Open(args[0]) if err != nil { return err } defer inputFile.Close() input = inputFile } identities, err := age.ParseIdentities(input) switch { case err != nil: return err case len(identities) == 0: return errors.New("no identities found in input") } var builder strings.Builder for _, identity := range identities { id, ok := identity.(*age.X25519Identity) if !ok { return fmt.Errorf("internal error: unexpected identity type: %T", id) } builder.WriteString(id.Recipient().String()) builder.WriteByte('\n') } return c.writeOutputString(builder.String(), 0o666) } func (c *Config) runAgeKeygenGenerateCmd(cmd *cobra.Command, args []string) error { if len(args) > 0 { return fmt.Errorf("unknown command %q for %q", args[0], cmd.CommandPath()) } var identity age.Identity var recipient age.Recipient if c.ageKeygen.postQuantum { key, err := age.GenerateHybridIdentity() if err != nil { return err } identity = key recipient = key.Recipient() } else { key, err := age.GenerateX25519Identity() if err != nil { return err } identity = key recipient = key.Recipient() } if stdout, ok := c.stdout.(*os.File); ok && term.IsTerminal(int(stdout.Fd())) { fmt.Fprintf(c.stderr, "Public key: %s\n", recipient) } if !c.outputAbsPath.IsEmpty() && c.outputAbsPath != chezmoi.NewAbsPath("-") { switch fileInfo, err := c.baseSystem.Stat(c.outputAbsPath); { case errors.Is(err, fs.ErrNotExist): // Do nothing. case err != nil: return err case fileInfo.Mode().IsRegular() && fileInfo.Mode().Perm()&0o004 != 0: c.errorf("writing secret key to a world-readable file\n") } } var builder strings.Builder fmt.Fprintf(&builder, "# created: %s\n", time.Now().Format(time.RFC3339)) fmt.Fprintf(&builder, "# public key: %s\n", recipient) fmt.Fprintf(&builder, "%s\n", identity) return c.writeOutputString(builder.String(), 0o660) } ================================================ FILE: internal/cmd/annotation.go ================================================ package cmd import ( "github.com/spf13/cobra" ) // Annotations. var ( createSourceDirectoryIfNeeded = tagAnnotation("chezmoi_create_source_directory_if_needed") doesNotRequireValidConfig = tagAnnotation("chezmoi_runs_with_invalid_config") dryRun = tagAnnotation("chezmoi_dry_run") modifiesConfigFile = tagAnnotation("chezmoi_modifies_config_file") modifiesDestinationDirectory = tagAnnotation("chezmoi_modifies_destination_directory") modifiesSourceDirectory = tagAnnotation("chezmoi_modifies_source_directory") outputsDiff = tagAnnotation("chezmoi_outputs_diff") persistentStateModeKey = tagAnnotation("chezmoi_persistent_state_mode") requiresConfigDirectory = tagAnnotation("chezmoi_requires_config_directory") requiresSourceDirectory = tagAnnotation("chezmoi_requires_source_directory") requiresWorkingTree = tagAnnotation("chezmoi_requires_working_tree") runsCommands = tagAnnotation("chezmoi_runs_commands") ) // Persistent state modes. const ( persistentStateModeEmpty persistentStateModeValue = "empty" persistentStateModeNone persistentStateModeValue = "none" persistentStateModeReadOnly persistentStateModeValue = "read-only" persistentStateModeReadMockWrite persistentStateModeValue = "read-mock-write" persistentStateModeReadWrite persistentStateModeValue = "read-write" ) type annotation interface { key() string value() string } type annotationsSet map[string]string func getAnnotations(cmd *cobra.Command) annotationsSet { return annotationsSet(cmd.Annotations) } func newAnnotations(annotations ...annotation) annotationsSet { result := make(map[string]string, len(annotations)) for _, annotation := range annotations { result[annotation.key()] = annotation.value() } return result } func (a annotationsSet) hasTag(tag annotation) bool { return a[tag.key()] == tag.value() } func (a annotationsSet) persistentStateMode() persistentStateModeValue { return persistentStateModeValue(a[persistentStateModeKey.key()]) } type persistentStateModeValue string func (m persistentStateModeValue) key() string { return string(persistentStateModeKey) } func (m persistentStateModeValue) value() string { return string(m) } type tagAnnotation string func (a tagAnnotation) key() string { return string(a) } func (a tagAnnotation) value() string { return "true" } ================================================ FILE: internal/cmd/applycmd.go ================================================ package cmd import ( "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type applyCmdConfig struct { filter *chezmoi.EntryTypeFilter init bool parentDirs bool recursive bool } func (c *Config) newApplyCmd() *cobra.Command { applyCmd := &cobra.Command{ GroupID: groupIDDaily, Use: "apply [target]...", Short: "Update the destination directory to match the target state", Long: mustLongHelp("apply"), Example: example("apply"), ValidArgsFunction: c.targetValidArgs, RunE: c.runApplyCmd, Annotations: newAnnotations( modifiesDestinationDirectory, persistentStateModeReadWrite, requiresSourceDirectory, ), } applyCmd.Flags().VarP(c.apply.filter.Exclude, "exclude", "x", "Exclude entry types") applyCmd.Flags().VarP(c.apply.filter.Include, "include", "i", "Include entry types") applyCmd.Flags().BoolVar(&c.apply.init, "init", c.apply.init, "Recreate config file from template") applyCmd.Flags().BoolVarP(&c.apply.parentDirs, "parent-dirs", "P", c.apply.parentDirs, "Apply all parent directories") applyCmd.Flags().BoolVarP(&c.apply.recursive, "recursive", "r", c.apply.recursive, "Recurse into subdirectories") return applyCmd } func (c *Config) runApplyCmd(cmd *cobra.Command, args []string) error { return c.applyArgs(cmd.Context(), c.destSystem, c.DestDirAbsPath, args, applyArgsOptions{ cmd: cmd, filter: c.apply.filter, init: c.apply.init, parentDirs: c.apply.parentDirs, recursive: c.apply.recursive, umask: c.Umask, preApplyFunc: c.defaultPreApplyFunc, }) } ================================================ FILE: internal/cmd/applycmd_test.go ================================================ package cmd import ( "fmt" "io/fs" "net/http" "net/http/httptest" "path/filepath" "strings" "sync/atomic" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestApplyCmd(t *testing.T) { for _, tc := range []struct { name string extraRoot any args []string tests []any }{ { name: "all", tests: []any{ vfst.TestPath("/home/user/.create", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .create\n"), ), vfst.TestPath("/home/user/.dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.dir/file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .dir/file\n"), ), vfst.TestPath("/home/user/.dir/subdir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.dir/subdir/file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .dir/subdir/file\n"), ), vfst.TestPath("/home/user/.empty", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContents(nil), ), vfst.TestPath("/home/user/.executable", vfst.TestModeIsRegular(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), vfst.TestContentsString("# contents of .executable\n"), ), vfst.TestPath("/home/user/.file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .file\n"), ), vfst.TestPath("/home/user/.private", vfst.TestModeIsRegular(), vfst.TestModePerm(0o600&^chezmoitest.Umask), vfst.TestContentsString("# contents of .private\n"), ), vfst.TestPath("/home/user/.remove", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.symlink", vfst.TestModeType(fs.ModeSymlink), vfst.TestSymlinkTarget(filepath.FromSlash(".dir/subdir/file")), ), vfst.TestPath("/home/user/.template", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("key = value\n"), ), }, }, { name: "all_with_--dry-run", args: []string{"--dry-run"}, tests: []any{ vfst.TestPath("/home/user/.create", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.dir", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.empty", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.executable", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.file", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.private", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.remove", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.symlink", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.template", vfst.TestDoesNotExist(), ), }, }, { name: "dir", args: []string{"~/.dir"}, tests: []any{ vfst.TestPath("/home/user/.dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.dir/file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .dir/file\n"), ), vfst.TestPath("/home/user/.dir/subdir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.dir/subdir/file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .dir/subdir/file\n"), ), }, }, { name: "dir_with_--recursive=false", args: []string{"~/.dir", "--recursive=false"}, tests: []any{ vfst.TestPath("/home/user/.dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.dir/file", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.dir/subdir", vfst.TestDoesNotExist(), ), }, }, { name: "create", args: []string{"~/.create"}, extraRoot: map[string]any{ "/home/user/.create": "# existing contents of .create\n", }, tests: []any{ vfst.TestPath("/home/user/.create", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# existing contents of .create\n"), ), }, }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, map[string]any{ "/home/user": map[string]any{ ".config": map[string]any{ "chezmoi": map[string]any{ "chezmoi.toml": chezmoitest.JoinLines( `[data]`, ` variable = "value"`, ), }, }, ".local": map[string]any{ "share": map[string]any{ "chezmoi": map[string]any{ "create_dot_create": "# contents of .create\n", "dot_dir": map[string]any{ "file": "# contents of .dir/file\n", "subdir": map[string]any{ "file": "# contents of .dir/subdir/file\n", }, }, "dot_file": "# contents of .file\n", "dot_remove": "", "dot_template.tmpl": chezmoitest.JoinLines( `key = {{ "value" }}`, ), "empty_dot_empty": "", "executable_dot_executable": "# contents of .executable\n", "private_dot_private": "# contents of .private\n", "symlink_dot_symlink": ".dir/subdir/file\n", }, }, }, }, }, func(fileSystem vfs.FS) { if tc.extraRoot != nil { assert.NoError(t, vfst.NewBuilder().Build(fileSystem, tc.extraRoot)) } assert.NoError(t, newTestConfig(t, fileSystem).execute(append([]string{"apply"}, tc.args...))) vfst.RunTests(t, fileSystem, "", tc.tests) }) }) } } func TestUserAgent(t *testing.T) { var userAgent string httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { userAgent = r.Header.Get("User-Agent") })) defer httpServer.Close() chezmoitest.WithTestFS(t, map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi": map[string]any{ ".chezmoiexternal.toml.tmpl": chezmoitest.JoinLines( `[".local/bin/file"]`, ` type = "file"`, ` url = "`+httpServer.URL+`/file"`, ), }, }, }, func(fileSystem vfs.FS) { assert.NoError(t, newTestConfig(t, fileSystem).execute([]string{"apply"})) }) assert.True(t, strings.HasPrefix(userAgent, "chezmoi.io/")) } func TestIssue2132(t *testing.T) { chezmoitest.WithTestFS(t, map[string]any{ "/home/user/.local/share/chezmoi/remove_dot_dir/non_existent_file": "", }, func(fileSystem vfs.FS) { config1 := newTestConfig(t, fileSystem) assert.NoError(t, config1.execute([]string{"apply"})) vfst.RunTests(t, fileSystem, "", vfst.TestPath("/home/user/.dir", vfst.TestDoesNotExist(), ), ) config2 := newTestConfig(t, fileSystem) assert.NoError(t, config2.execute([]string{"apply", "--no-tty"})) vfst.RunTests(t, fileSystem, "", vfst.TestPath("/home/user/.dir", vfst.TestDoesNotExist(), ), ) }) } func TestIssue3206(t *testing.T) { chezmoitest.WithTestFS(t, map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi": map[string]any{ ".chezmoiignore": "", "dot_config/private_expanso/match": map[string]any{ ".chezmoidata.yaml": "key: value\n", "greek.yml.tmpl": "{{ .key }}", }, }, }, }, func(fileSystem vfs.FS) { assert.NoError(t, newTestConfig(t, fileSystem).execute([]string{"apply"})) }) } func TestIssue3216(t *testing.T) { chezmoitest.WithTestFS(t, map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi": map[string]any{ ".chezmoiignore": "", "dot_config/private_expanso/match": map[string]any{ ".chezmoidata.yaml": "", "greek.yml.tmpl": "{{ .chezmoi.os }}", }, }, }, }, func(fileSystem vfs.FS) { assert.NoError(t, newTestConfig(t, fileSystem).execute([]string{"apply"})) }) } func TestIssue3703(t *testing.T) { httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, err := w.Write([]byte("contents of file\n")) assert.NoError(t, err) })) defer httpServer.Close() chezmoitest.WithTestFS(t, map[string]any{ "/home/user": map[string]any{ ".local": map[string]any{ "bin": map[string]any{ "unmanaged": "", }, "share/chezmoi": map[string]any{ ".chezmoiexternal.toml.tmpl": chezmoitest.JoinLines( `[".local/bin/file"]`, ` type = "file"`, ` url = "`+httpServer.URL+`/file"`, ), "dot_local/exact_bin/.keep": "", }, }, }, }, func(fileSystem vfs.FS) { assert.NoError(t, newTestConfig(t, fileSystem).execute([]string{"apply"})) vfst.RunTests(t, fileSystem, ".local/bin", vfst.TestPath("/home/user/.local/bin/file", vfst.TestContentsString("contents of file\n"), ), vfst.TestPath("/home/user/.local/bin/unmanaged", vfst.TestDoesNotExist(), ), ) }) } func TestIssue4927(t *testing.T) { // httpServer returns a different file contents every time. var count atomic.Int64 httpServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, err := fmt.Fprintf(w, "contents of file, count %d\n", count.Add(1)) assert.NoError(t, err) })) defer httpServer.Close() chezmoitest.WithTestFS(t, map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi": map[string]any{ ".chezmoiexternal.toml.tmpl": chezmoitest.JoinLines( `[".file"]`, ` type = "file"`, ` url = "`+httpServer.URL+`/file"`, ` checksum.sha256 = "b48186336296270875649bac2e973968ec1f252154da32a4e906a462eb5f6c0a"`, ), }, }, }, func(fileSystem vfs.FS) { // Apply an external. assert.NoError(t, newTestConfig(t, fileSystem).execute([]string{"apply"})) vfst.RunTests(t, fileSystem, ".file", vfst.TestPath("/home/user/.file", vfst.TestContentsString("contents of file, count 1\n"), ), ) // Clear the cache. assert.NoError(t, fileSystem.RemoveAll("/home/user/.cache/chezmoi")) // Apply the external again. As the cache has been cleared, apply should // download a new file with a new checksum. assert.NoError(t, fileSystem.WriteFile( "/home/user/.local/share/chezmoi/.chezmoiexternal.toml.tmpl", []byte(chezmoitest.JoinLines( `[".file"]`, ` type = "file"`, ` url = "`+httpServer.URL+`/file"`, ` checksum.sha256 = "5ecfe762ad9a450cbc2317d8fd114d226ad97209ba1e11dc30ab26423a335fed"`, )), 0o666)) assert.NoError(t, newTestConfig(t, fileSystem).execute([]string{"apply"})) vfst.RunTests(t, fileSystem, ".file", vfst.TestPath("/home/user/.file", vfst.TestContentsString("contents of file, count 2\n"), ), ) // Clear the cache again. assert.NoError(t, fileSystem.RemoveAll("/home/user/.cache/chezmoi")) // Apply the external again. As the cache has been cleared, the apply // should download the external again, with a new SHA256 sum. assert.EqualError( t, newTestConfig(t, fileSystem).execute([]string{"apply"}), ".file: .file: SHA256 mismatch: "+ "expected 5ecfe762ad9a450cbc2317d8fd114d226ad97209ba1e11dc30ab26423a335fed, "+ "got f1517c117381f6ac8c0b07e96152fe296891d9b56fb1ebc44ed0f8da2ab72688", ) vfst.RunTests(t, fileSystem, ".file", vfst.TestPath("/home/user/.file", vfst.TestContentsString("contents of file, count 2\n"), ), ) // Apply the external again. As the cache has not be cleared, the apply // should have the same SHA256 sum as in the cache. assert.EqualError(t, newTestConfig(t, fileSystem).execute([]string{"apply"}), ".file: .file: SHA256 mismatch: expected "+ "5ecfe762ad9a450cbc2317d8fd114d226ad97209ba1e11dc30ab26423a335fed, "+ "got f1517c117381f6ac8c0b07e96152fe296891d9b56fb1ebc44ed0f8da2ab72688", ) vfst.RunTests(t, fileSystem, ".file", vfst.TestPath("/home/user/.file", vfst.TestContentsString("contents of file, count 2\n"), ), ) // Apply the external again, this time with --refresh-externals=always. // Due to the refresh, the SHA256 sum should have changed again. assert.EqualError(t, newTestConfig(t, fileSystem).execute([]string{"apply", "--refresh-externals=always"}), ".file: .file: SHA256 mismatch: expected "+ "5ecfe762ad9a450cbc2317d8fd114d226ad97209ba1e11dc30ab26423a335fed, "+ "got 32da3f52b369ad56a60d0f9707268b5a8d0aba1839dde3fcfe6e3bf092777f97", ) vfst.RunTests(t, fileSystem, ".file", vfst.TestPath("/home/user/.file", vfst.TestContentsString("contents of file, count 2\n"), ), ) }) } ================================================ FILE: internal/cmd/archivecmd.go ================================================ package cmd import ( "archive/tar" "fmt" "os/user" "strconv" "strings" "time" "github.com/klauspost/compress/gzip" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) var archiveFormatValues = []string{"", "tar", "tar.gz", "tgz", "zip"} type archiveCmdConfig struct { filter *chezmoi.EntryTypeFilter format *choiceFlag gzip bool init bool parentDirs bool recursive bool } func (c *Config) newArchiveCmd() *cobra.Command { archiveCmd := &cobra.Command{ GroupID: groupIDMigration, Use: "archive [target]...", Short: "Generate a tar archive of the target state", Long: mustLongHelp("archive"), Example: example("archive"), ValidArgsFunction: c.targetValidArgs, RunE: c.runArchiveCmd, Annotations: newAnnotations( persistentStateModeEmpty, requiresSourceDirectory, ), } archiveCmd.Flags().VarP(c.archive.filter.Exclude, "exclude", "x", "Exclude entry types") archiveCmd.Flags().VarP(c.archive.format, "format", "f", "Set archive format") must(archiveCmd.RegisterFlagCompletionFunc("format", c.archive.format.FlagCompletionFunc())) archiveCmd.Flags().BoolVarP(&c.archive.gzip, "gzip", "z", c.archive.gzip, "Compress output with gzip") archiveCmd.Flags().VarP(c.archive.filter.Exclude, "include", "i", "Include entry types") archiveCmd.Flags().BoolVar(&c.archive.init, "init", c.archive.init, "Recreate config file from template") archiveCmd.Flags().BoolVarP(&c.archive.parentDirs, "parent-dirs", "P", c.archive.parentDirs, "Archive parent directories") archiveCmd.Flags().BoolVarP(&c.archive.recursive, "recursive", "r", c.archive.recursive, "Recurse into subdirectories") return archiveCmd } func (c *Config) runArchiveCmd(cmd *cobra.Command, args []string) error { var format chezmoi.ArchiveFormat switch formatStr := c.archive.format.String(); formatStr { case "": format = chezmoi.GuessArchiveFormat(c.outputAbsPath.String(), nil) if format == chezmoi.ArchiveFormatUnknown { format = chezmoi.ArchiveFormatTar } case "tar": format = chezmoi.ArchiveFormatTar case "tar.gz", "tgz": format = chezmoi.ArchiveFormatTarGz case "zip": format = chezmoi.ArchiveFormatZip default: return fmt.Errorf("%s: invalid format", formatStr) } gzipOutput := c.archive.gzip if format == chezmoi.ArchiveFormatTarGz { gzipOutput = true } output := strings.Builder{} var archiveSystem interface { chezmoi.System Close() error } switch format { case chezmoi.ArchiveFormatTar, chezmoi.ArchiveFormatTarGz: archiveSystem = chezmoi.NewTarWriterSystem(&output, tarHeaderTemplate()) case chezmoi.ArchiveFormatZip: archiveSystem = chezmoi.NewZIPWriterSystem(&output, time.Now().UTC()) } if err := c.applyArgs(cmd.Context(), archiveSystem, chezmoi.EmptyAbsPath, args, applyArgsOptions{ cmd: cmd, filter: c.archive.filter, init: c.archive.init, parentDirs: c.archive.parentDirs, recursive: c.archive.recursive, }); err != nil { return err } if err := archiveSystem.Close(); err != nil { return err } if format == chezmoi.ArchiveFormatZip || !gzipOutput { return c.writeOutputString(output.String(), 0o666) } gzippedArchive := strings.Builder{} gzipWriter := gzip.NewWriter(&gzippedArchive) if _, err := gzipWriter.Write([]byte(output.String())); err != nil { return err } if err := gzipWriter.Close(); err != nil { return err } return c.writeOutputString(gzippedArchive.String(), 0o666) } // tarHeaderTemplate returns a [tar.Header] template populated with the current // user and time. func tarHeaderTemplate() tar.Header { // Attempt to lookup the current user. Ignore errors because the default // zero values are reasonable. var ( uid int gid int uname string gname string ) if currentUser, err := user.Current(); err == nil { uid, _ = strconv.Atoi(currentUser.Uid) gid, _ = strconv.Atoi(currentUser.Gid) uname = currentUser.Username if group, err := user.LookupGroupId(currentUser.Gid); err == nil { gname = group.Name } } now := time.Now().UTC() return tar.Header{ Uid: uid, Gid: gid, Uname: uname, Gname: gname, ModTime: now, AccessTime: now, ChangeTime: now, } } ================================================ FILE: internal/cmd/autobool.go ================================================ package cmd import ( "bytes" "fmt" "reflect" "strconv" "strings" "github.com/go-viper/mapstructure/v2" "chezmoi.io/chezmoi/internal/chezmoi" ) type autoBool struct { auto bool value bool } // autoBoolFlagCompletionFunc is a function that completes the value of autoBool // flags. var autoBoolFlagCompletionFunc = chezmoi.FlagCompletionFunc([]string{ "1", "t", "T", "true", "TRUE", "True", "0", "f", "F", "false", "FALSE", "False", "auto", "AUTO", "Auto", }) // MarshalJSON implements encoding/json.Marshaler.MarshalJSON. func (b autoBool) MarshalJSON() ([]byte, error) { switch { case b.auto: return []byte(`"auto"`), nil case b.value: return []byte(`true`), nil default: return []byte(`false`), nil } } // MarshalYAML implements github.com/goccy/go-yaml.Marshaler. func (b autoBool) MarshalYAML() (any, error) { if b.auto { return "auto", nil } return b.value, nil } // Set implements github.com/spf13/pflag.Value.Set. func (b *autoBool) Set(s string) error { if strings.EqualFold(s, "auto") { b.auto = true return nil } b.auto = false var err error b.value, err = chezmoi.ParseBool(s) if err != nil { return err } return nil } func (b *autoBool) String() string { if b.auto { return "auto" } return strconv.FormatBool(b.value) } // Type implements github.com/spf13/pflag.Value.Type. func (b *autoBool) Type() string { return "bool|auto" } // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON. func (b *autoBool) UnmarshalJSON(data []byte) error { if string(data) == `"auto"` { b.auto = true return nil } value, err := chezmoi.ParseBool(string(data)) if err != nil { return err } b.auto = false b.value = value return nil } // UnmarshalText implements encoding.TextUnmarshaler.UnmarshalText. func (b *autoBool) UnmarshalText(text []byte) error { if bytes.Equal(text, []byte("auto")) { b.auto = true return nil } boolValue, err := chezmoi.ParseBool(string(text)) if err != nil { return err } b.auto = false b.value = boolValue return nil } // Value returns b's value, calling b's autoFunc if needed. func (b *autoBool) Value(autoFunc func() bool) bool { if b.auto { b.value = autoFunc() b.auto = false } return b.value } // StringOrBoolToAutoBoolHookFunc is a // github.com/go-viper/mapstructure/v2.DecodeHookFunc that parses an autoBool // from a bool or string. func StringOrBoolToAutoBoolHookFunc() mapstructure.DecodeHookFunc { return func(from, to reflect.Type, data any) (any, error) { if to != reflect.TypeFor[autoBool]() { return data, nil } var b autoBool switch data := data.(type) { case bool: b.auto = false b.value = data case string: if err := b.Set(data); err != nil { return nil, err } default: return nil, fmt.Errorf("expected a bool or string, got a %T", data) } return b, nil } } ================================================ FILE: internal/cmd/awssecretsmanagertemplatefuncs.go ================================================ package cmd import ( "context" "encoding/base64" "encoding/json" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/secretsmanager" ) type awsSecretsManagerConfig struct { Region string `json:"region" mapstructure:"region" yaml:"region"` Profile string `json:"profile" mapstructure:"profile" yaml:"profile"` svc *secretsmanager.Client cache map[string]string jsonCache map[string]map[string]any } func (c *Config) awsSecretsManagerRawTemplateFunc(arn string) string { if secret, ok := c.AWSSecretsManager.cache[arn]; ok { return secret } if c.AWSSecretsManager.svc == nil { var opts []func(*config.LoadOptions) error if region := c.AWSSecretsManager.Region; region != "" { opts = append(opts, config.WithRegion(region)) } if profile := c.AWSSecretsManager.Profile; profile != "" { opts = append(opts, config.WithSharedConfigProfile(profile)) } opts = append(opts, config.WithRetryMaxAttempts(1)) cfg, err := config.LoadDefaultConfig(context.Background(), opts...) if err != nil { panic(err) } c.AWSSecretsManager.svc = secretsmanager.NewFromConfig(cfg) } result, err := c.AWSSecretsManager.svc.GetSecretValue(context.Background(), &secretsmanager.GetSecretValueInput{ SecretId: aws.String(arn), }) if err != nil { panic(err) } var secret string if result.SecretString != nil { secret = *result.SecretString } else { decodedBinarySecretBytes := make([]byte, base64.StdEncoding.DecodedLen(len(result.SecretBinary))) length, err := base64.StdEncoding.Decode(decodedBinarySecretBytes, result.SecretBinary) if err != nil { panic(err) } secret = string(decodedBinarySecretBytes[:length]) } if c.AWSSecretsManager.cache == nil { c.AWSSecretsManager.cache = make(map[string]string) } c.AWSSecretsManager.cache[arn] = secret return secret } func (c *Config) awsSecretsManagerTemplateFunc(arn string) map[string]any { if secret, ok := c.AWSSecretsManager.jsonCache[arn]; ok { return secret } raw := c.awsSecretsManagerRawTemplateFunc(arn) var data map[string]any if err := json.Unmarshal([]byte(raw), &data); err != nil { panic(err) } if c.AWSSecretsManager.jsonCache == nil { c.AWSSecretsManager.jsonCache = make(map[string]map[string]any) } c.AWSSecretsManager.jsonCache[arn] = data return data } ================================================ FILE: internal/cmd/azurekeyvaulttemplatefuncs.go ================================================ package cmd import ( "context" "errors" "fmt" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets" ) type azureKeyVault struct { client *azsecrets.Client cache map[string]string } func (v *azureKeyVault) URL(vaultName string) string { return fmt.Sprintf("https://%s.vault.azure.net/", vaultName) } type azureKeyVaultConfig struct { DefaultVault string `json:"defaultVault" mapstructure:"defaultVault" yaml:"defaultVault"` vaults map[string]*azureKeyVault cred *azidentity.DefaultAzureCredential } func (a *azureKeyVaultConfig) GetSecret(secretName, vaultName string) string { if a.vaults == nil { a.vaults = make(map[string]*azureKeyVault) } if _, ok := a.vaults[vaultName]; !ok { a.vaults[vaultName] = &azureKeyVault{} } if secret, ok := a.vaults[vaultName].cache[secretName]; ok { return secret } if a.cred == nil { a.cred = mustValue(azidentity.NewDefaultAzureCredential(nil)) } if a.vaults[vaultName].client == nil { a.vaults[vaultName].client = mustValue(azsecrets.NewClient(a.vaults[vaultName].URL(vaultName), a.cred, nil)) } resp := mustValue(a.vaults[vaultName].client.GetSecret(context.Background(), secretName, "", nil)) if a.vaults[vaultName].cache == nil { a.vaults[vaultName].cache = make(map[string]string) } a.vaults[vaultName].cache[secretName] = *resp.Value return *resp.Value } func (c *Config) azureKeyVaultTemplateFunc(args ...string) string { var secretName, vaultName string switch len(args) { case 1: if c.AzureKeyVault.DefaultVault == "" { panic(errors.New("no value set in azureKeyVault.defaultVault")) } secretName, vaultName = args[0], c.AzureKeyVault.DefaultVault case 2: secretName, vaultName = args[0], args[1] default: panic(fmt.Errorf("expected 1 or 2 arguments, got %d", len(args))) } return c.AzureKeyVault.GetSecret(secretName, vaultName) } ================================================ FILE: internal/cmd/bitwardensecretstemplatefuncs.go ================================================ package cmd import ( "encoding/json" "fmt" "os" "os/exec" "strings" "chezmoi.io/chezmoi/internal/chezmoilog" ) type bitwardenSecretsConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` outputCache map[string][]byte } func (c *Config) bitwardenSecretsTemplateFunc(secretID string, additionalArgs ...string) any { args := []string{"secret", "get", secretID} switch len(additionalArgs) { case 0: // Do nothing. case 1: args = append(args, "--access-token", additionalArgs[0]) default: panic(fmt.Errorf("expected 1 or 2 arguments, got %d", len(additionalArgs)+1)) } output := mustValue(c.bitwardenSecretsOutput(args)) var data map[string]any must(json.Unmarshal(output, &data)) return data } func (c *Config) bitwardenSecretsOutput(args []string) ([]byte, error) { key := strings.Join(args, "\x00") if data, ok := c.Bitwarden.outputCache[key]; ok { return data, nil } name := c.BitwardenSecrets.Command cmd := exec.Command(name, args...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } if c.BitwardenSecrets.outputCache == nil { c.BitwardenSecrets.outputCache = make(map[string][]byte) } c.BitwardenSecrets.outputCache[key] = output return output, nil } ================================================ FILE: internal/cmd/bitwardentemplatefuncs.go ================================================ package cmd import ( "bytes" "encoding/json" "fmt" "os" "os/exec" "strings" "chezmoi.io/chezmoi/internal/chezmoilog" ) type bitwardenConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Unlock autoBool `json:"unlock" mapstructure:"unlock" yaml:"unlock"` session string outputCache map[string][]byte } func (c *Config) bitwardenAttachmentTemplateFunc(name, itemID string) string { must(c.bitwardenMaybeUnlock()) return string(mustValue(c.bitwardenOutput([]string{"get", "attachment", name, "--itemid", itemID, "--raw"}))) } func (c *Config) bitwardenAttachmentByRefTemplateFunc(name string, args ...string) string { must(c.bitwardenMaybeUnlock()) output := mustValue(c.bitwardenOutput(append([]string{"get"}, args...))) var data struct { ID string `json:"id"` } must(json.Unmarshal(output, &data)) return c.bitwardenAttachmentTemplateFunc(name, data.ID) } func (c *Config) bitwardenFieldsTemplateFunc(args ...string) map[string]any { must(c.bitwardenMaybeUnlock()) output := mustValue(c.bitwardenOutput(append([]string{"get"}, args...))) var data struct { Fields []map[string]any `json:"fields"` } must(json.Unmarshal(output, &data)) result := make(map[string]any) for _, field := range data.Fields { if name, ok := field["name"].(string); ok { result[name] = field } } return result } func (c *Config) bitwardenTemplateFunc(args ...string) map[string]any { must(c.bitwardenMaybeUnlock()) output := mustValue(c.bitwardenOutput(append([]string{"get"}, args...))) var data map[string]any must(json.Unmarshal(output, &data)) return data } func (c *Config) bitwardenLock() error { if c.Bitwarden.session == "" { return nil } output, err := c.bitwardenOutput([]string{"lock"}) if err != nil { return fmt.Errorf("%w: %s", err, output) } return nil } func (c *Config) bitwardenMaybeUnlock() error { unlock := c.Bitwarden.Unlock.Value(func() bool { _, ok := os.LookupEnv("BW_SESSION") return !ok }) if !unlock { return nil } output, err := c.bitwardenOutput([]string{"unlock", "--raw"}) if err != nil { return err } c.Bitwarden.session = string(bytes.TrimSpace(output)) return nil } func (c *Config) bitwardenOutput(args []string) ([]byte, error) { key := strings.Join(args, "\x00") if data, ok := c.Bitwarden.outputCache[key]; ok { return data, nil } output, err := c.bitwardenUncachedOutput(args) if err != nil { return nil, err } if c.Bitwarden.outputCache == nil { c.Bitwarden.outputCache = make(map[string][]byte) } c.Bitwarden.outputCache[key] = output return output, nil } func (c *Config) bitwardenUncachedOutput(args []string) ([]byte, error) { name := c.Bitwarden.Command cmd := exec.Command(name, args...) if c.Bitwarden.session != "" { cmd.Env = append(os.Environ(), "BW_SESSION="+c.Bitwarden.session) } cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } return output, err } ================================================ FILE: internal/cmd/catcmd.go ================================================ package cmd import ( "fmt" "strings" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) func (c *Config) newCatCmd() *cobra.Command { catCmd := &cobra.Command{ GroupID: groupIDTemplate, Use: "cat target...", Short: "Print the target contents of a file, script, or symlink", Long: mustLongHelp("cat"), Example: example("cat"), ValidArgsFunction: c.targetValidArgs, Args: cobra.MinimumNArgs(1), RunE: c.makeRunEWithSourceState(c.runCatCmd), Annotations: newAnnotations( persistentStateModeReadWrite, requiresSourceDirectory, ), } return catCmd } func (c *Config) runCatCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { targetRelPaths, err := c.targetRelPaths(sourceState, args, targetRelPathsOptions{}) if err != nil { return err } builder := strings.Builder{} for _, targetRelPath := range targetRelPaths { sourceStateEntry := sourceState.MustEntry(targetRelPath) targetStateEntry, err := sourceStateEntry.TargetStateEntry(c.destSystem, c.DestDirAbsPath.Join(targetRelPath)) if err != nil { return fmt.Errorf("%s: %w", targetRelPath, err) } switch targetStateEntry := targetStateEntry.(type) { case *chezmoi.TargetStateFile: contents, err := targetStateEntry.Contents() if err != nil { return fmt.Errorf("%s: %w", targetRelPath, err) } builder.Write(contents) case *chezmoi.TargetStateScript: contents, err := targetStateEntry.Contents() if err != nil { return fmt.Errorf("%s: %w", targetRelPath, err) } builder.Write(contents) case *chezmoi.TargetStateSymlink: linkname, err := targetStateEntry.Linkname() if err != nil { return fmt.Errorf("%s: %w", targetRelPath, err) } builder.WriteString(linkname) builder.WriteByte('\n') default: return fmt.Errorf("%s: not a file, script, or symlink", targetRelPath) } } return c.writeOutputString(builder.String(), 0o666) } ================================================ FILE: internal/cmd/catcmd_test.go ================================================ package cmd import ( "runtime" "strings" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestCatCmd(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("fails due to Windows paths on GitHub Actions") } for _, tc := range []struct { name string root any args []string expectedStr string }{ { name: "template_delimiters", root: map[string]any{ "/home/user/.local/share/chezmoi/dot_template.tmpl": chezmoitest.JoinLines( `# chezmoi:template:left-delimiter=[[ right-delimiter=]]`, `[[ "ok" ]]`, ), }, args: []string{ "/home/user/.template", }, expectedStr: chezmoitest.JoinLines( "ok", ), }, { name: "json_indent", root: map[string]any{ "/home/user/.local/share/chezmoi/dot_template.tmpl": chezmoitest.JoinLines( `# chezmoi:template:format-indent-width=3`, `{{ dict "a" (dict "b" "c") | toJson }}`, ), }, args: []string{ "/home/user/.template", }, expectedStr: chezmoitest.JoinLines( `{`, ` "a": {`, ` "b": "c"`, ` }`, `}`, ``, ), }, { name: "yaml_indent", root: map[string]any{ "/home/user/.local/share/chezmoi/dot_template.tmpl": chezmoitest.JoinLines( `# chezmoi:template:format-indent-width=3`, `{{ dict "a" (dict "b" "c") | toYaml }}`, ), }, args: []string{ "/home/user/.template", }, expectedStr: chezmoitest.JoinLines( `a:`, ` b: c`, ``, ), }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { stdout := strings.Builder{} c := newTestConfig(t, fileSystem, withStdout(&stdout)) assert.NoError(t, c.execute(append([]string{"cat"}, tc.args...))) assert.Equal(t, tc.expectedStr, stdout.String()) }) }) } } ================================================ FILE: internal/cmd/catconfigcmd.go ================================================ package cmd import "github.com/spf13/cobra" func (c *Config) newCatConfigCmd() *cobra.Command { catConfigCmd := &cobra.Command{ GroupID: groupIDInternal, Use: "cat-config", Short: "Print the configuration file", Long: mustLongHelp("cat-config"), Example: example("cat-config"), Args: cobra.NoArgs, ValidArgsFunction: cobra.NoFileCompletions, RunE: c.runCatConfigCmd, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeReadOnly, requiresConfigDirectory, ), } return catConfigCmd } func (c *Config) runCatConfigCmd(cmd *cobra.Command, args []string) error { configFileAbsPath, err := c.getConfigFileAbsPath() if err != nil { return err } data, err := c.baseSystem.ReadFile(configFileAbsPath) if err != nil { return err } return c.writeOutput(data, 0o666) } ================================================ FILE: internal/cmd/cdcmd.go ================================================ package cmd import ( "fmt" "os" "github.com/spf13/cobra" "github.com/twpayne/go-shell" "chezmoi.io/chezmoi/internal/chezmoi" ) type cdCmdConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Args []string `json:"args" mapstructure:"args" yaml:"args"` } func (c *Config) newCDCmd() *cobra.Command { cdCmd := &cobra.Command{ GroupID: groupIDAdvanced, Use: "cd [path]", Short: "Launch a shell in the source directory", Long: mustLongHelp("cd"), Example: example("cd"), RunE: c.runCDCmd, Args: cobra.MaximumNArgs(1), Annotations: newAnnotations( createSourceDirectoryIfNeeded, doesNotRequireValidConfig, persistentStateModeReadWrite, requiresWorkingTree, runsCommands, ), } return cdCmd } func (c *Config) runCDCmd(cmd *cobra.Command, args []string) error { os.Setenv("CHEZMOI_SUBSHELL", "1") cdCommand, cdArgs, err := c.cdCommand() if err != nil { return err } var dir chezmoi.AbsPath if len(args) == 0 { dir = c.WorkingTreeAbsPath } else { switch argAbsPath, err := chezmoi.NewAbsPathFromExtPath(args[0], c.homeDirAbsPath); { case err != nil: return err case argAbsPath == c.DestDirAbsPath: dir, err = c.getSourceDirAbsPath(nil) if err != nil { return err } default: sourceState, err := c.getSourceState(cmd.Context(), cmd) if err != nil { return err } sourceAbsPaths, err := c.sourceAbsPaths(sourceState, args) if err != nil { return err } dir = sourceAbsPaths[0] } } switch fileInfo, err := c.baseSystem.Stat(dir); { case err != nil: return err case !fileInfo.IsDir(): return fmt.Errorf("%s: not a directory", dir) } return c.run(dir, cdCommand, cdArgs) } func (c *Config) cdCommand() (string, []string, error) { cdCommand := c.CD.Command cdArgs := c.CD.Args if cdCommand != "" { return cdCommand, cdArgs, nil } cdCommand, _ = shell.CurrentUserShell() return parseCommand(cdCommand, cdArgs) } ================================================ FILE: internal/cmd/chattrcmd.go ================================================ package cmd import ( "fmt" "slices" "strings" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type chattrCmdConfig struct { recursive bool } type boolModifier int const ( boolModifierSet boolModifier = 1 boolModifierLeaveUnchanged boolModifier = 0 boolModifierClear boolModifier = -1 ) type conditionModifier int const ( conditionModifierLeaveUnchanged conditionModifier = iota conditionModifierClearOnce conditionModifierSetOnce conditionModifierClearOnChange conditionModifierSetOnChange ) type orderModifier int const ( orderModifierSetBefore orderModifier = -2 orderModifierClearBefore orderModifier = -1 orderModifierLeaveUnchanged orderModifier = 0 orderModifierClearAfter orderModifier = 1 orderModifierSetAfter orderModifier = 2 ) type sourceFileTypeModifier int const ( sourceFileTypeModifierLeaveUnchanged sourceFileTypeModifier = iota sourceFileTypeModifierSetCreate sourceFileTypeModifierClearCreate sourceFileTypeModifierSetModify sourceFileTypeModifierClearModify sourceFileTypeModifierSetRemove sourceFileTypeModifierClearRemove sourceFileTypeModifierSetScript sourceFileTypeModifierClearScript sourceFileTypeModifierSetSymlink sourceFileTypeModifierClearSymlink ) type modifier struct { sourceFileType sourceFileTypeModifier condition conditionModifier empty boolModifier encrypted boolModifier exact boolModifier executable boolModifier external boolModifier order orderModifier private boolModifier readOnly boolModifier remove boolModifier template boolModifier } func (c *Config) newChattrCmd() *cobra.Command { chattrCmd := &cobra.Command{ GroupID: groupIDDaily, Use: "chattr attributes target...", Short: "Change the attributes of a target in the source state", Long: mustLongHelp("chattr"), Example: example("chattr"), Args: cobra.MinimumNArgs(2), ValidArgsFunction: c.chattrCmdValidArgs, RunE: c.makeRunEWithSourceState(c.runChattrCmd), Annotations: newAnnotations( persistentStateModeReadWrite, modifiesSourceDirectory, requiresSourceDirectory, ), } chattrCmd.Flags().BoolVarP(&c.chattr.recursive, "recursive", "r", c.chattr.recursive, "Recurse into subdirectories") return chattrCmd } // chattrCmdValidArgs returns the completions for the chattr command. func (c *Config) chattrCmdValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { switch len(args) { case 0: prefixes := []string{"", "-", "+", "no"} attributes := []string{ "after", "before", "create", "empty", "encrypted", "exact", "executable", "external", "modify", "once", "onchange", "private", "readonly", "remove", "script", "symlink", "template", } validModifiers := make([]string, 0, len(prefixes)*len(attributes)) for _, prefix := range prefixes { for _, attribute := range attributes { modifier := prefix + attribute validModifiers = append(validModifiers, modifier) } } modifiers := strings.Split(toComplete, ",") modifierToComplete := modifiers[len(modifiers)-1] completionPrefix := toComplete[:len(toComplete)-len(modifierToComplete)] var completions []string for _, modifier := range validModifiers { if strings.HasPrefix(modifier, modifierToComplete) { completion := completionPrefix + modifier completions = append(completions, completion) } } return completions, cobra.ShellCompDirectiveNoFileComp default: return c.targetValidArgs(cmd, args, toComplete) } } func (c *Config) runChattrCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { // LATER should the core functionality of chattr move to chezmoi.SourceState? m, err := parseModifier(args[0]) if err != nil { return err } targetRelPaths, err := c.targetRelPaths(sourceState, args[1:], targetRelPathsOptions{ mustNotBeExternal: true, recursive: c.chattr.recursive, }) if err != nil { return err } // Visit targets in reverse so we update children before their parent // directories. encryptedSuffix := sourceState.Encryption().EncryptedSuffix() for _, targetRelPath := range slices.Backward(targetRelPaths) { sourceStateEntry := sourceState.MustEntry(targetRelPath) sourceRelPath := sourceStateEntry.SourceRelPath() parentSourceRelPath, fileSourceRelPath := sourceRelPath.Split() parentRelPath := parentSourceRelPath.RelPath() fileRelPath := fileSourceRelPath.RelPath() switch sourceStateEntry := sourceStateEntry.(type) { case *chezmoi.SourceStateDir: relPath := m.modifyDirAttr(sourceStateEntry.Attr()).SourceName() if newBaseNameRelPath := chezmoi.NewRelPath(relPath); newBaseNameRelPath != fileRelPath { oldSourceAbsPath := c.SourceDirAbsPath.Join(parentRelPath, fileRelPath) newSourceAbsPath := c.SourceDirAbsPath.Join(parentRelPath, newBaseNameRelPath) if err := c.sourceSystem.Rename(oldSourceAbsPath, newSourceAbsPath); err != nil { return err } } case *chezmoi.SourceStateFile: newAttr := m.modifyFileAttr(sourceStateEntry.Attr()) newBaseNameRelPath := chezmoi.NewRelPath(newAttr.SourceName(encryptedSuffix)) oldSourceAbsPath := c.SourceDirAbsPath.Join(parentRelPath, fileRelPath) newSourceAbsPath := c.SourceDirAbsPath.Join(parentRelPath, newBaseNameRelPath) switch encryptedBefore, encryptedAfter := sourceStateEntry.Attr().Encrypted, newAttr.Encrypted; { case encryptedBefore && !encryptedAfter: // Write the plaintext and then remove the ciphertext. plaintext, err := sourceStateEntry.Contents() if err != nil { return err } if err := c.sourceSystem.WriteFile(newSourceAbsPath, plaintext, 0o666&^c.Umask); err != nil { return err } if err := c.sourceSystem.Remove(oldSourceAbsPath); err != nil { return err } case !encryptedBefore && encryptedAfter: // Write the ciphertext and then remove the plaintext. plaintext, err := sourceStateEntry.Contents() if err != nil { return err } ciphertext, err := sourceState.Encryption().Encrypt(plaintext) if err != nil { return err } if err := c.sourceSystem.WriteFile(newSourceAbsPath, ciphertext, 0o666&^c.Umask); err != nil { return err } if err := c.sourceSystem.Remove(oldSourceAbsPath); err != nil { return err } case newBaseNameRelPath != fileRelPath: // Contents have not changed so a rename is sufficient. if err := c.sourceSystem.Rename(oldSourceAbsPath, newSourceAbsPath); err != nil { return err } } } } return nil } // modify returns the modified value of b. func (m boolModifier) modify(b bool) bool { switch m { case boolModifierSet: return true case boolModifierLeaveUnchanged: return b case boolModifierClear: return false default: panic(fmt.Sprintf("%d: unknown bool modifier", m)) } } // modify returns the modified value of condition. func (m conditionModifier) modify(condition chezmoi.ScriptCondition) chezmoi.ScriptCondition { switch m { case conditionModifierLeaveUnchanged: return condition case conditionModifierClearOnce: if condition == chezmoi.ScriptConditionOnce { return chezmoi.ScriptConditionAlways } return condition case conditionModifierSetOnce: return chezmoi.ScriptConditionOnce case conditionModifierClearOnChange: if condition == chezmoi.ScriptConditionOnChange { return chezmoi.ScriptConditionAlways } return condition case conditionModifierSetOnChange: return chezmoi.ScriptConditionOnChange default: panic(fmt.Sprintf("%d: unknown order modifier", m)) } } // modify returns the modified value of order. func (m orderModifier) modify(order chezmoi.ScriptOrder) chezmoi.ScriptOrder { switch m { case orderModifierSetBefore: return chezmoi.ScriptOrderBefore case orderModifierClearBefore: if order == chezmoi.ScriptOrderBefore { return chezmoi.ScriptOrderDuring } return order case orderModifierLeaveUnchanged: return order case orderModifierClearAfter: if order == chezmoi.ScriptOrderAfter { return chezmoi.ScriptOrderDuring } return order case orderModifierSetAfter: return chezmoi.ScriptOrderAfter default: panic(fmt.Sprintf("%d: unknown order modifier", m)) } } // modify returns the modified value of type. func (m sourceFileTypeModifier) modify(sourceFileType chezmoi.SourceFileTargetType) chezmoi.SourceFileTargetType { switch m { case sourceFileTypeModifierLeaveUnchanged: return sourceFileType case sourceFileTypeModifierSetCreate: return chezmoi.SourceFileTypeCreate case sourceFileTypeModifierClearCreate: if sourceFileType == chezmoi.SourceFileTypeCreate { return chezmoi.SourceFileTypeFile } return sourceFileType case sourceFileTypeModifierSetRemove: return chezmoi.SourceFileTypeRemove case sourceFileTypeModifierClearRemove: if sourceFileType == chezmoi.SourceFileTypeRemove { return chezmoi.SourceFileTypeFile } return sourceFileType case sourceFileTypeModifierSetModify: return chezmoi.SourceFileTypeModify case sourceFileTypeModifierClearModify: if sourceFileType == chezmoi.SourceFileTypeModify { return chezmoi.SourceFileTypeFile } return sourceFileType case sourceFileTypeModifierSetScript: return chezmoi.SourceFileTypeScript case sourceFileTypeModifierClearScript: if sourceFileType == chezmoi.SourceFileTypeScript { return chezmoi.SourceFileTypeFile } return sourceFileType case sourceFileTypeModifierSetSymlink: return chezmoi.SourceFileTypeSymlink case sourceFileTypeModifierClearSymlink: if sourceFileType == chezmoi.SourceFileTypeSymlink { return chezmoi.SourceFileTypeFile } return sourceFileType default: panic(fmt.Sprintf("%d: unknown type modifier", m)) } } // parseModifier parses the modifier from s. func parseModifier(s string) (*modifier, error) { m := &modifier{} for modifierStr := range strings.SplitSeq(s, ",") { modifierStr = strings.TrimSpace(modifierStr) if modifierStr == "" { continue } var bm boolModifier var attribute string switch { case modifierStr[0] == '-': bm = boolModifierClear attribute = modifierStr[1:] case modifierStr[0] == '+': bm = boolModifierSet attribute = modifierStr[1:] case strings.HasPrefix(modifierStr, "no"): bm = boolModifierClear attribute = modifierStr[2:] default: bm = boolModifierSet attribute = modifierStr } switch attribute { case "after", "a": switch bm { case boolModifierClear: m.order = orderModifierClearAfter case boolModifierLeaveUnchanged: m.order = orderModifierLeaveUnchanged case boolModifierSet: m.order = orderModifierSetAfter } case "before", "b": switch bm { case boolModifierClear: m.order = orderModifierClearBefore case boolModifierLeaveUnchanged: m.order = orderModifierLeaveUnchanged case boolModifierSet: m.order = orderModifierSetBefore } case "create": switch bm { case boolModifierClear: m.sourceFileType = sourceFileTypeModifierClearCreate case boolModifierSet: m.sourceFileType = sourceFileTypeModifierSetCreate } case "empty", "e": m.empty = bm case "encrypted": m.encrypted = bm case "exact": m.exact = bm case "executable", "x": m.executable = bm case "external": m.external = bm case "modify": switch bm { case boolModifierClear: m.sourceFileType = sourceFileTypeModifierClearModify case boolModifierSet: m.sourceFileType = sourceFileTypeModifierSetModify } case "once", "o": switch bm { case boolModifierClear: m.condition = conditionModifierClearOnce case boolModifierSet: m.condition = conditionModifierSetOnce } case "onchange": switch bm { case boolModifierClear: m.condition = conditionModifierClearOnChange case boolModifierSet: m.condition = conditionModifierSetOnChange } case "private", "p": m.private = bm case "readonly", "r": m.readOnly = bm case "remove": switch bm { case boolModifierClear: m.remove = bm m.sourceFileType = sourceFileTypeModifierClearRemove case boolModifierSet: m.remove = bm m.sourceFileType = sourceFileTypeModifierSetRemove } case "script": switch bm { case boolModifierClear: m.sourceFileType = sourceFileTypeModifierClearScript case boolModifierSet: m.sourceFileType = sourceFileTypeModifierSetScript } case "symlink": switch bm { case boolModifierClear: m.sourceFileType = sourceFileTypeModifierClearSymlink case boolModifierSet: m.sourceFileType = sourceFileTypeModifierSetSymlink } case "template", "t": m.template = bm default: return nil, fmt.Errorf("%s: unknown attribute", attribute) } } return m, nil } // modifyDirAttr returns the modified value of dirAttr. func (m *modifier) modifyDirAttr(dirAttr chezmoi.DirAttr) chezmoi.DirAttr { return chezmoi.DirAttr{ TargetName: dirAttr.TargetName, Exact: m.exact.modify(dirAttr.Exact), External: m.external.modify(dirAttr.External), Private: m.private.modify(dirAttr.Private), ReadOnly: m.readOnly.modify(dirAttr.ReadOnly), Remove: m.remove.modify(dirAttr.Remove), } } // modifyFileAttr returns the modified value of fileAttr. func (m *modifier) modifyFileAttr(fileAttr chezmoi.FileAttr) chezmoi.FileAttr { switch m.sourceFileType.modify(fileAttr.Type) { case chezmoi.SourceFileTypeFile: return chezmoi.FileAttr{ TargetName: fileAttr.TargetName, Type: chezmoi.SourceFileTypeFile, Empty: m.empty.modify(fileAttr.Empty), Encrypted: m.encrypted.modify(fileAttr.Encrypted), Executable: m.executable.modify(fileAttr.Executable), Private: m.private.modify(fileAttr.Private), ReadOnly: m.readOnly.modify(fileAttr.ReadOnly), Template: m.template.modify(fileAttr.Template), } case chezmoi.SourceFileTypeModify: return chezmoi.FileAttr{ TargetName: fileAttr.TargetName, Type: chezmoi.SourceFileTypeModify, Executable: m.executable.modify(fileAttr.Executable), Private: m.private.modify(fileAttr.Private), ReadOnly: m.readOnly.modify(fileAttr.ReadOnly), Template: m.template.modify(fileAttr.Template), } case chezmoi.SourceFileTypeCreate: return chezmoi.FileAttr{ TargetName: fileAttr.TargetName, Type: chezmoi.SourceFileTypeCreate, Empty: m.encrypted.modify(fileAttr.Empty), Encrypted: m.encrypted.modify(fileAttr.Encrypted), Executable: m.executable.modify(fileAttr.Executable), Private: m.private.modify(fileAttr.Private), ReadOnly: m.readOnly.modify(fileAttr.ReadOnly), Template: m.template.modify(fileAttr.Template), } case chezmoi.SourceFileTypeScript: return chezmoi.FileAttr{ TargetName: fileAttr.TargetName, Type: chezmoi.SourceFileTypeScript, Condition: m.condition.modify(fileAttr.Condition), Order: m.order.modify(fileAttr.Order), } case chezmoi.SourceFileTypeSymlink: return chezmoi.FileAttr{ TargetName: fileAttr.TargetName, Type: chezmoi.SourceFileTypeSymlink, Template: m.template.modify(fileAttr.Template), } case chezmoi.SourceFileTypeRemove: return chezmoi.FileAttr{ TargetName: fileAttr.TargetName, Type: chezmoi.SourceFileTypeRemove, } default: panic(fmt.Sprintf("%d: unknown source file type", fileAttr.Type)) } } ================================================ FILE: internal/cmd/chattrcmd_test.go ================================================ package cmd import ( "fmt" "testing" "github.com/alecthomas/assert/v2" "github.com/spf13/cobra" ) func TestChattrCmdValidArgs(t *testing.T) { for _, tc := range []struct { args []string toComplete string expectedCompletions []string expectedShellCompDirective cobra.ShellCompDirective }{ { toComplete: "a", expectedCompletions: []string{"after"}, expectedShellCompDirective: cobra.ShellCompDirectiveNoFileComp, }, { toComplete: "e", expectedCompletions: []string{"empty", "encrypted", "exact", "executable", "external"}, expectedShellCompDirective: cobra.ShellCompDirectiveNoFileComp, }, { toComplete: "-c", expectedCompletions: []string{"-create"}, expectedShellCompDirective: cobra.ShellCompDirectiveNoFileComp, }, { toComplete: "+o", expectedCompletions: []string{"+once", "+onchange"}, expectedShellCompDirective: cobra.ShellCompDirectiveNoFileComp, }, { toComplete: "nop", expectedCompletions: []string{"noprivate"}, expectedShellCompDirective: cobra.ShellCompDirectiveNoFileComp, }, { toComplete: "empty,s", expectedCompletions: []string{"empty,script", "empty,symlink"}, expectedShellCompDirective: cobra.ShellCompDirectiveNoFileComp, }, } { name := fmt.Sprintf("chattrValidArgs(_, %+v, %q)", tc.args, tc.toComplete) t.Run(name, func(t *testing.T) { c := &Config{} actualCompletions, actualShellCompDirective := c.chattrCmdValidArgs(&cobra.Command{}, tc.args, tc.toComplete) assert.Equal(t, tc.expectedCompletions, actualCompletions) assert.Equal(t, tc.expectedShellCompDirective, actualShellCompDirective) }) } } func TestParseAttrModifier(t *testing.T) { for _, tc := range []struct { s string expected *modifier expectedErr bool }{ { s: "empty", expected: &modifier{ empty: boolModifierSet, }, }, { s: "+empty", expected: &modifier{ empty: boolModifierSet, }, }, { s: "-empty", expected: &modifier{ empty: boolModifierClear, }, }, { s: "noempty", expected: &modifier{ empty: boolModifierClear, }, }, { s: "e", expected: &modifier{ empty: boolModifierSet, }, }, { s: "encrypted", expected: &modifier{ encrypted: boolModifierSet, }, }, { s: "executable", expected: &modifier{ executable: boolModifierSet, }, }, { s: "x", expected: &modifier{ executable: boolModifierSet, }, }, { s: "b", expected: &modifier{ order: orderModifierSetBefore, }, }, { s: "-b", expected: &modifier{ order: orderModifierClearBefore, }, }, { s: "after", expected: &modifier{ order: orderModifierSetAfter, }, }, { s: "noafter", expected: &modifier{ order: orderModifierClearAfter, }, }, { s: "once", expected: &modifier{ condition: conditionModifierSetOnce, }, }, { s: "private", expected: &modifier{ private: boolModifierSet, }, }, { s: "p", expected: &modifier{ private: boolModifierSet, }, }, { s: "template", expected: &modifier{ template: boolModifierSet, }, }, { s: "t", expected: &modifier{ template: boolModifierSet, }, }, { s: "create", expected: &modifier{ sourceFileType: sourceFileTypeModifierSetCreate, }, }, { s: "-create", expected: &modifier{ sourceFileType: sourceFileTypeModifierClearCreate, }, }, { s: "modify", expected: &modifier{ sourceFileType: sourceFileTypeModifierSetModify, }, }, { s: "-script", expected: &modifier{ sourceFileType: sourceFileTypeModifierClearScript, }, }, { s: "+symlink", expected: &modifier{ sourceFileType: sourceFileTypeModifierSetSymlink, }, }, { s: "empty,+executable,noprivate,-t", expected: &modifier{ empty: boolModifierSet, executable: boolModifierSet, private: boolModifierClear, template: boolModifierClear, }, }, { s: " empty , -private, notemplate ", expected: &modifier{ empty: boolModifierSet, private: boolModifierClear, template: boolModifierClear, }, }, { s: "p,,-t", expected: &modifier{ private: boolModifierSet, template: boolModifierClear, }, }, { s: "unknown", expectedErr: true, }, } { t.Run(tc.s, func(t *testing.T) { actual, err := parseModifier(tc.s) if tc.expectedErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tc.expected, actual) } }) } } ================================================ FILE: internal/cmd/choiceflag.go ================================================ package cmd import ( "encoding/json" "errors" "fmt" "maps" "reflect" "slices" "strconv" "strings" "github.com/go-viper/mapstructure/v2" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoiset" ) // A choiceFlag is a flag which accepts a limited set of allowed values. type choiceFlag struct { value string allowedValues chezmoiset.Set[string] uniqueAbbreviations map[string]string } // newChoiceFlag returns a new choiceFlag with the given value and allowed // values. If value is not allowed then it panics. // // If allowedValues is empty then all values are allowed. This functionality, // although counter-intuitive, is required because the allowed values are // carried in the value, not in the type, so a serialization/deserialization // round trip discards the allowed values. To allow deserialization to succeed, // we must allow all values. func newChoiceFlag(value string, allowedValues []string) *choiceFlag { allowedValuesSet := chezmoiset.New(allowedValues...) if !allowedValuesSet.IsEmpty() && !allowedValuesSet.Contains(value) { panic("value not allowed") } return &choiceFlag{ value: value, allowedValues: allowedValuesSet, uniqueAbbreviations: chezmoi.UniqueAbbreviations(allowedValues), } } // FlagCompletionFunc returns f's flag completion function. func (f *choiceFlag) FlagCompletionFunc() func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { return chezmoi.FlagCompletionFunc(slices.Sorted(maps.Keys(f.allowedValues))) } // MarshalJSON implements encoding/json.Marshaler.MarshalJSON. func (f *choiceFlag) MarshalJSON() ([]byte, error) { return []byte(strconv.Quote(f.value)), nil } // MarshalText implements encoding.TextMarshaler.MarshalText. func (f *choiceFlag) MarshalText() ([]byte, error) { return []byte(f.value), nil } // Set implements github.com/spf13/pflag.Value.Set. func (f *choiceFlag) Set(s string) error { // If uniqueAbbreviations is nil then all values are allowed. This // functionality, although counter-intuitive, is required because the unique // abbreviations are carried in the value, not in the type, so a // serialization/deserialization round trip discards the unique // abbreviations. To allow deserialization to succeed, we must allow all // values. if f.uniqueAbbreviations == nil { f.value = s return nil } value, ok := f.uniqueAbbreviations[s] if !ok { return errors.New("invalid value") } f.value = value return nil } func (f *choiceFlag) String() string { return f.value } // Type implements github.com/spf13/pflag.Value.Type. func (f *choiceFlag) Type() string { sortedKeys := slices.Sorted(maps.Keys(f.allowedValues)) if len(sortedKeys) > 0 && sortedKeys[0] == "" { sortedKeys[0] = "" } return strings.Join(sortedKeys, "|") } // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON. func (f *choiceFlag) UnmarshalJSON(data []byte) error { var value string if err := json.Unmarshal(data, &value); err != nil { return err } if !f.allowedValues.IsEmpty() && !f.allowedValues.Contains(value) { return fmt.Errorf("%s: invalid value", value) } f.value = value return nil } // UnmarshalText implements encoding.TextUnmarshaler.UnmarshalText. func (f *choiceFlag) UnmarshalText(text []byte) error { value := string(text) if !f.allowedValues.IsEmpty() && !f.allowedValues.Contains(value) { return fmt.Errorf("%s: invalid value", value) } f.value = value return nil } // StringToChoiceFlagHookFunc is a // github.com/go-viper/mapstructure/v2.DecodeHookFunc that parses a choiceFlag // from a string. // // Unfortunately, we only receive the type of the value that we're decoding // into, not its value, so we do not have access to the set of allowed values. // So, we have to return a choiceFlag that allows all values. func StringToChoiceFlagHookFunc() mapstructure.DecodeHookFunc { return func(from, to reflect.Type, data any) (any, error) { if to != reflect.TypeFor[choiceFlag]() { return data, nil } var cf choiceFlag switch data := data.(type) { case string: cf.value = data default: return nil, fmt.Errorf("expected a string, got a %T", data) } return cf, nil } } ================================================ FILE: internal/cmd/cmd.go ================================================ // Package cmd contains chezmoi's commands. package cmd import ( "errors" "fmt" "log/slog" "os" "os/exec" "regexp" "runtime/debug" "strconv" "strings" "github.com/spf13/cobra" "github.com/spf13/pflag" bbolterrors "go.etcd.io/bbolt/errors" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoierrors" "chezmoi.io/chezmoi/internal/chezmoiset" ) const readSourceStateHookName = "read-source-state" var ( noArgs = []string(nil) deDuplicateErrorRx = regexp.MustCompile(`:\s+`) chezmoiDev = make(map[string]string) ) // A VersionInfo contains a version. type VersionInfo struct { Version string Commit string Date string BuiltBy string } func (v VersionInfo) LogValue() slog.Value { return slog.GroupValue( slog.String("version", v.Version), slog.String("commit", v.Commit), slog.String("date", v.Date), slog.String("builtBy", v.BuiltBy), ) } // Main runs chezmoi and returns an exit code. func Main(versionInfo VersionInfo, args []string) int { for pair := range strings.SplitSeq(os.Getenv("CHEZMOIDEV"), ",") { key, value, _ := strings.Cut(pair, "=") chezmoiDev[key] = value } if err := runMain(versionInfo, args); err != nil { if errExitCode := chezmoi.ExitCodeError(0); errors.As(err, &errExitCode) { return int(errExitCode) } fmt.Fprintf(os.Stderr, "chezmoi: %s\n", deDuplicateError(err)) return 1 } return 0 } // deDuplicateError returns err's human-readable string with duplicate // components removed. func deDuplicateError(err error) string { components := deDuplicateErrorRx.Split(err.Error(), -1) seenComponents := chezmoiset.NewWithCapacity[string](len(components)) uniqueComponents := make([]string, 0, len(components)) for _, component := range components { if seenComponents.Contains(component) { continue } uniqueComponents = append(uniqueComponents, component) seenComponents.Add(component) } return strings.Join(uniqueComponents, ": ") } // example returns command's example. func example(command string) string { help, ok := helps[command] if !ok { return "" } return help.example } // markFlagsRequired marks all of flags as required for cmd. func markFlagsRequired(cmd *cobra.Command, flags ...string) { for _, flag := range flags { must(cmd.MarkFlagRequired(flag)) } } // must panics if err is not nil. func must(err error) { if err != nil { panic(err) } } // mustValue panics if err is not nil, otherwise it returns value. func mustValue[T any](value T, err error) T { if err != nil { panic(err) } return value } // mustValues panics if err is not nil, otherwise it returns value1 and value2. func mustValues[T1, T2 any](value1 T1, value2 T2, err error) (T1, T2) { if err != nil { panic(err) } return value1, value2 } // mustLongHelp returns the long help for command or panics if no long help // exists, unless ignorehelp=1 is set in the CHEZMOIDEV environment variable. func mustLongHelp(command string) string { help, ok := helps[command] if chezmoiDev["ignorehelp"] != "1" && (!ok || strings.TrimSpace(help.longHelp) == "") { panic(command + ": missing long help") } return "Description\n" + help.longHelp } // ensureHasGroupID ensures that cmd has a GroupID set, unless it has the // explicit "hidden" annotation. func ensureHasGroupID(cmd *cobra.Command) { if cmd.GroupID == "" && !cmd.Hidden { panic(cmd.Name() + ": missing group ID") } } // ensureAllFlagsDocumented ensures that all flags are documented, unless // ignoreflags=1 is set in the CHEZMOIDEV environment variable. func ensureAllFlagsDocumented(cmd *cobra.Command, persistentFlags *pflag.FlagSet) { if chezmoiDev["ignoreflags"] == "1" { return } cmdName := cmd.Name() help, ok := helps[cmdName] if !ok && !cmd.Flags().HasFlags() { return } if !ok { panic(cmdName + ": missing flags") } // Check if all flags are documented. cmd.Flags().VisitAll(func(flag *pflag.Flag) { if _, ok := help.longFlags[flag.Name]; !ok { panic(fmt.Sprintf("%s: undocumented long flag --%s", cmdName, flag.Name)) } if flag.Shorthand != "" { if _, ok := help.shortFlags[flag.Shorthand]; !ok { panic(fmt.Sprintf("%s: undocumented short flag -%s", cmdName, flag.Shorthand)) } } }) // Check if all documented flags exist. for flag := range help.longFlags { if cmd.Flags().Lookup(flag) == nil && persistentFlags.Lookup(flag) == nil { panic(fmt.Sprintf("%s: flag --%s documented but not implemented", cmdName, flag)) } } for flag := range help.shortFlags { if cmd.Flags().ShorthandLookup(flag) == nil && persistentFlags.ShorthandLookup(flag) == nil { panic(fmt.Sprintf("%s: flag -%s documented but not implemented", cmdName, flag)) } } } // runMain runs chezmoi's main function. func runMain(versionInfo VersionInfo, args []string) (err error) { if versionInfo.Commit == "" || versionInfo.Date == "" { if buildInfo, ok := debug.ReadBuildInfo(); ok { var vcs, vcsRevision, vcsTime, vcsModified string for _, setting := range buildInfo.Settings { switch setting.Key { case "vcs": vcs = setting.Value case "vcs.revision": vcsRevision = setting.Value case "vcs.time": vcsTime = setting.Value case "vcs.modified": vcsModified = setting.Value } } if versionInfo.Commit == "" && vcs == "git" { versionInfo.Commit = vcsRevision if modified, err := strconv.ParseBool(vcsModified); err == nil && modified { versionInfo.Commit += "-dirty" } } if versionInfo.Date == "" { versionInfo.Date = vcsTime } } } var config *Config if config, err = newConfig( withVersionInfo(versionInfo), ); err != nil { return err } defer chezmoierrors.CombineFunc(&err, config.Close) switch err = config.execute(args); { case errors.Is(err, bbolterrors.ErrTimeout): // Translate bbolt timeout errors into a friendlier message. As the // persistent state is opened lazily, this error could occur at any // time, so it's easiest to intercept it here. return errors.New("timeout obtaining persistent state lock, is another instance of chezmoi running?") case err != nil && strings.Contains(err.Error(), "unknown command") && len(args) > 0: // If the command is unknown then look for a plugin. if name, lookPathErr := exec.LookPath("chezmoi-" + args[0]); lookPathErr == nil { // The following is a bit of a hack, as cobra does not have a way to // call a function if a command is not found. We need to run the // pre- and post- run commands to set up the environment, so we // create a fake cobra.Command that corresponds to the name of the // plugin. cmd := &cobra.Command{ Use: args[0], Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeEmpty, runsCommands, ), } if err := config.persistentPreRunRootE(cmd, args[1:]); err != nil { return err } pluginCmd := exec.Command(name, args[1:]...) pluginCmd.Stdin = os.Stdin pluginCmd.Stdout = os.Stdout pluginCmd.Stderr = os.Stderr err = config.run("", name, args[1:]) if persistentPostRunRootEErr := config.persistentPostRunRootE(cmd, args[1:]); persistentPostRunRootEErr != nil { err = chezmoierrors.Combine(err, persistentPostRunRootEErr) } } } return err } ================================================ FILE: internal/cmd/cmd_test.go ================================================ package cmd import ( "errors" "strconv" "testing" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoitest" ) func init() { // chezmoi.io/chezmoi/internal/chezmoi reads the umask before // chezmoi.io/chezmoi/internal/chezmoitest sets it, so update it. chezmoi.Umask = chezmoitest.Umask } func TestDeDuplicateError(t *testing.T) { for i, tc := range []struct { errStr string expected string }{ { errStr: "", expected: "", }, { errStr: "a", expected: "a", }, { errStr: "a: a", expected: "a", }, { errStr: "a: b", expected: "a: b", }, { errStr: "a: a: b", //nolint:dupword expected: "a: b", }, { errStr: "a: b: b", expected: "a: b", }, { errStr: "a: b: c: b: a: d", expected: "a: b: c: d", }, { errStr: "a: b: a: b: c", expected: "a: b: c", }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { actual := deDuplicateError(errors.New(tc.errStr)) assert.Equal(t, tc.expected, actual) }) } } func TestMustGetLongHelpPanics(t *testing.T) { assert.Panics(t, func() { mustLongHelp("non-existent-command") }) } ================================================ FILE: internal/cmd/completioncmd.go ================================================ package cmd import ( "fmt" "strings" "github.com/spf13/cobra" ) type completionCmdConfig struct { Custom bool `json:"custom" mapstructure:"custom" yaml:"custom"` } func (c *Config) newCompletionCmd() *cobra.Command { completionCmd := &cobra.Command{ GroupID: groupIDInternal, Use: "completion shell", Short: "Generate shell completion code", Args: cobra.ExactArgs(1), ValidArgs: []string{"bash", "fish", "powershell", "zsh"}, Long: mustLongHelp("completion"), Example: example("completion"), RunE: c.runCompletionCmd, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeNone, ), } return completionCmd } func (c *Config) runCompletionCmd(cmd *cobra.Command, args []string) error { completion, err := completion(cmd, args[0]) if err != nil { return err } return c.writeOutputString(completion, 0o666) } func completion(cmd *cobra.Command, shell string) (string, error) { builder := strings.Builder{} builder.Grow(16384) switch shell { case "bash": includeDesc := true if err := cmd.Root().GenBashCompletionV2(&builder, includeDesc); err != nil { return "", err } case "fish": includeDesc := true if err := cmd.Root().GenFishCompletion(&builder, includeDesc); err != nil { return "", err } case "powershell": if err := cmd.Root().GenPowerShellCompletionWithDesc(&builder); err != nil { return "", err } case "zsh": if err := cmd.Root().GenZshCompletion(&builder); err != nil { return "", err } default: return "", fmt.Errorf("%s: unsupported shell", shell) } return builder.String(), nil } ================================================ FILE: internal/cmd/config.go ================================================ package cmd import ( "bufio" "bytes" "cmp" "context" "crypto/sha256" "encoding/json" "errors" "fmt" "io" "io/fs" "log/slog" "maps" "net/http" "os" "os/exec" "os/user" "path" "path/filepath" "reflect" "regexp" "runtime" "slices" "strconv" "strings" "text/template" "time" "github.com/Masterminds/sprig/v3" "github.com/coreos/go-semver/semver" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/format/diff" "github.com/go-viper/mapstructure/v2" "github.com/gregjones/httpcache" "github.com/gregjones/httpcache/diskcache" "github.com/muesli/termenv" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/twpayne/go-shell" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-xdg/v6" "github.com/zricethezav/gitleaks/v8/detect" "golang.org/x/term" "mvdan.cc/sh/v3/expand" "mvdan.cc/sh/v3/syntax" "chezmoi.io/chezmoi/assets/templates" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoierrors" "chezmoi.io/chezmoi/internal/chezmoigit" "chezmoi.io/chezmoi/internal/chezmoilog" "chezmoi.io/chezmoi/internal/chezmoiset" ) // defaultSentinel is a string value used to indicate that the default value // should be used. It is a string unlikely to be an actual value set by the // user. const defaultSentinel = "\x00" const ( logComponentKey = "component" logComponentValueEncryption = "encryption" logComponentValuePersistentState = "persistentState" logComponentValueSourceState = "sourceState" logComponentValueSystem = "system" ) const ( groupIDAdvanced = "advanced" groupIDDaily = "daily" groupIDDocumentation = "documentation" groupIDEncryption = "encryption" groupIDInternal = "internal" groupIDMigration = "migration" groupIDRemote = "remote" groupIDTemplate = "template" ) var groups = []*cobra.Group{ {ID: groupIDDocumentation, Title: "Documentation commands:"}, {ID: groupIDDaily, Title: "Daily commands:"}, {ID: groupIDTemplate, Title: "Template commands:"}, {ID: groupIDAdvanced, Title: "Advanced commands:"}, {ID: groupIDEncryption, Title: "Encryption commands:"}, {ID: groupIDRemote, Title: "Remote commands:"}, {ID: groupIDMigration, Title: "Migration commands:"}, {ID: groupIDInternal, Title: "Internal commands:"}, } type doPurgeOptions struct { binary bool cache bool config bool persistentState bool sourceDir bool workingTree bool } type commandConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Script string `json:"script" mapstructure:"script" yaml:"script"` Args []string `json:"args" mapstructure:"args" yaml:"args"` } type hookConfig struct { Pre commandConfig `json:"pre" mapstructure:"pre" yaml:"pre"` Post commandConfig `json:"post" mapstructure:"post" yaml:"post"` } type templateConfig struct { Options []string `json:"options" mapstructure:"options" yaml:"options"` } type warningsConfig struct { ConfigFileTemplateHasChanged bool `json:"configFileTemplateHasChanged" mapstructure:"configFileTemplateHasChanged" yaml:"configFileTemplateHasChanged"` } // ConfigFile contains all data settable in the config file. type ConfigFile struct { // Global configuration. CacheDirAbsPath chezmoi.AbsPath `json:"cacheDir" mapstructure:"cacheDir" yaml:"cacheDir"` Color autoBool `json:"color" mapstructure:"color" yaml:"color"` Data map[string]any `json:"data" mapstructure:"data" yaml:"data"` Env map[string]string `json:"env" mapstructure:"env" yaml:"env"` Format *choiceFlag `json:"format" mapstructure:"format" yaml:"format"` DestDirAbsPath chezmoi.AbsPath `json:"destDir" mapstructure:"destDir" yaml:"destDir"` GitHub gitHubConfig `json:"gitHub" mapstructure:"gitHub" yaml:"gitHub"` Hooks map[string]hookConfig `json:"hooks" mapstructure:"hooks" yaml:"hooks"` Interactive bool `json:"interactive" mapstructure:"interactive" yaml:"interactive"` Interpreters map[string]chezmoi.Interpreter `json:"interpreters" mapstructure:"interpreters" yaml:"interpreters"` LessInteractive bool `json:"lessInteractive" mapstructure:"lessInteractive" yaml:"lessInteractive"` Mode chezmoi.Mode `json:"mode" mapstructure:"mode" yaml:"mode"` Pager string `json:"pager" mapstructure:"pager" yaml:"pager"` PagerArgs []string `json:"pagerArgs" mapstructure:"pagerArgs" yaml:"pagerArgs"` PersistentStateAbsPath chezmoi.AbsPath `json:"persistentState" mapstructure:"persistentState" yaml:"persistentState"` PINEntry pinEntryConfig `json:"pinentry" mapstructure:"pinentry" yaml:"pinentry"` Progress autoBool `json:"progress" mapstructure:"progress" yaml:"progress"` Safe bool `json:"safe" mapstructure:"safe" yaml:"safe"` ScriptEnv map[string]string `json:"scriptEnv" mapstructure:"scriptEnv" yaml:"scriptEnv"` ScriptTempDir chezmoi.AbsPath `json:"scriptTempDir" mapstructure:"scriptTempDir" yaml:"scriptTempDir"` SourceDirAbsPath chezmoi.AbsPath `json:"sourceDir" mapstructure:"sourceDir" yaml:"sourceDir"` TempDir chezmoi.AbsPath `json:"tempDir" mapstructure:"tempDir" yaml:"tempDir"` Template templateConfig `json:"template" mapstructure:"template" yaml:"template"` TextConv textConv `json:"textConv" mapstructure:"textConv" yaml:"textConv"` Umask fs.FileMode `json:"umask" mapstructure:"umask" yaml:"umask"` UseBuiltinAge autoBool `json:"useBuiltinAge" mapstructure:"useBuiltinAge" yaml:"useBuiltinAge"` UseBuiltinGit autoBool `json:"useBuiltinGit" mapstructure:"useBuiltinGit" yaml:"useBuiltinGit"` Verbose bool `json:"verbose" mapstructure:"verbose" yaml:"verbose"` Warnings warningsConfig `json:"warnings" mapstructure:"warnings" yaml:"warnings"` WorkingTreeAbsPath chezmoi.AbsPath `json:"workingTree" mapstructure:"workingTree" yaml:"workingTree"` // Password manager configurations. AWSSecretsManager awsSecretsManagerConfig `json:"awsSecretsManager" mapstructure:"awsSecretsManager" yaml:"awsSecretsManager"` AzureKeyVault azureKeyVaultConfig `json:"azureKeyVault" mapstructure:"azureKeyVault" yaml:"azureKeyVault"` Bitwarden bitwardenConfig `json:"bitwarden" mapstructure:"bitwarden" yaml:"bitwarden"` BitwardenSecrets bitwardenSecretsConfig `json:"bitwardenSecrets" mapstructure:"bitwardenSecrets" yaml:"bitwardenSecrets"` Dashlane dashlaneConfig `json:"dashlane" mapstructure:"dashlane" yaml:"dashlane"` Doppler dopplerConfig `json:"doppler" mapstructure:"doppler" yaml:"doppler"` Ejson ejsonConfig `json:"ejson" mapstructure:"ejson" yaml:"ejson"` Gopass gopassConfig `json:"gopass" mapstructure:"gopass" yaml:"gopass"` Keepassxc keepassxcConfig `json:"keepassxc" mapstructure:"keepassxc" yaml:"keepassxc"` Keeper keeperConfig `json:"keeper" mapstructure:"keeper" yaml:"keeper"` Lastpass lastpassConfig `json:"lastpass" mapstructure:"lastpass" yaml:"lastpass"` Onepassword onepasswordConfig `json:"onepassword" mapstructure:"onepassword" yaml:"onepassword"` Pass passConfig `json:"pass" mapstructure:"pass" yaml:"pass"` Passhole passholeConfig `json:"passhole" mapstructure:"passhole" yaml:"passhole"` ProtonPass protonPassConfig `json:"protonPass" mapstructure:"protonPass" yaml:"protonPass"` RBW rbwConfig `json:"rbw" mapstructure:"rbw" yaml:"rbw"` Secret secretConfig `json:"secret" mapstructure:"secret" yaml:"secret"` Vault vaultConfig `json:"vault" mapstructure:"vault" yaml:"vault"` // Encryption configurations. Encryption string `json:"encryption" mapstructure:"encryption" yaml:"encryption"` Age chezmoi.AgeEncryption `json:"age" mapstructure:"age" yaml:"age"` GPG chezmoi.GPGEncryption `json:"gpg" mapstructure:"gpg" yaml:"gpg"` // Command configurations. Add addCmdConfig `json:"add" mapstructure:"add" yaml:"add"` CD cdCmdConfig `json:"cd" mapstructure:"cd" yaml:"cd"` Completion completionCmdConfig `json:"completion" mapstructure:"completion" yaml:"completion"` Docker dockerCmdConfig `json:"docker" mapstructure:"docker" yaml:"docker"` Diff diffCmdConfig `json:"diff" mapstructure:"diff" yaml:"diff"` Edit editCmdConfig `json:"edit" mapstructure:"edit" yaml:"edit"` Git gitCmdConfig `json:"git" mapstructure:"git" yaml:"git"` Merge mergeCmdConfig `json:"merge" mapstructure:"merge" yaml:"merge"` Status statusCmdConfig `json:"status" mapstructure:"status" yaml:"status"` Update updateCmdConfig `json:"update" mapstructure:"update" yaml:"update"` Verify verifyCmdConfig `json:"verify" mapstructure:"verify" yaml:"verify"` } // A Config represents a configuration. type Config struct { ConfigFile // Global configuration. ageRecipient string ageRecipientFile string configFormat *choiceFlag debug bool dryRun bool force bool homeDir string keepGoing bool noPager bool noTTY bool outputAbsPath chezmoi.AbsPath refreshExternals chezmoi.RefreshExternals sourcePath bool templateFuncs template.FuncMap useBuiltinDiff bool // Password manager data. gitHub gitHubData keyring keyringData // Command configurations, not settable in the config file. age ageCmdConfig ageKeygen ageKeygenCmdConfig apply applyCmdConfig archive archiveCmdConfig chattr chattrCmdConfig data dataCmdConfig destroy destroyCmdConfig doctor doctorCmdConfig dump dumpCmdConfig dumpConfig dumpConfigCmdConfig executeTemplate executeTemplateCmdConfig generate generateCmdConfig ignored ignoredCmdConfig _import importCmdConfig init initCmdConfig managed managedCmdConfig mergeAll mergeAllCmdConfig ssh sshCmdConfig purge purgeCmdConfig reAdd reAddCmdConfig secret secretCmdConfig state stateCmdConfig unmanaged unmanagedCmdConfig upgrade upgradeCmdConfig // Common configuration. interactiveTemplateFuncs interactiveTemplateFuncsConfig overrideData string overrideDataFileAbsPath chezmoi.AbsPath // Version information. version semver.Version versionInfo VersionInfo versionStr string // Configuration. fileSystem vfs.FS bds *xdg.BaseDirectorySpecification defaultConfigFileAbsPath chezmoi.AbsPath defaultConfigFileAbsPathErr error customConfigFileAbsPath chezmoi.AbsPath baseSystem chezmoi.System sourceSystem chezmoi.System destSystem chezmoi.System persistentState chezmoi.PersistentState httpClient *http.Client logger *slog.Logger // Computed configuration. commandDirAbsPath chezmoi.AbsPath homeDirAbsPath chezmoi.AbsPath encryption chezmoi.Encryption sourceDirAbsPath chezmoi.AbsPath sourceDirAbsPathErr error sourceState *chezmoi.SourceState sourceStateErr error templateData *templateData gitleaksDetector *detect.Detector gitleaksDetectorErr error stdin io.Reader stdout io.Writer stderr io.Writer bufioReader *bufio.Reader diffPagerCmdStdin io.WriteCloser diffPagerCmd *exec.Cmd tempDirs map[string]chezmoi.AbsPath ioregData ioregData restoreWindowsConsole func() error } type templateData struct { arch string args []string cacheDir chezmoi.AbsPath command string commandDir chezmoi.AbsPath config map[string]any configFile chezmoi.AbsPath destDir chezmoi.AbsPath executable chezmoi.AbsPath fqdnHostname string gid string group string homeDir chezmoi.AbsPath hostname string kernel map[string]any os string osRelease map[string]any pathListSeparator string pathSeparator string sourceDir chezmoi.AbsPath uid string username string version map[string]any windowsVersion map[string]any workingTree chezmoi.AbsPath } // A configOption sets and option on a Config. type configOption func(*Config) error type configState struct { ConfigTemplateContentsSHA256 chezmoi.HexBytes `json:"configTemplateContentsSHA256" yaml:"configTemplateContentsSHA256"` //nolint:tagliatelle } var ( chezmoiRelPath = chezmoi.NewRelPath("chezmoi") persistentStateFileRelPath = chezmoi.NewRelPath("chezmoistate.boltdb") httpCacheDirRelPath = chezmoi.NewRelPath("httpcache") configStateKey = []byte("configState") defaultAgeEncryptionConfig = chezmoi.AgeEncryption{ Command: "age", Suffix: ".age", } defaultGPGEncryptionConfig = chezmoi.GPGEncryption{ Command: "gpg", Suffix: ".asc", } whitespaceRx = regexp.MustCompile(`\s+`) commonFlagCompletionFuncs = map[string]func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective){ "exclude": chezmoi.EntryTypeSetFlagCompletionFunc, "include": chezmoi.EntryTypeSetFlagCompletionFunc, } ) // newConfig creates a new Config with the given options. func newConfig(options ...configOption) (*Config, error) { userHomeDir, err := chezmoi.UserHomeDir() if err != nil { return nil, err } homeDirAbsPath, err := chezmoi.NormalizePath(userHomeDir) if err != nil { return nil, err } bds, err := xdg.NewBaseDirectorySpecification() if err != nil { return nil, err } logger := slog.Default() c := &Config{ ConfigFile: newConfigFile(bds), // Global configuration. configFormat: newChoiceFlag("", readDataFormatValues), homeDir: userHomeDir, templateFuncs: sprig.TxtFuncMap(), // Command configurations. apply: applyCmdConfig{ filter: chezmoi.NewEntryTypeFilter(chezmoi.EntryTypesAll, chezmoi.EntryTypesNone), recursive: true, }, archive: archiveCmdConfig{ filter: chezmoi.NewEntryTypeFilter(chezmoi.EntryTypesAll, chezmoi.EntryTypesNone), format: newChoiceFlag("tar.gz", archiveFormatValues), recursive: true, }, data: dataCmdConfig{ format: newChoiceFlag("", writeDataFormatValues), }, dump: dumpCmdConfig{ filter: chezmoi.NewEntryTypeFilter(chezmoi.EntryTypesAll, chezmoi.EntryTypesNone), format: newChoiceFlag("", writeDataFormatValues), recursive: true, }, dumpConfig: dumpConfigCmdConfig{ format: newChoiceFlag("", writeDataFormatValues), }, executeTemplate: executeTemplateCmdConfig{ stdinIsATTY: true, }, generate: generateCmdConfig{ installInitShellSh: generateInstallInitShellShCmdConfig{ interactive: true, shell: true, }, }, _import: importCmdConfig{ destination: homeDirAbsPath, filter: chezmoi.NewEntryTypeFilter(chezmoi.EntryTypesAll, chezmoi.EntryTypesNone), }, init: initCmdConfig{ data: true, filter: chezmoi.NewEntryTypeFilter(chezmoi.EntryTypesAll, chezmoi.EntryTypesNone), guessRepoURL: true, recurseSubmodules: true, }, managed: managedCmdConfig{ filter: chezmoi.NewEntryTypeFilter(chezmoi.EntryTypesAll, chezmoi.EntryTypesNone), format: newChoiceFlag(formatJSON, writeDataFormatValues), pathStyle: newChoiceFlag(pathStyleRelative, sourceOrTargetPathStyleValues), }, mergeAll: mergeAllCmdConfig{ recursive: true, }, reAdd: reAddCmdConfig{ filter: chezmoi.NewEntryTypeFilter(chezmoi.EntryTypesAll, chezmoi.EntryTypesNone), recursive: true, }, ssh: sshCmdConfig{ shell: true, }, state: stateCmdConfig{ data: stateDataCmdConfig{ format: newChoiceFlag(formatJSON, writeDataFormatValues), }, dump: stateDumpCmdConfig{ format: newChoiceFlag(formatJSON, writeDataFormatValues), }, getBucket: stateGetBucketCmdConfig{ format: newChoiceFlag(formatJSON, writeDataFormatValues), }, }, unmanaged: unmanagedCmdConfig{ filter: chezmoi.NewEntryTypeFilter(chezmoi.EntryTypesAll, chezmoi.EntryTypesNone), pathStyle: newChoiceFlag(pathStyleRelative, targetPathStyleValues), }, // Configuration. fileSystem: vfs.OSFS, bds: bds, logger: logger, // Computed configuration. homeDirAbsPath: homeDirAbsPath, tempDirs: make(map[string]chezmoi.AbsPath), stdin: os.Stdin, stdout: os.Stdout, stderr: os.Stderr, } // Override sprig template functions. Delete them from the template function // map first to avoid a duplicate function panic. for _, templateFunc := range []string{ "fromJson", "quote", "splitList", "squote", "toPrettyJson", "toString", "toStrings", } { if _, ok := c.templateFuncs[templateFunc]; !ok { panic(templateFunc + ": deleting non-existent template function") } delete(c.templateFuncs, templateFunc) } // The completion template function is added in persistentPreRunRootE as // it needs a *cobra.Command, which we don't yet have. for key, value := range map[string]any{ "abortEmpty": chezmoi.AbortEmptyTemplateFunc, "awsSecretsManager": c.awsSecretsManagerTemplateFunc, "awsSecretsManagerRaw": c.awsSecretsManagerRawTemplateFunc, "azureKeyVault": c.azureKeyVaultTemplateFunc, "bitwarden": c.bitwardenTemplateFunc, "bitwardenAttachment": c.bitwardenAttachmentTemplateFunc, "bitwardenAttachmentByRef": c.bitwardenAttachmentByRefTemplateFunc, "bitwardenFields": c.bitwardenFieldsTemplateFunc, "bitwardenSecrets": c.bitwardenSecretsTemplateFunc, "comment": c.commentTemplateFunc, "dashlaneNote": c.dashlaneNoteTemplateFunc, "dashlanePassword": c.dashlanePasswordTemplateFunc, "decrypt": c.decryptTemplateFunc, "deleteValueAtPath": c.deleteValueAtPathTemplateFunc, "doppler": c.dopplerTemplateFunc, "dopplerProjectJson": c.dopplerProjectJSONTemplateFunc, "ejsonDecrypt": c.ejsonDecryptTemplateFunc, "ejsonDecryptWithKey": c.ejsonDecryptWithKeyTemplateFunc, "encrypt": c.encryptTemplateFunc, "ensureLinePrefix": c.ensureLinePrefixTemplateFunc, "eqFold": c.eqFoldTemplateFunc, "exec": c.execTemplateFunc, "findExecutable": c.findExecutableTemplateFunc, "findOneExecutable": c.findOneExecutableTemplateFunc, "fromIni": c.fromIniTemplateFunc, "fromJson": c.fromJsonTemplateFunc, "fromJsonc": c.fromJsoncTemplateFunc, "fromToml": c.fromTomlTemplateFunc, "fromYaml": c.fromYamlTemplateFunc, "getRedirectedURL": c.getRedirectedURLTemplateFunc, "gitHubKeys": c.gitHubKeysTemplateFunc, "gitHubLatestRelease": c.gitHubLatestReleaseTemplateFunc, "gitHubLatestReleaseAssetURL": c.gitHubLatestReleaseAssetURLTemplateFunc, "gitHubLatestTag": c.gitHubLatestTagTemplateFunc, "gitHubRelease": c.gitHubReleaseTemplateFunc, "gitHubReleaseAssetURL": c.gitHubReleaseAssetURLTemplateFunc, "gitHubReleases": c.gitHubReleasesTemplateFunc, "gitHubTags": c.gitHubTagsTemplateFunc, "glob": c.globTemplateFunc, "gopass": c.gopassTemplateFunc, "gopassRaw": c.gopassRawTemplateFunc, "hexDecode": c.hexDecodeTemplateFunc, "hexEncode": c.hexEncodeTemplateFunc, "include": c.includeTemplateFunc, "includeTemplate": c.includeTemplateTemplateFunc, "ioreg": c.ioregTemplateFunc, "isExecutable": c.isExecutableTemplateFunc, "joinPath": c.joinPathTemplateFunc, "jq": c.jqTemplateFunc, "keepassxc": c.keepassxcTemplateFunc, "keepassxcAttachment": c.keepassxcAttachmentTemplateFunc, "keepassxcAttribute": c.keepassxcAttributeTemplateFunc, "keeper": c.keeperTemplateFunc, "keeperDataFields": c.keeperDataFieldsTemplateFunc, "keeperFindPassword": c.keeperFindPasswordTemplateFunc, "keyring": c.keyringTemplateFunc, "lastpass": c.lastpassTemplateFunc, "lastpassRaw": c.lastpassRawTemplateFunc, "lookPath": c.lookPathTemplateFunc, "lstat": c.lstatTemplateFunc, "mozillaInstallHash": c.mozillaInstallHashTemplateFunc, "onepassword": c.onepasswordTemplateFunc, "onepasswordDetailsFields": c.onepasswordDetailsFieldsTemplateFunc, "onepasswordDocument": c.onepasswordDocumentTemplateFunc, "onepasswordItemFields": c.onepasswordItemFieldsTemplateFunc, "onepasswordRead": c.onepasswordReadTemplateFunc, "output": c.outputTemplateFunc, "outputList": c.outputListTemplateFunc, "pass": c.passTemplateFunc, "passFields": c.passFieldsTemplateFunc, "passRaw": c.passRawTemplateFunc, "passhole": c.passholeTemplateFunc, "protonPass": c.protonPassTemplateFunc, "protonPassJSON": c.protonPassJSONTemplateFunc, "pruneEmptyDicts": c.pruneEmptyDictsTemplateFunc, "quote": c.quoteTemplateFunc, "quoteList": c.quoteListTemplateFunc, "rbw": c.rbwTemplateFunc, "rbwFields": c.rbwFieldsTemplateFunc, "replaceAllRegex": c.replaceAllRegexTemplateFunc, "secret": c.secretTemplateFunc, "secretJSON": c.secretJSONTemplateFunc, "setValueAtPath": c.setValueAtPathTemplateFunc, "splitList": c.splitListTemplateFunc, "squote": c.squoteTemplateFunc, "stat": c.statTemplateFunc, "toIni": c.toIniTemplateFunc, "toPrettyJson": c.toPrettyJsonTemplateFunc, "toString": c.toStringTemplateFunc, "toStrings": c.toStringsTemplateFunc, "toToml": c.toTomlTemplateFunc, "toYaml": c.toYamlTemplateFunc, "vault": c.vaultTemplateFunc, "warnf": c.warnfTemplateFunc, } { c.addTemplateFunc(key, value) } for _, option := range options { if err := option(c); err != nil { return nil, err } } wd, err := os.Getwd() if err != nil { return nil, err } c.commandDirAbsPath, err = chezmoi.NormalizePath(wd) if err != nil { return nil, err } c.homeDirAbsPath, err = chezmoi.NormalizePath(c.homeDir) if err != nil { return nil, err } c.defaultConfigFileAbsPath, c.defaultConfigFileAbsPathErr = c.defaultConfigFile(c.fileSystem, c.bds) c.SourceDirAbsPath, err = c.defaultSourceDir(c.fileSystem, c.bds) if err != nil { return nil, err } c.DestDirAbsPath = c.homeDirAbsPath c._import.destination = c.homeDirAbsPath return c, nil } // Close closes resources associated with c. func (c *Config) Close() error { errs := make([]error, 0, len(c.tempDirs)) for _, tempDirAbsPath := range c.tempDirs { err := os.RemoveAll(tempDirAbsPath.String()) chezmoilog.InfoOrError(c.logger, "RemoveAll", err, chezmoilog.Stringer("tempDir", tempDirAbsPath), ) errs = append(errs, err) } return chezmoierrors.Combine(errs...) } // addTemplateFunc adds the template function with the given key and value // to c. It panics if there is already an existing template function with the // same key. func (c *Config) addTemplateFunc(key string, value any) { if _, ok := c.templateFuncs[key]; ok { panic(key + ": already defined") } c.templateFuncs[key] = value } type applyArgsOptions struct { cmd *cobra.Command filter *chezmoi.EntryTypeFilter init bool parentDirs bool recursive bool umask fs.FileMode preApplyFunc chezmoi.PreApplyFunc } // applyArgs is the core of all commands that make changes to a target system. // It checks config file freshness, reads the source state, and then applies the // source state for each target entry in args. If args is empty then the source // state is applied to all target entries. func (c *Config) applyArgs( ctx context.Context, targetSystem chezmoi.System, targetDirAbsPath chezmoi.AbsPath, args []string, options applyArgsOptions, ) error { if options.init { if err := c.createAndReloadConfigFile(options.cmd); err != nil { return err } } var currentConfigTemplateContentsSHA256 []byte configTemplate, err := c.findConfigTemplate() if err != nil { return err } if configTemplate != nil { currentConfigTemplateContentsSHA256 = sha256Sum(configTemplate.contents) } var previousConfigTemplateContentsSHA256 []byte if configStateData, err := c.persistentState.Get(chezmoi.ConfigStateBucket, configStateKey); err != nil { return err } else if configStateData != nil { var configState configState if err := json.Unmarshal(configStateData, &configState); err != nil { return err } previousConfigTemplateContentsSHA256 = []byte(configState.ConfigTemplateContentsSHA256) } configTemplatesEmpty := currentConfigTemplateContentsSHA256 == nil && previousConfigTemplateContentsSHA256 == nil configTemplateContentsUnchanged := configTemplatesEmpty || bytes.Equal(currentConfigTemplateContentsSHA256, previousConfigTemplateContentsSHA256) if !configTemplateContentsUnchanged { if c.force { if configTemplate == nil { if err := c.persistentState.Delete(chezmoi.ConfigStateBucket, configStateKey); err != nil { return err } } else { configStateValue, err := chezmoi.FormatJSON.Marshal(configState{ ConfigTemplateContentsSHA256: chezmoi.HexBytes(currentConfigTemplateContentsSHA256), }) if err != nil { return err } if err := c.persistentState.Set(chezmoi.ConfigStateBucket, configStateKey, configStateValue); err != nil { return err } } } else if c.Warnings.ConfigFileTemplateHasChanged { c.errorf("warning: config file template has changed, run chezmoi init to regenerate config file\n") } } sourceState, err := c.getSourceState(ctx, options.cmd) if err != nil { return err } var targetRelPaths []chezmoi.RelPath switch { case len(args) == 0: targetRelPaths = sourceState.TargetRelPaths() case c.sourcePath: targetRelPaths, err = c.targetRelPathsBySourcePath(sourceState, args) if err != nil { return err } default: targetRelPaths, err = c.targetRelPaths(sourceState, args, targetRelPathsOptions{ recursive: options.recursive, }) if err != nil { return err } } if options.parentDirs { targetRelPaths = prependParentRelPaths(targetRelPaths) } applyOptions := chezmoi.ApplyOptions{ Filter: options.filter, PreApplyFunc: options.preApplyFunc, Umask: options.umask, } keptGoingAfterErr := false for _, targetRelPath := range targetRelPaths { switch err := sourceState.Apply(targetSystem, c.destSystem, c.persistentState, targetDirAbsPath, targetRelPath, applyOptions); { case errors.Is(err, fs.SkipDir): continue case err != nil: err = fmt.Errorf("%s: %w", targetRelPath, err) if !c.keepGoing { return err } c.errorf("%v\n", err) keptGoingAfterErr = true } } switch err := sourceState.PostApply(targetSystem, c.persistentState, targetDirAbsPath, targetRelPaths); { case err != nil && c.keepGoing: c.errorf("%v\n", err) keptGoingAfterErr = true case err != nil: return err } if keptGoingAfterErr { return chezmoi.ExitCodeError(1) } return nil } // builtinDiffFile outputs the diff between fromData and fromMode and toData and // toMode at path. func (c *Config) builtinDiffFile( relPath chezmoi.RelPath, fromData []byte, fromMode fs.FileMode, toData []byte, toMode fs.FileMode, ) error { builder := strings.Builder{} unifiedEncoder := diff.NewUnifiedEncoder(&builder, diff.DefaultContextLines) color := c.Color.Value(c.colorAutoFunc) if color { unifiedEncoder.SetColor(diff.NewColorConfig()) } if fromMode.IsRegular() { var err error fromData, _, err = c.TextConv.convert(relPath.String(), fromData) if err != nil { return err } } if toMode.IsRegular() { var err error toData, _, err = c.TextConv.convert(relPath.String(), toData) if err != nil { return err } } diffPatch, err := chezmoi.DiffPatch(relPath, fromData, fromMode, toData, toMode) if err != nil { return err } if err := unifiedEncoder.Encode(diffPatch); err != nil { return err } return c.pageDiffOutput(builder.String()) } // checkVersion checks that chezmoi is at least the required version for the // source state. func (c *Config) checkVersion() error { versionAbsPath := c.SourceDirAbsPath.JoinString(chezmoi.VersionName) switch data, err := c.baseSystem.ReadFile(versionAbsPath); { case errors.Is(err, fs.ErrNotExist): case err != nil: return err default: minVersion, err := semver.NewVersion(strings.TrimSpace(string(data))) if err != nil { return fmt.Errorf("%s: %q: %w", versionAbsPath, data, err) } var zeroVersion semver.Version if c.version != zeroVersion && c.version.LessThan(*minVersion) { return &chezmoi.TooOldError{ Need: *minVersion, Have: c.version, } } } return nil } // cmdOutput returns the of running the command name with args in dirAbsPath. func (c *Config) cmdOutput(dirAbsPath chezmoi.AbsPath, name string, args []string) ([]byte, error) { cmd := exec.Command(name, args...) cmd.Stderr = os.Stderr if !dirAbsPath.IsEmpty() { dirRawAbsPath, err := c.baseSystem.RawPath(dirAbsPath) if err != nil { return nil, err } cmd.Dir = dirRawAbsPath.String() } return chezmoilog.LogCmdOutput(slog.Default(), cmd) } // colorAutoFunc detects whether color should be used. func (c *Config) colorAutoFunc() bool { if _, ok := os.LookupEnv("NO_COLOR"); ok { return false } if stdout, ok := c.stdout.(*os.File); ok { return term.IsTerminal(int(stdout.Fd())) } return false } // createAndReloadConfigFile creates a config file if it there is a config file // template and reloads it. func (c *Config) createAndReloadConfigFile(cmd *cobra.Command) error { // Refresh the source directory, as there might be a .chezmoiroot file and // the template data is set before .chezmoiroot is read. sourceDirAbsPath, err := c.getSourceDirAbsPath(&getSourceDirAbsPathOptions{ refresh: true, }) if err != nil { return err } c.templateData.sourceDir = sourceDirAbsPath os.Setenv("CHEZMOI_SOURCE_DIR", sourceDirAbsPath.String()) // Find config template, execute it, and create config file. configTemplate, err := c.findConfigTemplate() if err != nil { return err } if configTemplate == nil { return c.persistentState.Delete(chezmoi.ConfigStateBucket, configStateKey) } configFileContents, err := c.createConfigFileContents(configTemplate.targetRelPath, configTemplate.contents, cmd) if err != nil { return err } // Validate the config file. var configFile ConfigFile if err := c.decodeConfigContents(configTemplate.format, configFileContents, &configFile); err != nil { return fmt.Errorf("%s: %w", configTemplate.sourceAbsPath, err) } // Write the config. configPath := c.init.configPath if c.init.configPath.IsEmpty() { if c.customConfigFileAbsPath.IsEmpty() { configPath = chezmoi.NewAbsPath(c.bds.ConfigHome).Join(chezmoiRelPath, configTemplate.targetRelPath) } else { configPath = c.customConfigFileAbsPath } } if err := chezmoi.MkdirAll(c.destSystem, configPath.Dir(), fs.ModePerm); err != nil { return err } if err := c.destSystem.WriteFile(configPath, configFileContents, 0o600); err != nil { return err } configStateValue, err := chezmoi.FormatJSON.Marshal(configState{ ConfigTemplateContentsSHA256: chezmoi.HexBytes(sha256Sum(configTemplate.contents)), }) if err != nil { return err } if err := c.persistentState.Set(chezmoi.ConfigStateBucket, configStateKey, configStateValue); err != nil { return err } // Reload the config. if err := c.decodeConfigContents(configTemplate.format, configFileContents, &c.ConfigFile); err != nil { return fmt.Errorf("%s: %w", configTemplate.sourceAbsPath, err) } if err := c.setEncryption(); err != nil { return err } return c.setEnvironmentVariables() } // createConfigFileContents creates a config file using a template and returns // its contents. func (c *Config) createConfigFileContents(filename chezmoi.RelPath, data []byte, cmd *cobra.Command) ([]byte, error) { // Clone funcMap and restore it after creating the config. // This ensures that the init template functions // are removed before "normal" template parsing. funcMap := make(template.FuncMap) chezmoi.RecursiveMerge(funcMap, c.templateFuncs) defer func() { c.templateFuncs = funcMap }() initTemplateFuncs := map[string]any{ "exit": c.exitInitTemplateFunc, "promptBool": c.promptBoolInteractiveTemplateFunc, "promptBoolOnce": c.promptBoolOnceInteractiveTemplateFunc, "promptChoice": c.promptChoiceInteractiveTemplateFunc, "promptChoiceOnce": c.promptChoiceOnceInteractiveTemplateFunc, "promptInt": c.promptIntInteractiveTemplateFunc, "promptIntOnce": c.promptIntOnceInteractiveTemplateFunc, "promptMultichoice": c.promptMultichoiceInteractiveTemplateFunc, "promptMultichoiceOnce": c.promptMultichoiceOnceInteractiveTemplateFunc, "promptString": c.promptStringInteractiveTemplateFunc, "promptStringOnce": c.promptStringOnceInteractiveTemplateFunc, "stdinIsATTY": c.stdinIsATTYInitTemplateFunc, "writeToStdout": c.writeToStdout, } chezmoi.RecursiveMerge(c.templateFuncs, initTemplateFuncs) tmpl, err := chezmoi.ParseTemplate(filename.String(), data, chezmoi.TemplateOptions{ Funcs: c.templateFuncs, Options: slices.Clone(c.Template.Options), }) if err != nil { return nil, err } templateData := c.getTemplateDataMap(cmd) if c.init.data { chezmoi.RecursiveMerge(templateData, c.Data) } return tmpl.Execute(templateData) } // defaultConfigFile returns the default config file according to the XDG Base // Directory Specification. func (c *Config) defaultConfigFile(fileSystem vfs.FS, bds *xdg.BaseDirectorySpecification) (chezmoi.AbsPath, error) { // Search XDG Base Directory Specification config directories first. CONFIG_DIR: for _, configDir := range bds.ConfigDirs { configDirAbsPath, err := chezmoi.NewAbsPathFromExtPath(configDir, c.homeDirAbsPath) if err != nil { return chezmoi.EmptyAbsPath, err } dirEntries, err := fileSystem.ReadDir(configDirAbsPath.JoinString("chezmoi").String()) switch { case errors.Is(err, fs.ErrNotExist): continue CONFIG_DIR case err != nil: return chezmoi.EmptyAbsPath, err } dirEntryNames := chezmoiset.NewWithCapacity[string](len(dirEntries)) for _, dirEntry := range dirEntries { dirEntryNames.Add(dirEntry.Name()) } var names []string for _, extension := range chezmoi.FormatExtensions { name := "chezmoi." + extension if dirEntryNames.Contains(name) { names = append(names, name) } } switch len(names) { case 0: // Do nothing. case 1: return configDirAbsPath.JoinString("chezmoi", names[0]), nil default: configFileAbsPathStrs := make([]string, 0, len(names)) for _, name := range names { configFileAbsPathStr := configDirAbsPath.JoinString("chezmoi", name) configFileAbsPathStrs = append(configFileAbsPathStrs, configFileAbsPathStr.String()) } return chezmoi.EmptyAbsPath, fmt.Errorf("multiple config files: %s", englishList(configFileAbsPathStrs)) } } // Fallback to XDG Base Directory Specification default. configHomeAbsPath, err := chezmoi.NewAbsPathFromExtPath(bds.ConfigHome, c.homeDirAbsPath) if err != nil { return chezmoi.EmptyAbsPath, err } return configHomeAbsPath.JoinString("chezmoi", "chezmoi.toml"), nil } // decodeConfigContents decodes data in format into configFile. func (c *Config) decodeConfigContents(format chezmoi.Format, contents []byte, configFile *ConfigFile) error { var configMap map[string]any if err := format.Unmarshal(contents, &configMap); err != nil { return err } return c.decodeConfigMap(configMap, configFile) } // decodeConfigFile decodes the config file at configFileAbsPath into // configFile. func (c *Config) decodeConfigFile(configFileAbsPath chezmoi.AbsPath, configFile *ConfigFile) error { var format chezmoi.Format switch formatStr := c.configFormat.String(); formatStr { case "": var err error format, err = chezmoi.FormatFromAbsPath(configFileAbsPath) if err != nil { return err } case formatJSON: format = chezmoi.FormatJSON case formatTOML: format = chezmoi.FormatTOML case formatYAML: format = chezmoi.FormatYAML default: return fmt.Errorf("%s: invalid format", formatStr) } configFileContents, err := c.fileSystem.ReadFile(configFileAbsPath.String()) if err != nil { return fmt.Errorf("%s: %w", configFileAbsPath, err) } if err := c.decodeConfigContents(format, configFileContents, configFile); err != nil { return fmt.Errorf("%s: %w", configFileAbsPath, err) } if configFile.Git.CommitMessageTemplate != "" && configFile.Git.CommitMessageTemplateFile != "" { return fmt.Errorf( "%s: cannot specify both git.commitMessageTemplate and git.commitMessageTemplateFile", configFileAbsPath, ) } return nil } // decodeConfigMap decodes configMap into configFile. func (c *Config) decodeConfigMap(configMap map[string]any, configFile *ConfigFile) error { decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.ComposeDecodeHookFunc( mapstructure.StringToTimeDurationHookFunc(), mapstructure.StringToSliceHookFunc(","), chezmoi.StringSliceToEntryTypeSetHookFunc(), chezmoi.StringToAbsPathHookFunc(), StringOrBoolToAutoBoolHookFunc(), StringToChoiceFlagHookFunc(), ), Result: configFile, }) if err != nil { return err } return decoder.Decode(configMap) } // defaultPreApplyFunc is the default pre-apply function. If the target entry // has changed since chezmoi last wrote it then it prompts the user for the // action to take. func (c *Config) defaultPreApplyFunc( targetRelPath chezmoi.RelPath, targetEntryState, lastWrittenEntryState, actualEntryState *chezmoi.EntryState, ) error { c.logger.Info("defaultPreApplyFunc", chezmoilog.Stringer("targetRelPath", targetRelPath), slog.Any("targetEntryState", targetEntryState), slog.Any("lastWrittenEntryState", lastWrittenEntryState), slog.Any("actualEntryState", actualEntryState), ) switch { case c.force: return nil case targetEntryState.Equivalent(actualEntryState): return nil } // Prepare decision for which kind of prompt we need (if any) type promptMode int const ( promptNone promptMode = iota promptYesNoAll // yes/no/all/quit (just ask, don't indicate if there is a conflict) promptConflict // overwrite/all-overwrite/skip/quit (conflict-specific prompt) ) mode := promptNone targetDirty := lastWrittenEntryState != nil && !lastWrittenEntryState.Equivalent(actualEntryState) targetPreExisting := lastWrittenEntryState == nil && actualEntryState.Type != chezmoi.EntryStateTypeRemove // Select prompt mode based on command line flag switch { case c.Interactive: // Prompt no matter what mode = promptYesNoAll case c.LessInteractive: // Prompt if target is dirty or pre-existing (i.e., only overwrite what chezmoi has written) if targetDirty || targetPreExisting { mode = promptConflict } default: // Prompt in *some* cases of a dirty target: switch { case targetEntryState.Overwrite(): mode = promptNone case targetEntryState.Type == chezmoi.EntryStateTypeScript: mode = promptNone case lastWrittenEntryState == nil: mode = promptNone case lastWrittenEntryState.Equivalent(actualEntryState): mode = promptNone case targetDirty: mode = promptConflict } } if mode == promptNone { return nil } // Now prompt based on choice made above actualContents := actualEntryState.Contents() targetContents := targetEntryState.Contents() var choices []string if actualContents != nil || targetContents != nil { choices = append(choices, "diff") } var promptText string if mode == promptYesNoAll { choices = append(choices, choicesYesNoAllQuit...) promptText = fmt.Sprintf("Apply %s", targetRelPath) } else { choices = append(choices, choicesOverwrite...) if targetDirty { promptText = fmt.Sprintf("%s has changed since chezmoi last wrote it", targetRelPath) } else { promptText = fmt.Sprintf("%s already exists", targetRelPath) } } for { switch choice, err := c.promptChoice(promptText, choices); { case err != nil: return err case choice == "diff": if err := c.diffFile( targetRelPath, c.DestDirAbsPath.Join(targetRelPath), actualContents, actualEntryState.Mode, chezmoi.EmptyAbsPath, targetContents, targetEntryState.Mode, ); err != nil { return err } case choice == "yes": return nil case choice == "no": return fs.SkipDir case choice == "all": // Delicate difference to all-overwrite (mainly for backwards compatibility): Disabling --interactive means // we still prompt for dirty files, whereas all-overwrite adds --force to really prompt no more. c.Interactive = false return nil case choice == "overwrite": return nil case choice == "all-overwrite": c.force = true return nil case choice == "skip": return fs.SkipDir case choice == "quit": return chezmoi.ExitCodeError(0) default: panic(choice + ": unexpected choice") } } } // defaultSourceDir returns the default source directory according to the XDG // Base Directory Specification. func (c *Config) defaultSourceDir(fileSystem vfs.Stater, bds *xdg.BaseDirectorySpecification) (chezmoi.AbsPath, error) { // Check for XDG Base Directory Specification data directories first. for _, dataDir := range bds.DataDirs { dataDirAbsPath, err := chezmoi.NewAbsPathFromExtPath(dataDir, c.homeDirAbsPath) if err != nil { return chezmoi.EmptyAbsPath, err } sourceDirAbsPath := dataDirAbsPath.Join(chezmoiRelPath) if _, err := fileSystem.Stat(sourceDirAbsPath.String()); err == nil { return sourceDirAbsPath, nil } } // Fallback to XDG Base Directory Specification default. dataHomeAbsPath, err := chezmoi.NewAbsPathFromExtPath(bds.DataHome, c.homeDirAbsPath) if err != nil { return chezmoi.EmptyAbsPath, err } return dataHomeAbsPath.Join(chezmoiRelPath), nil } type onNotExist int const ( onNotExistError onNotExist = iota onNotExistIgnore onNotExistAdd ) type destAbsPathInfosOptions struct { follow bool onIgnoreFunc func(chezmoi.RelPath) onNotExist onNotExist recursive bool } // destAbsPathInfos returns the os/fs.FileInfos for each destination entry in // args, recursing into subdirectories and following symlinks if configured in // options. func (c *Config) destAbsPathInfos( sourceState *chezmoi.SourceState, args []string, options destAbsPathInfosOptions, ) (map[chezmoi.AbsPath]fs.FileInfo, error) { destAbsPathInfos := make(map[chezmoi.AbsPath]fs.FileInfo) for _, arg := range args { arg = filepath.Clean(arg) destAbsPath, err := chezmoi.NewAbsPathFromExtPath(arg, c.homeDirAbsPath) if err != nil { return nil, err } targetRelPath, err := c.targetRelPath(destAbsPath) if err != nil { return nil, err } if sourceState.Ignore(targetRelPath) { options.onIgnoreFunc(targetRelPath) continue } if options.recursive { walkFunc := func(destAbsPath chezmoi.AbsPath, fileInfo fs.FileInfo, err error) error { switch { case errors.Is(err, fs.ErrNotExist): switch options.onNotExist { case onNotExistError: return err case onNotExistIgnore: case onNotExistAdd: if _, ok := destAbsPathInfos[destAbsPath]; !ok { destAbsPathInfos[destAbsPath] = nil } } return nil case err != nil: return err } targetRelPath, err := c.targetRelPath(destAbsPath) if err != nil { return err } if sourceState.Ignore(targetRelPath) { options.onIgnoreFunc(targetRelPath) if fileInfo.IsDir() { return fs.SkipDir } return nil } if options.follow && fileInfo.Mode().Type() == fs.ModeSymlink { fileInfo, err = c.destSystem.Stat(destAbsPath) if err != nil { return err } } return sourceState.AddDestAbsPathInfos(destAbsPathInfos, c.destSystem, destAbsPath, fileInfo) } if err := chezmoi.Walk(c.destSystem, destAbsPath, walkFunc); err != nil { return nil, err } } else { var fileInfo fs.FileInfo if options.follow { fileInfo, err = c.destSystem.Stat(destAbsPath) } else { fileInfo, err = c.destSystem.Lstat(destAbsPath) } switch { case errors.Is(err, fs.ErrNotExist): switch options.onNotExist { case onNotExistError: return nil, err case onNotExistIgnore: case onNotExistAdd: if _, ok := destAbsPathInfos[destAbsPath]; !ok { destAbsPathInfos[destAbsPath] = nil } } continue case err != nil: return nil, err } if err := sourceState.AddDestAbsPathInfos(destAbsPathInfos, c.destSystem, destAbsPath, fileInfo); err != nil { return nil, err } } } return destAbsPathInfos, nil } // diffFile outputs the diff between fromAbsPath, fromData and fromMode and // toAbsPath, toData and toMode at relPath. func (c *Config) diffFile( relPath chezmoi.RelPath, fromAbsPath chezmoi.AbsPath, fromData []byte, fromMode fs.FileMode, toAbsPath chezmoi.AbsPath, toData []byte, toMode fs.FileMode, ) error { if c.useBuiltinDiff || c.Diff.Command == "" { return c.builtinDiffFile(relPath, fromData, fromMode, toData, toMode) } return c.externalDiffFile( relPath, fromAbsPath, fromData, fromMode, toAbsPath, toData, toMode, ) } // editor returns the path to the user's editor and any extra arguments. func (c *Config) editor(args []string) (string, []string, error) { editCommand := c.Edit.Command editArgs := c.Edit.Args // If the user has set an edit command then use it. if editCommand != "" { return editCommand, append(editArgs, args...), nil } // Prefer $VISUAL over $EDITOR and fallback to the OS's default editor. editCommand = cmp.Or(os.Getenv("VISUAL"), os.Getenv("EDITOR"), defaultEditor) return parseCommand(editCommand, append(editArgs, args...)) } // errorf writes an error to stderr. func (c *Config) errorf(format string, args ...any) { fmt.Fprintf(c.stderr, "chezmoi: "+format, args...) } // execute creates a new root command and executes it with args. func (c *Config) execute(args []string) error { rootCmd, err := c.newRootCmd() if err != nil { return err } defer c.finalize() rootCmd.SetArgs(args) return rootCmd.Execute() } func (c *Config) externalDiffFile( relPath chezmoi.RelPath, fromAbsPath chezmoi.AbsPath, fromData []byte, fromMode fs.FileMode, toAbsPath chezmoi.AbsPath, toData []byte, toMode fs.FileMode, ) error { if fromAbsPath.IsEmpty() { fromTempDir, err := c.tempDir("chezmoi-diff-from") if err != nil { return err } fromAbsPath = fromTempDir.Join(relPath) if err := os.MkdirAll(fromAbsPath.Dir().String(), 0o777); err != nil { return err } if err := os.WriteFile(fromAbsPath.String(), fromData, fromMode); err != nil { return err } } if toAbsPath.IsEmpty() { toTempDir, err := c.tempDir("chezmoi-diff-to") if err != nil { return err } toAbsPath = toTempDir.Join(relPath) if err := os.MkdirAll(toAbsPath.Dir().String(), 0o777); err != nil { return err } if err := os.WriteFile(toAbsPath.String(), toData, toMode); err != nil { return err } } externalDiffSystem := c.newExternalDiffSystem(c.baseSystem) return externalDiffSystem.RunDiffCommand(fromAbsPath, toAbsPath) } // filterInput reads from args (or the standard input if args is empty), // transforms it with f, and writes the output. func (c *Config) filterInput(args []string, f func([]byte) ([]byte, error)) error { if len(args) == 0 { input, err := io.ReadAll(c.stdin) if err != nil { return err } output, err := f(input) if err != nil { return err } return c.writeOutput(output, 0o666) } for _, arg := range args { argAbsPath, err := chezmoi.NewAbsPathFromExtPath(arg, c.homeDirAbsPath) if err != nil { return err } input, err := c.baseSystem.ReadFile(argAbsPath) if err != nil { return err } output, err := f(input) if err != nil { return err } if err := c.writeOutput(output, 0o666); err != nil { return err } } return nil } type configTemplate struct { sourceAbsPath chezmoi.AbsPath format chezmoi.Format targetRelPath chezmoi.RelPath contents []byte } // findConfigTemplate searches for a config template, returning the path, // format, and contents. It returns an error if multiple config file templates // are found. func (c *Config) findConfigTemplate() (*configTemplate, error) { sourceDirAbsPath, err := c.getSourceDirAbsPath(nil) if err != nil { return nil, err } dirEntries, err := c.baseSystem.ReadDir(sourceDirAbsPath) switch { case errors.Is(err, fs.ErrNotExist): return nil, nil case err != nil: return nil, err } dirEntryNames := chezmoiset.NewWithCapacity[chezmoi.RelPath](len(dirEntries)) for _, dirEntry := range dirEntries { dirEntryNames.Add(chezmoi.NewRelPath(dirEntry.Name())) } var configTemplates []*configTemplate //nolint:prealloc for _, extension := range chezmoi.FormatExtensions { relPath := chezmoi.NewRelPath(chezmoi.Prefix + "." + extension + chezmoi.TemplateSuffix) if !dirEntryNames.Contains(relPath) { continue } absPath := sourceDirAbsPath.Join(relPath) contents, err := c.baseSystem.ReadFile(absPath) if err != nil { return nil, err } configTemplate := &configTemplate{ targetRelPath: chezmoi.NewRelPath("chezmoi." + extension), format: chezmoi.FormatsByExtension[extension], sourceAbsPath: absPath, contents: contents, } configTemplates = append(configTemplates, configTemplate) } switch len(configTemplates) { case 0: return nil, nil case 1: return configTemplates[0], nil default: sourceAbsPathStrs := make([]string, len(configTemplates)) for i, configTemplate := range configTemplates { sourceAbsPathStr := configTemplate.sourceAbsPath.String() sourceAbsPathStrs[i] = sourceAbsPathStr } return nil, fmt.Errorf("multiple config file templates: %s ", englishList(sourceAbsPathStrs)) } } func (c *Config) getConfigFileAbsPath() (chezmoi.AbsPath, error) { if c.customConfigFileAbsPath.IsEmpty() { return c.defaultConfigFileAbsPath, c.defaultConfigFileAbsPathErr } return c.customConfigFileAbsPath, nil } // getDiffPager returns the pager for diff output. func (c *Config) getDiffPager() (command string, args []string) { switch { case c.noPager: return "", nil case c.Diff.Pager != defaultSentinel: return c.Diff.Pager, c.Diff.PagerArgs default: return c.Pager, c.PagerArgs } } // getDiffPagerCmd returns a command to run the diff pager, or nil if there is // no diff pager configured. func (c *Config) getDiffPagerCmd() (*exec.Cmd, error) { pager, pagerArgs := c.getDiffPager() if pager == "" { return nil, nil } // If we're not on Windows and the pager command contains any spaces, assume // that it is a full shell command to be executed via the user's shell. // Otherwise, execute it directly. var pagerCmd *exec.Cmd if runtime.GOOS != "windows" && whitespaceRx.MatchString(pager) { shellCommand, _ := shell.CurrentUserShell() shellCommand, shellArgs, err := parseCommand(shellCommand, []string{"-c", pager}) if err != nil { return nil, err } pagerCmd = exec.Command(shellCommand, slices.Concat(shellArgs, pagerArgs)...) } else { pagerCmd = exec.Command(pager, pagerArgs...) } pagerCmd.Stdout = c.stdout pagerCmd.Stderr = c.stderr // If the LESS or LV environment variables are not set, set them to sensible // defaults the same way the git does. See // https://git-scm.com/docs/git-config#Documentation/git-config.txt-corepager. if _, ok := os.LookupEnv("LESS"); !ok { pagerCmd.Env = append(pagerCmd.Environ(), "LESS=FRX") } if _, ok := os.LookupEnv("LV"); !ok { pagerCmd.Env = append(pagerCmd.Environ(), "LV=-c") } return pagerCmd, nil } func (c *Config) getGitleaksDetector() (*detect.Detector, error) { if c.gitleaksDetector == nil && c.gitleaksDetectorErr == nil { c.gitleaksDetector, c.gitleaksDetectorErr = detect.NewDetectorDefaultConfig() } return c.gitleaksDetector, c.gitleaksDetectorErr } // A modifyHTTPRequestFunc is a function that modifies a [net/http.Request] // before it is sent. type modifyHTTPRequestFunc func(*http.Request) (*http.Request, error) // A modifyHTTPRequestRoundTripper is a [net/http.Transport] that modifies the // request before it is sent. type modifyHTTPRequestRoundTripper struct { modifyHTTPRequestFunc modifyHTTPRequestFunc httpRoundTripper http.RoundTripper } func newModifyHTTPRequestRoundTripper(f modifyHTTPRequestFunc, t http.RoundTripper) modifyHTTPRequestRoundTripper { return modifyHTTPRequestRoundTripper{ modifyHTTPRequestFunc: f, httpRoundTripper: t, } } // RoundTrip implements [net/http.Transport.RoundTrip]. func (m modifyHTTPRequestRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { modifiedRequest, err := m.modifyHTTPRequestFunc(req) if err != nil { return nil, err } return m.httpRoundTripper.RoundTrip(modifiedRequest) } func (c *Config) getHTTPClient() (*http.Client, error) { if c.httpClient != nil { return c.httpClient, nil } httpCacheBasePath, err := c.baseSystem.RawPath(c.CacheDirAbsPath.Join(httpCacheDirRelPath)) if err != nil { return nil, err } httpCache := diskcache.New(httpCacheBasePath.String()) httpTransport := httpcache.NewTransport(httpCache) c.httpClient = httpTransport.Client() c.httpClient.Transport = newModifyHTTPRequestRoundTripper( func(req *http.Request) (*http.Request, error) { req = req.Clone(req.Context()) req.Header.Add("User-Agent", "chezmoi.io/"+c.version.String()) return req, nil }, c.httpClient.Transport, ) return c.httpClient, nil } type getSourceDirAbsPathOptions struct { refresh bool } // getSourceDirAbsPath returns the source directory, using .chezmoiroot if it // exists. func (c *Config) getSourceDirAbsPath(options *getSourceDirAbsPathOptions) (chezmoi.AbsPath, error) { if options == nil || !options.refresh { if !c.sourceDirAbsPath.IsEmpty() || c.sourceDirAbsPathErr != nil { return c.sourceDirAbsPath, c.sourceDirAbsPathErr } } switch data, err := c.sourceSystem.ReadFile(c.SourceDirAbsPath.JoinString(chezmoi.RootName)); { case errors.Is(err, fs.ErrNotExist): c.sourceDirAbsPath = c.SourceDirAbsPath case err != nil: c.sourceDirAbsPathErr = err default: c.sourceDirAbsPath = c.SourceDirAbsPath.JoinString(string(bytes.TrimSpace(data))) } return c.sourceDirAbsPath, c.sourceDirAbsPathErr } func (c *Config) getSourceState(ctx context.Context, cmd *cobra.Command) (*chezmoi.SourceState, error) { if c.sourceState != nil || c.sourceStateErr != nil { return c.sourceState, c.sourceStateErr } c.sourceState, c.sourceStateErr = c.newSourceState(ctx, cmd) return c.sourceState, c.sourceStateErr } // getTemplateData returns the default template data. func (c *Config) getTemplateData(cmd *cobra.Command) *templateData { if c.templateData == nil { c.templateData = c.newTemplateData(cmd) } return c.templateData } // getTemplateDataMap returns the template data as a map. func (c *Config) getTemplateDataMap(cmd *cobra.Command) map[string]any { templateData := c.getTemplateData(cmd) return map[string]any{ "chezmoi": map[string]any{ "arch": templateData.arch, "args": templateData.args, "cacheDir": templateData.cacheDir.String(), "command": templateData.command, "commandDir": templateData.commandDir.String(), "config": templateData.config, "configFile": templateData.configFile.String(), "destDir": templateData.destDir, "executable": templateData.executable.String(), "fqdnHostname": templateData.fqdnHostname, "gid": templateData.gid, "group": templateData.group, "homeDir": templateData.homeDir.String(), "hostname": templateData.hostname, "kernel": templateData.kernel, "os": templateData.os, "osRelease": templateData.osRelease, "pathListSeparator": templateData.pathListSeparator, "pathSeparator": templateData.pathSeparator, "sourceDir": templateData.sourceDir.String(), "uid": templateData.uid, "username": templateData.username, "version": templateData.version, "windowsVersion": templateData.windowsVersion, "workingTree": templateData.workingTree.String(), }, } } // gitAutoAdd adds all changes to the git index and returns the new git status. func (c *Config) gitAutoAdd() (*chezmoigit.Status, error) { if err := c.run(c.SourceDirAbsPath, c.Git.Command, []string{"add", "."}); err != nil { return nil, err } output, err := c.cmdOutput(c.SourceDirAbsPath, c.Git.Command, []string{"status", "--porcelain=v2"}) if err != nil { return nil, err } return chezmoigit.ParseStatusPorcelainV2(output) } // gitAutoCommit commits all changes in the git index, including generating a // commit message from status. func (c *Config) gitAutoCommit(cmd *cobra.Command, status *chezmoigit.Status) error { if status.IsEmpty() { return nil } if err := c.runHookPre("git-auto-commit"); err != nil { return err } commitMessage, err := c.gitCommitMessage(cmd, status) if err != nil { return err } if err := c.run(c.SourceDirAbsPath, c.Git.Command, []string{"commit", "--message", string(commitMessage)}); err != nil { return err } return c.runHookPost("git-auto-commit") } // gitAutoPush pushes all changes to the remote if status is not empty. func (c *Config) gitAutoPush(status *chezmoigit.Status) error { if status.IsEmpty() { return nil } if err := c.runHookPre("git-auto-push"); err != nil { return err } if err := c.run(c.SourceDirAbsPath, c.Git.Command, []string{"push"}); err != nil { return err } return c.runHookPost("git-auto-push") } // gitCommitMessage returns the git commit message for the given status. func (c *Config) gitCommitMessage(cmd *cobra.Command, status *chezmoigit.Status) ([]byte, error) { templateFuncs := maps.Clone(c.templateFuncs) maps.Copy(templateFuncs, map[string]any{ "promptBool": c.promptBoolInteractiveTemplateFunc, "promptChoice": c.promptChoiceInteractiveTemplateFunc, "promptInt": c.promptIntInteractiveTemplateFunc, "promptMultichoice": c.promptMultichoiceInteractiveTemplateFunc, "promptString": c.promptStringInteractiveTemplateFunc, "targetRelPath": func(source string) string { return mustValue(chezmoi.NewSourceRelPath(source).TargetRelPath(c.encryption.EncryptedSuffix())).String() }, }) var name string var commitMessageTemplateData []byte switch { case c.Git.CommitMessageTemplate != "": name = "git.commitMessageTemplate" commitMessageTemplateData = []byte(c.Git.CommitMessageTemplate) case c.Git.CommitMessageTemplateFile != "": if c.sourceDirAbsPathErr != nil { return nil, c.sourceDirAbsPathErr } commitMessageTemplateFileAbsPath := c.sourceDirAbsPath.JoinString(c.Git.CommitMessageTemplateFile) name = c.sourceDirAbsPath.String() var err error commitMessageTemplateData, err = c.baseSystem.ReadFile(commitMessageTemplateFileAbsPath) if err != nil { return nil, err } default: name = "COMMIT_MESSAGE" commitMessageTemplateData = []byte(templates.CommitMessageTmpl) } commitMessageTmpl, err := chezmoi.ParseTemplate(name, commitMessageTemplateData, chezmoi.TemplateOptions{ Funcs: templateFuncs, Options: slices.Clone(c.Template.Options), }) if err != nil { return nil, err } sourceState, err := c.getSourceState(cmd.Context(), cmd) if err != nil { return nil, err } templateDataMap := sourceState.TemplateData() templateDataMap["chezmoi"].(map[string]any)["status"] = status //nolint:forcetypeassert return commitMessageTmpl.Execute(templateDataMap) } // makeRunEWithSourceState returns a function for // github.com/spf13/cobra.Command.RunE that includes reading the source state. func (c *Config) makeRunEWithSourceState( runE func(*cobra.Command, []string, *chezmoi.SourceState) error, ) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { sourceState, err := c.getSourceState(cmd.Context(), cmd) if err != nil { return err } return runE(cmd, args, sourceState) } } // marshal formats data in dataFormat and writes it to the standard output. func (c *Config) marshal(dataFormat string, data any) error { var format chezmoi.Format switch dataFormat { case formatJSON: format = chezmoi.FormatJSON case formatYAML: format = chezmoi.FormatYAML default: return fmt.Errorf("%s: invalid format", dataFormat) } marshaledData, err := format.Marshal(data) if err != nil { return err } return c.writeOutput(marshaledData, 0o666) } // newBuiltinDifSystem returns a new builtin diff system. func (c *Config) newBuiltinDiffSystem(s chezmoi.System, w io.Writer, dirAbsPath chezmoi.AbsPath) *chezmoi.GitDiffSystem { options := &chezmoi.GitDiffSystemOptions{ Color: c.Color.Value(c.colorAutoFunc), Filter: chezmoi.NewEntryTypeFilter(c.Diff.include.Bits(), c.Diff.Exclude.Bits()), Reverse: c.Diff.Reverse, ScriptContents: c.Diff.ScriptContents, TextConvFunc: c.TextConv.convert, } return chezmoi.NewGitDiffSystem(s, w, dirAbsPath, options) } // newRootCmd returns a new root github.com/spf13/cobra.Command. func (c *Config) newRootCmd() (*cobra.Command, error) { rootCmd := &cobra.Command{ Use: "chezmoi", Short: "Manage your dotfiles across multiple diverse machines, securely", Version: c.versionStr, PersistentPreRunE: c.persistentPreRunRootE, PersistentPostRunE: c.persistentPostRunRootE, SilenceErrors: true, SilenceUsage: true, } rootCmd.AddGroup(groups...) persistentFlags := rootCmd.PersistentFlags() persistentFlags.StringVar(&c.ageRecipient, "age-recipient", c.ageRecipient, "Override age recipient") persistentFlags.StringVar(&c.ageRecipientFile, "age-recipient-file", c.ageRecipient, "Override age recipient") persistentFlags.Var(&c.CacheDirAbsPath, "cache", "Set cache directory") persistentFlags.Var(&c.Color, "color", "Colorize output") persistentFlags.VarP(&c.DestDirAbsPath, "destination", "D", "Set destination directory") persistentFlags.BoolVar(&c.Interactive, "interactive", c.Interactive, "Prompt for all changes") persistentFlags.BoolVar( &c.LessInteractive, "less-interactive", c.LessInteractive, "Prompt for changed or pre-existing targets", ) persistentFlags.Var(&c.Mode, "mode", "Mode") persistentFlags.Var(&c.PersistentStateAbsPath, "persistent-state", "Set persistent state file") persistentFlags.Var(&c.Progress, "progress", "Display progress bars") persistentFlags.BoolVar(&c.Safe, "safe", c.Safe, "Safely replace files and symlinks") persistentFlags.VarP(&c.SourceDirAbsPath, "source", "S", "Set source directory") persistentFlags.Var(&c.UseBuiltinAge, "use-builtin-age", "Use builtin age") persistentFlags.Var(&c.UseBuiltinGit, "use-builtin-git", "Use builtin git") persistentFlags.BoolVarP(&c.Verbose, "verbose", "v", c.Verbose, "Make output more verbose") persistentFlags.VarP(&c.WorkingTreeAbsPath, "working-tree", "W", "Set working tree directory") persistentFlags.VarP(&c.customConfigFileAbsPath, "config", "c", "Set config file") persistentFlags.Var(c.configFormat, "config-format", "Set config file format") persistentFlags.BoolVar(&c.debug, "debug", c.debug, "Include debug information in output") persistentFlags.BoolVarP(&c.dryRun, "dry-run", "n", c.dryRun, "Do not make any modifications to the destination directory") persistentFlags.BoolVar(&c.force, "force", c.force, "Make all changes without prompting") persistentFlags.BoolVarP(&c.keepGoing, "keep-going", "k", c.keepGoing, "Keep going as far as possible after an error") persistentFlags.BoolVar(&c.noPager, "no-pager", c.noPager, "Do not use the pager") persistentFlags.BoolVar(&c.noTTY, "no-tty", c.noTTY, "Do not attempt to get a TTY for prompts") persistentFlags.VarP(&c.outputAbsPath, "output", "o", "Write output to path instead of stdout") persistentFlags.StringVar(&c.overrideData, "override-data", c.overrideData, "Override data") persistentFlags.Var(&c.overrideDataFileAbsPath, "override-data-file", "Override data with file") persistentFlags.VarP(&c.refreshExternals, "refresh-externals", "R", "Refresh external cache") persistentFlags.Lookup("refresh-externals").NoOptDefVal = chezmoi.RefreshExternalsAlways.String() persistentFlags.BoolVar(&c.sourcePath, "source-path", c.sourcePath, "Specify targets by source path") persistentFlags.BoolVarP(&c.useBuiltinDiff, "use-builtin-diff", "", c.useBuiltinDiff, "Use builtin diff") if err := chezmoierrors.Combine( rootCmd.MarkPersistentFlagFilename("config"), rootCmd.MarkPersistentFlagDirname("destination"), rootCmd.MarkPersistentFlagFilename("output"), persistentFlags.MarkHidden("safe"), rootCmd.MarkPersistentFlagDirname("source"), rootCmd.RegisterFlagCompletionFunc("color", autoBoolFlagCompletionFunc), rootCmd.RegisterFlagCompletionFunc("config-format", c.configFormat.FlagCompletionFunc()), rootCmd.RegisterFlagCompletionFunc("mode", chezmoi.ModeFlagCompletionFunc), rootCmd.RegisterFlagCompletionFunc("progress", autoBoolFlagCompletionFunc), rootCmd.RegisterFlagCompletionFunc("refresh-externals", chezmoi.RefreshExternalsFlagCompletionFunc), rootCmd.RegisterFlagCompletionFunc("use-builtin-age", autoBoolFlagCompletionFunc), rootCmd.RegisterFlagCompletionFunc("use-builtin-git", autoBoolFlagCompletionFunc), rootCmd.MarkPersistentFlagDirname("working-tree"), ); err != nil { return nil, err } rootCmd.SetHelpCommand(c.newHelpCmd()) for _, cmd := range []*cobra.Command{ c.newAddCmd(), c.newAgeCmd(), c.newAgeKeygenCmd(), c.newApplyCmd(), c.newArchiveCmd(), c.newCatCmd(), c.newCatConfigCmd(), c.newCDCmd(), c.newChattrCmd(), c.newCompletionCmd(), c.newDataCmd(), c.newDecryptCommand(), c.newDestroyCmd(), c.newDiffCmd(), c.newDockerCmd(), c.newDoctorCmd(), c.newDumpCmd(), c.newDumpConfigCmd(), c.newEditCmd(), c.newEditConfigCmd(), c.newEditConfigTemplateCmd(), c.newEditEncryptedCmd(), c.newEncryptCmd(), c.newExecuteTemplateCmd(), c.newForgetCmd(), c.newGenerateCmd(), c.newGitCmd(), c.newIgnoredCmd(), c.newImportCmd(), c.newInitCmd(), c.newInternalTestCmd(), c.newLicenseCmd(), c.newMackupCmd(), c.newManagedCmd(), c.newMergeCmd(), c.newMergeAllCmd(), c.newPurgeCmd(), c.newReAddCmd(), c.newRemoveCmd(), c.newSSHCmd(), c.newSecretCmd(), c.newSourcePathCmd(), c.newStateCmd(), c.newStatusCmd(), c.newTargetPathCmd(), c.newUnmanagedCmd(), c.newUpdateCmd(), c.newUpgradeCmd(), c.newVerifyCmd(), } { if cmd != nil { ensureHasGroupID(cmd) ensureAllFlagsDocumented(cmd, persistentFlags) registerCommonFlagCompletionFuncs(cmd) rootCmd.AddCommand(cmd) } } for _, cmd := range rootCmd.Commands() { annotations := getAnnotations(cmd) if len(annotations) == 0 { panic(cmd.Name() + ": no annotations") } if annotations.persistentStateMode() == "" { panic(cmd.Name() + ": persistent state mode not set") } } return rootCmd, nil } // newDiffSystem returns a system that logs all changes to s to w using // diff.command if set or the builtin git diff otherwise. func (c *Config) newDiffSystem(s chezmoi.System, w io.Writer, dirAbsPath chezmoi.AbsPath) chezmoi.System { if c.useBuiltinDiff || c.Diff.Command == "" { return c.newBuiltinDiffSystem(s, w, dirAbsPath) } return c.newExternalDiffSystem(s) } // newExternalDiffSystem returns a new external diff system. func (c *Config) newExternalDiffSystem(s chezmoi.System) *chezmoi.ExternalDiffSystem { options := &chezmoi.ExternalDiffSystemOptions{ Filter: chezmoi.NewEntryTypeFilter(c.Diff.include.Bits(), c.Diff.Exclude.Bits()), Reverse: c.Diff.Reverse, ScriptContents: c.Diff.ScriptContents, TemplateOptions: chezmoi.TemplateOptions{ Funcs: c.templateFuncs, Options: c.Template.Options, }, TextConvFunc: c.TextConv.convert, } return chezmoi.NewExternalDiffSystem(s, c.Diff.Command, c.Diff.Args, c.DestDirAbsPath, c.getDiffPagerCmd, options) } // newSourceState returns a new SourceState with options. func (c *Config) newSourceState( ctx context.Context, cmd *cobra.Command, options ...chezmoi.SourceStateOption, ) (*chezmoi.SourceState, error) { if err := c.checkVersion(); err != nil { return nil, err } httpClient, err := c.getHTTPClient() if err != nil { return nil, err } sourceStateLogger := c.logger.With(slog.String(logComponentKey, logComponentValueSourceState)) c.SourceDirAbsPath, err = c.getSourceDirAbsPath(nil) if err != nil { return nil, err } if err := c.runHookPre(readSourceStateHookName); err != nil { return nil, err } priorityTemplateData := c.Data if !c.overrideDataFileAbsPath.IsEmpty() { var overrideData map[string]any data, err := c.baseSystem.ReadFile(c.overrideDataFileAbsPath) if err != nil { return nil, err } if err := chezmoi.UnmarshalFileData(c.overrideDataFileAbsPath, data, &overrideData); err != nil { return nil, err } chezmoi.RecursiveMerge(priorityTemplateData, overrideData) } if c.overrideData != "" { var overrideData map[string]any if err := json.Unmarshal([]byte(c.overrideData), &overrideData); err != nil { return nil, err } chezmoi.RecursiveMerge(priorityTemplateData, overrideData) } sourceState := chezmoi.NewSourceState(append([]chezmoi.SourceStateOption{ chezmoi.WithBaseSystem(c.baseSystem), chezmoi.WithCacheDir(c.CacheDirAbsPath), chezmoi.WithDefaultTemplateDataFunc(func() map[string]any { return c.getTemplateDataMap(cmd) }), chezmoi.WithDestDir(c.DestDirAbsPath), chezmoi.WithEncryption(c.encryption), chezmoi.WithHTTPClient(httpClient), chezmoi.WithInterpreters(c.Interpreters), chezmoi.WithLogger(sourceStateLogger), chezmoi.WithMode(c.Mode), chezmoi.WithPriorityTemplateData(priorityTemplateData), chezmoi.WithScriptTempDir(c.ScriptTempDir), chezmoi.WithSourceDir(c.SourceDirAbsPath), chezmoi.WithSystem(c.sourceSystem), chezmoi.WithTemplateFuncs(c.templateFuncs), chezmoi.WithTemplateOptions(c.Template.Options), chezmoi.WithUmask(c.Umask), chezmoi.WithVersion(c.version), chezmoi.WithWarnFunc(c.errorf), }, options...)...) if err := sourceState.Read(ctx, &chezmoi.ReadOptions{ RefreshExternals: c.refreshExternals, ReadHTTPResponse: c.readHTTPResponse, }); err != nil { return nil, err } if err := c.runHookPost(readSourceStateHookName); err != nil { return nil, err } return sourceState, nil } // persistentPostRunRootE performs post-run actions for the root command. func (c *Config) persistentPostRunRootE(cmd *cobra.Command, args []string) error { annotations := getAnnotations(cmd) // Verify modified config. if annotations.hasTag(modifiesConfigFile) { configFileAbsPath, err := c.getConfigFileAbsPath() if err != nil { return err } configFileContents, err := c.baseSystem.ReadFile(configFileAbsPath) switch { case errors.Is(err, fs.ErrNotExist): err = nil case err != nil: // err is already set, do nothing. default: var format chezmoi.Format if format, err = chezmoi.FormatFromAbsPath(configFileAbsPath); err == nil { var config map[string]any if err = format.Unmarshal(configFileContents, &config); err != nil { // err is already set, do nothing. } else { err = c.decodeConfigMap(config, &ConfigFile{}) } } } if err != nil { c.errorf("warning: %s: %v\n", configFileAbsPath, err) } } // Perform auto git commands. if annotations.hasTag(modifiesSourceDirectory) { var status *chezmoigit.Status if c.Git.AutoAdd || c.Git.AutoCommit || c.Git.AutoPush { var err error status, err = c.gitAutoAdd() if err != nil { return err } } if c.Git.AutoCommit || c.Git.AutoPush { if err := c.gitAutoCommit(cmd, status); err != nil { return err } } if c.Git.AutoPush { if err := c.gitAutoPush(status); err != nil { return err } } } return c.runHookPost(cmd.Name()) } // finalize cleans up. func (c *Config) finalize() { if c.persistentState != nil { if err := c.persistentState.Close(); err != nil { c.errorf("error: failed to close persistent state: %v\n", err) } } // Wait for any diff pager process to terminate. if c.diffPagerCmd != nil { if err := c.diffPagerCmdStdin.Close(); err != nil { c.errorf("error: failed to close diff pager stdin: %v\n", err) } if c.diffPagerCmd.Process != nil { if err := chezmoilog.LogCmdWait(c.logger, c.diffPagerCmd); err != nil { c.errorf("error: failed to wait for diff pager to close: %v\n", err) } } } if c.restoreWindowsConsole != nil { if err := c.restoreWindowsConsole(); err != nil { c.errorf("error: failed to restore console: %v\n", err) } } // Lock Bitwarden CLI. if err := c.bitwardenLock(); err != nil { c.errorf("error: failed to lock bitwarden-cli: %v\n", err) } // Close any connection to keepassxc-cli. if err := c.keepassxcClose(); err != nil { c.errorf("error: failed to close connection to keepassxc-cli: %v\n", err) } } // pageDiffOutput pages the diff output to stdout. func (c *Config) pageDiffOutput(output string) error { switch pagerCmd, err := c.getDiffPagerCmd(); { case err != nil: return err case pagerCmd == nil: return c.writeOutputString(output, 0o666) default: pagerCmd.Stdin = bytes.NewBufferString(output) return chezmoilog.LogCmdRun(c.logger, pagerCmd) } } // persistentPreRunRootE performs pre-run actions for the root command. func (c *Config) persistentPreRunRootE(cmd *cobra.Command, args []string) error { annotations := getAnnotations(cmd) // Add the completion template function. This needs cmd, so we can't do it // in newConfig. c.addTemplateFunc("completion", func(shell string) string { return mustValue(completion(cmd, shell)) }) if runtime.GOOS == "windows" { var err error c.restoreWindowsConsole, err = termenv.EnableVirtualTerminalProcessing(termenv.DefaultOutput()) if err != nil { return err } } // Save flags that were set on the command line. Skip some types as // spf13/pflag does not round trip them correctly. changedFlags := make(map[pflag.Value]string) brokenFlagTypes := map[string]bool{ "stringToInt": true, "stringToInt64": true, "stringToString": true, } cmd.Flags().VisitAll(func(flag *pflag.Flag) { if flag.Changed && !brokenFlagTypes[flag.Value.Type()] { changedFlags[flag.Value] = flag.Value.String() } }) // Read the config file. if annotations.hasTag(doesNotRequireValidConfig) { if configFileAbsPath, err := c.getConfigFileAbsPath(); err == nil { _ = c.readConfig(configFileAbsPath) } } else { configFileAbsPath, err := c.getConfigFileAbsPath() if err != nil { return err } if err := c.readConfig(configFileAbsPath); err != nil { return fmt.Errorf("invalid config: %s: %w", configFileAbsPath, err) } } // Restore flags that were set on the command line. for value, original := range changedFlags { if err := value.Set(original); err != nil { return err } } if c.force && c.Interactive { return errors.New("the --force and --interactive flags are mutually exclusive") } // Configure the logger. var handler slog.Handler if c.debug { handler = slog.NewTextHandler(c.stderr, nil) } else { handler = chezmoilog.NullHandler{} } c.logger = slog.New(handler) slog.SetDefault(c.logger) // Log basic information. c.logger.Info("persistentPreRunRootE", slog.Any("version", c.versionInfo), slog.Any("args", os.Args), slog.String("goVersion", runtime.Version()), ) realSystem := chezmoi.NewRealSystem(c.fileSystem, chezmoi.RealSystemWithSafe(c.Safe), chezmoi.RealSystemWithScriptTempDir(c.ScriptTempDir), ) c.baseSystem = realSystem if c.debug { systemLogger := c.logger.With(slog.String(logComponentKey, logComponentValueSystem)) c.baseSystem = chezmoi.NewDebugSystem(c.baseSystem, systemLogger) } // Set up the persistent state. switch persistentStateMode := annotations.persistentStateMode(); { case persistentStateMode == persistentStateModeEmpty: c.persistentState = chezmoi.NewMockPersistentState() case persistentStateMode == persistentStateModeReadOnly: persistentStateFileAbsPath, err := c.persistentStateFile() if err != nil { return err } c.persistentState, err = chezmoi.NewBoltPersistentState( c.baseSystem, persistentStateFileAbsPath, chezmoi.BoltPersistentStateReadOnly, ) if err != nil { return err } case persistentStateMode == persistentStateModeReadMockWrite: fallthrough case persistentStateMode == persistentStateModeReadWrite && c.dryRun: persistentStateFileAbsPath, err := c.persistentStateFile() if err != nil { return err } persistentState, err := chezmoi.NewBoltPersistentState( c.baseSystem, persistentStateFileAbsPath, chezmoi.BoltPersistentStateReadOnly, ) if err != nil { return err } dryRunPersistentState := chezmoi.NewMockPersistentState() if err := persistentState.CopyTo(dryRunPersistentState); err != nil { return err } if err := persistentState.Close(); err != nil { return err } c.persistentState = dryRunPersistentState case persistentStateMode == persistentStateModeReadWrite: persistentStateFileAbsPath, err := c.persistentStateFile() if err != nil { return err } c.persistentState, err = chezmoi.NewBoltPersistentState( c.baseSystem, persistentStateFileAbsPath, chezmoi.BoltPersistentStateReadWrite, ) if err != nil { return err } default: c.persistentState = chezmoi.NullPersistentState{} } if c.debug && c.persistentState != nil { persistentStateLogger := c.logger.With(slog.String(logComponentKey, logComponentValuePersistentState)) c.persistentState = chezmoi.NewDebugPersistentState(c.persistentState, persistentStateLogger) } // Set up the source and destination systems. c.sourceSystem = c.baseSystem c.destSystem = c.baseSystem if !annotations.hasTag(modifiesDestinationDirectory) { c.destSystem = chezmoi.NewReadOnlySystem(c.destSystem) } if !annotations.hasTag(modifiesSourceDirectory) { c.sourceSystem = chezmoi.NewReadOnlySystem(c.sourceSystem) } if c.dryRun || annotations.hasTag(dryRun) { c.sourceSystem = chezmoi.NewDryRunSystem(c.sourceSystem) c.destSystem = chezmoi.NewDryRunSystem(c.destSystem) } if annotations.hasTag(outputsDiff) || c.Verbose && (annotations.hasTag(modifiesDestinationDirectory) || annotations.hasTag(modifiesSourceDirectory)) { // If the user has configured a diff pager, then start it as a process. // Otherwise, write the diff output directly to stdout. var writer io.Writer switch pagerCmd, err := c.getDiffPagerCmd(); { case err != nil: return err case pagerCmd == nil: writer = c.stdout default: pipeReader, pipeWriter := io.Pipe() pagerCmd.Stdin = pipeReader lazyWriter := newLazyWriter(func() (io.WriteCloser, error) { if err := chezmoilog.LogCmdStart(c.logger, pagerCmd); err != nil { return nil, err } return pipeWriter, nil }) writer = lazyWriter c.diffPagerCmd = pagerCmd c.diffPagerCmdStdin = lazyWriter } c.sourceSystem = c.newDiffSystem(c.sourceSystem, writer, c.SourceDirAbsPath) c.destSystem = c.newDiffSystem(c.destSystem, writer, c.DestDirAbsPath) } if err := c.setEncryption(); err != nil && !annotations.hasTag(doesNotRequireValidConfig) { return err } // Create the config directory if needed. if annotations.hasTag(requiresConfigDirectory) { configFileAbsPath, err := c.getConfigFileAbsPath() if err != nil { return err } if err := chezmoi.MkdirAll(c.baseSystem, configFileAbsPath.Dir(), fs.ModePerm); err != nil { return err } } // Create the source directory if needed. if annotations.hasTag(createSourceDirectoryIfNeeded) { if err := chezmoi.MkdirAll(c.baseSystem, c.SourceDirAbsPath, fs.ModePerm); err != nil { return err } } // Verify that the source directory exists and is a directory, if needed. if annotations.hasTag(requiresSourceDirectory) { switch fileInfo, err := c.baseSystem.Stat(c.SourceDirAbsPath); { case err == nil && fileInfo.IsDir(): // Do nothing. case err == nil: return fmt.Errorf("%s: not a directory", c.SourceDirAbsPath) default: return err } } // Create the runtime directory if needed. if annotations.hasTag(runsCommands) { if runtime.GOOS == "linux" && c.bds.RuntimeDir != "" { // Snap sets the $XDG_RUNTIME_DIR environment variable to // /run/user/$uid/snap.$snap_name, but does not create this // directory. Consequently, any spawned processes that need // $XDG_DATA_DIR will fail. As a work-around, create the directory // if it does not exist. See // https://forum.snapcraft.io/t/wayland-dconf-and-xdg-runtime-dir/186/13. if err := chezmoi.MkdirAll(c.baseSystem, chezmoi.NewAbsPath(c.bds.RuntimeDir), 0o700); err != nil { return err } } } // Determine the working tree directory if it is not configured. if c.WorkingTreeAbsPath.IsEmpty() { workingTreeAbsPath := c.SourceDirAbsPath for { gitDirAbsPath := workingTreeAbsPath.JoinString(git.GitDirName) if _, err := c.baseSystem.Stat(gitDirAbsPath); err == nil { c.WorkingTreeAbsPath = workingTreeAbsPath break } prevWorkingTreeDirAbsPath := workingTreeAbsPath workingTreeAbsPath = workingTreeAbsPath.Dir() if workingTreeAbsPath == c.homeDirAbsPath || workingTreeAbsPath.Len() >= prevWorkingTreeDirAbsPath.Len() { c.WorkingTreeAbsPath = c.SourceDirAbsPath break } } } // Create the working tree directory if needed. if annotations.hasTag(requiresWorkingTree) { if _, err := c.SourceDirAbsPath.TrimDirPrefix(c.WorkingTreeAbsPath); err != nil { return err } if err := chezmoi.MkdirAll(c.baseSystem, c.WorkingTreeAbsPath, fs.ModePerm); err != nil { return err } } templateData := c.getTemplateData(cmd) os.Setenv("CHEZMOI", "1") for key, value := range map[string]string{ "ARCH": templateData.arch, "ARGS": strings.Join(templateData.args, " "), "CACHE_DIR": templateData.cacheDir.String(), "COMMAND": templateData.command, "COMMAND_DIR": templateData.commandDir.String(), "CONFIG_FILE": templateData.configFile.String(), "DEST_DIR": templateData.destDir.String(), "EXECUTABLE": templateData.executable.String(), "FQDN_HOSTNAME": templateData.fqdnHostname, "GID": templateData.gid, "GROUP": templateData.group, "HOME_DIR": templateData.homeDir.String(), "HOSTNAME": templateData.hostname, "OS": templateData.os, "SOURCE_DIR": templateData.sourceDir.String(), "UID": templateData.uid, "USERNAME": templateData.username, "WORKING_TREE": templateData.workingTree.String(), } { os.Setenv("CHEZMOI_"+key, value) } if c.Verbose { os.Setenv("CHEZMOI_VERBOSE", "1") } for groupKey, group := range map[string]map[string]any{ "KERNEL": templateData.kernel, "OS_RELEASE": templateData.osRelease, "VERSION": templateData.version, "WINDOWS_VERSION": templateData.windowsVersion, } { for key, value := range group { key := "CHEZMOI_" + groupKey + "_" + camelCaseToUpperSnakeCase(key) var valueStr string switch value := value.(type) { case string: valueStr = value case uint64: valueStr = strconv.FormatUint(value, 10) default: panic(fmt.Errorf("%s has unexpected type %T", key, value)) } os.Setenv(key, valueStr) } } if err := c.setEnvironmentVariables(); err != nil { return err } return c.runHookPre(cmd.Name()) } // persistentStateFile returns the absolute path to the persistent state file, // returning the first persistent file found, and returning the default path if // none are found. func (c *Config) persistentStateFile() (chezmoi.AbsPath, error) { if !c.PersistentStateAbsPath.IsEmpty() { return c.PersistentStateAbsPath, nil } configFileAbsPath, err := c.getConfigFileAbsPath() if err != nil { return chezmoi.EmptyAbsPath, err } return configFileAbsPath.Dir().Join(persistentStateFileRelPath), nil } // progressAutoFunc detects whether progress bars should be displayed. func (c *Config) progressAutoFunc() bool { if stdout, ok := c.stdout.(*os.File); ok { return term.IsTerminal(int(stdout.Fd())) } return false } func (c *Config) newTemplateData(cmd *cobra.Command) *templateData { // Determine the user's username and group, if possible. // // os/user.Current and os/user.LookupGroupId in Go's standard library are // generally unreliable, so work around errors if possible, or ignore them. // // On Android, user.Current always fails. Instead, use $LOGNAME (as this is // set by Termux), or $USER if $LOGNAME is not set. // // If CGO is disabled, then the Go standard library falls back to parsing // /etc/passwd and /etc/group, which will return incorrect results without // error if the system uses an alternative password database such as NIS or // LDAP. // // If CGO is enabled then os/user.Current and os/user.LookupGroupId will use // the underlying libc functions, namely getpwuid_r and getgrnam_r. If // linked with glibc this will return the correct result. If linked with // musl then they will use musl's implementation which, like Go's non-CGO // implementation, also only parses /etc/passwd and /etc/group and so also // returns incorrect results without error if NIS or LDAP are being used. // // On Windows, the user's group ID returned by os/user.Current() is an SID // and no further useful lookup is possible with Go's standard library. // // If os/user.Current fails, then fallback to $USER. // // Since neither the username nor the group are likely widely used in // templates, leave these variables unset if their values cannot be // determined. Unset variables will trigger template errors if used, // alerting the user to the problem and allowing them to find alternative // solutions. var gid, group, uid, username string if runtime.GOOS == "android" { username = cmp.Or(os.Getenv("LOGNAME"), os.Getenv("USER")) } else if currentUser, err := user.Current(); err == nil { gid = currentUser.Gid uid = currentUser.Uid username = currentUser.Username if runtime.GOOS != "windows" { if rawGroup, err := user.LookupGroupId(currentUser.Gid); err == nil { group = rawGroup.Name } else { c.logger.Info("user.LookupGroupId", slog.Any("err", err), slog.String("gid", currentUser.Gid)) } } } else { c.logger.Error("user.Current", slog.Any("err", err)) var ok bool username, ok = os.LookupEnv("USER") if !ok { c.logger.Info("os.LookupEnv", slog.String("key", "USER"), slog.Bool("ok", ok)) } } fqdnHostname, err := chezmoi.FQDNHostname(c.fileSystem) if err != nil { c.logger.Info("chezmoi.FQDNHostname", slog.Any("err", err)) } hostname, _, _ := strings.Cut(fqdnHostname, ".") kernel, err := chezmoi.Kernel(c.fileSystem) if err != nil { c.logger.Info("chezmoi.Kernel", slog.Any("err", err)) } var osRelease map[string]any switch runtime.GOOS { case "openbsd", "windows": // Don't populate osRelease on OSes where /etc/os-release does not // exist. default: if rawOSRelease, err := chezmoi.OSRelease(c.fileSystem); err == nil { osRelease = upperSnakeCaseToCamelCaseMap(rawOSRelease) } else { c.logger.Info("chezmoi.OSRelease", slog.Any("err", err)) } } configFileAbsPath, _ := c.getConfigFileAbsPath() executable, _ := os.Executable() windowsVersion, _ := windowsVersion() sourceDirAbsPath, _ := c.getSourceDirAbsPath(nil) return &templateData{ arch: runtime.GOARCH, args: os.Args, cacheDir: c.CacheDirAbsPath, command: cmd.Name(), commandDir: c.commandDirAbsPath, config: c.toMap(), configFile: configFileAbsPath, destDir: c.DestDirAbsPath, executable: chezmoi.NewAbsPath(executable), fqdnHostname: fqdnHostname, gid: gid, group: group, homeDir: c.homeDirAbsPath, hostname: hostname, kernel: kernel, os: runtime.GOOS, osRelease: osRelease, pathListSeparator: string(os.PathListSeparator), pathSeparator: string(os.PathSeparator), sourceDir: sourceDirAbsPath, uid: uid, username: username, version: map[string]any{ "builtBy": c.versionInfo.BuiltBy, "commit": c.versionInfo.Commit, "date": c.versionInfo.Date, "version": c.versionInfo.Version, }, windowsVersion: windowsVersion, workingTree: c.WorkingTreeAbsPath, } } // readConfig reads the config file, if it exists. func (c *Config) readConfig(configFileAbsPath chezmoi.AbsPath) error { switch err := c.decodeConfigFile(configFileAbsPath, &c.ConfigFile); { case errors.Is(err, fs.ErrNotExist): return nil default: return err } } // resetSourceState clears the cached source state, if any. func (c *Config) resetSourceState() { c.sourceState = nil c.sourceStateErr = nil } // run runs name with args in dir. func (c *Config) run(dir chezmoi.AbsPath, name string, args []string) error { cmd := exec.Command(name, args...) if !dir.IsEmpty() { dirRawAbsPath, err := c.baseSystem.RawPath(dir) if err != nil { return err } cmd.Dir = dirRawAbsPath.String() } cmd.Stdin = c.stdin cmd.Stdout = c.stdout cmd.Stderr = c.stderr if err := chezmoilog.LogCmdRun(c.logger, cmd); err != nil { return fmt.Errorf("%s: %w", name, err) } return nil } // runEditor runs the configured editor with args. func (c *Config) runEditor(args []string) error { if err := c.persistentState.Close(); err != nil { return err } editor, editorArgs, err := c.editor(args) if err != nil { return err } start := time.Now() err = c.run(chezmoi.EmptyAbsPath, editor, editorArgs) if runtime.GOOS != "windows" && c.Edit.MinDuration != 0 { if duration := time.Since(start); duration < c.Edit.MinDuration { c.errorf("warning: %s: returned in less than %s\n", shellQuoteCommand(editor, editorArgs), c.Edit.MinDuration) } } return err } // runHook runs a command or script hook. func (c *Config) runHook(command commandConfig) error { var name string var args []string switch { case command.Command != "" && command.Script != "": return errors.New("cannot specify both command and script") case command.Command != "": name = command.Command args = command.Args case command.Script != "": extension := strings.TrimPrefix(strings.ToLower(path.Ext(command.Script)), ".") if interpreter, ok := c.Interpreters[extension]; ok { name = interpreter.Command args = slices.Concat(interpreter.Args, []string{command.Script}, command.Args) } else { name = command.Script args = command.Args } default: return nil } return c.run(c.homeDirAbsPath, name, args) } // runHookPost runs the hook's post command, if it is set. func (c *Config) runHookPost(hook string) error { if err := c.runHook(c.Hooks[hook].Post); err != nil { return fmt.Errorf("%s: post: %w", hook, err) } return nil } // runHookPre runs the hook's pre command, if it is set. func (c *Config) runHookPre(hook string) error { if err := c.runHook(c.Hooks[hook].Pre); err != nil { return fmt.Errorf("%s: pre: %w", hook, err) } return nil } type runInstallInitShellOptions struct { args []string interactive bool packageManager string shell bool } func (c *Config) runInstallInitShellSh( sourceState *chezmoi.SourceState, command string, commandArgs []string, options runInstallInitShellOptions, ) error { script, err := sourceState.ExecuteTemplateData(chezmoi.ExecuteTemplateDataOptions{ NameRelPath: chezmoi.NewRelPath("install-init-shell.sh.tmpl"), Data: templates.InstallInitShellShTmpl, ExtraData: map[string]any{ "args": options.args, "interactive": options.interactive, "packageManager": options.packageManager, "shell": options.shell, }, }) if err != nil { return err } allArgs := slices.Concat( commandArgs, []string{"sh", "-c", string(script)}, ) return c.run(chezmoi.EmptyAbsPath, command, allArgs) } // setEncryption configures c's encryption. func (c *Config) setEncryption() error { // Override the age recipients for encryption if --age-recipient or // --age-recipient-file is set. switch { case c.ageRecipient != "" && c.ageRecipientFile != "": return errors.New("--age-recipient and --age-recipient-file cannot both be set") case c.ageRecipient != "": c.Age.Recipient = c.ageRecipient c.Age.Recipients = nil c.Age.RecipientsFile = chezmoi.EmptyAbsPath c.Age.RecipientsFiles = nil case c.ageRecipientFile != "": c.Age.Recipient = "" c.Age.Recipients = nil c.Age.RecipientsFile = chezmoi.NewAbsPath(c.ageRecipientFile) c.Age.RecipientsFiles = nil } switch c.Encryption { case "age": c.Age.UseBuiltin = c.UseBuiltinAge.Value(c.useBuiltinAgeAutoFunc) c.encryption = &c.Age case "gpg": c.encryption = &c.GPG case "transparent": c.encryption = chezmoi.TransparentEncryption{} case "": // Detect encryption if any non-default configuration is set, preferring // gpg for backwards compatibility. switch { case !reflect.DeepEqual(c.GPG, defaultGPGEncryptionConfig): c.errorf( "warning: 'encryption' not set, using gpg configuration. " + "Check if 'encryption' is correctly set as the top-level key.\n", ) c.encryption = &c.GPG case !reflect.DeepEqual(c.Age, defaultAgeEncryptionConfig): c.errorf( "warning: 'encryption' not set, using age configuration. " + "Check if 'encryption' is correctly set as the top-level key.\n", ) c.Age.UseBuiltin = c.UseBuiltinAge.Value(c.useBuiltinAgeAutoFunc) c.encryption = &c.Age default: c.encryption = chezmoi.NoEncryption{} } default: return fmt.Errorf("%s: unknown encryption", c.Encryption) } if c.debug { encryptionLogger := c.logger.With(slog.String(logComponentKey, logComponentValueEncryption)) c.encryption = chezmoi.NewDebugEncryption(c.encryption, encryptionLogger) } return nil } // setEnvironmentVariables sets all environment variables defined in c. func (c *Config) setEnvironmentVariables() error { var env map[string]string switch { case len(c.Env) != 0 && len(c.ScriptEnv) != 0: return errors.New("only one of env or scriptEnv may be set") case len(c.Env) != 0: env = c.Env case len(c.ScriptEnv) != 0: env = c.ScriptEnv } for key, value := range env { if strings.HasPrefix(key, "CHEZMOI_") { c.errorf("warning: %s: overriding reserved environment variable\n", key) } if err := os.Setenv(key, value); err != nil { return err } } return nil } // sourceAbsPaths returns the source absolute paths for each target path in // args. func (c *Config) sourceAbsPaths(sourceState *chezmoi.SourceState, args []string) ([]chezmoi.AbsPath, error) { targetRelPaths, err := c.targetRelPaths(sourceState, args, targetRelPathsOptions{ mustBeInSourceState: true, }) if err != nil { return nil, err } sourceAbsPaths := make([]chezmoi.AbsPath, 0, len(targetRelPaths)) for _, targetRelPath := range targetRelPaths { sourceAbsPath := c.SourceDirAbsPath.Join(sourceState.MustEntry(targetRelPath).SourceRelPath().RelPath()) sourceAbsPaths = append(sourceAbsPaths, sourceAbsPath) } return sourceAbsPaths, nil } func (c *Config) targetRelPath(absPath chezmoi.AbsPath) (chezmoi.RelPath, error) { relPath, err := absPath.TrimDirPrefix(c.DestDirAbsPath) if notInAbsDirError := (&chezmoi.NotInAbsDirError{}); errors.As(err, ¬InAbsDirError) { return chezmoi.EmptyRelPath, fmt.Errorf("%s: not in destination directory (%s)", absPath, c.DestDirAbsPath) } return relPath, err } type targetRelPathsOptions struct { mustBeInSourceState bool mustNotBeExternal bool recursive bool } // targetRelPaths returns the target relative paths for each target path in // args. The returned paths are sorted and de-duplicated. func (c *Config) targetRelPaths( sourceState *chezmoi.SourceState, args []string, options targetRelPathsOptions, ) ([]chezmoi.RelPath, error) { targetRelPaths := make([]chezmoi.RelPath, 0, len(args)) for _, arg := range args { argAbsPath, err := chezmoi.NewAbsPathFromExtPath(arg, c.homeDirAbsPath) if err != nil { return nil, err } targetRelPath, err := c.targetRelPath(argAbsPath) if err != nil { return nil, err } sourceStateEntry := sourceState.Get(targetRelPath) if sourceStateEntry == nil { return nil, fmt.Errorf("%s: not managed", arg) } if options.mustBeInSourceState { if _, ok := sourceStateEntry.(*chezmoi.SourceStateRemove); ok { return nil, fmt.Errorf("%s: not in source state", arg) } } if options.mustNotBeExternal { targetStateEntry, err := sourceStateEntry.TargetStateEntry(c.destSystem, c.DestDirAbsPath.Join(targetRelPath)) if err != nil { return nil, err } if targetStateEntry.SourceAttr().External { return nil, fmt.Errorf("%s: is an external", arg) } } targetRelPaths = append(targetRelPaths, targetRelPath) if options.recursive { parentRelPath := targetRelPath // FIXME we should not call s.TargetRelPaths() here - risk of // accidentally quadratic for _, targetRelPath := range sourceState.TargetRelPaths() { if _, err := targetRelPath.TrimDirPrefix(parentRelPath); err == nil { targetRelPaths = append(targetRelPaths, targetRelPath) } } } } if len(targetRelPaths) == 0 { return nil, nil } // Sort and de-duplicate targetRelPaths in place. slices.SortFunc(targetRelPaths, chezmoi.CompareRelPaths) n := 1 for i := 1; i < len(targetRelPaths); i++ { if targetRelPaths[i] != targetRelPaths[i-1] { targetRelPaths[n] = targetRelPaths[i] n++ } } return targetRelPaths[:n], nil } // targetRelPathsBySourcePath returns the target relative paths for each arg in // args. func (c *Config) targetRelPathsBySourcePath(sourceState *chezmoi.SourceState, args []string) ([]chezmoi.RelPath, error) { targetRelPaths := make([]chezmoi.RelPath, len(args)) targetRelPathsBySourceRelPath := make(map[chezmoi.RelPath]chezmoi.RelPath) _ = sourceState.ForEach( func(targetRelPath chezmoi.RelPath, sourceStateEntry chezmoi.SourceStateEntry) error { sourceRelPath := sourceStateEntry.SourceRelPath().RelPath() targetRelPathsBySourceRelPath[sourceRelPath] = targetRelPath return nil }, ) for i, arg := range args { argAbsPath, err := chezmoi.NewAbsPathFromExtPath(arg, c.homeDirAbsPath) if err != nil { return nil, err } sourceRelPath, err := argAbsPath.TrimDirPrefix(c.SourceDirAbsPath) if err != nil { return nil, err } targetRelPath, ok := targetRelPathsBySourceRelPath[sourceRelPath] if !ok { return nil, fmt.Errorf("%s: not in source state", arg) } targetRelPaths[i] = targetRelPath } return targetRelPaths, nil } // targetValidArgs returns target completions for toComplete given args. func (c *Config) targetValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if !c.Completion.Custom { return nil, cobra.ShellCompDirectiveDefault } toCompleteAbsPath, err := chezmoi.NewAbsPathFromExtPath(toComplete, c.homeDirAbsPath) if err != nil { cobra.CompErrorln(err.Error()) return nil, cobra.ShellCompDirectiveError } sourceState, err := c.getSourceState(cmd.Context(), cmd) if err != nil { cobra.CompErrorln(err.Error()) return nil, cobra.ShellCompDirectiveError } var completions []string if err := sourceState.ForEach(func(targetRelPath chezmoi.RelPath, sourceStateEntry chezmoi.SourceStateEntry) error { completion := c.DestDirAbsPath.Join(targetRelPath).String() if _, ok := sourceStateEntry.(*chezmoi.SourceStateDir); ok { completion += "/" } if strings.HasPrefix(completion, toCompleteAbsPath.String()) { completions = append(completions, completion) } return nil }); err != nil { cobra.CompErrorln(err.Error()) return nil, cobra.ShellCompDirectiveError } if !filepath.IsAbs(toComplete) { for i, completion := range completions { completions[i] = strings.TrimPrefix(completion, c.commandDirAbsPath.String()+"/") } } return completions, cobra.ShellCompDirectiveNoFileComp } // tempDir returns the temporary directory for the given key, creating it if // needed. func (c *Config) tempDir(key string) (chezmoi.AbsPath, error) { if tempDirAbsPath, ok := c.tempDirs[key]; ok { return tempDirAbsPath, nil } tempDir, err := os.MkdirTemp(c.TempDir.String(), key) chezmoilog.InfoOrError(c.logger, "MkdirTemp", err, slog.String("tempDir", tempDir)) if err != nil { return chezmoi.EmptyAbsPath, err } tempDirAbsPath := chezmoi.NewAbsPath(tempDir) c.tempDirs[key] = tempDirAbsPath if runtime.GOOS != "windows" { if err := os.Chmod(tempDir, 0o700); err != nil { return chezmoi.EmptyAbsPath, err } } return tempDirAbsPath, nil } // useBuiltinAgeAutoFunc detects whether the builtin age should be used. func (c *Config) useBuiltinAgeAutoFunc() bool { if _, err := chezmoi.LookPath(c.Age.Command); err == nil { return false } return true } // useBuiltinGitAutoFunc detects whether the builtin git should be used. func (c *Config) useBuiltinGitAutoFunc() bool { if _, err := chezmoi.LookPath(c.Git.Command); err == nil { return false } return true } // writeOutput writes data to the configured output. func (c *Config) writeOutput(data []byte, perm fs.FileMode) error { if c.outputAbsPath.IsEmpty() || c.outputAbsPath == chezmoi.NewAbsPath("-") { _, err := c.stdout.Write(data) return err } return os.WriteFile(c.outputAbsPath.String(), data, perm) } type writePathsOptions struct { nulPathSeparator bool tree bool } func (c *Config) writePaths(paths []string, options writePathsOptions) error { pathSeparator := byte('\n') if options.nulPathSeparator { pathSeparator = '\x00' } builder := strings.Builder{} if options.tree { newPathListTreeFromPathsSlice(paths).writeChildren(&builder, "", " ") } else { slices.Sort(paths) for _, path := range paths { builder.WriteString(path) builder.WriteByte(pathSeparator) } } return c.writeOutputString(builder.String(), 0o666) } // writeOutputString writes data to the configured output. func (c *Config) writeOutputString(data string, perm fs.FileMode) error { return c.writeOutput([]byte(data), perm) } func newConfigFile(bds *xdg.BaseDirectorySpecification) ConfigFile { return ConfigFile{ // Global configuration. CacheDirAbsPath: chezmoi.NewAbsPath(bds.CacheHome).Join(chezmoiRelPath), Color: autoBool{ auto: true, }, Data: make(map[string]any), Interpreters: DefaultInterpreters, Mode: chezmoi.ModeFile, Pager: os.Getenv("PAGER"), Progress: autoBool{ auto: true, }, PINEntry: pinEntryConfig{ Options: pinEntryDefaultOptions, }, Safe: true, TempDir: chezmoi.NewAbsPath(os.TempDir()), Template: templateConfig{ Options: chezmoi.DefaultTemplateOptions, }, Umask: chezmoi.Umask, UseBuiltinAge: autoBool{ auto: true, }, UseBuiltinGit: autoBool{ auto: true, }, Warnings: warningsConfig{ ConfigFileTemplateHasChanged: true, }, // Password manager configurations. Bitwarden: bitwardenConfig{ Command: "bw", }, BitwardenSecrets: bitwardenSecretsConfig{ Command: "bws", }, Dashlane: dashlaneConfig{ Command: "dcli", }, Doppler: dopplerConfig{ Command: "doppler", }, Ejson: ejsonConfig{ KeyDir: cmp.Or(os.Getenv("EJSON_KEYDIR"), "/opt/ejson/keys"), }, Gopass: gopassConfig{ Command: "gopass", }, Keepassxc: keepassxcConfig{ Command: "keepassxc-cli", Prompt: true, Mode: keepassxcModeCachePassword, }, Keeper: keeperConfig{ Command: "keeper", }, Lastpass: lastpassConfig{ Command: "lpass", }, Onepassword: onepasswordConfig{ Command: "op", Prompt: true, Mode: onepasswordModeAccount, }, Pass: passConfig{ Command: "pass", }, Passhole: passholeConfig{ Command: "ph", Prompt: true, }, ProtonPass: protonPassConfig{ Command: "pass-cli", }, RBW: rbwConfig{ Command: "rbw", }, Vault: vaultConfig{ Command: "vault", }, // Encryption configurations. Age: defaultAgeEncryptionConfig, GPG: defaultGPGEncryptionConfig, // Command configurations. Add: addCmdConfig{ Secrets: newChoiceFlag("warning", severityValues), filter: chezmoi.NewEntryTypeFilter(chezmoi.EntryTypesAll, chezmoi.EntryTypesNone), recursive: true, }, Diff: diffCmdConfig{ Exclude: chezmoi.NewEntryTypeSet(chezmoi.EntryTypesNone), Pager: defaultSentinel, ScriptContents: true, include: chezmoi.NewEntryTypeSet(chezmoi.EntryTypesAll), }, Docker: dockerCmdConfig{ Command: "docker", exec: dockerExecCmdConfig{ interactive: true, shell: true, }, }, Edit: editCmdConfig{ Hardlink: true, MinDuration: 1 * time.Second, filter: chezmoi.NewEntryTypeFilter(chezmoi.EntryTypesAll, chezmoi.EntryTypesNone), }, Format: newChoiceFlag(formatJSON, readDataFormatValues), Git: gitCmdConfig{ Command: "git", }, GitHub: gitHubConfig{ RefreshPeriod: 1 * time.Minute, }, Merge: mergeCmdConfig{ Command: "vimdiff", }, Status: statusCmdConfig{ Exclude: chezmoi.NewEntryTypeSet(chezmoi.EntryTypesNone), PathStyle: newChoiceFlag(pathStyleRelative, targetPathStyleValues), include: chezmoi.NewEntryTypeSet(chezmoi.EntryTypesAll), recursive: true, }, Update: updateCmdConfig{ Apply: true, RecurseSubmodules: true, filter: chezmoi.NewEntryTypeFilter(chezmoi.EntryTypesAll, chezmoi.EntryTypesNone), recursive: true, }, Verify: verifyCmdConfig{ Exclude: chezmoi.NewEntryTypeSet(chezmoi.EntryTypesNone), include: chezmoi.NewEntryTypeSet(chezmoi.EntryTypesAll), recursive: true, }, } } func (f *ConfigFile) toMap() map[string]any { // Make a copy of f and replace any default sentinels with the empty string // to ensure that there are no default sentinels in the result. configFile := *f if configFile.Diff.Pager == defaultSentinel { configFile.Diff.Pager = "" } // This is a horrible hack. We want the returned map to contain only simple // types because they are used with masterminds/sprig template functions // which don't accept fmt.Stringers in place of strings. As a work-around, // round-trip via JSON. data, err := json.Marshal(configFile) if err != nil { return nil } var result map[string]any must(json.Unmarshal(data, &result)) return result } func parseCommand(command string, args []string) (string, []string, error) { // If command is found, then return it. if commandPath, err := chezmoi.LookPath(command); err == nil { return commandPath, args, nil } // Otherwise, if the command contains spaces, parse it as a shell command. if whitespaceRx.MatchString(command) { var words []*syntax.Word for word, err := range syntax.NewParser().WordsSeq(strings.NewReader(command)) { if err != nil { return "", nil, err } words = append(words, word) } switch fields, err := expand.Fields(&expand.Config{ Env: expand.FuncEnviron(os.Getenv), }, words...); { case err != nil: return "", nil, err case len(fields) > 1: return fields[0], append(fields[1:], args...), nil case len(fields) == 1: return fields[0], args, nil } } // Fallback to the command only. return command, args, nil } // prependParentRelPaths returns a new slice of RelPaths where the parents of // each RelPath appear before each RelPath. func prependParentRelPaths(relPaths []chezmoi.RelPath) []chezmoi.RelPath { // For each target relative path, enumerate its parents from the root down // and insert any parents which have not yet been seen. result := make([]chezmoi.RelPath, 0, len(relPaths)) seenRelPaths := make(map[chezmoi.RelPath]struct{}, len(relPaths)) for _, relPath := range relPaths { components := relPath.SplitAll() for i := 1; i < len(components); i++ { parentRelPath := chezmoi.EmptyRelPath.Join(components[:i]...) if _, ok := seenRelPaths[parentRelPath]; !ok { result = append(result, parentRelPath) seenRelPaths[parentRelPath] = struct{}{} } } result = append(result, relPath) seenRelPaths[relPath] = struct{}{} } return result } // registerCommonFlagCompletionFuncs registers completion functions for cmd's // common flags, recursively. It panics on any error. func registerCommonFlagCompletionFuncs(cmd *cobra.Command) { cmd.Flags().VisitAll(func(flag *pflag.Flag) { if _, exist := cmd.GetFlagCompletionFunc(flag.Name); exist { return } if flagCompletionFunc, ok := commonFlagCompletionFuncs[flag.Name]; ok { must(cmd.RegisterFlagCompletionFunc(flag.Name, flagCompletionFunc)) } }) for _, command := range cmd.Commands() { registerCommonFlagCompletionFuncs(command) } } // sha256Sum returns the SHA256 sum of data. func sha256Sum(data []byte) []byte { sha256SumArr := sha256.Sum256(data) return sha256SumArr[:] } // withVersionInfo sets the version information. func withVersionInfo(versionInfo VersionInfo) configOption { return func(c *Config) error { var version *semver.Version var versionElems []string if versionInfo.Version != "" { var err error version, err = semver.NewVersion(strings.TrimPrefix(versionInfo.Version, "v")) if err != nil { return err } versionElems = append(versionElems, "v"+version.String()) } else { versionElems = append(versionElems, "dev") } if versionInfo.Commit != "" { versionElems = append(versionElems, "commit "+versionInfo.Commit) } if versionInfo.Date != "" { date := versionInfo.Date if sec, err := strconv.ParseInt(date, 10, 64); err == nil { date = time.Unix(sec, 0).UTC().Format(time.RFC3339) } versionElems = append(versionElems, "built at "+date) } if versionInfo.BuiltBy != "" { versionElems = append(versionElems, "built by "+versionInfo.BuiltBy) } if version != nil { c.version = *version } c.versionInfo = versionInfo c.versionStr = strings.Join(versionElems, ", ") return nil } } ================================================ FILE: internal/cmd/config_tags_test.go ================================================ package cmd import ( "fmt" "reflect" "strings" "testing" ) var expectedTags = []string{"json", "yaml", "mapstructure"} func TestExportedFieldsHaveMatchingMarshalTags(t *testing.T) { failed, errMsg := verifyTagsArePresentAndMatch(reflect.TypeFor[ConfigFile]()) if failed { t.Error(errMsg) } } func fieldTypesNeedsVerification(ft reflect.Type) []reflect.Type { kind := ft.Kind() if kind < reflect.Array || kind == reflect.String { // its a ~scalar type return []reflect.Type{} } else if kind == reflect.Struct { return []reflect.Type{ft} } switch kind { case reflect.Pointer: fallthrough case reflect.Array: fallthrough case reflect.Slice: return fieldTypesNeedsVerification(ft.Elem()) case reflect.Map: return append(fieldTypesNeedsVerification(ft.Key()), fieldTypesNeedsVerification(ft.Elem())...) default: return []reflect.Type{} // ... we'll assume interface types, funcs, channels are okay. } } func verifyTagsArePresentAndMatch(structType reflect.Type) (failed bool, errMsg string) { name := structType.Name() fields := reflect.VisibleFields(structType) failed = false var errs strings.Builder for _, f := range fields { if !f.IsExported() { continue } ts := f.Tag tagValueGroups := make(map[string][]string) for _, tagName := range expectedTags { tagValue, tagPresent := ts.Lookup(tagName) if !tagPresent { fmt.Fprintf(&errs, "\n%s field %s is missing a `%s:` tag", name, f.Name, tagName) failed = true } matchingTags, notFirstOccurrence := tagValueGroups[tagValue] if notFirstOccurrence { tagValueGroups[tagValue] = append(matchingTags, tagName) } else { tagValueGroups[tagValue] = []string{tagName} } } if len(tagValueGroups) > 1 { fmt.Fprintf(&errs, "\n%s field %s has non-matching tag names:", name, f.Name) for value, tagsMatching := range tagValueGroups { if len(tagsMatching) == 1 { fmt.Fprintf(&errs, "\n %s says %q", tagsMatching[0], value) } else { fmt.Fprintf(&errs, "\n (%s) each say %q", strings.Join(tagsMatching, ", "), value) } } failed = true } verifyTypes := fieldTypesNeedsVerification(f.Type) for _, ft := range verifyTypes { subFailed, subErrs := verifyTagsArePresentAndMatch(ft) if subFailed { fmt.Fprintf(&errs, "\n In %s.%s:", name, f.Name) errs.WriteString(strings.ReplaceAll(subErrs, "\n", "\n ")) failed = true } } } return failed, errs.String() } ================================================ FILE: internal/cmd/config_test.go ================================================ package cmd import ( "bytes" "fmt" "io" "io/fs" "path/filepath" "reflect" "runtime" "slices" "strconv" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-xdg/v6" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoiset" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestConfigFileFieldTagNamesMatch(t *testing.T) { expectedTags := []string{"json", "mapstructure", "yaml"} for _, field := range reflect.VisibleFields(reflect.TypeFor[ConfigFile]()) { t.Run(field.Name, func(t *testing.T) { tagValues := chezmoiset.New[string]() for _, tagName := range expectedTags { tagValue, ok := field.Tag.Lookup(tagName) assert.True(t, ok, "missing %s tag", tagName) tagValues.Add(tagValue) } elements := slices.Sorted(tagValues.Elements()) assert.Equal(t, []string{elements[0]}, elements, "inconsistent tag values") }) } } func TestAddTemplateFuncPanic(t *testing.T) { chezmoitest.WithTestFS(t, nil, func(fileSystem vfs.FS) { config := newTestConfig(t, fileSystem) assert.NotPanics(t, func() { config.addTemplateFunc("func", nil) }) assert.Panics(t, func() { config.addTemplateFunc("func", nil) }) }) } func TestConfigFileFormatRoundTrip(t *testing.T) { for _, format := range []chezmoi.Format{ chezmoi.FormatJSON, chezmoi.FormatYAML, } { t.Run(format.Name(), func(t *testing.T) { configFile := ConfigFile{ Color: autoBool{auto: true}, Data: map[string]any{}, Env: map[string]string{}, Hooks: map[string]hookConfig{}, Interpreters: map[string]chezmoi.Interpreter{}, Mode: chezmoi.ModeFile, PINEntry: pinEntryConfig{ Args: []string{}, Options: []string{}, }, ScriptEnv: map[string]string{}, Template: templateConfig{ Options: []string{}, }, TextConv: []*textConvElement{}, UseBuiltinAge: autoBool{value: false}, UseBuiltinGit: autoBool{value: true}, Dashlane: dashlaneConfig{ Args: []string{}, }, Doppler: dopplerConfig{ Args: []string{}, }, Keepassxc: keepassxcConfig{ Args: []string{}, }, Keeper: keeperConfig{ Args: []string{}, }, Passhole: passholeConfig{ Args: []string{}, }, Secret: secretConfig{ Args: []string{}, }, Age: chezmoi.AgeEncryption{ Args: []string{}, Identity: chezmoi.NewAbsPath("/identity.txt"), Identities: []chezmoi.AbsPath{}, Recipients: []string{}, RecipientsFiles: []chezmoi.AbsPath{}, }, GPG: chezmoi.GPGEncryption{ Args: []string{}, Recipients: []string{}, }, Add: addCmdConfig{ Secrets: newChoiceFlag(severityWarning, nil), }, CD: cdCmdConfig{ Args: []string{}, }, Diff: diffCmdConfig{ Args: []string{}, }, Edit: editCmdConfig{ Args: []string{}, }, Merge: mergeCmdConfig{ Args: []string{}, }, Update: updateCmdConfig{ Args: []string{}, }, } data, err := format.Marshal(configFile) assert.NoError(t, err) var actualConfigFile ConfigFile assert.NoError(t, format.Unmarshal(data, &actualConfigFile)) assert.Equal(t, configFile, actualConfigFile) }) } } func TestParseCommand(t *testing.T) { for i, tc := range []struct { command string args []string expectedCommand string expectedArgs []string expectedErr bool }{ { command: "chezmoi-editor", expectedCommand: "chezmoi-editor", }, { command: `chezmoi-editor -f --nomru -c "au VimLeave * !open -a Terminal"`, expectedCommand: "chezmoi-editor", expectedArgs: []string{"-f", "--nomru", "-c", "au VimLeave * !open -a Terminal"}, }, { command: `"chezmoi editor" $CHEZMOI_TEST_VAR`, args: []string{"extra-arg"}, expectedCommand: "chezmoi editor", expectedArgs: []string{"chezmoi-test-value", "extra-arg"}, }, { command: `"chezmoi editor`, expectedErr: true, }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { t.Setenv("CHEZMOI_TEST_VAR", "chezmoi-test-value") actualCommand, actualArgs, err := parseCommand(tc.command, tc.args) if tc.expectedErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedCommand, actualCommand) assert.Equal(t, tc.expectedArgs, actualArgs) } }) } } func TestParseConfig(t *testing.T) { for _, tc := range []struct { name string filename string contents string expectedColor bool }{ { name: "json_bool", filename: "chezmoi.json", contents: chezmoitest.JoinLines( `{`, ` "color":true`, `}`, ), expectedColor: true, }, { name: "json_string", filename: "chezmoi.json", contents: chezmoitest.JoinLines( `{`, ` "color":"on"`, `}`, ), expectedColor: true, }, { name: "toml_bool", filename: "chezmoi.toml", contents: chezmoitest.JoinLines( `color = true`, ), expectedColor: true, }, { name: "toml_string", filename: "chezmoi.toml", contents: chezmoitest.JoinLines( `color = "y"`, ), expectedColor: true, }, { name: "yaml_bool", filename: "chezmoi.yaml", contents: chezmoitest.JoinLines( `color: true`, ), expectedColor: true, }, { name: "yaml_string", filename: "chezmoi.yaml", contents: chezmoitest.JoinLines( `color: "yes"`, ), expectedColor: true, }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, map[string]any{ "/home/user/.config/chezmoi/" + tc.filename: tc.contents, }, func(fileSystem vfs.FS) { c := newTestConfig(t, fileSystem) assert.NoError(t, c.execute([]string{"init"})) assert.Equal(t, tc.expectedColor, c.Color.Value(c.colorAutoFunc)) }) }) } } func TestPrependParentRelPaths(t *testing.T) { for _, tc := range []struct { name string relPathStrs []string expectedRelPathStrs []string }{ { name: "empty", }, { name: "single", relPathStrs: []string{"a"}, expectedRelPathStrs: []string{"a"}, }, { name: "multiple", relPathStrs: []string{"a", "b", "c"}, expectedRelPathStrs: []string{"a", "b", "c"}, }, { name: "single_parent", relPathStrs: []string{"a/b"}, expectedRelPathStrs: []string{"a", "a/b"}, }, { name: "multiple_parents", relPathStrs: []string{"a/b/c"}, expectedRelPathStrs: []string{"a", "a/b", "a/b/c"}, }, { name: "duplicate_parents", relPathStrs: []string{"a/b/c", "a/b/d"}, expectedRelPathStrs: []string{"a", "a/b", "a/b/c", "a/b/d"}, }, } { t.Run(tc.name, func(t *testing.T) { relPaths := make([]chezmoi.RelPath, len(tc.relPathStrs)) for i, relPathStr := range tc.relPathStrs { relPaths[i] = chezmoi.NewRelPath(relPathStr) } expected := make([]chezmoi.RelPath, len(tc.expectedRelPathStrs)) for i, relPathStr := range tc.expectedRelPathStrs { expected[i] = chezmoi.NewRelPath(relPathStr) } assert.Equal(t, expected, prependParentRelPaths(relPaths)) }) } } func TestInitConfigWithIncludedTemplate(t *testing.T) { mainFilename := ".chezmoi.yaml.tmpl" secondaryFilename := "personal.config.yaml.tmpl" mainContents := chezmoitest.JoinLines( `color: true`, fmt.Sprintf(`{{ includeTemplate %q . }}`, secondaryFilename), ) secondaryContents := chezmoitest.JoinLines( `verbose: true`, `safe: {{ stdinIsATTY }}`, ) chezmoitest.WithTestFS(t, map[string]any{ "/home/user/.local/share/chezmoi/" + mainFilename: mainContents, "/home/user/.local/share/chezmoi/" + secondaryFilename: secondaryContents, }, func(fileSystem vfs.FS) { c := newTestConfig(t, fileSystem) assert.NoError(t, c.execute([]string{"init"})) assert.True(t, c.Color.Value(c.colorAutoFunc)) assert.True(t, c.Verbose) assert.False(t, c.Safe) }) } func TestUpperSnakeCaseToCamelCase(t *testing.T) { for s, expected := range map[string]string{ "BUG_REPORT_URL": "bugReportURL", "ID": "id", "ID_LIKE": "idLike", "NAME": "name", "VERSION_CODENAME": "versionCodename", "VERSION_ID": "versionID", } { assert.Equal(t, expected, upperSnakeCaseToCamelCase(s)) } } func TestIssue3980(t *testing.T) { tests := []struct { name string filename string contents string expectsErr bool warns []string expectedEncryptionType any usesBuiltinAge bool }{ { name: "empty_config", filename: "chezmoi.toml", contents: chezmoitest.JoinLines( "", ), expectsErr: false, warns: nil, expectedEncryptionType: chezmoi.NoEncryption{}, usesBuiltinAge: false, }, { name: "empty_encryption_no_configs", filename: "chezmoi.toml", contents: chezmoitest.JoinLines( "encryption = \"\"", ), expectsErr: false, warns: nil, expectedEncryptionType: chezmoi.NoEncryption{}, usesBuiltinAge: false, }, { name: "valid_age_config", filename: "chezmoi.toml", contents: chezmoitest.JoinLines( "encryption = \"age\"", "[age]", "command = \"fakeage\"", "identity = \"key.txt\"", ), expectsErr: false, warns: nil, expectedEncryptionType: &chezmoi.AgeEncryption{}, usesBuiltinAge: true, }, { name: "valid_age_config_no_builtin", filename: "chezmoi.toml", contents: chezmoitest.JoinLines( "encryption = \"age\"", "useBuiltinAge = \"off\"", "[age]", "command = \"fakeage\"", "identity = \"key.txt\"", ), expectsErr: false, warns: nil, expectedEncryptionType: &chezmoi.AgeEncryption{}, usesBuiltinAge: false, }, { name: "valid_gpg_config", filename: "chezmoi.toml", contents: chezmoitest.JoinLines( "encryption = \"gpg\"", "[gpg]", "command = \"fakegpg\"", "symmetric = true", ), expectsErr: false, warns: nil, expectedEncryptionType: &chezmoi.GPGEncryption{}, usesBuiltinAge: false, }, { name: "unset_encryption_uses_gpg", filename: "chezmoi.toml", contents: chezmoitest.JoinLines( "[gpg]", "command = \"fakegpg\"", "symmetric = true", ), expectsErr: false, warns: []string{"warning: 'encryption' not set, using gpg configuration"}, expectedEncryptionType: &chezmoi.GPGEncryption{}, usesBuiltinAge: false, }, { name: "unset_encryption_uses_age", filename: "chezmoi.toml", contents: chezmoitest.JoinLines( "[age]", "command = \"fakeage\"", "identity = \"key.txt\"", ), expectsErr: false, warns: []string{"warning: 'encryption' not set, using age configuration"}, expectedEncryptionType: &chezmoi.AgeEncryption{}, usesBuiltinAge: true, }, { name: "unset_encryption_uses_age_no_builtin", filename: "chezmoi.toml", contents: chezmoitest.JoinLines( "useBuiltinAge = \"off\"", "[age]", "command = \"fakeage\"", "identity = \"key.txt\"", ), expectsErr: false, warns: []string{"warning: 'encryption' not set, using age configuration"}, expectedEncryptionType: &chezmoi.AgeEncryption{}, usesBuiltinAge: false, }, { name: "unset_encryption_gpg_priority", filename: "chezmoi.toml", contents: chezmoitest.JoinLines( "[age]", "command = \"fakeage\"", "identity = \"key.txt\"", "[gpg]", "command = \"fakegpg\"", "symmetric = true", ), expectsErr: false, warns: []string{"warning: 'encryption' not set, using gpg configuration"}, expectedEncryptionType: &chezmoi.GPGEncryption{}, usesBuiltinAge: false, }, { name: "unknown_encryption", filename: "chezmoi.toml", contents: chezmoitest.JoinLines( "encryption = \"unknown\"", ), expectsErr: true, warns: nil, usesBuiltinAge: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { chezmoitest.WithTestFS(t, map[string]any{ "/home/user/.config/chezmoi/" + tt.filename: tt.contents, }, func(fileSystem vfs.FS) { c := newTestConfig(t, fileSystem) // Create a buffer to capture stderr. var stderr bytes.Buffer c.stderr = &stderr err := c.execute([]string{"init"}) if tt.expectsErr { assert.Error(t, err) } else { assert.NoError(t, err) } assert.Equal(t, reflect.TypeOf(tt.expectedEncryptionType), reflect.TypeOf(c.encryption)) assert.Equal(t, tt.usesBuiltinAge, c.Age.UseBuiltin) for _, warn := range tt.warns { assert.Contains(t, stderr.String(), warn) } }) }) } } func newTestConfig(t *testing.T, fileSystem vfs.FS, options ...configOption) *Config { t.Helper() system := chezmoi.NewRealSystem(fileSystem) config, err := newConfig( append([]configOption{ withBaseSystem(system), withDestSystem(system), withSourceSystem(system), withTestFS(fileSystem), withTestUser(t, "user"), withUmask(chezmoitest.Umask), withVersionInfo(VersionInfo{ Version: "2.0.0", }), }, options...)..., ) assert.NoError(t, err) return config } func withBaseSystem(baseSystem chezmoi.System) configOption { return func(c *Config) error { c.baseSystem = baseSystem return nil } } func withDestSystem(destSystem chezmoi.System) configOption { return func(c *Config) error { c.destSystem = destSystem return nil } } func withNoTTY(noTTY bool) configOption { //nolint:unparam return func(c *Config) error { c.noTTY = noTTY return nil } } func withSourceSystem(sourceSystem chezmoi.System) configOption { return func(c *Config) error { c.sourceSystem = sourceSystem return nil } } func withStdin(stdin io.Reader) configOption { return func(c *Config) error { c.stdin = stdin return nil } } func withStdout(stdout io.Writer) configOption { return func(c *Config) error { c.stdout = stdout return nil } } func withTestFS(fileSystem vfs.FS) configOption { return func(c *Config) error { c.fileSystem = fileSystem return nil } } func withTestUser(t *testing.T, username string) configOption { t.Helper() return func(config *Config) error { var env string switch runtime.GOOS { case "plan9": config.homeDir = "/home/" + username env = "home" case "windows": config.homeDir = "C:\\home\\" + username env = "USERPROFILE" default: config.homeDir = "/home/" + username env = "HOME" } t.Setenv(env, config.homeDir) var err error config.homeDirAbsPath, err = chezmoi.NormalizePath(config.homeDir) if err != nil { t.Fatal(err) } config.CacheDirAbsPath = config.homeDirAbsPath.JoinString(".cache", "chezmoi") config.SourceDirAbsPath = config.homeDirAbsPath.JoinString(".local", "share", "chezmoi") config.DestDirAbsPath = config.homeDirAbsPath config.Umask = 0o22 configHome := filepath.Join(config.homeDir, ".config") dataHome := filepath.Join(config.homeDir, ".local", "share") config.bds = &xdg.BaseDirectorySpecification{ ConfigHome: configHome, ConfigDirs: []string{configHome}, DataHome: dataHome, DataDirs: []string{dataHome}, CacheHome: filepath.Join(config.homeDir, ".cache"), RuntimeDir: filepath.Join(config.homeDir, ".run"), } return nil } } func withUmask(umask fs.FileMode) configOption { return func(c *Config) error { c.Umask = umask return nil } } ================================================ FILE: internal/cmd/dashlanetemplatefuncs.go ================================================ package cmd import ( "encoding/json" "os" "os/exec" "slices" "chezmoi.io/chezmoi/internal/chezmoilog" ) type dashlaneConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Args []string `json:"args" mapstructure:"args" yaml:"args"` cacheNote map[string]any cachePassword map[string]any } func (c *Config) dashlaneNoteTemplateFunc(filter string) any { if data, ok := c.Dashlane.cacheNote[filter]; ok { return data } if c.Dashlane.cacheNote == nil { c.Dashlane.cacheNote = make(map[string]any) } output := mustValue(c.dashlaneOutput("note", filter)) data := string(output) c.Dashlane.cacheNote[filter] = data return data } func (c *Config) dashlanePasswordTemplateFunc(filter string) any { if data, ok := c.Dashlane.cachePassword[filter]; ok { return data } if c.Dashlane.cachePassword == nil { c.Dashlane.cachePassword = make(map[string]any) } output := mustValue(c.dashlaneOutput("password", "--output", "json", filter)) var data any must(json.Unmarshal(output, &data)) c.Dashlane.cachePassword[filter] = data return data } func (c *Config) dashlaneOutput(args ...string) ([]byte, error) { name := c.Dashlane.Command args = append(slices.Clone(c.Dashlane.Args), args...) cmd := exec.Command(name, args...) cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, err } return output, nil } ================================================ FILE: internal/cmd/datacmd.go ================================================ package cmd import ( "cmp" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type dataCmdConfig struct { format *choiceFlag } func (c *Config) newDataCmd() *cobra.Command { dataCmd := &cobra.Command{ GroupID: groupIDTemplate, Use: "data", Short: "Print the template data", Long: mustLongHelp("data"), Example: example("data"), Args: cobra.NoArgs, ValidArgsFunction: cobra.NoFileCompletions, RunE: c.runDataCmd, Annotations: newAnnotations( persistentStateModeReadOnly, ), } dataCmd.Flags().VarP(c.data.format, "format", "f", "Output format") must(dataCmd.RegisterFlagCompletionFunc("format", c.data.format.FlagCompletionFunc())) return dataCmd } func (c *Config) runDataCmd(cmd *cobra.Command, args []string) error { sourceState, err := c.newSourceState(cmd.Context(), cmd, chezmoi.WithTemplateDataOnly(true), ) if err != nil { return err } return c.marshal(cmp.Or(c.data.format.String(), c.Format.String()), sourceState.TemplateData()) } ================================================ FILE: internal/cmd/datacmd_test.go ================================================ package cmd import ( "encoding/json" "strings" "testing" "github.com/alecthomas/assert/v2" "github.com/goccy/go-yaml" "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestDataCmd(t *testing.T) { for _, tc := range []struct { format string unmarshal func([]byte, any) error root map[string]any }{ { format: "json", unmarshal: json.Unmarshal, root: map[string]any{ "home/user/.config/chezmoi/chezmoi.json": chezmoitest.JoinLines( `{`, ` "mode": "symlink",`, ` "sourceDir": "/tmp/source",`, ` "encryption": "age",`, ` "age": {`, ` "args": [`, ` "arg"`, ` ],`, ` "identity": "/my-age-identity"`, ` },`, ` "data": {`, ` "test": true`, ` }`, `}`, ), }, }, { format: "yaml", unmarshal: yaml.Unmarshal, root: map[string]any{ "home/user/.config/chezmoi/chezmoi.yaml": chezmoitest.JoinLines( `mode: symlink`, `sourceDir: /tmp/source`, `encryption: age`, `age:`, ` args:`, ` - arg`, ` identity: /my-age-identity`, `data:`, ` test: true`, ), }, }, } { t.Run(tc.format, func(t *testing.T) { chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { args := []string{ "data", "--format", tc.format, } stdout := strings.Builder{} config := newTestConfig(t, fileSystem, withStdout(&stdout)) assert.NoError(t, config.execute(args)) var data struct { Chezmoi struct { Config struct { Age struct { Args []string `json:"args" yaml:"args"` Identity string `json:"identity" yaml:"identity"` } `json:"age" yaml:"age"` Mode string `json:"mode" yaml:"mode"` } `json:"config" yaml:"config"` SourceDir string `json:"sourceDir" yaml:"sourceDir"` } `json:"chezmoi" yaml:"chezmoi"` Test bool `json:"test" yaml:"test"` } assert.NoError(t, tc.unmarshal([]byte(stdout.String()), &data)) assert.Equal(t, []string{"arg"}, data.Chezmoi.Config.Age.Args) normalizedAgeIdentity, err := chezmoi.NormalizePath("/my-age-identity") assert.NoError(t, err) assert.Equal(t, normalizedAgeIdentity.String(), data.Chezmoi.Config.Age.Identity) assert.Equal(t, "symlink", data.Chezmoi.Config.Mode) normalizedSourceDir, err := chezmoi.NormalizePath("/tmp/source") assert.NoError(t, err) assert.Equal(t, normalizedSourceDir.String(), data.Chezmoi.SourceDir) assert.True(t, data.Test) }) }) } } ================================================ FILE: internal/cmd/dataformat.go ================================================ package cmd const ( formatUnknown = "" formatJSON = "json" formatTOML = "toml" formatYAML = "yaml" ) var ( readDataFormatValues = []string{ formatUnknown, formatJSON, formatTOML, formatYAML, } writeDataFormatValues = []string{ formatUnknown, formatJSON, formatYAML, } ) ================================================ FILE: internal/cmd/decryptcmd.go ================================================ package cmd import ( "github.com/spf13/cobra" ) func (c *Config) newDecryptCommand() *cobra.Command { decryptCmd := &cobra.Command{ GroupID: groupIDEncryption, Use: "decrypt [file...]", Short: "Decrypt file or standard input", Long: mustLongHelp("decrypt"), Example: example("decrypt"), RunE: c.runDecryptCmd, Annotations: newAnnotations( persistentStateModeReadOnly, ), } return decryptCmd } func (c *Config) runDecryptCmd(cmd *cobra.Command, args []string) error { return c.filterInput(args, c.encryption.Decrypt) } ================================================ FILE: internal/cmd/destroycmd.go ================================================ package cmd import ( "errors" "fmt" "io/fs" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type destroyCmdConfig struct { recursive bool } func (c *Config) newDestroyCmd() *cobra.Command { destroyCmd := &cobra.Command{ GroupID: groupIDMigration, Use: "destroy target...", Short: "Permanently delete an entry from the source state, the destination directory, and the state", Long: mustLongHelp("destroy"), Example: example("destroy"), ValidArgsFunction: c.targetValidArgs, Args: cobra.MinimumNArgs(1), RunE: c.makeRunEWithSourceState(c.runDestroyCmd), Annotations: newAnnotations( modifiesDestinationDirectory, modifiesSourceDirectory, persistentStateModeReadWrite, ), } destroyCmd.Flags().BoolVarP(&c.destroy.recursive, "recursive", "r", c.destroy.recursive, "Recurse into subdirectories") return destroyCmd } func (c *Config) runDestroyCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { targetRelPaths, err := c.targetRelPaths(sourceState, args, targetRelPathsOptions{ recursive: c.destroy.recursive, }) if err != nil { return err } for _, targetRelPath := range targetRelPaths { destAbsPath := c.DestDirAbsPath.Join(targetRelPath) // Find the path of the entry in the source state, if any. // // chezmoi destroy might be called on an entry in an exact_ directory // that is not present in the source directory. The entry is still // managed by chezmoi because chezmoi apply will remove it. Therefore, // chezmoi destroy should remove such entries from the target state, // even if they are not present in the source state. So, when calling // chezmoi destroy on entries like this, we should only remove the entry // from the target state, not the source state. // // For entries in exact_ directories in the target state that are not // present in the source state, we generate SourceStateRemove entries. // So, if the source state entry is a SourceStateRemove then we know // that there is no actual source state entry to remove. var sourceAbsPath chezmoi.AbsPath sourceStateEntry := sourceState.MustEntry(targetRelPath) if sourceStateEntry.Origin().IsExternal() { c.errorf("warning: skipping external %s\n", targetRelPath) continue } if _, ok := sourceStateEntry.(*chezmoi.SourceStateRemove); !ok { relPath := sourceStateEntry.SourceRelPath().RelPath() if !relPath.IsEmpty() { sourceAbsPath = c.SourceDirAbsPath.Join(relPath) } } if !c.force { var prompt string if sourceAbsPath.IsEmpty() { prompt = fmt.Sprintf("Destroy %s", destAbsPath) } else { prompt = fmt.Sprintf("Destroy %s and %s", destAbsPath, sourceAbsPath) } choice, err := c.promptChoice(prompt, choicesYesNoAllQuit) if err != nil { return err } switch choice { case "yes": case "no": continue case "all": c.force = true case "quit": return nil } } if err := c.destSystem.RemoveAll(destAbsPath); err != nil && !errors.Is(err, fs.ErrNotExist) { return err } if !sourceAbsPath.IsEmpty() { if err := c.sourceSystem.RemoveAll(sourceAbsPath); err != nil && !errors.Is(err, fs.ErrNotExist) { return err } } if err := c.persistentState.Delete(chezmoi.EntryStateBucket, destAbsPath.Bytes()); err != nil { return err } } return nil } ================================================ FILE: internal/cmd/diffcmd.go ================================================ package cmd import ( "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type diffCmdConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Args []string `json:"args" mapstructure:"args" yaml:"args"` Exclude *chezmoi.EntryTypeSet `json:"exclude" mapstructure:"exclude" yaml:"exclude"` Pager string `json:"pager" mapstructure:"pager" yaml:"pager"` PagerArgs []string `json:"pagerArgs" mapstructure:"pagerArgs" yaml:"pagerArgs"` Reverse bool `json:"reverse" mapstructure:"reverse" yaml:"reverse"` ScriptContents bool `json:"scriptContents" mapstructure:"scriptContents" yaml:"scriptContents"` include *chezmoi.EntryTypeSet init bool parentDirs bool recursive bool } func (c *Config) newDiffCmd() *cobra.Command { diffCmd := &cobra.Command{ GroupID: groupIDDaily, Use: "diff [target]...", Short: "Print the diff between the target state and the destination state", Long: mustLongHelp("diff"), Example: example("diff"), ValidArgsFunction: c.targetValidArgs, RunE: c.runDiffCmd, Annotations: newAnnotations( dryRun, outputsDiff, persistentStateModeReadMockWrite, requiresSourceDirectory, ), } diffCmd.Flags().VarP(c.Diff.Exclude, "exclude", "x", "Exclude entry types") diffCmd.Flags().VarP(c.Diff.include, "include", "i", "Include entry types") diffCmd.Flags().BoolVar(&c.Diff.init, "init", c.Diff.init, "Recreate config file from template") diffCmd.Flags().StringVar(&c.Diff.Pager, "pager", c.Diff.Pager, "Set pager") diffCmd.Flags(). BoolVarP(&c.Diff.parentDirs, "parent-dirs", "P", c.apply.parentDirs, "Print the diff of all parent directories") diffCmd.Flags().BoolVarP(&c.Diff.recursive, "recursive", "r", c.Diff.recursive, "Recurse into subdirectories") diffCmd.Flags().BoolVar(&c.Diff.Reverse, "reverse", c.Diff.Reverse, "Reverse the direction of the diff") diffCmd.Flags().BoolVar(&c.Diff.ScriptContents, "script-contents", c.Diff.ScriptContents, "Show script contents") return diffCmd } func (c *Config) runDiffCmd(cmd *cobra.Command, args []string) (err error) { return c.applyArgs(cmd.Context(), c.destSystem, c.DestDirAbsPath, args, applyArgsOptions{ cmd: cmd, filter: chezmoi.NewEntryTypeFilter(c.Diff.include.Bits(), c.Diff.Exclude.Bits()), init: c.Diff.init, parentDirs: c.Diff.parentDirs, recursive: c.Diff.recursive, umask: c.Umask, }) } ================================================ FILE: internal/cmd/diffcmd_test.go ================================================ package cmd import ( "io/fs" "strings" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestDiffCmd(t *testing.T) { if chezmoitest.Umask != 0o22 { t.Skip("umask not 0o22") } for _, tc := range []struct { name string extraRoot any args []string stdoutStr string }{ { name: "empty", }, { name: "file", extraRoot: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ "dot_file": "# contents of .file\n", }, }, stdoutStr: chezmoitest.JoinLines( `diff --git a/.file b/.file`, `new file mode 100644`, `index 0000000000000000000000000000000000000000..8a52cb9ce9551221716a53786ad74104c5902362`, `--- /dev/null`, `+++ b/.file`, `@@ -0,0 +1 @@`, `+# contents of .file`, ), }, { name: "simple_exclude_files", extraRoot: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ "dot_file": "# contents of .file\n", "symlink_dot_symlink": ".file\n", }, }, args: []string{ "--exclude", "files", }, stdoutStr: chezmoitest.JoinLines( `diff --git a/.symlink b/.symlink`, `new file mode 120000`, `index 0000000000000000000000000000000000000000..3e6844d17780d623d817c3e22bcd1128d64422ae`, `--- /dev/null`, `+++ b/.symlink`, `@@ -0,0 +1 @@`, `+.file`, ), }, { name: "simple_exclude_files_with_config", extraRoot: map[string]any{ "/home/user": map[string]any{ ".config/chezmoi/chezmoi.toml": chezmoitest.JoinLines( `[diff]`, ` exclude = ["files"]`, ), ".local/share/chezmoi": map[string]any{ "dot_file": "# contents of .file\n", "symlink_dot_symlink": ".file\n", }, }, }, stdoutStr: chezmoitest.JoinLines( `diff --git a/.symlink b/.symlink`, `new file mode 120000`, `index 0000000000000000000000000000000000000000..3e6844d17780d623d817c3e22bcd1128d64422ae`, `--- /dev/null`, `+++ b/.symlink`, `@@ -0,0 +1 @@`, `+.file`, ), }, { name: "simple_exclude_externals_with_config", extraRoot: map[string]any{ "/home/user": map[string]any{ ".config/chezmoi/chezmoi.toml": chezmoitest.JoinLines( `[diff]`, ` exclude = ["externals"]`, ), ".local/share/chezmoi": map[string]any{ "dot_file": "# contents of .file\n", "symlink_dot_symlink": ".file\n", }, }, }, stdoutStr: chezmoitest.JoinLines( `diff --git a/.file b/.file`, `new file mode 100644`, `index 0000000000000000000000000000000000000000..8a52cb9ce9551221716a53786ad74104c5902362`, `--- /dev/null`, `+++ b/.file`, `@@ -0,0 +1 @@`, `+# contents of .file`, `diff --git a/.symlink b/.symlink`, `new file mode 120000`, `index 0000000000000000000000000000000000000000..3e6844d17780d623d817c3e22bcd1128d64422ae`, `--- /dev/null`, `+++ b/.symlink`, `@@ -0,0 +1 @@`, `+.file`, ), }, { name: "issue_4425", extraRoot: map[string]any{ "/home/user": map[string]any{ ".config/git/config": "# contents of .config/git/config\n", ".local/share/chezmoi": map[string]any{ "dot_config/remove_git/config": "# contents of .config/git/config\n", }, }, }, }, { name: "issue_4425_nested_dir", extraRoot: map[string]any{ "/home/user": map[string]any{ ".config/git": map[string]any{ "subdir": &vfst.Dir{ Perm: fs.ModePerm &^ chezmoitest.Umask, }, }, ".local/share/chezmoi": map[string]any{ "dot_config/remove_git/remove_subdir/.keep": "", }, }, }, stdoutStr: chezmoitest.JoinLines( "diff --git a/.config/git/subdir b/.config/git/subdir", "deleted file mode 40755", "index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000", "--- a/.config/git/subdir", "+++ /dev/null", "diff --git a/.config/git b/.config/git", "deleted file mode 40755", "index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000", "--- a/.config/git", "+++ /dev/null", ), }, { name: "issue_4425_nested_file", extraRoot: map[string]any{ "/home/user": map[string]any{ ".config/git/config": "# contents of .config/git/config\n", ".local/share/chezmoi": map[string]any{ "dot_config/remove_git/config": "", }, }, }, stdoutStr: chezmoitest.JoinLines( "diff --git a/.config/git/config b/.config/git/config", "deleted file mode 100644", "index c3d477a648b5cb9739a359d54234ec5627c3a64b..0000000000000000000000000000000000000000", "--- a/.config/git/config", "+++ /dev/null", "@@ -1 +0,0 @@", "-# contents of .config/git/config", "diff --git a/.config/git b/.config/git", "deleted file mode 40755", "index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000", "--- a/.config/git", "+++ /dev/null", ), }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, map[string]any{ "/home/user/.local/share/chezmoi": &vfst.Dir{ Perm: fs.ModePerm &^ chezmoitest.Umask, }, }, func(fileSystem vfs.FS) { if tc.extraRoot != nil { assert.NoError(t, vfst.NewBuilder().Build(fileSystem, tc.extraRoot)) } stdout := strings.Builder{} config := newTestConfig(t, fileSystem, withStdout(&stdout)) assert.NoError(t, config.execute(append([]string{"diff"}, tc.args...))) assert.Equal(t, tc.stdoutStr, stdout.String()) }) }) } } ================================================ FILE: internal/cmd/dockercmd.go ================================================ package cmd import ( "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type dockerCmdConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` exec dockerExecCmdConfig run dockerRunCmdConfig } type dockerExecCmdConfig struct { interactive bool packageManager string shell bool } type dockerRunCmdConfig struct { packageManager string } func (c *Config) newDockerCmd() *cobra.Command { commandCmd := &cobra.Command{ GroupID: groupIDRemote, Use: "docker", Aliases: []string{"podman"}, Short: "Use your dotfiles in a Docker container", Annotations: newAnnotations( persistentStateModeNone, ), } commandExecCmd := &cobra.Command{ Use: "exec container-id [args...]", Short: "Install your dotfiles in an existing Docker container and execute a shell", Args: cobra.MinimumNArgs(1), RunE: c.makeRunEWithSourceState(c.runDockerExecCmd), Annotations: newAnnotations( persistentStateModeReadWrite, ), } commandExecCmd.Flags(). BoolVarP(&c.Docker.exec.interactive, "interactive", "i", c.Docker.exec.interactive, "Run interactively") commandExecCmd.Flags(). StringVarP(&c.Docker.exec.packageManager, "package-manager", "p", c.Docker.exec.packageManager, "Package manager") commandExecCmd.Flags().BoolVarP(&c.Docker.exec.shell, "shell", "s", c.Docker.exec.shell, "Execute shell afterwards") commandCmd.AddCommand(commandExecCmd) commandRunCmd := &cobra.Command{ Use: "run image-id [args...]", Short: "Create a new Docker container with your dotfiles and execute a shell", Args: cobra.MinimumNArgs(1), RunE: c.makeRunEWithSourceState(c.runDockerRunCmd), Annotations: newAnnotations( persistentStateModeReadWrite, ), } commandRunCmd.Flags(). StringVarP(&c.Docker.run.packageManager, "package-manager", "p", c.Docker.run.packageManager, "Package manager") commandCmd.AddCommand(commandRunCmd) return commandCmd } func (c *Config) runDockerExecCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { commandArgs := []string{"exec"} if c.Docker.exec.interactive { commandArgs = append(commandArgs, "--interactive", "--tty", ) } commandArgs = append(commandArgs, args[0]) return c.runInstallInitShellSh(sourceState, c.Docker.Command, commandArgs, runInstallInitShellOptions{ args: args[1:], interactive: c.Docker.exec.interactive, packageManager: c.Docker.exec.packageManager, shell: c.Docker.exec.shell, }, ) } func (c *Config) runDockerRunCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { return c.runInstallInitShellSh(sourceState, c.Docker.Command, []string{"run", "--interactive", "--tty", args[0]}, runInstallInitShellOptions{ args: args[1:], interactive: true, packageManager: c.Docker.run.packageManager, shell: true, }, ) } ================================================ FILE: internal/cmd/doctorcmd.go ================================================ package cmd // FIXME add check for $TMPDIR mount options (specifically noexec) import ( "bytes" "context" "errors" "fmt" "io/fs" "log/slog" "net/http" "os" "os/exec" "regexp" "runtime" "strings" "text/tabwriter" "time" "github.com/coreos/go-semver/semver" "github.com/google/go-github/v61/github" "github.com/spf13/cobra" "github.com/twpayne/go-shell" "github.com/twpayne/go-xdg/v6" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoigit" "chezmoi.io/chezmoi/internal/chezmoilog" ) // A checkResult is the result of a check. type checkResult int const ( checkResultOmitted checkResult = -3 // The check was omitted. checkResultFailed checkResult = -2 // The check could not be completed. checkResultSkipped checkResult = -1 // The check was skipped. checkResultOK checkResult = 0 // The check completed and did not find any problems. checkResultInfo checkResult = 1 // The check completed and found something interesting, but not a problem. checkResultWarning checkResult = 2 // The check completed and found something that might indicate a problem. checkResultError checkResult = 3 // The check completed and found a definite problem. ) // A gitStatus is the status of a git working copy. type gitStatus string const ( gitStatusNotAWorkingCopy gitStatus = "" gitStatusClean gitStatus = "clean" gitStatusDirty gitStatus = "dirty" gitStatusError gitStatus = "error" ) // A check is an individual check. type check interface { Name() string // Name returns the check's name. Run(config *Config) (checkResult, string) // Run runs the check. } var checkResultStr = map[checkResult]string{ checkResultOmitted: "omitted", checkResultFailed: "failed", checkResultSkipped: "skipped", checkResultOK: "ok", checkResultInfo: "info", checkResultWarning: "warning", checkResultError: "error", } // An argsCheck checks that arguments for a binary. type argsCheck struct { name string command string args []string } // A binaryCheck checks that a binary called name is installed and optionally at // least version minVersion. type binaryCheck struct { name string binaryName string ifNotSet checkResult ifNotExist checkResult versionArgs []string versionRx *regexp.Regexp minVersion *semver.Version } // A configFileCheck checks that only one config file exists and that is // readable. type configFileCheck struct { basename chezmoi.RelPath bds *xdg.BaseDirectorySpecification } // A dirCheck checks that a directory exists. type dirCheck struct { name string dirname chezmoi.AbsPath } // An executableCheck checks the executable. type executableCheck struct{} // A fileCheck checks that a file exists. type fileCheck struct { name string filename chezmoi.AbsPath ifNotSet checkResult ifNotExist checkResult } // A goVersionCheck checks the Go version. type goVersionCheck struct{} // A latestVersionCheck checks the latest version. type latestVersionCheck struct { network bool httpClient *http.Client httpClientErr error version semver.Version } // An osArchCheck checks that [runtime.GOOS] and [runtime.GOARCH] are supported. type osArchCheck struct{} // A omittedCheck is a check that is omitted. type omittedCheck struct{} // A suspiciousEntriesCheck checks that a source directory does not contain any // suspicious files. type suspiciousEntriesCheck struct { dirname chezmoi.AbsPath encryptedSuffixes []string } // A symlinkCheck checks that symlinks can be created. type symlinkCheck struct{} // A upgradeMethodCheck checks the upgrade method. type upgradeMethodCheck struct{} // A versionCheck checks the version information. type versionCheck struct { versionInfo VersionInfo versionStr string } type doctorCmdConfig struct { noNetwork bool } func (c *Config) newDoctorCmd() *cobra.Command { doctorCmd := &cobra.Command{ GroupID: groupIDDocumentation, Args: cobra.NoArgs, ValidArgsFunction: cobra.NoFileCompletions, Use: "doctor", Short: "Check your system for potential problems", Example: example("doctor"), Long: mustLongHelp("doctor"), RunE: c.runDoctorCmd, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeNone, runsCommands, ), } doctorCmd.Flags().BoolVar(&c.doctor.noNetwork, "no-network", c.doctor.noNetwork, "do not use network connection") return doctorCmd } func (c *Config) runDoctorCmd(cmd *cobra.Command, args []string) error { homeDirAbsPath, err := chezmoi.HomeDirAbsPath() if err != nil { return err } httpClient, httpClientErr := c.getHTTPClient() shellCommand, _ := shell.CurrentUserShell() shellCommand, shellArgs, _ := parseCommand(shellCommand, nil) cdCommand, cdArgs, _ := c.cdCommand() editCommand, editArgs, _ := c.editor(nil) checks := []check{ &versionCheck{ versionInfo: c.versionInfo, versionStr: c.versionStr, }, &latestVersionCheck{ network: !c.doctor.noNetwork, httpClient: httpClient, httpClientErr: httpClientErr, version: c.version, }, osArchCheck{}, unameCheck{}, systeminfoCheck{}, goVersionCheck{}, executableCheck{}, upgradeMethodCheck{}, &configFileCheck{ basename: chezmoiRelPath, bds: c.bds, }, &dirCheck{ name: "source-dir", dirname: c.SourceDirAbsPath, }, &suspiciousEntriesCheck{ dirname: c.SourceDirAbsPath, encryptedSuffixes: []string{ c.Age.Suffix, c.GPG.Suffix, }, }, &dirCheck{ name: "working-tree", dirname: c.WorkingTreeAbsPath, }, &dirCheck{ name: "dest-dir", dirname: c.DestDirAbsPath, }, hardlinkCheck{}, symlinkCheck{}, umaskCheck{}, &binaryCheck{ name: "cd-command", binaryName: cdCommand, ifNotSet: checkResultError, ifNotExist: checkResultError, }, &argsCheck{ name: "cd-args", command: cdCommand, args: cdArgs, }, &binaryCheck{ name: "diff-command", binaryName: c.Diff.Command, ifNotSet: checkResultInfo, ifNotExist: checkResultWarning, }, &binaryCheck{ name: "edit-command", binaryName: editCommand, ifNotSet: checkResultWarning, ifNotExist: checkResultWarning, }, &argsCheck{ name: "edit-args", command: editCommand, args: editArgs, }, &binaryCheck{ name: "git-command", binaryName: c.Git.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultWarning, versionArgs: []string{"--version"}, versionRx: regexp.MustCompile(`^git\s+version\s+(\d+\.\d+\.\d+)`), }, &binaryCheck{ name: "merge-command", binaryName: c.Merge.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultWarning, }, &binaryCheck{ name: "shell-command", binaryName: shellCommand, ifNotSet: checkResultError, ifNotExist: checkResultError, }, &argsCheck{ name: "shell-args", command: shellCommand, args: shellArgs, }, &binaryCheck{ name: "age-command", binaryName: c.Age.Command, versionArgs: []string{"--version"}, versionRx: regexp.MustCompile(`(\d+\.\d+\.\d+\S*)`), ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, }, &binaryCheck{ name: "gpg-command", binaryName: c.GPG.Command, versionArgs: []string{"--version"}, versionRx: regexp.MustCompile(`(?m)^gpg\s+\(.*?\)\s+(\d+\.\d+\.\d+)`), ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, }, &binaryCheck{ name: "pinentry-command", binaryName: c.PINEntry.Command, versionArgs: []string{"--version"}, versionRx: regexp.MustCompile(`^\S+\s+\(pinentry\)\s+(\d+\.\d+\.\d+)`), ifNotSet: checkResultInfo, ifNotExist: checkResultWarning, }, &binaryCheck{ name: "1password-command", binaryName: c.Onepassword.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, versionArgs: []string{"--version"}, versionRx: onepasswordVersionRx, minVersion: &onepasswordMinVersion, }, &binaryCheck{ name: "bitwarden-command", binaryName: c.Bitwarden.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, versionArgs: []string{"--version"}, versionRx: regexp.MustCompile(`(?m)^(\d+\.\d+\.\d+)$`), }, &binaryCheck{ name: "bitwarden-secrets-command", binaryName: c.BitwardenSecrets.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, versionArgs: []string{"--version"}, versionRx: regexp.MustCompile(`Bitwarden\s+Secrets\s+CLI\s+(\d+\.\d+\.\d+)`), }, &binaryCheck{ name: "dashlane-command", binaryName: c.Dashlane.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, versionArgs: []string{"--version"}, versionRx: regexp.MustCompile(`^(\d+\.\d+\.\d+)`), }, &binaryCheck{ name: "doppler-command", binaryName: c.Doppler.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, versionArgs: []string{"--version"}, versionRx: regexp.MustCompile(`^v(\d+\.\d+\.\d+)`), }, &binaryCheck{ name: "gopass-command", binaryName: c.Gopass.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, versionArgs: gopassVersionArgs, versionRx: gopassVersionRx, minVersion: &gopassMinVersion, }, &binaryCheck{ name: "keepassxc-command", binaryName: c.Keepassxc.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, versionArgs: []string{"--version"}, versionRx: regexp.MustCompile(`^(\d+\.\d+\.\d+)`), minVersion: &keepassxcMinVersion, }, &fileCheck{ name: "keepassxc-db", filename: c.Keepassxc.Database, ifNotSet: checkResultInfo, ifNotExist: checkResultInfo, }, &binaryCheck{ name: "keeper-command", binaryName: c.Keeper.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, versionArgs: []string{"version"}, versionRx: regexp.MustCompile(`^Commander\s+Version:\s+(\d+\.\d+\.\d+)`), }, &binaryCheck{ name: "lastpass-command", binaryName: c.Lastpass.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, versionArgs: lastpassVersionArgs, versionRx: lastpassVersionRx, minVersion: &lastpassMinVersion, }, &binaryCheck{ name: "pass-command", binaryName: c.Pass.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, versionArgs: []string{"version"}, versionRx: regexp.MustCompile(`(?m)=\s*v(\d+\.\d+\.\d+)`), }, &binaryCheck{ name: "passhole-command", binaryName: c.Passhole.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, versionArgs: []string{"--version"}, versionRx: regexp.MustCompile(`^(\d+\.\d+\.\d+)`), minVersion: &passholeMinVersion, }, &binaryCheck{ name: "protonpass-command", binaryName: c.ProtonPass.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, versionArgs: []string{"--version"}, versionRx: regexp.MustCompile(`^Proton Pass CLI (\d+\.\d+\.\d+)`), }, &binaryCheck{ name: "rbw-command", binaryName: c.RBW.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, versionArgs: []string{"--version"}, versionRx: regexp.MustCompile(`^rbw\s+(\d+\.\d+\.\d+)`), minVersion: &rbwMinVersion, }, &binaryCheck{ name: "vault-command", binaryName: c.Vault.Command, ifNotSet: checkResultWarning, ifNotExist: checkResultInfo, versionArgs: []string{"version"}, versionRx: regexp.MustCompile(`^Vault\s+v(\d+\.\d+\.\d+)`), }, &binaryCheck{ name: "secret-command", binaryName: c.Secret.Command, ifNotSet: checkResultInfo, ifNotExist: checkResultInfo, }, } worstResult := checkResultOK resultWriter := tabwriter.NewWriter(c.stdout, 3, 0, 3, ' ', 0) fmt.Fprint(resultWriter, "RESULT\tCHECK\tMESSAGE\n") for _, check := range checks { checkResult, message := check.Run(c) if checkResult == checkResultOmitted { continue } // Conceal the user's actual home directory in the message as the // output of chezmoi doctor is often posted publicly and would otherwise // reveal the user's username. message = strings.ReplaceAll(message, homeDirAbsPath.String(), "~") fmt.Fprintf(resultWriter, "%s\t%s\t%s\n", checkResultStr[checkResult], check.Name(), message) if checkResult > worstResult { worstResult = checkResult } } resultWriter.Flush() if worstResult > checkResultWarning { return chezmoi.ExitCodeError(1) } return nil } func (c *argsCheck) Name() string { return c.name } func (c *argsCheck) Run(config *Config) (checkResult, string) { return checkResultOK, shellQuoteCommand(c.command, c.args) } func (c *binaryCheck) Name() string { return c.name } func (c *binaryCheck) Run(config *Config) (checkResult, string) { if c.binaryName == "" { return c.ifNotSet, "not set" } var pathAbsPath chezmoi.AbsPath switch path, err := chezmoi.LookPath(c.binaryName); { case errors.Is(err, exec.ErrNotFound): return c.ifNotExist, c.binaryName + " not found in $PATH" case err != nil: return checkResultFailed, err.Error() default: pathAbsPath, err = chezmoi.NewAbsPathFromExtPath(path, config.homeDirAbsPath) if err != nil { return checkResultFailed, err.Error() } } if c.versionArgs == nil { return checkResultOK, fmt.Sprintf("found %s", pathAbsPath) } cmd := exec.Command(pathAbsPath.String(), c.versionArgs...) output, err := chezmoilog.LogCmdCombinedOutput(slog.Default(), cmd) if err != nil { return checkResultFailed, err.Error() } versionBytes := output if c.versionRx != nil { match := c.versionRx.FindSubmatch(versionBytes) if len(match) != 2 { s := fmt.Sprintf("found %s, cannot parse version from %s", pathAbsPath, bytes.TrimSpace(versionBytes)) return checkResultWarning, s } versionBytes = match[1] } version, err := semver.NewVersion(string(versionBytes)) if err != nil { return checkResultFailed, err.Error() } if c.minVersion != nil && version.LessThan(*c.minVersion) { return checkResultError, fmt.Sprintf("found %s, version %s, need %s", pathAbsPath, version, c.minVersion) } return checkResultOK, fmt.Sprintf("found %s, version %s", pathAbsPath, version) } func (c *configFileCheck) Name() string { return "config-file" } func (c *configFileCheck) Run(config *Config) (checkResult, string) { configFileAbsPath, err := config.getConfigFileAbsPath() if err != nil { return checkResultError, err.Error() } fileInfo, err := config.baseSystem.Stat(configFileAbsPath) switch { case errors.Is(err, fs.ErrNotExist): return checkResultInfo, fmt.Sprintf("%s: not found", configFileAbsPath) case err != nil: return checkResultError, fmt.Sprintf("%s: %s", configFileAbsPath, err) case fileInfo.Mode().Type() != 0: return checkResultError, fmt.Sprintf( "found %s, which is a %s", configFileAbsPath, chezmoi.FileModeTypeNames[fileInfo.Mode().Type()], ) } tmpConfig, err := newConfig() if err != nil { return checkResultError, err.Error() } if err := config.decodeConfigFile(configFileAbsPath, &tmpConfig.ConfigFile); err != nil { return checkResultError, fmt.Sprintf("%s: %v", configFileAbsPath, err) } message := fmt.Sprintf("found %s, last modified %s", configFileAbsPath, fileInfo.ModTime().Format(time.RFC3339)) return checkResultOK, message } func (c *dirCheck) Name() string { return c.name } func (c *dirCheck) Run(config *Config) (checkResult, string) { dirEntries, err := config.baseSystem.ReadDir(c.dirname) if err != nil { return checkResultError, err.Error() } gitStatus := gitStatusNotAWorkingCopy for _, dirEntry := range dirEntries { if dirEntry.Name() != ".git" { continue } cmd := exec.Command("git", "-C", c.dirname.String(), "status", "--porcelain=v2") cmd.Stderr = os.Stderr output, err := cmd.Output() if err != nil { gitStatus = gitStatusError break } switch status, err := chezmoigit.ParseStatusPorcelainV2(output); { case err != nil: gitStatus = gitStatusError case status.IsEmpty(): gitStatus = gitStatusClean default: gitStatus = gitStatusDirty } break } switch gitStatus { case gitStatusNotAWorkingCopy: return checkResultOK, fmt.Sprintf("%s is a directory", c.dirname) case gitStatusClean: return checkResultOK, fmt.Sprintf("%s is a git working tree (clean)", c.dirname) case gitStatusDirty: return checkResultWarning, fmt.Sprintf("%s is a git working tree (dirty)", c.dirname) case gitStatusError: return checkResultError, fmt.Sprintf("%s is a git working tree (error)", c.dirname) default: panic(fmt.Sprintf("%s: unknown git status", gitStatus)) } } func (executableCheck) Name() string { return "executable" } func (executableCheck) Run(config *Config) (checkResult, string) { executable, err := os.Executable() if err != nil { return checkResultError, err.Error() } executableAbsPath, err := chezmoi.NewAbsPathFromExtPath(executable, config.homeDirAbsPath) if err != nil { return checkResultError, err.Error() } return checkResultOK, executableAbsPath.String() } func (c *fileCheck) Name() string { return c.name } func (c *fileCheck) Run(config *Config) (checkResult, string) { if c.filename.IsEmpty() { return c.ifNotSet, "not set" } switch _, err := config.baseSystem.ReadFile(c.filename); { case errors.Is(err, fs.ErrNotExist): return c.ifNotExist, fmt.Sprintf("%s does not exist", c.filename) case err != nil: return checkResultError, fmt.Sprintf("%s: %v", c.filename, err) default: return checkResultOK, fmt.Sprintf("%s is a file", c.filename) } } func (goVersionCheck) Name() string { return "go-version" } func (goVersionCheck) Run(config *Config) (checkResult, string) { return checkResultOK, fmt.Sprintf("%s (%s)", runtime.Version(), runtime.Compiler) } func (c *latestVersionCheck) Name() string { return "latest-version" } func (c *latestVersionCheck) Run(config *Config) (checkResult, string) { switch { case !c.network: return checkResultSkipped, "no network" case c.httpClientErr != nil: return checkResultFailed, c.httpClientErr.Error() } ctx := context.Background() gitHubClient := chezmoi.NewGitHubClient(ctx, c.httpClient) rr, _, err := gitHubClient.Repositories.GetLatestRelease(ctx, "twpayne", "chezmoi") var rateLimitErr *github.RateLimitError var abuseRateLimitErr *github.AbuseRateLimitError switch { case err == nil: // Do nothing. case errors.As(err, &rateLimitErr): return checkResultFailed, "GitHub rate limit exceeded" case errors.As(err, &abuseRateLimitErr): return checkResultFailed, "GitHub abuse rate limit exceeded" default: return checkResultFailed, err.Error() } version, err := semver.NewVersion(strings.TrimPrefix(rr.GetName(), "v")) if err != nil { return checkResultError, err.Error() } checkResult := checkResultOK if c.version.LessThan(*version) { checkResult = checkResultWarning } return checkResult, "v" + version.String() } func (osArchCheck) Name() string { return "os-arch" } func (osArchCheck) Run(config *Config) (checkResult, string) { fields := []string{runtime.GOOS + "/" + runtime.GOARCH} if osRelease, err := chezmoi.OSRelease(config.baseSystem.UnderlyingFS()); err == nil { if name, ok := osRelease["NAME"].(string); ok { if version, ok := osRelease["VERSION"].(string); ok { fields = append(fields, "("+name+" "+version+")") } else { fields = append(fields, "("+name+")") } } } return checkResultOK, strings.Join(fields, " ") } func (omittedCheck) Name() string { return "omitted" } func (omittedCheck) Run(config *Config) (checkResult, string) { return checkResultOmitted, "" } func (c *suspiciousEntriesCheck) Name() string { return "suspicious-entries" } func (c *suspiciousEntriesCheck) Run(config *Config) (checkResult, string) { // FIXME check that config file templates are in root var suspiciousEntries []string walkFunc := func(absPath chezmoi.AbsPath, fileInfo fs.FileInfo, err error) error { if err != nil { return err } if chezmoi.IsSuspiciousSourceDirEntry(absPath.Base(), fileInfo, c.encryptedSuffixes) { suspiciousEntries = append(suspiciousEntries, absPath.String()) } return nil } switch err := chezmoi.WalkSourceDir(config.baseSystem, c.dirname, walkFunc); { case errors.Is(err, fs.ErrNotExist): return checkResultOK, fmt.Sprintf("%s: no such file or directory", c.dirname) case err != nil: return checkResultError, err.Error() } if len(suspiciousEntries) > 0 { return checkResultWarning, englishList(suspiciousEntries) } return checkResultOK, "no suspicious entries" } func (symlinkCheck) Name() string { return "symlink" } func (symlinkCheck) Run(config *Config) (checkResult, string) { tempDirAbsPath, err := config.tempDir("chezmoi-doctor") if err != nil { return checkResultFailed, err.Error() } oldName := ".old-name" newName := ".new-name" newNameAbsPath := tempDirAbsPath.JoinString(newName) if err := config.baseSystem.WriteSymlink(oldName, newNameAbsPath); err != nil { return checkResultFailed, err.Error() } if err := config.baseSystem.Remove(newNameAbsPath); err != nil { return checkResultFailed, err.Error() } return checkResultOK, fmt.Sprintf("created symlink from %s to %s", newName, oldName) } func (upgradeMethodCheck) Name() string { return "upgrade-method" } func (upgradeMethodCheck) Run(config *Config) (checkResult, string) { executable, err := os.Executable() if err != nil { return checkResultFailed, err.Error() } method, err := getUpgradeMethod(config.baseSystem.UnderlyingFS(), chezmoi.NewAbsPath(executable)) if err != nil { return checkResultFailed, err.Error() } if method == "" { return checkResultOmitted, "" } return checkResultOK, method } func (c *versionCheck) Name() string { return "version" } func (c *versionCheck) Run(config *Config) (checkResult, string) { if c.versionInfo.Version == "" || c.versionInfo.Commit == "" { return checkResultWarning, c.versionStr } return checkResultOK, c.versionStr } ================================================ FILE: internal/cmd/doctorcmd_unix.go ================================================ //go:build unix package cmd import ( "bytes" "errors" "fmt" "log/slog" "os" "os/exec" "golang.org/x/sys/unix" "chezmoi.io/chezmoi/internal/chezmoilog" ) type ( // Check if hardlinks work between tempDir and sourceDir. hardlinkCheck struct{} systeminfoCheck struct{ omittedCheck } umaskCheck struct{} unameCheck struct{} ) func (hardlinkCheck) Name() string { return "hardlink" } func (hardlinkCheck) Run(config *Config) (checkResult, string) { if !config.Edit.Hardlink { return checkResultInfo, "edit.hardlink disabled" } testFileName := ".chezmoi-doctor-hardlink-test" tempDirAbsPath, err := config.tempDir("chezmoi-doctor") if err != nil { return checkResultFailed, err.Error() } hardlinkAbsPath := tempDirAbsPath.JoinString(testFileName) sourceAbsPath := config.SourceDirAbsPath.JoinString(testFileName) if err := config.baseSystem.WriteFile(sourceAbsPath, nil, 0o666); err != nil { return checkResultFailed, err.Error() } if err := os.MkdirAll(hardlinkAbsPath.Dir().String(), 0o666); err != nil { return checkResultFailed, err.Error() } if err := config.baseSystem.Link(config.SourceDirAbsPath.JoinString(testFileName), hardlinkAbsPath); err != nil { errCleanUp := config.baseSystem.Remove(sourceAbsPath) return checkResultError, fmt.Sprintf( "failed creating hardlink from %s to %s: %s", config.SourceDirAbsPath, config.TempDir, errors.Join(err, errCleanUp), ) } if err := config.baseSystem.Remove(sourceAbsPath); err != nil { return checkResultFailed, err.Error() } return checkResultOK, fmt.Sprintf("created hardlink from %s to %s", config.SourceDirAbsPath, config.TempDir) } func (umaskCheck) Name() string { return "umask" } func (umaskCheck) Run(config *Config) (checkResult, string) { umask := unix.Umask(0) unix.Umask(umask) result := checkResultOK if umask != 0o002 && umask != 0o022 { result = checkResultWarning } return result, fmt.Sprintf("%03o", umask) } func (unameCheck) Name() string { return "uname" } func (unameCheck) Run(config *Config) (checkResult, string) { cmd := exec.Command("uname", "-a") cmd.Stderr = os.Stderr data, err := chezmoilog.LogCmdOutput(slog.Default(), cmd) if err != nil { return checkResultFailed, err.Error() } return checkResultOK, string(bytes.TrimSpace(data)) } ================================================ FILE: internal/cmd/doctorcmd_windows.go ================================================ package cmd import ( "fmt" "log/slog" "os/exec" "strings" "chezmoi.io/chezmoi/internal/chezmoilog" ) type ( hardlinkCheck struct{ omittedCheck } systeminfoCheck struct{} umaskCheck struct{ omittedCheck } unameCheck struct{ omittedCheck } ) func (systeminfoCheck) Name() string { return "systeminfo" } func (systeminfoCheck) Run(config *Config) (checkResult, string) { cmd := exec.Command("systeminfo") data, err := chezmoilog.LogCmdOutput(slog.Default(), cmd) if err != nil { return checkResultFailed, err.Error() } var osName, osVersion string for line := range strings.Lines(string(data)) { switch key, value, found := strings.Cut(line, ":"); { case !found: // Do nothing. case key == "OS Name": osName = strings.TrimSpace(value) case key == "OS Version": osVersion = strings.TrimSpace(value) } } return checkResultOK, fmt.Sprintf("%s (%s)", osName, osVersion) } ================================================ FILE: internal/cmd/dopplertemplatefuncs.go ================================================ package cmd import ( "encoding/json" "fmt" "os" "os/exec" "slices" "strings" "chezmoi.io/chezmoi/internal/chezmoilog" ) type dopplerConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Args []string `json:"args" mapstructure:"args" yaml:"args"` Project string `json:"project" mapstructure:"project" yaml:"project"` Config string `json:"config" mapstructure:"config" yaml:"config"` outputCache map[string][]byte } func (c *Config) dopplerTemplateFunc(key string, additionalArgs ...string) any { if len(additionalArgs) > 2 { // Add one to the number of received arguments as the key // is the first argument. panic(fmt.Errorf("expected 1 to 3 arguments, got %d", len(additionalArgs)+1)) } args := c.appendDopplerAdditionalArgs([]string{"secrets", "download", "--json", "--no-file"}, additionalArgs) data := mustValue(c.dopplerOutput(args)) var value map[string]any must(json.Unmarshal(data, &value)) secret, ok := value[key] if !ok { panic(fmt.Errorf("could not find requested secret: %s", key)) } return secret } func (c *Config) dopplerProjectJSONTemplateFunc(additionalArgs ...string) any { if len(additionalArgs) > 2 { panic(fmt.Errorf("expected 0 to 2 arguments, got %d", len(additionalArgs))) } args := c.appendDopplerAdditionalArgs([]string{"secrets", "download", "--json", "--no-file"}, additionalArgs) data := mustValue(c.dopplerOutput(args)) var value any must(json.Unmarshal(data, &value)) return value } func (c *Config) appendDopplerAdditionalArgs(args, additionalArgs []string) []string { if len(additionalArgs) > 0 && additionalArgs[0] != "" { args = append(args, "--project", additionalArgs[0]) } else if c.Doppler.Project != "" { args = append(args, "--project", c.Doppler.Project) } if len(additionalArgs) > 1 && additionalArgs[1] != "" { args = append(args, "--config", additionalArgs[1]) } else if c.Doppler.Config != "" { args = append(args, "--config", c.Doppler.Config) } return args } func (c *Config) dopplerOutput(args []string) ([]byte, error) { args = append(slices.Clone(c.Doppler.Args), args...) key := strings.Join(args, "\x00") if data, ok := c.Doppler.outputCache[key]; ok { return data, nil } cmd := exec.Command(c.Doppler.Command, args...) // Always run the doppler command in the destination path because doppler uses // relative paths to find its .doppler.json config file. cmd.Dir = c.DestDirAbsPath.String() cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } if c.Doppler.outputCache == nil { c.Doppler.outputCache = make(map[string][]byte) } c.Doppler.outputCache[key] = output return output, nil } ================================================ FILE: internal/cmd/dumpcmd.go ================================================ package cmd import ( "cmp" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type dumpCmdConfig struct { filter *chezmoi.EntryTypeFilter format *choiceFlag init bool parentDirs bool recursive bool } func (c *Config) newDumpCmd() *cobra.Command { dumpCmd := &cobra.Command{ GroupID: groupIDInternal, Use: "dump [target]...", Short: "Generate a dump of the target state", Long: mustLongHelp("dump"), Example: example("dump"), ValidArgsFunction: c.targetValidArgs, RunE: c.runDumpCmd, Annotations: newAnnotations( persistentStateModeReadMockWrite, requiresSourceDirectory, ), } dumpCmd.Flags().VarP(c.dump.filter.Exclude, "exclude", "x", "Exclude entry types") dumpCmd.Flags().VarP(c.dump.format, "format", "f", "Output format") must(dumpCmd.RegisterFlagCompletionFunc("format", c.dump.format.FlagCompletionFunc())) dumpCmd.Flags().VarP(c.dump.filter.Include, "include", "i", "Include entry types") dumpCmd.Flags().BoolVar(&c.dump.init, "init", c.dump.init, "Recreate config file from template") dumpCmd.Flags().BoolVarP(&c.dump.parentDirs, "parent-dirs", "P", c.dump.parentDirs, "Dump all parent directories") dumpCmd.Flags().BoolVarP(&c.dump.recursive, "recursive", "r", c.dump.recursive, "Recurse into subdirectories") return dumpCmd } func (c *Config) runDumpCmd(cmd *cobra.Command, args []string) error { dumpSystem := chezmoi.NewDumpSystem() if err := c.applyArgs(cmd.Context(), dumpSystem, chezmoi.EmptyAbsPath, args, applyArgsOptions{ cmd: cmd, filter: c.dump.filter, init: c.dump.init, parentDirs: c.dump.parentDirs, recursive: c.dump.recursive, umask: c.Umask, }); err != nil { return err } return c.marshal(cmp.Or(c.dump.format.String(), c.Format.String()), dumpSystem.Data()) } ================================================ FILE: internal/cmd/dumpconfigcmd.go ================================================ package cmd import ( "cmp" "github.com/spf13/cobra" ) type dumpConfigCmdConfig struct { format *choiceFlag } func (c *Config) newDumpConfigCmd() *cobra.Command { dumpConfigCmd := &cobra.Command{ GroupID: groupIDInternal, Use: "dump-config", Short: "Dump the configuration values", Long: mustLongHelp("dump-config"), Example: example("dump-config"), Args: cobra.NoArgs, ValidArgsFunction: cobra.NoFileCompletions, RunE: c.runDumpConfigCmd, Annotations: newAnnotations( persistentStateModeReadOnly, ), } dumpConfigCmd.Flags().VarP(c.dumpConfig.format, "format", "f", "Output format") must(dumpConfigCmd.RegisterFlagCompletionFunc("format", c.dumpConfig.format.FlagCompletionFunc())) return dumpConfigCmd } func (c *Config) runDumpConfigCmd(cmd *cobra.Command, args []string) error { return c.marshal(cmp.Or(c.dumpConfig.format.String(), c.Format.String()), c) } ================================================ FILE: internal/cmd/editcmd.go ================================================ package cmd import ( "bytes" "log/slog" "os" "runtime" "time" "github.com/fsnotify/fsnotify" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoilog" ) type editCmdConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Args []string `json:"args" mapstructure:"args" yaml:"args"` Hardlink bool `json:"hardlink" mapstructure:"hardlink" yaml:"hardlink"` MinDuration time.Duration `json:"minDuration" mapstructure:"minDuration" yaml:"minDuration"` Watch bool `json:"watch" mapstructure:"watch" yaml:"watch"` Apply bool `json:"apply" mapstructure:"apply" yaml:"apply"` filter *chezmoi.EntryTypeFilter init bool } func (c *Config) newEditCmd() *cobra.Command { editCmd := &cobra.Command{ GroupID: groupIDDaily, Use: "edit targets...", Short: "Edit the source state of a target", Long: mustLongHelp("edit"), Example: example("edit"), ValidArgsFunction: c.targetValidArgs, RunE: c.runEditCmd, Annotations: newAnnotations( modifiesDestinationDirectory, modifiesSourceDirectory, persistentStateModeReadWrite, requiresSourceDirectory, runsCommands, ), } editCmd.Flags().BoolVarP(&c.Edit.Apply, "apply", "a", c.Edit.Apply, "Apply after editing") editCmd.Flags().VarP(c.Edit.filter.Exclude, "exclude", "x", "Exclude entry types") editCmd.Flags().BoolVar(&c.Edit.Hardlink, "hardlink", c.Edit.Hardlink, "Invoke editor with a hardlink to the source file") editCmd.Flags().VarP(c.Edit.filter.Include, "include", "i", "Include entry types") editCmd.Flags().BoolVar(&c.Edit.init, "init", c.Edit.init, "Recreate config file from template") editCmd.Flags().BoolVar(&c.Edit.Watch, "watch", c.Edit.Watch, "Apply on save") return editCmd } func (c *Config) runEditCmd(cmd *cobra.Command, args []string) error { if len(args) == 0 { if err := c.runEditor([]string{c.WorkingTreeAbsPath.String()}); err != nil { return err } if c.Edit.Apply { if err := c.applyArgs(cmd.Context(), c.destSystem, c.DestDirAbsPath, noArgs, applyArgsOptions{ cmd: cmd, filter: c.Edit.filter, init: c.Edit.init, recursive: true, umask: c.Umask, preApplyFunc: c.defaultPreApplyFunc, }); err != nil { return err } } return nil } sourceState, err := c.newSourceState(cmd.Context(), cmd) if err != nil { return err } targetRelPaths, err := c.targetRelPaths(sourceState, args, targetRelPathsOptions{ mustBeInSourceState: true, }) if err != nil { return err } editorArgs := make([]string, 0, len(targetRelPaths)) type transparentlyDecryptedFile struct { sourceAbsPath chezmoi.AbsPath decryptedAbsPath chezmoi.AbsPath preEditPlaintext []byte } var transparentlyDecryptedFiles []transparentlyDecryptedFile TARGET_REL_PATH: for _, targetRelPath := range targetRelPaths { sourceStateEntry := sourceState.MustEntry(targetRelPath) sourceRelPath := sourceStateEntry.SourceRelPath() switch sourceStateFile, ok := sourceStateEntry.(*chezmoi.SourceStateFile); { case ok && sourceStateFile.Attr().Encrypted: // FIXME in the case that the file is an encrypted template then we // should first decrypt the file to a temporary directory and // secondly add a hardlink from the edit directory to the temporary // directory. tempDirAbsPath, err := c.tempDir("chezmoi-encrypted") if err != nil { return err } // FIXME use RawContents and DecryptFile decryptedRelPath, err := sourceRelPath.TargetRelPath(c.encryption.EncryptedSuffix()) if err != nil { return err } decryptedAbsPath := tempDirAbsPath.Join(decryptedRelPath) contents, err := sourceStateFile.Contents() if err != nil { return err } if err := os.MkdirAll(decryptedAbsPath.Dir().String(), 0o700); err != nil { return err } if err := c.baseSystem.WriteFile(decryptedAbsPath, contents, 0o600); err != nil { return err } transparentlyDecryptedFile := transparentlyDecryptedFile{ sourceAbsPath: c.SourceDirAbsPath.Join(sourceRelPath.RelPath()), decryptedAbsPath: decryptedAbsPath, preEditPlaintext: contents, } transparentlyDecryptedFiles = append(transparentlyDecryptedFiles, transparentlyDecryptedFile) editorArgs = append(editorArgs, decryptedAbsPath.String()) case ok && c.Edit.Hardlink && runtime.GOOS != "windows": // If the operating system supports hard links and the file is not // encrypted, then create a hard link to the file in the source // directory in the temporary edit directory. This means that the // editor will see the target filename while simultaneously updating // the file in the source directory. // Compute the hard link path from the target path. If the file is a // template then preserve the .tmpl suffix as a clue to the editor. targetRelPath, err := sourceRelPath.TargetRelPath(c.encryption.EncryptedSuffix()) if err != nil { return err } if sourceStateFile.Attr().Template { targetRelPath = targetRelPath.AppendString(chezmoi.TemplateSuffix) } tempDirAbsPath, err := c.tempDir("chezmoi-edit") if err != nil { return err } hardlinkAbsPath := tempDirAbsPath.Join(targetRelPath) // Attempt to create the hard link. If this succeeds, continue to // the next target. Hardlinking will fail if the temporary directory // is on a different filesystem to the source directory, which is // not the case for most users. The user can set the tempDir // configuration variable if needed. if err := os.MkdirAll(hardlinkAbsPath.Dir().String(), 0o700); err != nil { return err } if err := c.baseSystem.Link(c.SourceDirAbsPath.Join(sourceRelPath.RelPath()), hardlinkAbsPath); err == nil { editorArgs = append(editorArgs, hardlinkAbsPath.String()) continue TARGET_REL_PATH } // Otherwise, fall through to the default option of editing the // source file in the source state. fallthrough default: sourceAbsPath := c.SourceDirAbsPath.Join(sourceRelPath.RelPath()) editorArgs = append(editorArgs, sourceAbsPath.String()) } } postEditFunc := func() error { for _, transparentlyDecryptedFile := range transparentlyDecryptedFiles { postEditPlaintext, err := c.baseSystem.ReadFile(transparentlyDecryptedFile.decryptedAbsPath) if err != nil { return err } if bytes.Equal(postEditPlaintext, transparentlyDecryptedFile.preEditPlaintext) { return nil } contents, err := c.encryption.EncryptFile(transparentlyDecryptedFile.decryptedAbsPath) if err != nil { return err } if err := c.baseSystem.WriteFile(transparentlyDecryptedFile.sourceAbsPath, contents, 0o666&^c.Umask); err != nil { return err } } if c.Edit.Apply || c.Edit.Watch { // Reset the cached source state to ensure that we re-read any // changed files. // // FIXME Be more precise in what we invalidate. Only the changed // files need to be re-read, not the entire source state. c.resetSourceState() if err := c.applyArgs(cmd.Context(), c.destSystem, c.DestDirAbsPath, args, applyArgsOptions{ cmd: cmd, filter: c.Edit.filter, init: c.Edit.init, recursive: true, umask: c.Umask, preApplyFunc: c.defaultPreApplyFunc, }); err != nil { return err } } return nil } if c.Edit.Watch { watcher, err := fsnotify.NewWatcher() if err != nil { return err } defer watcher.Close() for _, editorArg := range editorArgs { // FIXME watch directories recursively if err := watcher.Add(editorArg); err != nil { return err } } go func() { for { select { case event, ok := <-watcher.Events: if !ok { return } c.logger.Debug("watcher.Events", slog.String("Name", event.Name), chezmoilog.Stringer("Op", event.Op)) err := postEditFunc() chezmoilog.InfoOrError(c.logger, "postEditFunc", err) case _, ok := <-watcher.Errors: if !ok { return } chezmoilog.InfoOrError(c.logger, "watcher.Errors", err) } } }() } if err := c.runEditor(editorArgs); err != nil { return err } return postEditFunc() } ================================================ FILE: internal/cmd/editconfigcmd.go ================================================ package cmd import ( "github.com/spf13/cobra" ) func (c *Config) newEditConfigCmd() *cobra.Command { editConfigCmd := &cobra.Command{ GroupID: groupIDAdvanced, Use: "edit-config", Short: "Edit the configuration file", Long: mustLongHelp("edit-config"), Example: example("edit-config"), Args: cobra.NoArgs, ValidArgsFunction: cobra.NoFileCompletions, RunE: c.runEditConfigCmd, Annotations: newAnnotations( doesNotRequireValidConfig, modifiesConfigFile, persistentStateModeReadOnly, requiresConfigDirectory, runsCommands, ), } return editConfigCmd } func (c *Config) runEditConfigCmd(cmd *cobra.Command, args []string) error { configFileAbsPath, err := c.getConfigFileAbsPath() if err != nil { return err } return c.runEditor([]string{configFileAbsPath.String()}) } ================================================ FILE: internal/cmd/editconfigtemplatecmd.go ================================================ package cmd import ( "errors" "io/fs" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) func (c *Config) newEditConfigTemplateCmd() *cobra.Command { editConfigCmd := &cobra.Command{ GroupID: groupIDAdvanced, Use: "edit-config-template", Short: "Edit the configuration file template", Long: mustLongHelp("edit-config-template"), Example: example("edit-config-template"), Args: cobra.NoArgs, ValidArgsFunction: cobra.NoFileCompletions, RunE: c.makeRunEWithSourceState(c.runEditConfigTemplateCmd), Annotations: newAnnotations( doesNotRequireValidConfig, modifiesSourceDirectory, persistentStateModeReadWrite, runsCommands, ), } return editConfigCmd } func (c *Config) runEditConfigTemplateCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { var configTemplateAbsPath chezmoi.AbsPath switch configTemplate, err := c.findConfigTemplate(); { case err != nil: return err case configTemplate != nil: configTemplateAbsPath = configTemplate.sourceAbsPath default: if err := chezmoi.MkdirAll(c.sourceSystem, c.sourceDirAbsPath, fs.ModePerm); err != nil && !errors.Is(err, fs.ErrExist) { return err } configFileAbsPath, err := c.getConfigFileAbsPath() if err != nil { return err } configFileBase := "." + configFileAbsPath.Base() + ".tmpl" configTemplateAbsPath = c.sourceDirAbsPath.JoinString(configFileBase) switch data, err := c.baseSystem.ReadFile(configFileAbsPath); { case errors.Is(err, fs.ErrNotExist): // Do nothing. case err != nil: return err default: if err := c.sourceSystem.WriteFile(configTemplateAbsPath, data, 0o666&^c.Umask); err != nil { return err } } } return c.runEditor([]string{configTemplateAbsPath.String()}) } ================================================ FILE: internal/cmd/editencryptedcmd.go ================================================ package cmd import ( "path/filepath" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) func (c *Config) newEditEncryptedCmd() *cobra.Command { editEncryptedCmd := &cobra.Command{ GroupID: groupIDEncryption, Use: "edit-encrypted filename...", Short: "Edit an encrypted file", Long: mustLongHelp("edit-encrypted"), Example: example("edit-encrypted"), Args: cobra.MinimumNArgs(1), RunE: c.runEditEncryptedCmd, Annotations: newAnnotations( modifiesSourceDirectory, persistentStateModeEmpty, runsCommands, ), } return editEncryptedCmd } func (c *Config) runEditEncryptedCmd(cmd *cobra.Command, args []string) error { type argument struct { ciphertextAbsPath chezmoi.AbsPath plaintextAbsPath chezmoi.AbsPath } arguments := make([]argument, len(args)) // Write plaintexts to a temporary directory. tempDirAbsPath, err := c.tempDir("chezmoi-edit-encrypted") if err != nil { return err } for i, arg := range args { arg = filepath.Clean(arg) ciphertextAbsPath, err := chezmoi.NewAbsPathFromExtPath(arg, c.homeDirAbsPath) if err != nil { return err } arguments[i].ciphertextAbsPath = ciphertextAbsPath ciphertext, err := c.baseSystem.ReadFile(ciphertextAbsPath) if err != nil { return err } relPath := ciphertextAbsPath.MustTrimDirPrefix(c.homeDirAbsPath) plaintextAbsPath := tempDirAbsPath.Join(relPath) if err := chezmoi.MkdirAll(c.baseSystem, plaintextAbsPath.Dir(), 0o700); err != nil { return err } if err := c.encryption.DecryptToFile(plaintextAbsPath, ciphertext); err != nil { return err } arguments[i].plaintextAbsPath = plaintextAbsPath } // Run the editor on the plaintexts. editorArgs := make([]string, len(arguments)) for i, argument := range arguments { editorArgs[i] = argument.plaintextAbsPath.String() } if err := c.runEditor(editorArgs); err != nil { return err } // Write the ciphertexts. // // FIXME only write the ciphertext if the plaintext has changed // FIXME preserve original plaintext file mode for _, argument := range arguments { ciphertext, err := c.encryption.EncryptFile(argument.plaintextAbsPath) if err != nil { return err } if err := c.baseSystem.WriteFile(argument.ciphertextAbsPath, ciphertext, 0o666&^c.Umask); err != nil { return err } } return nil } ================================================ FILE: internal/cmd/ejsontemplatefuncs.go ================================================ package cmd import ( "encoding/json" "github.com/Shopify/ejson" ) type ejsonConfig struct { KeyDir string `json:"keyDir" mapstructure:"keyDir" yaml:"keyDir"` Key string `json:"key" mapstructure:"key" yaml:"key"` cache map[string]any } func (c *Config) ejsonDecryptWithKeyTemplateFunc(filePath, key string) any { if data, ok := c.Ejson.cache[filePath]; ok { return data } if c.Ejson.cache == nil { c.Ejson.cache = make(map[string]any) } decrypted := mustValue(ejson.DecryptFile(filePath, c.Ejson.KeyDir, key)) var data any must(json.Unmarshal(decrypted, &data)) c.Ejson.cache[filePath] = data return data } func (c *Config) ejsonDecryptTemplateFunc(filePath string) any { return c.ejsonDecryptWithKeyTemplateFunc(filePath, c.Ejson.Key) } ================================================ FILE: internal/cmd/encryptcmd.go ================================================ package cmd import ( "github.com/spf13/cobra" ) func (c *Config) newEncryptCmd() *cobra.Command { encryptCmd := &cobra.Command{ GroupID: groupIDEncryption, Use: "encrypt [file...]", Short: "Encrypt file or standard input", Long: mustLongHelp("encrypt"), Example: example("encrypt"), RunE: c.runEncryptCmd, Annotations: newAnnotations( persistentStateModeReadOnly, ), } return encryptCmd } func (c *Config) runEncryptCmd(cmd *cobra.Command, args []string) error { return c.filterInput(args, c.encryption.Encrypt) } ================================================ FILE: internal/cmd/encryptiontemplatefuncs.go ================================================ package cmd func (c *Config) decryptTemplateFunc(ciphertext string) string { return string(mustValue(c.encryption.Decrypt([]byte(ciphertext)))) } func (c *Config) encryptTemplateFunc(plaintext string) string { return string(mustValue(c.encryption.Encrypt([]byte(plaintext)))) } ================================================ FILE: internal/cmd/errors.go ================================================ package cmd import ( "fmt" "os/exec" ) type cmdOutputError struct { path string args []string output []byte err error } func newCmdOutputError(cmd *exec.Cmd, output []byte, err error) *cmdOutputError { return &cmdOutputError{ path: cmd.Path, args: cmd.Args, output: output, err: err, } } func (e *cmdOutputError) Error() string { if len(e.output) == 0 { return fmt.Sprintf("%s: %v", shellQuoteCommand(e.path, e.args[1:]), e.err) } return fmt.Sprintf("%s: %v\n%s", shellQuoteCommand(e.path, e.args[1:]), e.err, e.output) } func (e *cmdOutputError) Unwrap() error { return e.err } ================================================ FILE: internal/cmd/executetemplatecmd.go ================================================ package cmd import ( "fmt" "io" "os" "slices" "strconv" "strings" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type executeTemplateCmdConfig struct { file bool init bool promptBool map[string]string promptChoice map[string]string promptInt map[string]int promptMultichoice map[string]string promptString map[string]string stdinIsATTY bool templateOptions chezmoi.TemplateOptions withStdin bool } func (c *Config) newExecuteTemplateCmd() *cobra.Command { executeTemplateCmd := &cobra.Command{ GroupID: groupIDTemplate, Use: "execute-template [template]...", Short: "Execute the given template(s)", Long: mustLongHelp("execute-template"), Example: example("execute-template"), RunE: c.runExecuteTemplateCmd, Annotations: newAnnotations( persistentStateModeReadWrite, ), } executeTemplateCmd.Flags(). BoolVarP(&c.executeTemplate.file, "file", "f", c.executeTemplate.file, "Treat arguments as filenames") executeTemplateCmd.Flags().BoolVarP(&c.executeTemplate.init, "init", "i", c.executeTemplate.init, "Simulate chezmoi init") executeTemplateCmd.Flags(). StringToStringVar(&c.executeTemplate.promptBool, "promptBool", c.executeTemplate.promptBool, "Simulate promptBool") executeTemplateCmd.Flags(). StringToStringVar(&c.executeTemplate.promptChoice, "promptChoice", c.executeTemplate.promptChoice, "Simulate promptChoice") executeTemplateCmd.Flags(). StringToIntVar(&c.executeTemplate.promptInt, "promptInt", c.executeTemplate.promptInt, "Simulate promptInt") executeTemplateCmd.Flags(). StringToStringVar(&c.executeTemplate.promptMultichoice, "promptMultichoice", c.executeTemplate.promptMultichoice, "Simulate promptMultichoice") executeTemplateCmd.Flags(). StringToStringVarP(&c.executeTemplate.promptString, "promptString", "p", c.executeTemplate.promptString, "Simulate promptString") executeTemplateCmd.Flags(). BoolVar(&c.executeTemplate.stdinIsATTY, "stdinisatty", c.executeTemplate.stdinIsATTY, "Simulate stdinIsATTY") executeTemplateCmd.Flags(). StringVar(&c.executeTemplate.templateOptions.LeftDelimiter, "left-delimiter", c.executeTemplate.templateOptions.LeftDelimiter, "Set left template delimiter") executeTemplateCmd.Flags(). StringVar(&c.executeTemplate.templateOptions.RightDelimiter, "right-delimiter", c.executeTemplate.templateOptions.RightDelimiter, "Set right template delimiter") executeTemplateCmd.Flags(). BoolVar(&c.executeTemplate.withStdin, "with-stdin", c.executeTemplate.withStdin, "Set .chezmoi.stdin to the contents of the standard input") return executeTemplateCmd } func (c *Config) runExecuteTemplateCmd(cmd *cobra.Command, args []string) error { options := []chezmoi.SourceStateOption{ chezmoi.WithTemplateDataOnly(true), chezmoi.WithReadTemplates(!c.executeTemplate.init), } if c.executeTemplate.init { options = append(options, chezmoi.WithReadTemplateData(false)) } if c.executeTemplate.withStdin && len(args) > 0 { stdin, err := io.ReadAll(c.stdin) if err != nil { return err } options = append(options, chezmoi.WithPriorityTemplateData(map[string]any{ "chezmoi": map[string]any{ "stdin": string(stdin), }, })) } sourceState, err := c.newSourceState(cmd.Context(), cmd, options...) if err != nil { return err } promptBool := make(map[string]bool) for key, valueStr := range c.executeTemplate.promptBool { value, err := chezmoi.ParseBool(valueStr) if err != nil { return err } promptBool[key] = value } if c.executeTemplate.init { promptBoolInitTemplateFunc := func(prompt string, args ...bool) bool { switch len(args) { case 0: return promptBool[prompt] case 1: if value, ok := promptBool[prompt]; ok { return value } return args[0] default: panic(fmt.Errorf("want 1 or 2 arguments, got %d", len(args)+1)) } } promptBoolOnceInitTemplateFunc := func(m map[string]any, path any, field string, args ...bool) bool { nestedMap, lastKey := mustValues(nestedMapAtPath(m, path)) if value, ok := nestedMap[lastKey]; ok { if boolValue, ok := value.(bool); ok { return boolValue } } return promptBoolInitTemplateFunc(field, args...) } promptChoiceInitTemplateFunc := func(prompt string, choices any, args ...string) string { choiceStrs := mustValue(anyToStringSlice(choices)) switch len(args) { case 0: if value, ok := c.executeTemplate.promptChoice[prompt]; ok { if !slices.Contains(choiceStrs, value) { panic(fmt.Errorf("%s: invalid choice", value)) } return value } return prompt case 1: if value, ok := c.executeTemplate.promptChoice[prompt]; ok { if !slices.Contains(choiceStrs, value) { panic(fmt.Errorf("%s: invalid choice", value)) } return value } return args[0] default: panic(fmt.Errorf("want 2 or 3 arguments, got %d", len(args)+1)) } } promptChoiceOnceInitTemplateFunc := func(m map[string]any, path any, prompt string, choices []any, args ...string) string { nestedMap, lastKey := mustValues(nestedMapAtPath(m, path)) if value, ok := nestedMap[lastKey]; ok { if stringValue, ok := value.(string); ok { return stringValue } } return promptChoiceInitTemplateFunc(prompt, choices, args...) } promptMultichoiceInitTemplateFunc := func(prompt string, choices any, args ...any) []string { choiceStrs := mustValue(anyToStringSlice(choices)) if value, ok := c.executeTemplate.promptMultichoice[prompt]; ok { values := strings.Split(value, "/") for _, v := range values { if !slices.Contains(choiceStrs, v) { panic(fmt.Errorf("%s: invalid choice", value)) } } return values } if len(args) == 0 { return []string{prompt} } return mustValue(anyToStringSlice(args[0])) } promptMultichoiceOnceInitTemplateFunc := func(m map[string]any, path any, prompt string, choices []any, args ...any) []string { nestedMap, lastKey := mustValues(nestedMapAtPath(m, path)) if value, ok := nestedMap[lastKey]; ok { if stringValue, ok := value.(string); ok { return strings.Split(stringValue, ",") } } return promptMultichoiceInitTemplateFunc(prompt, choices, args...) } promptIntInitTemplateFunc := func(prompt string, args ...int64) int64 { switch len(args) { case 0: return int64(c.executeTemplate.promptInt[prompt]) case 1: if value, ok := c.executeTemplate.promptInt[prompt]; ok { return int64(value) } return args[0] default: panic(fmt.Errorf("want 1 or 2 arguments, got %d", len(args)+1)) } } promptIntOnceInitTemplateFunc := func(m map[string]any, path any, prompt string, args ...int64) int64 { nestedMap, lastKey := mustValues(nestedMapAtPath(m, path)) if value, ok := nestedMap[lastKey]; ok { if intValue, ok := value.(int64); ok { return intValue } } return promptIntInitTemplateFunc(prompt, args...) } promptStringInitTemplateFunc := func(prompt string, args ...string) string { switch len(args) { case 0: if value, ok := c.executeTemplate.promptString[prompt]; ok { return value } return prompt case 1: if value, ok := c.executeTemplate.promptString[prompt]; ok { return value } return args[0] default: panic(fmt.Errorf("want 1 or 2 arguments, got %d", len(args)+1)) } } promptStringOnceInitTemplateFunc := func(m map[string]any, path any, prompt string, args ...string) string { nestedMap, lastKey := mustValues(nestedMapAtPath(m, path)) if value, ok := nestedMap[lastKey]; ok { if stringValue, ok := value.(string); ok { return stringValue } } return promptStringInitTemplateFunc(prompt, args...) } stdinIsATTYInitTemplateFunc := func() bool { return c.executeTemplate.stdinIsATTY } initTemplateFuncs := map[string]any{ "exit": c.exitInitTemplateFunc, "promptBool": promptBoolInitTemplateFunc, "promptBoolOnce": promptBoolOnceInitTemplateFunc, "promptChoice": promptChoiceInitTemplateFunc, "promptChoiceOnce": promptChoiceOnceInitTemplateFunc, "promptInt": promptIntInitTemplateFunc, "promptIntOnce": promptIntOnceInitTemplateFunc, "promptMultichoice": promptMultichoiceInitTemplateFunc, "promptMultichoiceOnce": promptMultichoiceOnceInitTemplateFunc, "promptString": promptStringInitTemplateFunc, "promptStringOnce": promptStringOnceInitTemplateFunc, "stdinIsATTY": stdinIsATTYInitTemplateFunc, "writeToStdout": c.writeToStdout, } chezmoi.RecursiveMerge(c.templateFuncs, initTemplateFuncs) } if len(args) == 0 { data, err := io.ReadAll(c.stdin) if err != nil { return err } output, err := sourceState.ExecuteTemplateData(chezmoi.ExecuteTemplateDataOptions{ NameRelPath: chezmoi.NewRelPath("stdin"), Data: data, TemplateOptions: c.executeTemplate.templateOptions, }) if err != nil { return err } return c.writeOutput(output, 0o666) } output := strings.Builder{} for i, arg := range args { var data []byte var nameRelPath chezmoi.RelPath if c.executeTemplate.file { // If the argument filename is in the source directory, then // specify the template sourcePath as relative to sourceDir, just // like `chezmoi apply` does. If it is not, then pass it unmodified. path, err := chezmoi.NormalizePath(arg) if err != nil { return err } sourceDir, err := c.getSourceDirAbsPath(nil) if err != nil { return err } if relPath, err := path.TrimDirPrefix(sourceDir); err == nil { nameRelPath = relPath } else { nameRelPath = chezmoi.NewRelPath(path.String()) } data, err = os.ReadFile(arg) if err != nil { return err } } else { nameRelPath = chezmoi.NewRelPath("arg" + strconv.Itoa(i+1)) data = []byte(arg) } result, err := sourceState.ExecuteTemplateData(chezmoi.ExecuteTemplateDataOptions{ NameRelPath: nameRelPath, Data: data, TemplateOptions: c.executeTemplate.templateOptions, }) if err != nil { return err } if _, err := output.Write(result); err != nil { return err } } return c.writeOutputString(output.String(), 0o666) } ================================================ FILE: internal/cmd/forgetcmd.go ================================================ package cmd import ( "fmt" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) func (c *Config) newForgetCmd() *cobra.Command { forgetCmd := &cobra.Command{ GroupID: groupIDDaily, Use: "forget target...", Aliases: []string{"unmanage"}, Short: "Remove a target from the source state", Long: mustLongHelp("forget"), Example: example("forget"), ValidArgsFunction: c.targetValidArgs, Args: cobra.MinimumNArgs(1), RunE: c.makeRunEWithSourceState(c.runForgetCmd), Annotations: newAnnotations( modifiesSourceDirectory, persistentStateModeReadWrite, ), } return forgetCmd } func (c *Config) runForgetCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { targetRelPaths, err := c.targetRelPaths(sourceState, args, targetRelPathsOptions{}) if err != nil { return err } for _, targetRelPath := range targetRelPaths { sourceStateEntry := sourceState.MustEntry(targetRelPath) // Skip source state entries that are not regular entries. These are // removes or externals, which we cannot handle. switch sourceStateOrigin := sourceStateEntry.Origin(); sourceStateOrigin.(type) { case chezmoi.SourceStateOriginAbsPath: // OK, keep going. case chezmoi.SourceStateOriginRemove: c.errorf("warning: %s: cannot forget entry from remove\n", targetRelPath) continue case *chezmoi.External: c.errorf("warning: %s: cannot forget entry from external %s\n", targetRelPath, sourceStateOrigin.OriginString()) continue default: panic(fmt.Sprintf("%s: %T: unknown source state origin type", targetRelPath, sourceStateOrigin)) } relPath := sourceStateEntry.SourceRelPath().RelPath() if relPath.IsEmpty() { c.errorf("warning: %s: ignoring implicitly managed file\n", targetRelPath) continue } sourceAbsPath := c.SourceDirAbsPath.Join(relPath) if !c.force { choice, err := c.promptChoice(fmt.Sprintf("Remove %s", sourceAbsPath), choicesYesNoAllQuit) if err != nil { return err } switch choice { case "yes": case "no": continue case "all": c.force = false case "quit": return nil } } if err := c.sourceSystem.RemoveAll(sourceAbsPath); err != nil { return err } targetAbsPath := c.DestDirAbsPath.Join(targetRelPath) if err := c.persistentState.Delete(chezmoi.EntryStateBucket, targetAbsPath.Bytes()); err != nil { return err } } return nil } ================================================ FILE: internal/cmd/generatecmd.go ================================================ package cmd import ( "strings" "github.com/spf13/cobra" "chezmoi.io/chezmoi/assets/templates" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoigit" ) type generateCmdConfig struct { installInitShellSh generateInstallInitShellShCmdConfig } type generateInstallInitShellShCmdConfig struct { interactive bool packageManager string shell bool } func (c *Config) newGenerateCmd() *cobra.Command { generateCmd := &cobra.Command{ GroupID: groupIDAdvanced, Use: "generate file", Short: "Generate a file for use with chezmoi", Long: mustLongHelp("generate"), Example: example("generate"), Annotations: newAnnotations( persistentStateModeNone, ), } generateGitCommitMessageCmd := &cobra.Command{ Use: "git-commit-message", Short: "Generate a git commit message", Args: cobra.NoArgs, RunE: c.runGenerateGitCommitMessageCmd, Annotations: newAnnotations( persistentStateModeNone, ), } generateCmd.AddCommand(generateGitCommitMessageCmd) generateInstallShCmd := &cobra.Command{ Use: "install.sh", Short: "Generate an install script", Args: cobra.NoArgs, RunE: c.runGenerateInstallShCmd, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeNone, ), } generateCmd.AddCommand(generateInstallShCmd) generateInstallInitShellShCmd := &cobra.Command{ Use: "install-init-shell.sh", Short: "Generate an install script that also executes a shell", Args: cobra.MinimumNArgs(1), RunE: c.makeRunEWithSourceState(c.runGenerateInstallInitShellShCmd), Annotations: newAnnotations( persistentStateModeReadWrite, ), } generateInstallInitShellShCmd.Flags(). BoolVarP(&c.generate.installInitShellSh.interactive, "interactive", "i", c.generate.installInitShellSh.interactive, "Set interactive") generateInstallInitShellShCmd.Flags(). StringVarP(&c.generate.installInitShellSh.packageManager, "package-manager", "p", c.generate.installInitShellSh.packageManager, "Package manager") generateInstallInitShellShCmd.Flags(). BoolVarP(&c.generate.installInitShellSh.shell, "shell", "s", c.generate.installInitShellSh.shell, "Set shell") generateCmd.AddCommand(generateInstallInitShellShCmd) return generateCmd } func (c *Config) runGenerateGitCommitMessageCmd(cmd *cobra.Command, args []string) error { builder := strings.Builder{} builder.Grow(16384) output, err := c.cmdOutput(c.WorkingTreeAbsPath, c.Git.Command, []string{"status", "--porcelain=v2"}) if err != nil { return err } status, err := chezmoigit.ParseStatusPorcelainV2(output) if err != nil { return err } data, err := c.gitCommitMessage(cmd, status) if err != nil { return err } if _, err := builder.Write(data); err != nil { return err } return c.writeOutputString(builder.String(), 0o666) } func (c *Config) runGenerateInstallShCmd(cmd *cobra.Command, args []string) error { return c.writeOutput(templates.InstallSh, 0o777) } func (c *Config) runGenerateInstallInitShellShCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { script, err := sourceState.ExecuteTemplateData(chezmoi.ExecuteTemplateDataOptions{ NameRelPath: chezmoi.NewRelPath("install-init-shell.sh.tmpl"), Data: templates.InstallInitShellShTmpl, ExtraData: map[string]any{ "args": args, "interactive": c.generate.installInitShellSh.interactive, "packageManager": c.generate.installInitShellSh.packageManager, "shell": c.generate.installInitShellSh.shell, }, }) if err != nil { return err } return c.writeOutput(script, 0o777) } ================================================ FILE: internal/cmd/gitcmd.go ================================================ package cmd import ( "github.com/spf13/cobra" ) type gitCmdConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` AutoAdd bool `json:"autoadd" mapstructure:"autoadd" yaml:"autoadd"` AutoCommit bool `json:"autocommit" mapstructure:"autocommit" yaml:"autocommit"` AutoPush bool `json:"autopush" mapstructure:"autopush" yaml:"autopush"` CommitMessageTemplate string `json:"commitMessageTemplate" mapstructure:"commitMessageTemplate" yaml:"commitMessageTemplate"` CommitMessageTemplateFile string `json:"commitMessageTemplateFile" mapstructure:"commitMessageTemplateFile" yaml:"commitMessageTemplateFile"` LFS bool `json:"lfs" mapstructure:"lfs" yaml:"lfs"` } func (c *Config) newGitCmd() *cobra.Command { gitCmd := &cobra.Command{ GroupID: groupIDAdvanced, Use: "git [arg]...", Short: "Run git in the source directory", Long: mustLongHelp("git"), Example: example("git"), RunE: c.runGitCmd, Annotations: newAnnotations( createSourceDirectoryIfNeeded, persistentStateModeNone, requiresWorkingTree, runsCommands, ), } return gitCmd } func (c *Config) runGitCmd(cmd *cobra.Command, args []string) error { return c.run(c.WorkingTreeAbsPath, c.Git.Command, args) } ================================================ FILE: internal/cmd/githubtemplatefuncs.go ================================================ package cmd import ( "context" "fmt" "path" "strings" "time" "github.com/google/go-github/v61/github" "chezmoi.io/chezmoi/internal/chezmoi" ) type gitHubConfig struct { RefreshPeriod time.Duration `json:"refreshPeriod" mapstructure:"refreshPeriod" yaml:"refreshPeriod"` } type gitHubKeysState struct { RequestedAt time.Time `json:"requestedAt" yaml:"requestedAt"` Keys []*github.Key `json:"keys" yaml:"keys"` } type gitHubLatestReleaseState struct { RequestedAt time.Time `json:"requestedAt" yaml:"requestedAt"` Release *github.RepositoryRelease `json:"release" yaml:"release"` } type gitHubReleasesState struct { RequestedAt time.Time `json:"requestedAt" yaml:"requestedAt"` Releases []*github.RepositoryRelease `json:"releases" yaml:"releases"` } type gitHubTagsState struct { RequestedAt time.Time `json:"requestedAt" yaml:"requestedAt"` Tags []*github.RepositoryTag `json:"tags" yaml:"tags"` } var ( gitHubKeysStateBucket = []byte("gitHubLatestKeysState") gitHubLatestReleaseStateBucket = []byte("gitHubLatestReleaseState") gitHubReleasesStateBucket = []byte("gitHubReleasesState") gitHubTagsStateBucket = []byte("gitHubTagsState") gitHubVersionReleaseStateBucket = []byte("gitHubVersionReleaseState") ) type gitHubData struct { client *github.Client clientErr error keysCache map[string][]*github.Key versionReleaseCache map[string]map[string]map[string]*github.RepositoryRelease latestReleaseCache map[string]map[string]*github.RepositoryRelease releasesCache map[string]map[string][]*github.RepositoryRelease tagsCache map[string]map[string][]*github.RepositoryTag } func (c *Config) gitHubKeysTemplateFunc(user string) []*github.Key { if keys, ok := c.gitHub.keysCache[user]; ok { return keys } now := time.Now() gitHubKeysKey := []byte(user) if c.GitHub.RefreshPeriod != 0 { var gitHubKeysValue gitHubKeysState ok := mustValue(chezmoi.PersistentStateGet(c.persistentState, gitHubKeysStateBucket, gitHubKeysKey, &gitHubKeysValue)) if ok && now.Before(gitHubKeysValue.RequestedAt.Add(c.GitHub.RefreshPeriod)) { return gitHubKeysValue.Keys } } ctx, cancel := context.WithCancel(context.Background()) defer cancel() gitHubClient := mustValue(c.getGitHubClient(ctx)) var allKeys []*github.Key opts := &github.ListOptions{ PerPage: 100, } for { keys, resp := mustValues(gitHubClient.Users.ListKeys(ctx, user, opts)) allKeys = append(allKeys, keys...) if resp.NextPage == 0 { break } opts.Page = resp.NextPage } must(chezmoi.PersistentStateSet(c.persistentState, gitHubKeysStateBucket, gitHubKeysKey, &gitHubKeysState{ RequestedAt: now, Keys: allKeys, })) if c.gitHub.keysCache == nil { c.gitHub.keysCache = make(map[string][]*github.Key) } c.gitHub.keysCache[user] = allKeys return allKeys } func (c *Config) githubMatchingReleaseAssetURL(release *github.RepositoryRelease, pattern string) string { for _, asset := range release.Assets { if asset.Name == nil { continue } if mustValue(path.Match(pattern, *asset.Name)) { return *asset.BrowserDownloadURL } } return "" } func (c *Config) gitHubLatestReleaseAssetURLTemplateFunc(ownerRepo, pattern string) string { release := mustValue(c.gitHubLatestRelease(ownerRepo)) return c.githubMatchingReleaseAssetURL(release, pattern) } func (c *Config) gitHubReleaseAssetURLTemplateFunc(ownerRepo, version, pattern string) string { release := mustValue(c.gitHubRelease(ownerRepo, version)) return c.githubMatchingReleaseAssetURL(release, pattern) } func (c *Config) gitHubRelease(ownerRepo, version string) (*github.RepositoryRelease, error) { owner, repo, err := gitHubSplitOwnerRepo(ownerRepo) if err != nil { return nil, err } if c.gitHub.versionReleaseCache == nil { c.gitHub.versionReleaseCache = make(map[string]map[string]map[string]*github.RepositoryRelease) } if c.gitHub.versionReleaseCache[owner] == nil { c.gitHub.versionReleaseCache[owner] = make(map[string]map[string]*github.RepositoryRelease) } if c.gitHub.versionReleaseCache[owner][repo] == nil { c.gitHub.versionReleaseCache[owner][repo] = make(map[string]*github.RepositoryRelease) } if release := c.gitHub.versionReleaseCache[owner][repo][version]; release != nil { return release, nil } now := time.Now() gitHubVersionReleaseKey := []byte(owner + "/" + repo + "/" + version) if c.GitHub.RefreshPeriod != 0 { var gitHubVersionReleaseStateValue gitHubLatestReleaseState switch ok, err := chezmoi.PersistentStateGet(c.persistentState, gitHubVersionReleaseStateBucket, gitHubVersionReleaseKey, &gitHubVersionReleaseStateValue); { case err != nil: return nil, err case ok && now.Before(gitHubVersionReleaseStateValue.RequestedAt.Add(c.GitHub.RefreshPeriod)): return gitHubVersionReleaseStateValue.Release, nil } } ctx, cancel := context.WithCancel(context.Background()) defer cancel() gitHubClient, err := c.getGitHubClient(ctx) if err != nil { return nil, err } release, _, err := gitHubClient.Repositories.GetReleaseByTag(ctx, owner, repo, version) if err != nil { return nil, err } if err := chezmoi.PersistentStateSet( c.persistentState, gitHubVersionReleaseStateBucket, gitHubVersionReleaseKey, &gitHubLatestReleaseState{ RequestedAt: now, Release: release, }, ); err != nil { return nil, err } c.gitHub.versionReleaseCache[owner][repo][version] = release return release, nil } func (c *Config) gitHubLatestRelease(ownerRepo string) (*github.RepositoryRelease, error) { owner, repo, err := gitHubSplitOwnerRepo(ownerRepo) if err != nil { return nil, err } if release := c.gitHub.latestReleaseCache[owner][repo]; release != nil { return release, nil } now := time.Now() gitHubLatestReleaseKey := []byte(owner + "/" + repo) if c.GitHub.RefreshPeriod != 0 { var gitHubLatestReleaseStateValue gitHubLatestReleaseState switch ok, err := chezmoi.PersistentStateGet(c.persistentState, gitHubLatestReleaseStateBucket, gitHubLatestReleaseKey, &gitHubLatestReleaseStateValue); { case err != nil: return nil, err case ok && now.Before(gitHubLatestReleaseStateValue.RequestedAt.Add(c.GitHub.RefreshPeriod)): return gitHubLatestReleaseStateValue.Release, nil } } ctx, cancel := context.WithCancel(context.Background()) defer cancel() gitHubClient, err := c.getGitHubClient(ctx) if err != nil { return nil, err } release, _, err := gitHubClient.Repositories.GetLatestRelease(ctx, owner, repo) if err != nil { return nil, err } if err := chezmoi.PersistentStateSet( c.persistentState, gitHubLatestReleaseStateBucket, gitHubLatestReleaseKey, &gitHubLatestReleaseState{ RequestedAt: now, Release: release, }, ); err != nil { return nil, err } if c.gitHub.latestReleaseCache == nil { c.gitHub.latestReleaseCache = make(map[string]map[string]*github.RepositoryRelease) } if c.gitHub.latestReleaseCache[owner] == nil { c.gitHub.latestReleaseCache[owner] = make(map[string]*github.RepositoryRelease) } c.gitHub.latestReleaseCache[owner][repo] = release return release, nil } func (c *Config) gitHubLatestReleaseTemplateFunc(ownerRepo string) *github.RepositoryRelease { return mustValue(c.gitHubLatestRelease(ownerRepo)) } func (c *Config) gitHubReleaseTemplateFunc(ownerRepo, version string) *github.RepositoryRelease { return mustValue(c.gitHubRelease(ownerRepo, version)) } func (c *Config) gitHubLatestTagTemplateFunc(ownerRepo string) *github.RepositoryTag { tags := mustValue(c.getGitHubTags(ownerRepo)) if len(tags) > 0 { return tags[0] } return nil } func (c *Config) gitHubReleasesTemplateFunc(ownerRepo string) []*github.RepositoryRelease { owner, repo := mustValues(gitHubSplitOwnerRepo(ownerRepo)) if releases := c.gitHub.releasesCache[owner][repo]; releases != nil { return releases } now := time.Now() gitHubReleasesKey := []byte(owner + "/" + repo) if c.GitHub.RefreshPeriod != 0 { var gitHubReleasesStateValue gitHubReleasesState ok := mustValue( chezmoi.PersistentStateGet( c.persistentState, gitHubReleasesStateBucket, gitHubReleasesKey, &gitHubReleasesStateValue, ), ) if ok && now.Before(gitHubReleasesStateValue.RequestedAt.Add(c.GitHub.RefreshPeriod)) { return gitHubReleasesStateValue.Releases } } ctx, cancel := context.WithCancel(context.Background()) defer cancel() gitHubClient := mustValue(c.getGitHubClient(ctx)) releases, _ := mustValues(gitHubClient.Repositories.ListReleases(ctx, owner, repo, nil)) must(chezmoi.PersistentStateSet(c.persistentState, gitHubReleasesStateBucket, gitHubReleasesKey, &gitHubReleasesState{ RequestedAt: now, Releases: releases, })) if c.gitHub.releasesCache == nil { c.gitHub.releasesCache = make(map[string]map[string][]*github.RepositoryRelease) } if c.gitHub.releasesCache[owner] == nil { c.gitHub.releasesCache[owner] = make(map[string][]*github.RepositoryRelease) } c.gitHub.releasesCache[owner][repo] = releases return releases } func (c *Config) gitHubTagsTemplateFunc(ownerRepo string) []*github.RepositoryTag { return mustValue(c.getGitHubTags(ownerRepo)) } func (c *Config) getGitHubTags(ownerRepo string) ([]*github.RepositoryTag, error) { owner, repo, err := gitHubSplitOwnerRepo(ownerRepo) if err != nil { return nil, err } if tags := c.gitHub.tagsCache[owner][repo]; tags != nil { return tags, nil } now := time.Now() gitHubTagsKey := []byte(owner + "/" + repo) if c.GitHub.RefreshPeriod != 0 { var gitHubTagsStateValue gitHubTagsState switch ok, err := chezmoi.PersistentStateGet(c.persistentState, gitHubTagsStateBucket, gitHubTagsKey, &gitHubTagsStateValue); { case err != nil: return nil, err case ok && now.Before(gitHubTagsStateValue.RequestedAt.Add(c.GitHub.RefreshPeriod)): return gitHubTagsStateValue.Tags, nil } } ctx, cancel := context.WithCancel(context.Background()) defer cancel() gitHubClient, err := c.getGitHubClient(ctx) if err != nil { return nil, err } tags, _, err := gitHubClient.Repositories.ListTags(ctx, owner, repo, nil) if err != nil { return nil, err } if err := chezmoi.PersistentStateSet(c.persistentState, gitHubTagsStateBucket, gitHubTagsKey, &gitHubTagsState{ RequestedAt: now, Tags: tags, }); err != nil { return nil, err } if c.gitHub.tagsCache == nil { c.gitHub.tagsCache = make(map[string]map[string][]*github.RepositoryTag) } if c.gitHub.tagsCache[owner] == nil { c.gitHub.tagsCache[owner] = make(map[string][]*github.RepositoryTag) } c.gitHub.tagsCache[owner][repo] = tags return tags, nil } func (c *Config) getGitHubClient(ctx context.Context) (*github.Client, error) { if c.gitHub.client != nil || c.gitHub.clientErr != nil { return c.gitHub.client, c.gitHub.clientErr } httpClient, err := c.getHTTPClient() if err != nil { c.gitHub.clientErr = err return nil, err } c.gitHub.client = chezmoi.NewGitHubClient(ctx, httpClient) return c.gitHub.client, nil } func gitHubSplitOwnerRepo(ownerRepo string) (owner, repo string, err error) { var ok bool owner, repo, ok = strings.Cut(ownerRepo, "/") if !ok { return "", "", fmt.Errorf("%s: not an owner/repo", ownerRepo) } return owner, repo, nil } ================================================ FILE: internal/cmd/gopasstemplatefuncs.go ================================================ package cmd import ( "bytes" "context" "fmt" "os" "os/exec" "regexp" "github.com/coreos/go-semver/semver" "github.com/gopasspw/gopass/pkg/ctxutil" "github.com/gopasspw/gopass/pkg/gopass" "github.com/gopasspw/gopass/pkg/gopass/api" "chezmoi.io/chezmoi/internal/chezmoilog" ) type gopassMode string const ( gopassModeBuiltin gopassMode = "builtin" gopassModeDefault gopassMode = "" ) var ( // chezmoi uses gopass show --password which was added in v1.6.1. gopassMinVersion = semver.Version{Major: 1, Minor: 6, Patch: 1} gopassVersionArgs = []string{"--version"} gopassVersionRx = regexp.MustCompile(`gopass\s+(\d+\.\d+\.\d+)`) ) type gopassConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Mode gopassMode `json:"mode" mapstructure:"mode" yaml:"mode"` ctx context.Context //nolint:containedctx client *api.Gopass clientErr error passwordCache map[string][]byte cache map[string]string rawCache map[string][]byte } func (c *Config) gopassTemplateFunc(id string) string { if password, ok := c.Gopass.cache[id]; ok { return password } var password string switch c.Gopass.Mode { case gopassModeBuiltin: password = mustValue(c.builtinGopassSecret(id, "latest")).Password() case gopassModeDefault: output := mustValue(c.gopassOutput("show", "--password", id)) passwordBytes, _, _ := bytes.Cut(output, []byte{'\n'}) password = string(passwordBytes) default: panic(fmt.Errorf("%s: invalid mode", c.Gopass.Mode)) } if c.Gopass.cache == nil { c.Gopass.cache = make(map[string]string) } c.Gopass.cache[id] = password return password } func (c *Config) gopassRawTemplateFunc(id string) string { if output, ok := c.Gopass.rawCache[id]; ok { return string(output) } var output []byte switch c.Gopass.Mode { case gopassModeBuiltin: output = mustValue(c.builtinGopassSecret(id, "latest")).Bytes() case gopassModeDefault: output = mustValue(c.gopassOutput("show", "--noparsing", id)) default: panic(fmt.Errorf("%s: invalid mode", c.Gopass.Mode)) } if c.Gopass.rawCache == nil { c.Gopass.rawCache = make(map[string][]byte) } c.Gopass.rawCache[id] = output return string(output) } func (c *Config) builtinGopassClient() (*api.Gopass, error) { if c.Gopass.ctx != nil { return c.Gopass.client, c.Gopass.clientErr } ctx := context.Background() ctx = ctxutil.WithPasswordCallback(ctx, func(filename string, confirm bool) ([]byte, error) { if _, ok := c.Gopass.passwordCache[filename]; !ok { password, err := c.readPassword("Passphrase for "+filename+": ", "passphrase") if err != nil { return nil, err } if c.Gopass.passwordCache == nil { c.Gopass.passwordCache = make(map[string][]byte, 1) } c.Gopass.passwordCache[filename] = []byte(password) } return c.Gopass.passwordCache[filename], nil }) c.Gopass.ctx = ctx //nolint:fatcontext c.Gopass.client, c.Gopass.clientErr = api.New(c.Gopass.ctx) return c.Gopass.client, c.Gopass.clientErr } func (c *Config) builtinGopassSecret(name, revision string) (gopass.Secret, error) { client, err := c.builtinGopassClient() if err != nil { return nil, err } secret, err := client.Get(c.Gopass.ctx, name, revision) if err != nil { return nil, err } return secret, nil } func (c *Config) gopassOutput(args ...string) ([]byte, error) { name := c.Gopass.Command cmd := exec.Command(name, args...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } return output, nil } ================================================ FILE: internal/cmd/helpcmd.go ================================================ package cmd import ( "fmt" "strings" "github.com/spf13/cobra" ) func (c *Config) newHelpCmd() *cobra.Command { helpCmd := &cobra.Command{ GroupID: groupIDDocumentation, Use: "help [command]", Short: "Print help about a command", Long: mustLongHelp("help"), Example: example("help"), RunE: c.runHelpCmd, ValidArgsFunction: cobra.NoFileCompletions, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeNone, ), } return helpCmd } func (c *Config) runHelpCmd(cmd *cobra.Command, args []string) error { subCmd, _, err := cmd.Root().Find(args) if err != nil { return err } if subCmd == nil { return fmt.Errorf("unknown command: %s", strings.Join(args, " ")) } return subCmd.Help() } ================================================ FILE: internal/cmd/helps.gen.go ================================================ // Code generated by chezmoi.io/chezmoi/internal/cmds/generate-helps. DO NOT EDIT. package cmd import ( "chezmoi.io/chezmoi/internal/chezmoiset" ) type help struct { longHelp string example string longFlags chezmoiset.Set[string] shortFlags chezmoiset.Set[string] } var helps = map[string]*help{ "add": { longHelp: "" + " Add targets to the source state. If any target is already in the source\n" + " state, then its source state is replaced with its current state in the\n" + " destination directory.", example: "" + " chezmoi add ~/.bashrc\n" + " chezmoi add ~/.gitconfig --template\n" + " chezmoi add ~/.ssh/id_rsa --encrypt\n" + " chezmoi add ~/.vim --recursive\n" + " chezmoi add ~/.oh-my-zsh --exact --recursive", longFlags: chezmoiset.New( "autotemplate", "create", "encrypt", "exact", "exclude", "follow", "force", "include", "new", "prompt", "quiet", "recursive", "secrets", "template", "template-symlinks", ), shortFlags: chezmoiset.New( "T", "a", "f", "i", "p", "q", "r", "x", ), }, "age": { longHelp: "" + " Interact with age's passphrase-based encryption.", example: "" + " chezmoi age encrypt --passphrase plaintext.txt > ciphertext.txt\n" + " chezmoi age decrypt --passphrase ciphertext.txt > decrypted-ciphertext.txt", }, "age-keygen": { longHelp: "" + " Generate an age identity or convert an age identity to an age recipient.", example: "" + " chezmoi age-keygen\n" + " chezmoi age-keygen -o identity.txt\n" + " chezmoi age-keygen -y identity.txt", longFlags: chezmoiset.New( "convert", "pq", ), shortFlags: chezmoiset.New( "y", ), }, "apply": { longHelp: "" + " Ensure that target... are in the target state, updating them if necessary.\n" + " If no targets are specified, the state of all targets are ensured. If a\n" + " target has been modified since chezmoi last wrote it then the user will be\n" + " prompted if they want to overwrite the file.", example: "" + " chezmoi apply\n" + " chezmoi apply --dry-run --verbose\n" + " chezmoi apply ~/.bashrc", longFlags: chezmoiset.New( "exclude", "include", "init", "parent-dirs", "recursive", "source-path", ), shortFlags: chezmoiset.New( "P", "i", "r", "x", ), }, "archive": { longHelp: "" + " Generate an archive of the target state, or only the targets specified. This\n" + " can be piped into tar to inspect the target state.", example: "" + " chezmoi archive | tar tvf -\n" + " chezmoi archive --output=dotfiles.tar.gz\n" + " chezmoi archive --output=dotfiles.zip", longFlags: chezmoiset.New( "exclude", "format", "gzip", "include", "init", "parent-dirs", "recursive", ), shortFlags: chezmoiset.New( "P", "f", "i", "r", "x", "z", ), }, "cat": { longHelp: "" + " Write the target contents of targets to stdout. targets must be files,\n" + " scripts, or symlinks. For files, the target file contents are written. For\n" + " scripts, the script's contents are written. For symlinks, the target is\n" + " written.", example: "" + " chezmoi cat ~/.bashrc", }, "cat-config": { longHelp: "" + " Print the configuration file.", example: "" + " chezmoi cat-config", }, "cd": { longHelp: "" + " Launch a shell in the working tree (typically the source directory). chezmoi\n" + " will launch the command set by the cd.command configuration variable with\n" + " any extra arguments specified by cd.args. If this is not set, chezmoi will\n" + " attempt to detect your shell and finally fall back to an OS-specific default.\n" + "\n" + " If the optional argument path is present, the shell will be launched in the\n" + " source directory corresponding to path.\n" + "\n" + " The shell will have various CHEZMOI* environment variables set, as for\n" + " scripts.", example: "" + " chezmoi cd\n" + " chezmoi cd ~\n" + " chezmoi cd ~/.config", }, "chattr": { longHelp: "" + " Change the attributes and/or type of targets. modifier specifies what to\n" + " modify.\n" + "\n" + " See attributes for a description of each attribute.\n" + "\n" + " Add attributes by specifying them or their abbreviations directly,\n" + " optionally prefixed with a plus sign (+). Remove attributes by prefixing\n" + " them or their attributes with the string no or a minus sign (-). The\n" + " available attribute modifiers and their abbreviations are:\n" + "\n" + " Attribute modifier | Abbreviation\n" + " -------------------------------------|------------------------------------\n" + " after | a\n" + " before | b\n" + " empty | e\n" + " encrypted | none\n" + " exact | none\n" + " executable | x\n" + " external | none\n" + " once | o\n" + " onchange | none\n" + " private | p\n" + " readonly | r\n" + " remove | none\n" + " template | t\n" + "\n" + " The type of a target can be changed using a type modifier:\n" + "\n" + " Type modifier\n" + " --------------------------------------------------------------------------\n" + " create\n" + " modify\n" + " script\n" + " symlink\n" + "\n" + " The negative form of type modifiers, e.g. nocreate, changes the target to be\n" + " a regular file if it is of that type, otherwise the type is left unchanged.\n" + "\n" + " Multiple modifications may be specified by separating them with a comma (,).\n" + " If you use the -modifier form then you must put modifier after a -- to\n" + " prevent\n" + " chezmoi from interpreting -modifier as an option.", example: "" + " chezmoi chattr template ~/.bashrc\n" + " chezmoi chattr noempty ~/.profile\n" + " chezmoi chattr private,template ~/.netrc\n" + " chezmoi chattr -- -x ~/.zshrc\n" + " chezmoi chattr +create,+private ~/.kube/config", longFlags: chezmoiset.New( "recursive", ), shortFlags: chezmoiset.New( "r", ), }, "completion": { longHelp: "" + " Generate shell completion code for the specified shell (bash, fish,\n" + " powershell, or zsh).", example: "" + " chezmoi completion bash\n" + " chezmoi completion fish --output=~/.config/fish/completions/chezmoi.fish", }, "data": { longHelp: "" + " Write the computed template data to stdout.", example: "" + " chezmoi data\n" + " chezmoi data --format=yaml", longFlags: chezmoiset.New( "format", ), shortFlags: chezmoiset.New( "f", ), }, "decrypt": { longHelp: "" + " Decrypt files using chezmoi's configured encryption. If no files are given,\n" + " decrypt the standard input. The decrypted result is written to the standard\n" + " output or a file if the --output flag is set.", }, "destroy": { longHelp: "" + " Remove target from the source state, the destination directory, and the\n" + " state.", longFlags: chezmoiset.New( "force", "recursive", ), shortFlags: chezmoiset.New( "r", ), }, "diff": { longHelp: "" + " Print the difference between the target state and the destination state for\n" + " targets. If no targets are specified, print the differences for all targets.\n" + "\n" + " If a diff.pager command is set in the configuration file then the output\n" + " will be piped into it.\n" + "\n" + " If diff.command is set then it will be invoked to show individual file\n" + " differences with diff.args passed as arguments. Each element of diff.args is\n" + " interpreted as a template with the variables .Destination and .Target\n" + " available corresponding to the path of the file in the source and target\n" + " state respectively. The default value of diff.args is [\"{{ .Destination }}\",\n" + " \"{{ .Target }}\"]. If diff.args does not contain any template arguments then\n" + " {{ .Destination }} and {{ .Target }} will be appended automatically.", example: "" + " chezmoi diff\n" + " chezmoi diff ~/.bashrc", longFlags: chezmoiset.New( "exclude", "include", "init", "pager", "parent-dirs", "recursive", "reverse", "script-contents", ), shortFlags: chezmoiset.New( "P", "i", "r", "x", ), }, "docker": { example: "" + " chezmoi docker exec $CONTAINER_ID $GITHUB_USERNAME\n" + " chezmoi docker run -p apk alpine:latest $GITHUB_USERNAME", }, "doctor": { longHelp: "" + " Check for potential problems.", example: "" + " chezmoi doctor", longFlags: chezmoiset.New( "no-network", ), }, "dump": { longHelp: "" + " Dump the target state of targets. If no targets are specified, then the\n" + " entire target state.", example: "" + " chezmoi dump ~/.bashrc\n" + " chezmoi dump --format=yaml", longFlags: chezmoiset.New( "exclude", "format", "include", "init", "parent-dirs", "recursive", ), shortFlags: chezmoiset.New( "P", "f", "i", "r", "x", ), }, "dump-config": { longHelp: "" + " Dump the configuration.", example: "" + " chezmoi dump-config", longFlags: chezmoiset.New( "format", ), shortFlags: chezmoiset.New( "f", ), }, "edit": { longHelp: "" + " Edit the source state of targets, which must be files or symlinks. If no\n" + " targets are given then the working tree of the source directory is opened.\n" + "\n" + " Encrypted files are decrypted to a private temporary directory and the\n" + " editor is invoked with the decrypted file. When the editor exits the edited\n" + " decrypted file is re-encrypted and replaces the original file in the source\n" + " state.\n" + "\n" + " If the operating system supports hard links, then the edit command invokes\n" + " the editor with filenames which match the target filename, unless the\n" + " edit.hardlink configuration variable is set to false or the --hardlink=false\n" + " command line flag is set. Templates preserve their .tmpl extension so\n" + " editors can highlight them as templates.", example: "" + " chezmoi edit ~/.bashrc\n" + " chezmoi edit ~/.bashrc --apply\n" + " chezmoi edit", longFlags: chezmoiset.New( "apply", "exclude", "hardlink", "include", "init", "watch", ), shortFlags: chezmoiset.New( "a", "i", "x", ), }, "edit-config": { longHelp: "" + " Edit the configuration file.", example: "" + " chezmoi edit-config", }, "edit-config-template": { longHelp: "" + " Edit the configuration file template. If no configuration file template\n" + " exists, then a new one is created with the contents of the current config\n" + " file.", example: "" + " chezmoi edit-config-template", }, "edit-encrypted": { longHelp: "" + " Edit the encrypted files filenames.\n" + "\n" + " Each filename is decrypted to a temporary directory, the editor is invoked\n" + " on the decrypted files. After the editor returns, each the decrypted file is\n" + " re-encrypted.", example: "" + " chezmoi edit-encrypted encrypted_file", }, "encrypt": { longHelp: "" + " Encrypt files using chezmoi's configured encryption. If no files are given,\n" + " encrypt the standard input. The encrypted result is written to the standard\n" + " output or a file if the --output flag is set.", }, "execute-template": { longHelp: "" + " Execute templates. This is useful for testing templates or for calling\n" + " chezmoi from other scripts. templates are interpreted as literal templates,\n" + " with no whitespace added to the output between arguments. If no templates\n" + " are specified, the template is read from stdin.", example: "" + " chezmoi execute-template '{{ .chezmoi.sourceDir }}'\n" + " chezmoi execute-template '{{ .chezmoi.os }}' / '{{ .chezmoi.arch }}'\n" + " echo '{{ .chezmoi | toJson }}' | chezmoi execute-template\n" + " chezmoi execute-template --init --promptString email=me@home.org < ~/.\n" + "local/share/chezmoi/.chezmoi.toml.tmpl", longFlags: chezmoiset.New( "file", "init", "left-delimiter", "promptBool", "promptChoice", "promptInt", "promptMultichoice", "promptString", "right-delimiter", "stdinisatty", "with-stdin", ), shortFlags: chezmoiset.New( "f", "i", "p", ), }, "forget": { longHelp: "" + " Remove targets from the source state, i.e. stop managing them. targets must\n" + " have entries in the source state. They cannot be externals.", example: "" + " chezmoi forget ~/.bashrc", }, "generate": { longHelp: "" + " Generates output for use with chezmoi. The currently supported outputs are:\n" + "\n" + " Output | Description\n" + " -----------------------|--------------------------------------------------\n" + " git-commit-message | A git commit message, describing the changes to\n" + " | the source directory.\n" + " install.sh | An install script, suitable for use with GitHub\n" + " | Codespaces\n" + " install-init-shell.sh | A script which installs chezmoi, runs chezmoi\n" + " | init, and executes your shell", example: "" + " chezmoi generate install.sh > install.sh\n" + " chezmoi git -- commit -m \"$(chezmoi generate git-commit-message)\"\n" + " chezmoi generate install-init-shell.sh $GITHUB_USERNAME", }, "git": { longHelp: "" + " Run git args in the working tree (typically the source directory).", example: "" + " chezmoi git add .\n" + " chezmoi git add dot_gitconfig\n" + " chezmoi git -- commit -m \"Add .gitconfig\"", }, "help": { longHelp: "" + " Print the help associated with command, or general help if no command is\n" + " given.", }, "ignored": { longHelp: "" + " Print the list of entries ignored by chezmoi.", example: "" + " chezmoi ignored", longFlags: chezmoiset.New( "nul-path-separator", "tree", ), shortFlags: chezmoiset.New( "0", "t", ), }, "import": { longHelp: "" + " Import the source state from an archive file in to a directory in the source\n" + " state. This is primarily used to make subdirectories of your home directory\n" + " exactly match the contents of a downloaded archive. You will generally\n" + " always want to set the --destination, --exact, and --remove-destination\n" + " flags.\n" + "\n" + " The supported archive formats are rar, tar, tar.gz, tgz, tar.bz2, tbz2, txz,\n" + " tar.zst, and zip.", example: "" + " curl -s -L -o ${TMPDIR}/oh-my-zsh-master.tar.gz https://github.\n" + "com/ohmyzsh/ohmyzsh/archive/master.tar.gz\n" + " mkdir -p $(chezmoi source-path)/dot_oh-my-zsh\n" + " chezmoi import --strip-components 1 --destination ~/.oh-my-zsh ${TMPDIR}/oh-my-\n" + "zsh-master.tar.gz", longFlags: chezmoiset.New( "destination", "exact", "exclude", "include", "remove-destination", "strip-components", ), shortFlags: chezmoiset.New( "d", "i", "r", "x", ), }, "init": { longHelp: "" + " Setup the source directory, generate the config file, and optionally update\n" + " the destination directory to match the target state. This is done in the\n" + " following order:\n" + "\n" + " 1. The source directory is initialized. If chezmoi does not detect a Git\n" + " repository in the source directory, chezmoi will clone the provided repo\n" + " into the source directory. If no repo is provided, chezmoi will initialize\n" + " a new Git repository.\n" + " 2. If the initialized source directory contains a .chezmoi.$FORMAT.tmpl file,\n" + " a new configuration file will be created using that file as a template.\n" + " 3. If the --apply flag is provided, chezmoi apply is run.\n" + " 4. If the --purge flag is provided, chezmoi will remove the source, config,\n" + " and cache directories.\n" + " 5. If the --purge-binary is passed, chezmoi will attempt to remove its own\n" + " binary.\n" + "\n" + " By default, if repo is given, chezmoi will guess the full git repo URL,\n" + " using HTTPS by default, or SSH if the --ssh option is specified, according\n" + " to\n" + " the following patterns:\n" + "\n" + " Pattern | HTTPS Repo | SSH repo\n" + " ------------------|---------------------------|---------------------------\n" + " user | https://user@github.com/u | git@github.com:user/dotfi\n" + " | ser/dotfiles.git | les.git\n" + " user/repo | https://user@github.com/u | git@github.com:user/repo.\n" + " | ser/repo.git | git\n" + " site/user/repo | https://user@site/user/re | git@site:user/repo.git\n" + " | po.git |\n" + " sr.ht/~user | https://user@git.sr.ht/~u | git@git.sr.ht:~user/dotfi\n" + " | ser/dotfiles | les.git\n" + " sr.ht/~user/repo | https://user@git.sr.ht/~u | git@git.sr.ht:~user/repo.\n" + " | ser/repo | git\n" + "\n" + " To disable git repo URL guessing, pass the --guess-repo-url=false option.", example: "" + " chezmoi init user\n" + " chezmoi init user --apply\n" + " chezmoi init user --apply --purge\n" + " chezmoi init user/dots\n" + " chezmoi init codeberg.org/user\n" + " chezmoi init gitlab.com/user", longFlags: chezmoiset.New( "apply", "branch", "config-path", "data", "depth", "exclude", "git-lfs", "guess-repo-url", "include", "one-shot", "prompt", "promptBool", "promptChoice", "promptDefaults", "promptInt", "promptMultichoice", "promptString", "purge", "purge-binary", "recurse-submodules", "ssh", ), shortFlags: chezmoiset.New( "C", "P", "a", "d", "g", "i", "p", "x", ), }, "license": { longHelp: "" + " Print chezmoi's license.", example: "" + " chezmoi license", }, "list": { longHelp: "" + " list is an alias for managed.", }, "manage": { longHelp: "" + " manage is an alias for add for symmetry with unmanage.", }, "managed": { longHelp: "" + " List all managed entries in the destination directory under all paths in\n" + " alphabetical order. When no paths are supplied, list all managed entries in\n" + " the destination directory in alphabetical order.", example: "" + " chezmoi managed\n" + " chezmoi managed --include=files\n" + " chezmoi managed --include=files,symlinks\n" + " chezmoi managed -i dirs\n" + " chezmoi managed -i dirs,files\n" + " chezmoi managed -i files ~/.config\n" + " chezmoi managed --exclude=encrypted --path-style=source-relative", longFlags: chezmoiset.New( "exclude", "format", "include", "nul-path-separator", "path-style", "tree", ), shortFlags: chezmoiset.New( "0", "f", "i", "p", "t", "x", ), }, "merge": { longHelp: "" + " Perform a three-way merge between the destination state, the target state,\n" + " and the source state for each target. The merge tool is defined by the\n" + " merge.command configuration variable, and defaults to vimdiff. If multiple\n" + " targets are specified the merge tool is invoked separately and sequentially\n" + " for each target. If the target state cannot be computed (for example if\n" + " source is a template containing errors or an encrypted file that cannot be\n" + " decrypted) a two-way merge is performed instead.\n" + "\n" + " The order of arguments to merge.command is set by merge.args. Each argument\n" + " is interpreted as a template with the variables .Destination, .Source, and\n" + " .Target available corresponding to the path of the file in the destination\n" + " state, the source state, and the target state respectively. The default\n" + " value of merge.args is [\"{{ .Destination }}\", \"{{ .Source }}\", \"{{ .Target\n" + " }}\"]. If merge.args does not contain any template arguments then {{\n" + " .Destination }}, {{ .Source }}, and {{ .Target }} will be appended\n" + " automatically.", example: "" + " chezmoi merge ~/.bashrc", }, "merge-all": { longHelp: "" + " Perform a three-way merge for file whose actual state does not match its\n" + " target state. The merge is performed with chezmoi merge.", example: "" + " chezmoi merge-all", longFlags: chezmoiset.New( "init", "recursive", ), shortFlags: chezmoiset.New( "r", ), }, "podman": { longHelp: "" + " podman is an alias for docker.", }, "purge": { longHelp: "" + " Remove chezmoi's configuration, state, and source directory, but leave the\n" + " target state intact.", example: "" + " chezmoi purge\n" + " chezmoi purge --force", longFlags: chezmoiset.New( "binary", "force", ), shortFlags: chezmoiset.New( "P", ), }, "re-add": { longHelp: "" + " Re-add modified files in the target state, preserving any encrypted_\n" + " attributes. chezmoi will not overwrite templates, and all entries that are\n" + " not files are ignored. Directories are recursed into by default.\n" + "\n" + " If no targets are specified then all modified files are re-added. If one or\n" + " more targets are given then only those targets are re-added.", example: "" + " chezmoi re-add\n" + " chezmoi re-add ~/.bashrc\n" + " chezmoi re-add --recursive=false ~/.config/git", longFlags: chezmoiset.New( "exclude", "include", "re-encrypt", "recursive", ), shortFlags: chezmoiset.New( "i", "r", "x", ), }, "remove": { longHelp: "" + " The remove command has been removed. Use the forget command or the destroy\n" + " command instead.", }, "rm": { longHelp: "" + " The rm command has been removed. Use the forget command or the destroy\n" + " command instead.", }, "secret": { longHelp: "" + " Run a secret manager's CLI, passing any extra arguments to the secret\n" + " manager's CLI. This is primarily for verifying chezmoi's integration with a\n" + " custom secret manager. Normally you would use chezmoi's existing template\n" + " functions to retrieve secrets.", example: "" + " chezmoi secret keyring set --service=service --user=user --value=password\n" + " chezmoi secret keyring get --service=service --user=user\n" + " chezmoi secret keyring delete --service=service --user=user", }, "source-path": { longHelp: "" + " Print the path to each target's source state. If no targets are specified\n" + " then print the source directory.", example: "" + " chezmoi source-path\n" + " chezmoi source-path ~/.bashrc", }, "ssh": { longHelp: "" + " SSH to host, install chezmoi, run chezmoi init --apply *init-args*, and\n" + " executes your shell.", example: "" + " chezmoi ssh $HOSTNAME $GITHUB_USERNAME\n" + " chezmoi ssh $HOSTNAME -- --one-shot $GITHUB_USERNAME", longFlags: chezmoiset.New( "package-manager", "shell", ), shortFlags: chezmoiset.New( "p", "s", ), }, "state": { longHelp: "" + " Manipulate the persistent state.", example: "" + " chezmoi state data\n" + " chezmoi state delete --bucket=$BUCKET --key=$KEY\n" + " chezmoi state delete-bucket --bucket=$BUCKET\n" + " chezmoi state dump\n" + " chezmoi state get --bucket=$BUCKET --key=$KEY\n" + " chezmoi state get-bucket --bucket=$BUCKET\n" + " chezmoi state set --bucket=$BUCKET --key=$KEY --value=$VALUE\n" + " chezmoi state reset", }, "status": { longHelp: "" + " Print the status of the files and scripts managed by chezmoi in a format\n" + " similar to git status.\n" + "\n" + " The first column of output indicates the difference between the last state\n" + " written by chezmoi and the actual state. The second column indicates the\n" + " difference between the actual state and the target state, and what effect\n" + " running chezmoi apply will have.\n" + "\n" + " Character | Meaning | First column | Second column\n" + " --------------|-------------|--------------------|------------------------\n" + " Space | No change | No change | No change\n" + " A | Added | Entry was created | Entry will be created\n" + " D | Deleted | Entry was deleted | Entry will be deleted\n" + " M | Modified | Entry was modified | Entry will be modified\n" + " R | Run | Not applicable | Script will be run", example: "" + " chezmoi status", longFlags: chezmoiset.New( "exclude", "include", "init", "parent-dirs", "path-style", "recursive", ), shortFlags: chezmoiset.New( "P", "i", "p", "r", "x", ), }, "target-path": { longHelp: "" + " Print the target path of each source path. If no source paths are specified\n" + " then print the target directory.", example: "" + " chezmoi target-path\n" + " chezmoi target-path ~/.local/share/chezmoi/dot_zshrc", }, "unmanage": { longHelp: "" + " unmanage is an alias for forget for symmetry with manage.", }, "unmanaged": { longHelp: "" + " List all unmanaged files in paths. When no paths are supplied, list all\n" + " unmanaged files in the destination directory.\n" + "\n" + " It is an error to supply paths that are not found on the file system.", example: "" + " chezmoi unmanaged\n" + " chezmoi unmanaged ~/.config/chezmoi ~/.ssh", longFlags: chezmoiset.New( "exclude", "include", "nul-path-separator", "path-style", "tree", ), shortFlags: chezmoiset.New( "0", "i", "p", "t", "x", ), }, "update": { longHelp: "" + " Pull changes from the source repo and apply any changes.\n" + "\n" + " If update.command is set then chezmoi will run update.command with\n" + " update.args in the working tree. Otherwise, chezmoi will run git pull --\n" + " autostash --rebase [--recurse-submodules] , using chezmoi's builtin git if\n" + " useBuiltinGit is true or if git.command cannot be found in $PATH.", example: "" + " chezmoi update", longFlags: chezmoiset.New( "apply", "exclude", "include", "init", "parent-dirs", "recurse-submodules", "recursive", ), shortFlags: chezmoiset.New( "P", "a", "i", "r", "x", ), }, "upgrade": { longHelp: "" + " Upgrade chezmoi by downloading and installing the latest released version.\n" + " This will call the GitHub API to determine if there is a new version of\n" + " chezmoi available, and if so, download and attempt to install it in the same\n" + " way as chezmoi was previously installed.\n" + "\n" + " If the any of the $CHEZMOI_GITHUB_ACCESS_TOKEN, $CHEZMOI_GITHUB_TOKEN,\n" + " $GITHUB_ACCESS_TOKEN, or $GITHUB_TOKEN environment variables are set, then\n" + " the first value found will be used to authenticate requests to the GitHub\n" + " API, otherwise unauthenticated requests are used which are subject to\n" + " stricter rate limiting. Unauthenticated requests should be sufficient for\n" + " most cases.", longFlags: chezmoiset.New( "executable", "method", ), }, "verify": { longHelp: "" + " Verify that all targets match their target state. chezmoi exits with code 0\n" + " (success) if all targets match their target state, or 1 (failure) otherwise.\n" + " If no targets are specified then all targets are checked.", example: "" + " chezmoi verify\n" + " chezmoi verify ~/.bashrc", longFlags: chezmoiset.New( "exclude", "include", "init", "parent-dirs", "recursive", ), shortFlags: chezmoiset.New( "P", "i", "r", "x", ), }, } ================================================ FILE: internal/cmd/ignoredcmd.go ================================================ package cmd import ( "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type ignoredCmdConfig struct { nulPathSeparator bool tree bool } func (c *Config) newIgnoredCmd() *cobra.Command { ignoredCmd := &cobra.Command{ GroupID: groupIDAdvanced, Use: "ignored", Short: "Print ignored targets", Long: mustLongHelp("ignored"), Example: example("ignored"), Args: cobra.NoArgs, ValidArgsFunction: cobra.NoFileCompletions, RunE: c.makeRunEWithSourceState(c.runIgnoredCmd), Annotations: newAnnotations( persistentStateModeReadMockWrite, ), } ignoredCmd.Flags().BoolVarP(&c.ignored.tree, "tree", "t", c.ignored.tree, "Print paths as a tree") ignoredCmd.Flags(). BoolVarP(&c.ignored.nulPathSeparator, "nul-path-separator", "0", c.ignored.nulPathSeparator, "Use the NUL character as a path separator") return ignoredCmd } func (c *Config) runIgnoredCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { return c.writePaths(stringersToStrings(sourceState.Ignored()), writePathsOptions{ nulPathSeparator: c.ignored.nulPathSeparator, tree: c.ignored.tree, }) } ================================================ FILE: internal/cmd/importcmd.go ================================================ package cmd import ( "io" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type importCmdConfig struct { destination chezmoi.AbsPath exact bool filter *chezmoi.EntryTypeFilter removeDestination bool stripComponents int } func (c *Config) newImportCmd() *cobra.Command { importCmd := &cobra.Command{ GroupID: groupIDMigration, Use: "import archive", Short: "Import an archive into the source state", Long: mustLongHelp("import"), Example: example("import"), Args: cobra.MaximumNArgs(1), RunE: c.makeRunEWithSourceState(c.runImportCmd), Annotations: newAnnotations( createSourceDirectoryIfNeeded, modifiesSourceDirectory, persistentStateModeReadWrite, ), } importCmd.Flags().VarP(&c._import.destination, "destination", "d", "Set destination prefix") importCmd.Flags().BoolVar(&c._import.exact, "exact", c._import.exact, "Set exact_ attribute on imported directories") importCmd.Flags().VarP(c._import.filter.Exclude, "exclude", "x", "Exclude entry types") importCmd.Flags().VarP(c._import.filter.Include, "include", "i", "Include entry types") importCmd.Flags(). BoolVarP(&c._import.removeDestination, "remove-destination", "r", c._import.removeDestination, "Remove destination before import") importCmd.Flags(). IntVar(&c._import.stripComponents, "strip-components", c._import.stripComponents, "Strip leading path components") return importCmd } func (c *Config) runImportCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { var ( name string data []byte ) if len(args) == 0 { name = ".tar" var err error data, err = io.ReadAll(c.stdin) if err != nil { return err } } else { absPath, err := chezmoi.NewAbsPathFromExtPath(args[0], c.homeDirAbsPath) if err != nil { return err } name = absPath.String() data, err = c.baseSystem.ReadFile(absPath) if err != nil { return err } } archiveReaderSystem, err := chezmoi.NewArchiveReaderSystem( name, data, chezmoi.ArchiveFormatUnknown, chezmoi.ArchiveReaderSystemOptions{ RootAbsPath: c._import.destination, StripComponents: c._import.stripComponents, }, ) if err != nil { return err } var removeDir chezmoi.RelPath if c._import.removeDestination { removeDir, err = c._import.destination.TrimDirPrefix(c.DestDirAbsPath) if err != nil { return err } } return sourceState.Add( c.sourceSystem, c.persistentState, archiveReaderSystem, archiveReaderSystem.FileInfos(), &chezmoi.AddOptions{ Errorf: c.errorf, Exact: c._import.exact, Filter: c._import.filter, RemoveDir: removeDir, }, ) } ================================================ FILE: internal/cmd/importcmd_test.go ================================================ package cmd import ( "bytes" "io/fs" "strings" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" "chezmoi.io/chezmoi/internal/archivetest" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestImportCmd(t *testing.T) { data, err := archivetest.NewTar(map[string]any{ "archive": map[string]any{ ".dir": map[string]any{ ".file": "# contents of archive/.dir/.file\n", ".symlink": &archivetest.Symlink{ Target: ".file", }, }, }, }) assert.NoError(t, err) for _, tc := range []struct { args []string extraRoot any tests []any }{ { args: []string{ "--strip-components=1", }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/dot_file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of archive/.dir/.file\n"), ), vfst.TestPath("/home/user/.local/share/chezmoi/dot_dir/symlink_dot_symlink", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString(".file\n"), ), }, }, { args: []string{ "--destination=~/dir", "--strip-components=1", }, extraRoot: map[string]any{ "/home/user/.local/share/chezmoi/dir": &vfst.Dir{Perm: fs.ModePerm}, }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dir/dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dir/dot_dir/dot_file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of archive/.dir/.file\n"), ), vfst.TestPath("/home/user/.local/share/chezmoi/dir/dot_dir/symlink_dot_symlink", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString(".file\n"), ), }, }, { args: []string{ "--destination=~/dir", "--remove-destination", "--strip-components=1", }, extraRoot: map[string]any{ "/home/user/.local/share/chezmoi/dir/file": "# contents of dir/file\n", }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dir/dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dir/file", vfst.TestDoesNotExist(), ), vfst.TestPath("/home/user/.local/share/chezmoi/dir/dot_dir/dot_file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of archive/.dir/.file\n"), ), vfst.TestPath("/home/user/.local/share/chezmoi/dir/dot_dir/symlink_dot_symlink", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString(".file\n"), ), }, }, { args: []string{ "--destination=~/dir", "--exact", "--strip-components=1", }, extraRoot: map[string]any{ "/home/user/.local/share/chezmoi/dir": &vfst.Dir{Perm: fs.ModePerm}, }, tests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dir/exact_dot_dir", vfst.TestIsDir(), vfst.TestModePerm(fs.ModePerm&^chezmoitest.Umask), ), vfst.TestPath("/home/user/.local/share/chezmoi/dir/exact_dot_dir/dot_file", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of archive/.dir/.file\n"), ), vfst.TestPath("/home/user/.local/share/chezmoi/dir/exact_dot_dir/symlink_dot_symlink", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString(".file\n"), ), }, }, } { t.Run(strings.Join(tc.args, "_"), func(t *testing.T) { chezmoitest.WithTestFS(t, map[string]any{ "/home/user": &vfst.Dir{Perm: fs.ModePerm}, }, func(fileSystem vfs.FS) { if tc.extraRoot != nil { assert.NoError(t, vfst.NewBuilder().Build(fileSystem, tc.extraRoot)) } config := newTestConfig(t, fileSystem, withStdin(bytes.NewReader(data))) assert.NoError(t, config.execute(append([]string{"import"}, tc.args...))) vfst.RunTests(t, fileSystem, "", tc.tests...) }) }) } } ================================================ FILE: internal/cmd/initcmd.go ================================================ package cmd import ( "errors" "fmt" "io/fs" "log/slog" "regexp" "strconv" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/plumbing/transport/http" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoilog" ) type initCmdConfig struct { apply bool branch string configPath chezmoi.AbsPath data bool depth int filter *chezmoi.EntryTypeFilter guessRepoURL bool oneShot bool purge bool purgeBinary bool recurseSubmodules bool ssh bool } var repoGuesses = []struct { rx *regexp.Regexp httpRepoGuessRepl string sshRepoGuessRepl string }{ { rx: regexp.MustCompile(`\A([-0-9A-Za-z]+)\z`), httpRepoGuessRepl: "https://github.com/$1/dotfiles.git", sshRepoGuessRepl: "git@github.com:$1/dotfiles.git", }, { rx: regexp.MustCompile(`\A([-0-9A-Za-z]+)/([-.0-9A-Z_a-z]+?)(\.git)?\z`), httpRepoGuessRepl: "https://github.com/$1/$2.git", sshRepoGuessRepl: "git@github.com:$1/$2.git", }, { rx: regexp.MustCompile(`\A([-.0-9A-Za-z]+)/([-0-9A-Za-z]+)\z`), httpRepoGuessRepl: "https://$1/$2/dotfiles.git", sshRepoGuessRepl: "git@$1:$2/dotfiles.git", }, { rx: regexp.MustCompile(`\A([-0-9A-Za-z]+)/([-0-9A-Za-z]+)/([-.0-9A-Za-z]+)\z`), httpRepoGuessRepl: "https://$1/$2/$3.git", sshRepoGuessRepl: "git@$1:$2/$3.git", }, { rx: regexp.MustCompile(`\A([-.0-9A-Za-z]+)/([-0-9A-Za-z]+)/([-0-9A-Za-z]+)(\.git)?\z`), httpRepoGuessRepl: "https://$1/$2/$3.git", sshRepoGuessRepl: "git@$1:$2/$3.git", }, { rx: regexp.MustCompile(`\A(https?://)([-.0-9A-Za-z]+)/([-0-9A-Za-z]+)/([-0-9A-Za-z]+)(\.git)?\z`), httpRepoGuessRepl: "$1$2/$3/$4.git", sshRepoGuessRepl: "git@$2:$3/$4.git", }, { rx: regexp.MustCompile(`\Asr\.ht/~([a-z_][a-z0-9_-]+)\z`), httpRepoGuessRepl: "https://git.sr.ht/~$1/dotfiles", sshRepoGuessRepl: "git@git.sr.ht:~$1/dotfiles", }, { rx: regexp.MustCompile(`\Asr\.ht/~([a-z_][a-z0-9_-]+)/([-0-9A-Za-z]+)\z`), httpRepoGuessRepl: "https://git.sr.ht/~$1/$2", sshRepoGuessRepl: "git@git.sr.ht:~$1/$2", }, } // A gitCloneOptionsLogValuer is a git.CloneOptions that implements // [log/slog.LogValuer]. type gitCloneOptionsLogValuer git.CloneOptions func (c *Config) newInitCmd() *cobra.Command { initCmd := &cobra.Command{ GroupID: groupIDDaily, Args: cobra.MaximumNArgs(1), Use: "init [repo]", Short: "Setup the source directory and update the destination directory to match the target state", Long: mustLongHelp("init"), Example: example("init"), RunE: c.runInitCmd, Annotations: newAnnotations( createSourceDirectoryIfNeeded, modifiesDestinationDirectory, persistentStateModeReadWrite, requiresWorkingTree, runsCommands, ), } c.addInteractiveTemplateFuncFlags(initCmd.Flags()) initCmd.Flags().BoolVarP(&c.init.apply, "apply", "a", c.init.apply, "Update destination directory") initCmd.Flags().StringVar(&c.init.branch, "branch", c.init.branch, "Set initial branch to checkout") initCmd.Flags().VarP(&c.init.configPath, "config-path", "C", "Path to write generated config file") initCmd.Flags().BoolVar(&c.init.data, "data", c.init.data, "Include existing template data") initCmd.Flags().IntVarP(&c.init.depth, "depth", "d", c.init.depth, "Create a shallow clone") initCmd.Flags().VarP(c.init.filter.Exclude, "exclude", "x", "Exclude entry types") initCmd.Flags().BoolVarP(&c.init.guessRepoURL, "guess-repo-url", "g", c.init.guessRepoURL, "Guess the repo URL") initCmd.Flags().VarP(c.init.filter.Include, "include", "i", "Include entry types") initCmd.Flags().BoolVar(&c.Git.LFS, "git-lfs", c.Git.LFS, "Run git pull lfs after cloning") initCmd.Flags().BoolVar(&c.init.oneShot, "one-shot", c.init.oneShot, "Run in one-shot mode") initCmd.Flags().BoolVarP(&c.init.purge, "purge", "p", c.init.purge, "Purge config and source directories after running") initCmd.Flags().BoolVarP(&c.init.purgeBinary, "purge-binary", "P", c.init.purgeBinary, "Purge chezmoi binary after running") initCmd.Flags(). BoolVar(&c.init.recurseSubmodules, "recurse-submodules", c.init.recurseSubmodules, "Checkout submodules recursively") initCmd.Flags().BoolVar(&c.init.ssh, "ssh", c.init.ssh, "Use ssh instead of https when guessing repo URL") return initCmd } func (c *Config) runInitCmd(cmd *cobra.Command, args []string) error { if c.init.oneShot { c.force = true c.init.apply = true c.init.depth = 1 c.init.purge = true c.init.purgeBinary = true } useBuiltinGit := c.UseBuiltinGit.Value(c.useBuiltinGitAutoFunc) // If we're not in a working tree then init it or clone it. gitDirAbsPath := c.WorkingTreeAbsPath.JoinString(git.GitDirName) switch _, err := c.baseSystem.Stat(gitDirAbsPath); { case errors.Is(err, fs.ErrNotExist): workingTreeRawPath, err := c.baseSystem.RawPath(c.WorkingTreeAbsPath) if err != nil { return err } if len(args) == 0 { if useBuiltinGit { if err := c.builtinGitInit(workingTreeRawPath); err != nil { return err } } else if err := c.run(c.WorkingTreeAbsPath, c.Git.Command, []string{"init", "--quiet"}); err != nil { return err } } else { var repoURLStr string if c.init.guessRepoURL { repoURLStr = guessRepoURL(args[0], c.init.ssh) } else { repoURLStr = args[0] } if useBuiltinGit { if err := c.builtinGitClone(repoURLStr, workingTreeRawPath); err != nil { return err } } else { args := []string{ "clone", } if c.init.recurseSubmodules { args = append(args, "--recurse-submodules", ) } if c.init.branch != "" { args = append(args, "--branch", c.init.branch, ) } if c.init.depth != 0 { args = append(args, "--depth", strconv.Itoa(c.init.depth), ) } args = append(args, repoURLStr, workingTreeRawPath.String()) if err := c.run(chezmoi.EmptyAbsPath, c.Git.Command, args); err != nil { return err } } } case err != nil: return err } if err := c.checkVersion(); err != nil { return err } if err := c.createAndReloadConfigFile(cmd); err != nil { return err } if c.Git.LFS && !useBuiltinGit { args := []string{"lfs", "pull"} if err := c.run(chezmoi.EmptyAbsPath, c.Git.Command, args); err != nil { return err } } // Apply. Explicitly run any apply pre or post hooks because the normal hook // mechanism will only run init's hooks. if c.init.apply { if err := c.runHookPre("apply"); err != nil { return err } if err := c.applyArgs(cmd.Context(), c.destSystem, c.DestDirAbsPath, noArgs, applyArgsOptions{ cmd: cmd, filter: c.init.filter, recursive: false, umask: c.Umask, preApplyFunc: c.defaultPreApplyFunc, }); err != nil { return err } if err := c.runHookPost("apply"); err != nil { return err } } // Purge. if c.init.purge || c.init.purgeBinary { if err := c.doPurge(&doPurgeOptions{ binary: c.init.purgeBinary, cache: c.init.purge, config: c.init.purge, persistentState: c.init.purge, sourceDir: c.init.purge, workingTree: c.init.purge, }); err != nil { return err } } return nil } // builtinGitClone clones a repo using the builtin git command. func (c *Config) builtinGitClone(repoURLStr string, workingTreeRawPath chezmoi.AbsPath) error { endpoint, err := transport.NewEndpoint(repoURLStr) if err != nil { return err } if c.init.ssh || endpoint.Protocol == "ssh" { return errors.New("builtin git does not support cloning repos over ssh, please install git") } isBare := false var referenceName plumbing.ReferenceName if c.init.branch != "" { referenceName = plumbing.NewBranchReferenceName(c.init.branch) } cloneOptions := git.CloneOptions{ URL: repoURLStr, Depth: c.init.depth, ReferenceName: referenceName, RecurseSubmodules: git.DefaultSubmoduleRecursionDepth, ShallowSubmodules: c.init.depth == 1, } for { _, err := git.PlainClone(workingTreeRawPath.String(), isBare, &cloneOptions) chezmoilog.InfoOrError(c.logger, "PlainClone", err, chezmoilog.Stringer("path", workingTreeRawPath), slog.Bool("isBare", isBare), slog.Any("options", gitCloneOptionsLogValuer(cloneOptions)), ) if !errors.Is(err, transport.ErrAuthenticationRequired) { return err } if _, err := fmt.Fprintf(c.stdout, "chezmoi: %s: %v\n", repoURLStr, err); err != nil { return err } var basicAuth http.BasicAuth if basicAuth.Username, err = c.readString("Username? ", nil); err != nil { return err } if basicAuth.Password, err = c.readPassword("Password? ", "password"); err != nil { return err } cloneOptions.Auth = &basicAuth } } // builtinGitInit initializes a repo using the builtin git command. func (c *Config) builtinGitInit(workingTreeRawPath chezmoi.AbsPath) error { isBare := false _, err := git.PlainInit(workingTreeRawPath.String(), isBare) chezmoilog.InfoOrError(c.logger, "PlainInit", err, chezmoilog.Stringer("path", workingTreeRawPath), slog.Bool("isBare", isBare), ) return err } // LogValue implements log/slog.LogValuer.LogValue. func (o gitCloneOptionsLogValuer) LogValue() slog.Value { var attrs []slog.Attr if o.URL != "" { attrs = append(attrs, slog.String("URL", o.URL)) } if o.Auth != nil { attrs = append(attrs, chezmoilog.Stringer("Auth", o.Auth)) } if o.RemoteName != "" { attrs = append(attrs, slog.String("RemoteName", o.RemoteName)) } if o.ReferenceName != "" { attrs = append(attrs, slog.String("ReferenceName", string(o.ReferenceName))) } if o.SingleBranch { attrs = append(attrs, slog.Bool("SingleBranch", o.SingleBranch)) } if o.NoCheckout { attrs = append(attrs, slog.Bool("NoCheckout", o.NoCheckout)) } if o.Depth != 0 { attrs = append(attrs, slog.Int("Depth", o.Depth)) } if o.RecurseSubmodules != 0 { attrs = append(attrs, slog.Uint64("RecurseSubmodules", uint64(o.RecurseSubmodules))) } if o.Tags != 0 { attrs = append(attrs, slog.Int("Tags", int(o.Tags))) } if o.InsecureSkipTLS { attrs = append(attrs, slog.Bool("InsecureSkipTLS", o.InsecureSkipTLS)) } if o.CABundle != nil { attrs = append(attrs, slog.Any("CABundle", o.CABundle)) } return slog.GroupValue(attrs...) } // guessRepoURL guesses the user's username and repo from arg. func guessRepoURL(arg string, ssh bool) string { for _, repoGuess := range repoGuesses { switch { case !repoGuess.rx.MatchString(arg): continue case ssh && repoGuess.sshRepoGuessRepl != "": return repoGuess.rx.ReplaceAllString(arg, repoGuess.sshRepoGuessRepl) case !ssh && repoGuess.httpRepoGuessRepl != "": return repoGuess.rx.ReplaceAllString(arg, repoGuess.httpRepoGuessRepl) } } return arg } ================================================ FILE: internal/cmd/initcmd_test.go ================================================ package cmd import ( "errors" "runtime" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestGuessRepoURL(t *testing.T) { for _, tc := range []struct { arg string expectedHTTPRepoURL string expectedSSHRepoURL string }{ { arg: "git@github.com:user/dotfiles.git", expectedHTTPRepoURL: "git@github.com:user/dotfiles.git", expectedSSHRepoURL: "git@github.com:user/dotfiles.git", }, { arg: "codeberg.org/user", expectedHTTPRepoURL: "https://codeberg.org/user/dotfiles.git", expectedSSHRepoURL: "git@codeberg.org:user/dotfiles.git", }, { arg: "codeberg.org/user/dots", expectedHTTPRepoURL: "https://codeberg.org/user/dots.git", expectedSSHRepoURL: "git@codeberg.org:user/dots.git", }, { arg: "gitlab.com/user", expectedHTTPRepoURL: "https://gitlab.com/user/dotfiles.git", expectedSSHRepoURL: "git@gitlab.com:user/dotfiles.git", }, { arg: "gitlab.com/user/dots", expectedHTTPRepoURL: "https://gitlab.com/user/dots.git", expectedSSHRepoURL: "git@gitlab.com:user/dots.git", }, { arg: "gitlab.com/user/dots.git", expectedHTTPRepoURL: "https://gitlab.com/user/dots.git", expectedSSHRepoURL: "git@gitlab.com:user/dots.git", }, { arg: "http://gitlab.com/user/dots.git", expectedHTTPRepoURL: "http://gitlab.com/user/dots.git", expectedSSHRepoURL: "git@gitlab.com:user/dots.git", }, { arg: "https://gitlab.com/user/dots.git", expectedHTTPRepoURL: "https://gitlab.com/user/dots.git", expectedSSHRepoURL: "git@gitlab.com:user/dots.git", }, { arg: "sr.ht/~user_name", expectedHTTPRepoURL: "https://git.sr.ht/~user_name/dotfiles", expectedSSHRepoURL: "git@git.sr.ht:~user_name/dotfiles", }, { arg: "sr.ht/~user_name/dots", expectedHTTPRepoURL: "https://git.sr.ht/~user_name/dots", expectedSSHRepoURL: "git@git.sr.ht:~user_name/dots", }, { arg: "user", expectedHTTPRepoURL: "https://github.com/user/dotfiles.git", expectedSSHRepoURL: "git@github.com:user/dotfiles.git", }, { arg: "user/chezmoi_dotfiles", expectedHTTPRepoURL: "https://github.com/user/chezmoi_dotfiles.git", expectedSSHRepoURL: "git@github.com:user/chezmoi_dotfiles.git", }, { arg: "user/.dotfiles", expectedHTTPRepoURL: "https://github.com/user/.dotfiles.git", expectedSSHRepoURL: "git@github.com:user/.dotfiles.git", }, { arg: "user/dots", expectedHTTPRepoURL: "https://github.com/user/dots.git", expectedSSHRepoURL: "git@github.com:user/dots.git", }, { arg: "user/dots.git", expectedHTTPRepoURL: "https://github.com/user/dots.git", expectedSSHRepoURL: "git@github.com:user/dots.git", }, } { t.Run(tc.arg, func(t *testing.T) { ssh := false actualHTTPRepoURL := guessRepoURL(tc.arg, ssh) assert.Equal(t, tc.expectedHTTPRepoURL, actualHTTPRepoURL, "HTTPRepoURL") ssh = true actualSSHRepoURL := guessRepoURL(tc.arg, ssh) assert.Equal(t, tc.expectedSSHRepoURL, actualSSHRepoURL, "SSHRepoURL") }) } } func TestIssue2137(t *testing.T) { chezmoitest.WithTestFS(t, map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiversion": "3.0.0", ".git": map[string]any{ ".keep": nil, }, }, }, func(fileSystem vfs.FS) { err := newTestConfig(t, fileSystem).execute([]string{"init"}) tooOldError := &chezmoi.TooOldError{} assert.True(t, errors.As(err, &tooOldError)) }) } func TestIssue2283(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("skipping UNIX test on Windows") } chezmoitest.WithTestFS(t, map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ ".chezmoiroot": "home", "home": map[string]any{ ".chezmoi.yaml.tmpl": "sourceDir: {{ .chezmoi.sourceDir }}\n", }, }, }, func(fileSystem vfs.FS) { assert.NoError(t, newTestConfig(t, fileSystem).execute([]string{"init"})) data, err := fileSystem.ReadFile("/home/user/.config/chezmoi/chezmoi.yaml") assert.NoError(t, err) assert.Equal(t, "sourceDir: /home/user/.local/share/chezmoi/home\n", string(data)) }) } ================================================ FILE: internal/cmd/inittemplatefuncs.go ================================================ package cmd import ( "os" "golang.org/x/term" "chezmoi.io/chezmoi/internal/chezmoi" ) func (c *Config) exitInitTemplateFunc(code int) string { panic(chezmoi.ExitCodeError(code)) } func (c *Config) stdinIsATTYInitTemplateFunc() bool { if c.noTTY { return false } file, ok := c.stdin.(*os.File) if !ok { return false } return term.IsTerminal(int(file.Fd())) } func (c *Config) writeToStdout(args ...string) string { for _, arg := range args { _ = mustValue(c.stdout.Write([]byte(arg))) } return "" } ================================================ FILE: internal/cmd/inittemplatefuncs_test.go ================================================ package cmd import ( "strings" "testing" "github.com/alecthomas/assert/v2" ) func TestPromptBoolInteractiveTemplateFunc(t *testing.T) { for _, tc := range []struct { name string prompt string args []bool stdinStr string expectedStdoutStr string expected bool expectedErr bool }{ { name: "response_without_default", prompt: "bool", stdinStr: "false\n", expectedStdoutStr: "bool? ", expected: false, }, { name: "response_with_default", prompt: "bool", args: []bool{true}, stdinStr: "no\n", expectedStdoutStr: "bool (default true)? ", expected: false, }, { name: "no_response_with_default", prompt: "bool", args: []bool{true}, stdinStr: "\n", expectedStdoutStr: "bool (default true)? ", expected: true, }, { name: "invalid_response", stdinStr: "invalid\n", expectedErr: true, }, { name: "invalid_response_with_default", args: []bool{false}, stdinStr: "invalid\n", expectedErr: true, }, { name: "too_many_args", prompt: "bool", args: []bool{false, false}, stdinStr: "\n", expectedErr: true, }, } { t.Run(tc.name, func(t *testing.T) { stdin := strings.NewReader(tc.stdinStr) stdout := &strings.Builder{} config, err := newConfig( withNoTTY(true), withStdin(stdin), withStdout(stdout), ) assert.NoError(t, err) if tc.expectedErr { assert.Panics(t, func() { config.promptBoolInteractiveTemplateFunc(tc.prompt, tc.args...) }) } else { assert.Equal(t, tc.expected, config.promptBoolInteractiveTemplateFunc(tc.prompt, tc.args...)) assert.Equal(t, tc.expectedStdoutStr, stdout.String()) } }) } } func TestPromptChoiceInteractiveTemplateFunc(t *testing.T) { for _, tc := range []struct { name string prompt string choices []any args []string stdinStr string expectedStdoutStr string expected string expectedErr bool }{ { name: "response_without_default", prompt: "choice", choices: []any{"one", "two", "three"}, stdinStr: "one\n", expectedStdoutStr: "choice (one/two/three)? ", expected: "one", }, { name: "response_with_default", prompt: "choice", choices: []any{"one", "two", "three"}, args: []string{"one"}, stdinStr: "two\n", expectedStdoutStr: "choice (one/two/three, default one)? ", expected: "two", }, { name: "no_response_with_default", prompt: "choice", choices: []any{"one", "two", "three"}, args: []string{"three"}, stdinStr: "\n", expectedStdoutStr: "choice (one/two/three, default three)? ", expected: "three", }, { name: "invalid_response", prompt: "choice", choices: []any{"one", "two", "three"}, stdinStr: "invalid\n", expectedErr: true, }, { name: "invalid_response_with_default", prompt: "choice", choices: []any{"one", "two", "three"}, args: []string{"one"}, stdinStr: "invalid\n", expectedErr: true, }, { name: "too_many_args", prompt: "choice", choices: []any{"one", "two", "three"}, args: []string{"two", "three"}, stdinStr: "\n", expectedErr: true, }, { name: "invalid_default", prompt: "choice", choices: []any{"one", "two", "three"}, args: []string{"four"}, stdinStr: "\n", expectedErr: true, }, } { t.Run(tc.name, func(t *testing.T) { stdin := strings.NewReader(tc.stdinStr) stdout := &strings.Builder{} config, err := newConfig( withNoTTY(true), withStdin(stdin), withStdout(stdout), ) assert.NoError(t, err) if tc.expectedErr { assert.Panics(t, func() { config.promptChoiceInteractiveTemplateFunc(tc.prompt, tc.choices, tc.args...) }) } else { assert.Equal(t, tc.expected, config.promptChoiceInteractiveTemplateFunc(tc.prompt, tc.choices, tc.args...)) assert.Equal(t, tc.expectedStdoutStr, stdout.String()) } }) } } func TestPromptIntInteractiveTemplateFunc(t *testing.T) { for _, tc := range []struct { name string prompt string args []int64 stdinStr string expectedStdoutStr string expected int64 expectedErr bool }{ { name: "response_without_default", prompt: "int", stdinStr: "1\n", expectedStdoutStr: "int? ", expected: 1, }, { name: "response_with_default", prompt: "int", args: []int64{1}, stdinStr: "2\n", expectedStdoutStr: "int (default 1)? ", expected: 2, }, { name: "no_response_with_default", prompt: "int", args: []int64{1}, stdinStr: "\n", expectedStdoutStr: "int (default 1)? ", expected: 1, }, { name: "invalid_response", stdinStr: "invalid\n", expectedErr: true, }, { name: "invalid_response_with_default", args: []int64{1}, stdinStr: "invalid\n", expectedErr: true, }, { name: "too_many_args", prompt: "bool", args: []int64{0, 0}, stdinStr: "\n", expectedErr: true, }, } { t.Run(tc.name, func(t *testing.T) { stdin := strings.NewReader(tc.stdinStr) stdout := &strings.Builder{} config, err := newConfig( withNoTTY(true), withStdin(stdin), withStdout(stdout), ) assert.NoError(t, err) if tc.expectedErr { assert.Panics(t, func() { config.promptIntInteractiveTemplateFunc(tc.prompt, tc.args...) }) } else { assert.Equal(t, tc.expected, config.promptIntInteractiveTemplateFunc(tc.prompt, tc.args...)) assert.Equal(t, tc.expectedStdoutStr, stdout.String()) } }) } } func TestPromptMultichoiceInteractiveTemplateFunc(t *testing.T) { for _, tc := range []struct { name string prompt string multichoices []any args []any stdinStr string expectedStdoutStr string expected []string expectedErr bool }{ { name: "response_without_default", prompt: "multichoice", multichoices: []any{"one", "two", "three"}, stdinStr: "one\n\n", expectedStdoutStr: "multichoice?\nChoices (one/two/three)\nChoice (ENTER to stop)> Choice (ENTER to stop)> ", expected: []string{"one"}, }, { name: "response_with_default", prompt: "multichoice", multichoices: []any{"one", "two", "three"}, args: []any{[]string{"one"}}, stdinStr: "two\n\n", expectedStdoutStr: "multichoice?\nChoices (one/two/three)\nDefault [one]\nChoice (ENTER to stop)> Choice (ENTER to stop)> ", expected: []string{"two"}, }, { name: "no_response_with_default", prompt: "multichoice", multichoices: []any{"one", "two", "three"}, args: []any{[]string{"three", "two"}}, stdinStr: "\n", expectedStdoutStr: "multichoice?\nChoices (one/two/three)\nDefault [three, two]\nChoice (ENTER to stop)> ", expected: []string{"three", "two"}, }, { name: "empty_response_without_default", prompt: "multichoice", multichoices: []any{"one", "two", "three"}, stdinStr: "[]\n", expectedStdoutStr: "multichoice?\nChoices (one/two/three)\nChoice (ENTER to stop)> ", expected: []string{}, }, { name: "empty_response_without_default", prompt: "multichoice", multichoices: []any{"one", "two", "three"}, args: []any{[]string{"three", "two"}}, stdinStr: "[]\n", expectedStdoutStr: "multichoice?\nChoices (one/two/three)\nDefault [three, two]\nChoice (ENTER to stop)> ", expected: []string{}, }, { name: "invalid_response", prompt: "multichoice", multichoices: []any{"one", "two", "three"}, stdinStr: "invalid\n", expectedErr: true, }, { name: "invalid_response_with_default", prompt: "multichoice", multichoices: []any{"one", "two", "three"}, args: []any{[]string{"one"}}, stdinStr: "invalid\n", expectedErr: true, }, { name: "invalid_default", prompt: "multichoice", multichoices: []any{"one", "two", "three"}, args: []any{[]string{"four"}}, stdinStr: "\n", expectedErr: true, }, } { t.Run(tc.name, func(t *testing.T) { stdin := strings.NewReader(tc.stdinStr) stdout := &strings.Builder{} config, err := newConfig( withNoTTY(true), withStdin(stdin), withStdout(stdout), ) assert.NoError(t, err) if tc.expectedErr { assert.Panics(t, func() { config.promptMultichoiceInteractiveTemplateFunc(tc.prompt, tc.multichoices, tc.args...) }) } else { assert.Equal( t, tc.expected, config.promptMultichoiceInteractiveTemplateFunc(tc.prompt, tc.multichoices, tc.args...), ) assert.Equal(t, tc.expectedStdoutStr, stdout.String()) } }) } } func TestPromptStringInteractiveTemplateFunc(t *testing.T) { for _, tc := range []struct { name string prompt string args []string stdinStr string expectedStdoutStr string expected string expectedErr bool }{ { name: "response_without_default", prompt: "string", stdinStr: "one\n", expectedStdoutStr: "string? ", expected: "one", }, { name: "response_with_default", prompt: "string", args: []string{"one"}, stdinStr: "two\n", expectedStdoutStr: `string (default "one")? `, expected: "two", }, { name: "response_with_space_with_default", prompt: "string", args: []string{"one"}, stdinStr: " two \n", expectedStdoutStr: `string (default "one")? `, expected: "two", }, { name: "no_response_with_default_with_space", prompt: "string", args: []string{" one "}, stdinStr: "\n", expectedStdoutStr: `string (default "one")? `, expected: "one", }, { name: "no_response_with_default", prompt: "string", args: []string{"one"}, stdinStr: "\n", expectedStdoutStr: `string (default "one")? `, expected: "one", }, { name: "whitespace_response_with_default", prompt: "string", args: []string{"one"}, stdinStr: " \r\n", expectedStdoutStr: `string (default "one")? `, expected: "one", }, { name: "too_many_args", prompt: "bool", args: []string{"", ""}, stdinStr: "\n", expectedErr: true, }, } { t.Run(tc.name, func(t *testing.T) { stdin := strings.NewReader(tc.stdinStr) stdout := &strings.Builder{} config, err := newConfig( withNoTTY(true), withStdin(stdin), withStdout(stdout), ) assert.NoError(t, err) if tc.expectedErr { assert.Panics(t, func() { config.promptStringInteractiveTemplateFunc(tc.prompt, tc.args...) }) } else { assert.Equal(t, tc.expected, config.promptStringInteractiveTemplateFunc(tc.prompt, tc.args...)) assert.Equal(t, tc.expectedStdoutStr, stdout.String()) } }) } } ================================================ FILE: internal/cmd/interactivetemplatefuncs.go ================================================ package cmd import ( "fmt" "strings" "github.com/spf13/pflag" "chezmoi.io/chezmoi/internal/chezmoi" ) type interactiveTemplateFuncsConfig struct { forcePromptOnce bool promptBool map[string]string promptChoice map[string]string promptDefaults bool promptInt map[string]int promptMultichoice map[string]string promptString map[string]string } func (c *Config) addInteractiveTemplateFuncFlags(flags *pflag.FlagSet) { flags.BoolVar( &c.interactiveTemplateFuncs.forcePromptOnce, "prompt", c.interactiveTemplateFuncs.forcePromptOnce, "Force prompt*Once template functions to prompt", ) flags.BoolVar( &c.interactiveTemplateFuncs.promptDefaults, "promptDefaults", c.interactiveTemplateFuncs.promptDefaults, "Make prompt functions return default values", ) flags.StringToStringVar( &c.interactiveTemplateFuncs.promptBool, "promptBool", c.interactiveTemplateFuncs.promptBool, "Populate promptBool", ) flags.StringToStringVar( &c.interactiveTemplateFuncs.promptChoice, "promptChoice", c.interactiveTemplateFuncs.promptChoice, "Populate promptChoice", ) flags.StringToStringVar( &c.interactiveTemplateFuncs.promptMultichoice, "promptMultichoice", c.interactiveTemplateFuncs.promptMultichoice, "Populate promptMultichoice", ) flags.StringToIntVar( &c.interactiveTemplateFuncs.promptInt, "promptInt", c.interactiveTemplateFuncs.promptInt, "Populate promptInt", ) flags.StringToStringVar( &c.interactiveTemplateFuncs.promptString, "promptString", c.interactiveTemplateFuncs.promptString, "Populate promptString", ) } func (c *Config) promptBoolInteractiveTemplateFunc(prompt string, args ...bool) bool { if len(args) > 1 { panic(fmt.Errorf("want 1 or 2 arguments, got %d", len(args)+1)) } if valueStr, ok := c.interactiveTemplateFuncs.promptBool[prompt]; ok { return mustValue(chezmoi.ParseBool(valueStr)) } return mustValue(c.promptBool(prompt, args...)) } func (c *Config) promptBoolOnceInteractiveTemplateFunc(m map[string]any, path any, prompt string, args ...bool) bool { if len(args) > 1 { panic(fmt.Errorf("want 3 or 4 arguments, got %d", len(args)+2)) } nestedMap, lastKey := mustValues(nestedMapAtPath(m, path)) if !c.interactiveTemplateFuncs.forcePromptOnce { if value, ok := nestedMap[lastKey]; ok { switch value := value.(type) { case bool: return value case string: if boolValue, err := chezmoi.ParseBool(value); err == nil { return boolValue } } } } return c.promptBoolInteractiveTemplateFunc(prompt, args...) } func (c *Config) promptChoiceInteractiveTemplateFunc(prompt string, choices any, args ...string) string { if len(args) > 1 { panic(fmt.Errorf("want 2 or 3 arguments, got %d", len(args)+2)) } if valueStr, ok := c.interactiveTemplateFuncs.promptChoice[prompt]; ok { return valueStr } choiceStrs := mustValue(anyToStringSlice(choices)) return mustValue(c.promptChoice(prompt, choiceStrs, args...)) } func (c *Config) promptChoiceOnceInteractiveTemplateFunc( m map[string]any, path any, prompt string, choices any, args ...string, ) string { if len(args) > 1 { panic(fmt.Errorf("want 4 or 5 arguments, got %d", len(args)+4)) } nestedMap, lastKey := mustValues(nestedMapAtPath(m, path)) if !c.interactiveTemplateFuncs.forcePromptOnce { if value, ok := nestedMap[lastKey]; ok { if valueStr, ok := value.(string); ok { return valueStr } } } return c.promptChoiceInteractiveTemplateFunc(prompt, choices, args...) } func (c *Config) promptMultichoiceInteractiveTemplateFunc(prompt string, choices any, args ...any) []string { if len(args) > 1 { panic(fmt.Errorf("want 2 or 3 arguments, got %d", len(args)+2)) } if valueStr, ok := c.interactiveTemplateFuncs.promptMultichoice[prompt]; ok { return strings.Split(valueStr, "/") } choiceStrs := mustValue(anyToStringSlice(choices)) var defaultValue *[]string if len(args) == 1 && args[0] != nil { values := mustValue(anyToStringSlice(args[0])) defaultValue = &values } return mustValue(c.promptMultichoice(prompt, choiceStrs, defaultValue)) } func (c *Config) promptMultichoiceOnceInteractiveTemplateFunc( m map[string]any, path any, prompt string, choices any, args ...any, ) []string { if len(args) > 1 { panic(fmt.Errorf("want 4 or 5 arguments, got %d", len(args)+4)) } nestedMap, lastKey := mustValues(nestedMapAtPath(m, path)) if !c.interactiveTemplateFuncs.forcePromptOnce { if value, ok := nestedMap[lastKey]; ok { return mustValue(anyToStringSlice(value)) } } return c.promptMultichoiceInteractiveTemplateFunc(prompt, choices, args...) } func (c *Config) promptIntInteractiveTemplateFunc(prompt string, args ...int64) int64 { if len(args) > 1 { panic(fmt.Errorf("want 1 or 2 arguments, got %d", len(args)+1)) } if value, ok := c.interactiveTemplateFuncs.promptInt[prompt]; ok { return int64(value) } return mustValue(c.promptInt(prompt, args...)) } func (c *Config) promptIntOnceInteractiveTemplateFunc(m map[string]any, path any, prompt string, args ...int64) int64 { if len(args) > 1 { panic(fmt.Errorf("want 2 or 3 arguments, got %d", len(args)+2)) } nestedMap, lastKey := mustValues(nestedMapAtPath(m, path)) if !c.interactiveTemplateFuncs.forcePromptOnce { if value, ok := nestedMap[lastKey]; ok { if intValue, ok := value.(int64); ok { return intValue } } } return c.promptIntInteractiveTemplateFunc(prompt, args...) } func (c *Config) promptStringInteractiveTemplateFunc(prompt string, args ...string) string { if len(args) > 1 { panic(fmt.Errorf("want 1 or 2 arguments, got %d", len(args)+1)) } if value, ok := c.interactiveTemplateFuncs.promptString[prompt]; ok { return value } return mustValue(c.promptString(prompt, args...)) } func (c *Config) promptStringOnceInteractiveTemplateFunc(m map[string]any, path any, prompt string, args ...string) string { if len(args) > 1 { panic(fmt.Errorf("want 2 or 3 arguments, got %d", len(args)+2)) } nestedMap, lastKey := mustValues(nestedMapAtPath(m, path)) if !c.interactiveTemplateFuncs.forcePromptOnce { if value, ok := nestedMap[lastKey]; ok { if stringValue, ok := value.(string); ok { return stringValue } } } return c.promptStringInteractiveTemplateFunc(prompt, args...) } ================================================ FILE: internal/cmd/internaltestcmd.go ================================================ package cmd import ( "strconv" "strings" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) func (c *Config) newInternalTestCmd() *cobra.Command { internalTestCmd := &cobra.Command{ Use: "internal-test", Short: "Expose functionality for testing", Hidden: true, Annotations: newAnnotations( persistentStateModeNone, ), } internalTestPromptBoolCmd := &cobra.Command{ Use: "prompt-bool prompt [default]", Args: cobra.RangeArgs(1, 2), Short: "Run promptBool", RunE: c.runInternalTestPromptBoolCmd, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeNone, ), Example: " chezmoi internal-test prompt-bool overwrite false", } internalTestCmd.AddCommand(internalTestPromptBoolCmd) internalTestPromptChoiceCmd := &cobra.Command{ Use: "prompt-choice prompt choices [default]", Args: cobra.RangeArgs(2, 3), Short: "Run promptChoice", RunE: c.runInternalTestPromptChoiceCmd, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeNone, ), Example: " chezmoi internal-test prompt-choice color red,green,blue red", } internalTestCmd.AddCommand(internalTestPromptChoiceCmd) internalTestPromptIntCmd := &cobra.Command{ Use: "prompt-int prompt [default]", Args: cobra.RangeArgs(1, 2), Short: "Run promptInt", RunE: c.runInternalTestPromptIntCmd, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeNone, ), Example: " chezmoi internal-test prompt-int count 1", } internalTestCmd.AddCommand(internalTestPromptIntCmd) internalTestPromptMultichoiceCmd := &cobra.Command{ Use: "prompt-multichoice prompt choices [defaults]", Args: cobra.RangeArgs(2, 3), Short: "Run promptMultichoice", RunE: c.runInternalTestPromptMultichoiceCmd, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeNone, ), Example: " chezmoi internal-test prompt-multichoice days mon,tue,wed,thu,fri,sat,sun sat,sun", } internalTestCmd.AddCommand(internalTestPromptMultichoiceCmd) internalTestPromptStringCmd := &cobra.Command{ Use: "prompt-string prompt [default]", Args: cobra.RangeArgs(1, 2), Short: "Run promptString", RunE: c.runInternalTestPromptStringCmd, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeNone, ), Example: " chezmoi internal-test prompt-string username root", } internalTestCmd.AddCommand(internalTestPromptStringCmd) internalTestReadPasswordCmd := &cobra.Command{ Use: "read-password", Args: cobra.NoArgs, Short: "Read a password", RunE: c.runInternalTestReadPasswordCmd, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeNone, ), Example: " chezmoi internal-test read-password", } internalTestCmd.AddCommand(internalTestReadPasswordCmd) return internalTestCmd } func (c *Config) runInternalTestPromptBoolCmd(cmd *cobra.Command, args []string) error { boolArgs := make([]bool, len(args)-1) for i, arg := range args[1:] { boolArg, err := chezmoi.ParseBool(arg) if err != nil { return err } boolArgs[i] = boolArg } value, err := c.promptBool(args[0], boolArgs...) if err != nil { return err } return c.writeOutputString(strconv.FormatBool(value)+"\n", 0o666) } func (c *Config) runInternalTestPromptChoiceCmd(cmd *cobra.Command, args []string) error { value, err := c.promptChoice(args[0], strings.Split(args[1], ","), args[2:]...) if err != nil { return err } return c.writeOutputString(value+"\n", 0o666) } func (c *Config) runInternalTestPromptMultichoiceCmd(cmd *cobra.Command, args []string) error { var defaults *[]string if len(args) > 2 { values := strings.Split(args[2], ",") defaults = &values } value, err := c.promptMultichoice(args[0], strings.Split(args[1], ","), defaults) if err != nil { return err } for _, entry := range value { if err := c.writeOutputString(entry+"\n", 0o666); err != nil { return err } } return nil } func (c *Config) runInternalTestPromptIntCmd(cmd *cobra.Command, args []string) error { int64Args := make([]int64, len(args)-1) for i, arg := range args[1:] { int64Arg, err := strconv.ParseInt(arg, 10, 64) if err != nil { return err } int64Args[i] = int64Arg } value, err := c.promptInt(args[0], int64Args...) if err != nil { return err } return c.writeOutputString(strconv.FormatInt(value, 10)+"\n", 0o666) } func (c *Config) runInternalTestPromptStringCmd(cmd *cobra.Command, args []string) error { value, err := c.promptString(args[0], args[1:]...) if err != nil { return err } return c.writeOutputString(value+"\n", 0o666) } func (c *Config) runInternalTestReadPasswordCmd(cmd *cobra.Command, args []string) error { password, err := c.readPassword("Password? ", "password") if err != nil { return err } return c.writeOutputString(password+"\n", 0o666) } ================================================ FILE: internal/cmd/interpreters.go ================================================ package cmd import "chezmoi.io/chezmoi/internal/chezmoi" // NewDefaultInterpreters returns the default interpreters map, using the // provided findExecutable function. func NewDefaultInterpreters(findExecutable func([]string, []string) (string, error)) map[string]chezmoi.Interpreter { interpreters := map[string]chezmoi.Interpreter{ "bat": {}, "cmd": {}, "com": {}, "exe": {}, "nu": { Command: "nu", }, "pl": { Command: "perl", }, // select the platform-appropriate interpreter for .ps1 scripts - prefer pwsh for UTF-8 and cross-platform support // if available with a fallback to powershell on Windows "ps1": func() chezmoi.Interpreter { i := getPS1Interpreter(findExecutable) return chezmoi.Interpreter{ Command: i.Command, Args: i.Args, } }(), "py": { Command: "python3", }, "rb": { Command: "ruby", }, } return interpreters } // DefaultInterpreters is the default interpreters map for the current platform. var DefaultInterpreters = NewDefaultInterpreters(chezmoi.FindExecutable) ================================================ FILE: internal/cmd/interpreters_test.go ================================================ package cmd import ( "testing" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/chezmoi" ) func TestNewDefaultInterpreters_OtherInterpreters(t *testing.T) { // Verify that other interpreters are not affected by ps1 changes interpreters := NewDefaultInterpreters(func([]string, []string) (string, error) { return "", nil }) for _, tc := range []struct { ext string expected chezmoi.Interpreter }{ {ext: "bat"}, {ext: "cmd"}, {ext: "com"}, {ext: "exe"}, {ext: "nu", expected: chezmoi.Interpreter{Command: "nu"}}, {ext: "pl", expected: chezmoi.Interpreter{Command: "perl"}}, {ext: "py", expected: chezmoi.Interpreter{Command: "python3"}}, {ext: "rb", expected: chezmoi.Interpreter{Command: "ruby"}}, } { t.Run(tc.ext, func(t *testing.T) { assert.Equal(t, tc.expected, interpreters[tc.ext]) }) } } ================================================ FILE: internal/cmd/interpreters_unix_test.go ================================================ //go:build !windows package cmd import ( "testing" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/chezmoi" ) func TestNewDefaultInterpreters_PS1(t *testing.T) { for _, tc := range []struct { name string findExecutable func([]string, []string) (string, error) expected chezmoi.Interpreter }{ { name: "pwsh_available", findExecutable: func(names, paths []string) (string, error) { if names[0] == "pwsh" { return "/usr/bin/pwsh", nil } return "", nil }, expected: chezmoi.Interpreter{ Command: "pwsh", Args: []string{"-NoLogo", "-File"}, }, }, { name: "pwsh_not_available", findExecutable: func(names, paths []string) (string, error) { return "", nil }, }, } { t.Run(tc.name, func(t *testing.T) { interpreters := NewDefaultInterpreters(tc.findExecutable) assert.Equal(t, tc.expected, interpreters["ps1"]) }) } } ================================================ FILE: internal/cmd/interpreters_windows_test.go ================================================ //go:build windows package cmd import ( "testing" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/chezmoi" ) func TestNewDefaultInterpreters_PS1(t *testing.T) { for _, tc := range []struct { name string findExecutable func([]string, []string) (string, error) expected chezmoi.Interpreter }{ { name: "pwsh_available", findExecutable: func(names, paths []string) (string, error) { if names[0] == "pwsh" || names[0] == "pwsh.exe" { return "C:\\Program Files\\PowerShell\\7\\pwsh.exe", nil } return "", nil }, expected: chezmoi.Interpreter{ Command: "pwsh", Args: []string{"-NoLogo", "-File"}, }, }, { name: "only_powershell_available", findExecutable: func(names, paths []string) (string, error) { if names[0] == "pwsh" || names[0] == "pwsh.exe" { return "", nil } if names[0] == "powershell" || names[0] == "powershell.exe" { return "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", nil } return "", nil }, expected: chezmoi.Interpreter{ Command: "powershell", Args: []string{"-NoLogo", "-File"}, }, }, { name: "neither_available", findExecutable: func(names, paths []string) (string, error) { return "", nil }, }, } { t.Run(tc.name, func(t *testing.T) { interpreters := NewDefaultInterpreters(tc.findExecutable) assert.Equal(t, tc.expected, interpreters["ps1"]) }) } } ================================================ FILE: internal/cmd/keepassxctemplatefuncs.go ================================================ package cmd import ( "bytes" "errors" "fmt" "os" "os/exec" "regexp" "slices" "strconv" "strings" "time" "github.com/coreos/go-semver/semver" "github.com/tobischo/gokeepasslib/v3" "github.com/twpayne/go-expect" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoilog" ) type keepassxcMode string const ( keepassxcModeBuiltin keepassxcMode = "builtin" keepassxcModeCachePassword keepassxcMode = "cache-password" keepassxcModeOpen keepassxcMode = "open" ) type keepassxcAttributeCacheKey struct { entry string attribute string } type keepassxcConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Database chezmoi.AbsPath `json:"database" mapstructure:"database" yaml:"database"` Mode keepassxcMode `json:"mode" mapstructure:"mode" yaml:"mode"` Args []string `json:"args" mapstructure:"args" yaml:"args"` Prompt bool `json:"prompt" mapstructure:"prompt" yaml:"prompt"` cmd *exec.Cmd console *expect.Console promptStr string cache map[string]map[string]string attachmentCache map[string]map[string]string attributeCache map[keepassxcAttributeCacheKey]string password string } var ( keepassxcMinVersion = semver.Version{Major: 2, Minor: 7, Patch: 0} keepassxcEnterPasswordToUnlockDatabaseRx = regexp.MustCompile(`^Enter password to unlock .*: `) keepassxcPleasePresentOrTouchYourYubiKeyToContinueRx = regexp.MustCompile( "^Please present or touch your \\S+ to continue\\.\r\n", ) keepassxcAnyResponseRx = regexp.MustCompile(`(?m)\A.*\r\n`) keepassxcPairRx = regexp.MustCompile(`\A([A-Z]\w*):\s*(.*?)\r?\n\z`) keepassxcPromptRx = regexp.MustCompile(`^.*> `) ) func (c *Config) keepassxcAttachmentTemplateFunc(entry, name string) string { if data, ok := c.Keepassxc.attachmentCache[entry][name]; ok { return data } switch c.Keepassxc.Mode { case keepassxcModeCachePassword: // In cache password mode use --stdout to read the attachment data directly. return string(mustValue(c.keepassxcOutput("attachment-export", "--quiet", "--stdout", entry, name))) case keepassxcModeOpen: // In open mode write the attachment data to a temporary file. tempDir := mustValue(c.tempDir("chezmoi-keepassxc")) tempFilename := tempDir.JoinString("attachment-" + strconv.FormatInt(time.Now().UnixNano(), 10)).String() _ = mustValue(c.keepassxcOutputOpen("attachment-export", "--quiet", entry, name, tempFilename)) data := mustValue(os.ReadFile(tempFilename)) must(os.Remove(tempFilename)) return string(data) case keepassxcModeBuiltin: c.Keepassxc.cache[entry] = c.keepassxcBuiltinExtractValues(entry, keepassxcBuiltinMapAttachmentCache) if data, ok := c.Keepassxc.cache[entry]; ok { if att, ok := data[name]; ok { return att } } panic(fmt.Sprintf("attachment %s of entry %s not found", name, entry)) default: panic(fmt.Sprintf("%s: invalid mode", c.Keepassxc.Mode)) } } func (c *Config) keepassxcTemplateFunc(entry string) map[string]string { if c.Keepassxc.cache == nil { c.Keepassxc.cache = make(map[string]map[string]string) } if data, ok := c.Keepassxc.cache[entry]; ok { return data } if c.Keepassxc.Mode == keepassxcModeBuiltin { c.Keepassxc.cache[entry] = c.keepassxcBuiltinExtractValues(entry, keepassxcBuiltinMapValueCache) return c.Keepassxc.cache[entry] } args := []string{"--quiet", "--show-protected", entry} output := mustValue(c.keepassxcOutput("show", args...)) data := keepassxcParseOutput(output) c.Keepassxc.cache[entry] = data return data } func (c *Config) keepassxcAttributeTemplateFunc(entry, attribute string) string { if c.Keepassxc.Mode == keepassxcModeBuiltin { // builtin stores attributes in cache if c.Keepassxc.cache == nil { c.Keepassxc.cache = make(map[string]map[string]string) } if data, ok := c.Keepassxc.cache[entry]; ok { return data[attribute] } c.Keepassxc.cache[entry] = c.keepassxcBuiltinExtractValues(entry, keepassxcBuiltinMapValueCache) if data, ok := c.Keepassxc.cache[entry]; ok { return data[attribute] } panic(fmt.Sprintf("attribute %s of entry %s not found", entry, attribute)) } key := keepassxcAttributeCacheKey{ entry: entry, attribute: attribute, } if data, ok := c.Keepassxc.attributeCache[key]; ok { return data } output := mustValue(c.keepassxcOutput("show", entry, "--attributes", attribute, "--quiet", "--show-protected")) outputStr := string(bytes.TrimSpace(output)) if c.Keepassxc.attributeCache == nil { c.Keepassxc.attributeCache = make(map[keepassxcAttributeCacheKey]string) } c.Keepassxc.attributeCache[key] = outputStr return outputStr } // keepassxcOutput returns the output of command and args. func (c *Config) keepassxcOutput(command string, args ...string) ([]byte, error) { if c.Keepassxc.Database.IsEmpty() { panic(errors.New("keepassxc.database not set")) } switch c.Keepassxc.Mode { case keepassxcModeCachePassword: return c.keepassxcOutputCachePassword(command, args...) case keepassxcModeOpen: return c.keepassxcOutputOpen(command, args...) default: panic(fmt.Sprintf("%s: invalid mode", c.Keepassxc.Mode)) } } // keepassxcOutputCachePassword returns the output of command and args, // prompting the user for the password and caching it for later use. func (c *Config) keepassxcOutputCachePassword(command string, args ...string) ([]byte, error) { cmdArgs := []string{command} cmdArgs = append(cmdArgs, c.Keepassxc.Args...) cmdArgs = append(cmdArgs, c.Keepassxc.Database.String()) cmdArgs = append(cmdArgs, args...) cmd := exec.Command(c.Keepassxc.Command, cmdArgs...) if c.Keepassxc.password == "" && c.Keepassxc.Prompt { password, err := c.readPassword(fmt.Sprintf("Enter password to unlock %s: ", c.Keepassxc.Database), "password") if err != nil { return nil, err } c.Keepassxc.password = password } if c.Keepassxc.password != "" { cmd.Stdin = bytes.NewBufferString(c.Keepassxc.password + "\n") } else { cmd.Stdin = os.Stdin } cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } return output, nil } // keepassxcOutputOpen returns the output of command and args using an // interactive connection via keepassxc-cli open command's interactive console. func (c *Config) keepassxcOutputOpen(command string, args ...string) ([]byte, error) { // Create the console if it is not already created. if c.Keepassxc.console == nil { // Create the console. console, err := expect.NewConsole() if err != nil { return nil, err } // Start the keepassxc-cli open command. cmdArgs := []string{"open"} cmdArgs = append(cmdArgs, c.Keepassxc.Args...) cmdArgs = append(cmdArgs, c.Keepassxc.Database.String()) cmd := exec.Command(c.Keepassxc.Command, cmdArgs...) env := os.Environ() // Ensure prompt is in English. env = append(env, "LANGUAGE=en") // Reduce injection of terminal control characters. env = slices.DeleteFunc(env, func(s string) bool { return strings.HasPrefix(s, "TERM=") }) cmd.Env = env cmd.Stdin = console.Tty() cmd.Stdout = console.Tty() cmd.Stderr = console.Tty() if err := chezmoilog.LogCmdStart(c.logger, cmd); err != nil { return nil, err } if c.Keepassxc.Prompt { // Expect the password or YubiKey response. response, err := console.Expect( expect.Regexp(keepassxcEnterPasswordToUnlockDatabaseRx), expect.Regexp(keepassxcPleasePresentOrTouchYourYubiKeyToContinueRx), expect.Regexp(keepassxcAnyResponseRx), ) if err != nil { return nil, err } switch { case keepassxcEnterPasswordToUnlockDatabaseRx.MatchString(response): // Read the password from the user, if necessary. var password string if c.Keepassxc.password != "" { password = c.Keepassxc.password } else { password, err = c.readPassword(response, "password") if err != nil { return nil, err } } // Send the password. if _, err := console.SendLine(password); err != nil { return nil, err } // Wait for the end of the password prompt. if _, err := console.ExpectString("\r\n"); err != nil { return nil, err } case keepassxcPleasePresentOrTouchYourYubiKeyToContinueRx.MatchString(response): if _, err := console.ExpectString("\r\n"); err != nil { return nil, err } if _, err := c.stderr.Write([]byte(strings.TrimSpace(response) + "\n")); err != nil { return nil, err } default: return nil, fmt.Errorf("%q: unexpected response", response) } } // Read the response, e.g "Passwords> ", so we can expect it later. response, err := console.Expect( expect.Regexp(keepassxcPromptRx), expect.Regexp(keepassxcAnyResponseRx), ) if err != nil { return nil, err } if !keepassxcPromptRx.MatchString(response) { return nil, fmt.Errorf("%q: unexpected response", response) } c.Keepassxc.cmd = cmd c.Keepassxc.console = console c.Keepassxc.promptStr = keepassxcPromptRx.FindString(response) } // Build the command line. Strings with spaces and other non-word characters // need to be quoted. quotedArgs := make([]string, 0, len(args)) for _, arg := range args { quotedArgs = append(quotedArgs, maybeQuote(arg)) } line := strings.Join(append([]string{command}, quotedArgs...), " ") // Send the line. if _, err := c.Keepassxc.console.SendLine(line); err != nil { return nil, err } // Read everything up to and including the prompt. output, err := c.Keepassxc.console.ExpectString(c.Keepassxc.promptStr) if err != nil { return nil, err } outputLines := strings.Split(output, "\r\n") // Trim the echoed command from the output, which is the first line. if len(outputLines) > 0 { outputLines = outputLines[1:] } // Trim the prompt from the output, which is the last line. if len(outputLines) > 0 { outputLines = outputLines[:len(outputLines)-1] } return []byte(strings.Join(outputLines, "\r\n")), nil } // keepassxcParseOutput parses a list of key-value pairs. func keepassxcParseOutput(output []byte) map[string]string { data := make(map[string]string) var key string for line := range bytes.Lines(output) { switch match := keepassxcPairRx.FindSubmatch(line); { case match != nil: key = string(match[1]) data[key] = string(match[2]) case key != "": data[key] += "\n" + string(bytes.TrimSuffix(line, []byte{'\n'})) } } return data } // keepassxcClose closes any open connection to keepassxc-cli. func (c *Config) keepassxcClose() error { // FIXME should we wait for EOF somewhere? if c.Keepassxc.console == nil { return nil } if _, err := c.Keepassxc.console.SendLine("exit"); err != nil { return err } if _, err := c.Keepassxc.console.ExpectString("exit\r\n"); err != nil { return err } if err := chezmoilog.LogCmdWait(c.logger, c.Keepassxc.cmd); err != nil { return err } return c.Keepassxc.console.Close() } // keepassxcBuiltinExtractValues extract builtin values. func (c *Config) keepassxcBuiltinExtractValues( entry string, mapper func(db *gokeepasslib.Database, entry gokeepasslib.Entry) map[string]string, ) map[string]string { file, err := os.Open(c.Keepassxc.Database.String()) if err != nil { panic(err) } defer file.Close() if c.Keepassxc.password == "" && c.Keepassxc.Prompt { password, err := c.readPassword(fmt.Sprintf("Enter password to unlock %s: ", c.Keepassxc.Database), "password") if err != nil { panic(err) } c.Keepassxc.password = password } db := gokeepasslib.NewDatabase() db.Credentials = gokeepasslib.NewPasswordCredentials(c.Keepassxc.password) err = gokeepasslib.NewDecoder(file).Decode(db) if err != nil { panic(err) } err = db.UnlockProtectedEntries() if err != nil { panic(err) } return keepassxcBuiltinBuildCache(db, "", db.Content.Root.Groups[0].Groups, nil, entry, mapper) } // keepassxcBuiltinBuildCache build the builtin cache using a given mapper // function. func keepassxcBuiltinBuildCache( db *gokeepasslib.Database, path string, groups []gokeepasslib.Group, mapData map[string]string, entry string, mapper func(db *gokeepasslib.Database, entry gokeepasslib.Entry) map[string]string, ) map[string]string { for _, group := range groups { if len(group.Entries) > 0 { for _, groupEntry := range group.Entries { var elements []string if path != "" { elements = append(elements, path) } elements = append(elements, group.Name, groupEntry.GetTitle()) key := strings.Join(elements, "/") if entry == key { return mapper(db, groupEntry) } } } if len(group.Groups) > 0 { mapData = keepassxcBuiltinBuildCache( db, strings.TrimPrefix(fmt.Sprintf("%s/%s", path, group.Name), "/"), group.Groups, mapData, entry, mapper, ) if len(mapData) > 0 { return mapData } } } return map[string]string{} } // keepassxcBuiltinMapValueCache map builtin entries for values and attributes. func keepassxcBuiltinMapValueCache(_ *gokeepasslib.Database, entry gokeepasslib.Entry) map[string]string { m := make(map[string]string) for _, value := range entry.Values { m[value.Key] = value.Value.Content } return m } // keepassxcBuiltinMapAttachmentCache map builtin entries for attachments. func keepassxcBuiltinMapAttachmentCache(db *gokeepasslib.Database, entry gokeepasslib.Entry) map[string]string { m := make(map[string]string) for _, bin := range entry.Binaries { b := db.FindBinary(bin.Value.ID) if b != nil { str, err := b.GetContentString() if err != nil { panic(err) } m[bin.Name] = str } } return m } ================================================ FILE: internal/cmd/keepassxctemplatefuncs_test.go ================================================ package cmd import ( "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestKeepassxcParseOutput(t *testing.T) { for i, tc := range []struct { output []byte expected map[string]string }{ { expected: map[string]string{}, }, { output: []byte(chezmoitest.JoinLines( "Title: test", "UserName: test", "Password: test", "URL:", "Notes: account: 123456789", "2021-11-27 [expires: 2023-02-25]", "main = false", )), expected: map[string]string{ "Title": "test", "UserName": "test", "Password": "test", "URL": "", "Notes": strings.Join([]string{ "account: 123456789", "2021-11-27 [expires: 2023-02-25]", "main = false", }, "\n"), }, }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { assert.Equal(t, tc.expected, keepassxcParseOutput(tc.output)) }) } } type keepassEntry struct { database string databasePassword string groupName string entryName string entryUsername string entryPassword string attachmentName string attachmentData string } func TestKeepassxcTemplateFuncs(t *testing.T) { if runtime.GOOS == "darwin" { t.Skip("test does not work on darwin") } // Find the path to keepassxc-cli command. command, err := exec.LookPath("keepassxc-cli") if err != nil { t.Skip("keepassxc-cli not found in $PATH") } assert.NoError(t, err) tempDir := t.TempDir() // The following test data includes spaces and slashes to test quoting. database := filepath.Join(tempDir, "KeePassXC Passwords.kdbx") databasePassword := "test / database / password" groupName := "test group" entryName := groupName + "/test entry" entryUsername := "test / username" entryPassword := "test / password" attachmentName := "test / attachment name" attachmentData := "test / attachment data" nestedGroupName := "some/nested/group" nestedEntryName := nestedGroupName + "/nested entry" nestedEntryUsername := "nested / username" nestedEntryPassword := "nested / password" nestedAttachmentName := "nested / attachment name" nestedAttachmentData := "nested / attachment data" // Create a KeePassXC database. dbCreateCmd := exec.Command(command, "db-create", "--set-password", database) dbCreateCmd.Stdin = strings.NewReader(chezmoitest.JoinLines( databasePassword, databasePassword, )) dbCreateCmd.Stdout = os.Stdout dbCreateCmd.Stderr = os.Stderr assert.NoError(t, dbCreateCmd.Run()) createKeepassEntry(t, command, tempDir, keepassEntry{ database: database, databasePassword: databasePassword, groupName: groupName, entryName: entryName, entryUsername: entryUsername, entryPassword: entryPassword, attachmentName: attachmentName, attachmentData: attachmentData, }) createKeepassEntry(t, command, tempDir, keepassEntry{ database: database, databasePassword: databasePassword, groupName: nestedGroupName, entryName: nestedEntryName, entryUsername: nestedEntryUsername, entryPassword: nestedEntryPassword, attachmentName: nestedAttachmentName, attachmentData: nestedAttachmentData, }) for _, mode := range []keepassxcMode{ keepassxcModeBuiltin, keepassxcModeCachePassword, keepassxcModeOpen, } { t.Run(string(mode), func(t *testing.T) { t.Run("correct_password", func(t *testing.T) { config := newTestConfig(t, vfs.OSFS) defer config.keepassxcClose() config.Keepassxc.Database = chezmoi.NewAbsPath(database) config.Keepassxc.Mode = mode config.Keepassxc.Prompt = true config.Keepassxc.password = databasePassword assert.Equal(t, entryPassword, config.keepassxcTemplateFunc(entryName)["Password"]) assert.Equal(t, entryUsername, config.keepassxcAttributeTemplateFunc(entryName, "UserName")) assert.Equal(t, attachmentData, config.keepassxcAttachmentTemplateFunc(entryName, attachmentName)) assert.Equal(t, nestedEntryPassword, config.keepassxcTemplateFunc(nestedEntryName)["Password"]) assert.Equal(t, nestedEntryUsername, config.keepassxcAttributeTemplateFunc(nestedEntryName, "UserName")) assert.Equal( t, nestedAttachmentData, config.keepassxcAttachmentTemplateFunc(nestedEntryName, nestedAttachmentName), ) }) t.Run("incorrect_password", func(t *testing.T) { config := newTestConfig(t, vfs.OSFS) defer config.keepassxcClose() config.Keepassxc.Database = chezmoi.NewAbsPath(database) config.Keepassxc.Mode = mode config.Keepassxc.Prompt = true config.Keepassxc.password = "incorrect-" + databasePassword assert.Panics(t, func() { config.keepassxcTemplateFunc(entryName) }) assert.Panics(t, func() { config.keepassxcAttributeTemplateFunc(entryName, "UserName") }) assert.Panics(t, func() { config.keepassxcAttachmentTemplateFunc(entryName, attachmentName) }) }) t.Run("incorrect_database", func(t *testing.T) { config := newTestConfig(t, vfs.OSFS) defer config.keepassxcClose() config.Keepassxc.Database = chezmoi.NewAbsPath(filepath.Join(tempDir, "Non-existent database.kdbx")) config.Keepassxc.Mode = mode config.Keepassxc.Prompt = true config.Keepassxc.password = databasePassword assert.Panics(t, func() { config.keepassxcTemplateFunc(entryName) }) assert.Panics(t, func() { config.keepassxcAttributeTemplateFunc(entryName, "UserName") }) assert.Panics(t, func() { config.keepassxcAttachmentTemplateFunc(entryName, attachmentName) }) }) }) } } func createKeepassEntry(t *testing.T, command, tempDir string, kpe keepassEntry) { t.Helper() // Create nested groups in the database. groupPath := strings.Split(kpe.groupName, "/") for i := range groupPath { name := strings.Join(groupPath[0:i+1], "/") mkdirCmd := exec.Command(command, "mkdir", kpe.database, name) mkdirCmd.Stdin = strings.NewReader(kpe.databasePassword + "\n") mkdirCmd.Stdout = os.Stdout mkdirCmd.Stderr = os.Stderr assert.NoError(t, mkdirCmd.Run()) } // Create an entry in the database. addCmd := exec.Command(command, "add", kpe.database, kpe.entryName, "--username", kpe.entryUsername, "--password-prompt") addCmd.Stdin = strings.NewReader(chezmoitest.JoinLines( kpe.databasePassword, kpe.entryPassword, )) addCmd.Stdout = os.Stdout addCmd.Stderr = os.Stderr assert.NoError(t, addCmd.Run()) // Import an attachment to the entry in the database. importFile := filepath.Join(tempDir, "import file") assert.NoError(t, os.WriteFile(importFile, []byte(kpe.attachmentData), 0o666)) attachmentImportCmd := exec.Command( command, "attachment-import", kpe.database, kpe.entryName, kpe.attachmentName, importFile, ) attachmentImportCmd.Stdin = strings.NewReader(kpe.databasePassword + "\n") attachmentImportCmd.Stdout = os.Stdout attachmentImportCmd.Stderr = os.Stderr assert.NoError(t, attachmentImportCmd.Run()) } ================================================ FILE: internal/cmd/keepertemplatefuncs.go ================================================ package cmd import ( "bytes" "encoding/json" "os" "os/exec" "strings" "chezmoi.io/chezmoi/internal/chezmoilog" ) type keeperConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Args []string `json:"args" mapstructure:"args" yaml:"args"` outputCache map[string][]byte } func (c *Config) keeperTemplateFunc(record string) map[string]any { output := mustValue(c.keeperOutput([]string{"get", "--format=json", record})) var result map[string]any must(json.Unmarshal(output, &result)) return result } func (c *Config) keeperDataFieldsTemplateFunc(record string) map[string]any { output := mustValue(c.keeperOutput([]string{"get", "--format=json", record})) var data struct { Data struct { Fields []struct { Type string `json:"type"` Value any `json:"value"` } `json:"fields"` } `json:"data"` } must(json.Unmarshal(output, &data)) result := make(map[string]any) for _, field := range data.Data.Fields { result[field.Type] = field.Value } return result } func (c *Config) keeperFindPasswordTemplateFunc(record string) string { output := mustValue(c.keeperOutput([]string{"find-password", record})) return string(bytes.TrimSpace(output)) } func (c *Config) keeperOutput(args []string) ([]byte, error) { key := strings.Join(args, "\x00") if data, ok := c.Keeper.outputCache[key]; ok { return data, nil } name := c.Keeper.Command args = append(args, c.Keeper.Args...) cmd := exec.Command(name, args...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } if c.Keeper.outputCache == nil { c.Keeper.outputCache = make(map[string][]byte) } c.Keeper.outputCache[key] = output return output, nil } ================================================ FILE: internal/cmd/keyringtemplatefuncs.go ================================================ //go:build !freebsd || (freebsd && cgo) package cmd import ( "fmt" "github.com/zalando/go-keyring" ) type keyringKey struct { service string user string } type keyringData struct { cache map[keyringKey]string } func (c *Config) keyringTemplateFunc(service, user string) string { key := keyringKey{ service: service, user: user, } if password, ok := c.keyring.cache[key]; ok { return password } password, err := keyring.Get(service, user) if err != nil { panic(fmt.Errorf("%s %s: %w", service, user, err)) } if c.keyring.cache == nil { c.keyring.cache = make(map[keyringKey]string) } c.keyring.cache[key] = password return password } ================================================ FILE: internal/cmd/keyringtemplatefuncs_freebsdnocgo.go ================================================ //go:build freebsd && !cgo package cmd type keyringData struct{} func (c *Config) keyringTemplateFunc(service, user string) string { return "" } ================================================ FILE: internal/cmd/lastpasstemplatefuncs.go ================================================ package cmd import ( "encoding/json" "fmt" "os" "os/exec" "regexp" "strings" "unicode" "github.com/coreos/go-semver/semver" "chezmoi.io/chezmoi/internal/chezmoilog" ) var ( // chezmoi uses lpass show --json which was added in v1.3.0~6. lastpassMinVersion = semver.Version{Major: 1, Minor: 3, Patch: 0} lastpassParseNoteRx = regexp.MustCompile(`\A([ A-Za-z]*):(.*)(?:\r?\n)?\z`) lastpassVersionArgs = []string{"--version"} lastpassVersionRx = regexp.MustCompile(`^LastPass CLI v(\d+\.\d+\.\d+)`) ) type lastpassConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` cache map[string][]map[string]any } func (c *Config) lastpassTemplateFunc(id string) []map[string]any { data := mustValue(c.lastpassData(id)) for _, d := range data { if note, ok := d["note"].(string); ok { d["note"] = lastpassParseNote(note) } } return data } func (c *Config) lastpassRawTemplateFunc(id string) []map[string]any { return mustValue(c.lastpassData(id)) } func (c *Config) lastpassData(id string) ([]map[string]any, error) { if data, ok := c.Lastpass.cache[id]; ok { return data, nil } output, err := c.lastpassOutput("show", "--json", id) if err != nil { return nil, err } var data []map[string]any if err := json.Unmarshal(output, &data); err != nil { return nil, fmt.Errorf("%s: parse error: %w", output, err) } if c.Lastpass.cache == nil { c.Lastpass.cache = make(map[string][]map[string]any) } c.Lastpass.cache[id] = data return data, nil } func (c *Config) lastpassOutput(args ...string) ([]byte, error) { name := c.Lastpass.Command cmd := exec.Command(name, args...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, err } return output, nil } func lastpassParseNote(note string) map[string]string { result := make(map[string]string) key := "" for line := range strings.Lines(note) { if m := lastpassParseNoteRx.FindStringSubmatch(line); m != nil { keyComponents := strings.Split(m[1], " ") firstComponentRunes := []rune(keyComponents[0]) firstComponentRunes[0] = unicode.ToLower(firstComponentRunes[0]) keyComponents[0] = string(firstComponentRunes) key = strings.Join(keyComponents, "") result[key] = m[2] + "\n" } else { result[key] += line } } return result } ================================================ FILE: internal/cmd/lastpasstemplatefuncs_test.go ================================================ package cmd import ( "strconv" "testing" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestLastpassParseNote(t *testing.T) { for i, tc := range []struct { note string expected map[string]string }{ { note: "Foo:bar\n", expected: map[string]string{ "foo": "bar\n", }, }, { note: chezmoitest.JoinLines( "Foo:bar", "baz", ), expected: map[string]string{ "foo": chezmoitest.JoinLines( "bar", "baz", ), }, }, { note: chezmoitest.JoinLines( "NoteType:SSH Key", "Language:en-US", "Bit Strength:2048", "Format:RSA", "Passphrase:Passphrase", "Private Key:-----BEGIN OPENSSH PRIVATE KEY-----", "-----END OPENSSH PRIVATE KEY-----", "Public Key:ssh-rsa public-key you@example", "Hostname:Hostname", "Date:Date", ) + "Notes:", expected: map[string]string{ "noteType": "SSH Key\n", "language": "en-US\n", "bitStrength": "2048\n", "format": "RSA\n", "passphrase": "Passphrase\n", "privateKey": "-----BEGIN OPENSSH PRIVATE KEY-----\n-----END OPENSSH PRIVATE KEY-----\n", "publicKey": "ssh-rsa public-key you@example\n", "hostname": "Hostname\n", "date": "Date\n", "notes": "\n", }, }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { assert.Equal(t, tc.expected, lastpassParseNote(tc.note)) }) } } ================================================ FILE: internal/cmd/lazyscryptidentity.go ================================================ package cmd import ( "errors" "fmt" "filippo.io/age" ) // This is copied from https://github.com/FiloSottile/age/blob/6ad4560f4afc3fe46b6cda0bc568e50b89a22e4c/cmd/age/encrypted_keys.go#L15-L51 // LazyScryptIdentity is an age.Identity that requests a passphrase only if it // encounters an scrypt stanza. After obtaining a passphrase, it delegates to // ScryptIdentity. type LazyScryptIdentity struct { Passphrase func() (string, error) } var _ age.Identity = &LazyScryptIdentity{} func (i *LazyScryptIdentity) Unwrap(stanzas []*age.Stanza) (fileKey []byte, err error) { for _, s := range stanzas { if s.Type == "scrypt" && len(stanzas) != 1 { return nil, errors.New("an scrypt recipient must be the only one") } } if len(stanzas) != 1 || stanzas[0].Type != "scrypt" { return nil, age.ErrIncorrectIdentity } pass, err := i.Passphrase() if err != nil { return nil, fmt.Errorf("could not read passphrase: %w", err) } ii, err := age.NewScryptIdentity(pass) if err != nil { return nil, err } fileKey, err = ii.Unwrap(stanzas) if errors.Is(err, age.ErrIncorrectIdentity) { // ScryptIdentity returns ErrIncorrectIdentity for an incorrect // passphrase, which would lead Decrypt to returning "no identity // matched any recipient". That makes sense in the API, where there // might be multiple configured ScryptIdentity. Since in cmd/age there // can be only one, return a better error message. return nil, errors.New("incorrect passphrase") } return fileKey, err } ================================================ FILE: internal/cmd/lazywriter.go ================================================ package cmd import ( "io" "sync" ) // A lazyWriter only opens its destination on first write. type lazyWriter struct { mutex sync.Mutex openFunc func() (io.WriteCloser, error) writeCloser io.WriteCloser err error } func newLazyWriter(openFunc func() (io.WriteCloser, error)) *lazyWriter { return &lazyWriter{ openFunc: openFunc, } } func (w *lazyWriter) Close() error { w.mutex.Lock() defer w.mutex.Unlock() if w.writeCloser == nil { return nil } return w.writeCloser.Close() } func (w *lazyWriter) Write(p []byte) (int, error) { w.mutex.Lock() defer w.mutex.Unlock() if w.openFunc != nil { w.writeCloser, w.err = w.openFunc() w.openFunc = nil } if w.err != nil { return 0, w.err } return w.writeCloser.Write(p) } ================================================ FILE: internal/cmd/license.gen.go ================================================ package cmd var license = "" + "\n" + " The MIT License (MIT)\n" + "\n" + " Copyright (c) 2018 Tom Payne\n" + "\n" + " Permission is hereby granted, free of charge, to any person obtaining a copy\n" + " of this software and associated documentation files (the \"Software\"), to\n" + " deal in the Software without restriction, including without limitation the\n" + " rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n" + " sell copies of the Software, and to permit persons to whom the Software is\n" + " furnished to do so, subject to the following conditions:\n" + "\n" + " The above copyright notice and this permission notice shall be included in\n" + " all copies or substantial portions of the Software.\n" + "\n" + " THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" + " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" + " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n" + " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" + " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n" + " FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n" + " IN THE SOFTWARE.\n" + "\n" + "" ================================================ FILE: internal/cmd/licensecmd.go ================================================ package cmd import ( "github.com/spf13/cobra" ) func (c *Config) newLicenseCmd() *cobra.Command { licenseCmd := &cobra.Command{ GroupID: groupIDDocumentation, Use: "license", Short: "Print license", Long: mustLongHelp("license"), Example: example("license"), Args: cobra.NoArgs, ValidArgsFunction: cobra.NoFileCompletions, RunE: c.runLicenseCmd, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeNone, ), } return licenseCmd } func (c *Config) runLicenseCmd(cmd *cobra.Command, args []string) error { return c.writeOutputString(license, 0o666) } ================================================ FILE: internal/cmd/mackupcmd_darwin.go ================================================ package cmd import ( "errors" "fmt" "io/fs" "path/filepath" "regexp" "strings" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) var ( mackupCommentRx = regexp.MustCompile(`\A#.*\n\z`) mackupKeyValueRx = regexp.MustCompile(`\A(\w+)\s*=\s*(.*)\z`) mackupSectionRx = regexp.MustCompile(`\A\[(.*)\]\n\z`) ) type mackupApplicationApplicationConfig struct { Name string } type mackupApplicationConfig struct { Application mackupApplicationApplicationConfig ConfigurationFiles []chezmoi.RelPath XDGConfigurationFiles []chezmoi.RelPath } func (c *Config) newMackupCmd() *cobra.Command { mackupCmd := &cobra.Command{ Use: "mackup", Short: "Interact with Mackup", Hidden: true, Annotations: newAnnotations( persistentStateModeNone, ), } mackupAddCmd := &cobra.Command{ Use: "add application...", Short: "Add an application's configuration from its Mackup configuration", Args: cobra.MinimumNArgs(1), RunE: c.makeRunEWithSourceState(c.runMackupAddCmd), Annotations: newAnnotations( createSourceDirectoryIfNeeded, modifiesSourceDirectory, persistentStateModeReadWrite, requiresSourceDirectory, ), } mackupAddCmd.Flags().Var(c.Add.Secrets, "secrets", "Scan for secrets when adding unencrypted files") must(mackupAddCmd.RegisterFlagCompletionFunc("secrets", c.Add.Secrets.FlagCompletionFunc())) mackupCmd.AddCommand(mackupAddCmd) return mackupCmd } func (c *Config) runMackupAddCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { mackupApplicationsDir, err := c.mackupApplicationsDir() if err != nil { return err } mackupDirAbsPath := c.homeDirAbsPath.JoinString(".mackup") var addArgs []string for _, arg := range args { configRelPath := chezmoi.NewRelPath(arg + ".cfg") data, err := c.baseSystem.ReadFile(mackupDirAbsPath.Join(configRelPath)) if errors.Is(err, fs.ErrNotExist) { data, err = c.baseSystem.ReadFile(mackupApplicationsDir.Join(configRelPath)) } if err != nil { return err } config := parseMackupApplication(data) for _, filename := range config.ConfigurationFiles { addArg := c.DestDirAbsPath.Join(filename) addArgs = append(addArgs, addArg.String()) } configHomeAbsPath := chezmoi.NewAbsPath(c.bds.ConfigHome) for _, filename := range config.XDGConfigurationFiles { addArg := configHomeAbsPath.Join(filename) addArgs = append(addArgs, addArg.String()) } } destAbsPathInfos, err := c.destAbsPathInfos(sourceState, addArgs, destAbsPathInfosOptions{ follow: c.Add.follow, onIgnoreFunc: c.defaultOnIgnoreFunc, onNotExist: onNotExistIgnore, recursive: c.Add.recursive, }) if err != nil { return err } return sourceState.Add( c.sourceSystem, c.persistentState, c.destSystem, destAbsPathInfos, &chezmoi.AddOptions{ Errorf: c.errorf, Filter: chezmoi.NewEntryTypeFilter(chezmoi.EntryTypesAll, chezmoi.EntryTypesNone), OnIgnoreFunc: c.defaultOnIgnoreFunc, PreAddFunc: c.defaultPreAddFunc, ReplaceFunc: c.defaultReplaceFunc, }, ) } func (c *Config) mackupApplicationsDir() (chezmoi.AbsPath, error) { mackupBinaryPath, err := chezmoi.LookPath("mackup") if err != nil { return chezmoi.EmptyAbsPath, err } mackupBinaryPathResolved, err := filepath.EvalSymlinks(mackupBinaryPath) if err != nil { return chezmoi.EmptyAbsPath, err } mackupBinaryAbsPath := chezmoi.NewAbsPath(mackupBinaryPathResolved) libDirAbsPath := mackupBinaryAbsPath.Dir().Dir().JoinString("lib") dirEntries, err := c.baseSystem.ReadDir(libDirAbsPath) if err != nil { return chezmoi.EmptyAbsPath, err } for _, dirEntry := range dirEntries { if !dirEntry.IsDir() || !strings.HasPrefix(dirEntry.Name(), "python") { continue } mackupApplicationsDirAbsPath := libDirAbsPath.JoinString(dirEntry.Name(), "site-packages", "mackup", "applications") if fileInfo, err := c.baseSystem.Stat(mackupApplicationsDirAbsPath); err == nil && fileInfo.IsDir() { return mackupApplicationsDirAbsPath, nil } } return chezmoi.EmptyAbsPath, fmt.Errorf("%s: mackup application directory not found", libDirAbsPath) } func parseMackupApplication(data []byte) mackupApplicationConfig { var config mackupApplicationConfig var section string for line := range strings.Lines(string(data)) { if mackupCommentRx.MatchString(line) { continue } if m := mackupSectionRx.FindStringSubmatch(line); m != nil { section = m[1] continue } line = strings.TrimSpace(line) if line == "" { continue } switch section { case "application": if m := mackupKeyValueRx.FindStringSubmatch(line); m != nil { if m[1] == "name" { config.Application.Name = m[2] } } case "configuration_files": config.ConfigurationFiles = append(config.ConfigurationFiles, chezmoi.NewRelPath(line)) case "xdg_configuration_files": config.XDGConfigurationFiles = append(config.XDGConfigurationFiles, chezmoi.NewRelPath(line)) } } return config } ================================================ FILE: internal/cmd/mackupcmd_darwin_test.go ================================================ package cmd import ( "testing" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestParseMackupApplication(t *testing.T) { for _, tc := range []struct { name string lines []string expected mackupApplicationConfig }{ { name: "curl.cfg", lines: []string{ "[application]", "name = Curl", "", "[configuration_files]", ".netrc", ".curlrc", }, expected: mackupApplicationConfig{ Application: mackupApplicationApplicationConfig{ Name: "Curl", }, ConfigurationFiles: []chezmoi.RelPath{ chezmoi.NewRelPath(".netrc"), chezmoi.NewRelPath(".curlrc"), }, }, }, { name: "vscode.cfg", lines: []string{ "[application]", "name = Visual Studio Code", "", "[configuration_files]", "Library/Application Support/Code/User/snippets", "Library/Application Support/Code/User/keybindings.json", "Library/Application Support/Code/User/settings.json", "", "[xdg_configuration_files]", "Code/User/snippets", "Code/User/keybindings.json", "Code/User/settings.json", }, expected: mackupApplicationConfig{ Application: mackupApplicationApplicationConfig{ Name: "Visual Studio Code", }, ConfigurationFiles: []chezmoi.RelPath{ chezmoi.NewRelPath("Library/Application Support/Code/User/snippets"), chezmoi.NewRelPath("Library/Application Support/Code/User/keybindings.json"), chezmoi.NewRelPath("Library/Application Support/Code/User/settings.json"), }, XDGConfigurationFiles: []chezmoi.RelPath{ chezmoi.NewRelPath("Code/User/snippets"), chezmoi.NewRelPath("Code/User/keybindings.json"), chezmoi.NewRelPath("Code/User/settings.json"), }, }, }, } { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, parseMackupApplication([]byte(chezmoitest.JoinLines(tc.lines...)))) }) } } ================================================ FILE: internal/cmd/mackupcmd_nodarwin.go ================================================ //go:build !darwin package cmd import ( "github.com/spf13/cobra" ) func (c *Config) newMackupCmd() *cobra.Command { return nil } ================================================ FILE: internal/cmd/main_test.go ================================================ package cmd_test import ( "bytes" _ "embed" "encoding/hex" "errors" "flag" "fmt" "io/fs" "maps" "net/http" "net/http/httptest" "os" "path" "path/filepath" "regexp" "runtime" "slices" "strconv" "strings" "testing" "text/template" "time" "github.com/rogpeppe/go-internal/imports" "github.com/rogpeppe/go-internal/testscript" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoitest" "chezmoi.io/chezmoi/internal/cmd" ) var ( envConditionRx = regexp.MustCompile(`\Aenv:(\w+)\z`) envVarRx = regexp.MustCompile(`\$\w+`) umaskConditionRx = regexp.MustCompile(`\Aumask:([0-7]{3})\z`) filterRegex = flag.String("filter", "", "regex to filter test scripts") //go:embed mockcommand.tmpl mockcommandTmplText string //go:embed mockcommand.cmd.tmpl mockcommandCmdTmplText string ) func TestMain(m *testing.M) { testscript.Main(m, map[string]func(){ "chezmoi": func() { //nolint:revive os.Exit(cmd.Main(cmd.VersionInfo{ Version: "v2.0.0+test", Commit: "HEAD", Date: time.Now().UTC().Format(time.RFC3339), BuiltBy: "testscript", }, os.Args[1:])) }, }) } func TestScript(t *testing.T) { if testing.Short() { t.Skip("skipping testscript tests in short mode") } files, err := filepath.Glob("testdata/scripts/*.txtar") if err != nil { t.Fatalf("failed to glob files: %v", err) } if *filterRegex != "" { re, err := regexp.Compile(*filterRegex) if err != nil { t.Fatalf("invalid regex %q: %v", *filterRegex, err) } var filteredFiles []string for _, f := range files { baseName := strings.Split(filepath.Base(f), ".")[0] if re.MatchString(baseName) { filteredFiles = append(filteredFiles, f) } } files = filteredFiles if len(files) == 0 { t.Fatalf("no test scripts match regex %q", *filterRegex) } } // Use dependency injection for interpreter selection. findExecutableMock := func(files, paths []string) (string, error) { home := os.Getenv("HOME") if strings.Contains(home, "home_none") { return "", nil } if strings.Contains(home, "home_pwsh") { if slices.Contains(files, "pwsh.exe") || slices.Contains(files, "pwsh") { return "/usr/bin/pwsh", nil } } if strings.Contains(home, "home_powershell") { if slices.Contains(files, "powershell.exe") || slices.Contains(files, "powershell") { return "/usr/bin/powershell", nil } } switch os.Getenv("CHEZMOI_TEST_INTERPRETER") { case "pwsh": if slices.Contains(files, "pwsh.exe") || slices.Contains(files, "pwsh") { return "C:\\Program Files\\PowerShell\\7\\pwsh.exe", nil } case "powershell": if slices.Contains(files, "powershell.exe") || slices.Contains(files, "powershell") { return "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", nil } case "none": return "", nil } // fallback to the real implementation return chezmoi.FindExecutable(files, paths) } // Override defaultInterpreters for the test. cmd.DefaultInterpreters = cmd.NewDefaultInterpreters(findExecutableMock) if strings.Contains(os.Getenv("HOME"), "home_none") { cmd.DefaultInterpreters["ps1"] = chezmoi.Interpreter{} } testscript.Run(t, testscript.Params{ Files: files, Cmds: map[string]func(*testscript.TestScript, bool, []string){ "appendline": cmdAppendLine, "chhome": cmdChHome, "cmpmod": cmdCmpMod, "edit": cmdEdit, "expandenv": cmdExpandEnv, "hexdecode": cmdHexDecode, "httpd": cmdHTTPD, "isdir": cmdIsDir, "issymlink": cmdIsSymlink, "lexists": cmdLExists, "mkfile": cmdMkFile, "mkageconfig": cmdMkAgeConfig, "mkgitconfig": cmdMkGitConfig, "mkgpgconfig": cmdMkGPGConfig, "mkhomedir": cmdMkHomeDir, "mksourcedir": cmdMkSourceDir, "mockcommand": cmdMockCommand, "modifyfile": cmdModifyFile, "prependline": cmdPrependLine, "readlink": cmdReadLink, "removeline": cmdRemoveLine, "rmdir": cmdRmDir, "rmfinalnewline": cmdRmFinalNewline, "sleep": cmdSleep, "unix2dos": cmdUNIX2DOS, }, Condition: func(cond string) (bool, error) { if result, valid := goosCondition(cond); valid { return result, nil } if m := envConditionRx.FindStringSubmatch(cond); m != nil { return os.Getenv(m[1]) != "", nil } if m := umaskConditionRx.FindStringSubmatch(cond); m != nil { umask, _ := strconv.ParseInt(m[1], 8, 64) return chezmoitest.Umask == fs.FileMode(umask), nil } return false, fmt.Errorf("%s: unknown condition", cond) }, RequireExplicitExec: true, RequireUniqueNames: true, Setup: setup, }) } // cmdAppendLine appends lines to a file. func cmdAppendLine(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! appendline") } if len(args) != 2 { ts.Fatalf("usage: appendline file line") } filename := ts.MkAbs(args[0]) data, err := os.ReadFile(filename) ts.Check(err) data = append(data, append([]byte(args[1]), '\n')...) ts.Check(os.WriteFile(filename, data, 0o666)) } // cmdChHome changes the home directory to its argument, creating the directory // if it does not already exist. It updates the HOME environment variable, and, // if running on Windows, USERPROFILE too. func cmdChHome(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! chhome") } if len(args) != 1 { ts.Fatalf("usage: chhome dir") } var ( homeDir = ts.MkAbs(args[0]) chezmoiCacheDir = path.Join(homeDir, ".cache", "chezmoi") chezmoiConfigDir = path.Join(homeDir, ".config", "chezmoi") chezmoiSourceDir = path.Join(homeDir, ".local", "share", "chezmoi") ) ts.Check(os.MkdirAll(homeDir, fs.ModePerm)) ts.Setenv("HOME", homeDir) ts.Setenv("CHEZMOICACHEDIR", chezmoiCacheDir) ts.Setenv("CHEZMOICONFIGDIR", chezmoiConfigDir) ts.Setenv("CHEZMOISOURCEDIR", chezmoiSourceDir) if runtime.GOOS == "windows" { ts.Setenv("USERPROFILE", homeDir) } } // cmdCmpMod compares modes. func cmdCmpMod(ts *testscript.TestScript, neg bool, args []string) { if len(args) != 2 { ts.Fatalf("usage: cmpmod mode path") } mode64, err := strconv.ParseUint(args[0], 8, 32) if err != nil || fs.FileMode(mode64).Perm() != fs.FileMode(mode64) { ts.Fatalf("invalid mode: %s", args[0]) } if runtime.GOOS == "windows" { return } fileInfo, err := os.Stat(ts.MkAbs(args[1])) if err != nil { ts.Fatalf("%s: %v", args[1], err) } equal := fileInfo.Mode().Perm() == fs.FileMode(mode64)&^chezmoitest.Umask if neg && equal { ts.Fatalf("%s unexpectedly has mode %03o", args[1], fileInfo.Mode().Perm()) } if !neg && !equal { format := "%s has mode %03o, expected %03o" ts.Fatalf(format, args[1], fileInfo.Mode().Perm(), fs.FileMode(mode64)&^chezmoitest.Umask) } } // cmdEdit edits all of its arguments by appending "# edited\n" to them. func cmdEdit(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! edit") } for _, arg := range args { filename := ts.MkAbs(arg) data, err := os.ReadFile(filename) if err != nil { ts.Fatalf("edit: %v", err) } data = append(data, []byte("# edited\n")...) if err := os.WriteFile(filename, data, 0o666); err != nil { ts.Fatalf("edit: %v", err) } } } // cmdExpandEnv expands environment variables in the given paths. func cmdExpandEnv(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! expandenv") } if len(args) == 0 { ts.Fatalf("usage: expandenv paths...") } for _, arg := range args { filename := ts.MkAbs(arg) data, err := os.ReadFile(filename) if err != nil { ts.Fatalf("%s: %v", filename, err) } data = envVarRx.ReplaceAllFunc(data, func(key []byte) []byte { if value := ts.Getenv(string(bytes.TrimPrefix(key, []byte{'$'}))); value != "" { return []byte(value) } return key }) if err := os.WriteFile(filename, data, 0o666); err != nil { ts.Fatalf("%s: %v", filename, err) } } } // cmdHTTPD starts an HTTP server serving files from the given directory and // sets the HTTPD_URL environment variable to the URL of the server. func cmdHTTPD(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! httpd") } if len(args) != 1 { ts.Fatalf("usage: httpd dir") } dir := ts.MkAbs(args[0]) server := httptest.NewServer(http.FileServer(http.Dir(dir))) ts.Setenv("HTTPD_URL", server.URL) } // cmdHexDecode decodes each argument, which must be a file with the extension // .hex, writing the result to the same path but without the .hex extension. func cmdHexDecode(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! hexdecode") } for _, arg := range args { filename := ts.MkAbs(arg) if filepath.Ext(filename) != ".hex" { ts.Fatalf("%s: no .hex extension", arg) } hexData, err := os.ReadFile(filename) if err != nil { ts.Fatalf("%s: %v", arg, err) } hexData = regexp.MustCompile(`(?m)#.*$`).ReplaceAll(hexData, nil) hexData = regexp.MustCompile(`\s+`).ReplaceAll(hexData, nil) data := make([]byte, hex.DecodedLen(len(hexData))) if _, err := hex.Decode(data, hexData); err != nil { ts.Fatalf("%s: %v", arg, err) } if err := os.WriteFile(strings.TrimSuffix(filename, ".hex"), data, 0o666); err != nil { ts.Fatalf("%s: %v", arg, err) } ts.Check(os.Remove(filename)) } } // cmdIsDir succeeds if all of its arguments are directories. func cmdIsDir(ts *testscript.TestScript, neg bool, args []string) { for _, arg := range args { filename := ts.MkAbs(arg) fileInfo, err := os.Lstat(filename) if err != nil { ts.Fatalf("%s: %v", arg, err) } switch isDir := fileInfo.IsDir(); { case isDir && neg: ts.Fatalf("%s is a directory", arg) case !isDir && !neg: ts.Fatalf("%s is not a directory", arg) } } } // cmdIsSymlink succeeds if all of its arguments are symlinks. func cmdIsSymlink(ts *testscript.TestScript, neg bool, args []string) { for _, arg := range args { filename := ts.MkAbs(arg) fileInfo, err := os.Lstat(filename) if err != nil { ts.Fatalf("%s: %v", arg, err) } switch isSymlink := fileInfo.Mode().Type() == fs.ModeSymlink; { case isSymlink && neg: ts.Fatalf("%s is a symlink", arg) case !isSymlink && !neg: ts.Fatalf("%s is not a symlink", arg) } } } // cmdLExists succeeds if all if its arguments exist, without following // symlinks. func cmdLExists(ts *testscript.TestScript, neg bool, args []string) { if len(args) == 0 { ts.Fatalf("usage: exists file...") } for _, arg := range args { filename := ts.MkAbs(arg) switch _, err := os.Lstat(filename); { case err == nil && neg: ts.Fatalf("%s unexpectedly exists", filename) case errors.Is(err, fs.ErrNotExist) && !neg: ts.Fatalf("%s does not exist", filename) } } } // cmdMkFile creates empty files. func cmdMkFile(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! mkfile") } perm := fs.FileMode(0o666) if len(args) >= 1 && strings.HasPrefix(args[0], "-perm=") { permStr := strings.TrimPrefix(args[0], "-perm=") permUint32, err := strconv.ParseUint(permStr, 8, 32) if err != nil { ts.Fatalf("%s: bad permissions", permStr) } perm = fs.FileMode(permUint32) args = args[1:] } for _, arg := range args { filename := ts.MkAbs(arg) switch _, err := os.Lstat(filename); { case err == nil: ts.Fatalf("%s: already exists", arg) case !errors.Is(err, fs.ErrNotExist): ts.Fatalf("%s: %v", arg, err) } if err := writeNewFile(filename, nil, perm); err != nil { ts.Fatalf("%s: %v", arg, err) } } } // cmdMkAgeConfig creates an age key and a chezmoi configuration file. func cmdMkAgeConfig(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! mkageconfig") } if len(args) > 1 || len(args) == 1 && args[0] != "-symmetric" { ts.Fatalf("usage: mkageconfig [-symmetric]") } symmetric := len(args) == 1 && args[0] == "-symmetric" homeDir := ts.Getenv("HOME") ts.Check(os.MkdirAll(homeDir, fs.ModePerm)) identityFile := filepath.Join(homeDir, "key.txt") recipient, err := chezmoitest.AgeGenerateKey("age", ts.MkAbs(identityFile)) ts.Check(err) configFile := filepath.Join(homeDir, ".config", "chezmoi", "chezmoi.toml") ts.Check(os.MkdirAll(filepath.Dir(configFile), fs.ModePerm)) lines := []string{ `encryption = "age"`, `[age]`, ` identity = ` + strconv.Quote(identityFile), } if symmetric { lines = append(lines, ` symmetric = true`) } else { lines = append(lines, ` recipient = `+strconv.Quote(recipient.String())) } ts.Check(writeNewFile(configFile, []byte(chezmoitest.JoinLines(lines...)), 0o666)) } // cmdMkGitConfig makes a .gitconfig file in the home directory. func cmdMkGitConfig(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! mkgitconfig") } if len(args) > 1 { ts.Fatalf(("usage: mkgitconfig [path]")) } gitconfigPath := filepath.Join(ts.Getenv("HOME"), ".gitconfig") if len(args) > 0 { gitconfigPath = ts.MkAbs(args[0]) } ts.Check(os.MkdirAll(filepath.Dir(gitconfigPath), fs.ModePerm)) ts.Check(writeNewFile(gitconfigPath, []byte(chezmoitest.JoinLines( `[core]`, ` autocrlf = false`, `[init]`, ` defaultBranch = master`, `[user]`, ` name = User`, ` email = user@example.com`, )), 0o666)) } // cmdMkGPGConfig creates a GPG key and a chezmoi configuration file. func cmdMkGPGConfig(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! mkgpgconfig") } if len(args) > 1 || len(args) == 1 && args[0] != "-symmetric" { ts.Fatalf("usage: mkgpgconfig [-symmetric]") } symmetric := len(args) == 1 && args[0] == "-symmetric" // Create a new directory for GPG. We can't use a subdirectory of the // testscript's working directory because on darwin the absolute path can // exceed GPG's limit of sockaddr_un.sun_path (107 characters, see man // unix(7)). The limit exists because GPG creates a UNIX domain socket in // its home directory and UNIX domain socket paths are limited to // sockaddr_un.sun_path characters. gpgHomeDir, err := os.MkdirTemp("", "test-gpg-homedir") ts.Check(err) ts.Defer(func() { os.RemoveAll(gpgHomeDir) }) if runtime.GOOS != "windows" { ts.Check(os.Chmod(gpgHomeDir, 0o700)) } command, err := chezmoi.LookPath("gpg") ts.Check(err) key, passphrase, err := chezmoitest.GPGGenerateKey(command, gpgHomeDir) ts.Check(err) configFile := filepath.Join(ts.Getenv("HOME"), ".config", "chezmoi", "chezmoi.toml") ts.Check(os.MkdirAll(filepath.Dir(configFile), fs.ModePerm)) lines := []string{ `encryption = "gpg"`, `[gpg]`, ` args = [`, ` "--homedir", ` + strconv.Quote(gpgHomeDir) + `,`, ` "--no-tty",`, ` "--passphrase", ` + strconv.Quote(passphrase) + `,`, ` "--pinentry-mode", "loopback",`, ` ]`, } if symmetric { lines = append(lines, ` symmetric = true`) } else { lines = append(lines, ` recipient = "`+key+`"`) } ts.Check(writeNewFile(configFile, []byte(chezmoitest.JoinLines(lines...)), 0o666)) } // cmdMkHomeDir makes and populates a home directory. func cmdMkHomeDir(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! mkhomedir") } if len(args) > 1 { ts.Fatalf(("usage: mkhomedir [path]")) } homeDir := ts.Getenv("HOME") if len(args) > 0 { homeDir = ts.MkAbs(args[0]) } workDir := ts.Getenv("WORK") relPath, err := filepath.Rel(workDir, homeDir) ts.Check(err) if err := vfst.NewBuilder().Build(vfs.NewPathFS(vfs.OSFS, workDir), map[string]any{ relPath: map[string]any{ ".create": "# contents of .create\n", ".dir": map[string]any{ "file": "# contents of .dir/file\n", "subdir": map[string]any{ "file": "# contents of .dir/subdir/file\n", }, }, ".empty": "", ".executable": &vfst.File{ Perm: fs.ModePerm, Contents: []byte("# contents of .executable\n"), }, ".file": "# contents of .file\n", ".private": &vfst.File{ Perm: 0o600, Contents: []byte("# contents of .private\n"), }, ".readonly": &vfst.File{ Perm: 0o444, Contents: []byte("# contents of .readonly\n"), }, ".symlink": &vfst.Symlink{Target: ".dir/subdir/file"}, ".template": "key = value\n", }, }); err != nil { ts.Fatalf("mkhomedir: %v", err) } } // cmdMkSourceDir makes and populates a source directory. func cmdMkSourceDir(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! mksourcedir") } if len(args) > 1 { ts.Fatalf("usage: mksourcedir [path]") } sourceDir := ts.Getenv("CHEZMOISOURCEDIR") if len(args) > 0 { sourceDir = ts.MkAbs(args[0]) } workDir := ts.Getenv("WORK") relPath, err := filepath.Rel(workDir, sourceDir) ts.Check(err) err = vfst.NewBuilder().Build(vfs.NewPathFS(vfs.OSFS, workDir), map[string]any{ relPath: map[string]any{ "create_dot_create": "# contents of .create\n", "dot_dir": map[string]any{ "file": "# contents of .dir/file\n", "exact_subdir": map[string]any{ "file": "# contents of .dir/subdir/file\n", }, }, "dot_remove": "", "empty_dot_empty": "", "executable_dot_executable": "# contents of .executable\n", "dot_file": "# contents of .file\n", "private_dot_private": "# contents of .private\n", "readonly_dot_readonly": "# contents of .readonly\n", "symlink_dot_symlink": ".dir/subdir/file\n", "dot_template.tmpl": chezmoitest.JoinLines( `key = {{ "value" }}`, ), }, }) if err != nil { ts.Fatalf("mksourcedir: %v", err) } } // cmdMockCommand creates a mock command from a definition. func cmdMockCommand(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! mockcommand") } if len(args) != 1 { ts.Fatalf("usage: mockcommand command") } command := ts.MkAbs(args[0]) definitionYAML, err := os.ReadFile(command + ".yaml") ts.Check(err) // Parse the definition. type response struct { Args string `yaml:"args"` OrArgs []string `yaml:"orArgs"` WindowsArgs string `yaml:"windowsArgs"` RequireEnv map[string]string `yaml:"requireEnv"` Response string `yaml:"response"` Destination string `yaml:"destination"` EscapeChars bool `yaml:"escapeChars"` SuppressLastNewline bool `yaml:"suppressLastNewline"` ExitCode int `yaml:"exitCode"` } var definition struct { Responses []response `yaml:"responses"` Default response `yaml:"default"` } ts.Check(chezmoi.FormatYAML.Unmarshal(definitionYAML, &definition)) // Parse the mock command template. var templateName, templateText string var renderResponseFunc func(response) string switch runtime.GOOS { case "windows": templateName = "mockcommand.cmd.tmpl" templateText = mockcommandCmdTmplText escapeCharsRx := regexp.MustCompile(`[\\&|><^]`) escapeChars := func(s string) string { return escapeCharsRx.ReplaceAllString(s, "^$0") } renderResponseFunc = func(r response) string { var builder strings.Builder for _, key := range slices.Sorted(maps.Keys(r.RequireEnv)) { value := r.RequireEnv[key] fmt.Fprintf(&builder, " IF NOT \"%%%s%%\" == \"%s\" (\n", key, value) fmt.Fprintf(&builder, " echo.%s=%%%s%%, expected %s\n", key, key, value) fmt.Fprint(&builder, " exit /b 1\n") fmt.Fprint(&builder, " )\n") } var redirect string if r.Destination == "stderr" { redirect = " 1>&2" } lines := strings.Split(strings.TrimSuffix(r.Response, "\n"), "\n") for i, line := range lines { if r.EscapeChars { line = escapeChars(line) } if r.SuppressLastNewline && i == len(lines)-1 { fmt.Fprintf(&builder, " echo | set /p=%s%s\n", line, redirect) } else { fmt.Fprintf(&builder, " echo.%s%s\n", line, redirect) } } fmt.Fprintf(&builder, " exit /b %d", r.ExitCode) return builder.String() } default: templateName = "mockcommand.tmpl" templateText = mockcommandTmplText renderResponseFunc = func(r response) string { var builder strings.Builder for _, key := range slices.Sorted(maps.Keys(r.RequireEnv)) { value := r.RequireEnv[key] fmt.Fprintf(&builder, " if [ \"${%s}\" != \"%s\" ]; then\n", key, value) fmt.Fprintf(&builder, " echo \"%s=${%s}, expected %s\"\n", key, key, value) fmt.Fprint(&builder, " exit 1\n") fmt.Fprint(&builder, " fi\n") } var redirect string if r.Destination == "stderr" { redirect = " 1>&2" } if strings.Contains(r.Response, "\n") { fmt.Fprintf(&builder, " cat%s < %s, expected %s", args[0], link, args[1]) case neg && link == args[1]: ts.Fatalf("readlink: %s -> %s, expected ! %s", args[0], link, args[1]) } } // cmdRemoveLine removes lines matching line from file, which must be present. func cmdRemoveLine(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! removeline") } if len(args) != 2 { ts.Fatalf("usage: removeline file line") } filename := ts.MkAbs(args[0]) data, err := os.ReadFile(filename) ts.Check(err) lineSlice := []byte(args[1]) lines := bytes.Split(data, []byte{'\n'}) n := 0 for _, line := range lines { if bytes.Equal(line, lineSlice) { continue } lines[n] = line n++ } if n == len(lines) { ts.Fatalf("removeline: %q not found in %s", args[1], args[0]) } data = append(bytes.Join(lines[:n], []byte{'\n'}), '\n') ts.Check(os.WriteFile(filename, data, 0o666)) } // cmdRmDir removes directories. func cmdRmDir(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! rmdir") } if len(args) < 1 { ts.Fatalf("usage: rmdir paths...") } for _, path := range args { ts.Check(os.RemoveAll(ts.MkAbs(path))) } } // cmdRmFinalNewline removes final newlines. func cmdRmFinalNewline(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! rmfinalnewline") } if len(args) < 1 { ts.Fatalf("usage: rmfinalnewline paths...") } for _, arg := range args { filename := ts.MkAbs(arg) data, err := os.ReadFile(filename) if err != nil { ts.Fatalf("%s: %v", filename, err) } if len(data) == 0 || data[len(data)-1] != '\n' { continue } if err := os.WriteFile(filename, data[:len(data)-1], 0o666); err != nil { ts.Fatalf("%s: %v", filename, err) } } } // cmdSleep sleeps. func cmdSleep(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! sleep") } if len(args) < 1 { ts.Fatalf("usage: sleep duration") } duration, err := time.ParseDuration(args[0]) ts.Check(err) time.Sleep(duration) } // cmdUNIX2DOS converts files from UNIX line endings to DOS line endings. func cmdUNIX2DOS(ts *testscript.TestScript, neg bool, args []string) { if neg { ts.Fatalf("unsupported: ! unix2dos") } if len(args) < 1 { ts.Fatalf("usage: unix2dos paths...") } for _, arg := range args { filename := ts.MkAbs(arg) data, err := os.ReadFile(filename) ts.Check(err) dosData, err := unix2DOS(data) ts.Check(err) if err := os.WriteFile(filename, dosData, 0o666); err != nil { ts.Fatalf("%s: %v", filename, err) } } } // goosCondition evaluates cond as a logical OR of GOARCHes or GOOSes enclosed // in parentheses, returning true if any of them match. func goosCondition(cond string) (result, valid bool) { // Interpret the condition as a logical OR of terms in parentheses. if !strings.HasPrefix(cond, "(") || !strings.HasSuffix(cond, ")") { result = false valid = false return result, valid } cond = strings.TrimPrefix(cond, "(") cond = strings.TrimSuffix(cond, ")") terms := strings.Split(cond, "||") // If any of the terms are neither known GOOSes nor GOARCHes then reject the // condition as invalid. for _, term := range terms { if term == "unix" { continue } if _, ok := imports.KnownOS[term]; !ok { if _, ok := imports.KnownArch[term]; !ok { valid = false return result, valid } } } // At this point, we know the expression is valid. valid = true // If any of the terms match either runtime.GOOS or runtime.GOARCH then // the condition is true. for _, term := range terms { switch { case term == runtime.GOOS || term == "unix" && imports.UnixOS[runtime.GOOS]: result = true return result, valid case term == runtime.GOARCH: result = true return result, valid } } // Otherwise, the condition is false. result = false return result, valid } func prependDirToPath(dir, pathStr string) string { return strings.Join(append([]string{dir}, filepath.SplitList(pathStr)...), string(os.PathListSeparator)) } func setup(env *testscript.Env) error { var ( binDir = filepath.Join(env.WorkDir, "bin") homeDir = filepath.Join(env.WorkDir, "home", "user") ) absHomeDir, err := filepath.Abs(homeDir) if err != nil { return err } absSlashHomeDir := filepath.ToSlash(absHomeDir) env.Setenv("HOME", homeDir) env.Setenv("PATH", prependDirToPath(binDir, env.Getenv("PATH"))) if runtime.GOOS == "windows" { env.Setenv("PATHEXT", ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.CPL") } env.Setenv("CHEZMOICACHEDIR", path.Join(absSlashHomeDir, ".cache", "chezmoi")) env.Setenv("CHEZMOICONFIGDIR", path.Join(absSlashHomeDir, ".config", "chezmoi")) env.Setenv("CHEZMOISOURCEDIR", path.Join(absSlashHomeDir, ".local", "share", "chezmoi")) env.Setenv("CHEZMOI_GITHUB_TOKEN", os.Getenv("CHEZMOI_GITHUB_TOKEN")) switch runtime.GOOS { case "windows": env.Setenv("EDITOR", filepath.Join(binDir, "editor.cmd")) env.Setenv("USERPROFILE", homeDir) // There is not currently a convenient way to override the shell on // Windows. default: env.Setenv("EDITOR", filepath.Join(binDir, "editor")) env.Setenv("SHELL", filepath.Join(binDir, "shell")) } root := make(map[string]any) switch runtime.GOOS { case "windows": root["/bin"] = map[string]any{ // editor.cmd is a non-interactive script that appends "# edited\n" // to the end of each file and creates an empty .edited file in each // directory. "editor.cmd": &vfst.File{ Contents: []byte(chezmoitest.JoinLines( `@echo off`, `:loop`, `IF EXIST %~s1\NUL (`, ` copy /y NUL "%~1\.edited" >NUL`, // FIXME recursively edit all files if in a directory `) ELSE (`, ` echo.# edited>>"%~1"`, `)`, `shift`, `IF NOT "%~1"=="" goto loop`, )), }, } default: root["/bin"] = map[string]any{ // editor is a non-interactive script that appends "# edited\n" to // the end of each file. "editor": &vfst.File{ Perm: 0o755, Contents: []byte(chezmoitest.JoinLines( `#!/bin/sh`, ``, `for name in $*; do`, ` if [ -d $name ]; then`, ` touch $name/.edited`, ` for filename in $(find $name -type f); do`, ` echo "# edited" >> $filename`, ` done`, ` else`, ` echo "# edited" >> $name`, ` fi`, `done`, )), }, } } return vfst.NewBuilder().Build(vfs.NewPathFS(vfs.OSFS, env.WorkDir), root) } // unix2DOS returns data with UNIX line endings converted to DOS line endings. func unix2DOS(data []byte) ([]byte, error) { builder := strings.Builder{} for line := range bytes.Lines(data) { if _, err := builder.Write(line); err != nil { return nil, err } if _, err := builder.WriteString("\r\n"); err != nil { return nil, err } } return []byte(builder.String()), nil } func writeNewFile(filename string, data []byte, perm fs.FileMode) error { switch _, err := os.Lstat(filename); { case err == nil: return fmt.Errorf("%s: %w", filename, fs.ErrExist) case errors.Is(err, fs.ErrNotExist): return os.WriteFile(filename, data, perm) default: return err } } ================================================ FILE: internal/cmd/managedcmd.go ================================================ package cmd import ( "cmp" "fmt" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type managedCmdConfig struct { filter *chezmoi.EntryTypeFilter format *choiceFlag nulPathSeparator bool pathStyle *choiceFlag tree bool } func (c *Config) newManagedCmd() *cobra.Command { managedCmd := &cobra.Command{ GroupID: groupIDAdvanced, Use: "managed [path]...", Aliases: []string{"list"}, Short: "List the managed entries in the destination directory", Long: mustLongHelp("managed"), Example: example("managed"), Args: cobra.ArbitraryArgs, RunE: c.makeRunEWithSourceState(c.runManagedCmd), Annotations: newAnnotations( persistentStateModeReadWrite, ), } managedCmd.Flags().VarP(c.managed.filter.Exclude, "exclude", "x", "Exclude entry types") managedCmd.Flags().VarP(c.managed.format, "format", "f", "Format") managedCmd.Flags().VarP(c.managed.filter.Include, "include", "i", "Include entry types") managedCmd.Flags(). BoolVarP(&c.managed.nulPathSeparator, "nul-path-separator", "0", c.managed.nulPathSeparator, "Use the NUL character as a path separator") managedCmd.Flags().VarP(c.managed.pathStyle, "path-style", "p", "Path style") must(managedCmd.RegisterFlagCompletionFunc("path-style", c.managed.pathStyle.FlagCompletionFunc())) managedCmd.Flags().BoolVarP(&c.managed.tree, "tree", "t", c.managed.tree, "Print paths as a tree") return managedCmd } func (c *Config) runManagedCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { // Build queued relPaths. When there are no arguments, start from root, // otherwise start from arguments. relPaths := make([]chezmoi.RelPath, len(args)) for i, arg := range args { absPath, err := chezmoi.NormalizePath(arg) if err != nil { return err } relPaths[i], err = absPath.TrimDirPrefix(c.DestDirAbsPath) if err != nil { return err } } type entryPaths struct { targetRelPath chezmoi.RelPath Absolute chezmoi.AbsPath `json:"absolute" yaml:"absolute"` SourceAbsolute chezmoi.AbsPath `json:"sourceAbsolute" yaml:"sourceAbsolute"` SourceRelative chezmoi.SourceRelPath `json:"sourceRelative" yaml:"sourceRelative"` } var allEntryPaths []*entryPaths _ = sourceState.ForEach( func(targetRelPath chezmoi.RelPath, sourceStateEntry chezmoi.SourceStateEntry) error { if !c.managed.filter.IncludeSourceStateEntry(sourceStateEntry) { return nil } targetStateEntry, err := sourceStateEntry.TargetStateEntry(c.destSystem, c.DestDirAbsPath.Join(targetRelPath)) if err != nil { return err } if !c.managed.filter.IncludeTargetStateEntry(targetStateEntry) { return nil } // When arguments are given, only include paths under these arguments. if len(relPaths) != 0 { included := false for _, path := range relPaths { if targetRelPath.HasDirPrefix(path) || targetRelPath.String() == path.String() { included = true break } } if !included { return nil } } entryPaths := &entryPaths{ targetRelPath: targetRelPath, Absolute: c.DestDirAbsPath.Join(targetRelPath), SourceAbsolute: c.SourceDirAbsPath.Join(sourceStateEntry.SourceRelPath().RelPath()), SourceRelative: sourceStateEntry.SourceRelPath(), } allEntryPaths = append(allEntryPaths, entryPaths) return nil }, ) switch pathStyle := c.managed.pathStyle.String(); pathStyle { case pathStyleAbsolute, pathStyleRelative, pathStyleSourceAbsolute, pathStyleSourceRelative: paths := make([]string, len(allEntryPaths)) for i, structuredPath := range allEntryPaths { switch c.managed.pathStyle.String() { case pathStyleAbsolute: paths[i] = structuredPath.Absolute.String() case pathStyleRelative: paths[i] = structuredPath.targetRelPath.String() case pathStyleSourceAbsolute: paths[i] = structuredPath.SourceAbsolute.String() case pathStyleSourceRelative: paths[i] = structuredPath.SourceRelative.String() } } return c.writePaths(paths, writePathsOptions{ nulPathSeparator: c.managed.nulPathSeparator, tree: c.managed.tree, }) case pathStyleAll: allEntryPathsMap := make(map[string]*entryPaths, len(allEntryPaths)) for _, entryPaths := range allEntryPaths { allEntryPathsMap[entryPaths.targetRelPath.String()] = entryPaths } return c.marshal(cmp.Or(c.managed.format.String(), c.Format.String()), allEntryPathsMap) default: return fmt.Errorf("%s: invalid path style", pathStyle) } } ================================================ FILE: internal/cmd/managedcmd_test.go ================================================ package cmd import ( "bytes" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestManagedCmd(t *testing.T) { templateContents := `{{ fail "Template should not be executed" }}` for _, tc := range []struct { name string root any args []string expectedOutput string }{ { name: "simple", root: map[string]any{ "/home/user/.local/share/chezmoi/dot_file": "# contents of .file\n", }, expectedOutput: chezmoitest.JoinLines( ".file", ), }, { name: "nul_path_separator", root: map[string]any{ "/home/user/.local/share/chezmoi": map[string]any{ "dot_file1": "# contents of .file1\n", "dot_file2": "# contents of .file2\n", }, }, args: []string{ "-0", }, expectedOutput: ".file1\x00.file2\x00", }, { name: "template", root: map[string]any{ "/home/user/.local/share/chezmoi/dot_template.tmpl": templateContents, }, expectedOutput: chezmoitest.JoinLines( ".template", ), }, { name: "create_template", root: map[string]any{ "/home/user/.local/share/chezmoi/create_dot_file.tmpl": templateContents, }, expectedOutput: chezmoitest.JoinLines( ".file", ), }, { name: "modify_template", root: map[string]any{ "/home/user/.local/share/chezmoi/modify_dot_file.tmpl": templateContents, }, expectedOutput: chezmoitest.JoinLines( ".file", ), }, { name: "remove", root: map[string]any{ "/home/user": map[string]any{ ".local/share/chezmoi/.chezmoiremove": chezmoitest.JoinLines( ".remove", ), ".remove": "", }, }, expectedOutput: chezmoitest.JoinLines( ".remove", ), }, { name: "script_template", root: map[string]any{ "/home/user/.local/share/chezmoi/run_script.tmpl": templateContents, }, args: []string{ "--include", "always,scripts", }, expectedOutput: chezmoitest.JoinLines( "script", ), }, { name: "symlink_template", root: map[string]any{ "/home/user/.local/share/chezmoi/symlink_dot_symlink.tmpl": templateContents, }, expectedOutput: chezmoitest.JoinLines( ".symlink", ), }, { name: "external_git_repo", root: map[string]any{ "/home/user/.local/share/chezmoi/.chezmoiexternal.toml": chezmoitest.JoinLines( `[".dir"]`, ` type = "git-repo"`, ` url = "https://github.com/example/example.git"`, ), }, expectedOutput: chezmoitest.JoinLines( ".dir", ), }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { stdout := &bytes.Buffer{} config := newTestConfig(t, fileSystem, withStdout(stdout)) assert.NoError(t, config.execute(append([]string{"managed"}, tc.args...))) assert.Equal(t, tc.expectedOutput, stdout.String()) }) }) } } ================================================ FILE: internal/cmd/mergeallcmd.go ================================================ package cmd import ( "io/fs" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type mergeAllCmdConfig struct { init bool recursive bool } func (c *Config) newMergeAllCmd() *cobra.Command { mergeAllCmd := &cobra.Command{ GroupID: groupIDDaily, Use: "merge-all", Short: "Perform a three-way merge for each modified file", Long: mustLongHelp("merge-all"), Example: example("merge-all"), RunE: c.runMergeAllCmd, Annotations: newAnnotations( dryRun, modifiesSourceDirectory, persistentStateModeReadWrite, requiresSourceDirectory, ), } mergeAllCmd.Flags().BoolVar(&c.mergeAll.init, "init", c.mergeAll.init, "Recreate config file from template") mergeAllCmd.Flags().BoolVarP(&c.mergeAll.recursive, "recursive", "r", c.mergeAll.recursive, "Recurse into subdirectories") return mergeAllCmd } func (c *Config) runMergeAllCmd(cmd *cobra.Command, args []string) error { var targetRelPaths []chezmoi.RelPath preApplyFunc := func(targetRelPath chezmoi.RelPath, targetEntryState, lastWrittenEntryState, actualEntryState *chezmoi.EntryState) error { if targetEntryState.Type == chezmoi.EntryStateTypeFile && !targetEntryState.Equivalent(actualEntryState) { targetRelPaths = append(targetRelPaths, targetRelPath) } return fs.SkipDir } if err := c.applyArgs(cmd.Context(), c.destSystem, c.DestDirAbsPath, args, applyArgsOptions{ cmd: cmd, filter: chezmoi.NewEntryTypeFilter(chezmoi.EntryTypesAll, chezmoi.EntryTypesNone), init: c.mergeAll.init, recursive: c.mergeAll.recursive, umask: c.Umask, preApplyFunc: preApplyFunc, }); err != nil { return err } sourceState, err := c.getSourceState(cmd.Context(), cmd) if err != nil { return err } for _, targetRelPath := range targetRelPaths { sourceStateEntry := sourceState.MustEntry(targetRelPath) if err := c.doMerge(targetRelPath, sourceStateEntry); err != nil { return err } } return nil } ================================================ FILE: internal/cmd/mergecmd.go ================================================ package cmd import ( "fmt" "os" "strconv" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoierrors" ) type mergeCmdConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Args []string `json:"args" mapstructure:"args" yaml:"args"` } func (c *Config) newMergeCmd() *cobra.Command { mergeCmd := &cobra.Command{ GroupID: groupIDDaily, Use: "merge target...", Args: cobra.MinimumNArgs(1), Short: "Perform a three-way merge between the destination state, the source state, and the target state", Long: mustLongHelp("merge"), Example: example("merge"), ValidArgsFunction: c.targetValidArgs, RunE: c.makeRunEWithSourceState(c.runMergeCmd), Annotations: newAnnotations( modifiesSourceDirectory, requiresSourceDirectory, persistentStateModeReadWrite, ), } return mergeCmd } func (c *Config) runMergeCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { targetRelPaths, err := c.targetRelPaths(sourceState, args, targetRelPathsOptions{ mustBeInSourceState: true, recursive: true, }) if err != nil { return err } for _, targetRelPath := range targetRelPaths { sourceStateEntry := sourceState.MustEntry(targetRelPath) if err := c.doMerge(targetRelPath, sourceStateEntry); err != nil { return err } } return nil } // doMerge is the core merge functionality. It invokes the merge tool to do a // three-way merge between the destination, source, and target, including // transparently decrypting the file in the source state. func (c *Config) doMerge(targetRelPath chezmoi.RelPath, sourceStateEntry chezmoi.SourceStateEntry) (err error) { sourceAbsPath := c.SourceDirAbsPath.Join(sourceStateEntry.SourceRelPath().RelPath()) // If the source state entry is an encrypted file, then decrypt it to a // temporary directory and pass the plaintext to the merge command // instead. var plaintextAbsPath chezmoi.AbsPath if sourceStateFile, ok := sourceStateEntry.(*chezmoi.SourceStateFile); ok { if sourceStateFile.Attr().Encrypted { var plaintextTempDirAbsPath chezmoi.AbsPath if plaintextTempDirAbsPath, err = c.tempDir("chezmoi-merge-plaintext"); err != nil { return err } plaintextAbsPath = plaintextTempDirAbsPath.Join(sourceStateEntry.SourceRelPath().RelPath()) defer chezmoierrors.CombineFunc(&err, func() error { return os.RemoveAll(plaintextTempDirAbsPath.String()) }) var plaintext []byte if plaintext, err = sourceStateFile.Contents(); err != nil { return err } if err := chezmoi.MkdirAll(c.baseSystem, plaintextAbsPath.Dir(), 0o700); err != nil { return err } if err := c.baseSystem.WriteFile(plaintextAbsPath, plaintext, 0o600); err != nil { return err } sourceAbsPath = plaintextAbsPath } } // FIXME sourceStateEntry.TargetStateEntry eagerly evaluates the return // targetStateEntry's contents, which means that we cannot fallback to a // two-way merge if the source state's contents cannot be decrypted or // are an invalid template var targetStateEntry chezmoi.TargetStateEntry if targetStateEntry, err = sourceStateEntry.TargetStateEntry( c.destSystem, c.DestDirAbsPath.Join(targetRelPath), ); err != nil { return fmt.Errorf("%s: %w", targetRelPath, err) } targetStateFile, ok := targetStateEntry.(*chezmoi.TargetStateFile) if !ok { // LATER consider handling symlinks? return fmt.Errorf("%s: not a file", targetRelPath) } var contents []byte if contents, err = targetStateFile.Contents(); err != nil { return err } // Create a temporary directory to store the target state and ensure that it // is removed afterwards. var tempDirAbsPath chezmoi.AbsPath if tempDirAbsPath, err = c.tempDir("chezmoi-merge"); err != nil { return err } targetStateAbsPath := tempDirAbsPath.JoinString(targetRelPath.Base()) if err := c.baseSystem.WriteFile(targetStateAbsPath, contents, 0o600); err != nil { return err } templateData := struct { Destination string Source string Target string }{ Destination: c.DestDirAbsPath.Join(targetRelPath).String(), Source: sourceAbsPath.String(), Target: targetStateAbsPath.String(), } args := make([]string, 0, len(c.Merge.Args)) // Work around a regression introduced in 2.1.4 // (https://github.com/twpayne/chezmoi/pull/1324) in a user-friendly // way. // // Prior to #1324, the merge.args config option was prepended to the // default order of files to the merge command. Post #1324, the // merge.args config option replaced all arguments to the merge command. // // Work around this by looking for any templates in merge.args. An arg // is considered a template if, after execution as a template, it is // not equal to the original arg. anyTemplateArgs := false for i, arg := range c.Merge.Args { tmpl, err := chezmoi.ParseTemplate("merge.args["+strconv.Itoa(i)+"]", []byte(arg), chezmoi.TemplateOptions{ Funcs: c.templateFuncs, Options: c.Template.Options, }) if err != nil { return err } newArg, err := tmpl.ExecuteString(templateData) if err != nil { return err } args = append(args, newArg) // Detect template arguments. if newArg != arg { anyTemplateArgs = true } } // If there are no template arguments, then append the destination, // source, and target paths as prior to #1324. if !anyTemplateArgs { args = append(args, templateData.Destination, templateData.Source, templateData.Target) } if err := c.persistentState.Close(); err != nil { return err } if err = c.run(c.DestDirAbsPath, c.Merge.Command, args); err != nil { return fmt.Errorf("%s: %w", targetRelPath, err) } // If the source state entry was an encrypted file, then re-encrypt the // plaintext. if !plaintextAbsPath.IsEmpty() { var encryptedContents []byte if encryptedContents, err = c.encryption.EncryptFile(plaintextAbsPath); err != nil { return err } if err := c.baseSystem.WriteFile( c.SourceDirAbsPath.Join(sourceStateEntry.SourceRelPath().RelPath()), encryptedContents, 0o644, ); err != nil { return err } } return nil } ================================================ FILE: internal/cmd/mockcommand.cmd.tmpl ================================================ @echo off {{ range $index, $response := .Responses }} {{ if $index }}) ELSE {{ end }}IF "%*" == {{ $response.WindowsArgs | default $response.Args | quote }} ( {{ $response | renderResponse }} {{ range $_, $arg := $response.OrArgs }} ) ELSE IF "%*" == {{ $arg | quote }} ( {{ $response | renderResponse }} {{ end }} {{- end }} ) ELSE ( {{ .Default | renderResponse | replaceAll "$*" "%*" }} ) ================================================ FILE: internal/cmd/mockcommand.tmpl ================================================ #!/bin/sh {{ range $index, $response := .Responses }} {{ if $index }}elif{{ else }}if{{ end }} [ "$*" = {{ $response.Args | quote }} ]; then {{ $response | renderResponse }} {{ range $_, $arg := .OrArgs }} elif [ "$*" = {{ $arg | quote }} ]; then {{ $response | renderResponse }} {{ end }} {{- end }} else {{ .Default | renderResponse }} fi ================================================ FILE: internal/cmd/noupgradecmd.go ================================================ //go:build noupgrade package cmd import ( "github.com/spf13/cobra" "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoi" ) type upgradeCmdConfig struct { method string owner string repo string } func (c *Config) newUpgradeCmd() *cobra.Command { return nil } func getUpgradeMethod(vfs.FS, chezmoi.AbsPath) (string, error) { return "", nil } ================================================ FILE: internal/cmd/onepasswordtemplatefuncs.go ================================================ package cmd import ( "encoding/json" "errors" "fmt" "os" "os/exec" "regexp" "strings" "github.com/coreos/go-semver/semver" "chezmoi.io/chezmoi/internal/chezmoilog" ) type onepasswordMode string const ( onepasswordModeAccount onepasswordMode = "account" onepasswordModeConnect onepasswordMode = "connect" onepasswordModeService onepasswordMode = "service" ) type withSessionTokenType bool const ( withSessionToken withSessionTokenType = true withoutSessionToken withSessionTokenType = false ) var ( onepasswordVersionRx = regexp.MustCompile(`^(\d+\.\d+\.\d+\S*)`) onepasswordMinVersion = semver.Version{Major: 2} ) type onepasswordAccount struct { URL string `json:"url"` Email string `json:"email"` UserUUID string `json:"user_uuid"` //nolint:tagliatelle AccountUUID string `json:"account_uuid"` //nolint:tagliatelle Shorthand string `json:"shorthand"` } type onepasswordConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Prompt bool `json:"prompt" mapstructure:"prompt" yaml:"prompt"` Mode onepasswordMode `json:"mode" mapstructure:"mode" yaml:"mode"` outputCache map[string][]byte sessionTokens map[string]string accountMap map[string]string accountMapErr error modeChecked bool } type onepasswordArgs struct { item string vault string account string args []string } type onepasswordItem struct { Fields []map[string]any `json:"fields"` } func (c *Config) onepasswordTemplateFunc(userArgs ...string) map[string]any { must(c.onepasswordCheckMode()) args := mustValue(c.newOnepasswordArgs([]string{"item", "get", "--format", "json"}, userArgs)) output := mustValue(c.onepasswordOutput(args, withSessionToken)) var data map[string]any must(json.Unmarshal(output, &data)) return data } func (c *Config) onepasswordDetailsFieldsTemplateFunc(userArgs ...string) map[string]any { item := mustValue(c.onepasswordItem(userArgs)) result := make(map[string]any) for _, field := range item.Fields { if _, ok := field["section"]; ok { continue } if id, ok := field["id"].(string); ok && id != "" { result[id] = field continue } if label, ok := field["label"].(string); ok && label != "" { result[label] = field continue } } return result } func (c *Config) onepasswordDocumentTemplateFunc(userArgs ...string) string { must(c.onepasswordCheckMode()) if c.Onepassword.Mode == onepasswordModeConnect { panic(fmt.Errorf("onepasswordDocument cannot be used in %s mode", onepasswordModeConnect)) } args := mustValue(c.newOnepasswordArgs([]string{"document", "get"}, userArgs)) return string(mustValue(c.onepasswordOutput(args, withSessionToken))) } func (c *Config) onepasswordItemFieldsTemplateFunc(userArgs ...string) map[string]any { item := mustValue(c.onepasswordItem(userArgs)) result := make(map[string]any) for _, field := range item.Fields { if _, ok := field["section"]; !ok { continue } if label, ok := field["label"].(string); ok { result[label] = field } } return result } // onepasswordGetOrRefreshSessionToken will return the current session token if // the token within the environment is still valid. Otherwise it will ask the // user to sign in and get the new token. func (c *Config) onepasswordGetOrRefreshSessionToken(args *onepasswordArgs) (string, error) { if !c.Onepassword.Prompt { return "", nil } // Check if there's already a valid session token cached in this run for // this account. sessionToken, ok := c.Onepassword.sessionTokens[args.account] if ok { return sessionToken, nil } // If no account has been given then look for any session tokens in the // environment. if args.account == "" { sessionToken = onepasswordUniqueSessionToken(os.Environ()) if sessionToken != "" { return sessionToken, nil } } commandArgs := []string{"signin"} if args.account != "" { commandArgs = append(commandArgs, "--account", args.account) } commandArgs = append(commandArgs, "--raw") if session := os.Getenv("OP_SESSION_" + args.account); session != "" { commandArgs = append(commandArgs, "--session", session) } cmd := exec.Command(c.Onepassword.Command, commandArgs...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return "", newCmdOutputError(cmd, output, err) } sessionToken = strings.TrimSpace(string(output)) // Cache the session token in memory, so we don't try to refresh it again // for this run for this account. if c.Onepassword.sessionTokens == nil { c.Onepassword.sessionTokens = make(map[string]string) } c.Onepassword.sessionTokens[args.account] = sessionToken return sessionToken, nil } func (c *Config) onepasswordItem(userArgs []string) (*onepasswordItem, error) { args, err := c.newOnepasswordArgs([]string{"item", "get", "--format", "json"}, userArgs) if err != nil { return nil, err } output, err := c.onepasswordOutput(args, withSessionToken) if err != nil { return nil, err } var item onepasswordItem if err := json.Unmarshal(output, &item); err != nil { return nil, err } return &item, nil } func (c *Config) onepasswordOutput(args *onepasswordArgs, withSessionToken withSessionTokenType) ([]byte, error) { key := strings.Join(args.args, "\x00") if output, ok := c.Onepassword.outputCache[key]; ok { return output, nil } commandArgs := args.args if c.Onepassword.Mode == onepasswordModeAccount && withSessionToken { sessionToken, err := c.onepasswordGetOrRefreshSessionToken(args) if err != nil { return nil, err } if sessionToken != "" { commandArgs = append([]string{"--session", sessionToken}, commandArgs...) } } cmd := exec.Command(c.Onepassword.Command, commandArgs...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } if c.Onepassword.outputCache == nil { c.Onepassword.outputCache = make(map[string][]byte) } c.Onepassword.outputCache[key] = output return output, nil } func (c *Config) onepasswordReadTemplateFunc(url string, args ...string) string { must(c.onepasswordCheckMode()) onepasswordArgs := &onepasswordArgs{ args: []string{"read", "--no-newline", url}, } switch len(args) { case 0: // Do nothing. case 1: must(onepasswordCheckInvalidAccountParameters(c.Onepassword.Mode)) onepasswordArgs.account = c.onepasswordAccount(args[0]) onepasswordArgs.args = append(onepasswordArgs.args, "--account", onepasswordArgs.account) default: panic(fmt.Errorf("expected 1..2 arguments, got %d", len(args)+1)) } output := mustValue(c.onepasswordOutput(onepasswordArgs, withSessionToken)) return string(output) } func (c *Config) onepasswordAccount(key string) string { // This should not happen, but better to be safe must(onepasswordCheckInvalidAccountParameters(c.Onepassword.Mode)) accounts := mustValue(c.onepasswordAccounts()) if account, exists := accounts[key]; exists { return account } panic(fmt.Errorf("no 1Password account found matching %s", key)) } // onepasswordAccounts returns a map of keys to unique account UUIDs. func (c *Config) onepasswordAccounts() (map[string]string, error) { // This should not happen, but better to be safe if err := onepasswordCheckInvalidAccountParameters(c.Onepassword.Mode); err != nil { return nil, err } if c.Onepassword.accountMap != nil || c.Onepassword.accountMapErr != nil { return c.Onepassword.accountMap, c.Onepassword.accountMapErr } args := &onepasswordArgs{ args: []string{"account", "list", "--format=json"}, } output, err := c.onepasswordOutput(args, withoutSessionToken) if err != nil { c.Onepassword.accountMapErr = err return nil, c.Onepassword.accountMapErr } var accounts []onepasswordAccount if err := json.Unmarshal(output, &accounts); err != nil { c.Onepassword.accountMapErr = err return nil, c.Onepassword.accountMapErr } c.Onepassword.accountMap = onepasswordAccountMap(accounts) return c.Onepassword.accountMap, c.Onepassword.accountMapErr } func (c *Config) newOnepasswordArgs(baseArgs, userArgs []string) (*onepasswordArgs, error) { maxArgs := 3 if c.Onepassword.Mode != onepasswordModeAccount { maxArgs = 2 } // `session` and `connect` modes do not support the account parameter. Better // to error out early. if len(userArgs) < 1 || maxArgs < len(userArgs) { if err := onepasswordCheckInvalidAccountParameters(c.Onepassword.Mode); maxArgs < len(userArgs) && err != nil { return nil, err } return nil, fmt.Errorf("expected 1..%d arguments in %s mode, got %d", maxArgs, c.Onepassword.Mode, len(userArgs)) } a := &onepasswordArgs{ args: baseArgs, } a.item = userArgs[0] a.args = append(a.args, a.item) if len(userArgs) > 1 && userArgs[1] != "" { a.vault = userArgs[1] a.args = append(a.args, "--vault", a.vault) } if len(userArgs) > 2 && userArgs[2] != "" { a.account = c.onepasswordAccount(userArgs[2]) a.args = append(a.args, "--account", a.account) } return a, nil } // onepasswordAccountMap returns a map of unique IDs to account UUIDs. func onepasswordAccountMap(accounts []onepasswordAccount) map[string]string { // Build a map of keys to account UUIDs. accountsMap := make(map[string][]string) for _, account := range accounts { keys := []string{ account.URL, account.Email, account.UserUUID, account.AccountUUID, account.Shorthand, } accountName, _, accountNameOk := strings.Cut(account.URL, ".") if accountNameOk { keys = append(keys, accountName) } emailName, _, emailNameOk := strings.Cut(account.Email, "@") if emailNameOk { keys = append(keys, emailName, emailName+"@"+account.URL) } if accountNameOk && emailNameOk { keys = append(keys, emailName+"@"+accountName) } for _, key := range keys { accountsMap[key] = append(accountsMap[key], account.AccountUUID) } } // Select unique, non-empty keys. accountMap := make(map[string]string) for key, values := range accountsMap { if key != "" && len(values) == 1 { accountMap[key] = values[0] } } return accountMap } // onepasswordUniqueSessionToken will look for any session tokens in the // environment. If it finds exactly one then it will return it. func onepasswordUniqueSessionToken(environ []string) string { var token string for _, env := range environ { key, value, found := strings.Cut(env, "=") if found && strings.HasPrefix(key, "OP_SESSION_") { if token != "" { return "" } token = value } } return token } // onepasswordCheckMode verifies that things are set up correctly for the // 1Password mode. func (c *Config) onepasswordCheckMode() error { if c.Onepassword.modeChecked { return nil } c.Onepassword.modeChecked = true switch c.Onepassword.Mode { case onepasswordModeAccount: if os.Getenv("OP_SERVICE_ACCOUNT_TOKEN") != "" { return errors.New("onepassword.mode is account, but OP_SERVICE_ACCOUNT_TOKEN is set") } if os.Getenv("OP_CONNECT_HOST") != "" && os.Getenv("OP_CONNECT_TOKEN") != "" { return errors.New("onepassword.mode is account, but OP_CONNECT_HOST and OP_CONNECT_TOKEN are set") } case onepasswordModeConnect: if os.Getenv("OP_CONNECT_HOST") == "" { return errors.New("onepassword.mode is connect, but OP_CONNECT_HOST is not set") } if os.Getenv("OP_CONNECT_TOKEN") == "" { return errors.New("onepassword.mode is connect, but OP_CONNECT_TOKEN is not set") } if os.Getenv("OP_SERVICE_ACCOUNT_TOKEN") != "" { return errors.New("onepassword.mode is connect, but OP_SERVICE_ACCOUNT_TOKEN is set") } case onepasswordModeService: if os.Getenv("OP_SERVICE_ACCOUNT_TOKEN") == "" { return errors.New("onepassword.mode is service, but OP_SERVICE_ACCOUNT_TOKEN is not set") } if os.Getenv("OP_CONNECT_HOST") != "" && os.Getenv("OP_CONNECT_TOKEN") != "" { return errors.New("onepassword.mode is service, but OP_CONNECT_HOST and OP_CONNECT_TOKEN are set") } } return nil } // onepasswordCheckInvalidAccountParameters returns the error "1Password // account parameters cannot be used in %s mode" if the provided mode is not // account mode. func onepasswordCheckInvalidAccountParameters(mode onepasswordMode) error { if mode != onepasswordModeAccount { return fmt.Errorf("1Password account parameters cannot be used in %s mode", mode) } return nil } ================================================ FILE: internal/cmd/onepasswordtemplatefuncs_test.go ================================================ package cmd import ( "testing" "github.com/alecthomas/assert/v2" ) func TestOnepasswordAccountMap(t *testing.T) { for _, tc := range []struct { name string accounts []onepasswordAccount expected map[string]string }{ { name: "single_account_without_shorthand", accounts: []onepasswordAccount{ { URL: "account1.1password.ca", Email: "my@email.com", UserUUID: "some-user-uuid", AccountUUID: "some-account-uuid", }, }, expected: map[string]string{ "account1.1password.ca": "some-account-uuid", "account1": "some-account-uuid", "my@account1.1password.ca": "some-account-uuid", "my@account1": "some-account-uuid", "my@email.com": "some-account-uuid", "my": "some-account-uuid", "some-account-uuid": "some-account-uuid", "some-user-uuid": "some-account-uuid", }, }, { name: "single_account_with_shorthand", accounts: []onepasswordAccount{ { URL: "account1.1password.ca", Email: "my@email.com", UserUUID: "some-user-uuid", AccountUUID: "some-account-uuid", Shorthand: "some-account-shorthand", }, }, expected: map[string]string{ "account1.1password.ca": "some-account-uuid", "account1": "some-account-uuid", "my@account1.1password.ca": "some-account-uuid", "my@account1": "some-account-uuid", "my@email.com": "some-account-uuid", "my": "some-account-uuid", "some-account-shorthand": "some-account-uuid", "some-account-uuid": "some-account-uuid", "some-user-uuid": "some-account-uuid", }, }, { name: "multiple_unambiguous_accounts", accounts: []onepasswordAccount{ { URL: "account1.1password.ca", Email: "my@email.com", UserUUID: "some-user-uuid", AccountUUID: "some-account-uuid", Shorthand: "some-account-shorthand", }, { URL: "account2.1password.ca", Email: "me@otheremail.org", UserUUID: "some-other-user-uuid", AccountUUID: "some-other-account-uuid", Shorthand: "some-other-account-shorthand", }, }, expected: map[string]string{ "account1.1password.ca": "some-account-uuid", "account1": "some-account-uuid", "account2.1password.ca": "some-other-account-uuid", "account2": "some-other-account-uuid", "me@account2.1password.ca": "some-other-account-uuid", "me@account2": "some-other-account-uuid", "me@otheremail.org": "some-other-account-uuid", "me": "some-other-account-uuid", "my@account1.1password.ca": "some-account-uuid", "my@account1": "some-account-uuid", "my@email.com": "some-account-uuid", "my": "some-account-uuid", "some-account-shorthand": "some-account-uuid", "some-account-uuid": "some-account-uuid", "some-other-account-shorthand": "some-other-account-uuid", "some-other-account-uuid": "some-other-account-uuid", "some-other-user-uuid": "some-other-account-uuid", "some-user-uuid": "some-account-uuid", }, }, { name: "multiple_ambiguous_accounts", accounts: []onepasswordAccount{ { URL: "account1.1password.ca", Email: "my@email.com", UserUUID: "some-user-uuid", AccountUUID: "some-account-uuid", Shorthand: "some-account-shorthand", }, { URL: "account1.1password.ca", Email: "your@email.com", UserUUID: "some-other-user-uuid", AccountUUID: "some-other-account-uuid", Shorthand: "some-other-account-shorthand", }, }, expected: map[string]string{ "my@account1.1password.ca": "some-account-uuid", "my@account1": "some-account-uuid", "my@email.com": "some-account-uuid", "my": "some-account-uuid", "some-account-shorthand": "some-account-uuid", "some-account-uuid": "some-account-uuid", "some-other-account-shorthand": "some-other-account-uuid", "some-other-account-uuid": "some-other-account-uuid", "some-other-user-uuid": "some-other-account-uuid", "some-user-uuid": "some-account-uuid", "your@account1.1password.ca": "some-other-account-uuid", "your@account1": "some-other-account-uuid", "your@email.com": "some-other-account-uuid", "your": "some-other-account-uuid", }, }, } { t.Run(tc.name, func(t *testing.T) { actual := onepasswordAccountMap(tc.accounts) assert.Equal(t, tc.expected, actual) }) } } ================================================ FILE: internal/cmd/passholetemplatefuncs.go ================================================ package cmd import ( "bytes" "io" "os" "os/exec" "slices" "github.com/coreos/go-semver/semver" "chezmoi.io/chezmoi/internal/chezmoilog" ) type passholeCacheKey struct { path string field string } type passholeConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Args []string `json:"args" mapstructure:"args" yaml:"args"` Prompt bool `json:"prompt" mapstructure:"prompt" yaml:"prompt"` cache map[passholeCacheKey]string password string } var passholeMinVersion = semver.Version{Major: 1, Minor: 10, Patch: 0} func (c *Config) passholeTemplateFunc(path, field string) string { key := passholeCacheKey{ path: path, field: field, } if value, ok := c.Passhole.cache[key]; ok { return value } args := slices.Clone(c.Passhole.Args) var stdin io.Reader if c.Passhole.Prompt { if c.Passhole.password == "" { password := mustValue(c.readPassword("Enter database password: ", "password")) c.Passhole.password = password } args = append(args, "--password", "-") stdin = bytes.NewBufferString(c.Passhole.password + "\n") } args = append(args, "show", "--field", field, path) output := mustValue(c.passholeOutput(c.Passhole.Command, args, stdin)) if c.Passhole.cache == nil { c.Passhole.cache = make(map[passholeCacheKey]string) } c.Passhole.cache[key] = output return output } func (c *Config) passholeOutput(name string, args []string, stdin io.Reader) (string, error) { cmd := exec.Command(name, args...) cmd.Stdin = stdin cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return "", newCmdOutputError(cmd, output, err) } return string(output), nil } ================================================ FILE: internal/cmd/passtemplatefuncs.go ================================================ package cmd import ( "bytes" "os" "os/exec" "chezmoi.io/chezmoi/internal/chezmoilog" ) type passConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` cache map[string][]byte } func (c *Config) passTemplateFunc(id string) string { output := mustValue(c.passOutput(id)) firstLine, _, _ := bytes.Cut(output, []byte{'\n'}) return string(bytes.TrimSpace(firstLine)) } func (c *Config) passFieldsTemplateFunc(id string) map[string]string { output := mustValue(c.passOutput(id)) result := make(map[string]string) for line := range bytes.SplitSeq(output, []byte{'\n'}) { if key, value, ok := bytes.Cut(line, []byte{':'}); ok { result[string(bytes.TrimSpace(key))] = string(bytes.TrimSpace(value)) } } return result } func (c *Config) passRawTemplateFunc(id string) string { return string(mustValue(c.passOutput(id))) } func (c *Config) passOutput(id string) ([]byte, error) { if output, ok := c.Pass.cache[id]; ok { return output, nil } args := []string{"show", id} cmd := exec.Command(c.Pass.Command, args...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } if c.Pass.cache == nil { c.Pass.cache = make(map[string][]byte) } c.Pass.cache[id] = output return output, nil } ================================================ FILE: internal/cmd/pathlist.go ================================================ package cmd import ( "maps" "slices" "strings" ) type pathListTreeNode struct { component string children map[string]*pathListTreeNode } func newPathListTreeNode(component string) *pathListTreeNode { return &pathListTreeNode{ component: component, children: make(map[string]*pathListTreeNode), } } func newPathListTreeFromPathsSlice(paths []string) *pathListTreeNode { root := newPathListTreeNode("") for _, path := range paths { n := root for component := range strings.SplitSeq(path, "/") { child, ok := n.children[component] if !ok { child = newPathListTreeNode(component) n.children[component] = child } n = child } } return root } func (n *pathListTreeNode) write(sb *strings.Builder, prefix, indent string) { sb.WriteString(prefix) sb.WriteString(n.component) sb.WriteByte('\n') n.writeChildren(sb, prefix+indent, indent) } func (n *pathListTreeNode) writeChildren(sb *strings.Builder, prefix, indent string) { for _, key := range slices.Sorted(maps.Keys(n.children)) { child := n.children[key] child.write(sb, prefix, indent) } } ================================================ FILE: internal/cmd/pathlist_test.go ================================================ package cmd import ( "strings" "testing" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestNewNodeFromPathsSlice(t *testing.T) { for _, tc := range []struct { name string paths []string expected []string }{ { name: "empty", }, { name: "root", paths: []string{ "a", }, expected: []string{ "a", }, }, { name: "simple", paths: []string{ "a", "b", }, expected: []string{ "a", "b", }, }, { name: "simple_nesting", paths: []string{ "a/b", }, expected: []string{ "a", " b", }, }, { name: "multiple_simple_nesting", paths: []string{ "a/a", "a/b", "b/a", "b/b", }, expected: []string{ "a", " a", " b", "b", " a", " b", }, }, } { t.Run(tc.name, func(t *testing.T) { var sb strings.Builder newPathListTreeFromPathsSlice(tc.paths).writeChildren(&sb, "", " ") assert.Equal(t, chezmoitest.JoinLines(tc.expected...), sb.String()) }) } } ================================================ FILE: internal/cmd/pathstyle.go ================================================ package cmd const ( pathStyleAbsolute = "absolute" pathStyleRelative = "relative" pathStyleSourceAbsolute = "source-absolute" pathStyleSourceRelative = "source-relative" pathStyleAll = "all" ) var ( sourceOrTargetPathStyleValues = []string{ pathStyleAbsolute, pathStyleRelative, pathStyleSourceAbsolute, pathStyleSourceRelative, pathStyleAll, } targetPathStyleValues = []string{ pathStyleAbsolute, pathStyleRelative, } ) ================================================ FILE: internal/cmd/pinentry.go ================================================ package cmd import ( "github.com/twpayne/go-pinentry/v4" "chezmoi.io/chezmoi/internal/chezmoierrors" ) type pinEntryConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Args []string `json:"args" mapstructure:"args" yaml:"args"` Options []string `json:"options" mapstructure:"options" yaml:"options"` } var pinEntryDefaultOptions = []string{ pinentry.OptionAllowExternalPasswordCache, } func (c *Config) readPINEntry(prompt string) (string, error) { var client *pinentry.Client client, err := pinentry.NewClient( pinentry.WithArgs(c.PINEntry.Args), pinentry.WithBinaryName(c.PINEntry.Command), pinentry.WithGPGTTY(), pinentry.WithOptions(c.PINEntry.Options), pinentry.WithPrompt(prompt), pinentry.WithTitle("chezmoi"), ) if err != nil { return "", err } defer chezmoierrors.CombineFunc(&err, client.Close) result, err := client.GetPIN() if err != nil { return "", err } return result.PIN, nil } ================================================ FILE: internal/cmd/prompt.go ================================================ package cmd import ( "bufio" "fmt" "os" "slices" "strconv" "strings" tea "github.com/charmbracelet/bubbletea" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoibubbles" ) // readBool reads a bool. func (c *Config) readBool(prompt string, defaultValue *bool) (bool, error) { switch { case c.noTTY: fullPrompt := prompt if defaultValue != nil { fullPrompt += " (default " + strconv.FormatBool(*defaultValue) + ")" } fullPrompt += "? " for { valueStr, err := c.readLineRaw(fullPrompt) if err != nil { return false, err } if valueStr == "" && defaultValue != nil { return *defaultValue, nil } if value, err := chezmoi.ParseBool(valueStr); err == nil { return value, nil } } default: _, err := c.stdout.Write([]byte(prompt + "?\n")) if err != nil { return false, err } initModel := chezmoibubbles.NewBoolInputModel("> ", defaultValue) finalModel, err := runCancelableModel(initModel) if err != nil { return false, err } return finalModel.Value(), nil } } // readChoice reads a choice. func (c *Config) readChoice(prompt string, choices []string, defaultValue *string) (string, error) { switch { case c.noTTY: fullPrompt := prompt + " (" + strings.Join(choices, "/") if defaultValue != nil { fullPrompt += ", default " + *defaultValue } fullPrompt += ")? " abbreviations := chezmoi.UniqueAbbreviations(choices) for { value, err := c.readLineRaw(fullPrompt) if err != nil { return "", err } if value == "" && defaultValue != nil { return *defaultValue, nil } if value, ok := abbreviations[value]; ok { return value, nil } } default: _, err := c.stdout.Write([]byte(prompt + "?\n")) if err != nil { return "", err } initModel := chezmoibubbles.NewChoiceInputModel("> ", choices, defaultValue) finalModel, err := runCancelableModel(initModel) if err != nil { return "", err } return finalModel.Value(), nil } } // readInt reads an int. func (c *Config) readInt(prompt string, defaultValue *int64) (int64, error) { switch { case c.noTTY: fullPrompt := prompt if defaultValue != nil { fullPrompt += " (default " + strconv.FormatInt(*defaultValue, 10) + ")" } fullPrompt += "? " for { valueStr, err := c.readLineRaw(fullPrompt) if err != nil { return 0, err } if valueStr == "" && defaultValue != nil { return *defaultValue, nil } if value, err := strconv.ParseInt(valueStr, 10, 64); err == nil { return value, nil } } default: _, err := c.stdout.Write([]byte(prompt + "?\n")) if err != nil { return 0, err } initModel := chezmoibubbles.NewIntInputModel("> ", defaultValue) finalModel, err := runCancelableModel(initModel) if err != nil { return 0, err } return finalModel.Value(), nil } } // readLineRaw reads a line, trimming leading and trailing whitespace. func (c *Config) readLineRaw(prompt string) (string, error) { _, err := c.stdout.Write([]byte(prompt)) if err != nil { return "", err } if c.bufioReader == nil { c.bufioReader = bufio.NewReader(c.stdin) } line, err := c.bufioReader.ReadString('\n') if err != nil { return "", err } return strings.TrimSpace(line), nil } // readMultichoice reads multiple choices from a list. func (c *Config) readMultichoice(prompt string, choices []string, defaultValue *[]string) ([]string, error) { switch { case c.noTTY: shortPrompt := "Choice (ENTER to stop)> " fullPrompt := prompt + "?\nChoices (" + strings.Join(choices, "/") + ")" if defaultValue != nil { fullPrompt += "\nDefault [" + strings.Join(*defaultValue, ", ") + "]" } fullPrompt += "\n" _, err := c.stdout.Write([]byte(fullPrompt)) if err != nil { return []string{}, err } abbreviations := chezmoi.UniqueAbbreviations(choices) selected := make(map[string]struct{}) for { value, err := c.readLineRaw(shortPrompt) if err != nil { return []string{}, err } if value == "" { if len(selected) == 0 && defaultValue != nil { return *defaultValue, nil } if len(selected) > 0 { break } } if value == "[]" { return []string{}, nil } if value, ok := abbreviations[value]; ok { selected[value] = struct{}{} } if len(selected) == len(choices) { break } } result := make([]string, 0) for name := range selected { result = append(result, name) } return result, nil default: _, err := c.stdout.Write([]byte(prompt + "?\n")) if err != nil { return []string{}, err } initModel := chezmoibubbles.NewMultichoiceInputModel("> ", choices, defaultValue) finalModel, err := tea.NewProgram(initModel, tea.WithOutput(os.Stderr)).Run() if err != nil { return []string{}, err } return finalModel.(chezmoibubbles.MultichoiceInputModel).Value(), nil //nolint:forcetypeassert } } // readPassword reads a password. func (c *Config) readPassword(prompt, placeholder string) (string, error) { switch { case c.noTTY: return c.readLineRaw(prompt) case c.PINEntry.Command != "": return c.readPINEntry(prompt) default: initModel := chezmoibubbles.NewPasswordInputModel(prompt, placeholder) finalModel, err := runCancelableModel(initModel) if err != nil { return "", err } return finalModel.Value(), nil } } // readString reads a string. func (c *Config) readString(prompt string, defaultValue *string) (string, error) { switch { case c.noTTY: fullPrompt := prompt if defaultValue != nil { fullPrompt += " (default " + strconv.Quote(*defaultValue) + ")" } fullPrompt += "? " value, err := c.readLineRaw(fullPrompt) if err != nil { return "", err } if value == "" && defaultValue != nil { return *defaultValue, nil } return value, nil default: _, err := c.stdout.Write([]byte(prompt + "?\n")) if err != nil { return "", err } initModel := chezmoibubbles.NewStringInputModel("> ", defaultValue) finalModel, err := runCancelableModel(initModel) if err != nil { return "", err } return strings.TrimSpace(finalModel.Value()), nil } } func (c *Config) promptBool(prompt string, args ...bool) (bool, error) { var defaultValue *bool switch len(args) { case 0: // Do nothing. case 1: defaultValue = &args[0] default: return false, fmt.Errorf("want 1 or 2 arguments, got %d", len(args)+1) } if c.interactiveTemplateFuncs.promptDefaults && defaultValue != nil { return *defaultValue, nil } return c.readBool(prompt, defaultValue) } // promptChoice prompts the user for one of choices until a valid choice is // made. func (c *Config) promptChoice(prompt string, choices []string, args ...string) (string, error) { var defaultValue *string switch len(args) { case 0: // Do nothing. case 1: if !slices.Contains(choices, args[0]) { return "", fmt.Errorf("%s: invalid default value", args[0]) } defaultValue = &args[0] default: return "", fmt.Errorf("want 2 or 3 arguments, got %d", len(args)+2) } if c.interactiveTemplateFuncs.promptDefaults && defaultValue != nil { return *defaultValue, nil } return c.readChoice(prompt, choices, defaultValue) } func (c *Config) promptInt(prompt string, args ...int64) (int64, error) { var defaultValue *int64 switch len(args) { case 0: // Do nothing. case 1: defaultValue = &args[0] default: return 0, fmt.Errorf("want 1 or 2 arguments, got %d", len(args)+1) } if c.interactiveTemplateFuncs.promptDefaults && defaultValue != nil { return *defaultValue, nil } return c.readInt(prompt, defaultValue) } // promptMultiChoice prompts the user to select one or more values in a list. func (c *Config) promptMultichoice(prompt string, choices []string, defaults *[]string) ([]string, error) { if defaults != nil { for i, defaultValue := range *defaults { if !slices.Contains(choices, defaultValue) { return []string{}, fmt.Errorf("%s: invalid default value (index %d)", defaultValue, i+1) } } } if c.interactiveTemplateFuncs.promptDefaults && defaults != nil { return *defaults, nil } return c.readMultichoice(prompt, choices, defaults) } func (c *Config) promptString(prompt string, args ...string) (string, error) { var defaultValue *string switch len(args) { case 0: // Do nothing. case 1: arg := strings.TrimSpace(args[0]) defaultValue = &arg default: return "", fmt.Errorf("want 1 or 2 arguments, got %d", len(args)+1) } if c.interactiveTemplateFuncs.promptDefaults && defaultValue != nil { return *defaultValue, nil } return c.readString(prompt, defaultValue) } type cancelableModel interface { tea.Model Canceled() bool } func runCancelableModel[M cancelableModel](initModel M) (M, error) { switch finalModel, err := runModel(initModel); { case err != nil: return finalModel, err case finalModel.Canceled(): return finalModel, chezmoi.ExitCodeError(0) default: return finalModel, nil } } func runModel[M tea.Model](initModel M) (M, error) { program := tea.NewProgram(initModel) finalModel, err := program.Run() return finalModel.(M), err //nolint:forcetypeassert,revive } ================================================ FILE: internal/cmd/protonpasstemplatefuncs.go ================================================ package cmd import ( "encoding/json" "os" "os/exec" "strings" "chezmoi.io/chezmoi/internal/chezmoilog" ) type protonPassConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` outputCache map[string][]byte } func (c *Config) protonPassTemplateFunc(item string) string { args := []string{"item", "view", item} return string(mustValue(c.protonPassOutput(args))) } func (c *Config) protonPassJSONTemplateFunc(item string) any { args := []string{"item", "view", item, "--output=json"} output := mustValue(c.protonPassOutput(args)) var result map[string]any must(json.Unmarshal(output, &result)) return result } func (c *Config) protonPassOutput(args []string) ([]byte, error) { key := strings.Join(args, "\x00") if data, ok := c.ProtonPass.outputCache[key]; ok { return data, nil } cmd := exec.Command(c.ProtonPass.Command, args...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } if c.ProtonPass.outputCache == nil { c.ProtonPass.outputCache = make(map[string][]byte) } c.ProtonPass.outputCache[key] = output return output, nil } ================================================ FILE: internal/cmd/purgecmd.go ================================================ package cmd import ( "errors" "fmt" "io/fs" "os" "runtime" "strings" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type purgeCmdConfig struct { binary bool } func (c *Config) newPurgeCmd() *cobra.Command { purgeCmd := &cobra.Command{ GroupID: groupIDMigration, Use: "purge", Short: "Purge chezmoi's configuration and data", Long: mustLongHelp("purge"), Example: example("purge"), Args: cobra.NoArgs, ValidArgsFunction: cobra.NoFileCompletions, RunE: c.runPurgeCmd, Annotations: newAnnotations( modifiesDestinationDirectory, modifiesSourceDirectory, persistentStateModeNone, ), } purgeCmd.Flags().BoolVarP(&c.purge.binary, "binary", "P", c.purge.binary, "Purge chezmoi binary") return purgeCmd } func (c *Config) runPurgeCmd(cmd *cobra.Command, args []string) error { return c.doPurge(&doPurgeOptions{ binary: c.purge.binary, cache: true, config: true, persistentState: true, sourceDir: true, workingTree: true, }) } // doPurge is the core purge functionality. It removes all files and directories // associated with chezmoi. func (c *Config) doPurge(options *doPurgeOptions) error { // absPaths contains the list of paths to purge, in order. The order is // assembled so that parent directories are purged before their children. var absPaths []chezmoi.AbsPath if options.cache { absPaths = append(absPaths, c.CacheDirAbsPath) } if options.config { configFileAbsPath, err := c.getConfigFileAbsPath() if err != nil { return err } absPaths = append(absPaths, configFileAbsPath.Dir(), configFileAbsPath) } if options.persistentState { if c.persistentState != nil { if err := c.persistentState.Close(); err != nil { return err } } persistentStateFileAbsPath, err := c.persistentStateFile() if err != nil { return err } absPaths = append(absPaths, persistentStateFileAbsPath) } if options.workingTree { absPaths = append(absPaths, c.WorkingTreeAbsPath) } if options.sourceDir { absPaths = append(absPaths, c.SourceDirAbsPath) } if options.binary { switch executable, err := os.Executable(); { case err != nil: return err case runtime.GOOS == "windows": // On Windows the binary of a running process cannot be removed. // Warn the user, but otherwise continue. c.errorf("cannot purge binary (%s) on Windows\n", executable) case strings.Contains(executable, "test"): // Special case: do not purge the binary if it is a test binary created // by go test as this will break later or concurrent tests. default: // Otherwise, remove the binary normally. absPaths = append(absPaths, chezmoi.NewAbsPath(executable)) } } // Remove all paths that exist. for _, absPath := range absPaths { switch _, err := c.destSystem.Stat(absPath); { case errors.Is(err, fs.ErrNotExist): continue case err != nil: return err } if !c.force { switch choice, err := c.promptChoice(fmt.Sprintf("Remove %s", absPath), choicesYesNoAllQuit); { case err != nil: return err case choice == "yes": case choice == "no": continue case choice == "all": c.force = true case choice == "quit": return nil } } switch err := c.destSystem.RemoveAll(absPath); { case errors.Is(err, fs.ErrPermission): continue case err != nil: return err } } return nil } ================================================ FILE: internal/cmd/rbwtemplatefuncs.go ================================================ package cmd import ( "encoding/json" "os" "os/exec" "strings" "github.com/coreos/go-semver/semver" "chezmoi.io/chezmoi/internal/chezmoilog" ) type rbwConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` outputCache map[string][]byte } var rbwMinVersion = semver.Version{Major: 1, Minor: 7, Patch: 0} func (c *Config) rbwFieldsTemplateFunc(name string, extraArgs ...string) map[string]any { args := append([]string{"get", "--raw", name}, extraArgs...) output := mustValue(c.rbwOutput(args)) var data struct { Fields []map[string]any `json:"fields"` } must(json.Unmarshal(output, &data)) result := make(map[string]any) for _, field := range data.Fields { if name, ok := field["name"].(string); ok { result[name] = field } } return result } func (c *Config) rbwTemplateFunc(name string, extraArgs ...string) map[string]any { args := append([]string{"get", "--raw", name}, extraArgs...) output := mustValue(c.rbwOutput(args)) var data map[string]any must(json.Unmarshal(output, &data)) return data } func (c *Config) rbwOutput(args []string) ([]byte, error) { key := strings.Join(args, "\x00") if data, ok := c.RBW.outputCache[key]; ok { return data, nil } cmd := exec.Command(c.RBW.Command, args...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } if c.RBW.outputCache == nil { c.RBW.outputCache = make(map[string][]byte) } c.RBW.outputCache[key] = output return output, nil } ================================================ FILE: internal/cmd/readdcmd.go ================================================ package cmd import ( "bytes" "errors" "fmt" "io/fs" "runtime" "slices" "time" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoiset" ) type reAddCmdConfig struct { filter *chezmoi.EntryTypeFilter reEncrypt bool recursive bool } // A fileInfo is a simple struct that implements the [io/fs.FileInfo] interface // for the purpose of overriding the mode on Windows. type fileInfo struct { name string size int64 mode fs.FileMode modTime time.Time isDir bool } func (fi *fileInfo) Name() string { return fi.name } func (fi *fileInfo) Size() int64 { return fi.size } func (fi *fileInfo) Mode() fs.FileMode { return fi.mode } func (fi *fileInfo) ModTime() time.Time { return fi.modTime } func (fi *fileInfo) IsDir() bool { return fi.isDir } func (fi *fileInfo) Sys() any { return nil } // Sys always returns nil to avoid any inconsistency. func (c *Config) newReAddCmd() *cobra.Command { reAddCmd := &cobra.Command{ GroupID: groupIDDaily, Use: "re-add", Short: "Re-add modified files", Long: mustLongHelp("re-add"), Example: example("re-add"), ValidArgsFunction: c.targetValidArgs, Args: cobra.ArbitraryArgs, RunE: c.makeRunEWithSourceState(c.runReAddCmd), Annotations: newAnnotations( modifiesSourceDirectory, persistentStateModeReadWrite, requiresSourceDirectory, ), } reAddCmd.Flags().VarP(c.reAdd.filter.Exclude, "exclude", "x", "Exclude entry types") reAddCmd.Flags().VarP(c.reAdd.filter.Include, "include", "i", "Include entry types") reAddCmd.Flags().BoolVar(&c.reAdd.reEncrypt, "re-encrypt", c.reAdd.reEncrypt, "Re-encrypt encrypted files") reAddCmd.Flags().BoolVarP(&c.reAdd.recursive, "recursive", "r", c.reAdd.recursive, "Recurse into subdirectories") return reAddCmd } func (c *Config) runReAddCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { var targetRelPaths []chezmoi.RelPath sourceStateEntries := make(map[chezmoi.RelPath]chezmoi.SourceStateEntry) if len(args) == 0 { _ = sourceState.ForEach( func(targetRelPath chezmoi.RelPath, sourceStateEntry chezmoi.SourceStateEntry) error { targetRelPaths = append(targetRelPaths, targetRelPath) sourceStateEntries[targetRelPath] = sourceStateEntry return nil }, ) } else { var err error targetRelPaths, err = c.targetRelPaths(sourceState, args, targetRelPathsOptions{ recursive: c.reAdd.recursive, }) if err != nil { return err } for _, targetRelPath := range targetRelPaths { sourceStateEntries[targetRelPath] = sourceState.Get(targetRelPath) } } slices.SortFunc(targetRelPaths, chezmoi.CompareRelPaths) // Track which files were already processed to avoid double-processing with exact directories processedFiles := make(map[chezmoi.RelPath]bool) TARGET_REL_PATH: for _, targetRelPath := range targetRelPaths { sourceStateFile, ok := sourceStateEntries[targetRelPath].(*chezmoi.SourceStateFile) if !ok { continue } if sourceStateFile.Attr().Template { continue } if sourceStateFile.Attr().Type != chezmoi.SourceFileTypeFile { continue } destAbsPath := c.DestDirAbsPath.Join(targetRelPath) destAbsPathInfo, err := c.destSystem.Stat(destAbsPath) actualState, err := chezmoi.NewActualStateEntry(c.destSystem, destAbsPath, destAbsPathInfo, err) if err != nil { return err } actualStateFile, ok := actualState.(*chezmoi.ActualStateFile) if !ok { continue } targetState, err := sourceStateFile.TargetStateEntry(c.destSystem, c.DestDirAbsPath) if err != nil { return err } targetStateFile, ok := targetState.(*chezmoi.TargetStateFile) if !ok { continue } actualContents, err := actualStateFile.Contents() if err != nil { return err } targetContents, err := targetStateFile.Contents() if err != nil { return err } bytesEqual := bytes.Equal(actualContents, targetContents) // On Windows, ignore permission changes as they are not preserved // by the filesystem. On other systems, if there are no permission // changes, continue. // // See https://github.com/twpayne/chezmoi/issues/3891. permsEqual := runtime.GOOS == "windows" || actualStateFile.Perm() == targetStateFile.Perm(c.Umask) reEncrypt := sourceStateFile.Attr().Encrypted && c.reAdd.reEncrypt if bytesEqual && permsEqual && !reEncrypt { continue } if c.Interactive { prompt := fmt.Sprintf("Re-add %s", targetRelPath) var choices []string if actualContents != nil || targetContents != nil { choices = append(choices, "diff") } choices = append(choices, choicesYesNoAllQuit...) FOR: for { switch choice, err := c.promptChoice(prompt, choices); { case err != nil: return err case choice == "diff": if err := c.diffFile( targetRelPath, c.SourceDirAbsPath.Join(sourceStateFile.SourceRelPath().RelPath()), targetContents, targetStateFile.Perm(c.Umask), destAbsPath, actualContents, actualStateFile.Perm(), ); err != nil { return err } case choice == "yes": break FOR case choice == "no": continue TARGET_REL_PATH case choice == "all": c.Interactive = false break FOR case choice == "quit": return chezmoi.ExitCodeError(0) default: panic(choice + ": unexpected choice") } } } // On Windows, as the file mode is not preserved by the filesystem, copy // the existing mode from the target file. Hack this in by replacing the // io/fs.FileInfo of the destination file with a new io/fs.FileInfo with // the mode of the target file. // // See https://github.com/twpayne/chezmoi/issues/3891. if runtime.GOOS == "windows" { destAbsPathInfo = &fileInfo{ name: destAbsPathInfo.Name(), size: destAbsPathInfo.Size(), mode: targetStateFile.Perm(0), // Use the mode from the target. modTime: destAbsPathInfo.ModTime(), } } destAbsPathInfos := map[chezmoi.AbsPath]fs.FileInfo{ destAbsPath: destAbsPathInfo, } if err := sourceState.Add(c.sourceSystem, c.persistentState, c.destSystem, destAbsPathInfos, &chezmoi.AddOptions{ Encrypt: sourceStateFile.Attr().Encrypted, EncryptedSuffix: c.encryption.EncryptedSuffix(), Errorf: c.errorf, Filter: c.reAdd.filter, PreAddFunc: c.defaultPreAddFunc, }); err != nil { return err } // Mark this file as processed processedFiles[targetRelPath] = true } // Process exact directories - add new files and remove deleted files return c.processExactDirs(sourceState, sourceStateEntries, processedFiles) } // processExactDirs handles exact directory synchronization during re-add. // It adds new files found in exact target directories and removes files from // source that no longer exist in exact target directories. // processedFiles contains files that were already re-added in the file loop, // outside of the exact directory logic, to avoid double-processing them. func (c *Config) processExactDirs( sourceState *chezmoi.SourceState, sourceStateEntries map[chezmoi.RelPath]chezmoi.SourceStateEntry, processedFiles map[chezmoi.RelPath]bool, ) error { // Collect all exact directories from source state that are in scope // An exact directory is in scope if: // 1. It's directly in sourceStateEntries (was explicitly or implicitly targeted) // 2. Any of its children are in sourceStateEntries (parent dir needs sync) exactDirs := make(map[chezmoi.RelPath]*chezmoi.SourceStateDir) // First, collect exact directories that are directly in the entries for targetRelPath, entry := range sourceStateEntries { if sourceStateDir, ok := entry.(*chezmoi.SourceStateDir); ok && sourceStateDir.Attr().Exact { exactDirs[targetRelPath] = sourceStateDir } } // Then, collect parent exact directories for any entries for targetRelPath := range sourceStateEntries { // Walk up the path to find parent exact directories for parentPath := targetRelPath.Dir(); parentPath != chezmoi.DotRelPath; parentPath = parentPath.Dir() { // Check if this parent is an exact directory in source state parentEntry := sourceState.Get(parentPath) if sourceStateDir, ok := parentEntry.(*chezmoi.SourceStateDir); ok && sourceStateDir.Attr().Exact { // Only add if not already present if _, exists := exactDirs[parentPath]; !exists { exactDirs[parentPath] = sourceStateDir } } } } // Process each exact directory for targetRelPath := range exactDirs { targetDirAbsPath := c.DestDirAbsPath.Join(targetRelPath) // Read the target directory contents dirEntries, err := c.destSystem.ReadDir(targetDirAbsPath) switch { case errors.Is(err, fs.ErrNotExist): // If directory doesn't exist in target, skip it continue case err != nil: return err } // Build sets of files in source and target sourceFiles := make(map[string]chezmoi.SourceStateEntry) ignoredFiles := chezmoiset.New[chezmoi.RelPath]() targetFiles := make(map[string]fs.DirEntry) // Collect files from source state that are in this exact directory // Only consider actual files, not removes or other entry types for entryRelPath, entry := range sourceStateEntries { // Check if this entry is a direct child of the exact directory if entryRelPath.Dir() == targetRelPath { // Only count actual files in source, not remove entries or templates if sourceStateFile, ok := entry.(*chezmoi.SourceStateFile); ok { if sourceStateFile.Attr().Template { ignoredFiles.Add(entryRelPath) } else { sourceFiles[entryRelPath.Base()] = entry } } } } // Collect files from target directory for _, dirEntry := range dirEntries { name := dirEntry.Name() if name == "." || name == ".." { continue } targetFiles[name] = dirEntry } // Find new files (in target but not in source) and add them destAbsPathInfos := make(map[chezmoi.AbsPath]fs.FileInfo) for name, dirEntry := range targetFiles { entryRelPath := targetRelPath.JoinString(name) // Skip files that were already processed in the file re-add loop or // are ignored if processedFiles[entryRelPath] || ignoredFiles.Contains(entryRelPath) { continue } if _, inSource := sourceFiles[name]; !inSource { // Check if ignored if sourceState.Ignore(entryRelPath) { continue } // Skip subdirectories that are themselves exact directories // They will be processed separately in their own exact directory processing if dirEntry.IsDir() { childEntry := sourceState.Get(entryRelPath) if childDir, ok := childEntry.(*chezmoi.SourceStateDir); ok && childDir.Attr().Exact { continue } } // This is a new file - add it // Get full FileInfo (DirEntry only has partial info) destAbsPath := targetDirAbsPath.JoinString(name) fileInfo, err := dirEntry.Info() if err != nil { return err } destAbsPathInfos[destAbsPath] = fileInfo } } // Add new files if any were found if len(destAbsPathInfos) > 0 { if err := sourceState.Add(c.sourceSystem, c.persistentState, c.destSystem, destAbsPathInfos, &chezmoi.AddOptions{ Filter: c.reAdd.filter, PreAddFunc: c.defaultPreAddFunc, }); err != nil { return err } } // Find deleted files (in source but not in target) and remove them for name, sourceEntry := range sourceFiles { if _, inTarget := targetFiles[name]; !inTarget { // Check if ignored entryRelPath := targetRelPath.JoinString(name) if sourceState.Ignore(entryRelPath) { continue } // This file was deleted from target - remove it from source sourceAbsPath := c.SourceDirAbsPath.Join(sourceEntry.SourceRelPath().RelPath()) if err := c.sourceSystem.RemoveAll(sourceAbsPath); err != nil { return err } } } } return nil } ================================================ FILE: internal/cmd/readdcmd_test.go ================================================ package cmd import ( "io/fs" "runtime" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" "chezmoi.io/chezmoi/internal/chezmoitest" ) var _ fs.FileInfo = &fileInfo{} func TestIssue3891(t *testing.T) { if runtime.GOOS != "windows" { t.Skip("Windows only") } chezmoitest.WithTestFS(t, map[string]any{ "/home/user": map[string]any{ "run.sh": "#!/bin/sh\n", ".local/share/chezmoi": map[string]any{ "executable_run.sh": "#!/bin/sh", }, }, }, func(fileSystem vfs.FS) { assert.NoError(t, newTestConfig(t, fileSystem).execute([]string{"re-add"})) vfst.RunTests(t, fileSystem, "", vfst.TestPath("/home/user/.local/share/chezmoi/executable_run.sh", vfst.TestContentsString("#!/bin/sh\n"), ), vfst.TestPath("/home/user/.local/share/chezmoi/run.sh", vfst.TestDoesNotExist(), ), ) }) } ================================================ FILE: internal/cmd/readhttpresponse.go ================================================ package cmd import ( "io" "net/http" "strings" "time" "github.com/charmbracelet/bubbles/progress" "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" "chezmoi.io/chezmoi/internal/chezmoi" ) const httpProgressWidth = 38 type httpProgressModel struct { url string contentLength int progress progress.Model canceled bool } type httpSpinnerModel struct { url string spinner spinner.Model canceled bool } type bytesReadMsg int type doneMsg struct { err error } func (m httpProgressModel) Canceled() bool { return m.canceled } func (m httpProgressModel) Init() tea.Cmd { return nil } func (m httpProgressModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case bytesReadMsg: cmd := m.progress.SetPercent(float64(msg) / float64(m.contentLength)) return m, cmd case doneMsg: return m, tea.Quit case progress.FrameMsg: model, cmd := m.progress.Update(msg) m.progress = model.(progress.Model) //nolint:forcetypeassert,revive return m, cmd case tea.KeyMsg: switch msg.Type { case tea.KeyCtrlC, tea.KeyEsc: m.canceled = true return m, tea.Quit default: return m, nil } default: return m, nil } } func (m httpProgressModel) View() string { return "[" + m.progress.View() + "] " + m.url } func (m httpSpinnerModel) Canceled() bool { return m.canceled } func (m httpSpinnerModel) Init() tea.Cmd { return nil } func (m httpSpinnerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case bytesReadMsg: var cmd tea.Cmd m.spinner, cmd = m.spinner.Update(m.spinner.Tick()) return m, cmd case doneMsg: return m, tea.Quit case spinner.TickMsg: var cmd tea.Cmd m.spinner, cmd = m.spinner.Update(msg) return m, cmd case tea.KeyMsg: switch msg.Type { case tea.KeyCtrlC, tea.KeyEsc: m.canceled = true return m, tea.Quit default: return m, nil } default: return m, nil } } func (m httpSpinnerModel) View() string { return "[" + m.spinner.View() + "] " + m.url } func (c *Config) readHTTPResponse(url string, resp *http.Response) ([]byte, error) { switch { case c.noTTY || !c.Progress.Value(c.progressAutoFunc): return io.ReadAll(resp.Body) case resp.ContentLength >= 0: httpProgress := progress.New( progress.WithWidth(httpProgressWidth), ) httpProgress.Full = '#' httpProgress.FullColor = "" httpProgress.Empty = ' ' httpProgress.EmptyColor = "" httpProgress.ShowPercentage = false model := httpProgressModel{ url: url, contentLength: int(resp.ContentLength), progress: httpProgress, } return runReadHTTPResponse(model, resp) default: httpSpinner := spinner.New( spinner.WithSpinner(spinner.Spinner{ Frames: makeNightriderFrames("+", ' ', httpProgressWidth), FPS: time.Second / 60, }), ) model := httpSpinnerModel{ url: resp.Request.URL.String(), spinner: httpSpinner, } return runReadHTTPResponse(model, resp) } } type hookWriter struct { onWrite func([]byte) (int, error) } func (w *hookWriter) Write(p []byte) (int, error) { return w.onWrite(p) } func runReadHTTPResponse(model cancelableModel, resp *http.Response) ([]byte, error) { program := tea.NewProgram(model) bytesRead := 0 hookWriter := &hookWriter{ onWrite: func(p []byte) (int, error) { bytesRead += len(p) program.Send(bytesReadMsg(bytesRead)) return len(p), nil }, } var data []byte var err error go func() { data, err = io.ReadAll(io.TeeReader(resp.Body, hookWriter)) program.Send(doneMsg{ err: err, }) }() if model, err := program.Run(); err != nil { return nil, err } else if model.(cancelableModel).Canceled() { //nolint:forcetypeassert return nil, chezmoi.ExitCodeError(0) } return data, err } func makeNightriderFrames(shape string, padding rune, width int) []string { delta := width - len(shape) if delta <= 0 { return []string{shape[:width]} } frames := make([]string, 2*delta) paddingStr := string([]rune{padding}) for i := 0; i <= delta; i++ { frames[i] = strings.Repeat(paddingStr, i) + shape + strings.Repeat(paddingStr, delta-i) } for i := delta + 1; i < 2*delta; i++ { frames[i] = frames[2*delta-i] } return frames } ================================================ FILE: internal/cmd/readhttpresponse_test.go ================================================ package cmd import ( "strconv" "testing" "github.com/alecthomas/assert/v2" ) func TestMakeNightriderFrames(t *testing.T) { for i, tc := range []struct { shape string padding rune width int expected []string }{ { shape: "+", padding: ' ', width: 1, expected: []string{ "+", }, }, { shape: "+", padding: ' ', width: 2, expected: []string{ "+ ", " +", }, }, { shape: "+", padding: ' ', width: 3, expected: []string{ "+ ", " + ", " +", " + ", }, }, { shape: "<=>", padding: ' ', width: 1, expected: []string{ "<", }, }, { shape: "<=>", padding: ' ', width: 4, expected: []string{ "<=> ", " <=>", }, }, { shape: "<=>", padding: ' ', width: 5, expected: []string{ "<=> ", " <=> ", " <=>", " <=> ", }, }, { shape: "<=>", padding: ' ', width: 6, expected: []string{ "<=> ", " <=> ", " <=> ", " <=>", " <=> ", " <=> ", }, }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { actual := makeNightriderFrames(tc.shape, tc.padding, tc.width) assert.Equal(t, tc.expected, actual) }) } } ================================================ FILE: internal/cmd/removecmd.go ================================================ package cmd import ( "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) func (c *Config) newRemoveCmd() *cobra.Command { removeCmd := &cobra.Command{ Deprecated: "use forget or destroy instead", Use: "remove", Aliases: []string{"rm"}, RunE: c.runRemoveCmd, Long: mustLongHelp("remove"), Hidden: true, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeNone, ), } return removeCmd } func (c *Config) runRemoveCmd(cmd *cobra.Command, args []string) error { return chezmoi.ExitCodeError(1) } ================================================ FILE: internal/cmd/secretcmd.go ================================================ package cmd import "github.com/spf13/cobra" type secretCmdConfig struct { keyring secretKeyringCmdConfig } func (c *Config) newSecretCmd() *cobra.Command { secretCmd := &cobra.Command{ GroupID: groupIDInternal, Use: "secret", Args: cobra.NoArgs, Short: "Interact with a secret manager", Long: mustLongHelp("secret"), Example: example("secret"), Annotations: newAnnotations( persistentStateModeReadOnly, ), } if secretKeyringCmd := c.newSecretKeyringCmd(); secretKeyringCmd != nil { secretCmd.AddCommand(secretKeyringCmd) } return secretCmd } ================================================ FILE: internal/cmd/secretkeyringcmd.go ================================================ //go:build !freebsd || (freebsd && cgo) package cmd import ( "github.com/spf13/cobra" "github.com/zalando/go-keyring" ) type secretKeyringCmdConfig struct { delete secretKeyringDeleteCmdConfig get secretKeyringGetCmdConfig set secretKeyringSetCmdConfig } type secretKeyringDeleteCmdConfig struct { service string user string } type secretKeyringGetCmdConfig struct { service string user string } type secretKeyringSetCmdConfig struct { service string user string value string } func (c *Config) newSecretKeyringCmd() *cobra.Command { secretKeyringCmd := &cobra.Command{ Use: "keyring", Args: cobra.NoArgs, Short: "Interact with keyring", Annotations: newAnnotations( persistentStateModeNone, ), } secretKeyringDeleteCmd := &cobra.Command{ Use: "delete", Args: cobra.NoArgs, Short: "Delete a value from keyring", RunE: c.runSecretKeyringDeleteCmdE, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeNone, ), } secretKeyringDeleteCmd.Flags().StringVar(&c.secret.keyring.delete.service, "service", "", "service") secretKeyringDeleteCmd.Flags().StringVar(&c.secret.keyring.delete.user, "user", "", "user") markFlagsRequired(secretKeyringDeleteCmd, "service", "user") secretKeyringCmd.AddCommand(secretKeyringDeleteCmd) secretKeyringGetCmd := &cobra.Command{ Use: "get", Args: cobra.NoArgs, Short: "Get a value from keyring", RunE: c.runSecretKeyringGetCmdE, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeNone, ), } secretKeyringGetCmd.Flags().StringVar(&c.secret.keyring.get.service, "service", "", "service") secretKeyringGetCmd.Flags().StringVar(&c.secret.keyring.get.user, "user", "", "user") markFlagsRequired(secretKeyringGetCmd, "service", "user") secretKeyringCmd.AddCommand(secretKeyringGetCmd) secretKeyringSetCmd := &cobra.Command{ Use: "set", Args: cobra.NoArgs, Short: "Set a value in keyring", RunE: c.runSecretKeyringSetCmdE, Annotations: newAnnotations( doesNotRequireValidConfig, persistentStateModeNone, ), } secretKeyringSetCmd.Flags().StringVar(&c.secret.keyring.set.service, "service", "", "service") secretKeyringSetCmd.Flags().StringVar(&c.secret.keyring.set.user, "user", "", "user") secretKeyringSetCmd.Flags().StringVar(&c.secret.keyring.set.value, "value", "", "value") markFlagsRequired(secretKeyringSetCmd, "service", "user") secretKeyringCmd.AddCommand(secretKeyringSetCmd) return secretKeyringCmd } func (c *Config) runSecretKeyringDeleteCmdE(cmd *cobra.Command, args []string) error { return keyring.Delete(c.secret.keyring.delete.service, c.secret.keyring.delete.user) } func (c *Config) runSecretKeyringGetCmdE(cmd *cobra.Command, args []string) error { value, err := keyring.Get(c.secret.keyring.get.service, c.secret.keyring.get.user) if err != nil { return err } return c.writeOutputString(value, 0o666) } func (c *Config) runSecretKeyringSetCmdE(cmd *cobra.Command, args []string) error { value := c.secret.keyring.set.value if value == "" { var err error value, err = c.readPassword("Value: ", "value") if err != nil { return err } } return keyring.Set(c.secret.keyring.set.service, c.secret.keyring.set.user, value) } ================================================ FILE: internal/cmd/secretkeyringcmd_freebsdnocgo.go ================================================ //go:build freebsd && !cgo package cmd import "github.com/spf13/cobra" type secretKeyringCmdConfig struct{} func (c *Config) newSecretKeyringCmd() *cobra.Command { return nil } ================================================ FILE: internal/cmd/secrettemplatefuncs.go ================================================ package cmd import ( "bytes" "encoding/json" "os" "os/exec" "slices" "strings" "chezmoi.io/chezmoi/internal/chezmoilog" ) type secretConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Args []string `json:"args" mapstructure:"args" yaml:"args"` cache map[string][]byte } func (c *Config) secretTemplateFunc(args ...string) string { return string(bytes.TrimSpace(mustValue(c.secretOutput(args)))) } func (c *Config) secretJSONTemplateFunc(args ...string) any { output := mustValue(c.secretOutput(args)) var value any must(json.Unmarshal(output, &value)) return value } func (c *Config) secretOutput(args []string) ([]byte, error) { key := strings.Join(args, "\x00") if output, ok := c.Secret.cache[key]; ok { return output, nil } args = append(slices.Clone(c.Secret.Args), args...) cmd := exec.Command(c.Secret.Command, args...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { return nil, newCmdOutputError(cmd, output, err) } if c.Secret.cache == nil { c.Secret.cache = make(map[string][]byte) } c.Secret.cache[key] = output return output, nil } ================================================ FILE: internal/cmd/shellquote.go ================================================ package cmd import ( "regexp" "strings" ) // nonShellLiteralRx is a regular expression that matches anything that is not a // shell literal. var nonShellLiteralRx = regexp.MustCompile(`[^+\-./0-9=A-Z_a-z]`) // shellQuote returns s quoted as a shell argument, if necessary. func shellQuote(s string) string { const ( backslash = '\\' singleQuote = '\'' ) switch { case s == "": return "''" case nonShellLiteralRx.MatchString(s): result := make([]byte, 0, 2+len(s)) inSingleQuotes := false for _, b := range []byte(s) { switch b { case backslash: if !inSingleQuotes { result = append(result, singleQuote) inSingleQuotes = true } result = append(result, backslash, backslash) case singleQuote: if inSingleQuotes { result = append(result, singleQuote) inSingleQuotes = false } result = append(result, '\\', singleQuote) default: if !inSingleQuotes { result = append(result, singleQuote) inSingleQuotes = true } result = append(result, b) } } if inSingleQuotes { result = append(result, singleQuote) } return string(result) default: return s } } // shellQuoteCommand returns a string containing command and args shell quoted. func shellQuoteCommand(command string, args []string) string { if len(args) == 0 { return shellQuote(command) } elems := make([]string, 1+len(args)) elems[0] = shellQuote(command) for i, arg := range args { elems[i+1] = shellQuote(arg) } return strings.Join(elems, " ") } ================================================ FILE: internal/cmd/shellquote_test.go ================================================ package cmd import ( "testing" "github.com/alecthomas/assert/v2" ) func TestShellQuote(t *testing.T) { for s, expected := range map[string]string{ ``: `''`, `'`: `\'`, `''`: `\'\'`, `'a'`: `\''a'\'`, `\`: `'\\'`, `\a`: `'\\a'`, `$a`: `'$a'`, `a`: `a`, `a/b`: `a/b`, `a b`: `'a b'`, `--arg`: `--arg`, `--arg=value`: `--arg=value`, } { assert.Equal(t, expected, shellQuote(s), "quoting %q", s) } } func TestShellQuoteCommand(t *testing.T) { for _, tc := range []struct { command string args []string expected string }{ { command: "command", expected: "command", }, { command: "command with spaces", expected: "'command with spaces'", }, { command: "command", args: []string{"arg1"}, expected: "command arg1", }, { command: "command", args: []string{"arg1", "arg 2 with spaces"}, expected: "command arg1 'arg 2 with spaces'", }, } { assert.Equal(t, tc.expected, shellQuoteCommand(tc.command, tc.args)) } } ================================================ FILE: internal/cmd/sourcepathcmd.go ================================================ package cmd import ( "fmt" "strings" "github.com/spf13/cobra" ) func (c *Config) newSourcePathCmd() *cobra.Command { sourcePathCmd := &cobra.Command{ GroupID: groupIDInternal, Use: "source-path [target]...", Short: "Print the source path of a target", Long: mustLongHelp("source-path"), Example: example("source-path"), ValidArgsFunction: c.targetValidArgs, RunE: c.runSourcePathCmd, Annotations: newAnnotations( persistentStateModeReadMockWrite, ), } return sourcePathCmd } func (c *Config) runSourcePathCmd(cmd *cobra.Command, args []string) error { if len(args) == 0 { sourceDirAbsPath, err := c.getSourceDirAbsPath(nil) if err != nil { return err } return c.writeOutputString(sourceDirAbsPath.String()+"\n", 0o666) } sourceState, err := c.getSourceState(cmd.Context(), cmd) if err != nil { return err } sourceAbsPaths, err := c.sourceAbsPaths(sourceState, args) if err != nil { return err } builder := strings.Builder{} for _, sourceAbsPath := range sourceAbsPaths { fmt.Fprintln(&builder, sourceAbsPath) } return c.writeOutputString(builder.String(), 0o666) } ================================================ FILE: internal/cmd/sshcmd.go ================================================ package cmd import ( "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type sshCmdConfig struct { packageManager string shell bool } func (c *Config) newSSHCmd() *cobra.Command { sshCmd := &cobra.Command{ GroupID: groupIDRemote, Use: "ssh host", Short: "SSH to a host and initialize dotfiles", Long: mustLongHelp("ssh"), Example: example("ssh"), Args: cobra.MinimumNArgs(1), RunE: c.makeRunEWithSourceState(c.runSSHCmd), Annotations: newAnnotations( persistentStateModeReadWrite, ), } sshCmd.Flags().StringVarP(&c.ssh.packageManager, "package-manager", "p", c.ssh.packageManager, "Package manager") sshCmd.Flags().BoolVarP(&c.ssh.shell, "shell", "s", c.ssh.shell, "Execute shell afterwards") return sshCmd } func (c *Config) runSSHCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { return c.runInstallInitShellSh(sourceState, "ssh", []string{args[0]}, runInstallInitShellOptions{ args: args[1:], interactive: true, packageManager: c.ssh.packageManager, shell: c.ssh.shell, }, ) } ================================================ FILE: internal/cmd/statecmd.go ================================================ package cmd import ( "cmp" "errors" "fmt" "io/fs" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type stateCmdConfig struct { data stateDataCmdConfig delete stateDeleteCmdConfig deleteBucket stateDeleteBucketCmdConfig dump stateDumpCmdConfig get stateGetCmdConfig getBucket stateGetBucketCmdConfig set stateSetCmdConfig } type stateDataCmdConfig struct { format *choiceFlag } type stateDeleteCmdConfig struct { bucket string key string } type stateDeleteBucketCmdConfig struct { bucket string } type stateDumpCmdConfig struct { format *choiceFlag } type stateGetCmdConfig struct { bucket string key string } type stateGetBucketCmdConfig struct { bucket string format *choiceFlag } type stateSetCmdConfig struct { bucket string key string value string } func (c *Config) newStateCmd() *cobra.Command { stateCmd := &cobra.Command{ GroupID: groupIDInternal, Use: "state", Short: "Manipulate the persistent state", Long: mustLongHelp("state"), Example: example("state"), Annotations: newAnnotations( persistentStateModeNone, ), } stateDataCmd := &cobra.Command{ Use: "data", Short: "Print the raw data in the persistent state", Args: cobra.NoArgs, RunE: c.runStateDataCmd, Annotations: newAnnotations( persistentStateModeReadOnly, ), } stateDataCmd.Flags().VarP(c.state.data.format, "format", "f", "Output format") must(stateDataCmd.RegisterFlagCompletionFunc("format", c.state.data.format.FlagCompletionFunc())) stateCmd.AddCommand(stateDataCmd) stateDeleteCmd := &cobra.Command{ Use: "delete", Short: "Delete a value from the persistent state", Args: cobra.NoArgs, RunE: c.runStateDeleteCmd, Annotations: newAnnotations( persistentStateModeReadWrite, ), } stateDeleteCmd.Flags().StringVar(&c.state.delete.bucket, "bucket", c.state.delete.bucket, "Bucket") stateDeleteCmd.Flags().StringVar(&c.state.delete.key, "key", c.state.delete.key, "Key") stateCmd.AddCommand(stateDeleteCmd) stateDeleteBucketCmd := &cobra.Command{ Use: "delete-bucket", Short: "Delete a bucket from the persistent state", Args: cobra.NoArgs, RunE: c.runStateDeleteBucketCmd, Annotations: newAnnotations( persistentStateModeReadWrite, ), } stateDeleteBucketCmd.Flags().StringVar(&c.state.deleteBucket.bucket, "bucket", c.state.deleteBucket.bucket, "Bucket") stateCmd.AddCommand(stateDeleteBucketCmd) stateDumpCmd := &cobra.Command{ Use: "dump", Short: "Generate a dump of the persistent state", Args: cobra.NoArgs, RunE: c.runStateDumpCmd, Annotations: newAnnotations( persistentStateModeReadOnly, ), } stateDumpCmd.Flags().VarP(c.state.dump.format, "format", "f", "Output format") must(stateDumpCmd.RegisterFlagCompletionFunc("format", c.state.dump.format.FlagCompletionFunc())) stateCmd.AddCommand(stateDumpCmd) stateGetCmd := &cobra.Command{ Use: "get", Short: "Get a value from the persistent state", Args: cobra.NoArgs, RunE: c.runStateGetCmd, Annotations: newAnnotations( persistentStateModeReadOnly, ), } stateGetCmd.Flags().StringVar(&c.state.get.bucket, "bucket", c.state.get.bucket, "Bucket") stateGetCmd.Flags().StringVar(&c.state.get.key, "key", c.state.get.key, "Key") stateCmd.AddCommand(stateGetCmd) stateGetBucketCmd := &cobra.Command{ Use: "get-bucket", Short: "Get a bucket from the persistent state", Args: cobra.NoArgs, RunE: c.runStateGetBucketCmd, Annotations: newAnnotations( persistentStateModeReadOnly, ), } stateGetBucketCmd.Flags().StringVar(&c.state.getBucket.bucket, "bucket", c.state.getBucket.bucket, "bucket") stateGetBucketCmd.Flags().VarP(c.state.getBucket.format, "format", "f", "Output format") must(stateGetBucketCmd.RegisterFlagCompletionFunc("format", c.state.getBucket.format.FlagCompletionFunc())) stateCmd.AddCommand(stateGetBucketCmd) stateResetCmd := &cobra.Command{ Use: "reset", Short: "Reset the persistent state", Args: cobra.NoArgs, RunE: c.runStateResetCmd, Annotations: newAnnotations( modifiesDestinationDirectory, persistentStateModeNone, ), } stateCmd.AddCommand(stateResetCmd) stateSetCmd := &cobra.Command{ Use: "set", Short: "Set a value from the persistent state", Args: cobra.NoArgs, RunE: c.runStateSetCmd, Annotations: newAnnotations( persistentStateModeReadWrite, ), } stateSetCmd.Flags().StringVar(&c.state.set.bucket, "bucket", c.state.set.bucket, "Bucket") stateSetCmd.Flags().StringVar(&c.state.set.key, "key", c.state.set.key, "Key") stateSetCmd.Flags().StringVar(&c.state.set.value, "value", c.state.set.value, "Value") stateCmd.AddCommand(stateSetCmd) return stateCmd } func (c *Config) runStateDataCmd(cmd *cobra.Command, args []string) error { data, err := c.persistentState.Data() if err != nil { return err } return c.marshal(cmp.Or(c.state.data.format.String(), c.Format.String()), data) } func (c *Config) runStateDeleteCmd(cmd *cobra.Command, args []string) error { return c.persistentState.Delete([]byte(c.state.delete.bucket), []byte(c.state.delete.key)) } func (c *Config) runStateDeleteBucketCmd(cmd *cobra.Command, args []string) error { return c.persistentState.DeleteBucket([]byte(c.state.deleteBucket.bucket)) } func (c *Config) runStateDumpCmd(cmd *cobra.Command, args []string) error { data, err := chezmoi.PersistentStateData(c.persistentState, map[string][]byte{ "configState": chezmoi.ConfigStateBucket, "entryState": chezmoi.EntryStateBucket, "gitHubKeysState": gitHubKeysStateBucket, "gitHubLatestReleaseState": gitHubLatestReleaseStateBucket, "gitHubReleasesState": gitHubReleasesStateBucket, "gitHubTagsState": gitHubTagsStateBucket, "gitHubVersionReleaseState": gitHubVersionReleaseStateBucket, "gitRepoExternalState": chezmoi.GitRepoExternalStateBucket, "scriptState": chezmoi.ScriptStateBucket, }) if err != nil { return err } return c.marshal(cmp.Or(c.state.dump.format.String(), c.Format.String()), data) } func (c *Config) runStateGetCmd(cmd *cobra.Command, args []string) error { value, err := c.persistentState.Get([]byte(c.state.get.bucket), []byte(c.state.get.key)) if err != nil { return err } return c.writeOutput(value, 0o666) } func (c *Config) runStateGetBucketCmd(cmd *cobra.Command, args []string) error { data, err := chezmoi.PersistentStateBucketData(c.persistentState, []byte(c.state.getBucket.bucket)) if err != nil { return err } return c.marshal(cmp.Or(c.state.getBucket.format.String(), c.Format.String()), data) } func (c *Config) runStateResetCmd(cmd *cobra.Command, args []string) error { persistentStateFileAbsPath, err := c.persistentStateFile() if err != nil { return err } switch _, err := c.destSystem.Stat(persistentStateFileAbsPath); { case errors.Is(err, fs.ErrNotExist): return nil case err != nil: return err } if !c.force { switch choice, err := c.promptChoice(fmt.Sprintf("Remove %s", persistentStateFileAbsPath), choicesYesNoQuit); { case err != nil: return err case choice == "yes": case choice == "no": fallthrough case choice == "quit": return nil } } return c.destSystem.RemoveAll(persistentStateFileAbsPath) } func (c *Config) runStateSetCmd(cmd *cobra.Command, args []string) error { return c.persistentState.Set([]byte(c.state.set.bucket), []byte(c.state.set.key), []byte(c.state.set.value)) } ================================================ FILE: internal/cmd/statuscmd.go ================================================ package cmd import ( "fmt" "io/fs" "log/slog" "strings" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoilog" ) type statusCmdConfig struct { Exclude *chezmoi.EntryTypeSet `json:"exclude" mapstructure:"exclude" yaml:"exclude"` PathStyle *choiceFlag `json:"pathStyle" mapstructure:"pathStyle" yaml:"pathStyle"` include *chezmoi.EntryTypeSet init bool parentDirs bool recursive bool } func (c *Config) newStatusCmd() *cobra.Command { statusCmd := &cobra.Command{ GroupID: groupIDDaily, Use: "status [target]...", Short: "Show the status of targets", Long: mustLongHelp("status"), Example: example("status"), ValidArgsFunction: c.targetValidArgs, RunE: c.runStatusCmd, Annotations: newAnnotations( dryRun, persistentStateModeReadMockWrite, requiresSourceDirectory, ), } statusCmd.Flags().VarP(c.Status.Exclude, "exclude", "x", "Exclude entry types") statusCmd.Flags().VarP(c.Status.PathStyle, "path-style", "p", "Path style") must(statusCmd.RegisterFlagCompletionFunc("path-style", c.Status.PathStyle.FlagCompletionFunc())) statusCmd.Flags().VarP(c.Status.include, "include", "i", "Include entry types") statusCmd.Flags().BoolVar(&c.Status.init, "init", c.Status.init, "Recreate config file from template") statusCmd.Flags(). BoolVarP(&c.Status.parentDirs, "parent-dirs", "P", c.Status.parentDirs, "Show status of all parent directories") statusCmd.Flags().BoolVarP(&c.Status.recursive, "recursive", "r", c.Status.recursive, "Recurse into subdirectories") return statusCmd } func (c *Config) runStatusCmd(cmd *cobra.Command, args []string) error { builder := strings.Builder{} preApplyFunc := func(targetRelPath chezmoi.RelPath, targetEntryState, lastWrittenEntryState, actualEntryState *chezmoi.EntryState) error { c.logger.Info("statusPreApplyFunc", chezmoilog.Stringer("targetRelPath", targetRelPath), slog.Any("targetEntryState", targetEntryState), slog.Any("lastWrittenEntryState", lastWrittenEntryState), slog.Any("actualEntryState", actualEntryState), ) var ( x = ' ' y = ' ' ) switch { case targetEntryState.Type == chezmoi.EntryStateTypeScript: y = 'R' case !targetEntryState.Equivalent(actualEntryState): x = statusRune(lastWrittenEntryState, actualEntryState) y = statusRune(actualEntryState, targetEntryState) } if x != ' ' || y != ' ' { var path string switch pathStyle := c.Status.PathStyle.String(); pathStyle { case pathStyleAbsolute: path = c.DestDirAbsPath.Join(targetRelPath).String() case pathStyleRelative: path = targetRelPath.String() default: return fmt.Errorf("%s: invalid path style", pathStyle) } fmt.Fprintf(&builder, "%c%c %s\n", x, y, path) } return fs.SkipDir } if err := c.applyArgs(cmd.Context(), c.destSystem, c.DestDirAbsPath, args, applyArgsOptions{ cmd: cmd, filter: chezmoi.NewEntryTypeFilter(c.Status.include.Bits(), c.Status.Exclude.Bits()), init: c.Status.init, parentDirs: c.Status.parentDirs, recursive: c.Status.recursive, umask: c.Umask, preApplyFunc: preApplyFunc, }); err != nil { return err } return c.writeOutputString(builder.String(), 0o666) } func statusRune(fromState, toState *chezmoi.EntryState) rune { if fromState == nil || fromState.Equivalent(toState) { return ' ' } switch toState.Type { case chezmoi.EntryStateTypeRemove: return 'D' case chezmoi.EntryStateTypeDir, chezmoi.EntryStateTypeFile, chezmoi.EntryStateTypeSymlink: switch fromState.Type { case chezmoi.EntryStateTypeRemove: return 'A' default: return 'M' } case chezmoi.EntryStateTypeScript: return 'R' default: return '?' } } ================================================ FILE: internal/cmd/statuscmd_test.go ================================================ package cmd import ( "strings" "testing" "github.com/alecthomas/assert/v2" vfs "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestStatusCmd(t *testing.T) { for _, tc := range []struct { name string root any args []string postApplyTests []any stdoutStr string }{ { name: "add_file", root: map[string]any{ "/home/user/.local/share/chezmoi/dot_bashrc": "# contents of .bashrc\n", }, args: []string{"~/.bashrc"}, stdoutStr: chezmoitest.JoinLines( ` A .bashrc`, ), postApplyTests: []any{ vfst.TestPath("/home/user/.local/share/chezmoi/dot_bashrc", vfst.TestModeIsRegular(), vfst.TestModePerm(0o666&^chezmoitest.Umask), vfst.TestContentsString("# contents of .bashrc\n"), ), }, }, { name: "update_symlink", root: map[string]any{ "/home/user/.symlink": &vfst.Symlink{Target: "old-target"}, "/home/user/.local/share/chezmoi/symlink_dot_symlink": "new-target\n", }, args: []string{"~/.symlink"}, postApplyTests: []any{ vfst.TestPath("/home/user/.symlink", vfst.TestSymlinkTarget("new-target"), ), }, stdoutStr: chezmoitest.JoinLines( ` M .symlink`, ), }, { name: "path_style", root: map[string]any{ "/home/user/.config/chezmoi/chezmoi.toml": chezmoitest.JoinLines( `[status]`, ` pathStyle = "relative"`, ), "/home/user/.local/share/chezmoi": &vfst.Dir{Perm: 0o755}, }, }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { stdout := strings.Builder{} config1 := newTestConfig(t, fileSystem, withStdout(&stdout)) assert.NoError(t, config1.execute(append([]string{"status"}, tc.args...))) assert.Equal(t, tc.stdoutStr, stdout.String()) config2 := newTestConfig(t, fileSystem) assert.NoError(t, config2.execute(append([]string{"apply"}, tc.args...))) vfst.RunTests(t, fileSystem, "", tc.postApplyTests...) stdout.Reset() config3 := newTestConfig(t, fileSystem, withStdout(&stdout)) assert.NoError(t, config3.execute(append([]string{"status"}, tc.args...))) assert.Zero(t, stdout.String()) }) }) } } ================================================ FILE: internal/cmd/symlinks_test.go ================================================ package cmd import ( "io/fs" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestSymlinks(t *testing.T) { for _, tc := range []struct { name string extraRoot any args []string tests []any }{ { name: "symlink_forward_slash_unix", extraRoot: map[string]any{ "/home/user/.local/share/chezmoi/symlink_dot_symlink": ".dir/file", }, args: []string{"~/.symlink"}, tests: []any{ vfst.TestPath("/home/user/.symlink", vfst.TestModeType(fs.ModeSymlink), vfst.TestSymlinkTarget(".dir/file"), ), }, }, { name: "symlink_forward_slash_windows", extraRoot: map[string]any{ "/home/user/.local/share/chezmoi/symlink_dot_symlink": ".dir/file", }, args: []string{"~/.symlink"}, tests: []any{ vfst.TestPath("/home/user/.symlink", vfst.TestModeType(fs.ModeSymlink), vfst.TestSymlinkTarget(".dir\\file"), ), }, }, { name: "symlink_backward_slash_windows", extraRoot: map[string]any{ "/home/user/.local/share/chezmoi/symlink_dot_symlink": ".dir\\file", }, args: []string{"~/.symlink"}, tests: []any{ vfst.TestPath("/home/user/.symlink", vfst.TestModeType(fs.ModeSymlink), vfst.TestSymlinkTarget(".dir\\file"), ), }, }, { name: "symlink_mixed_slash_windows", extraRoot: map[string]any{ "/home/user/.local/share/chezmoi/symlink_dot_symlink": ".dir/subdir\\file", }, args: []string{"~/.symlink"}, tests: []any{ vfst.TestPath("/home/user/.symlink", vfst.TestModeType(fs.ModeSymlink), vfst.TestSymlinkTarget(".dir\\subdir\\file"), ), }, }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.SkipUnlessGOOS(t, tc.name) chezmoitest.WithTestFS(t, nil, func(fileSystem vfs.FS) { if tc.extraRoot != nil { assert.NoError(t, vfst.NewBuilder().Build(fileSystem, tc.extraRoot)) } assert.NoError(t, newTestConfig(t, fileSystem).execute(append([]string{"apply"}, tc.args...))) vfst.RunTests(t, fileSystem, "", tc.tests) assert.NoError(t, newTestConfig(t, fileSystem).execute([]string{"verify"})) }) }) } } ================================================ FILE: internal/cmd/targetpathcmd.go ================================================ package cmd import ( "strings" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) func (c *Config) newTargetPathCmd() *cobra.Command { targetPathCmd := &cobra.Command{ GroupID: groupIDInternal, Use: "target-path [source-path]...", Short: "Print the target path of a source path", Long: mustLongHelp("target-path"), Example: example("target-path"), RunE: c.runTargetPathCmd, Annotations: newAnnotations( persistentStateModeReadMockWrite, ), } return targetPathCmd } func (c *Config) runTargetPathCmd(cmd *cobra.Command, args []string) error { if len(args) == 0 { return c.writeOutputString(c.DestDirAbsPath.String()+"\n", 0o666) } builder := strings.Builder{} for _, arg := range args { argAbsPath, err := chezmoi.NewAbsPathFromExtPath(arg, c.homeDirAbsPath) if err != nil { return err } argRelPath, err := argAbsPath.TrimDirPrefix(c.sourceDirAbsPath) if err != nil { return err } var sourceRelPath chezmoi.SourceRelPath switch fileInfo, err := c.sourceSystem.Stat(argAbsPath); { case err != nil: return err case fileInfo.IsDir(): sourceRelPath = chezmoi.NewSourceRelDirPath(argRelPath.String()) default: sourceRelPath = chezmoi.NewSourceRelPath(argRelPath.String()) } targetRelPath, err := sourceRelPath.TargetRelPath(c.encryption.EncryptedSuffix()) if err != nil { return err } if _, err := builder.WriteString(c.DestDirAbsPath.String()); err != nil { return err } if err := builder.WriteByte('/'); err != nil { return err } if _, err := builder.WriteString(targetRelPath.String()); err != nil { return err } if err := builder.WriteByte('\n'); err != nil { return err } } return c.writeOutputString(builder.String(), 0o666) } ================================================ FILE: internal/cmd/templatefuncs.go ================================================ package cmd import ( "context" "encoding/hex" "encoding/json" "errors" "fmt" "io" "io/fs" "maps" "net/http" "os" "os/exec" "path/filepath" "regexp" "runtime" "slices" "strconv" "strings" "github.com/bradenhilton/mozillainstallhash" "github.com/itchyny/gojq" "gopkg.in/ini.v1" "howett.net/plist" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoilog" ) type ioregData struct { value map[string]any } // An emptyPathElementError is returned when a path element is empty. type emptyPathElementError struct { index int } func (e emptyPathElementError) Error() string { return fmt.Sprintf("empty path element at index %d", e.index) } // An invalidPathElementTypeError is returned when an element in a path has an // invalid type. type invalidPathElementTypeError struct { element any } func (e invalidPathElementTypeError) Error() string { return fmt.Sprintf("%v: invalid path element type %T", e.element, e.element) } // An invalidPathTypeError is returned when a path has an invalid type. type invalidPathTypeError struct { path any } func (e invalidPathTypeError) Error() string { return fmt.Sprintf("%v: invalid path type %T", e.path, e.path) } // errEmptyPath is returned when a path is empty. var errEmptyPath = errors.New("empty path") // needsQuoteRx matches any string that contains non-printable characters, // double quotes, or a backslash. var needsQuoteRx = regexp.MustCompile(`[^\x21\x23-\x5b\x5d-\x7e]`) func (c *Config) commentTemplateFunc(prefix, s string) string { var builder strings.Builder for line := range strings.Lines(s) { builder.WriteString(prefix) builder.WriteString(line) } return builder.String() } func (c *Config) deleteValueAtPathTemplateFunc(path string, dict map[string]any) any { keys, lastKey := mustValues(keysFromPath(path)) currentMap := dict for _, key := range keys { value, ok := currentMap[key] if !ok { return dict } nestedMap, ok := value.(map[string]any) if !ok { return dict } currentMap = nestedMap } delete(currentMap, lastKey) return dict } func (c *Config) eqFoldTemplateFunc(first, second string, more ...string) bool { if strings.EqualFold(first, second) { return true } for _, s := range more { if strings.EqualFold(first, s) { return true } } return false } func (c *Config) ensureLinePrefixTemplateFunc(args ...string) string { var prefix, prefixToAdd, s string switch len(args) { case 2: prefix, prefixToAdd, s = args[0], args[0], args[1] case 3: prefix, prefixToAdd, s = args[0], args[1], args[2] default: panic(fmt.Errorf("expected 2 or 3 arguments, got %d", len(args))) } var builder strings.Builder for line := range strings.Lines(s) { if !strings.HasPrefix(line, prefix) { builder.WriteString(prefixToAdd) } builder.WriteString(line) } return builder.String() } func (c *Config) execTemplateFunc(name string, args ...string) bool { cmd := exec.Command(name, args...) var exitError *exec.ExitError switch err := chezmoilog.LogCmdRun(c.logger, cmd); { case err == nil: return true case errors.As(err, &exitError): return false default: panic(err) } } func (c *Config) findExecutableTemplateFunc(file string, pathList any) string { files := []string{file} paths, err := anyToStringSlice(pathList) if err != nil { panic(fmt.Errorf("path list: %w", err)) } path, err := chezmoi.FindExecutable(files, paths) if err != nil { panic(err) } return path } func (c *Config) findOneExecutableTemplateFunc(fileList, pathList any) string { files, err := anyToStringSlice(fileList) if err != nil { panic(fmt.Errorf("file list: %w", err)) } paths, err := anyToStringSlice(pathList) if err != nil { panic(fmt.Errorf("path list: %w", err)) } path, err := chezmoi.FindExecutable(files, paths) if err != nil { panic(err) } return path } func (c *Config) fromIniTemplateFunc(s string) map[string]any { return iniFileToMap(mustValue(ini.LoadSources(ini.LoadOptions{ UnescapeValueDoubleQuotes: true, }, []byte(s)))) } // fromJsonTemplateFunc parses s as JSON and returns the result. In contrast to // encoding/json, numbers are represented as int64s or float64s if possible. // //nolint:revive,staticcheck func (c *Config) fromJsonTemplateFunc(s string) any { var value any must(chezmoi.FormatJSON.Unmarshal([]byte(s), &value)) return value } // fromJsoncTemplateFunc parses s as JSONC and returns the result. In contrast // to encoding/json, numbers are represented as int64s or float64s if possible. func (c *Config) fromJsoncTemplateFunc(s string) any { var value any must(chezmoi.FormatJSONC.Unmarshal([]byte(s), &value)) return value } func (c *Config) fromTomlTemplateFunc(s string) any { var value map[string]any must(chezmoi.FormatTOML.Unmarshal([]byte(s), &value)) return value } func (c *Config) fromYamlTemplateFunc(s string) any { var value any must(chezmoi.FormatYAML.Unmarshal([]byte(s), &value)) return value } func (c *Config) getRedirectedURLTemplateFunc(requestURL string) string { client := mustValue(c.getHTTPClient()) req := mustValue(http.NewRequestWithContext(context.Background(), http.MethodHead, requestURL, http.NoBody)) resp := mustValue(client.Do(req)) //nolint:bodyclose defer resp.Body.Close() return resp.Request.URL.String() } func (c *Config) globTemplateFunc(pattern string) []string { defer func() { value := recover() err := os.Chdir(c.commandDirAbsPath.String()) if value != nil { panic(value) } if err != nil { panic(err) } }() must(os.Chdir(c.DestDirAbsPath.String())) return mustValue(chezmoi.Glob(c.fileSystem, filepath.ToSlash(pattern))) } func (c *Config) hexDecodeTemplateFunc(s string) string { return string(mustValue(hex.DecodeString(s))) } func (c *Config) hexEncodeTemplateFunc(s string) string { return hex.EncodeToString([]byte(s)) } func (c *Config) includeTemplateFunc(filename string) string { searchDirAbsPaths := []chezmoi.AbsPath{c.sourceDirAbsPath} return string(mustValue(c.readFile(filename, searchDirAbsPaths))) } func (c *Config) includeTemplateTemplateFunc(filename string, args ...any) string { var data any switch len(args) { case 0: // Do nothing. case 1: data = args[0] default: panic(fmt.Errorf("expected 0 or 1 arguments, got %d", len(args))) } searchDirAbsPaths := []chezmoi.AbsPath{ c.sourceDirAbsPath.JoinString(chezmoi.TemplatesDirName), c.sourceDirAbsPath, } contents := mustValue(c.readFile(filename, searchDirAbsPaths)) tmpl := mustValue(chezmoi.ParseTemplate(filename, contents, chezmoi.TemplateOptions{ Funcs: c.templateFuncs, Options: slices.Clone(c.Template.Options), })) return string(mustValue(tmpl.Execute(data))) } func (c *Config) ioregTemplateFunc() map[string]any { if runtime.GOOS != "darwin" { return nil } if c.ioregData.value != nil { return c.ioregData.value } command := "ioreg" args := []string{"-a", "-l"} cmd := exec.Command(command, args...) cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { panic(newCmdOutputError(cmd, output, err)) } var value map[string]any _ = mustValue(plist.Unmarshal(output, &value)) c.ioregData.value = value return value } func (c *Config) joinPathTemplateFunc(elems ...any) string { args, err := anyToStringSlice(elems) if err != nil { panic(err) } return filepath.Join(args...) } func (c *Config) jqTemplateFunc(source string, input any) any { query := mustValue(gojq.Parse(source)) code := mustValue(gojq.Compile(query)) iter := code.Run(input) var result []any for { value, ok := iter.Next() if !ok { break } if err, ok := value.(error); ok { panic(err) } result = append(result, value) } return result } func (c *Config) lookPathTemplateFunc(file string) string { switch path, err := chezmoi.LookPath(file); { case err == nil: return path case errors.Is(err, exec.ErrNotFound): return "" case errors.Is(err, fs.ErrNotExist): return "" default: panic(err) } } func (c *Config) isExecutableTemplateFunc(file string) bool { switch fileInfo, err := c.fileSystem.Stat(file); { case err == nil: return chezmoi.IsExecutable(fileInfo) case errors.Is(err, fs.ErrNotExist): return false default: panic(err) } } func (c *Config) lstatTemplateFunc(name string) any { switch fileInfo, err := c.fileSystem.Lstat(name); { case err == nil: return fileInfoToMap(fileInfo) case errors.Is(err, fs.ErrNotExist): return nil default: panic(err) } } func (c *Config) mozillaInstallHashTemplateFunc(path string) string { return mustValue(mozillainstallhash.MozillaInstallHash(path)) } func (c *Config) outputTemplateFunc(name string, args ...string) string { cmd := exec.Command(name, args...) cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { panic(newCmdOutputError(cmd, output, err)) } return string(output) } func (c *Config) outputListTemplateFunc(name string, args []any) string { slice, err := anyToStringSlice(args) if err != nil { panic(fmt.Errorf("args list: %w", err)) } return c.outputTemplateFunc(name, slice...) } func (c *Config) pruneEmptyDictsTemplateFunc(dict map[string]any) map[string]any { pruneEmptyMaps(dict) return dict } func (c *Config) quoteTemplateFunc(list ...any) string { ss := make([]string, 0, len(list)) for _, elem := range list { if elem != nil { ss = append(ss, strconv.Quote(anyToString(elem))) } } return strings.Join(ss, " ") } func (c *Config) squoteTemplateFunc(list ...any) string { ss := make([]string, 0, len(list)) for _, elem := range list { if elem != nil { ss = append(ss, "'"+anyToString(elem)+"'") } } return strings.Join(ss, " ") } func (c *Config) quoteListTemplateFunc(list any) []string { stringSlice, err := anyToStringSlice(list) if err != nil { panic(err) } result := make([]string, len(stringSlice)) for i, elem := range stringSlice { result[i] = strconv.Quote(anyToString(elem)) } return result } func (c *Config) readFile(filename string, searchDirAbsPaths []chezmoi.AbsPath) ([]byte, error) { if filepath.IsAbs(filename) { absPath, err := chezmoi.NewAbsPathFromExtPath(filename, c.homeDirAbsPath) if err != nil { return nil, err } return c.fileSystem.ReadFile(absPath.String()) } var data []byte var err error for _, searchDir := range searchDirAbsPaths { data, err = c.fileSystem.ReadFile(searchDir.JoinString(filename).String()) if !errors.Is(err, fs.ErrNotExist) { return data, err } } return data, err } func (c *Config) replaceAllRegexTemplateFunc(expr, repl, s string) string { return regexp.MustCompile(expr).ReplaceAllString(s, repl) } func (c *Config) setValueAtPathTemplateFunc(path, value, dict any) any { keys, lastKey := mustValues(keysFromPath(path)) result, ok := dict.(map[string]any) if !ok { result = make(map[string]any) } currentMap := result for _, key := range keys { if value, ok := currentMap[key]; ok { if nestedMap, ok := value.(map[string]any); ok { currentMap = nestedMap } else { nestedMap := make(map[string]any) currentMap[key] = nestedMap currentMap = nestedMap } } else { nestedMap := make(map[string]any) currentMap[key] = nestedMap currentMap = nestedMap } } currentMap[lastKey] = value return result } func (c *Config) splitListTemplateFunc(sep, s string) []any { strSlice := strings.Split(s, sep) result := make([]any, len(strSlice)) for i, v := range strSlice { result[i] = v } return result } func (c *Config) statTemplateFunc(name string) any { switch fileInfo, err := c.fileSystem.Stat(name); { case err == nil: return fileInfoToMap(fileInfo) case errors.Is(err, fs.ErrNotExist): return nil default: panic(err) } } func (c *Config) toIniTemplateFunc(data map[string]any) string { var builder strings.Builder must(writeIniMap(&builder, data, "")) return builder.String() } func (c *Config) toPrettyJsonTemplateFunc(args ...any) string { //nolint:revive,staticcheck var ( indent = " " value any ) switch len(args) { case 1: value = args[0] case 2: var ok bool indent, ok = args[0].(string) if !ok { panic(fmt.Errorf("arg 1: expected a string, got a %T", args[0])) } value = args[1] default: panic(fmt.Errorf("expected 1 or 2 arguments, got %d", len(args))) } var builder strings.Builder encoder := json.NewEncoder(&builder) encoder.SetEscapeHTML(false) encoder.SetIndent("", indent) must(encoder.Encode(value)) return builder.String() } func (c *Config) toStringTemplateFunc(value any) string { return anyToString(value) } func (c *Config) toStringsTemplateFunc(values ...any) []string { result, err := anyToStringSlice(values) if err != nil { panic(err) } return result } func (c *Config) toTomlTemplateFunc(data any) string { return string(mustValue(chezmoi.FormatTOML.Marshal(data))) } func (c *Config) toYamlTemplateFunc(data any) string { return string(mustValue(chezmoi.FormatYAML.Marshal(data))) } func (c *Config) warnfTemplateFunc(format string, args ...any) string { c.errorf("warning: "+format+"\n", args...) return "" } func anyToString(value any) string { switch value := value.(type) { case bool: return strconv.FormatBool(value) case *bool: if value == nil { return "false" } return strconv.FormatBool(*value) case []byte: return string(value) case float64: return strconv.FormatFloat(value, 'f', -1, 64) case *float64: if value == nil { return "0" } return strconv.FormatFloat(*value, 'f', -1, 64) case int: return strconv.Itoa(value) case *int: if value == nil { return "0" } return strconv.Itoa(*value) case string: return value case *string: if value == nil { return "" } return *value case error: return value.Error() case fmt.Stringer: return value.String() default: return fmt.Sprintf("%v", value) } } func anyToStringSlice(value any) ([]string, error) { switch value := value.(type) { case []any: result := make([]string, 0, len(value)) for _, elem := range value { elemStrs, err := anyToStringSlice(elem) if err != nil { return nil, err } result = append(result, elemStrs...) } return result, nil case []bool: result := make([]string, len(value)) for i, value := range value { result[i] = strconv.FormatBool(value) } return result, nil case []float64: result := make([]string, len(value)) for i, value := range value { result[i] = strconv.FormatFloat(value, 'f', -1, 64) } return result, nil case []int: result := make([]string, len(value)) for i, value := range value { result[i] = strconv.Itoa(value) } return result, nil case []string: return value, nil default: return []string{anyToString(value)}, nil } } func fileInfoToMap(fileInfo fs.FileInfo) map[string]any { return map[string]any{ "name": fileInfo.Name(), "size": fileInfo.Size(), "mode": int(fileInfo.Mode()), "perm": int(fileInfo.Mode().Perm()), "modTime": fileInfo.ModTime().Unix(), "isDir": fileInfo.IsDir(), "type": chezmoi.FileModeTypeNames[fileInfo.Mode()&fs.ModeType], } } func iniFileToMap(file *ini.File) map[string]any { m := make(map[string]any) for _, section := range file.Sections() { if section.Name() == ini.DefaultSection { for _, k := range section.Keys() { m[k.Name()] = k.Value() } } else { m[section.Name()] = iniSectionToMap(section) } } return m } func iniSectionToMap(section *ini.Section) map[string]any { m := make(map[string]any) for _, s := range section.ChildSections() { m[s.Name()] = iniSectionToMap(s) } for _, k := range section.Keys() { m[k.Name()] = k.Value() } return m } func keysFromPath(path any) ([]string, string, error) { switch path := path.(type) { case string: if path == "" { return nil, "", errEmptyPath } keys := strings.Split(path, ".") for i, key := range keys { if key == "" { return nil, "", emptyPathElementError{ index: i, } } } return keys[:len(keys)-1], keys[len(keys)-1], nil case []any: if len(path) == 0 { return nil, "", errEmptyPath } keys := make([]string, len(path)) for i, pathElement := range path { switch pathElementStr, ok := pathElement.(string); { case !ok: return nil, "", invalidPathElementTypeError{ element: pathElement, } case pathElementStr == "": return nil, "", emptyPathElementError{ index: i, } default: keys[i] = pathElementStr } } return keys[:len(keys)-1], keys[len(keys)-1], nil case []string: if len(path) == 0 { return nil, "", errEmptyPath } for i, key := range path { if key == "" { return nil, "", emptyPathElementError{ index: i, } } } return path[:len(path)-1], path[len(path)-1], nil case nil: return nil, "", errEmptyPath default: return nil, "", invalidPathTypeError{ path: path, } } } func nestedMapAtPath(m map[string]any, path any) (map[string]any, string, error) { keys, lastKey, err := keysFromPath(path) if err != nil { return nil, "", err } for _, key := range keys { nestedMap, ok := m[key].(map[string]any) if !ok { return nil, "", nil } m = nestedMap } return m, lastKey, nil } func writeIniMap(w io.Writer, data map[string]any, sectionPrefix string) error { // Write keys in order and accumulate subsections. type subsection struct { key string value map[string]any } var subsections []subsection for _, key := range slices.Sorted(maps.Keys(data)) { switch value := data[key].(type) { case bool: fmt.Fprintf(w, "%s = %t\n", key, value) case float32, float64: fmt.Fprintf(w, "%s = %f\n", key, value) case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr: fmt.Fprintf(w, "%s = %d\n", key, value) case map[string]any: subsection := subsection{ key: key, value: value, } subsections = append(subsections, subsection) case string: fmt.Fprintf(w, "%s = %s\n", key, maybeQuote(value)) default: return fmt.Errorf("%s%s: %T: unsupported type", sectionPrefix, key, value) } } // Write subsections in order. for _, subsection := range subsections { if _, err := fmt.Fprintf(w, "\n[%s%s]\n", sectionPrefix, subsection.key); err != nil { return err } if err := writeIniMap(w, subsection.value, sectionPrefix+subsection.key+"."); err != nil { return err } } return nil } func maybeQuote(s string) string { if needsQuote(s) { return strconv.Quote(s) } return s } func needsQuote(s string) bool { if s == "" { return true } if needsQuoteRx.MatchString(s) { return true } if _, err := strconv.ParseBool(s); err == nil { return true } if _, err := strconv.ParseFloat(s, 64); err == nil { return true } return false } // pruneEmptyMaps prunes all empty maps from m and returns if m is now empty // itself. func pruneEmptyMaps(m map[string]any) bool { for key, value := range m { nestedMap, ok := value.(map[string]any) if !ok { continue } if pruneEmptyMaps(nestedMap) { delete(m, key) } } return len(m) == 0 } ================================================ FILE: internal/cmd/templatefuncs_test.go ================================================ package cmd import ( "errors" "net/http" "net/http/httptest" "strconv" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoiassert" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestCommentTemplateFunc(t *testing.T) { prefix := "# " for i, tc := range []struct { s string expected string }{ { s: "", expected: "", }, { s: "line", expected: "# line", }, { s: "\n", expected: "# \n", }, { s: "\n\n", expected: "# \n# \n", }, { s: "line1\nline2", expected: "# line1\n# line2", }, { s: "line1\nline2\n", expected: "# line1\n# line2\n", }, { s: "line1\n\nline3\n", expected: "# line1\n# \n# line3\n", }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { c := &Config{} assert.Equal(t, tc.expected, c.commentTemplateFunc(prefix, tc.s)) }) } } func TestDeleteValueAtPathTemplateFunc(t *testing.T) { for _, tc := range []struct { name string dict map[string]any path string expected any expectedErr string }{ { name: "empty", expectedErr: "empty path", }, { name: "outer", dict: map[string]any{ "key": "value", }, path: "key", expected: map[string]any{}, }, { name: "inner", dict: map[string]any{ "key1": map[string]any{ "key2a": "value2a", "key2b": "value2b", }, }, path: "key1.key2a", expected: map[string]any{ "key1": map[string]any{ "key2b": "value2b", }, }, }, { name: "missing", dict: map[string]any{ "key": "value", }, path: "missingKey", expected: map[string]any{ "key": "value", }, }, { name: "missing_inner", dict: map[string]any{ "key1": map[string]any{ "key2": 0, }, }, path: "key1.key3", expected: map[string]any{ "key1": map[string]any{ "key2": 0, }, }, }, { name: "missing_depth2", dict: map[string]any{ "key1": map[string]any{ "key2": map[string]any{ "key3": 0, }, }, }, path: "key1.key2.missingKey", expected: map[string]any{ "key1": map[string]any{ "key2": map[string]any{ "key3": 0, }, }, }, }, { name: "not_an_inner_dict", dict: map[string]any{ "key1": map[string]any{ "key2": 0, }, }, path: "key1.key2.key3", expected: map[string]any{ "key1": map[string]any{ "key2": 0, }, }, }, } { t.Run(tc.name, func(t *testing.T) { var c Config if tc.expectedErr == "" { actual := c.deleteValueAtPathTemplateFunc(tc.path, tc.dict) assert.Equal(t, tc.expected, actual) } else { chezmoiassert.PanicsWithErrorString(t, tc.expectedErr, func() { c.deleteValueAtPathTemplateFunc(tc.path, tc.dict) }) } }) } } func TestFromJson(t *testing.T) { c, err := newConfig() assert.NoError(t, err) for i, tc := range []struct { s string expected any }{ { s: `{"key":1}`, expected: map[string]any{"key": int64(1)}, }, { s: `{"key":2.2}`, expected: map[string]any{"key": 2.2}, }, { s: `{"key":[1,2.2,3]}`, expected: map[string]any{"key": []any{int64(1), 2.2, int64(3)}}, }, { s: `{"key":1}`, expected: map[string]any{"key": int64(1)}, }, { s: `{"key":1E400}`, expected: map[string]any{"key": "1E400"}, }, { s: `{"key":3.141592653589793238462643383279}`, expected: map[string]any{"key": 3.141592653589793238462643383279}, }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { assert.Equal(t, tc.expected, c.fromJsonTemplateFunc(tc.s)) }) } } func TestPruneEmptyDicts(t *testing.T) { for _, tc := range []struct { name string dict map[string]any expected map[string]any }{ { name: "nil", dict: nil, expected: nil, }, { name: "empty", dict: map[string]any{}, expected: map[string]any{}, }, { name: "nested_empty", dict: map[string]any{ "key1": map[string]any{}, "key2": 0, }, expected: map[string]any{ "key2": 0, }, }, } { t.Run(tc.name, func(t *testing.T) { assert.Equal(t, tc.expected, (&Config{}).pruneEmptyDictsTemplateFunc(tc.dict)) }) } } func TestQuoteTemplateFunc(t *testing.T) { a := "a" for _, tc := range []struct { name string values []any expected string }{ { name: "empty", }, { name: "single", values: []any{"a"}, expected: `"a"`, }, { name: "multiple", values: []any{"a", "b", "c"}, expected: `"a" "b" "c"`, }, { name: "quotes", values: []any{`"a"`, "b", "c"}, expected: `"\"a\"" "b" "c"`, }, { name: "ints", values: []any{1, 2, 3}, expected: `"1" "2" "3"`, }, { name: "string_pointer", values: []any{&a}, expected: `"a"`, }, { name: "byte_slice", values: []any{[]byte{'a'}}, expected: `"a"`, }, { name: "escaped_chars", values: []any{"\\\"a\\\"\n"}, expected: `"\\\"a\\\"\n"`, }, } { t.Run(tc.name, func(t *testing.T) { var c Config assert.Equal(t, tc.expected, c.quoteTemplateFunc(tc.values...)) }) } } func TestSquoteTemplateFunc(t *testing.T) { a := "a" for _, tc := range []struct { name string values []any expected string }{ { name: "empty", }, { name: "single", values: []any{"a"}, expected: `'a'`, }, { name: "multiple", values: []any{"a", "b", "c"}, expected: `'a' 'b' 'c'`, }, { name: "quotes", values: []any{`'a'`, `"b"`, "c"}, expected: `''a'' '"b"' 'c'`, }, { name: "ints", values: []any{1, 2, 3}, expected: `'1' '2' '3'`, }, { name: "string_pointer", values: []any{&a}, expected: `'a'`, }, { name: "byte_slice", values: []any{[]byte{'a'}}, expected: `'a'`, }, { name: "escaped_chars", values: []any{"\\'a\\'\n"}, expected: "'\\'a\\'\n'", }, } { t.Run(tc.name, func(t *testing.T) { var c Config assert.Equal(t, tc.expected, c.squoteTemplateFunc(tc.values...)) }) } } func TestSetValueAtPathTemplateFunc(t *testing.T) { for _, tc := range []struct { name string path any value any dict any expected any expectedErr string }{ { name: "simple", path: "key", value: "value", dict: make(map[string]any), expected: map[string]any{ "key": "value", }, }, { name: "create_map", path: "key", value: "value", expected: map[string]any{ "key": "value", }, }, { name: "modify_map", path: "key2", value: "value2", dict: map[string]any{ "key1": "value1", }, expected: map[string]any{ "key1": "value1", "key2": "value2", }, }, { name: "create_nested_map", path: "key1.key2", value: "value", expected: map[string]any{ "key1": map[string]any{ "key2": "value", }, }, }, { name: "modify_nested_map", path: "key1.key2", value: "value", dict: map[string]any{ "key1": map[string]any{ "key2": "value2", "key3": "value3", }, "key2": "value2", }, expected: map[string]any{ "key1": map[string]any{ "key2": "value", "key3": "value3", }, "key2": "value2", }, }, { name: "replace_map", path: "key1", value: "value1", dict: map[string]any{ "key1": map[string]any{ "key2": "value2", }, }, expected: map[string]any{ "key1": "value1", }, }, { name: "replace_nested_map", path: "key1.key2", value: "value2", dict: map[string]any{ "key1": map[string]any{ "key2": map[string]any{ "key3": "value3", }, }, }, expected: map[string]any{ "key1": map[string]any{ "key2": "value2", }, }, }, { name: "replace_nested_value", path: "key1.key2.key3", value: "value3", dict: map[string]any{ "key1": map[string]any{ "key2": "value2", }, }, expected: map[string]any{ "key1": map[string]any{ "key2": map[string]any{ "key3": "value3", }, }, }, }, { name: "string_list_path", path: []string{ "key1", "key2", }, value: "value2", expected: map[string]any{ "key1": map[string]any{ "key2": "value2", }, }, }, { name: "any_list_path", path: []any{ "key1", "key2", }, value: "value2", expected: map[string]any{ "key1": map[string]any{ "key2": "value2", }, }, }, { name: "invalid_path", path: 0, expectedErr: "0: invalid path type int", }, { name: "invalid_path_element", path: []any{ 0, }, expectedErr: "0: invalid path element type int", }, } { t.Run(tc.name, func(t *testing.T) { var c Config if tc.expectedErr == "" { actual := c.setValueAtPathTemplateFunc(tc.path, tc.value, tc.dict) assert.Equal(t, tc.expected, actual) } else { chezmoiassert.PanicsWithErrorString(t, tc.expectedErr, func() { c.setValueAtPathTemplateFunc(tc.path, tc.value, tc.dict) }) } }) } } func TestFromIniTemplateFunc(t *testing.T) { for i, tc := range []struct { text string expected map[string]any }{ { text: chezmoitest.JoinLines( `key = value`, ), expected: map[string]any{ "key": "value", }, }, { text: chezmoitest.JoinLines( `[section]`, `sectionKey = sectionValue`, ), expected: map[string]any{ "section": map[string]any{ "sectionKey": "sectionValue", }, }, }, { text: chezmoitest.JoinLines( `key = value`, `[section]`, `sectionKey = sectionValue`, ), expected: map[string]any{ "key": "value", "section": map[string]any{ "sectionKey": "sectionValue", }, }, }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { c := &Config{} assert.Equal(t, tc.expected, c.fromIniTemplateFunc(tc.text)) }) } } func TestKeysFromPath(t *testing.T) { for _, tc := range []struct { name string path any expectedLastKey string expectedKeys []string expectedErr error }{ { name: "string_key", path: "key", expectedKeys: []string{}, expectedLastKey: "key", }, { name: "string_period_separated_keys", path: "key1.key2", expectedKeys: []string{"key1"}, expectedLastKey: "key2", }, { name: "string_period_separated_nested_keys", path: "key1.key2.key3", expectedKeys: []string{"key1", "key2"}, expectedLastKey: "key3", }, { name: "string_empty", path: "", expectedErr: errEmptyPath, }, { name: "string_period_separated_empty_key", path: "key1..key3", expectedErr: emptyPathElementError{ index: 1, }, }, { name: "string_slice_one_key", path: []string{"key1"}, expectedKeys: []string{}, expectedLastKey: "key1", }, { name: "string_slice_two_keys", path: []string{"key1", "key2"}, expectedKeys: []string{"key1"}, expectedLastKey: "key2", }, { name: "string_slice_multiple_keys", path: []string{"key1", "key2", "key3"}, expectedKeys: []string{"key1", "key2"}, expectedLastKey: "key3", }, { name: "string_slice_empty", path: []string{}, expectedErr: errEmptyPath, }, { name: "string_slice_empty_key", path: []string{""}, expectedErr: emptyPathElementError{ index: 0, }, }, { name: "string_slice_empty_key_second", path: []string{"key", ""}, expectedErr: emptyPathElementError{ index: 1, }, }, { name: "any_slice_nil", expectedErr: errEmptyPath, }, { name: "any_slice_empty", path: []any{}, expectedErr: errEmptyPath, }, { name: "any_slice_one_key", path: []any{"key"}, expectedKeys: []string{}, expectedLastKey: "key", }, { name: "any_slice_two_keys", path: []any{"key1", "key2"}, expectedKeys: []string{"key1"}, expectedLastKey: "key2", }, { name: "any_slice_invalid_key", path: []any{0}, expectedErr: invalidPathElementTypeError{ element: 0, }, }, { name: "any_slice_empty_key", path: []any{""}, expectedErr: emptyPathElementError{ index: 0, }, }, } { t.Run(tc.name, func(t *testing.T) { actualKeys, actualLastKey, err := keysFromPath(tc.path) if tc.expectedErr != nil { assert.Error(t, tc.expectedErr, err) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedKeys, actualKeys) assert.Equal(t, tc.expectedLastKey, actualLastKey) } }) } } func TestNestedMapAtPath(t *testing.T) { for _, tc := range []struct { name string m map[string]any path any expectedMap map[string]any expectedLastKey string expectedErr error }{ { name: "simple", m: map[string]any{ "key": "value", }, path: "key", expectedMap: map[string]any{ "key": "value", }, expectedLastKey: "key", }, { name: "nested_map", m: map[string]any{ "key1": map[string]any{ "key2": "value", }, }, path: "key1.key2", expectedMap: map[string]any{ "key2": "value", }, expectedLastKey: "key2", }, { name: "not_a_map", m: map[string]any{ "key1": "value", }, path: "key1.key2", }, { name: "nested_not_a_map", m: map[string]any{ "key1": map[string]any{ "key2": "value", }, }, path: "key1.key2.key3", }, { name: "empty_path", expectedErr: errEmptyPath, }, } { t.Run(tc.name, func(t *testing.T) { actualMap, actualLastKey, err := nestedMapAtPath(tc.m, tc.path) if tc.expectedErr != nil { assert.Equal(t, tc.expectedErr, err) } else { assert.NoError(t, err) assert.Equal(t, tc.expectedMap, actualMap) assert.Equal(t, tc.expectedLastKey, actualLastKey) } }) } } func TestToIniTemplateFunc(t *testing.T) { for i, tc := range []struct { data map[string]any expected string }{ { data: map[string]any{ "bool": true, "float": 1.0, "int": 1, "quotedString": "\"", "string": "string", }, expected: chezmoitest.JoinLines( `bool = true`, `float = 1.000000`, `int = 1`, `quotedString = "\""`, `string = string`, ), }, { data: map[string]any{ "bool": "true", "float": "1.0", "int": "1", "string": "string string", //nolint:dupword }, expected: chezmoitest.JoinLines( `bool = "true"`, `float = "1.0"`, `int = "1"`, `string = "string string"`, ), }, { data: map[string]any{ "key": "value", "section": map[string]any{ "subKey": "subValue", }, }, expected: chezmoitest.JoinLines( `key = value`, ``, `[section]`, `subKey = subValue`, ), }, { data: map[string]any{ "section": map[string]any{ "subsection": map[string]any{ "subSubKey": "subSubValue", }, }, }, expected: chezmoitest.JoinLines( ``, `[section]`, ``, `[section.subsection]`, `subSubKey = subSubValue`, ), }, { data: map[string]any{ "key": "value", "section": map[string]any{ "subKey": "subValue", "subsection": map[string]any{ "subSubKey": "subSubValue", }, }, }, expected: chezmoitest.JoinLines( `key = value`, ``, `[section]`, `subKey = subValue`, ``, `[section.subsection]`, `subSubKey = subSubValue`, ), }, { data: map[string]any{ "section1": map[string]any{ "subKey1": "subValue1", "subsection1a": map[string]any{ "subSubKey1a": "subSubValue1a", }, "subsection1b": map[string]any{ "subSubKey1b": "subSubValue1b", }, }, "section2": map[string]any{ "subKey2": "subValue2", "subsection2a": map[string]any{ "subSubKey2a": "subSubValue2a", }, "subsection2b": map[string]any{ "subSubKey2b": "subSubValue2b", }, }, }, expected: chezmoitest.JoinLines( ``, `[section1]`, `subKey1 = subValue1`, ``, `[section1.subsection1a]`, `subSubKey1a = subSubValue1a`, ``, `[section1.subsection1b]`, `subSubKey1b = subSubValue1b`, ``, `[section2]`, `subKey2 = subValue2`, ``, `[section2.subsection2a]`, `subSubKey2a = subSubValue2a`, ``, `[section2.subsection2b]`, `subSubKey2b = subSubValue2b`, ), }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { c := &Config{} actual := c.toIniTemplateFunc(tc.data) assert.Equal(t, tc.expected, actual) }) } } func TestNeedsQuote(t *testing.T) { for i, tc := range []struct { s string expected bool }{ { s: "", expected: true, }, { s: "\\", expected: true, }, { s: "\a", expected: true, }, { s: "abc", expected: false, }, { s: "true", expected: true, }, { s: "1", expected: true, }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { assert.Equal(t, tc.expected, needsQuote(tc.s)) }) } } func TestQuoteListTemplateFunc(t *testing.T) { c, err := newConfig() assert.NoError(t, err) actual := c.quoteListTemplateFunc([]any{ []byte{65}, "b", errors.New("error"), 1, true, }) assert.Equal(t, []string{ `"A"`, `"b"`, `"error"`, `"1"`, `"true"`, }, actual) } func TestGetRedirectedURLTemplateFunc(t *testing.T) { var redirectServer *httptest.Server // Create a test server that redirects /redirect to /target redirectServer = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/redirect": // Use absolute URL for redirect targetURL := redirectServer.URL + "/target" http.Redirect(w, r, targetURL, http.StatusFound) case "/redirect-relative": // Use relative URL for redirect http.Redirect(w, r, "target", http.StatusFound) case "/redirect-chain-1": // First redirect in a chain (will be followed to final destination) chainURL := redirectServer.URL + "/redirect-chain-2" http.Redirect(w, r, chainURL, http.StatusFound) case "/redirect-chain-2": // Second redirect in a chain (will be followed to final destination) finalURL := redirectServer.URL + "/final-target" http.Redirect(w, r, finalURL, http.StatusFound) case "/final-target": w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("final target content")) case "/not-modified": // Return 304 Not Modified (should not be followed as redirect) w.WriteHeader(http.StatusNotModified) case "/target": w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("target content")) case "/no-redirect": w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("direct content")) default: w.WriteHeader(http.StatusNotFound) } })) defer redirectServer.Close() chezmoitest.WithTestFS(t, nil, func(fs vfs.FS) { c := newTestConfig(t, fs) // Test URL that redirects (absolute) redirectURL := redirectServer.URL + "/redirect" expectedRedirectTarget := redirectServer.URL + "/target" actualRedirectTarget := c.getRedirectedURLTemplateFunc(redirectURL) assert.Equal(t, expectedRedirectTarget, actualRedirectTarget) // Test URL that redirects (relative) redirectRelativeURL := redirectServer.URL + "/redirect-relative" expectedRelativeRedirectTarget := redirectServer.URL + "/target" actualRelativeRedirectTarget := c.getRedirectedURLTemplateFunc(redirectRelativeURL) assert.Equal(t, expectedRelativeRedirectTarget, actualRelativeRedirectTarget) // Test URL that doesn't redirect directURL := redirectServer.URL + "/no-redirect" actualDirectURL := c.getRedirectedURLTemplateFunc(directURL) assert.Equal(t, directURL, actualDirectURL) // Test URL with 304 Not Modified (should not be treated as redirect) notModifiedURL := redirectServer.URL + "/not-modified" actualNotModifiedURL := c.getRedirectedURLTemplateFunc(notModifiedURL) assert.Equal(t, notModifiedURL, actualNotModifiedURL) // Test multiple redirects (should follow all redirects to final destination) multipleRedirectURL := redirectServer.URL + "/redirect-chain-1" expectedFinalRedirectTarget := redirectServer.URL + "/final-target" actualMultipleRedirectTarget := c.getRedirectedURLTemplateFunc(multipleRedirectURL) assert.Equal(t, expectedFinalRedirectTarget, actualMultipleRedirectTarget) }) } ================================================ FILE: internal/cmd/testdata/scripts/add.txtar ================================================ mkhomedir mksourcedir golden # test chezmoi add --create exec chezmoi add --create $HOME${/}.create cmp $CHEZMOISOURCEDIR/create_dot_create golden/create_dot_create # test that adding a directory creates a .keep file exec chezmoi add --recursive=false $HOME${/}.dir exists $CHEZMOISOURCEDIR/dot_dir/.keep # test adding a file in a directory exec chezmoi add $HOME${/}.dir/file cmp $CHEZMOISOURCEDIR/dot_dir/file golden/dot_dir/file # test adding a subdirectory exec chezmoi add --exact $HOME${/}.dir/subdir cmp $CHEZMOISOURCEDIR/dot_dir/exact_subdir/file golden/dot_dir/exact_subdir/file # test adding an empty file exec chezmoi add $HOME${/}.empty exists $CHEZMOISOURCEDIR/empty_dot_empty # test adding an executable file exec chezmoi add $HOME${/}.executable [unix] cmp $CHEZMOISOURCEDIR/executable_dot_executable golden/executable_dot_executable [windows] cmp $CHEZMOISOURCEDIR/dot_executable golden/executable_dot_executable # test adding a private file exec chezmoi add $HOME${/}.private [unix] cmp $CHEZMOISOURCEDIR/private_dot_private $HOME/.private [windows] cmp $CHEZMOISOURCEDIR/dot_private $HOME/.private # test adding a symlink exec chezmoi add $HOME${/}.symlink cmp $CHEZMOISOURCEDIR/symlink_dot_symlink golden/symlink_dot_symlink # test adding a symlink with a separator symlink $HOME/.symlink2 -> .dir/subdir/file exec chezmoi add $HOME${/}.symlink2 cmp $CHEZMOISOURCEDIR/symlink_dot_symlink2 golden/symlink_dot_symlink # test adding a symlink with --follow symlink $HOME${/}.symlink3 -> .file exec chezmoi add --follow $HOME${/}.symlink3 cmp $CHEZMOISOURCEDIR/dot_symlink3 golden/dot_file chhome home2/user # test that chezmoi add only creates .keep files in empty directories mkdir $HOME/.dir/empty_subdir exec chezmoi add $HOME${/}.dir ! exists $CHEZMOISOURCEDIR/dot_dir/.keep exists $CHEZMOISOURCEDIR/dot_dir/empty_subdir/.keep ! exists $CHEZMOISOURCEDIR/dot_dir/non_empty_subdir/.keep chhome home3/user # test that chezmoi add respects .chezmoiignore exec chezmoi add $HOME${/}.dir exists $CHEZMOISOURCEDIR/dot_dir/file stderr 'warning: ignoring .dir/ignore' ! exists $CHEZMOISOURCEDIR/dot_dir/ignore chhome home4/user # test that chezmoi add does not overwrite an already-added file exec chezmoi add $HOME/.file cmp $CHEZMOISOURCEDIR/dot_file golden/dot_file edit $HOME/.file cmp $CHEZMOISOURCEDIR/dot_file golden/dot_file # test that chezmoi add --force does overwrite an already-added file exec chezmoi add --force $HOME/.file cmp $CHEZMOISOURCEDIR/dot_file golden/edited_dot_file chhome home5/user # test that chezmoi add --exact only applies exact_ to the target, not parent directories # This is a regression test for the bug where adding a child with --exact would also # mark parent directories as exact exec chezmoi add --exact $HOME${/}.parent/child # child directory should have exact_ prefix exists $CHEZMOISOURCEDIR/dot_parent/exact_child cmp $CHEZMOISOURCEDIR/dot_parent/exact_child/file golden/dot_parent/exact_child/file # parent directory should NOT have exact_ prefix (bug fix verification) exists $CHEZMOISOURCEDIR/dot_parent ! exists $CHEZMOISOURCEDIR/exact_dot_parent # parent's other files should NOT be tracked (parent is not exact) ! exists $CHEZMOISOURCEDIR/dot_parent/other_file -- golden/dot_parent/exact_child/file -- # contents of child file -- golden/edited_dot_file -- # contents of .file # edited -- home2/user/.dir/non_empty_subdir/file -- # contents of .dir/non_empty_subdir/file -- home3/user/.dir/file -- # contents of .dir/file -- home3/user/.dir/ignore -- # contents of .dir/ignore -- home3/user/.local/share/chezmoi/.chezmoiignore -- **/ignore -- home4/user/.file -- # contents of .file -- home5/user/.parent/child/file -- # contents of child file -- home5/user/.parent/other_file -- # this file should not be tracked when only child is added with --exact ================================================ FILE: internal/cmd/testdata/scripts/addattributes.txtar ================================================ mkhomedir # test that chezmoi add warns if adding a file would remove the template attribute stdin golden/yes exec chezmoi add --no-tty $HOME${/}.file stdout 'adding \.file would remove template attribute, continue' cmp $CHEZMOISOURCEDIR/dot_file golden/.file ! exists $CHEZMOISOURCEDIR/dot_file.tmpl -- golden/.file -- # contents of .file -- golden/yes -- y -- home/user/.local/share/chezmoi/dot_file.tmpl -- {{ "# contents .file" }} ================================================ FILE: internal/cmd/testdata/scripts/addautotemplate.txtar ================================================ # test that chezmoi add --autotemplate on a file with a replacement creates a template in the source directory exec chezmoi add --autotemplate $HOME${/}.template cmp $CHEZMOISOURCEDIR/dot_template.tmpl golden/dot_template.tmpl # test that chezmoi add --autotemplate on a symlink with a replacement creates a template in the source directory symlink $HOME/.symlink -> .target-value exec chezmoi add --autotemplate $HOME${/}.symlink cmp $CHEZMOISOURCEDIR/symlink_dot_symlink.tmpl golden/symlink_dot_symlink.tmpl # test that chezmoi add --autotemplate does not create a template if no replacements occurred exec chezmoi add --autotemplate $HOME${/}.notatemplate cmp $CHEZMOISOURCEDIR/dot_notatemplate golden/dot_notatemplate # test that chezmoi add --autotemplate escapes brackets exec chezmoi add --autotemplate $HOME${/}.vimrc cmp $CHEZMOISOURCEDIR/dot_vimrc.tmpl golden/dot_vimrc.tmpl -- golden/dot_notatemplate -- # contents of .notatemplate -- golden/dot_template.tmpl -- key = {{ .variable }} -- golden/dot_vimrc.tmpl -- set foldmarker={{ "{{" }},{{ "}}" }} -- golden/symlink_dot_symlink.tmpl -- .target-{{ .variable }} -- home/user/.config/chezmoi/chezmoi.toml -- [data] variable = "value" -- home/user/.notatemplate -- # contents of .notatemplate -- home/user/.template -- key = value -- home/user/.vimrc -- set foldmarker={{,}} ================================================ FILE: internal/cmd/testdata/scripts/addencrypted.txtar ================================================ [windows] skip 'skipping gpg tests on Windows' [!exec:gpg] skip 'gpg not found in $PATH' mkgpgconfig cp golden/.encrypted $HOME/.encrypted # test that chezmoi add adds a file unencrypted exec chezmoi add $HOME${/}.encrypted cmp $CHEZMOISOURCEDIR/dot_encrypted golden/.encrypted # test that chezmoi add --encrypt encrypts the file in the source state exec chezmoi add --encrypt $HOME${/}.encrypted ! exists $CHEZMOISOURCEDIR/dot_encrypted exists $CHEZMOISOURCEDIR/encrypted_dot_encrypted.asc grep '-----BEGIN PGP MESSAGE-----' $CHEZMOISOURCEDIR/encrypted_dot_encrypted.asc # test that chezmoi add without --encrypt replaces the source file exec chezmoi add --force $HOME${/}.encrypted ! exists $CHEZMOISOURCEDIR/encrypted_dot_encrypted.asc cmp $CHEZMOISOURCEDIR/dot_encrypted golden/.encrypted # test that chezmoi add always encrypts when add.encrypt is true appendline $CHEZMOICONFIGDIR/chezmoi.toml '[add]' appendline $CHEZMOICONFIGDIR/chezmoi.toml ' encrypt = true' cp golden/.encrypted $HOME/.encrypted2 exec chezmoi add $HOME/.encrypted2 exists $CHEZMOISOURCEDIR/encrypted_dot_encrypted2.asc -- golden/.encrypted -- # contents of .encrypted ================================================ FILE: internal/cmd/testdata/scripts/addnew.txtar ================================================ # test that chezmoi add --new creates a new file exec chezmoi add --new $HOME/.file1 exists $CHEZMOISOURCEDIR/empty_dot_file1 # test that chezmoi add --new --recursive=false creates a new file exec chezmoi add --new --recursive=false $HOME/.file2 exists $CHEZMOISOURCEDIR/empty_dot_file2 ================================================ FILE: internal/cmd/testdata/scripts/addsecrets.txtar ================================================ [windows] skip 'test requires path separator to be forward slash' # test that chezmoi add --secrets=ignore does not generate a warning when adding a file with a secret exec chezmoi add --secrets=ignore $HOME${/}.secret ! stderr . exists $CHEZMOISOURCEDIR/dot_secret # test that chezmoi add --secrets=warning generates a warning when adding a file with a secret but still adds the file rm $CHEZMOISOURCEDIR/dot_secret exec chezmoi add --secrets=warning $HOME${/}.secret stderr 'Identified a pattern that may indicate AWS credentials, risking unauthorized cloud resource access and data breaches on AWS platforms.' exists $CHEZMOISOURCEDIR/dot_secret # test that chezmoi add --secrets=error generates an error when adding a file with a secret and does not add the file rm $CHEZMOISOURCEDIR/dot_secret ! exec chezmoi add --secrets=error $HOME${/}.secret stderr 'Identified a pattern that may indicate AWS credentials, risking unauthorized cloud resource access and data breaches on AWS platforms.' ! exists $CHEZMOISOURCEDIR/dot_secret # test that chezmoi add --force --secrets=error generates an error when adding a file with a secret but still adds the file rm $CHEZMOISOURCEDIR/dot_secret exec chezmoi add --force --secrets=error $HOME${/}.secret stderr 'Identified a pattern that may indicate AWS credentials, risking unauthorized cloud resource access and data breaches on AWS platforms.' exists $CHEZMOISOURCEDIR/dot_secret # test that chezmoi re-add does not generate a warning when an added file already contains a secret exec chezmoi re-add ! stderr . # test that chezmoi re-add generates a warning when adding a file with a new secret cp golden/.secret2 $HOME/.secret exec chezmoi re-add stderr 'Identified a pattern that may indicate AWS credentials, risking unauthorized cloud resource access and data breaches on AWS platforms.' -- golden/.secret2 -- AWS_ACCESS_KEY_ID=AKIALALEMEL33244OLIA -- home/user/.secret -- AWS_ACCESS_KEY_ID=AKIALALEMEL33243OLIA ================================================ FILE: internal/cmd/testdata/scripts/age.txtar ================================================ # test that chezmoi age encrypt encrypts a file with a passphrase stdin $HOME/passphrases exec chezmoi age encrypt --output $HOME${/}secret.txt.age --passphrase --no-tty $HOME${/}secret.txt grep '-----BEGIN AGE ENCRYPTED FILE----' $HOME/secret.txt.age # test that chezmoi age decrypt decrypts a file with a passphrase stdin $HOME/passphrase exec chezmoi age decrypt --output $HOME${/}secret.txt.decrypted --passphrase --no-tty $HOME${/}secret.txt.age cmp $HOME/secret.txt.decrypted $HOME/secret.txt -- home/user/passphrase -- passphrase -- home/user/passphrases -- passphrase passphrase -- home/user/secret.txt -- secret ================================================ FILE: internal/cmd/testdata/scripts/ageencryption.txtar ================================================ [!exec:age] skip 'age not found in $PATH' mkhomedir mkageconfig # test that chezmoi add --encrypt encrypts cp golden/.encrypted $HOME exec chezmoi add --encrypt $HOME${/}.encrypted exists $CHEZMOISOURCEDIR/encrypted_dot_encrypted.age grep '-----BEGIN AGE ENCRYPTED FILE-----' $CHEZMOISOURCEDIR/encrypted_dot_encrypted.age cp $CHEZMOISOURCEDIR/encrypted_dot_encrypted.age golden # test that chezmoi apply decrypts rm $HOME/.encrypted exec chezmoi apply --force cmp golden/.encrypted $HOME/.encrypted # test that chezmoi apply --exclude=encrypted does not apply encrypted files rm $HOME/.encrypted exec chezmoi apply --exclude=encrypted --force ! exists $HOME/.encrypted exec chezmoi apply --force cmp $HOME/.encrypted golden/.encrypted # test that chezmoi detects age encryption if age is configured but encryption = "age" is not set removeline $CHEZMOICONFIGDIR/chezmoi.toml 'encryption = "age"' exec chezmoi cat $HOME${/}.encrypted cmp stdout golden/.encrypted # test that chezmoi decrypt decrypts stdin stdin $CHEZMOISOURCEDIR${/}encrypted_dot_encrypted.age exec chezmoi decrypt cmp stdout golden/.encrypted # test that chezmoi decrypt decrypts a file exec chezmoi decrypt $CHEZMOISOURCEDIR${/}encrypted_dot_encrypted.age cmp stdout golden/.encrypted # test chezmoi encrypt/chezmoi decrypt round trip exec chezmoi encrypt golden/.encrypted stdout '-----BEGIN AGE ENCRYPTED FILE-----' stdin stdout exec chezmoi decrypt cmp stdout golden/.encrypted # test that chezmoi --use-builtin-age=true decrypt decrypts a file encrypted by age exec chezmoi --use-builtin-age=true decrypt $CHEZMOISOURCEDIR${/}encrypted_dot_encrypted.age cmp stdout golden/.encrypted # test that chezmoi --use-builtin-age=true encrypts a file than age then decrypts exec chezmoi --use-builtin-age=true --output=$WORK${/}encrypted.age encrypt golden/.encrypted exec chezmoi --use-builtin-age=false decrypt $WORK${/}encrypted.age cmp stdout golden/.encrypted # test that chezmoi edit --apply transparently decrypts and re-encrypts exec chezmoi edit --apply --force $HOME${/}.encrypted grep '# edited' $HOME/.encrypted -- golden/.encrypted -- # contents of .encrypted ================================================ FILE: internal/cmd/testdata/scripts/ageencryptionsymmetric.txtar ================================================ [!exec:age] skip 'age not found in $PATH' mkhomedir mkageconfig -symmetric # test that chezmoi add --encrypt encrypts cp golden/.encrypted $HOME exec chezmoi add --encrypt $HOME${/}.encrypted exists $CHEZMOISOURCEDIR/encrypted_dot_encrypted.age grep '-----BEGIN AGE ENCRYPTED FILE-----' $CHEZMOISOURCEDIR/encrypted_dot_encrypted.age # test that chezmoi apply decrypts rm $HOME/.encrypted exec chezmoi apply --force cmp golden/.encrypted $HOME/.encrypted -- golden/.encrypted -- # contents of .encrypted ================================================ FILE: internal/cmd/testdata/scripts/agekeygen.txtar ================================================ # test that age-keygen generates an age identity on stdout exec chezmoi age-keygen stdout AGE-SECRET-KEY # test that age-keygen generates post-quantum age identities on stdout exec chezmoi age-keygen --pq stdout AGE-SECRET-KEY-PQ # test that age-keygen generates an age identity and writes it to a file exec chezmoi age-keygen --output=identity.txt grep AGE-SECRET-KEY identity.txt [umask:002] cmpmod 660 identity.txt [umask:022] cmpmod 640 identity.txt # test that age-keygen --convert converts an age identity to an age recipient exec chezmoi age-keygen --convert identity.txt stdout '^age' ================================================ FILE: internal/cmd/testdata/scripts/apply.txtar ================================================ mkhomedir golden mksourcedir # test that chezmoi apply --dry-run does not create any files exec chezmoi apply --dry-run --force ! exists $HOME/.create ! exists $HOME/.dir ! exists $HOME/.dir/file ! exists $HOME/.dir/subdir ! exists $HOME/.dir/subdir/file ! exists $HOME/.empty ! exists $HOME/.executable ! exists $HOME/.file ! exists $HOME/.private ! exists $HOME/.remove ! exists $HOME/.template # test that chezmoi apply file creates a single file only exec chezmoi apply --force $HOME${/}.file ! exists $HOME/.create ! exists $HOME/.dir ! exists $HOME/.dir/file ! exists $HOME/.dir/subdir ! exists $HOME/.dir/subdir/file ! exists $HOME/.empty ! exists $HOME/.executable exists $HOME/.file ! exists $HOME/.private ! exists $HOME/.remove ! exists $HOME/.template # test that chezmoi apply dir --recursive=false creates only the directory exec chezmoi apply --force --recursive=false $HOME${/}.dir exists $HOME/.dir ! exists $HOME/.dir/file ! exists $HOME/.dir/subdir ! exists $HOME/.dir/subdir/file # test that chezmoi apply dir creates all files in the directory exec chezmoi apply --force $HOME${/}.dir exists $HOME/.dir exists $HOME/.dir/file exists $HOME/.dir/subdir exists $HOME/.dir/subdir/file # test that chezmoi apply creates all files exec chezmoi apply --force exists $HOME/.create exists $HOME/.dir exists $HOME/.dir/file exists $HOME/.dir/subdir exists $HOME/.dir/subdir/file exists $HOME/.empty exists $HOME/.executable exists $HOME/.file exists $HOME/.private ! exists $HOME/.remove exists $HOME/.template # test apply after edit edit $CHEZMOISOURCEDIR/dot_file exec chezmoi apply --force cmp $HOME/.file $CHEZMOISOURCEDIR/dot_file # test that chezmoi apply --source-path applies a file based on its source path edit $CHEZMOISOURCEDIR/dot_file exec chezmoi apply --force --source-path $CHEZMOISOURCEDIR/dot_file grep -count=2 '# edited' $HOME/.file # test that chezmoi apply --source-path fails when called with a targetDirAbsPath ! exec chezmoi apply --force --source-path $HOME${/}.file [!windows] stderr ${HOME@R}/\.file:\snot\sin\s${CHEZMOISOURCEDIR@R}$ ================================================ FILE: internal/cmd/testdata/scripts/applychmod_unix.txtar ================================================ [windows] skip 'UNIX only' mkhomedir golden mkhomedir mksourcedir # test change file mode chmod 777 $HOME/.file exec chezmoi apply --force cmpmod 666 $HOME/.file # test change executable file mode chmod 666 $HOME/.executable exec chezmoi apply --force cmpmod 777 $HOME/.executable # test change directory mode chmod 700 $HOME/.dir exec chezmoi apply --force cmpmod 777 $HOME/.dir ================================================ FILE: internal/cmd/testdata/scripts/applyexact.txtar ================================================ # test that chezmoi apply --dry-run does not remove entries from exact directories exec chezmoi apply --dry-run --force exists $HOME/.dir/file1 exists $HOME/.dir/file2 exists $HOME/.dir/subdir/file # test that chezmoi apply removes entries from exact directories exec chezmoi apply --force exists $HOME/.dir/file1 ! exists $HOME/.dir/file2 ! exists $HOME/.dir/subdir/file # test that chezmoi apply creates entries in exact directories cp golden/file3 $CHEZMOISOURCEDIR/exact_dot_dir exec chezmoi apply --force cmp $HOME/.dir/file3 golden/file3 -- golden/file3 -- # contents of .dir/file2 -- home/user/.dir/file1 -- # contents of .dir/file1 -- home/user/.dir/file2 -- # contents of .dir/file2 -- home/user/.dir/subdir/file -- # contents of .dir/subdir/file -- home/user/.local/share/chezmoi/exact_dot_dir/file1 -- # contents of .dir/file1 ================================================ FILE: internal/cmd/testdata/scripts/applyremove.txtar ================================================ # test that chezmoi apply --dry-run does not remove entries exec chezmoi apply --dry-run --force exists $HOME/.dir/file exists $HOME/.file1 exists $HOME/.file2 # test that chezmoi apply file1 removes only file1 exec chezmoi apply --force $HOME${/}.file1 exists $HOME/.dir/file ! exists $HOME/.file1 exists $HOME/.file2 # test that chezmoi apply removes all entries exec chezmoi apply --force ! exists $HOME/.dir/file ! exists $HOME/.file1 ! exists $HOME/.file2 -- home/user/.dir/file -- # contents of .dir/file -- home/user/.file1 -- # contents of .file1 -- home/user/.file2 -- # contents of .file2 -- home/user/.local/share/chezmoi/.chezmoiremove -- .dir .file* ================================================ FILE: internal/cmd/testdata/scripts/applyskipencrypted.txtar ================================================ [windows] skip 'skipping gpg tests on Windows' [!exec:gpg] skip 'gpg not found in $PATH' mkhomedir mkgpgconfig # test that chezmoi apply --exclude=encrypted does not apply encrypted files cp golden/.encrypted $HOME exec chezmoi add --encrypt $HOME${/}.encrypted rm $HOME/.encrypted cp $CHEZMOICONFIGDIR/chezmoi.toml golden/chezmoi.toml rm $CHEZMOICONFIGDIR/chezmoi.toml exec chezmoi apply --force --exclude=encrypted ! exists $HOME/.encrypted # test that chezmoi apply applies the encrypted file cp golden/chezmoi.toml $CHEZMOICONFIGDIR/chezmoi.toml exec chezmoi apply --force cmp golden/.encrypted $HOME/.encrypted -- golden/.encrypted -- # contents of .encrypted ================================================ FILE: internal/cmd/testdata/scripts/applysourcepath.txtar ================================================ mksourcedir # test that chezmoi apply --source-path only applies the target exec chezmoi apply --source-path $CHEZMOISOURCEDIR/dot_file ! exists $HOME/.empty exists $HOME/.file exec chezmoi apply --source-path $CHEZMOISOURCEDIR/empty_dot_empty exists $HOME/.empty # test that chezmoi apply --source-path ignores other modified files edit $HOME/.file exec chezmoi status stdout 'MM \.file' exec chezmoi apply --source-path $CHEZMOISOURCEDIR/executable_dot_executable exists $HOME/.executable ================================================ FILE: internal/cmd/testdata/scripts/applystate.txtar ================================================ [!exec:sha256sum] skip 'sha256sum not found in path' mksourcedir # test that chezmoi apply does not modify the state if nothing needs to be done exec chezmoi apply --force exec sha256sum $CHEZMOICONFIGDIR/chezmoistate.boltdb cp stdout chezmoistate.boltdb-sha256-pre-apply exec chezmoi apply --force exec sha256sum $CHEZMOICONFIGDIR/chezmoistate.boltdb cmp stdout chezmoistate.boltdb-sha256-pre-apply ================================================ FILE: internal/cmd/testdata/scripts/applytype.txtar ================================================ mkhomedir golden mkhomedir mksourcedir # test that chezmoi apply replaces a directory with a file rm $HOME/.file mkdir $HOME/.file exec chezmoi apply --force cmp $HOME/.file golden/.file # test that chezmoi apply replaces a file with a directory rm $HOME/.dir mkfile $HOME/.dir exec chezmoi apply --force cmp $HOME/.dir/file golden/.dir/file cmp $HOME/.dir/subdir/file golden/.dir/subdir/file # test that chezmoi apply replaces a file with a symlink rm $HOME/.symlink mkfile $HOME/.symlink exec chezmoi apply --force cmp $HOME/.symlink golden/.symlink # test that chezmoi apply replaces a symlink with a directory rm $HOME/.dir/subdir symlink $HOME/.dir/subdir -> .file exec chezmoi apply --force cmp $HOME/.dir/file golden/.dir/file cmp $HOME/.dir/subdir/file golden/.dir/subdir/file ================================================ FILE: internal/cmd/testdata/scripts/applyverbose.txtar ================================================ # test that chezmoi apply --dry-run --verbose does not show scripts when scripts are excluded from diffs exec chezmoi apply --dry-run --verbose [unix] [umask:002] cmp stdout golden/apply-umask-002.diff [unix] [umask:022] cmp stdout golden/apply-umask-022.diff [windows] cmp stdout golden/apply-windows.diff chhome home2/user # test that chezmoi apply --dry-run --force --verbose does not show removes when removes are excluded from diffs exec chezmoi apply --dry-run --force --verbose ! stdout . -- golden/apply-umask-002.diff -- diff --git a/.file b/.file new file mode 100664 index 0000000000000000000000000000000000000000..8a52cb9ce9551221716a53786ad74104c5902362 --- /dev/null +++ b/.file @@ -0,0 +1 @@ +# contents of .file -- golden/apply-umask-022.diff -- diff --git a/.file b/.file new file mode 100644 index 0000000000000000000000000000000000000000..8a52cb9ce9551221716a53786ad74104c5902362 --- /dev/null +++ b/.file @@ -0,0 +1 @@ +# contents of .file -- golden/apply-windows.diff -- diff --git a/.file b/.file new file mode 100666 index 0000000000000000000000000000000000000000..8a52cb9ce9551221716a53786ad74104c5902362 --- /dev/null +++ b/.file @@ -0,0 +1 @@ +# contents of .file -- home/user/.config/chezmoi/chezmoi.toml -- [diff] exclude = ["scripts"] -- home/user/.local/share/chezmoi/dot_file -- # contents of .file -- home/user/.local/share/chezmoi/run_script -- # contents of script -- home2/user/.config/chezmoi/chezmoi.yaml -- diff: exclude: - remove -- home2/user/.file -- # contents of .file -- home2/user/.local/share/chezmoi/.chezmoiremove -- .file ================================================ FILE: internal/cmd/testdata/scripts/archivetar.txtar ================================================ mksourcedir [windows] unix2dos golden/archive-tar exec chezmoi archive --output=archive.tar exec tar -tf archive.tar [!openbsd] cmp stdout golden/archive-tar [openbsd] cmp stdout golden/archive-tar-openbsd exec chezmoi archive --gzip --output=archive.tar.gz exec tar -tzf archive.tar.gz [!openbsd] cmp stdout golden/archive-tar [openbsd] cmp stdout golden/archive-tar-openbsd -- golden/archive-tar -- .create .dir/ .dir/file .dir/subdir/ .dir/subdir/file .empty .executable .file .private .readonly .symlink .template -- golden/archive-tar-openbsd -- .create .dir .dir/file .dir/subdir .dir/subdir/file .empty .executable .file .private .readonly .symlink .template ================================================ FILE: internal/cmd/testdata/scripts/archivezip.txtar ================================================ [windows] skip 'windows line endings confuse cmp and diff' [!exec:unzip] skip 'unzip not found in $PATH' mksourcedir exec chezmoi archive --format=zip --output=archive.zip exec unzip -t archive.zip [!freebsd] cmp stdout golden/archive # FIXME whitespace output of unzip is different on FreeBSD -- golden/archive -- Archive: archive.zip testing: .create OK testing: .dir OK testing: .dir/file OK testing: .dir/subdir OK testing: .dir/subdir/file OK testing: .empty OK testing: .executable OK testing: .file OK testing: .private OK testing: .readonly OK testing: .symlink OK testing: .template OK No errors detected in compressed data of archive.zip. ================================================ FILE: internal/cmd/testdata/scripts/autocommit.txtar ================================================ [!exec:git] skip 'git not found in $PATH' mkgitconfig mkhomedir golden mkhomedir exec chezmoi init # test that chezmoi add creates a commit exec chezmoi add $HOME${/}.file exec git -C $CHEZMOISOURCEDIR show HEAD stdout 'Add \.file' # test that chezmoi edit createspushes a commit exec chezmoi edit $HOME${/}.file exec git -C $CHEZMOISOURCEDIR show HEAD stdout 'Update \.file' # test that chezmoi chattr creates a commit exec chezmoi chattr +executable $HOME${/}.file exec git -C $CHEZMOISOURCEDIR show HEAD stdout 'Change attributes of \.file' # test that chezmoi add on a directory creates a commit exec chezmoi add $HOME${/}.dir exec git -C $CHEZMOISOURCEDIR show HEAD stdout 'Add \.dir/file' # test that copying a file creates a valid commit message cp $CHEZMOISOURCEDIR/executable_dot_file $CHEZMOISOURCEDIR/executable_dot_file2 mv $CHEZMOISOURCEDIR/executable_dot_file $CHEZMOISOURCEDIR/executable_dot_file3 exec git -C $CHEZMOISOURCEDIR config --local diff.renames copies exec git -C $CHEZMOISOURCEDIR add . exec chezmoi edit $HOME${/}.dir${/}file exec git -C $CHEZMOISOURCEDIR show HEAD stdout 'Copy \.file to \.file2' # test that chezmoi chattr on a file in a directory creates a commit exec chezmoi chattr --debug +private $HOME${/}.dir/file exec git -C $CHEZMOISOURCEDIR show HEAD stdout 'Change attributes of \.dir' # test that chezmoi forget creates and commit exec chezmoi forget --force $HOME${/}.file2 exec git -C $CHEZMOISOURCEDIR show HEAD stdout 'Remove \.file2' # test that chezmoi edit uses a custom commit message template appendline $CHEZMOICONFIGDIR/chezmoi.toml ' commitMessageTemplate = "{{ .prefix }}my commit message"' exec chezmoi edit $HOME${/}.dir${/}file exec git -C $CHEZMOISOURCEDIR show HEAD stdout 'feat: my commit message' # test that only one of git.commitMessageTemplate and git.commitMessageTemplateFile can be set appendline $CHEZMOICONFIGDIR/chezmoi.toml ' commitMessageTemplateFile = ".COMMIT_MESSAGE.tmpl"' ! exec chezmoi edit $HOME${/}.dir${/}file stderr 'cannot specify both git.commitMessageTemplate and git.commitMessageTemplateFile' removeline $CHEZMOICONFIGDIR/chezmoi.toml ' commitMessageTemplate = "{{ .prefix }}my commit message"' # test that chezmoi edit uses a custom commit message template file exec chezmoi edit $HOME${/}.dir${/}file exec git -C $CHEZMOISOURCEDIR show HEAD stdout 'feat: my commit message file' removeline $CHEZMOICONFIGDIR/chezmoi.toml ' commitMessageTemplateFile = ".COMMIT_MESSAGE.tmpl"' -- home/user/.config/chezmoi/chezmoi.toml -- [data] prefix = "feat: " [git] autoCommit = true -- home/user/.local/share/chezmoi/.COMMIT_MESSAGE.tmpl -- {{ .prefix }}my commit message file ================================================ FILE: internal/cmd/testdata/scripts/autopush.txtar ================================================ [!exec:echo] skip 'echo not found in $PATH' [!exec:git] skip 'git not found in $PATH' mkgitconfig mkhomedir golden mkhomedir # create a repo exec git init --bare $WORK/dotfiles.git exec chezmoi init file://$WORK/dotfiles.git # test that chezmoi add creates and pushes a commit exec chezmoi add $HOME${/}.file stdout ^git-auto-commit-pre$ stdout ^git-auto-commit-post$ stdout ^git-auto-push-pre$ stdout ^git-auto-push-post$ exec git --git-dir=$WORK/dotfiles.git show HEAD stdout 'Add \.file' # test that chezmoi edit creates and pushes a commit exec chezmoi edit $HOME${/}.file exec git --git-dir=$WORK/dotfiles.git show HEAD stdout 'Update \.file' # test that chezmoi chattr on a file creates and pushes a commit exec chezmoi chattr +executable $HOME${/}.file exec git --git-dir=$WORK/dotfiles.git show HEAD stdout 'Change attributes of \.file' # test that chezmoi add on a directory creates and pushes a commit exec chezmoi add $HOME${/}.dir exec git --git-dir=$WORK/dotfiles.git show HEAD stdout 'Add \.dir/file' # test that chezmoi chattr on a file in a directory creates and pushes a commit exec chezmoi chattr --debug +private $HOME${/}.dir/file exec git --git-dir=$WORK/dotfiles.git show HEAD stdout 'Change attributes of \.dir' # test that chezmoi forget creates and pushes a commit exec chezmoi forget --force $HOME${/}.file exec git --git-dir=$WORK/dotfiles.git show HEAD stdout 'Remove \.file' -- home/user/.config/chezmoi/chezmoi.toml -- [git] autoPush = true [hooks.git-auto-commit.pre] command = "echo" args = ["git-auto-commit-pre"] [hooks.git-auto-commit.post] command = "echo" args = ["git-auto-commit-post"] [hooks.git-auto-push.pre] command = "echo" args = ["git-auto-push-pre"] [hooks.git-auto-push.post] command = "echo" args = ["git-auto-push-post"] ================================================ FILE: internal/cmd/testdata/scripts/bitwarden.txtar ================================================ mockcommand bin/bw mockcommand bin/bws [windows] unix2dos golden/bitwarden-attachment # test bitwarden template function exec chezmoi execute-template '{{ (bitwarden "item" "example.com").login.password }}' stdout ^password-value$ # test bitwardenFields template function exec chezmoi execute-template '{{ (bitwardenFields "item" "example.com").Hidden.value }}' stdout ^hidden-value$ # test bitwardenAttachment template function exec chezmoi execute-template '{{ (bitwardenAttachment "filename" "item-id") }}' cmp stdout golden/bitwarden-attachment # test bitwardenAttachmentByRef template function exec chezmoi execute-template '{{ (bitwardenAttachmentByRef "filename" "item" "example.com") }}' cmp stdout golden/bitwarden-attachment # test bitwardenSecrets template function exec chezmoi execute-template '{{ (bitwardenSecrets "be8e0ad8-d545-4017-a55a-b02f014d4158" "0.48c78342-1635-48a6-accd-afbe01336365.C0tMmQqHnAp1h0gL8bngprlPOYutt0:B3h5D+YgLvFiQhWkIq6Bow==").value }}' stdout '^0\.982492bc-7f37-4475-9e60$' -- bin/bw.yaml -- responses: - args: 'get item example.com' response: | { "object": "item", "id": "bf22e4b4-ae4a-4d1c-8c98-ac620004b628", "organizationId": null, "folderId": null, "type": 1, "name": "example.com", "notes": null, "favorite": false, "fields": [ { "name": "Text", "value": "text-value", "type": 0 }, { "name": "Hidden", "value": "hidden-value", "type": 1 } ], "login": { "username": "username-value", "password": "password-value", "totp": null, "passwordRevisionDate": null }, "collectionIds": [], "revisionDate": "2020-10-28T00:21:02.690Z" } - args: 'get attachment filename --itemid item-id --raw' response: 'hidden-file-value' - args: 'get attachment filename --itemid bf22e4b4-ae4a-4d1c-8c98-ac620004b628 --raw' response: 'hidden-file-value' default: response: | Invalid command: $* See --help for a list of available commands. exitCode: 1 -- bin/bws.yaml -- responses: - args: 'secret get be8e0ad8-d545-4017-a55a-b02f014d4158 --access-token 0.48c78342-1635-48a6-accd-afbe01336365.C0tMmQqHnAp1h0gL8bngprlPOYutt0:B3h5D+YgLvFiQhWkIq6Bow==' response: | { "object": "secret", "id": "be8e0ad8-d545-4017-a55a-b02f014d4158", "organizationId": "10e8cbfa-7bd2-4361-bd6f-b02e013f9c41", "projectId": "e325ea69-a3ab-4dff-836f-b02e013fe530", "key": "SES_KEY", "value": "0.982492bc-7f37-4475-9e60", "note": "", "creationDate": "2023-06-28T20:13:20.643567Z", "revisionDate": "2023-06-28T20:13:20.643567Z" } default: exitCode: 1 -- golden/bitwarden-attachment -- hidden-file-value ================================================ FILE: internal/cmd/testdata/scripts/bitwardenunlock.txtar ================================================ mockcommand bin/bw # test Bitwarden CLI automatic unlock without BW_SESSION set exec chezmoi execute-template '{{ (bitwarden "item" "example.com").login.password }}' stdout ^password-value$ # test Bitwarden CLI automatic unlock with BW_SESSION set env BW_SESSION=my-bitwarden-session-id exec chezmoi execute-template '{{ (bitwarden "item" "example.com").login.password }}' stdout ^password-value$ # test Bitwarden CLI automatic unlock with BW_SESSION set to an incorrect value env BW_SESSION=my-invalid-bitwarden-session-id ! exec chezmoi execute-template '{{ (bitwarden "item" "example.com").login.password }}' -- bin/bw.yaml -- responses: - args: 'get item example.com' requireEnv: BW_SESSION: my-bitwarden-session-id response: | { "object": "item", "id": "bf22e4b4-ae4a-4d1c-8c98-ac620004b628", "organizationId": null, "folderId": null, "type": 1, "name": "example.com", "notes": null, "favorite": false, "fields": [ { "name": "Text", "value": "text-value", "type": 0 }, { "name": "Hidden", "value": "hidden-value", "type": 1 } ], "login": { "username": "username-value", "password": "password-value", "totp": null, "passwordRevisionDate": null }, "collectionIds": [], "revisionDate": "2020-10-28T00:21:02.690Z" } - args: 'lock' requireEnv: BW_SESSION: my-bitwarden-session-id - args: 'unlock --raw' response: my-bitwarden-session-id default: response: | Invalid command: $* See --help for a list of available commands. exitCode: 1 -- home/user/.config/chezmoi/chezmoi.toml -- [bitwarden] unlock = "auto" ================================================ FILE: internal/cmd/testdata/scripts/builtinage.txtar ================================================ [!exec:age] skip 'age not found in $PATH' mkageconfig # disable age command appendline $CHEZMOICONFIGDIR/chezmoi.toml ' command = "false"' [unix] chmod 755 golden/age cp golden/age bin prependline $CHEZMOICONFIGDIR/chezmoi.toml 'useBuiltinAge = true' # test that chezmoi add --encrypt encrypts cp golden/.encrypted $HOME exec chezmoi add --encrypt $HOME${/}.encrypted exists $CHEZMOISOURCEDIR/encrypted_dot_encrypted.age grep '-----BEGIN AGE ENCRYPTED FILE-----' $CHEZMOISOURCEDIR/encrypted_dot_encrypted.age # test that chezmoi cat decrypts exec chezmoi cat $HOME${/}.encrypted cmp stdout golden/.encrypted -- golden/.encrypted -- # contents of .encrypted -- golden/age -- #!/bin/sh exit 1 ================================================ FILE: internal/cmd/testdata/scripts/builtingit.txtar ================================================ [!exec:git] skip 'git not found in $PATH' [windows] skip 'go-git does not support file:// URLs on windows' chmod 755 golden/git mkgitconfig mkhomedir golden mkhomedir # install fake git cp golden/git bin ! exec chezmoi init exec chezmoi init --use-builtin-git=true exists $CHEZMOISOURCEDIR/.git # create a commit rm bin/git # disable fake git cp golden/.file $CHEZMOISOURCEDIR/dot_file exec chezmoi git add dot_file exec chezmoi git commit -- --message 'Add dot_file' cp golden/git bin # restore fake git chhome home2/user # test chezmoi init --use-builtin-git=true exec chezmoi init --use-builtin-git=true file://$WORK/home/user/.local/share/chezmoi exists $CHEZMOISOURCEDIR/.git cmp $CHEZMOISOURCEDIR/dot_file golden/.file chhome home/user # create a second commit rm bin/git # disable fake git cp golden/.executable $CHEZMOISOURCEDIR/executable_dot_executable exec chezmoi git add executable_dot_executable exec chezmoi git commit -- --message 'Add executable_dot_executable' cp golden/git bin # restore fake git chhome home2/user # test chezmoi update --use-builtin-git=true ! exec chezmoi update exec chezmoi update --use-builtin-git=true cmp $CHEZMOISOURCEDIR/executable_dot_executable golden/.executable chhome home/user # test chezmoi init --branch --use-builtin-git=true rm bin/git # disable fake git exec chezmoi git checkout -- -b new-branch edit $CHEZMOISOURCEDIR/dot_file exec chezmoi git add dot_file exec chezmoi git commit -- --message 'Edit .file' exec chezmoi git checkout master cp golden/git bin # restore fake git chhome home3/user # test chezmoi init --apply --branch=new-branch --use-builtin-git=true exec chezmoi init --apply --branch=new-branch --use-builtin-git=true file://$WORK/home/user/.local/share/chezmoi grep '# edited' $HOME/.file -- golden/git -- #!/bin/sh exit 1 ================================================ FILE: internal/cmd/testdata/scripts/cat.txtar ================================================ mkhomedir golden mksourcedir # test that chezmoi cat prints an empty file exec chezmoi cat $HOME${/}.empty cmp stdout golden/.empty # test that chezmoi cat prints a file exec chezmoi cat $HOME${/}.file cmp stdout golden/.file # test that chezmoi cat prints a symlink exec chezmoi cat $HOME${/}.symlink stdout '\.dir/subdir/file' # test that chezmoi cat prints a template exec chezmoi cat $HOME${/}.template cmp stdout golden/.template # test that chezmoi cat does not print directories ! exec chezmoi cat $HOME${/}.dir stderr 'not a file, script, or symlink' # test that chezmoi cat does not print files outside the destination directory ! exec chezmoi cat ${/}etc${/}passwd stderr 'not in destination directory' # test that chezmoi cat uses relative paths mkdir $HOME/.dir cd $HOME/.dir exec chezmoi cat file cmp stdout $WORK/golden/.dir/file ================================================ FILE: internal/cmd/testdata/scripts/catconfig.txtar ================================================ # test that chezmoi cat-config prints the configuration file without parsing it exec chezmoi cat-config cmp stdout golden/cat-config -- golden/cat-config -- # contents of .config/chezmoi/chezmoi.json -- home/user/.config/chezmoi/chezmoi.json -- # contents of .config/chezmoi/chezmoi.json ================================================ FILE: internal/cmd/testdata/scripts/cd_unix.txtar ================================================ [windows] skip 'UNIX only' chmod 755 bin/shell # test that chezmoi cd creates source directory if needed exec chezmoi cd exists $CHEZMOISOURCEDIR grep ${CHEZMOISOURCEDIR@R} pwd.log rm pwd.log # test that chezmoi cd changes into an existing directory and sets the CHEZMOI environment variable exec chezmoi cd grep CHEZMOI=1 env.log rm env.log grep ${CHEZMOISOURCEDIR@R} pwd.log rm pwd.log # test chat chezmoi cd with an argument changes into the corresponding source directory exec chezmoi cd $HOME${/}.dir grep ${CHEZMOISOURCEDIR@R}/dot_dir pwd.log rm pwd.log # test that chezmoi cd works when $SHELL environment variable contains spaces env SHELL='shell arg1' exec chezmoi cd stdout '^shell arg1$' # test that chezmoi cd will not try to change to a non-directory ! exec chezmoi cd $HOME${/}.file stderr 'not a directory' # test that cd will not try to change to a non-existent directory ! exec chezmoi cd $HOME${/}.notexist stderr 'not managed' chhome home2/user # test chezmoi cd with shell command set in config file overrides $SHELL environment variable exec chezmoi cd stdout '^shell arg2$' env SHELL=$WORK/bin/shell chhome home3/user # test that chezmoi cd $HOME with .chezmoiroot changes into .chezmoiroot exec chezmoi cd $HOME grep ${CHEZMOISOURCEDIR@R}/home pwd.log rm pwd.log -- bin/shell -- #!/bin/sh echo CHEZMOI=$CHEZMOI > $WORK/env.log pwd > $WORK/pwd.log echo shell $* -- home/user/.dir/.keep -- -- home/user/.local/share/chezmoi/dot_dir/.keep -- -- home/user/.local/share/chezmoi/dot_file -- -- home2/user/.config/chezmoi/chezmoi.toml -- [cd] command = "shell" args = ["arg2"] -- home3/user/.local/share/chezmoi/.chezmoiroot -- home -- home3/user/.local/share/chezmoi/home/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/cd_windows.txtar ================================================ [unix] skip 'Windows only' # test chezmoi cd with command with args exec chezmoi cd ! stdout PowerShell stdout arg1 chhome home2/user # test chezmoi cd when $SHELL environment variable contains spaces env SHELL='shell arg2' exec chezmoi cd stdout 'arg2' -- bin/shell.cmd -- @echo off echo %* -- home/user/.config/chezmoi/chezmoi.toml -- [cd] command = "powershell" args = ["-nologo", "-command", "Write-Host 'arg1'"] ================================================ FILE: internal/cmd/testdata/scripts/chattr.txtar ================================================ mksourcedir # test that chezmoi chattr empty sets the empty attribute on a file exists $CHEZMOISOURCEDIR/dot_file exec chezmoi chattr empty $HOME${/}.file ! exists $CHEZMOISOURCEDIR/dot_file exists $CHEZMOISOURCEDIR/empty_dot_file # test that chezmoi chattr remove sets the remove attribute on a file exec chezmoi chattr remove $HOME${/}.file ! exists $CHEZMOISOURCEDIR/empty_dot_file exists $CHEZMOISOURCEDIR/remove_dot_file # test that chezmoi chattr noremove removes the remove attribute on a file exec chezmoi chattr noremove,empty $HOME${/}.file ! exists $CHEZMOISOURCEDIR/remove_dot_file exists $CHEZMOISOURCEDIR/empty_dot_file # test that chezmoi attr +p sets the private attribute on a file exec chezmoi chattr +p $HOME${/}.file ! exists $CHEZMOISOURCEDIR/empty_dot_file exists $CHEZMOISOURCEDIR/private_empty_dot_file # test that chezmoi chattr t,-e sets the template attribute and removes the empty attribute on a file exec chezmoi chattr t,-e $HOME${/}.file ! exists $CHEZMOISOURCEDIR/private_empty_dot_file exists $CHEZMOISOURCEDIR/private_dot_file.tmpl # test that chezmoi chattr -- -e,-p,r sets the readonly attribute on a file and removes the empty and private attributes exec chezmoi chattr -- -e,-p,r $HOME${/}.file ! exists $CHEZMOISOURCEDIR/private_dot_file.tmpl exists $CHEZMOISOURCEDIR/readonly_dot_file.tmpl # test that chezmoi chattr -- -r,-t removes the readonly and template attributes on a file exec chezmoi chattr -- -r,-t $HOME${/}.file ! exists $CHEZMOISOURCEDIR/readonly_dot_file.tmpl exists $CHEZMOISOURCEDIR/dot_file # test that chezmoi chattr +create changes a file to be a create_ file exec chezmoi chattr +create $HOME${/}.file ! exists $CHEZMOISOURCEDIR/dot_file exists $CHEZMOISOURCEDIR/create_dot_file # test that chezmoi chattr nomodify does not change a create_ file exec chezmoi chattr nomodify $HOME${/}.file ! exists $CHEZMOISOURCEDIR/dot_file exists $CHEZMOISOURCEDIR/create_dot_file # test that chezmoi chattr modify,script,symlink changes a create_ file to a symlink_ exec chezmoi chattr modify,script,symlink $HOME${/}.file ! exists $CHEZMOISOURCEDIR/create_dot_file exists $CHEZMOISOURCEDIR/symlink_dot_file # test that chezmoi chattr -- -symlink changes a symlink_ to a regular file exec chezmoi chattr -- -symlink $HOME${/}.file ! exists $CHEZMOISOURCEDIR/symlink_dot_file exists $CHEZMOISOURCEDIR/dot_file # test that chezmoi chattr nox removes the execute attribute on a file exists $CHEZMOISOURCEDIR/executable_dot_executable exec chezmoi chattr nox $HOME${/}.executable ! exists $CHEZMOISOURCEDIR/executable_dot_executable exists $CHEZMOISOURCEDIR/dot_executable # test that chezmoi chattr x sets the executable attribute on a file exec chezmoi chattr x $HOME${/}.executable ! exists $CHEZMOISOURCEDIR/dot_executable exists $CHEZMOISOURCEDIR/executable_dot_executable # test that chezmoi chattr +private sets the private attribute on a create file exec chezmoi chattr +private $HOME${/}.create ! exists $CHEZMOISOURCEDIR/create_dot_create exists $CHEZMOISOURCEDIR/create_private_dot_create # test that chezmoi chattr noprivate removes the private attribute on a create file exec chezmoi chattr noprivate $HOME${/}.create ! exists $CHEZMOISOURCEDIR/create_private_dot_create exists $CHEZMOISOURCEDIR/create_dot_create # test that chezmoi chattr exact sets the exact attribute on a directory exists $CHEZMOISOURCEDIR/dot_dir exec chezmoi chattr exact $HOME${/}.dir ! exists $CHEZMOISOURCEDIR/dot_dir exists $CHEZMOISOURCEDIR/exact_dot_dir # test that chezmoi chattr readonly sets the readonly attribute on a directory exec chezmoi chattr readonly $HOME${/}.dir ! exists $CHEZMOISOURCEDIR/exact_dot_dir exists $CHEZMOISOURCEDIR/exact_readonly_dot_dir # test that chezmoi chattr remove sets the remove attribute on a directory exec chezmoi chattr remove $HOME${/}.dir ! exists $CHEZMOISOURCEDIR/exact_readonly_dot_dir exists $CHEZMOISOURCEDIR/remove_exact_readonly_dot_dir # test that chezmoi chattr noremove removes the remove attribute on a directory exec chezmoi chattr noremove $HOME${/}.dir ! exists $CHEZMOISOURCEDIR/remove_exact_readonly_dot_dir exists $CHEZMOISOURCEDIR/exact_readonly_dot_dir # test that chezmoi chattr +t sets the template attribute on a symlink exists $CHEZMOISOURCEDIR/symlink_dot_symlink exec chezmoi chattr +t $HOME${/}.symlink ! exists $CHEZMOISOURCEDIR/symlink_dot_symlink exists $CHEZMOISOURCEDIR/symlink_dot_symlink.tmpl # test that chezmoi chattr -- -t removes the template attribute on a symlink exec chezmoi chattr -- -t $HOME${/}.symlink ! exists $CHEZMOISOURCEDIR/symlink_dot_symlink.tmpl exists $CHEZMOISOURCEDIR/symlink_dot_symlink # test that chezmoi chattr -- before sets the before attribute on a script exec chezmoi chattr -- before $HOME/script.sh ! exists $CHEZMOISOURCEDIR/run_script.sh exists $CHEZMOISOURCEDIR/run_before_script.sh # test that chezmoi chattr -- once sets the once attribute on a script exec chezmoi chattr -- once $HOME/script.sh ! exists $CHEZMOISOURCEDIR/run_before_script.sh exists $CHEZMOISOURCEDIR/run_once_before_script.sh # test that chezmoi chattr -- after sets the after attribute on a script and removes the before attribute exec chezmoi chattr -- after $HOME/script.sh ! exists $CHEZMOISOURCEDIR/run_once_before_script.sh exists $CHEZMOISOURCEDIR/run_once_after_script.sh # test that chezmoi chattr onchange sets the onchange attribute on a script and removes the only attribute exec chezmoi chattr -- onchange $HOME/script.sh ! exists $CHEZMOISOURCEDIR/run_once_after_script.sh exists $CHEZMOISOURCEDIR/run_onchange_after_script.sh # test that chezmoi chattr -- -onchange removes the onchange attribute on a script exec chezmoi chattr -- -onchange $HOME/script.sh ! exists $CHEZMOISOURCEDIR/run_onchange_after_script.sh exists $CHEZMOISOURCEDIR/run_after_script.sh # test that chezmoi chattr -- -a removes the after attribute on a script exec chezmoi chattr -- -a $HOME/script.sh ! exists $CHEZMOISOURCEDIR/run_after_script.sh exists $CHEZMOISOURCEDIR/run_script.sh # test that chezmoi chattr +executable,+private,+readonly,+template sets the attributes on a modify script exec chezmoi chattr +executable,+private,+readonly,+template $HOME${/}.modify ! exists $CHEZMOISOURCEDIR/modify_dot_modify exists $CHEZMOISOURCEDIR/modify_private_readonly_executable_dot_modify.tmpl # test that chezmoi chattr --dry-run --verbose generates a diff when a file is renamed exec chezmoi chattr --dry-run --verbose +executable $HOME${/}.file cmp stdout golden/chattr-diff # test that chezmoi chattr --recursive noexact recurses into subdirectories exists $CHEZMOISOURCEDIR/exact_readonly_dot_dir exists $CHEZMOISOURCEDIR/exact_readonly_dot_dir/exact_subdir exec chezmoi chattr --recursive noexact $HOME${/}.dir exists $CHEZMOISOURCEDIR/readonly_dot_dir exists $CHEZMOISOURCEDIR/readonly_dot_dir/subdir # test that chezmoi chattr exact adds the exact_ attribute on a subdirectory exec chezmoi chattr +exact $HOME${/}.dir/subdir exists $CHEZMOISOURCEDIR/readonly_dot_dir/exact_subdir -- golden/chattr-diff -- diff --git a/dot_file b/executable_dot_file rename from dot_file rename to executable_dot_file -- home/user/.local/share/chezmoi/modify_dot_modify -- #!/bin/sh cat -- home/user/.local/share/chezmoi/run_script.sh -- #!/bin/sh ================================================ FILE: internal/cmd/testdata/scripts/chattrencrypted.txtar ================================================ [!exec:age] skip 'age not found in $PATH' mkageconfig # test that chezmoi add --encrypted encrypts a file exec chezmoi add --encrypt $HOME${/}.file grep '-----BEGIN AGE ENCRYPTED FILE-----' $CHEZMOISOURCEDIR/encrypted_dot_file.age # test that chezmoi chattr noencrypted decrypts the file in the source state exec chezmoi chattr noencrypted $HOME${/}.file ! exists $CHEZMOISOURCEDIR/encrypted_dot_file.age cmp $CHEZMOISOURCEDIR/dot_file $HOME/.file # test that chezmoi chattr encrypted encrypts the file in the source state exec chezmoi chattr encrypted $HOME${/}.file ! exists $CHEZMOISOURCEDIR/dot_file grep '-----BEGIN AGE ENCRYPTED FILE-----' $CHEZMOISOURCEDIR/encrypted_dot_file.age -- home/user/.file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/completion.txtar ================================================ # test that chezmoi completion bash generates bash completions exec chezmoi completion bash stdout '# bash completion V2 for chezmoi' # test that chezmoi completion fish generates fish completions exec chezmoi completion fish stdout '# fish completion for chezmoi' # test that chezmoi completion powershell generates powershell completions exec chezmoi completion powershell stdout 'Register-ArgumentCompleter' # test that chezmoi completion zsh generates zsh completions exec chezmoi completion zsh stdout '#compdef chezmoi' # test that --color t values are completed exec chezmoi __complete --color t cmp stdout golden/auto-bool-t # test that --config-format flags are completed exec chezmoi __complete --config-format '' cmp stdout golden/config-format # test that --mode values are completed exec chezmoi __complete --mode '' cmp stdout golden/mode # test that --use-builtin flags are completed exec chezmoi __complete --use-builtin cmp stdout golden/use-builtin-flags # test that add --secrets values are completed exec chezmoi __complete add --secrets= cmp stdout golden/secrets # test that apply --exclude values are completed exec chezmoi __complete apply --exclude= cmp stdout golden/entry-type-set # test that apply --include values are completed exec chezmoi __complete apply --include= cmp stdout golden/entry-type-set # test that apply --refresh-externals values are completed exec chezmoi __complete apply --refresh-externals= cmp stdout golden/refresh-externals # test that archive --format values are completed exec chezmoi __complete archive --format= cmp stdout golden/archive-format # test that data --format values are completed exec chezmoi __complete data --format= cmp stdout golden/output-format-with-empty # test that dump --format values are completed exec chezmoi __complete dump --format= cmp stdout golden/output-format-with-empty # test that dump-config --format values are completed exec chezmoi __complete dump-config --format= cmp stdout golden/output-format-with-empty # test that managed path style values are completed exec chezmoi __complete managed --path-style= cmp stdout golden/path-style-with-source # test that state data --format values are completed exec chezmoi __complete state data --format= cmp stdout golden/output-format-with-empty # test that state dump --format values are completed exec chezmoi __complete state dump --format= cmp stdout golden/output-format-with-empty # test that status path style values are completed exec chezmoi __complete status --path-style= cmp stdout golden/path-style # test that unmanaged path style values are completed exec chezmoi __complete unmanaged --path-style= cmp stdout golden/unmanaged-path-style -- golden/archive-format -- tar tar.gz tgz zip :4 -- golden/auto-bool-t -- t true :4 -- golden/config-format -- json toml yaml :4 -- golden/entry-type-set -- all always dirs encrypted externals files noalways nodirs noencrypted noexternals nofiles none noremove noscripts nosymlinks notemplates remove scripts symlinks templates :6 -- golden/mode -- file symlink :4 -- golden/output-format -- json yaml :4 -- golden/output-format-with-empty -- json yaml :4 -- golden/path-style -- absolute relative :4 -- golden/path-style-with-source -- absolute all relative source-absolute source-relative :4 -- golden/refresh-externals -- always auto never :4 -- golden/secrets -- error ignore warning :4 -- golden/unmanaged-path-style -- absolute relative :4 -- golden/use-builtin-flags -- --use-builtin-age Use builtin age --use-builtin-diff Use builtin diff --use-builtin-git Use builtin git :4 ================================================ FILE: internal/cmd/testdata/scripts/completion_unix.txtar ================================================ [windows] skip 'UNIX only' # test chezmoi --include completion exec chezmoi __complete apply --include=d cmp stdout golden/complete-apply-include-d # test chezmoi --secrets completion exec chezmoi __complete add --secrets=e cmp stdout golden/complete-secrets-e # test chezmoi cat completion of targets in a directory exec chezmoi __complete cat $HOME cmpenv stdout golden/complete-target-home # test chezmoi cat completion of matching absolute targets exec chezmoi __complete cat $HOME/.f cmpenv stdout golden/complete-target-home-dot-f # test chezmoi cat completion of matching relative targets cd $HOME exec chezmoi __complete cat .f cmpenv stdout $WORK/golden/complete-dot-f-in-home cd $WORK # test chezmoi chattr completion of attributes exec chezmoi __complete chattr p cmp stdout golden/complete-attribute-p # test chezmoi chattr completion of targets exec chezmoi __complete cat private $HOME cmpenv stdout golden/complete-target-home -- golden/complete-apply-include-d -- dirs :6 -- golden/complete-attribute-p -- private :4 -- golden/complete-dot-f-in-home -- .file :4 -- golden/complete-secrets-e -- error :4 -- golden/complete-target-home -- $HOME/.dir/ $HOME/.dir/file $HOME/.file :4 -- golden/complete-target-home-dot-f -- $HOME/.file :4 -- home/user/.config/chezmoi/chezmoi.toml -- [completion] custom = true -- home/user/.local/share/chezmoi/dot_dir/file -- # contents of .dir/file -- home/user/.local/share/chezmoi/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/config.txtar ================================================ # test default config exec chezmoi data --format=yaml stdout 'sourceDir: .*/home/user/.local/share/chezmoi' # test that flags override default config exec chezmoi data --format=yaml --source=/flag/source stdout 'sourceDir: .*/flag/source' chhome home2/user # test that flags override default config exec chezmoi execute-template --source=/flag/source '{{ .chezmoi.sourceDir }}' stdout /flag/source # test that config files override default config exec chezmoi data --format=yaml stdout 'sourceDir: .*/config/source' # test that the config file can be set and can be in YAML format exec chezmoi data --config=$HOME/.chezmoi.yaml --format=yaml stdout 'sourceDir: .*/config2/source' # test that the config file can be in JSONC format exec chezmoi data --config=$HOME/.chezmoi.jsonc --format=yaml stdout 'sourceDir: .*/config3/source' # test that the cache directory can be set exec chezmoi data --cache=/flag/cache --format=yaml stdout 'cacheDir: .*/flag/cache' [windows] stop 'remaining tests require /dev/stdin' # test that chezmoi can read the config from stdin stdin home2/user/.chezmoi.yaml exec chezmoi data --config=/dev/stdin --config-format=yaml --format=yaml stdout 'sourceDir: .*/config2/source' -- home2/user/.chezmoi.jsonc -- { "color": "auto", // Color "sourceDir": "/config3/source", // Source directory } -- home2/user/.chezmoi.yaml -- color: auto sourceDir: /config2/source -- home2/user/.config/chezmoi/chezmoi.toml -- color = "auto" sourceDir = "/config/source" ================================================ FILE: internal/cmd/testdata/scripts/configstate.txtar ================================================ # test that chezmoi init creates a config file and updates the state exec chezmoi init cmp $CHEZMOICONFIGDIR/chezmoi.toml golden/chezmoi.toml exec chezmoi state dump --format=yaml cmp stdout golden/state-dump.yaml ! stderr . # test that chezmoi apply succeeds exec chezmoi apply ! stderr . # test that chezmoi apply prints a warning if the config file template has been changed cp golden/.chezmoi.toml.tmpl $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl exec chezmoi apply stderr 'warning: config file template has changed' # test that chezmoi apply does not print the warning if it is suppressed appendline $CHEZMOICONFIGDIR/chezmoi.toml '[warnings]' appendline $CHEZMOICONFIGDIR/chezmoi.toml ' configFileTemplateHasChanged = false' exec chezmoi apply ! stderr . cp golden/chezmoi.toml $CHEZMOICONFIGDIR # test that chezmoi init re-generates the config file exec chezmoi init cmp $CHEZMOICONFIGDIR/chezmoi.toml golden/chezmoi.toml ! stderr . # test that chezmoi apply no longer prints a warning after the config file is regenerated exec chezmoi apply ! stderr . # test that chezmoi apply --force ignores config file changes and updates the state edit $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl grep '# edited' $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl exec chezmoi apply --force ! stderr . ! grep '# edited' $CHEZMOICONFIGDIR/chezmoi.toml chhome home2/user # test that chezmoi diff prints a warning when a config file template is added exec chezmoi diff ! stderr . cp golden/chezmoi.toml $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl exec chezmoi diff stderr 'warning: config file template has changed' # test that chezmoi diff does not print a warning when the config file template is removed rm $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl exec chezmoi diff ! stderr . -- golden/.chezmoi.toml.tmpl -- # chezmoi:template:left-delimiter="((" right-delimiter="))" (( $email := get . "email" -)) (( if not $email -)) (( $email = promptString "email" -)) (( end -)) [data] email = (( $email | quote )) -- golden/chezmoi.toml -- [data] email = "me@home.org" -- golden/state-dump.yaml -- configState: configState: configTemplateContentsSHA256: af43121a524340707b84e390f510c949731177e6f2a25b3b6b11b2fc656cf8f2 entryState: {} gitHubKeysState: {} gitHubLatestReleaseState: {} gitHubReleasesState: {} gitHubTagsState: {} gitHubVersionReleaseState: {} gitRepoExternalState: {} scriptState: {} -- home/user/.local/share/chezmoi/.chezmoi.toml.tmpl -- [data] email = "me@home.org" -- home/user/.local/share/chezmoi/.git/.keep -- -- home2/user/.local/share/chezmoi/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/create.txtar ================================================ # test that chezmoi apply creates a file from a template mkdir $CHEZMOISOURCEDIR cp golden/create_dot_create.tmpl $CHEZMOISOURCEDIR exec chezmoi apply --force cmp $HOME/.create golden/.create [windows] skip 'UNIX only' [!exec:age] skip 'age not found in $PATH' chhome home2/user # test that chezmoi apply creates encrypted create_ targets mkageconfig mkdir $CHEZMOISOURCEDIR exec chezmoi encrypt --output=$CHEZMOISOURCEDIR/create_encrypted_dot_create.age golden/.create grep '-----BEGIN AGE ENCRYPTED FILE-----' $CHEZMOISOURCEDIR/create_encrypted_dot_create.age exec chezmoi apply --force cmp $HOME/.create golden/.create chhome home3/user # test that chezmoi apply creates encrypted template create_ targets mkageconfig mkdir $CHEZMOISOURCEDIR exec chezmoi encrypt --output=$CHEZMOISOURCEDIR/create_encrypted_dot_create.tmpl.age golden/create_dot_create.tmpl grep '-----BEGIN AGE ENCRYPTED FILE-----' $CHEZMOISOURCEDIR/create_encrypted_dot_create.tmpl.age exec chezmoi apply --force cmp $HOME/.create golden/.create chhome home4/user # test that chezmoi manage does not execute template create_ targets exec chezmoi managed cmp stdout golden/managed -- golden/.create -- # contents of .create -- golden/create_dot_create.tmpl -- {{ "# contents of .create" }} -- golden/managed -- .create -- home4/user/.local/share/chezmoi/create_dot_create.tmpl -- {{ fail "Template should not be executed }} ================================================ FILE: internal/cmd/testdata/scripts/dashlane.txtar ================================================ mockcommand bin/dcli [windows] unix2dos golden/dashlane-note # test dashlanePassword template function exec chezmoi execute-template '{{ (index (dashlanePassword "filter") 0).password }}' stdout ^$ # test dashlaneNote template function exec chezmoi execute-template '{{ dashlaneNote "filter" }}' cmp stdout golden/dashlane-note -- bin/dcli.yaml -- responses: - args: 'password --output json filter' response: | [ { "title": "", "useFixedUrl": false, "login": "", "status": "ACCOUNT_NOT_VERIFIED", "note": "", "autoLogin": false, "modificationDatetime": "", "checked": false, "id": "", "anonId": "", "localeFormat": "UNIVERSAL", "password": "", "creationDatetime": "", "userModificationDatetime": "", "lastBackupTime": "", "autoProtected": false, "strength": 0, "subdomainOnly": false } ] - args: 'note filter' response: '' escapeChars: true default: response: "error: unknown command '$*'" exitCode: 1 -- golden/dashlane-note -- ================================================ FILE: internal/cmd/testdata/scripts/data.txtar ================================================ # test that chezmoi data includes data set in config file exec chezmoi data stdout '"chezmoi":' stdout '"uniqueKey": "uniqueValue"' # test that chezmoi data --format=json includes data set in config file exec chezmoi data --format=json stdout '"chezmoi":' stdout '"uniqueKey": "uniqueValue"' # test that chezmoi data --format=yaml includes data set in config file exec chezmoi data --format=yaml stdout 'chezmoi:' stdout 'uniqueKey: uniqueValue' -- home/user/.config/chezmoi/chezmoi.toml -- [data] uniqueKey = "uniqueValue" ================================================ FILE: internal/cmd/testdata/scripts/debug.txtar ================================================ [!exec:age] skip 'age not found in $PATH' mkageconfig mksourcedir httpd www # test that chezmoi apply --debug writes logs exec chezmoi encrypt --output=$CHEZMOISOURCEDIR/encrypted_dot_encrypted.asc exec chezmoi apply --debug stderr component=encryption stderr component=persistentState stderr component=sourceState stderr component=system -- golden/.encrypted -- # contents of .encrypted -- home/user/.local/share/chezmoi/.chezmoiexternal.yaml -- .external: type: file url: {{ env "HTTPD_URL" }}/.external -- www/.external -- # contents of .external ================================================ FILE: internal/cmd/testdata/scripts/destroy.txtar ================================================ # test that chezmoi destroy file destroys a file exec chezmoi apply --force exists $HOME/.file exec chezmoi destroy --force $HOME${/}.file ! exists $HOME/.file exec chezmoi state get --bucket=entryState --key=$WORK/home/user/.file ! stdout . # test that chezmoi destroy dir destroys a directory exists $CHEZMOISOURCEDIR/dot_dir exists $HOME/.dir exec chezmoi destroy --force $HOME${/}.dir ! exists $HOME/.dir # test that if any chezmoi destroy stops on any error exists $CHEZMOISOURCEDIR/executable_dot_executable exists $HOME/.executable ! exec chezmoi destroy --force $HOME${/}.newfile $HOME${/}.executable stderr 'not managed' exists $HOME/.executable chhome home2/user # test that chezmoi apply destroys a file and a directory exists $HOME/.file exists $HOME/.dir exec chezmoi apply ! exists $HOME/.file ! exists $HOME/.dir chhome home3/user # test that chezmoi apply with .chezmoiremove with star works on destination dir with trailing slash exists $HOME/.star-file exists $HOME/.star-dir exec chezmoi apply --destination=$HOME/ ! exists $HOME/.star-file ! exists $HOME/.star-dir -- home/user/.local/share/chezmoi/dot_dir/.keep -- -- home/user/.local/share/chezmoi/dot_file -- # contents of .file -- home/user/.local/share/chezmoi/executable_dot_executable -- # contents of .executable -- home2/user/.dir/.keep -- -- home2/user/.file -- # contents of .file -- home2/user/.local/share/chezmoi/remove_dot_dir -- -- home2/user/.local/share/chezmoi/remove_dot_file -- -- home3/user/.local/share/chezmoi/.chezmoiremove -- .*-dir/ .*-file -- home3/user/.star-dir/.keep -- -- home3/user/.star-file -- # contents of .star-file ================================================ FILE: internal/cmd/testdata/scripts/diff.txtar ================================================ [!umask:022] skip mkhomedir golden mkhomedir mksourcedir # test that chezmoi diff generates no output when the source and destination states are equal exec chezmoi diff ! stdout . # test that chezmoi diff generates a diff when a file is added to the source state cp golden/dot_newfile $CHEZMOISOURCEDIR/dot_newfile exec chezmoi diff [unix] cmp stdout golden/add-newfile-diff-unix.diff [windows] cmp stdout golden/add-newfile-diff-windows.diff rm $CHEZMOISOURCEDIR/dot_newfile # test that chezmoi diff generates a diff when a file is edited edit $HOME/.file exec chezmoi diff [unix] cmp stdout golden/modify-file-diff-unix.diff [windows] cmp stdout golden/modify-file-diff-windows.diff exec chezmoi apply --force $HOME${/}.file # test chezmoi diff --reverse edit $HOME/.file exec chezmoi diff --reverse [unix] cmp stdout golden/modify-file-diff-reverse-unix.diff [windows] cmp stdout golden/modify-file-diff-reverse-windows.diff exec chezmoi apply --force $HOME${/}.file # test that chezmoi diff generates a diff when a file is removed from the destination directory rm $HOME/.file exec chezmoi diff [unix] cmp stdout golden/restore-file-diff-unix.diff [windows] cmp stdout golden/restore-file-diff-windows.diff exec chezmoi apply --force $HOME${/}.file # test that chezmoi diff generates a diff when a directory is removed from the destination directory rm $HOME/.dir exec chezmoi diff --recursive=false $HOME${/}.dir [unix] cmp stdout golden/restore-dir-diff-unix.diff [windows] cmp stdout golden/restore-dir-diff-windows.diff exec chezmoi apply --force $HOME${/}.dir # test that chezmoi diff generates a diff when the actual state is a file and the target state is a symlink rm $HOME/.symlink cp golden/.file $HOME/.symlink exec chezmoi diff [unix] cmp stdout golden/symlink-file-diff-unix.diff # [windows] cmp stdout golden/symlink-file-diff-windows # FIXME exec chezmoi apply --force $HOME${/}.symlink [windows] stop 'remaining tests use file modes' # test that chezmoi diff generates a diff when a file's permissions are changed chmod 777 $HOME/.file exec chezmoi diff cmp stdout golden/chmod-file-diff.diff exec chezmoi apply --force $HOME${/}.file # test that chezmoi diff generates a diff when a directory's permissions are changed chmod 700 $HOME/.dir exec chezmoi diff cmp stdout golden/chmod-dir-diff.diff exec chezmoi apply --force --recursive=false $HOME${/}.dir -- golden/add-newfile-diff-unix.diff -- diff --git a/.newfile b/.newfile new file mode 100644 index 0000000000000000000000000000000000000000..06e05235fdd12fd5c367b6d629fef94536c85525 --- /dev/null +++ b/.newfile @@ -0,0 +1 @@ +# contents of .newfile -- golden/add-newfile-diff-windows.diff -- diff --git a/.newfile b/.newfile new file mode 100666 index 0000000000000000000000000000000000000000..06e05235fdd12fd5c367b6d629fef94536c85525 --- /dev/null +++ b/.newfile @@ -0,0 +1 @@ +# contents of .newfile -- golden/chmod-dir-diff.diff -- diff --git a/.dir b/.dir old mode 40700 new mode 40755 -- golden/chmod-file-diff.diff -- diff --git a/.file b/.file old mode 100777 new mode 100644 -- golden/dot_newfile -- # contents of .newfile -- golden/modify-file-diff-reverse-unix.diff -- diff --git a/.file b/.file index 8a52cb9ce9551221716a53786ad74104c5902362..5d2730a8850a2db479af83de87cc8345437aef06 100644 --- a/.file +++ b/.file @@ -1 +1,2 @@ # contents of .file +# edited -- golden/modify-file-diff-reverse-windows.diff -- diff --git a/.file b/.file index 8a52cb9ce9551221716a53786ad74104c5902362..5d2730a8850a2db479af83de87cc8345437aef06 100666 --- a/.file +++ b/.file @@ -1 +1,2 @@ # contents of .file +# edited -- golden/modify-file-diff-unix.diff -- diff --git a/.file b/.file index 5d2730a8850a2db479af83de87cc8345437aef06..8a52cb9ce9551221716a53786ad74104c5902362 100644 --- a/.file +++ b/.file @@ -1,2 +1 @@ # contents of .file -# edited -- golden/modify-file-diff-windows.diff -- diff --git a/.file b/.file index 5d2730a8850a2db479af83de87cc8345437aef06..8a52cb9ce9551221716a53786ad74104c5902362 100666 --- a/.file +++ b/.file @@ -1,2 +1 @@ # contents of .file -# edited -- golden/restore-dir-diff-unix.diff -- diff --git a/.dir b/.dir new file mode 40755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 --- /dev/null +++ b/.dir -- golden/restore-dir-diff-windows.diff -- diff --git a/.dir b/.dir new file mode 40777 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 --- /dev/null +++ b/.dir -- golden/restore-file-diff-unix.diff -- diff --git a/.file b/.file new file mode 100644 index 0000000000000000000000000000000000000000..8a52cb9ce9551221716a53786ad74104c5902362 --- /dev/null +++ b/.file @@ -0,0 +1 @@ +# contents of .file -- golden/restore-file-diff-windows.diff -- diff --git a/.file b/.file new file mode 100666 index 0000000000000000000000000000000000000000..8a52cb9ce9551221716a53786ad74104c5902362 --- /dev/null +++ b/.file @@ -0,0 +1 @@ +# contents of .file -- golden/symlink-file-diff-unix.diff -- diff --git a/.symlink b/.symlink deleted file mode 100644 index 8a52cb9ce9551221716a53786ad74104c5902362..0000000000000000000000000000000000000000 --- a/.symlink +++ /dev/null @@ -1 +0,0 @@ -# contents of .file diff --git a/.symlink b/.symlink old mode 100644 new mode 120000 index 8a52cb9ce9551221716a53786ad74104c5902362..9b91fdbb83798a67fbbc5cc4f120c3f7726c0d70 --- a/.symlink +++ b/.symlink @@ -1 +1 @@ -# contents of .file +.dir/subdir/file ================================================ FILE: internal/cmd/testdata/scripts/diffcommand_unix.txtar ================================================ [windows] skip 'UNIX only' chmod 755 bin/diff-pager # test that chezmoi diff invokes diff.command when configured exec chezmoi diff stdout ^${HOME@R}/\.file\s+${WORK@R}/.*/\.file$ # test that chezmoi diff --use-builtin-diff uses the builtin diff even if diff.command is configured exec chezmoi diff --use-builtin-diff [umask:002] cmp stdout golden/diff-umask-002.diff [umask:022] cmp stdout golden/diff-umask-022.diff # test that chezmoi diff --reverse reverses the order of arguments exec chezmoi diff --reverse stdout ^${WORK@R}/.*/\.file\s+${HOME@R}/\.file$ # test that chezmoi apply --verbose uses diff.command exec chezmoi apply --verbose stdout ^${HOME@R}/\.file\s+${WORK@R}/.*/\.file$ chhome home2/user # test that chezmoi diff appends the destination and target paths if diff.args does not contain any templates exec chezmoi diff stdout ^arg\s+${HOME@R}/\.file\s+${WORK@R}/.*/\.file$ # test that chezmoi apply --verbose uses diff.command exec chezmoi apply --verbose stdout ^arg\s+${HOME@R}/\.file\s+${WORK@R}/.*/\.file$ exists $HOME/.file chhome home3/user # test that chezmoi diff ignores exit code 1 from diff.command if the files differ exec chezmoi diff chhome home4/user # test that chezmoi diff does not ignore exit code 2 from diff.command ! exec chezmoi diff stderr 'exit status 2' chhome home5/user # test that chezmoi diff does not invoke diff.command for directories if directories are excluded exec chezmoi diff stdout \.file ! stdout \.dir chhome home6/user # test that chezmoi diff does not invoke the diff pager when there is no diff exec chezmoi diff ! stdout . # test that chezmoi diff does invoke the diff pager when there is a diff cp golden/dot_file $CHEZMOISOURCEDIR exec chezmoi diff stdout diff-pager -- bin/diff-pager -- #!/bin/sh echo diff-pager -- golden/diff-umask-002.diff -- diff --git a/.file b/.file index bd729e8ee3cc005444c67dc77eed60016886b5e0..b508963510528ab709627ec448026a10a64c72ef 100664 --- a/.file +++ b/.file @@ -1 +1 @@ -# destination contents of .file +# target contents of .file -- golden/diff-umask-022.diff -- diff --git a/.file b/.file index bd729e8ee3cc005444c67dc77eed60016886b5e0..b508963510528ab709627ec448026a10a64c72ef 100644 --- a/.file +++ b/.file @@ -1 +1 @@ -# destination contents of .file +# target contents of .file -- golden/dot_file -- # contents of .file -- home/user/.config/chezmoi/chezmoi.toml -- [diff] command = "echo" -- home/user/.file -- # destination contents of .file -- home/user/.local/share/chezmoi/dot_file -- # target contents of .file -- home2/user/.config/chezmoi/chezmoi.toml -- [diff] command = "echo" args = ["arg"] -- home2/user/.file -- # destination contents of .file -- home2/user/.local/share/chezmoi/dot_file -- # target contents of .file -- home3/user/.config/chezmoi/chezmoi.toml -- [diff] command = "false" -- home3/user/.local/share/chezmoi/dot_file -- # target contents of .file -- home4/user/.config/chezmoi/chezmoi.toml -- [diff] command = "sh" args = ["-c", "exit 2"] -- home4/user/.local/share/chezmoi/dot_file -- # target contents of .file -- home5/user/.config/chezmoi/chezmoi.toml -- [diff] command = "echo" exclude = ["dirs"] -- home5/user/.local/share/chezmoi/dot_dir/.keep -- -- home5/user/.local/share/chezmoi/dot_file -- # contents of .file -- home6/user/.config/chezmoi/chezmoi.toml -- [diff] pager = "diff-pager" -- home6/user/.local/share/chezmoi/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/diffcommand_windows.txtar ================================================ [unix] skip 'Windows only' # test that chezmoi diff invokes diff.command when configured exec chezmoi diff stdout ^arg1\s+.*/\.file\s+.*/\.file\r$ -- bin/diff.cmd -- @echo off echo %* -- home/user/.config/chezmoi/chezmoi.toml -- [diff] command = "diff" args = ["arg1"] -- home/user/.file -- # destination contents of .file -- home/user/.local/share/chezmoi/dot_file -- # target contents of .file ================================================ FILE: internal/cmd/testdata/scripts/discussion4732.txtar ================================================ exec tar czf www/archive1.tar.gz archive1 exec tar czf www/archive2.tar.gz archive2 exec tar czf www/archive3.tar.gz archive3 exec tar czf www/archive4.tar.gz archive4 httpd www # test that chezmoi externals allow multiple entries targeting the same directory in a single file exec chezmoi apply cmp $HOME/.local/bin/uv archive1/uv cmp $HOME/.local/bin/uvx archive1/uvx cmp $HOME/.local/bin/ya archive2/ya cmp $HOME/.local/bin/yazi archive2/yazi cmp $HOME/.local/bin/todo.sh archive3/todo.sh cmp $HOME/.local/bin/print.sh archive4/print.sh -- archive1/uv -- # contents of uv binary -- archive1/uvx -- # contents of uvx binary -- archive2/ya -- # contents of ya binary -- archive2/yazi -- # contents of yazi binary -- archive3/todo.sh -- # contents of todo.sh -- archive4/print.sh -- # contents of print.sh -- home/user/.local/share/chezmoi/.chezmoiexternal.toml.tmpl -- [uv_binaries] type = "archive" url = "{{ env "HTTPD_URL" }}/archive1.tar.gz" stripComponents = 1 include = ["**/uv", "**/uvx"] targetPath = ".local/bin" [ya_binaries] type = "archive" url = "{{ env "HTTPD_URL" }}/archive2.tar.gz" stripComponents = 1 include = ["**/ya", "**/yazi"] targetPath = ".local/bin" [".local/bin/todo.sh"] type = "archive-file" url = "{{ env "HTTPD_URL" }}/archive3.tar.gz" stripComponents = 1 path = "todo.sh" [print_script] type = "archive-file" url = "{{ env "HTTPD_URL" }}/archive4.tar.gz" stripComponents = 1 path = "print.sh" targetPath = ".local/bin/print.sh" -- www/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/doctor_unix.txtar ================================================ [windows] skip 'UNIX only' chmod 755 bin/age chmod 755 bin/bw chmod 755 bin/bws chmod 755 bin/dcli chmod 755 bin/doppler chmod 755 bin/git chmod 755 bin/gopass chmod 755 bin/gpg chmod 755 bin/keepassxc-cli chmod 755 bin/keeper chmod 755 bin/lpass chmod 755 bin/op chmod 755 bin/pass chmod 755 bin/pass-cli chmod 755 bin/ph chmod 755 bin/pinentry chmod 755 bin/rbw chmod 755 bin/secret chmod 755 bin/shell chmod 755 bin/vault chmod 755 bin/vimdiff mkhomedir mksourcedir # test that chezmoi doctor behaves as expected exec chezmoi doctor stdout '^ok\s+version\s+' stdout '^\w+\s+latest-version\s+' stdout '^ok\s+os-arch\s+' ! stdout '^\S+\s+systeminfo\s+' stdout '^ok\s+uname\s+' stdout '^ok\s+config-file\s+' stdout '^ok\s+source-dir\s+' stdout '^warning\s+suspicious-entries\s+' stdout '^ok\s+dest-dir\s+' stdout '^ok\s+shell-command\s+' stdout '^ok\s+shell-args\s+' stdout '^ok\s+cd-command\s+' stdout '^ok\s+cd-args\s+' stdout '^ok\s+edit-command\s+' stdout '^ok\s+edit-args\s+' stdout '^ok\s+git-command\s+' stdout '^ok\s+merge-command\s+' stdout '^warning\s+age-command\s+' stdout '^ok\s+gpg-command\s+' stdout '^ok\s+pinentry-command\s+' stdout '^ok\s+1password-command\s+' stdout '^ok\s+bitwarden-command\s+' stdout '^ok\s+bitwarden-secrets-command\s+' stdout '^ok\s+dashlane-command\s+' stdout '^ok\s+doppler-command\s+' stdout '^ok\s+gopass-command\s+' stdout '^ok\s+keepassxc-command\s+' stdout '^info\s+keepassxc-db\s+' stdout '^ok\s+keeper-command\s+' stdout '^ok\s+passhole-command\s+' stdout '^ok\s+lastpass-command\s+' stdout '^ok\s+pass-command\s+' stdout '^ok\s+protonpass-command\s+' stdout '^ok\s+rbw-command\s+' stdout '^ok\s+vault-command\s+' stdout '^ok\s+secret-command\s+' chhome home2/user # test that chezmoi doctor warns about missing directories on an empty system ! exec chezmoi doctor stdout '^info\s+config-file\s+' stdout '^error\s+source-dir\s+' stdout '^ok\s+suspicious-entries\s+' chhome home3/user # test that chezmoi doctor warns about suspicious entries exec chezmoi doctor stdout '^warning\s+suspicious-entries\s+' chhome home4/user # test that chezmoi doctor prints an error about multiple config files ! exec chezmoi doctor stdout '^error\s+config-file\s+multiple config files' ! stderr . chhome home5/user # test that chezmoi doctor warns about encrypted files exec chezmoi doctor stdout '^warning\s+suspicious-entries\s+' -- bin/age -- #!/bin/sh echo "(devel)" -- bin/bw -- #!/bin/sh echo '(node:84023) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.' 1>&2 echo '(Use `node --trace-deprecation ...` to show where the warning was created)' 1>&2 echo "2023.10.0" -- bin/bws -- #!/bin/sh echo "Bitwarden Secrets CLI 0.3.0" -- bin/dcli -- #!/bin/sh echo 1.0.0 -- bin/doppler -- #!/bin/sh echo "v3.65.1" -- bin/git -- #!/bin/sh echo "git version 2.29.2" -- bin/gopass -- #!/bin/sh echo "gopass 1.10.1 go1.15 linux amd64" -- bin/gpg -- #!/bin/sh echo "gpg (GnuPG) 2.2.23" echo "libgcrypt 1.8.7" echo "Copyright (C) 2020 Free Software Foundation, Inc." echo "License GPLv3+: GNU GPL version 3 or later " echo "This is free software: you are free to change and redistribute it." echo "There is NO WARRANTY, to the extent permitted by law." echo "" echo "Home: /home/user/.gnupg" echo "Supported algorithms:" echo "Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA" echo "Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH," echo " CAMELLIA128, CAMELLIA192, CAMELLIA256" echo "Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224" echo "Compression: Uncompressed, ZIP, ZLIB, BZIP2" -- bin/keepassxc-cli -- #!/bin/sh echo "2.7.0" -- bin/keeper -- #!/bin/sh echo "Commander Version: 16.6.4 (Current version)" -- bin/lpass -- #!/bin/sh echo "LastPass CLI v1.3.3.GIT" -- bin/op -- #!/bin/sh echo "2.0.0" -- bin/pass -- #!/bin/sh echo "============================================" echo "= pass: the standard unix password manager =" echo "= =" echo "= v1.7.3 =" echo "= =" echo "= Jason A. Donenfeld =" echo "= Jason@zx2c4.com =" echo "= =" echo "= http://www.passwordstore.org/ =" echo "============================================" -- bin/pass-cli -- #!/bin/sh echo "Proton Pass CLI 1.2.0 (0d71fde)" -- bin/ph -- #!/bin/sh echo 1.10.0 -- bin/pinentry -- #!/bin/sh echo "pinentry-curses (pinentry) 1.2.0" echo "Copyright (C) 2016 g10 Code GmbH" echo "License GPLv2+: GNU GPL version 2 or later " echo "This is free software: you are free to change and redistribute it." echo "There is NO WARRANTY, to the extent permitted by law." -- bin/rbw -- #!/bin/sh echo rbw 1.7.0 -- bin/secret -- #!/bin/sh -- bin/shell -- #!/bin/sh -- bin/vault -- #!/bin/sh echo "Vault v1.5.5 ('f5d1ddb3750e7c28e25036e1ef26a4c02379fc01+CHANGES')" -- bin/vimdiff -- #!/bin/sh -- home/user/.config/chezmoi/chezmoi.toml -- [pinentry] command = "pinentry" [secret] command = "secret" -- home/user/.local/share/chezmoi/.chezmoidata.json -- -- home/user/.local/share/chezmoi/.chezmoiexternal.toml -- -- home/user/.local/share/chezmoi/.chezmoiscripts/.keep -- -- home/user/.local/share/chezmoi/dot_config/chezmoi/chezmoi.toml.tmpl -- -- home3/user/.local/share/chezmoi/.chezmoisuspicious -- -- home4/user/.config/chezmoi/chezmoi.json -- -- home4/user/.config/chezmoi/chezmoi.yaml -- -- home5/user/.config/chezmoi/chezmoi.toml -- encryption = "age" [age] suffix = ".age-encrypted" -- home5/user/.local/share/chezmoi/dot_config/chezmoi/encrypted_chezmoi.toml.age-encrypted -- ================================================ FILE: internal/cmd/testdata/scripts/doctor_windows.txtar ================================================ [unix] skip 'Windows only' mksourcedir # test chezmoi doctor exec chezmoi doctor stdout '^ok\s+systeminfo\s+' ================================================ FILE: internal/cmd/testdata/scripts/doppler.txtar ================================================ mockcommand bin/doppler # test doppler template function (global configuration) exec chezmoi execute-template '{{ doppler "PASSWORD_123"}}' stdout ^staplebatteryhorsecorrect$ # test doppler template function with project and config arguments (supplied configuration) exec chezmoi execute-template '{{ doppler "PASSWORD" "project" "config" }}' stdout ^correcthorsebatterystaple$ # test doppler template function with empty project and config arguments (global configuration) exec chezmoi execute-template '{{ doppler "PASSWORD" "" "" }}' stdout ^correcthorsebatterystaple$ # test dopplerProjectJson template function with project and config arguments (supplied configuration) exec chezmoi execute-template '{{ (dopplerProjectJson "project" "config").PASSWORD_123 }}' stdout ^staplebatteryhorsecorrect$ # test dopplerProjectJson template function with JSON secret piped to fromJson function, project and config arguments exec chezmoi execute-template '{{ ((dopplerProjectJson "project" "config").JSON_SECRET | fromJson).created_by.email }}' stdout ^user@example\.com$ # test dopplerProjectJson template function with project and empty config arguments (global configuration) exec chezmoi execute-template '{{ (dopplerProjectJson "project" "").PASSWORD }}' stdout ^correcthorsebatterystaple$ # test dopplerProjectJson template function with empty project and empty config arguments (global configuration) exec chezmoi execute-template '{{ (dopplerProjectJson "" "").PASSWORD }}' stdout ^correcthorsebatterystaple$ # test dopplerProjectJson template function without project and config arguments (global configuration) exec chezmoi execute-template '{{ dopplerProjectJson.PASSWORD }}' stdout ^correcthorsebatterystaple$ chhome home3/user # test doppler template function with default project and config arguments (chezmoi configuration) exec chezmoi execute-template '{{ doppler "PASSWORD" }}' stdout ^default-project-password$ # test doppler template function with project and default config arguments (chezmoi configuration) exec chezmoi execute-template '{{ doppler "PASSWORD" "other-project" }}' stdout ^other-project-password$ # test doppler template function with project and default config arguments (supplied configuration) exec chezmoi execute-template '{{ doppler "PASSWORD" "project" "config" }}' stdout ^correcthorsebatterystaple$ # test dopplerProjectJson template function with project and default config arguments (chezmoi configuration) exec chezmoi execute-template '{{ (dopplerProjectJson "default-project").DOPPLER_CONFIG }}' stdout ^default-config$ # test dopplerProjectJson template function with default project and config arguments (chezmoi configuration) exec chezmoi execute-template '{{ (dopplerProjectJson).DOPPLER_PROJECT }}' stdout ^default-project$ -- bin/doppler.yaml -- responses: - args: 'secrets download --json --no-file --project project --config config' orArgs: - 'secrets download --json --no-file --project project' - 'secrets download --json --no-file' response: | { "DOPPLER_CONFIG": "config", "DOPPLER_ENVIRONMENT": "config", "DOPPLER_PROJECT": "project", "PASSWORD": "correcthorsebatterystaple", "PASSWORD_123": "staplebatteryhorsecorrect", "JSON_SECRET": "{\n \"created_at\": \"2023-06-09T13:14:28.140Z\",\n \"created_by\": {\n \"email\": \"user@example.com\",\n \"name\": \"example\",\n \"type\": \"TYPE_USER\"\n },\n \"latest_version\": \"2\",\n \"name\": \"password\"\n}" } - args: 'secrets download --json --no-file --project default-project --config default-config' response: | { "DOPPLER_CONFIG": "default-config", "DOPPLER_ENVIRONMENT": "default-config", "DOPPLER_PROJECT": "default-project", "PASSWORD": "default-project-password" } - args: 'secrets download --json --no-file --project other-project --config default-config' response: | { "DOPPLER_CONFIG": "default-config", "DOPPLER_ENVIRONMENT": "default-config", "DOPPLER_PROJECT": "other-project", "PASSWORD": "other-project-password" } default: response: '$*: unknown command' exitCode: 1 -- home/user/.keep -- -- home3/user/.config/chezmoi/chezmoi.toml -- [doppler] project = "default-project" config = "default-config" ================================================ FILE: internal/cmd/testdata/scripts/dumpconfig.txtar ================================================ # test that chezmoi dump-config dumps the configuration in the given format exec chezmoi dump-config --format=yaml stdout 'key: value' -- home/user/.config/chezmoi/chezmoi.toml -- [data] key = "value" ================================================ FILE: internal/cmd/testdata/scripts/dumpjson.txtar ================================================ [!umask:022] skip mksourcedir exec chezmoi dump --format=json cmp stdout golden/dump.json exec chezmoi dump --format=json $HOME${/}.file cmp stdout golden/dump-file.json exec chezmoi dump --format=json $HOME${/}.dir cmp stdout golden/dump-dir.json exec chezmoi dump --format=json --recursive=false $HOME${/}.dir cmp stdout golden/dump-dir-non-recursive.json ! exec chezmoi dump $HOME${/}.inputrc stderr 'not managed' -- golden/dump-dir-non-recursive.json -- { ".dir": { "type": "dir", "name": ".dir", "perm": 493 } } -- golden/dump-dir.json -- { ".dir": { "type": "dir", "name": ".dir", "perm": 493 }, ".dir/file": { "type": "file", "name": ".dir/file", "contents": "# contents of .dir/file\n", "perm": 420 }, ".dir/subdir": { "type": "dir", "name": ".dir/subdir", "perm": 493 }, ".dir/subdir/file": { "type": "file", "name": ".dir/subdir/file", "contents": "# contents of .dir/subdir/file\n", "perm": 420 } } -- golden/dump-file.json -- { ".file": { "type": "file", "name": ".file", "contents": "# contents of .file\n", "perm": 420 } } -- golden/dump.json -- { ".create": { "type": "file", "name": ".create", "contents": "# contents of .create\n", "perm": 420 }, ".dir": { "type": "dir", "name": ".dir", "perm": 493 }, ".dir/file": { "type": "file", "name": ".dir/file", "contents": "# contents of .dir/file\n", "perm": 420 }, ".dir/subdir": { "type": "dir", "name": ".dir/subdir", "perm": 493 }, ".dir/subdir/file": { "type": "file", "name": ".dir/subdir/file", "contents": "# contents of .dir/subdir/file\n", "perm": 420 }, ".empty": { "type": "file", "name": ".empty", "contents": "", "perm": 420 }, ".executable": { "type": "file", "name": ".executable", "contents": "# contents of .executable\n", "perm": 493 }, ".file": { "type": "file", "name": ".file", "contents": "# contents of .file\n", "perm": 420 }, ".private": { "type": "file", "name": ".private", "contents": "# contents of .private\n", "perm": 384 }, ".readonly": { "type": "file", "name": ".readonly", "contents": "# contents of .readonly\n", "perm": 292 }, ".symlink": { "type": "symlink", "name": ".symlink", "linkname": ".dir/subdir/file" }, ".template": { "type": "file", "name": ".template", "contents": "key = value\n", "perm": 420 } } ================================================ FILE: internal/cmd/testdata/scripts/dumpyaml.txtar ================================================ [!umask:022] skip mksourcedir exec chezmoi dump --format=yaml cmp stdout golden/dump.yaml exec chezmoi dump --exclude=dirs --format=yaml cmp stdout golden/dump-except-dirs.yaml -- golden/dump-except-dirs.yaml -- .create: type: file name: .create contents: "# contents of .create\n" perm: 420 .dir/file: type: file name: .dir/file contents: "# contents of .dir/file\n" perm: 420 .dir/subdir/file: type: file name: .dir/subdir/file contents: "# contents of .dir/subdir/file\n" perm: 420 .empty: type: file name: .empty contents: "" perm: 420 .executable: type: file name: .executable contents: "# contents of .executable\n" perm: 493 .file: type: file name: .file contents: "# contents of .file\n" perm: 420 .private: type: file name: .private contents: "# contents of .private\n" perm: 384 .readonly: type: file name: .readonly contents: "# contents of .readonly\n" perm: 292 .symlink: type: symlink name: .symlink linkname: .dir/subdir/file .template: type: file name: .template contents: | key = value perm: 420 -- golden/dump.yaml -- .create: type: file name: .create contents: "# contents of .create\n" perm: 420 .dir: type: dir name: .dir perm: 493 .dir/file: type: file name: .dir/file contents: "# contents of .dir/file\n" perm: 420 .dir/subdir: type: dir name: .dir/subdir perm: 493 .dir/subdir/file: type: file name: .dir/subdir/file contents: "# contents of .dir/subdir/file\n" perm: 420 .empty: type: file name: .empty contents: "" perm: 420 .executable: type: file name: .executable contents: "# contents of .executable\n" perm: 493 .file: type: file name: .file contents: "# contents of .file\n" perm: 420 .private: type: file name: .private contents: "# contents of .private\n" perm: 384 .readonly: type: file name: .readonly contents: "# contents of .readonly\n" perm: 292 .symlink: type: symlink name: .symlink linkname: .dir/subdir/file .template: type: file name: .template contents: | key = value perm: 420 ================================================ FILE: internal/cmd/testdata/scripts/edgecases.txtar ================================================ # test that the source directory can be a symlink to another directory symlink $HOME/.chezmoi -> $CHEZMOISOURCEDIR exec chezmoi apply --source=$HOME${/}.chezmoi cmp $HOME/.file golden/.file # test that adding a directory ending in a slash only adds the directory once mkdir $HOME/.dir exec chezmoi add $HOME${/}.dir/ ! exists $CHEZMOISOURCEDIR/dot_dir/dot_dir chhome home2/user # test that chezmoi reports an inconsistent state error when a file should be both removed and present ! exec chezmoi apply stderr 'chezmoi: \.file: inconsistent state' chhome home3/user # test that chezmoi reports an error if there is a .chezmoi* file in the .chezmoitemplates directory ! exec chezmoi status stderr 'not allowed in \.chezmoitemplates directory' # test that chezmoi data returns an error if an unknown read format is specified ! exec chezmoi init --config-format=yml stderr 'flag: invalid value' # test that chezmoi data returns an error if an unknown write format is specified ! exec chezmoi data --format=yml stderr 'flag: invalid value' skip 'FIXME make the following test pass' chhome home5/user # test that chezmoi reports an inconsistent state error when a file should be both removed and present, even if the file is not already present ! exec chezmoi apply stderr 'chezmoi: \.file: inconsistent state -- golden/.file -- # contents of .file -- home/user/.local/share/chezmoi/dot_file -- # contents of .file -- home2/user/.file -- # contents of .file -- home2/user/.local/share/chezmoi/.chezmoiremove -- .file -- home2/user/.local/share/chezmoi/dot_file -- # contents of .file -- home3/user/.local/share/chezmoi/.chezmoitemplates/.chezmoiignore -- -- home5/user/.local/share/chezmoi/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/edgecasesumask.txtar ================================================ [!umask:022] skip mkhomedir # test that chezmoi add --dry-run does not modify anything exec chezmoi add --dry-run $HOME${/}.file ! exists $CHEZMOICONFIGDIR/chezmoistate.boltdb ! exists $CHEZMOISOURCEDIR/dot_file # test that chezmoi add updates the persistent state exec chezmoi add $HOME${/}.file exists $CHEZMOICONFIGDIR/chezmoistate.boltdb exists $CHEZMOISOURCEDIR/dot_file exec chezmoi state dump stdout 634a4dd193c7b3b926d2e08026aa81a416fd41cec52854863b974af422495663 # sha256sum of "# contents of .file\n" # test that chezmoi apply updates the state if the target and destination states match, even if the last written state does not edit $CHEZMOISOURCEDIR/dot_file edit $HOME/.file cmp $HOME/.file $CHEZMOISOURCEDIR/dot_file exec chezmoi apply --dry-run --force $HOME${/}.file exec chezmoi state dump ! stdout 2e9dd6a2a8c15b20d4b0882d4c0fb8c7eea4e8ece46818090b387132f9f84c34 # sha256sum of "# contents of .file\n# edited\n" exec chezmoi apply --force $HOME${/}.file exec chezmoi state dump stdout 2e9dd6a2a8c15b20d4b0882d4c0fb8c7eea4e8ece46818090b387132f9f84c34 # sha256sum of "# contents of .file\n# edited\n" ================================================ FILE: internal/cmd/testdata/scripts/edit.txtar ================================================ mkhomedir mksourcedir # test that chezmoi edit edits a single file exec chezmoi edit $HOME${/}.file grep -count=1 '# edited' $CHEZMOISOURCEDIR/dot_file ! grep '# edited' $HOME/.file # test that chezmoi edit --apply applies the edit. exec chezmoi edit --apply --force $HOME${/}.file grep -count=2 '# edited' $CHEZMOISOURCEDIR/dot_file grep -count=2 '# edited' $HOME/.file # test that chezmoi edit edits a symlink exec chezmoi edit $HOME${/}.symlink grep -count=1 '# edited' $CHEZMOISOURCEDIR/symlink_dot_symlink # test that chezmoi edit edits a script exec chezmoi edit $HOME${/}script.sh grep -count=1 '# edited' $CHEZMOISOURCEDIR/run_script.sh # test that chezmoi edit edits a file and a symlink exec chezmoi edit $HOME${/}.file $HOME${/}.symlink grep -count=3 '# edited' $CHEZMOISOURCEDIR/dot_file grep -count=2 '# edited' $CHEZMOISOURCEDIR/symlink_dot_symlink # test that chezmoi edit edits the working tree exec chezmoi edit exists $CHEZMOISOURCEDIR/.edited # test that chezmoi edit edits a directory [unix] exec chezmoi edit $HOME${/}.dir [unix] exists $CHEZMOISOURCEDIR/dot_dir/.edited chhome home2/user # test that chezmoi edit edits a file when the working tree and the source dir are different exec chezmoi edit $HOME${/}.file grep -count=1 '# edited' $CHEZMOISOURCEDIR/home/dot_file # test that chezmoi edit edits the working tree when working tree and the source dir are different exec chezmoi edit exists $CHEZMOISOURCEDIR/.edited ! exists $CHEZMOISOURCEDIR/home/.edited -- home/user/.local/share/chezmoi/run_script.sh -- #!/bin/sh -- home2/user/.config/chezmoi/chezmoi.toml -- sourceDir = "~/.local/share/chezmoi/home" -- home2/user/.local/share/chezmoi/.git/.keep -- -- home2/user/.local/share/chezmoi/home/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/editconfig.txtar ================================================ # test that edit-config creates a config file if needed exec chezmoi edit-config grep -count=1 '# edited' $CHEZMOICONFIGDIR/chezmoi.toml # test that edit-config edits an existing config file exec chezmoi edit-config grep -count=2 '# edited' $CHEZMOICONFIGDIR/chezmoi.toml chhome home2/user # test that edit-config edits an existing YAML config file exec chezmoi edit-config grep -count=1 '# edited' $CHEZMOICONFIGDIR/chezmoi.yaml ! stderr warning chhome home3/user # test that edit-config reports a warning if the config is no longer valid exec chezmoi edit-config stderr warning ! stderr 'returned in less than' grep -count=1 '# edited' $CHEZMOICONFIGDIR/chezmoi.json -- home2/user/.config/chezmoi/chezmoi.yaml -- data: email: "user@example.com" edit: minDuration: 0 -- home3/user/.config/chezmoi/chezmoi.json -- {"data":{"email":"user@example.com"},"edit":{"minDuration":"0"}} ================================================ FILE: internal/cmd/testdata/scripts/editconfigtemplate.txtar ================================================ [windows] unix2dos golden/edited [windows] unix2dos golden/edited-chezmoi.yaml [windows] unix2dos home3/user/.config/chezmoi/chezmoi.yaml [windows] unix2dos home4/user/.local/share/chezmoi/home/.chezmoi.yaml.tmpl # test that chezmoi edit-config-template creates a new config file template exec chezmoi edit-config-template cmp $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl golden/edited chhome home2/user # test that chezmoi edit-config-template creates a new config file template in .chezmoiroot exec chezmoi edit-config-template cmp $CHEZMOISOURCEDIR/home/.chezmoi.toml.tmpl golden/edited chhome home3/user # test that chezmoi edit-config-template creates a new config file template from an existing config file exec chezmoi edit-config-template cmp $CHEZMOISOURCEDIR/.chezmoi.yaml.tmpl golden/edited-chezmoi.yaml chhome home4/user # test that chezmoi edit-config-template edits an existing config file template exec chezmoi edit-config-template cmp $CHEZMOISOURCEDIR/home/.chezmoi.yaml.tmpl golden/edited-chezmoi.yaml -- golden/edited -- # edited -- golden/edited-chezmoi.yaml -- {} # edited -- home2/user/.local/share/chezmoi/.chezmoiroot -- home -- home3/user/.config/chezmoi/chezmoi.yaml -- {} -- home4/user/.local/share/chezmoi/.chezmoiroot -- home -- home4/user/.local/share/chezmoi/home/.chezmoi.yaml.tmpl -- {} ================================================ FILE: internal/cmd/testdata/scripts/editencrypted.txtar ================================================ mkageconfig [windows] unix2dos golden/edited-plaintext [windows] unix2dos golden/plaintext # test that chezmoi edit-encrypted edits encrypted files exec chezmoi encrypt -o $HOME/ciphertext golden/plaintext exec chezmoi edit-encrypted $HOME/ciphertext exec chezmoi decrypt $HOME/ciphertext cmp stdout golden/edited-plaintext -- golden/edited-plaintext -- # contents of plaintext # edited -- golden/plaintext -- # contents of plaintext ================================================ FILE: internal/cmd/testdata/scripts/edithardlink.txtar ================================================ [windows] skip 'Windows does not support hardlinks' # test that chezmoi edit uses a hardlink by default exec chezmoi edit $HOME${/}.file stdout /\.file$ # test that chezmoi edit --hardlink=false does not use a hardlink exec chezmoi edit --hardlink=false $HOME${/}.file stdout ${CHEZMOISOURCEDIR@R}/dot_file$ -- home/user/.config/chezmoi/chezmoi.toml -- [edit] command = "echo" -- home/user/.local/share/chezmoi/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/ejson.txtar ================================================ # test ejsonDecrypt uses default parameters ! exec chezmoi execute-template '{{ (ejsonDecrypt "golden/my-file.ejson") }}' stderr 'couldn''t read key file' stderr '/opt/ejson/keys/df82a403a3b58ebedd09758d3b131ff3113b39bdbfb92110940eb57832774345' # test ejsonDecrypt uses EJSON_KEYDIR when set env EJSON_KEYDIR=golden/keys exec chezmoi execute-template '{{ (ejsonDecrypt "golden/my-file.ejson").key1 }}' stdout ^value1$ # test ejsonDecrypt uses configuration's keyDir when set chhome home_set_valid_keydir/user env EJSON_KEYDIR=invalid/keys exec chezmoi execute-template '{{ (ejsonDecrypt "golden/my-file.ejson").key2 }}' stdout ^value2$ # test ejsonDecrypt uses configuration's key when set, and succeeds if valid chhome home_set_valid_key/user exec chezmoi execute-template '{{ (ejsonDecrypt "golden/my-file.ejson").key1 }}' stdout ^value1$ # test ejsonDecrypt uses configuration's key when set, and fails if invalid chhome home_set_invalid_key/user ! exec chezmoi execute-template '{{ (ejsonDecrypt "golden/my-file.ejson") }}' # test ejsonDecryptWithKey uses the key passed as parameter, and succeeds if valid chhome home_set_invalid_key/user exec chezmoi execute-template '{{ (ejsonDecryptWithKey "golden/my-file.ejson" "4fed3b88a33a4621b30230f1ad17e175e10f8587e37e84da740711c9fecfe16d").key2 }}' stdout ^value2$ # test ejsonDecryptWithKey uses the key passed as parameter, and fails if invalid chhome home_set_valid_key/user ! exec chezmoi execute-template '{{ (ejsonDecryptWithKey "golden/my-file.ejson" "invalid") }}' -- golden/keys/df82a403a3b58ebedd09758d3b131ff3113b39bdbfb92110940eb57832774345 -- 4fed3b88a33a4621b30230f1ad17e175e10f8587e37e84da740711c9fecfe16d -- golden/my-file.ejson -- { "_public_key": "df82a403a3b58ebedd09758d3b131ff3113b39bdbfb92110940eb57832774345", "key1": "EJ[1:t1Ql8sPo+fpQxHSxarJYDctfjXwfB9+OMH4BK/0CQEE=:9lajUfn0rbr/fbVYHi0yF/BH64htU4yF:8ydfFcJ7UO6rg7TGO2vqT19NBSk02Q==]", "key2": "EJ[1:t1Ql8sPo+fpQxHSxarJYDctfjXwfB9+OMH4BK/0CQEE=:vmOdZjp4gqY0pmjeVb/BQQaFzW17wK5f:7UtzdtHcrwvwZdjqm4Jmn9GHxFkR1Q==]" } -- home_set_invalid_key/user/.config/chezmoi/chezmoi.yaml -- ejson: key: "foo" -- home_set_valid_key/user/.config/chezmoi/chezmoi.yaml -- ejson: key: "4fed3b88a33a4621b30230f1ad17e175e10f8587e37e84da740711c9fecfe16d" -- home_set_valid_keydir/user/.config/chezmoi/chezmoi.yaml -- ejson: keyDir: "golden/keys" ================================================ FILE: internal/cmd/testdata/scripts/encryptiontemplatefuncs.txtar ================================================ [!exec:age] skip 'age not found in $PATH' mkageconfig # test encrypt template function exec chezmoi execute-template '{{ "plaintext" | encrypt }}' stdout '-----BEGIN AGE ENCRYPTED FILE-----' # test encrypt and decrypt template function round trip exec chezmoi execute-template '{{ "plaintext\n" | encrypt | decrypt }}' cmp stdout golden/plaintext [windows] stop 'remaining tests rely on UNIX path handling' # FIXME # test decrypt template function exec chezmoi encrypt --output=$HOME${/}ciphertext.age golden/plaintext exec chezmoi execute-template '{{ joinPath (env "HOME") "ciphertext.age" | include | decrypt }}' cmp stdout golden/plaintext -- golden/plaintext -- plaintext ================================================ FILE: internal/cmd/testdata/scripts/errors.txtar ================================================ mksourcedir # test duplicate source state entry detection cp $CHEZMOISOURCEDIR/dot_file $CHEZMOISOURCEDIR/empty_dot_file ! exec chezmoi verify stderr 'inconsistent state' chhome home2/user # test invalid config ! exec chezmoi verify stderr 'invalid config' chhome home3/user # test source directory is not a directory ! exec chezmoi verify stderr 'not a directory' chhome home4/user # test that chezmoi checks .chezmoiversion ! exec chezmoi verify stderr 'source state requires chezmoi version' chhome home5/user # test that chezmoi checks .chezmoiversion when .chezmoiroot is used ! exec chezmoi verify stderr 'source state requires chezmoi version' chhome home6/user # test duplicate script detection ! exec chezmoi verify stderr 'inconsistent state' chhome home7/user # test that chezmoi init returns an error if there are multiple config file templates ! exec chezmoi init stderr 'multiple config file templates' chhome home8/user # test that chezmoi verify returns an error if there are multiple config files ! exec chezmoi verify stderr 'multiple config files' -- home2/user/.config/chezmoi/chezmoi.json -- { -- home3/user/.local/share/chezmoi -- # contents of .local/share/chezmoi -- home4/user/.local/share/chezmoi/.chezmoiversion -- 3.0.0 -- home5/user/.local/share/chezmoi/.chezmoiroot -- home -- home5/user/.local/share/chezmoi/.chezmoiversion -- 3.0.0 -- home6/user/.local/share/chezmoi/run_install_packages -- # contents of install_packages -- home6/user/.local/share/chezmoi/run_once_install_packages -- # contents of install_packages -- home7/user/.local/share/chezmoi/.chezmoi.json.tmpl -- -- home7/user/.local/share/chezmoi/.chezmoi.toml.tmpl -- -- home8/user/.config/chezmoi/chezmoi.json -- -- home8/user/.config/chezmoi/chezmoi.toml -- -- home8/user/.local/share/chezmoi/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/exclude.txtar ================================================ [!umask:022] skip # test chezmoi diff --exclude exec chezmoi diff --exclude=scripts [unix] cmp stdout golden/diff-no-scripts.diff [windows] cmp stdout golden/diff-no-scripts-windows.diff # test that chezmoi diff respects the diff.exclude configuration variable exec chezmoi diff [unix] cmp stdout golden/diff.diff [windows] cmp stdout golden/diff-windows.diff mkdir $CHEZMOICONFIGDIR cp golden/chezmoi.toml $CHEZMOICONFIGDIR exec chezmoi diff [unix] cmp stdout golden/diff-no-scripts.diff [windows] cmp stdout golden/diff-no-scripts-windows.diff chhome home2/user # test that chezmoi diff --exclude=always excludes scripts that are always run exec chezmoi diff --exclude=always cmp stdout golden/diff-exclude-always.diff -- golden/chezmoi.toml -- [diff] exclude = ["scripts"] -- golden/diff-exclude-always.diff -- diff --git a/script-once.sh b/script-once.sh new file mode 100755 index 0000000000000000000000000000000000000000..d7dff6100cefc930f4161600c12b6f7ea37b7d3a --- /dev/null +++ b/script-once.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo once -- golden/diff-no-scripts-windows.diff -- diff --git a/.file b/.file new file mode 100666 index 0000000000000000000000000000000000000000..8a52cb9ce9551221716a53786ad74104c5902362 --- /dev/null +++ b/.file @@ -0,0 +1 @@ +# contents of .file -- golden/diff-no-scripts.diff -- diff --git a/.file b/.file new file mode 100644 index 0000000000000000000000000000000000000000..8a52cb9ce9551221716a53786ad74104c5902362 --- /dev/null +++ b/.file @@ -0,0 +1 @@ +# contents of .file -- golden/diff-windows.diff -- diff --git a/.file b/.file new file mode 100666 index 0000000000000000000000000000000000000000..8a52cb9ce9551221716a53786ad74104c5902362 --- /dev/null +++ b/.file @@ -0,0 +1 @@ +# contents of .file diff --git a/script.sh b/script.sh new file mode 100755 index 0000000000000000000000000000000000000000..3747a7ba08ee591c41b7c518e430d2802137eac4 --- /dev/null +++ b/script.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo $* -- golden/diff.diff -- diff --git a/.file b/.file new file mode 100644 index 0000000000000000000000000000000000000000..8a52cb9ce9551221716a53786ad74104c5902362 --- /dev/null +++ b/.file @@ -0,0 +1 @@ +# contents of .file diff --git a/script.sh b/script.sh new file mode 100755 index 0000000000000000000000000000000000000000..3747a7ba08ee591c41b7c518e430d2802137eac4 --- /dev/null +++ b/script.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo $* -- home/user/.local/share/chezmoi/dot_file -- # contents of .file -- home/user/.local/share/chezmoi/run_script.sh -- #!/bin/sh echo $* -- home2/user/.local/share/chezmoi/run_once_script-once.sh -- #!/bin/sh echo once -- home2/user/.local/share/chezmoi/run_script-always.sh -- #!/bin/sh echo always ================================================ FILE: internal/cmd/testdata/scripts/exectemplatefunc.txtar ================================================ [!exec:true] skip 'true not found in $PATH' [!exec:false] skip 'false not found in $PATH' # test that the exec template function returns true if the command succeeds exec chezmoi execute-template '{{ exec "true" }}' stdout ^true$ # test that the exec template function returns false if the command fails exec chezmoi execute-template '{{ exec "false" }}' stdout ^false$ # test that the exec template function returns fails if the command cannot be found ! exec chezmoi execute-template '{{ exec "non-existent-command" }}' ================================================ FILE: internal/cmd/testdata/scripts/executetemplate.txtar ================================================ # test reading args exec chezmoi execute-template '{{ "arg-template" }}' stdout arg-template # test reading from stdin stdin golden/stdin.tmpl exec chezmoi execute-template stdout stdin-template # test reading from stdin with an argument stdin golden/stdin exec chezmoi execute-template --with-stdin '{{ .chezmoi.stdin | upper }}' stdout '# CONTENTS OF STDIN' # test partial templates work exec chezmoi execute-template '{{ template "partial" }}' stdout 'hello world' # test that symlinks are supported in .chezmoitemplates symlink $CHEZMOISOURCEDIR/.chezmoitemplates/symlink -> partial exec chezmoi execute-template '{{ template "symlink" }}' stdout 'hello world' # FIXME merge the following tests into a single test exec chezmoi execute-template '{{ .last.config }}' stdout 'chezmoi\.toml' # test that template data are read from .chezmoidata.json exec chezmoi execute-template '{{ .last.json }}' stdout '\.chezmoidata\.json' # test that template data are read from .chezmoidata.toml exec chezmoi execute-template '{{ .last.toml }}' stdout '\.chezmoidata\.toml' # test that template data are read from .chezmoidata.yaml exec chezmoi execute-template '{{ .last.yaml }}' stdout '\.chezmoidata\.yaml' # test that the last .chezmoidata. file read wins exec chezmoi execute-template '{{ .last.format }}' stdout '\.chezmoidata\.yaml' # test that the config file wins over .chezmoidata. exec chezmoi execute-template '{{ .last.global }}' stdout chezmoi.toml # test that chezmoi execute-template --init does not include .chezmoidata. data ! exec chezmoi execute-template --init '{{ .last.format }}' stderr 'map has no entry for key "format"' # test --init --promptBool exec chezmoi execute-template --init --promptBool value=yes '{{ promptBool "value" }}' stdout true ! exec chezmoi execute-template --promptBool value=error stderr 'invalid syntax' # test --init --promptBool with a default value exec chezmoi execute-template --init '{{ promptBool "value" true }}' stdout true # test --init --promptChoice exec chezmoi execute-template --init --promptChoice value=one '{{ promptChoice "value" (list "one" "two" "three") }}' stdout one # test --init --promptChoice with an invalid value ! exec chezmoi execute-template --init --promptChoice value=four '{{ promptChoice "value" (list "one" "two" "three") }}' stderr 'invalid choice' # test --init --promptChoice with a default value exec chezmoi execute-template --init '{{ promptChoice "value" (list "one" "two" "three") "three" }}' stdout three # test --init --promptMultichoice exec chezmoi execute-template --init --promptMultichoice value=one/two '{{ promptMultichoice "value" (list "one" "two" "three") }}' stdout '[one two]' # test --init --promptMultichoice with an invalid value ! exec chezmoi execute-template --init --promptMultichoice value=four '{{ promptMultichoice "value" (list "one" "two" "three") }}' stderr 'invalid choice' # test --init --promptMultichoice with a default value exec chezmoi execute-template --init '{{ promptMultichoice "value" (list "one" "two" "three") (list "two" "three") }}' stdout '[two three]' # test --init --promptInt exec chezmoi execute-template --init --promptInt value=1 '{{ promptInt "value" }}' stdout 1 ! exec chezmoi execute-template --promptInt value=error stderr 'invalid syntax' # test --init --promptInt with a default value exec chezmoi execute-template --init '{{ promptInt "value" 1 }}' stdout 1 # test --init --promptString exec chezmoi execute-template --init --promptString email=user@example.com '{{ promptString "email" }}' stdout 'user@example.com' # test --init --promptString without a default value exec chezmoi execute-template --init '{{ promptString "value" }}' stdout value # test --init --promptString with a default value exec chezmoi execute-template --init '{{ promptString "value" "default" }}' stdout default # test that chezmoi execute-template reads all files in the .chezmoidata subdirectory exec chezmoi execute-template '{{ .a }}{{ .b }}' stdout 'alphabeta' # test chezmoi execute-template --file exec chezmoi execute-template --file $HOME${/}file.tmpl stdout '# contents of file.tmpl' # test that .chezmoi.sourceFile is a relative path in the source dir exec chezmoi execute-template --file $HOME${/}.local${/}share${/}chezmoi${/}path${/}to${/}template.tmpl stdout '# contents of path/to/template.tmpl' # test that .chezmoi.sourceFile is an absolute path outside the source dir exec chezmoi execute-template --file $HOME${/}path${/}to${/}template.tmpl stdout '# contents of .*/home/user/path/to/template.tmpl' chhome home2/user # test that files in .chezmoidata must have known extensions ! exec chezmoi execute-template stderr 'unknown format' chhome home3/user # test that .chezmoiignore files are not allowed in .chezmoidata ! exec chezmoi execute-template stderr 'not allowed in .chezmoidata directory' -- golden/stdin -- # contents of stdin -- golden/stdin.tmpl -- {{ "stdin-template" }} -- home/user/.config/chezmoi/chezmoi.toml -- [data.last] config = "chezmoi.toml" global = "chezmoi.toml" -- home/user/.local/share/chezmoi/.chezmoidata/a.json -- {"a":"alpha"} -- home/user/.local/share/chezmoi/.chezmoidata/dir/b.yaml -- b: beta -- home/user/.local/share/chezmoi/.chezmoidata.json -- { "last": { "format": ".chezmoidata.json", "global": ".chezmoidata.json", "json": ".chezmoidata.json" } } -- home/user/.local/share/chezmoi/.chezmoidata.toml -- [last] format = ".chezmoidata.toml" global = ".chezmoidata.toml" toml = ".chezmoidata.toml" -- home/user/.local/share/chezmoi/.chezmoidata.yaml -- last: format: ".chezmoidata.yaml" global: ".chezmoidata.yaml" yaml: ".chezmoidata.yaml" -- home/user/.local/share/chezmoi/.chezmoitemplates/.ignore -- {{ "invalid template" -- home/user/.local/share/chezmoi/.chezmoitemplates/partial -- {{ cat "hello" "world" }} -- home/user/.local/share/chezmoi/path/to/template.tmpl -- # contents of {{ .chezmoi.sourceFile }} -- home/user/file.tmpl -- # contents of file.tmpl -- home/user/path/to/template.tmpl -- # contents of {{ .chezmoi.sourceFile }} -- home2/user/.local/share/chezmoi/.chezmoidata/unknown -- -- home3/user/.local/share/chezmoi/.chezmoidata/.chezmoiignore -- ================================================ FILE: internal/cmd/testdata/scripts/external.txtar ================================================ symlink archive/dir/symlink -> file [darwin] exec xattr -w -s io.chezmoi.test metadata-test archive/dir archive/dir/symlink archive/dir/file exec tar czf www/archive.tar.gz archive # Force the collection of the Mac metadata for the home15/user test [darwin] exec tar czf www/archive-mac-metadata.tar.gz --mac-metadata archive httpd www # test that chezmoi diff includes external files by default exec chezmoi diff stdout '^diff --git a/\.file b/\.file$' # test that chezmoi diff --exclude=externals excludes diffs from external files exec chezmoi diff --exclude=externals ! stdout '^diff --git a/\.file b/\.file$' # test that chezmoi reads external files from .chezmoiexternal.toml exec chezmoi apply --force cmp $HOME/.file golden/.file [unix] cmpmod 666 $HOME/.file chhome home2/user # test that chezmoi reads executable external files from .chezmoiexternal.toml exec chezmoi apply --force cmp $HOME/.file golden/.file [unix] cmpmod 777 $HOME/.file chhome home3/user # test that chezmoi managed --include=externals lists external targets exec chezmoi managed --include=externals cmp stdout golden/managed # test that chezmoi diff includes external archives by default exec chezmoi diff stdout '^diff --git a/\.dir/dir/file b/\.dir/dir/file$' # test that chezmoi diff --exclude=externals excludes diffs from external archives exec chezmoi diff --exclude=externals ! stdout '^diff --git a/\.dir/dir/file b/\.dir/dir/file$' # test that chezmoi reads external archives from .chezmoiexternal.yaml exec chezmoi apply --force cmp $HOME/.dir/dir/file golden/dir/file [unix] readlink $HOME/.dir/dir/symlink file exists $HOME/.dir/file chhome home4/user # test that chezmoi reads exact external archives from .chezmoiexternal.yaml exec chezmoi apply --force cmp $HOME/.dir/dir/file golden/dir/file [unix] readlink $HOME/.dir/dir/symlink file ! exists $HOME/.dir/file chhome home5/user # test that chezmoi reads externals from subdirectories exec chezmoi apply --force cmp $HOME/.dir/subdir/dir/file golden/dir/file [unix] readlink $HOME/.dir/subdir/dir/symlink file chhome home6/user # test that .chezmoiignore applies to entries in externals exec chezmoi apply --force exists $HOME/.dir/dir/ exists $HOME/.dir/dir/file ! exists $HOME/.dir/dir/symlink chhome home7/user # test that .chezmoiignore applies to entire externals exec chezmoi apply --force chhome home8/user # test that parent directories are created if needed exec chezmoi apply --force cmp $HOME/.dir1/file golden/dir/file cmp $HOME/.dir2/dir2/file golden/dir/file cmp $HOME/.dir3/dir3/dir3/file golden/dir/file chhome home9/user # test that duplicate equivalent directories are allowed exec chezmoi apply --force chhome home10/user # test that checksums are verified exec chezmoi apply --force stderr '.file: warning: insecure MD5 checksum will be removed, use a secure hash like SHA256 instead' stderr '.file: warning: insecure RIPEMD-160 checksum will be removed, use a secure hash like SHA256 instead' stderr '.file: warning: insecure SHA1 checksum will be removed, use a secure hash like SHA256 instead' cp $HOME/.file golden/.file chhome home11/user # test that checksums detect corrupt files ! exec chezmoi apply --force stderr \.file:\sMD5\smismatch stderr 'SHA256 mismatch' chhome home12/user # test that chezmoi reads archive-file externals exec chezmoi apply cmp $HOME/.file golden/dir/file chhome home13/user # test that chezmoi can set executable bits on archive-file externals exec chezmoi apply [umask:002] cmpmod 775 $HOME/.file [umask:022] cmpmod 755 $HOME/.file chhome home14/user # test that chezmoi reads external files from the .chezmoiexternal directory exec chezmoi apply cmp $HOME/.file golden/dir/file [darwin] chhome home15/user # test that chezmoi managed --include=externals lists external targets including AppleDouble files [darwin] exec chezmoi managed --include=externals [darwin] cmp stdout golden/managed-appledouble chhome home16/user # test that readonly and private attributes are set [!windows] exec chezmoi apply [!windows] cmpmod 400 $HOME/.file -- archive/dir/file -- # contents of dir/file -- golden/.file -- # contents of .file -- golden/dir/file -- # contents of dir/file -- golden/managed -- .dir .dir/dir .dir/dir/file .dir/dir/symlink -- golden/managed-appledouble -- .dir .dir/._dir .dir/dir .dir/dir/._file .dir/dir/._symlink .dir/dir/file .dir/dir/symlink -- home/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".file"] type = "file" url = "{{ env "HTTPD_URL" }}/.file" -- home10/user/.local/share/chezmoi/.chezmoiexternal.yaml -- .file: type: file url: {{ env "HTTPD_URL" }}/.file checksum: size: 20 md5: "49fe9018f97349cdd0a0ac7b7f668b05" ripemd160: "2320636f6e74656e7473206f66202e66696c650a9c1185a5c5e9fc54612808977ee8f548b2258d31" sha1: "cb91d72dc73f6d984b33ac5745f1cf6f76745bd2" sha256: "634a4dd193c7b3b926d2e08026aa81a416fd41cec52854863b974af422495663" sha384: "f8545bb66433eb514727bbc61c4e4939c436d38079767f39f12b8803d6472ca1dfcd101675b20cd525f7e3d02c368b61" sha512: "a68814ec3d16e8bd28c9291bbc596f0282687c5ba5d1f4c26c4e427166666a03c11df1dab3577b4483142764c37d4887def77244c4a52cb9852a234fa8cb15ba" -- home11/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".file"] type = "file" url = "{{ env "HTTPD_URL" }}/.corrupt-file" checksum.md5 = "49fe9018f97349cdd0a0ac7b7f668b05" checksum.sha256 = "634a4dd193c7b3b926d2e08026aa81a416fd41cec52854863b974af422495663" -- home12/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".file"] type = "archive-file" url = "{{ env "HTTPD_URL" }}/archive.tar.gz" path = "dir/file" stripComponents = 1 -- home13/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".file"] type = "archive-file" url = "{{ env "HTTPD_URL" }}/archive.tar.gz" path = "dir/file" executable = true stripComponents = 1 -- home14/user/.local/share/chezmoi/.chezmoiexternals/external.toml -- [".file"] type = "archive-file" url = "{{ env "HTTPD_URL" }}/archive.tar.gz" path = "dir/file" stripComponents = 1 -- home15/user/.dir/file -- -- home15/user/.local/share/chezmoi/.chezmoiexternal.yaml -- .dir: type: archive url: {{ env "HTTPD_URL" }}/archive-mac-metadata.tar.gz archive: extractAppleDoubleFiles: true stripComponents: 1 -- home16/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".file"] type = "file" url = "{{ env "HTTPD_URL" }}/.file" private = true readonly = true -- home2/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".file"] type = "file" url = "{{ env "HTTPD_URL" }}/.file" executable = true -- home3/user/.dir/file -- -- home3/user/.local/share/chezmoi/.chezmoiexternal.yaml -- .dir: type: archive url: {{ env "HTTPD_URL" }}/archive.tar.gz stripComponents: 1 -- home4/user/.dir/file -- -- home4/user/.local/share/chezmoi/.chezmoiexternal.yaml -- .dir: type: archive url: {{ env "HTTPD_URL" }}/archive.tar.gz exact: true stripComponents: 1 -- home5/user/.local/share/chezmoi/dot_dir/.chezmoiexternal.yaml -- subdir: type: archive url: {{ env "HTTPD_URL" }}/archive.tar.gz exact: true stripComponents: 1 -- home6/user/.local/share/chezmoi/.chezmoiexternal.yaml -- .dir: type: archive url: {{ env "HTTPD_URL" }}/archive.tar.gz exact: true stripComponents: 1 -- home6/user/.local/share/chezmoi/.chezmoiignore -- .dir/dir/symlink -- home7/user/.local/share/chezmoi/.chezmoiexternal.yaml -- .dir: type: archive url: {{ env "HTTPD_URL" }}/non-existent-archive.tar.gz -- home7/user/.local/share/chezmoi/.chezmoiignore -- .dir -- home8/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".dir1"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive.tar.gz" stripComponents = 2 [".dir2/dir2"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive.tar.gz" stripComponents = 2 [".dir3/dir3/dir3"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive.tar.gz" stripComponents = 2 -- home9/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".dir"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive.tar.gz" stripComponents = 1 -- home9/user/.local/share/chezmoi/dot_dir/file2 -- # contents of .dir/file2 -- www/.corrupt-file -- # corrupt contents of .file -- www/.file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/externalarchiveinclude.txtar ================================================ symlink archive/symlink1 -> file1 symlink archive/symlink2 -> file2 mkdir www exec tar czf www/archive.tar.gz archive httpd www # test that chezmoi includes all files by default exec chezmoi managed cmp stdout golden/managed chhome home2/user # test that chezmoi can include only certain files by default exec chezmoi managed cmp stdout golden/managed2 chhome home3/user # test that chezmoi can exclude only certain files by default exec chezmoi managed cmp stdout golden/managed3 chhome home4/user # test that chezmoi can include and exclude files exec chezmoi managed cmp stdout golden/managed4 chhome home5/user # test that chezmoi can include selected files in sub-directories exec chezmoi managed cmp stdout golden/managed5 -- archive/dir/subdir1/file1 -- # contents of dir/subdir1/file1 -- archive/dir/subdir1/file2 -- # contents of dir/subdir1/file2 -- archive/dir/subdir2/file1 -- # contents of dir/subdir2/file1 -- archive/dir/subdir2/file2 -- # contents of dir/subdir2/file2 -- archive/file1 -- # contents of file1 -- archive/file2 -- # contents of file2 -- golden/managed -- .dir .dir/dir .dir/dir/subdir1 .dir/dir/subdir1/file1 .dir/dir/subdir1/file2 .dir/dir/subdir2 .dir/dir/subdir2/file1 .dir/dir/subdir2/file2 .dir/file1 .dir/file2 .dir/symlink1 .dir/symlink2 -- golden/managed2 -- .dir .dir/dir .dir/dir/subdir1 .dir/dir/subdir1/file1 .dir/dir/subdir1/file2 -- golden/managed3 -- .dir .dir/dir .dir/dir/subdir1 .dir/dir/subdir1/file1 .dir/file1 .dir/symlink1 -- golden/managed4 -- .dir .dir/dir .dir/dir/subdir1 .dir/dir/subdir1/file1 .dir/dir/subdir2 .dir/dir/subdir2/file1 .dir/file1 .dir/symlink1 .dir/symlink2 -- golden/managed5 -- .dir .dir/dir/subdir2/file1 .dir/dir/subdir2/file2 -- home/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".dir"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive.tar.gz" stripComponents = 1 -- home2/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".dir"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive.tar.gz" stripComponents = 1 include = ["*", "*/dir", "*/dir/subdir1", "*/dir/subdir1/**"] -- home3/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".dir"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive.tar.gz" stripComponents = 1 exclude = ["**/*2"] -- home4/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".dir"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive.tar.gz" stripComponents = 1 include = ["*/dir", "*/dir/**"] exclude = ["**/file2"] -- home5/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".dir"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive.tar.gz" stripComponents = 1 include = ["*/dir/subdir2/*"] ================================================ FILE: internal/cmd/testdata/scripts/externalcompression.txtar ================================================ [exec:bzip2] exec bzip2 www/file-bzip2 [exec:gzip] exec gzip www/file-gzip [exec:xz] exec xz www/file-xz [exec:zstd] exec zstd www/file-zstd httpd www # test that chezmoi apply decompresses files in multiple formats exec chezmoi apply [exec:bzip2] cmp $HOME/file-bzip2 golden/file-bzip2 [exec:gzip] cmp $HOME/file-gzip golden/file-gzip [exec:xz] cmp $HOME/file-xz golden/file-xz [exec:zstd] cmp $HOME/file-zstd golden/file-zstd -- golden/file-bzip2 -- # contents of file-bzip2 -- golden/file-gzip -- # contents of file-gzip -- golden/file-xz -- # contents of file-xz -- golden/file-zstd -- # contents of file-zstd -- home/user/.local/share/chezmoi/.chezmoiexternal.toml.tmpl -- {{ if lookPath "bzip2" }} [file-bzip2] type = "file" url = "{{ env "HTTPD_URL" }}/file-bzip2.bz2" decompress = "bzip2" {{ end }} {{ if lookPath "gzip" }} [file-gzip] type = "file" url = "{{ env "HTTPD_URL" }}/file-gzip.gz" decompress = "gzip" {{ end }} {{ if lookPath "xz" }} [file-xz] type = "file" url = "{{ env "HTTPD_URL" }}/file-xz.xz" decompress = "xz" {{ end }} {{ if lookPath "zstd" }} [file-zstd] type = "file" url = "{{ env "HTTPD_URL" }}/file-zstd.zst" decompress = "zstd" {{ end }} -- www/file-bzip2 -- # contents of file-bzip2 -- www/file-gzip -- # contents of file-gzip -- www/file-xz -- # contents of file-xz -- www/file-zstd -- # contents of file-zstd ================================================ FILE: internal/cmd/testdata/scripts/externaldiff.txtar ================================================ [windows] skip 'UNIX only' chmod 755 bin/external-diff # test that chezmoi diff invokes the external diff command for scripts exec chezmoi diff stdout '# contents of script' # test that chezmoi diff excludes scripts if configured appendline ${CHEZMOICONFIGDIR}/chezmoi.toml ' exclude = ["scripts"]' exec chezmoi diff ! stdout '# contents of script' chhome home2/user # test that chezmoi diff does not pass non-existent filenames to external diff command exec chezmoi diff chhome home3/user # test that chezmoi diff passes /dev/null and directory names when creating directories exec chezmoi diff stdout ^/dev/null\s${WORK@R}/.*/\.dir$ chhome home4/user # test that chezmoi diff uses textconv when an external diff tool is used exec chezmoi diff cmp stdout golden/external-diff -- bin/external-diff -- #!/bin/sh echo old/$(basename $1): cat $1 echo echo new/$(basename $2): cat $2 -- golden/external-diff -- old/file.txt: # OLD CONTENTS OF .DIR/FILE.TXT new/file.txt: # NEW CONTENTS OF .DIR/FILE.TXT -- home/user/.config/chezmoi/chezmoi.toml -- [diff] command = "cat" -- home/user/.local/share/chezmoi/run_script -- # contents of script -- home2/user/.config/chezmoi/chezmoi.yaml -- diff: command: cat -- home2/user/.local/share/chezmoi/dot_file -- # contents of .file -- home3/user/.config/chezmoi/chezmoi.toml -- [diff] command = "echo" -- home3/user/.local/share/chezmoi/dot_dir/.keep -- -- home4/user/.config/chezmoi/chezmoi.yaml -- diff: command: external-diff textconv: - pattern: '**/*.txt' command: tr args: - a-z - A-Z -- home4/user/.dir/file.txt -- # old contents of .dir/file.txt -- home4/user/.local/share/chezmoi/dot_dir/file.txt -- # new contents of .dir/file.txt ================================================ FILE: internal/cmd/testdata/scripts/externaldir.txtar ================================================ symlink $CHEZMOISOURCEDIR/dot_vim/external_bundle/symlink -> executable_file # test that chezmoi ignores attributes in external_ dirs exec chezmoi apply # test that executable_ attributes are ignored and empty files are created cmp $HOME/.vim/bundle/executable_file $CHEZMOISOURCEDIR/dot_vim/external_bundle/executable_file # test that scripts are not run cmp $HOME/.vim/bundle/run_script.sh $CHEZMOISOURCEDIR/dot_vim/external_bundle/run_script.sh ! stdout evil # test that private_ attributes are ignored and subdirectories are created isdir $HOME/.vim/bundle/private_subdir # test that files can be ignored ! exists $HOME/.vim/bundle/private_subdir/.keep # test that symlinks are created issymlink $HOME/.vim/bundle/symlink # test that symlink_ attributes are ignored cmp $HOME/.vim/bundle/symlink_example $CHEZMOISOURCEDIR/dot_vim/external_bundle/symlink_example ! issymlink $HOME/.vim/bundle/symlink_example -- home/user/.local/share/chezmoi/.chezmoiignore -- **/.keep -- home/user/.local/share/chezmoi/dot_vim/external_bundle/executable_file -- -- home/user/.local/share/chezmoi/dot_vim/external_bundle/private_subdir/.keep -- -- home/user/.local/share/chezmoi/dot_vim/external_bundle/run_script.sh -- #!/bin/sh echo evil -- home/user/.local/share/chezmoi/dot_vim/external_bundle/symlink_example -- # contents of .vim/bundle/symlink_example ================================================ FILE: internal/cmd/testdata/scripts/externalencrypted.txtar ================================================ [windows] skip 'skipping gpg tests on Windows' [!exec:gpg] skip 'gpg not found in $PATH' mkgpgconfig # use chezmoi's encryption to encrypt a file and an archive exec tar czf $HOME/archive.tar.gz archive exec chezmoi add --encrypt $HOME${/}.file $HOME${/}archive.tar.gz mkdir www cp $CHEZMOISOURCEDIR/encrypted_dot_file.asc www/.file.asc cp $CHEZMOISOURCEDIR/encrypted_archive.tar.gz.asc www/archive.tar.gz.asc httpd www chhome home2/user # test that chezmoi reads encrypted external files and archives mkdir $CHEZMOICONFIGDIR cp home/user/.config/chezmoi/chezmoi.toml $CHEZMOICONFIGDIR exec chezmoi apply --force cmp $HOME/.file golden/.file cmp $HOME/.dir/file golden/dir/file -- archive/dir/file -- # contents of dir/file -- golden/.file -- # contents of .file -- golden/dir/file -- # contents of dir/file -- home/user/.file -- # contents of .file -- home2/user/.local/share/chezmoi/.chezmoiexternal.json -- { ".file": { "type": "file", "url": "{{ env "HTTPD_URL" }}/.file.asc", "encrypted": true }, ".dir": { "type": "archive", "url": "{{ env "HTTPD_URL" }}/archive.tar.gz.asc", "encrypted": true, "stripComponents": 2 } } ================================================ FILE: internal/cmd/testdata/scripts/externalfileurl.txtar ================================================ # test that chezmoi apply reads from file:// URLs exec chezmoi apply cmp $HOME/.file golden/.file chhome home2/user # test that chezmoi apply reads from the first found URL exec chezmoi apply cmp $HOME/.file golden/.file2 chhome home3/user # test that chezmoi apply returns an error from the first URL if all URLs return errors ! exec chezmoi apply [!windows] stderr 'home3/user/\.local/share/file.txt: no such file or directory' [windows] stderr 'home3/user/\.local/share/file.txt: The system cannot find the file specified\.' -- golden/.file -- # contents of .file -- golden/.file2 -- # contents of .file2 -- home/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".file"] type = "file" url = "file://{{ .chezmoi.homeDir }}/.local/share/file.txt" -- home/user/.local/share/file.txt -- # contents of .file -- home2/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".file"] type = "file" urls = ["file://{{ .chezmoi.homeDir }}/.local/share/file1.txt", "file://{{ .chezmoi.homeDir }}/.local/share/file2.txt"] -- home2/user/.local/share/file2.txt -- # contents of .file2 -- home3/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".file"] type = "file" url = "file://{{ .chezmoi.homeDir }}/.local/share/file.txt" urls = ["file://{{ .chezmoi.homeDir }}/.local/share/file1.txt", "file://{{ .chezmoi.homeDir }}/.local/share/file2.txt"] ================================================ FILE: internal/cmd/testdata/scripts/externalfilter.txtar ================================================ [!exec:base64] skip 'base64 not found in $PATH' httpd www # test that chezmoi filters external files exec chezmoi cat $HOME${/}.file cmp stdout golden/.file -- golden/.file -- # contents of .file -- home/user/.local/share/chezmoi/.chezmoiexternal.yaml -- .file: type: file url: "{{ env "HTTPD_URL" }}/file.base64" filter: command: base64 args: ["-d"] -- www/file.base64 -- IyBjb250ZW50cyBvZiAuZmlsZQo= ================================================ FILE: internal/cmd/testdata/scripts/externalgitrepo.txtar ================================================ [windows] skip 'UNIX only' [!exec:git] skip 'git not found in $PATH' mkgitconfig expandenv $WORK/home/user/.local/share/chezmoi/.chezmoiexternal.toml # test that chezmoi managed lists the directory exec chezmoi managed stdout ^\.dir$ # create a git repo cd $WORK/repo exec git init exec git add . exec git commit --message 'initial commit' cd $WORK # test that chezmoi apply clones the git repo exec chezmoi apply cmp $HOME/.dir/.file golden/.file # test that chezmoi archive --format=tar creates an archive exec chezmoi archive --format=tar # test that chezmoi archive --format=zip creates an archive exec chezmoi archive --format=zip chhome home2/user mkgitconfig expandenv $WORK/home2/user/.local/share/chezmoi/.chezmoiexternal.toml # test that chezmoi apply clones the git repo exec chezmoi apply cmp $HOME/.dir/.file golden/.file # update the git repo cd $WORK/repo edit $WORK/repo/.file exec git commit --message 'edit .file' . cd $WORK chhome home/user # test that chezmoi apply does not pull from the git repo when refreshPeriod is zero exec chezmoi apply ! grep '# edited' $HOME/.dir/.file # test that chezmoi apply --refresh-externals does pull from the git repo exec chezmoi apply --refresh-externals grep '# edited' $HOME/.dir/.file chhome home2/user # test that chezmoi apply does not pull from the git repo within the refresh period exec chezmoi apply ! grep '# edited' $HOME/.dir/.file # test that chezmoi dump prints the git command exec chezmoi dump --format=yaml stdout 'type: command' -- golden/.file -- # contents of .file -- home/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".dir"] type = "git-repo" url = "file://$WORK/repo" -- home2/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".dir"] type = "git-repo" url = "file://$WORK/repo" refreshPeriod = "1h" -- repo/.file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/externalguess.txtar ================================================ [windows] skip 'UNIX only' httpd www # test that chezmoi sniffs the format of tar files exec tar -cf www/archive.tar archive/ cp www/archive.tar www/archive exec chezmoi apply --force --refresh-externals cmp $HOME/.dir/dir/file golden/dir/file # test that chezmoi sniffs the format of tar.bz2 files [exec:bzip2] exec tar -cjf www/archive.tar.bz2 archive/ [exec:bzip2] cp www/archive.tar.bz2 www/archive [exec:bzip2] exec chezmoi apply --force --refresh-externals [exec:bzip2] cmp $HOME/.dir/dir/file golden/dir/file # test that chezmoi sniffs the format of tar.gz files exec tar -czf www/archive.tar.gz archive/ cp www/archive.tar.gz www/archive exec chezmoi apply --force --refresh-externals cmp $HOME/.dir/dir/file golden/dir/file # test that chezmoi sniffs the format of zip files [exec:zip] exec zip -r www/archive.zip archive [exec:zip] cp www/archive.zip www/archive [exec:zip] exec chezmoi apply --force --refresh-externals [exec:zip] cmp $HOME/.dir/dir/file golden/dir/file chhome home2/user # test that chezmoi allows the format to be overridden [exec:zip] cp www/archive.zip www/archive2.tar.gz [exec:zip] exec chezmoi apply --force [exec:zip] cmp $HOME/.dir/dir/file golden/dir/file -- archive/dir/file -- # contents of dir/file -- golden/dir/file -- # contents of dir/file -- home/user/.local/share/chezmoi/.chezmoiexternal.yaml -- .dir: type: archive url: "{{ env "HTTPD_URL" }}/archive" stripComponents: 1 -- home2/user/.local/share/chezmoi/.chezmoiexternal.yaml -- .dir: type: archive url: "{{ env "HTTPD_URL" }}/archive2.tar.gz" stripComponents: 1 format: zip -- www/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/externalrar.txtar ================================================ hexdecode www/testfile.rar5.rar.hex httpd www # test that chezmoi unpacks .rar externals exec chezmoi apply cmp $HOME/testfile.txt golden/testfile.txt -- golden/testfile.txt -- Testing 123 -- home/user/.local/share/chezmoi/.chezmoiexternal.toml -- ["testfile.txt"] type = "archive-file" url = "{{ env "HTTPD_URL" }}/testfile.rar5.rar" path = "testfile.txt" -- www/testfile.rar5.rar.hex -- 526172211a0701003392b5e50a010506000501018080002499eca12202028c00068c00b68302d00e 503afe8fc16e8000010c7465737466696c652e74787454657374696e67203132330a1d7756510305 0400 ================================================ FILE: internal/cmd/testdata/scripts/externalzip.txtar ================================================ [windows] skip 'zip may not support the --symlinks option' [!exec:zip] skip 'zip not found in $PATH' symlink archive/dir/symlink -> file exec zip -r --symlinks www/archive.zip archive httpd www # test that chezmoi reads external zip archives from .chezmoiexternal.json exec chezmoi apply --force cmp $HOME/.dir/dir/file golden/dir/file issymlink $HOME/.dir/dir/symlink -- archive/dir/file -- # contents of dir/file -- golden/dir/file -- # contents of dir/file -- home/user/.local/share/chezmoi/.chezmoiexternal.json -- { ".dir": { "type": "archive", "url": "{{ env "HTTPD_URL" }}/archive.zip", "stripComponents": 1 } } -- www/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/forget.txtar ================================================ mksourcedir # test that chezmoi apply sets the state exec chezmoi apply --force exists $CHEZMOISOURCEDIR/dot_file exec chezmoi state get --bucket=entryState --key=$WORK/home/user/.dir [umask:002] cmp stdout golden/state-get-dir-umask-002.json [umask:022] cmp stdout golden/state-get-dir-umask-022.json exec chezmoi state get --bucket=entryState --key=$WORK/home/user/.file [umask:002] cmp stdout golden/state-get-file-umask-002.json [umask:022] cmp stdout golden/state-get-file-umask-022.json # test that chezmoi forget forgets a dir exists $CHEZMOISOURCEDIR/dot_dir exec chezmoi forget --force $HOME${/}.dir ! exists $CHEZMOISOURCEDIR/dot_dir exec chezmoi state get --bucket=entryState --key=$WORK/home/user/.dir ! stdout . # test that chezmoi forget forgets a file exec chezmoi forget --force $HOME${/}.file ! exists $CHEZMOISOURCEDIR/dot_file exec chezmoi state get --bucket=entryState --key=$WORK/home/user/.file ! stdout . chhome home2/user # test that chezmoi forget forgets a file when .chezmoiroot is used exec chezmoi forget --force $HOME${/}.file ! exists $CHEZMOISOURCEDIR/home/dot_file -- golden/state-get-dir-umask-002.json -- { "type": "dir", "mode": 2147484157 } -- golden/state-get-dir-umask-022.json -- { "type": "dir", "mode": 2147484141 } -- golden/state-get-file-umask-002.json -- { "type": "file", "mode": 436, "contentsSHA256": "634a4dd193c7b3b926d2e08026aa81a416fd41cec52854863b974af422495663" } -- golden/state-get-file-umask-022.json -- { "type": "file", "mode": 420, "contentsSHA256": "634a4dd193c7b3b926d2e08026aa81a416fd41cec52854863b974af422495663" } -- home2/user/.local/share/chezmoi/.chezmoiroot -- home -- home2/user/.local/share/chezmoi/home/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/generate.txtar ================================================ mkgitconfig # test that chezmoi generate install.sh generates a shell script exec chezmoi generate install.sh stdout '#!/bin/sh' [!exec:git] skip 'git not found in $PATH' # test that chezmoi generate git-commit-message generates a git commit message exec git --git-dir=$CHEZMOISOURCEDIR/.git init exec chezmoi git add . exec chezmoi generate git-commit-message stdout '^Add .file$' -- home/user/.local/share/chezmoi/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/git.txtar ================================================ [unix] chmod 755 bin/git [windows] unix2dos bin/git.cmd exec chezmoi git hello exists $CHEZMOISOURCEDIR stdout hello -- bin/git -- #!/bin/sh echo $* -- bin/git.cmd -- @echo off setlocal set out=%* set out=%out:\=% echo %out% endlocal ================================================ FILE: internal/cmd/testdata/scripts/githubtemplatefuncs.txtar ================================================ [!env:CHEZMOI_GITHUB_TOKEN] skip '$CHEZMOI_GITHUB_TOKEN not set' # test gitHubKeys template function exec chezmoi execute-template '{{ (index (gitHubKeys "twpayne") 0).Key }}' stdout ^ssh-rsa # test gitHubLatestRelease template function exec chezmoi execute-template '{{ (gitHubLatestRelease "twpayne/chezmoi").TagName }}' stdout ^v2\. # test gitHubLatestTag template function exec chezmoi execute-template '{{ (gitHubLatestTag "twpayne/chezmoi").Name }}' stdout ^v2\. # test gitHubTags template functions exec chezmoi execute-template '{{ (index (gitHubTags "twpayne/chezmoi") 0).Name }}' stdout ^v2\. # test gitHubRelease template function exec chezmoi execute-template '{{ (gitHubRelease "twpayne/chezmoi" "v2.49.0").TagName }}' stdout ^v2\.49\.0 # test gitHubReleaseAssetURL template function [!windows] exec chezmoi execute-template '{{ gitHubReleaseAssetURL "twpayne/chezmoi" "v2.50.0" (printf "chezmoi-%s-%s" .chezmoi.os .chezmoi.arch) }}' [!windows] stdout https://github.com/twpayne/chezmoi/releases/download/v2\.50\.0/chezmoi- # test gitHubReleases template functions exec chezmoi execute-template '{{ (index (gitHubReleases "twpayne/chezmoi") 0).TagName }}' stdout ^v2\. ================================================ FILE: internal/cmd/testdata/scripts/gitleaks.txtar ================================================ # test that there is no output when adding a complex file exec chezmoi add $HOME${/}.karabiner.json ! stderr . ! stdout . -- home/user/.karabiner.json -- { "profiles": [ { "complex_modifications": { "rules": [ { "manipulators": [ { "description": "Change caps_lock to command+control+option+shift.", "from": { "key_code": "caps_lock", "modifiers": { "optional": ["any"] } }, "to": [ { "key_code": "left_shift", "modifiers": ["left_command", "left_control", "left_option"] } ], "type": "basic" } ] } ] }, "devices": [ { "identifiers": { "is_keyboard": true, "product_id": 25903, "vendor_id": 3141 }, "manipulate_caps_lock_led": false, "simple_modifications": [ { "from": { "key_code": "left_command" }, "to": [{ "key_code": "left_option" }] }, { "from": { "key_code": "left_option" }, "to": [{ "key_code": "left_command" }] } ] } ], "fn_function_keys": [ { "from": { "key_code": "f3" }, "to": [{ "key_code": "mission_control" }] }, { "from": { "key_code": "f4" }, "to": [{ "key_code": "launchpad" }] }, { "from": { "key_code": "f5" }, "to": [{ "key_code": "illumination_decrement" }] }, { "from": { "key_code": "f6" }, "to": [{ "key_code": "illumination_increment" }] }, { "from": { "key_code": "f9" }, "to": [{ "consumer_key_code": "fastforward" }] } ], "name": "Default profile", "selected": true, "virtual_hid_keyboard": { "country_code": 0, "keyboard_type_v2": "ansi" } } ] } ================================================ FILE: internal/cmd/testdata/scripts/gopass.txtar ================================================ mockcommand bin/gopass [windows] unix2dos golden/gopass-raw # test gopass template function exec chezmoi execute-template '{{ gopass "misc/example.com" }}' stdout ^examplepassword$ # test gopass template function exec chezmoi execute-template '{{ gopassRaw "misc/example.com" }}' cmp stdout golden/gopass-raw -- bin/gopass.yaml -- responses: - args: '--version' response: 'gopass 1.10.1 go1.15 linux amd64' - args: 'show --noparsing misc/example.com' response: | Secret: misc/example.com examplepassword key: value - args: 'show --password misc/example.com' response: 'examplepassword' suppressLastNewline: true default: response: 'gopass: invalid command: $*' exitCode: 1 -- golden/gopass-raw -- Secret: misc/example.com examplepassword key: value ================================================ FILE: internal/cmd/testdata/scripts/gpg.txtar ================================================ [windows] skip 'skipping gpg tests on Windows' [!exec:gpg] skip 'gpg not found in $PATH' mkhomedir mkgpgconfig # test that chezmoi add --encrypt encrypts cp golden/.encrypted $HOME exec chezmoi add --encrypt $HOME${/}.encrypted exists $CHEZMOISOURCEDIR/encrypted_dot_encrypted.asc grep '-----BEGIN PGP MESSAGE-----' $CHEZMOISOURCEDIR/encrypted_dot_encrypted.asc # test that chezmoi apply decrypts rm $HOME/.encrypted exec chezmoi apply --force cmp golden/.encrypted $HOME/.encrypted # test that chezmoi edit --apply transparently decrypts and re-encrypts exec chezmoi edit --apply --force $HOME${/}.encrypted grep '# edited' $HOME/.encrypted -- golden/.encrypted -- # contents of .encrypted ================================================ FILE: internal/cmd/testdata/scripts/gpgencryption.txtar ================================================ [windows] skip 'skipping gpg tests on Windows' [!exec:gpg] skip 'gpg not found in $PATH' mkhomedir mkgpgconfig # test that chezmoi add --encrypt encrypts cp golden/.encrypted $HOME exec chezmoi add --encrypt $HOME${/}.encrypted exists $CHEZMOISOURCEDIR/encrypted_dot_encrypted.asc grep '-----BEGIN PGP MESSAGE-----' $CHEZMOISOURCEDIR/encrypted_dot_encrypted.asc # test that chezmoi apply decrypts rm $HOME/.encrypted exec chezmoi apply --force cmp golden/.encrypted $HOME/.encrypted # test that chezmoi apply --exclude=encrypted does not apply encrypted files rm $HOME/.encrypted exec chezmoi apply --exclude=encrypted --force ! exists $HOME/.encrypted exec chezmoi apply --force cmp $HOME/.encrypted golden/.encrypted # test that chezmoi detects gpg encryption if gpg is configured but encryption = "gpg" is not set removeline $CHEZMOICONFIGDIR/chezmoi.toml 'encryption = "gpg"' exec chezmoi cat $HOME${/}.encrypted cmp stdout golden/.encrypted stderr 'chezmoi: warning: ''encryption'' not set, using gpg configuration. Check if ''encryption'' is correctly set as the top-level key.' prependline $CHEZMOICONFIGDIR/chezmoi.toml 'encryption = "gpg"' # test that chezmoi decrypt decrypts stdin stdin $CHEZMOISOURCEDIR${/}encrypted_dot_encrypted.asc exec chezmoi decrypt cmp stdout golden/.encrypted # test that chezmoi decrypt decrypts a file exec chezmoi decrypt $CHEZMOISOURCEDIR${/}encrypted_dot_encrypted.asc cmp stdout golden/.encrypted # test chezmoi encrypt/chezmoi decrypt round trip exec chezmoi encrypt golden/.encrypted stdout '-----BEGIN PGP MESSAGE-----' stdin stdout exec chezmoi decrypt cmp stdout golden/.encrypted # test that chezmoi edit --apply transparently decrypts and re-encrypts exec chezmoi edit --apply --force $HOME${/}.encrypted grep '# edited' $HOME/.encrypted # test that chezmoi files in subdirectories can be encrypted and that suffix can be set appendline $CHEZMOICONFIGDIR/chezmoi.toml ' suffix = ".gpg"' mkdir $HOME/.dir cp golden/.encrypted $HOME/.dir exec chezmoi add --encrypt $HOME${/}.dir${/}.encrypted grep '-----BEGIN PGP MESSAGE-----' $CHEZMOISOURCEDIR/dot_dir/encrypted_dot_encrypted.gpg exec chezmoi edit --apply $HOME${/}.dir${/}.encrypted grep '# edited' $HOME/.dir/.encrypted # test that chezmoi edit strips the encrypted suffix [unix] env EDITOR=echo [windows] env EDITOR=printargs exec chezmoi edit $HOME${/}.dir${/}.encrypted stdout '\.dir/\.encrypted\r?$' -- bin/printargs.cmd -- @echo off setlocal set out=%* set out=%out:\=% echo %out% endlocal -- golden/.encrypted -- # contents of .encrypted ================================================ FILE: internal/cmd/testdata/scripts/gpgencryptionsymmetric.txtar ================================================ [windows] skip 'skipping gpg tests on Windows' [!exec:gpg] skip 'gpg not found in $PATH' mkhomedir mkgpgconfig -symmetric # test that chezmoi add --encrypt encrypts cp golden/.encrypted $HOME exec chezmoi add --encrypt $HOME${/}.encrypted exists $CHEZMOISOURCEDIR/encrypted_dot_encrypted.asc grep '-----BEGIN PGP MESSAGE-----' $CHEZMOISOURCEDIR/encrypted_dot_encrypted.asc # test that chezmoi apply decrypts rm $HOME/.encrypted exec chezmoi apply --force cmp golden/.encrypted $HOME/.encrypted # test that chezmoi apply --exclude=encrypted does not apply encrypted files rm $HOME/.encrypted exec chezmoi apply --exclude=encrypted --force ! exists $HOME/.encrypted exec chezmoi apply --force cmp $HOME/.encrypted golden/.encrypted # test that chezmoi detects gpg encryption if gpg is configured but encryption = "gpg" is not set removeline $CHEZMOICONFIGDIR/chezmoi.toml 'encryption = "gpg"' exec chezmoi cat $HOME${/}.encrypted cmp stdout golden/.encrypted stderr 'chezmoi: warning: ''encryption'' not set, using gpg configuration. Check if ''encryption'' is correctly set as the top-level key.' prependline $CHEZMOICONFIGDIR/chezmoi.toml 'encryption = "gpg"' # test that chezmoi edit --apply transparently decrypts and re-encrypts exec chezmoi edit --apply --force $HOME${/}.encrypted grep '# edited' $HOME/.encrypted # test that chezmoi files in subdirectories can be encrypted and that suffix can be set appendline $CHEZMOICONFIGDIR/chezmoi.toml ' suffix = ".gpg"' mkdir $HOME/.dir cp golden/.encrypted $HOME/.dir exec chezmoi add --encrypt $HOME${/}.dir${/}.encrypted grep '-----BEGIN PGP MESSAGE-----' $CHEZMOISOURCEDIR/dot_dir/encrypted_dot_encrypted.gpg exec chezmoi edit --apply $HOME${/}.dir${/}.encrypted grep '# edited' $HOME/.dir/.encrypted -- golden/.encrypted -- # contents of .encrypted ================================================ FILE: internal/cmd/testdata/scripts/help.txtar ================================================ exec chezmoi help stdout 'Manage your dotfiles across multiple diverse machines, securely' exec chezmoi help add stdout 'Add targets to the source state\.' ================================================ FILE: internal/cmd/testdata/scripts/hooks.txtar ================================================ [windows] unix2dos golden/stdout # test that chezmoi add runs pre and post hooks exec chezmoi add $HOME${/}.file cmp stdout golden/stdout # test that chezmoi does not run irrelevant hooks exec chezmoi help ! stdout add-hook ! stdout source-state-hook -- bin/echo.cmd -- @echo %* -- golden/stdout -- pre-add-hook pre-read-source-state-hook post-read-source-state-hook post-add-hook -- home/user/.config/chezmoi/chezmoi.yaml -- hooks: add: pre: command: 'echo' args: - 'pre-add-hook' post: command: 'echo' args: - 'post-add-hook' read-source-state: pre: command: 'echo' args: - 'pre-read-source-state-hook' post: command: 'echo' args: - 'post-read-source-state-hook' -- home/user/.file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/hooks_windows.txtar ================================================ [!windows] skip 'Windows only' # test that chezmoi status runs hooks with an interpreter exec chezmoi status stdout pre-status-hook -- home/user/.config/chezmoi/chezmoi.yaml -- hooks: status: pre: script: 'pre-status-hook.ps1' -- home/user/.local/share/chezmoi/.keep -- -- home/user/pre-status-hook.ps1 -- "pre-status-hook" ================================================ FILE: internal/cmd/testdata/scripts/ignore.txtar ================================================ mksourcedir # test that chezmoi apply does not write ignored files ! exists $HOME/.file exec chezmoi apply --force exists $HOME/.file ! exists $HOME/README.md ! exists $HOME/.dir # test that chezmoi ignored lists the ignored entries exec chezmoi ignored cmp stdout golden/ignored # test that chezmoi ignored --tree lists the ignored entries in a tree-like format exec chezmoi ignored --tree cmp stdout golden/ignored-tree chhome home2/user # test that chezmoi manage lists all managed files exec chezmoi managed cmp stdout golden/managed-all # test that chezmoiignore ignores all files in a directory cp golden/.chezmoiignore-dir $CHEZMOISOURCEDIR/.chezmoiignore exec chezmoi managed cmp stdout golden/managed-ignore-dir # test that chezmoiignore ignores all files in a subdirectory cp golden/.chezmoiignore-dir-subdir $CHEZMOISOURCEDIR/.chezmoiignore exec chezmoi managed cmp stdout golden/managed-ignore-dir-subdir # test that chezmoiignore ignores all files matching a simple pattern cp golden/.chezmoiignore-star-slash-star-dot-txt $CHEZMOISOURCEDIR/.chezmoiignore exec chezmoi managed cmp stdout golden/managed-ignore-star-slash-star-dot-txt # test that chezmoiignore ignores all files matching a doublestar pattern cp golden/.chezmoiignore-star-star-slash-star-dot-txt $CHEZMOISOURCEDIR/.chezmoiignore exec chezmoi managed cmp stdout golden/managed-ignore-star-star-slash-star-dot-txt -- golden/.chezmoiignore-dir -- .dir -- golden/.chezmoiignore-dir-subdir -- .dir/subdir/ -- golden/.chezmoiignore-star-slash-star-dot-txt -- */*.txt -- golden/.chezmoiignore-star-star-slash-star-dot-txt -- **/*.txt -- golden/ignored -- .dir README.md -- golden/ignored-tree -- .dir README.md -- golden/managed-all -- .dir .dir/file.txt .dir/subdir .dir/subdir/file.txt .file.txt -- golden/managed-ignore-dir -- .file.txt -- golden/managed-ignore-dir-subdir -- .dir .dir/file.txt .file.txt -- golden/managed-ignore-star-slash-star-dot-txt -- .dir .dir/subdir .dir/subdir/file.txt .file.txt -- golden/managed-ignore-star-star-slash-star-dot-txt -- .dir .dir/subdir -- home/user/.local/share/chezmoi/.chezmoiignore -- README.md .dir {{ if false }} .file {{ end }} -- home/user/.local/share/chezmoi/README.md -- # contents of README.md -- home2/user/.local/share/chezmoi/dot_dir/file.txt -- # contents of .dir/file.txt -- home2/user/.local/share/chezmoi/dot_dir/subdir/file.txt -- # contents of .dir/subdir/file.txt -- home2/user/.local/share/chezmoi/dot_file.txt -- # contents of .file.txt ================================================ FILE: internal/cmd/testdata/scripts/ignored.txtar ================================================ mksourcedir # test that chezmoi ignored prints the list of ignored paths exec chezmoi ignored cmp stdout golden/ignored -- golden/ignored -- .dir/file .dir/subdir .readonly .template -- home/user/.local/share/chezmoi/.chezmoiignore -- .dir/subdir # Comment following pattern .read* **/file .template ================================================ FILE: internal/cmd/testdata/scripts/import.txtar ================================================ mkhomedir symlink archive/.dir/.symlink -> .file exec tar czf archive.tar.gz archive # test that chezmoi import imports an archive exec chezmoi import --strip-components=1 archive.tar.gz cmp $CHEZMOISOURCEDIR/dot_dir/dot_file golden/dot_dir/dot_file [unix] cmp $CHEZMOISOURCEDIR/dot_dir/symlink_dot_symlink golden/dot_dir/symlink_dot_symlink # FIXME this should pass on Windows # test that chezmoi import run a second time overwrites exec chezmoi import --strip-components=1 archive.tar.gz chhome home2/user # test chezmoi import --destination exec chezmoi import --strip-components=1 --destination=$HOME${/}.subdir archive.tar.gz cmp $CHEZMOISOURCEDIR/dot_subdir/dot_dir/dot_file golden/dot_dir/dot_file [unix] cmp $CHEZMOISOURCEDIR/dot_subdir/dot_dir/symlink_dot_symlink golden/dot_dir/symlink_dot_symlink # FIXME this should pass on Windows chhome home3/user # test chezmoi --import --exclude exec chezmoi import --strip-components=1 --destination=$HOME${/}.subdir --exclude=symlinks archive.tar.gz cmp $CHEZMOISOURCEDIR/dot_subdir/dot_dir/dot_file golden/dot_dir/dot_file ! exists $CHEZMOISOURCEDIR/dot_subdir/dot_dir/symlink_dot_symlink -- archive/.dir/.file -- # contents of .dir/.file -- golden/dot_dir/dot_file -- # contents of .dir/.file -- golden/dot_dir/symlink_dot_symlink -- .file -- home2/user/.local/share/chezmoi/dot_subdir/.keep -- -- home3/user/.local/share/chezmoi/dot_subdir/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/importtarzst.txtar ================================================ [(openbsd||windows)] skip 'tar does not support zstd compression' [!exec:zstd] skip 'zstd not found in $PATH' exec tar -c -f archive.tar.zst --use-compress-program zstd archive # test that chezmoi import imports a tar.zst archive exec chezmoi import --destination=$HOME${/}.dir --strip-components=1 archive.tar.zst cmp $CHEZMOISOURCEDIR/dot_dir/dir/file golden/dot_dir/dir/file -- archive/dir/file -- # contents of dir/file -- golden/dot_dir/dir/file -- # contents of dir/file -- home/user/.local/share/chezmoi/dot_dir/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/importxz.txtar ================================================ [(openbsd||windows)] skip 'tar does not support XZ compression' [!exec:xz] skip 'xz not found in $PATH' exec tar cJf archive.tar.xz archive # test that chezmoi import imports a tar.xz archive exec chezmoi import --destination=$HOME${/}.dir --strip-components=1 archive.tar.xz cmp $CHEZMOISOURCEDIR/dot_dir/dir/file golden/dot_dir/dir/file -- archive/dir/file -- # contents of dir/file -- golden/dot_dir/dir/file -- # contents of dir/file -- home/user/.local/share/chezmoi/dot_dir/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/importzip.txtar ================================================ [!exec:zip] skip 'zip not found in $PATH' exec zip -r archive.zip archive # test that chezmoi import imports a zip archive exec chezmoi import --destination=$HOME${/}.dir --strip-components=1 archive.zip cmp $CHEZMOISOURCEDIR/dot_dir/dir/file golden/dot_dir/dir/file -- archive/dir/file -- # contents of dir/file -- golden/dot_dir/dir/file -- # contents of dir/file -- home/user/.local/share/chezmoi/dot_dir/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/init.txtar ================================================ [!exec:git] skip 'git not found in $PATH' mkgitconfig mkhomedir golden mkhomedir # test that chezmoi init creates a git repo exec chezmoi init exists $CHEZMOISOURCEDIR/.git # create a commit cp golden/chezmoi.toml $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl cp golden/.file $CHEZMOISOURCEDIR/dot_file exec chezmoi git add . exec chezmoi git commit -- --message 'Initial commit' chhome home2/user # test that chezmoi init fetches git repo but does not apply mkgitconfig exec chezmoi init file://$WORK/home/user/.local/share/chezmoi exists $CHEZMOISOURCEDIR/.git ! exists $HOME/.file chhome home3/user # test that chezmoi init --apply fetches a git repo and runs chezmoi apply mkgitconfig exec chezmoi init --apply --force file://$WORK/home/user/.local/share/chezmoi exists $CHEZMOISOURCEDIR/.git cmp $HOME/.file golden/.file chhome home4/user # test that chezmoi init --apply --depth 1 --force --purge clones, applies, and purges mkgitconfig exists $CHEZMOICONFIGDIR ! exists $CHEZMOISOURCEDIR exec chezmoi init --apply --depth 1 --force --purge file://$WORK/home/user/.local/share/chezmoi cmp $HOME/.file golden/.file ! exists $CHEZMOICONFIGDIR ! exists $CHEZMOISOURCEDIR chhome home5/user # test that chezmoi init does not clone the repo if it is already checked out but does create the config file mkgitconfig exec chezmoi init --source=$HOME/dotfiles file://$WORK/nonexistentrepo exists $CHEZMOICONFIGDIR/chezmoi.toml chhome home6/user # test chezmoi init --one-shot mkgitconfig exec chezmoi init --one-shot file://$WORK/home/user/.local/share/chezmoi cmp $HOME/.file golden/.file ! exists $CHEZMOICONFIGDIR ! exists $CHEZMOISOURCEDIR chhome home7/user # test chezmoi init --data=true mkgitconfig exec chezmoi init --data=true file://$WORK/home/user/.local/share/chezmoi cmp $CHEZMOICONFIGDIR/chezmoi.toml golden/chezmoi.toml # test chezmoi init --data=false exec chezmoi init --data=false file://$WORK/home/user/.local/share/chezmoi cmp $CHEZMOICONFIGDIR/chezmoi.toml golden/chezmoi.toml-no-data chhome home8/user # test that chezmoi init fails if the generated config is not valid mkgitconfig ! exec chezmoi init stderr 'toml: expected character =' ! exists .config/chezmoi chhome home/user # create a new branch exec chezmoi git checkout -- -b new-branch edit $CHEZMOISOURCEDIR/dot_file exec chezmoi git add dot_file exec chezmoi git commit -- --message 'Edit .file' exec chezmoi git checkout master chhome home9/user # test chezmoi init --branch mkgitconfig exec chezmoi init --apply --branch=new-branch file://$WORK/home/user/.local/share/chezmoi grep '# edited' $HOME/.file chhome home10/user # test chezmoi --config init mkgitconfig exec chezmoi --config=$HOME/.chezmoi.toml init file://$WORK/home/user/.local/share/chezmoi cmp $HOME/.chezmoi.toml golden/chezmoi.toml ! exists $CHEZMOICONFIGDIR/chezmoi.toml chhome home11/user # test chezmoi init --config-path mkgitconfig exec chezmoi init --config-path=$HOME/.chezmoi.toml file://$WORK/home/user/.local/share/chezmoi cmp $HOME/.chezmoi.toml golden/chezmoi.toml ! exists $CHEZMOICONFIGDIR/chezmoi.toml chhome home12/user # test chezmoi init when the source dir is already in a git working copy mkgitconfig exec git init $HOME/.local/share exec chezmoi init ! exists $CHEZMOISOURCEDIR/.git chhome home13/user # test chezmoi init --prompt* exec chezmoi init --promptBool bool=true,boolOncePrompt=true --promptChoice=choice=one,choiceOncePrompt=two --promptInt int=1,intOncePrompt=2 --promptString string=string,stringOncePrompt=once --promptMultichoice multichoice=one/two,multichoiceOncePrompt=two/three cmp $CHEZMOICONFIGDIR/chezmoi.yaml golden/chezmoi.yaml chhome home14/user # test that chezmoi init creates a config file with a .yml extension exec chezmoi init exists $CHEZMOICONFIGDIR/chezmoi.yml # test that chezmoi reads a config file with a .yml extension exec chezmoi execute-template '{{ .key }}' stdout ^value$ -- golden/chezmoi.toml -- [data] email = "firstname.lastname@company.com" -- golden/chezmoi.toml-no-data -- [data] email = "me@home.org" -- golden/chezmoi.yaml -- data: bool: true boolOnce: true choice: one choiceOnce: two int: 1 intOnce: 2 multichoice: ["one","two"] multichoiceOnce: ["two","three"] string: string stringOnce: once -- home13/user/.local/share/chezmoi/.chezmoi.yaml.tmpl -- data: bool: {{ promptBool "bool" }} boolOnce: {{ promptBoolOnce . "boolOnce" "boolOncePrompt" }} choice: {{ promptChoice "choice" (list "one" "two" "three") }} choiceOnce: {{ promptChoiceOnce . "choiceOnce" "choiceOncePrompt" (list "one" "two" "three") }} int: {{ promptInt "int" }} intOnce: {{ promptIntOnce . "intOnce" "intOncePrompt" }} multichoice: {{ promptMultichoice "multichoice" (list "one" "two" "three") | toJson}} multichoiceOnce: {{ promptMultichoiceOnce . "multichoiceOnce" "multichoiceOncePrompt" (list "one" "two" "three") | toJson }} string: {{ promptString "string" }} stringOnce: {{ promptStringOnce . "stringOnce" "stringOncePrompt" }} -- home14/user/.local/share/chezmoi/.chezmoi.yml.tmpl -- data: key: value -- home4/user/.config/chezmoi/chezmoi.toml -- -- home5/user/dotfiles/.chezmoi.toml.tmpl -- [data] email = "me@home.org" -- home5/user/dotfiles/.git/.keep -- -- home7/user/.config/chezmoi/chezmoi.toml -- [data] email = "firstname.lastname@company.com" -- home7/user/.local/share/chezmoi/.chezmoi.toml.tmpl -- {{- $email := get . "email" -}} {{- if not $email -}} {{- $email = "me@home.org" -}} {{- end -}} [data] email = {{ $email | quote }} -- home7/user/.local/share/chezmoi/.git/.keep -- -- home8/user/.local/share/chezmoi/.chezmoi.toml.tmpl -- [diff] exclude: ["scripts"] ================================================ FILE: internal/cmd/testdata/scripts/initconfig.txtar ================================================ [windows] skip 'test requires path separator to be forward slash' mkdir $CHEZMOISOURCEDIR # test that chezmoi init writes the initial config into the default config dir cp golden/chezmoi1.yaml $CHEZMOISOURCEDIR/.chezmoi.yaml.tmpl exec chezmoi init cmp $CHEZMOICONFIGDIR/chezmoi.yaml golden/chezmoi1.yaml # test that chezmoi init writes an updated config into the default config dir cp golden/chezmoi2.yaml $CHEZMOISOURCEDIR/.chezmoi.yaml.tmpl exec chezmoi init cmp $CHEZMOICONFIGDIR/chezmoi.yaml golden/chezmoi2.yaml # test that chezmoi init writes a config of a new format into the default config dir rm $CHEZMOISOURCEDIR/.chezmoi.yaml.tmpl cp golden/chezmoi3.toml $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl exec chezmoi init cmp $CHEZMOICONFIGDIR/chezmoi.yaml golden/chezmoi2.yaml cmp $CHEZMOICONFIGDIR/chezmoi.toml golden/chezmoi3.toml # test that the last operation broke chezmoi ! exec chezmoi status ! stdout . cmpenv stderr golden/error1.log # test that deleting the old config file fixes the issue rm $CHEZMOICONFIGDIR/chezmoi.yaml exec chezmoi status ! stdout . ! stderr . # test that the state file was written into the default config dir exists $CHEZMOICONFIGDIR/chezmoistate.boltdb chhome home2/user mkdir $CHEZMOISOURCEDIR # test that chezmoi --config=path init writes the initial config into path cp golden/chezmoi1.yaml $CHEZMOISOURCEDIR/.chezmoi.yaml.tmpl exec chezmoi --config=$HOME/.chezmoi/athome.yaml init cmp $HOME/.chezmoi/athome.yaml golden/chezmoi1.yaml # test that chezmoi --config=path init writes an updated config into path cp golden/chezmoi2.yaml $CHEZMOISOURCEDIR/.chezmoi.yaml.tmpl exec chezmoi --config=$HOME/.chezmoi/athome.yaml init cmp $HOME/.chezmoi/athome.yaml golden/chezmoi2.yaml # test that chezmoi --config=path init writes a config of a new format into path rm $CHEZMOISOURCEDIR/.chezmoi.yaml.tmpl cp golden/chezmoi3.toml $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl exec chezmoi --config=$HOME/.chezmoi/athome.yaml init cmp $HOME/.chezmoi/athome.yaml golden/chezmoi3.toml # test that the last operation broke chezmoi ! exec chezmoi --config=$HOME/.chezmoi/athome.yaml status ! stdout . cmpenv stderr golden/error2.log # test that renaming the file and updating the config path fixes the issue mv $HOME/.chezmoi/athome.yaml $HOME/.chezmoi/athome.toml exec chezmoi --config=$HOME/.chezmoi/athome.toml status ! stdout . ! stderr . # test that the state file was written next to the config file exists $HOME/.chezmoi/chezmoistate.boltdb # test that nothing was ever written into the default config dir ! exists $CHEZMOICONFIGDIR/chezmoi.toml ! exists $CHEZMOICONFIGDIR/chezmoistate.boltdb chhome home3/user mkdir $CHEZMOISOURCEDIR # test that chezmoi --config=path --config-format=format init writes the initial config into path cp golden/chezmoi1.yaml $CHEZMOISOURCEDIR/.chezmoi.yaml.tmpl exec chezmoi --config=$HOME/.chezmoi/athome.txt --config-format=yaml init cmp $HOME/.chezmoi/athome.txt golden/chezmoi1.yaml # test that chezmoi --config=path --config-format=format init writes an updated config into path cp golden/chezmoi2.yaml $CHEZMOISOURCEDIR/.chezmoi.yaml.tmpl exec chezmoi --config=$HOME/.chezmoi/athome.txt --config-format=yaml init cmp $HOME/.chezmoi/athome.txt golden/chezmoi2.yaml # test that chezmoi --config=path --config-format=format init writes a config of a new format into path rm $CHEZMOISOURCEDIR/.chezmoi.yaml.tmpl cp golden/chezmoi3.toml $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl exec chezmoi --config=$HOME/.chezmoi/athome.txt --config-format=yaml init cmp $HOME/.chezmoi/athome.txt golden/chezmoi3.toml # test that the last operation broke chezmoi ! exec chezmoi --config=$HOME/.chezmoi/athome.txt --config-format=yaml status ! stdout . cmpenv stderr golden/error3.log # test that updating the config format fixes the issue exec chezmoi --config=$HOME/.chezmoi/athome.txt --config-format=toml status ! stdout . ! stderr . # test that the state file was written next to the config file exists $HOME/.chezmoi/chezmoistate.boltdb # test that nothing was ever written into the default config dir ! exists $CHEZMOICONFIGDIR/chezmoi.toml ! exists $CHEZMOICONFIGDIR/chezmoistate.boltdb -- golden/chezmoi1.yaml -- data: email: "mail1@example.com" -- golden/chezmoi2.yaml -- data: email: "mail2@example.com" -- golden/chezmoi3.toml -- [data] email = "mail3@example.com" -- golden/error1.log -- chezmoi: multiple config files: $CHEZMOICONFIGDIR/chezmoi.toml and $CHEZMOICONFIGDIR/chezmoi.yaml -- golden/error2.log -- chezmoi: invalid config: $WORK/home2/user/.chezmoi/athome.yaml: [2:3] value is not allowed in this context 1 | [data] > 2 | email = "mail3@example.com" ^ -- golden/error3.log -- chezmoi: invalid config: $WORK/home3/user/.chezmoi/athome.txt: [2:3] value is not allowed in this context 1 | [data] > 2 | email = "mail3@example.com" ^ ================================================ FILE: internal/cmd/testdata/scripts/initgitlfs.txtar ================================================ [windows] skip 'UNIX only' chmod 755 bin/git # test that chezmoi init --git-lfs runs git lfs pull after cloning exec chezmoi init --git-lfs user cmpenv stdout golden/init -- bin/git -- #!/bin/sh echo git $* -- golden/init -- git clone --recurse-submodules https://github.com/user/dotfiles.git $HOME/.local/share/chezmoi git lfs pull ================================================ FILE: internal/cmd/testdata/scripts/inittemplatefuncs.txtar ================================================ # test exit template function exec chezmoi execute-template --init '{{ exit 0 }}' ! exec chezmoi execute-template --init '{{ exit 1 }}' # test promptBoolOnce template function with execute-template --init exec chezmoi execute-template --init --promptBool bool=true '{{ promptBoolOnce . "bool" "bool" }}' stdout true # test promptChoiceOnce template function with execute-template --init exec chezmoi execute-template --init --promptChoice choice=one '{{ promptChoiceOnce . "choice" "choice" (list "one" "two" "three") }}' stdout one # test promptMultichoiceOnce template function with execute-template --init exec chezmoi execute-template --init --promptMultichoice multichoice=one/two '{{ promptMultichoiceOnce . "multichoice" "multichoice" (list "one" "two" "three") }}' stdout '[one two]' # test promptIntOnce template function with execute-template --init exec chezmoi execute-template --init --promptInt int=1 '{{ promptIntOnce . "int" "int" }}' stdout 1 # test promptStringOnce template function with execute-template --init exec chezmoi execute-template --init --promptString string=value '{{ promptStringOnce . "string" "string" }}' stdout value # test writeToStdout template function exec chezmoi execute-template --init '{{ writeToStdout "string" }}' stdout string # test prompt*Once functions without existing data stdin golden/input exec chezmoi init --no-tty cmp ${CHEZMOICONFIGDIR}/chezmoi.toml golden/chezmoi.toml chhome home2/user # test prompt*Once functions with existing data exec chezmoi init cmp ${CHEZMOICONFIGDIR}/chezmoi.toml golden/chezmoi.toml chhome home3/user # test prompt*Once functions with existing data and nested keys exec chezmoi init cmp ${CHEZMOICONFIGDIR}/chezmoi.toml golden/chezmoi.toml -- golden/chezmoi.toml -- [data] bool = true int = 1 string = "value" -- golden/input -- true 1 value -- home/user/.local/share/chezmoi/.chezmoi.toml.tmpl -- {{ $bool := promptBoolOnce . "bool" "bool" -}} {{ $int := promptIntOnce . "int" "int" -}} {{ $string := promptStringOnce . "string" "string" -}} [data] bool = {{ $bool }} int = {{ $int }} string = {{ $string | quote }} -- home2/user/.config/chezmoi/chezmoi.toml -- [data] bool = true int = 1 string = "value" -- home2/user/.local/share/chezmoi/.chezmoi.toml.tmpl -- {{ $bool := promptBoolOnce . "bool" "bool" -}} {{ $int := promptIntOnce . "int" "int" -}} {{ $string := promptStringOnce . "string" "string" -}} [data] bool = {{ $bool }} int = {{ $int }} string = {{ $string | quote }} -- home3/user/.config/chezmoi/chezmoi.toml -- [data] nested.bool = true nested.int = 1 nested.string = "value" -- home3/user/.local/share/chezmoi/.chezmoi.toml.tmpl -- {{ $bool := promptBoolOnce . "nested.bool" "bool" -}} {{ $int := promptIntOnce . "nested.int" "int" -}} {{ $string := promptStringOnce . "nested.string" "string" -}} [data] bool = {{ $bool }} int = {{ $int }} string = {{ $string | quote }} ================================================ FILE: internal/cmd/testdata/scripts/issue1161.txtar ================================================ # test that chezmoi does not follow symlinks in the source directory if the file should be ignored symlink $CHEZMOISOURCEDIR/.HASH_dot_file -> non_existent_file exec chezmoi status rm $CHEZMOISOURCEDIR/.HASH_dot_file exec chezmoi status # test that chezmoi does follow symlinks in the source directory if the file should not be ignored symlink $CHEZMOISOURCEDIR/.chezmoifile -> non_existent_file ! exec chezmoi status [unix] stderr 'no such file or directory' [windows] stderr 'The system cannot find the file specified' rm $CHEZMOISOURCEDIR/.chezmoifile exec chezmoi status -- home/user/.local/share/chezmoi/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue1213.txtar ================================================ [!exec:git] skip 'git not found in $PATH' mkgitconfig # create a repo exec chezmoi init exists $CHEZMOISOURCEDIR/.git cp golden/.chezmoi.toml.tmpl $CHEZMOISOURCEDIR cp golden/dot_file.tmpl $CHEZMOISOURCEDIR exec chezmoi git add . exec chezmoi git commit -- --message 'Initial commit' chhome home2/user # test that chezmoi init --apply makes config template data available mkgitconfig exec chezmoi init --apply file://$WORK/home/user/.local/share/chezmoi cmp $HOME/.file golden/.file -- golden/.chezmoi.toml.tmpl -- [data.me.github] username = "user" -- golden/.file -- username = "user" -- golden/dot_file.tmpl -- username = {{ .me.github.username | quote }} ================================================ FILE: internal/cmd/testdata/scripts/issue1237.txtar ================================================ # test that chezmoi add does not add ignored directories exec chezmoi add ~/.config exec chezmoi managed cmp stdout golden/managed exists $CHEZMOISOURCEDIR/dot_config/.keep -- golden/managed -- .config -- home/user/.config/Bitwarden CLI/file -- # contents of .config/Bitwarden CLI/file -- home/user/.local/share/chezmoi/.chezmoiignore -- .config/Bitwarden CLI .config/Bitwarden CLI/** ================================================ FILE: internal/cmd/testdata/scripts/issue1365.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi diff generates the correct output when chezmoi's config file is managed by chezmoi chmod 700 $CHEZMOICONFIGDIR chmod 600 $CHEZMOICONFIGDIR/chezmoi.toml exec chezmoi diff cmp stdout golden/chezmoi.toml-diff.diff # test that chezmoi diff generates the correct output when a custom diff tool is set and the file is in a subdirectory cp golden/chezmoi.toml-custom-diff $CHEZMOICONFIGDIR/chezmoi.toml exec chezmoi diff stdout ^$HOME/\.config/chezmoi/chezmoi\.toml\s+$WORK/.*/\.config/chezmoi/chezmoi\.toml$ -- golden/chezmoi.toml-custom-diff -- [diff] command = "echo" -- golden/chezmoi.toml-diff.diff -- diff --git a/.config/chezmoi/chezmoi.toml b/.config/chezmoi/chezmoi.toml index 63caeb2522e9320690143749a6aee71e8fddd300..b9495f6120fb36c4cbda33cb72700c80f1ebb979 100600 --- a/.config/chezmoi/chezmoi.toml +++ b/.config/chezmoi/chezmoi.toml @@ -1 +1 @@ -# dest contents of chezmoi.toml +# target contents of chezmoi.toml -- home/user/.config/chezmoi/chezmoi.toml -- # dest contents of chezmoi.toml -- home/user/.local/share/chezmoi/dot_config/private_chezmoi/private_chezmoi.toml -- # target contents of chezmoi.toml ================================================ FILE: internal/cmd/testdata/scripts/issue1666.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi apply --verbose does not display scripts that are run always, but does run them exec chezmoi apply --verbose cmp stdout golden/apply ! stderr . -- golden/apply -- script -- home/user/.config/chezmoi/chezmoi.toml -- [diff] exclude = ["always"] -- home/user/.local/share/chezmoi/run_script.sh -- #!/bin/sh echo script ================================================ FILE: internal/cmd/testdata/scripts/issue1794.txtar ================================================ [!exec:age] skip 'age not found in $PATH' mkageconfig mkgitconfig # create a dotfile repo with a config file template and an encrypted file exec chezmoi init cp $CHEZMOICONFIGDIR/chezmoi.toml $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl exec chezmoi add --encrypt $HOME/.file exec chezmoi git add . exec chezmoi git commit -- --message 'Initial commit' chhome home2/user # test that chezmoi init --apply uses the configured encryption from the template exec chezmoi init --apply file://$WORK/home/user/.local/share/chezmoi cmp $HOME/.file golden/.file -- golden/.file -- # contents of .file -- home/user/.file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue1832.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi add succeeds when changing the permissions of an intermediate directory exec chezmoi add $HOME/.config/fish/config chmod 700 $HOME/.config exec chezmoi add --force --recursive=false $HOME/.config -- home/user/.config/fish/config -- # contents of .config/fish/config ================================================ FILE: internal/cmd/testdata/scripts/issue1866.txtar ================================================ # test that chezmoi ignores emacs symbolic link locks symlink 'home/user/.local/share/chezmoi/.#lock' -> invalid exec chezmoi apply cmp $HOME/.file golden/.file -- golden/.file -- # contents of .file -- home/user/.local/share/chezmoi/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue1869.txtar ================================================ # test concurrent read when there are multiple .chezmoiignore files exec chezmoi status chhome home2/user # test concurrent read when there are multitple .chezmoidata. files exec chezmoi data -- home/user/.local/share/chezmoi/.chezmoiignore -- pattern -- home/user/.local/share/chezmoi/dir1/.chezmoiignore -- pattern -- home/user/.local/share/chezmoi/dir1/subdir1/.chezmoiignore -- pattern -- home/user/.local/share/chezmoi/dir1/subdir2/.chezmoiignore -- pattern -- home/user/.local/share/chezmoi/dir1/subdir3/.chezmoiignore -- pattern -- home/user/.local/share/chezmoi/dir2/.chezmoiignore -- pattern -- home/user/.local/share/chezmoi/dir2/subdir1/.chezmoiignore -- pattern -- home/user/.local/share/chezmoi/dir2/subdir2/.chezmoiignore -- pattern -- home/user/.local/share/chezmoi/dir2/subdir3/.chezmoiignore -- pattern -- home/user/.local/share/chezmoi/dir3/.chezmoiignore -- pattern -- home/user/.local/share/chezmoi/dir3/subdir1/.chezmoiignore -- pattern -- home/user/.local/share/chezmoi/dir3/subdir2/.chezmoiignore -- pattern -- home/user/.local/share/chezmoi/dir3/subdir3/.chezmoiignore -- pattern -- home2/user/.local/share/chezmoi/.chezmoidata.yaml -- key: value -- home2/user/.local/share/chezmoi/dir1/.chezmoidata.yaml -- key: value -- home2/user/.local/share/chezmoi/dir1/subdir1/.chezmoidata.yaml -- key: value -- home2/user/.local/share/chezmoi/dir1/subdir2/.chezmoidata.yaml -- key: value -- home2/user/.local/share/chezmoi/dir1/subdir3/.chezmoidata.yaml -- key: value -- home2/user/.local/share/chezmoi/dir2/.chezmoidata.yaml -- key: value -- home2/user/.local/share/chezmoi/dir2/subdir1/.chezmoidata.yaml -- key: value -- home2/user/.local/share/chezmoi/dir2/subdir2/.chezmoidata.yaml -- key: value -- home2/user/.local/share/chezmoi/dir2/subdir3/.chezmoidata.yaml -- key: value -- home2/user/.local/share/chezmoi/dir3/.chezmoidata.yaml -- key: value -- home2/user/.local/share/chezmoi/dir3/subdir1/.chezmoidata.yaml -- key: value -- home2/user/.local/share/chezmoi/dir3/subdir2/.chezmoidata.yaml -- key: value -- home2/user/.local/share/chezmoi/dir3/subdir3/.chezmoidata.yaml -- key: value ================================================ FILE: internal/cmd/testdata/scripts/issue2016.txtar ================================================ symlink $HOME/.symlink -> .file # test that chezmoi apply removes broken symlinks exec chezmoi apply ! lexists $HOME/.symlink -- home/user/.local/share/chezmoi/.chezmoiremove -- .symlink ================================================ FILE: internal/cmd/testdata/scripts/issue2092.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi dump warns when the config file has not been generated exec chezmoi dump [umask:002] cmp stdout golden/dump-umask-002.json [umask:022] cmp stdout golden/dump-umask-022.json stderr 'config file template has changed' # test that chezmoi dump does not return an error when the config file template has been modified exec chezmoi init edit $CHEZMOICONFIGDIR${/}chezmoi.toml exec chezmoi dump [umask:002] cmp stdout golden/dump-umask-002.json [umask:022] cmp stdout golden/dump-umask-022.json ! stderr . -- golden/dump-umask-002.json -- { ".file": { "type": "file", "name": ".file", "contents": "# contents of .file\n", "perm": 436 } } -- golden/dump-umask-022.json -- { ".file": { "type": "file", "name": ".file", "contents": "# contents of .file\n", "perm": 420 } } -- home/user/.file -- # contents of .file -- home/user/.local/share/chezmoi/.chezmoi.toml.tmpl -- [data] -- home/user/.local/share/chezmoi/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue2132.txtar ================================================ # test that chezmoi apply does not create a remove_ directory if it has no contents exec chezmoi apply ! exists $HOME/.dir # test that running chezmoi apply a second time completes with no output exec chezmoi apply --no-tty ! stdout . ! exists $HOME/.dir -- home/user/.local/share/chezmoi/remove_dot_dir/non_existent_file -- ================================================ FILE: internal/cmd/testdata/scripts/issue2137.txtar ================================================ # test that chezmoi apply fails if .chezmoiversion requires a more recent version ! exec chezmoi apply stderr 'source state requires chezmoi version 3\.0\.0 or later' # test that chezmoi init fails if .chezmoiversion requires a more recent version ! exec chezmoi init stderr 'source state requires chezmoi version 3\.0\.0 or later' -- home/user/.local/share/chezmoi/.chezmoiversion -- 3.0.0 -- home/user/.local/share/chezmoi/.git/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue2177.txtar ================================================ # test that chezmoi forget prints a warning when asked to forget externals exec chezmoi forget $HOME${/}.external stderr 'cannot forget entry from external https://github\.com/user/repo\.git defined in .*/home/user/\.local/share/chezmoi/\.chezmoiexternal\.toml' -- home/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".external"] type = "git-repo" url = "https://github.com/user/repo.git" ================================================ FILE: internal/cmd/testdata/scripts/issue2283.txtar ================================================ # test that sourceDir has the correct value in chezmoi init with .chezmoiroot exec chezmoi init cmpenv $CHEZMOICONFIGDIR/chezmoi.yaml golden/chezmoi.yaml -- golden/chezmoi.yaml -- sourceDir: $CHEZMOISOURCEDIR/home -- home/user/.local/share/chezmoi/.chezmoiroot -- home -- home/user/.local/share/chezmoi/home/.chezmoi.yaml.tmpl -- sourceDir: {{ .chezmoi.sourceDir }} ================================================ FILE: internal/cmd/testdata/scripts/issue2300.txtar ================================================ [windows] skip 'UNIX only' # FIXME # test that chezmoi edit --apply applies changes to a directory exec chezmoi edit --apply $HOME/.dir grep '# edited' $HOME/.dir/file -- home/user/.config/chezmoi/chezmoi.toml -- [edit] minDuration = 0 -- home/user/.dir/file -- # contents of .dir/file -- home/user/.local/share/chezmoi/dot_dir/file -- # contents of .dir/file ================================================ FILE: internal/cmd/testdata/scripts/issue2302.txtar ================================================ # test that chezmoi does not panic if an external uses an absolute path ! exec chezmoi diff stderr 'path is not relative' -- home/user/.local/share/chezmoi/.chezmoiexternal.toml -- ["/home/user/.dir"] type = "archive" url = "https://example.com/example.tar.gz" ================================================ FILE: internal/cmd/testdata/scripts/issue2315.txtar ================================================ [windows] skip 'UNIX only' # Test that chezmoi init runs run_before_ scripts before executing templates env PATH=$PATH:$HOME/bin exec chezmoi init --apply cmp $HOME/.file golden/.file -- golden/.file -- output -- golden/binary.sh -- #!/bin/sh echo output -- home/user/.local/share/chezmoi/dot_file.tmpl -- {{ output "binary.sh" -}} -- home/user/.local/share/chezmoi/run_before_install-binary.sh -- #!/bin/sh cp $WORK/golden/binary.sh $HOME/bin chmod a+x $HOME/bin/binary.sh -- home/user/bin/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue2354.txtar ================================================ # test that chezmoi source-path does not read the source state if not needed exec chezmoi source-path stdout ${CHEZMOISOURCEDIR@R} -- home/user/.local/share/chezmoi/.chezmoiexternal.json -- invalid JSON file ================================================ FILE: internal/cmd/testdata/scripts/issue2380.txtar ================================================ # test that chezmoi source-path with no arguments respects .chezmoiroot exec chezmoi source-path stdout ${CHEZMOISOURCEDIR@R}/home -- home/user/.local/share/chezmoi/.chezmoiroot -- home ================================================ FILE: internal/cmd/testdata/scripts/issue2427.txtar ================================================ symlink archive/dir/symlink -> file exec tar czf www/archive.tar.gz archive httpd www # test that chezmoi managed lists all targets exec chezmoi managed cmp stdout golden/managed # test that chezmoi managed --include=encrypted lists encrypted files only exec chezmoi managed --include=encrypted cmp stdout golden/managed-encrypted # test that chezmoi managed --include=externals lists external targets only exec chezmoi managed --include=externals cmp stdout golden/managed-externals -- archive/dir/file -- # contents of dir/file -- golden/managed -- .dir .dir/dir .dir/dir/file .dir/dir/symlink .encrypted .file -- golden/managed-encrypted -- .encrypted -- golden/managed-externals -- .dir .dir/dir .dir/dir/file .dir/dir/symlink -- home/user/.config/chezmoi/chezmoi.toml -- encryption = "gpg" -- home/user/.local/share/chezmoi/.chezmoiexternal.yaml -- .dir: type: archive url: {{ env "HTTPD_URL" }}/archive.tar.gz stripComponents: 1 -- home/user/.local/share/chezmoi/dot_file -- -- home/user/.local/share/chezmoi/encrypted_dot_encrypted.asc -- -- www/.file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue2500.txtar ================================================ [unix] chmod 755 bin/git [windows] skip 'UNIX only' # test that the error message returned when git fails to update an external includes the target path exec chezmoi apply ! exec chezmoi apply --force --refresh-externals stderr ${HOME@R}/\.dir:\x20exit\x20status\x201 -- bin/git -- #!/bin/sh case "$1" in clone) mkdir -p $3/.git echo "clone ok" ;; *) echo "unknown command $*" exit 1 esac -- home/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".dir"] type = "git-repo" url = "https://github.com/owner/repo" ================================================ FILE: internal/cmd/testdata/scripts/issue2510.txtar ================================================ # test that .chezmoiignore works with scripts exec chezmoi diff cmp stdout golden/diff chhome home2/user # test that .chezmoiignore works with scripts in .chezmoiscripts exec chezmoi diff cmp stdout golden/diff2 -- golden/diff -- diff --git a/script-one.sh b/script-one.sh new file mode 100755 index 0000000000000000000000000000000000000000..a5498fa48acd6f616d60604b94352958c5e967fc --- /dev/null +++ b/script-one.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "script one" -- golden/diff2 -- diff --git a/.chezmoiscripts/script-one.sh b/.chezmoiscripts/script-one.sh new file mode 100755 index 0000000000000000000000000000000000000000..a5498fa48acd6f616d60604b94352958c5e967fc --- /dev/null +++ b/.chezmoiscripts/script-one.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "script one" -- home/user/.local/share/chezmoi/.chezmoiignore -- script-two.sh -- home/user/.local/share/chezmoi/run_script-one.sh -- #!/bin/sh echo "script one" -- home/user/.local/share/chezmoi/run_script-two.sh -- #!/bin/sh echo "script two" -- home2/user/.local/share/chezmoi/.chezmoiignore -- .chezmoiscripts/script-two.sh -- home2/user/.local/share/chezmoi/.chezmoiscripts/run_script-one.sh -- #!/bin/sh echo "script one" -- home2/user/.local/share/chezmoi/.chezmoiscripts/run_script-two.sh -- #!/bin/sh echo "script two" ================================================ FILE: internal/cmd/testdata/scripts/issue2573.txtar ================================================ [windows] skip 'UNIX only' chmod 755 bin/diff # test that chezmoi apply --verbose with an external diff command and dirs excluded does not run the diff command when a directory is removed exec chezmoi apply --verbose ! stdout diff -- bin/diff -- #!/bin/sh echo diff $* -- home/user/.config/chezmoi/chezmoi.toml -- [diff] command = "diff" exclude = ["dirs"] -- home/user/.dir/subdir/.keep -- -- home/user/.local/share/chezmoi/exact_dot_dir/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue2577.txtar ================================================ [windows] skip 'UNIX only' chmod 755 bin/fossil # test that chezmoi update runs a custom update command and applies changes exec chezmoi update cmp $HOME/.file golden/.file -- bin/fossil -- #!/bin/sh case "$*" in "update") echo "# contents of .file" > dot_file ;; *) echo fossil: unknown command: $* echo fossil: use "help" for more information exit 1 ;; esac -- golden/.file -- # contents of .file -- home/user/.config/chezmoi/chezmoi.toml -- [update] command = "fossil" args = ["update"] -- home/user/.local/share/chezmoi/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue2597.txtar ================================================ exec tar czf www/master.tar.gz master httpd www exec chezmoi apply exists $HOME/.oh-my-zsh/README.md ! exists $HOME/.oh-my-zsh/cache/.gitkeep -- home/user/.local/share/chezmoi/.chezmoiexternal.toml.tmpl -- [".oh-my-zsh"] type = "archive" url = "{{ env "HTTPD_URL" }}/master.tar.gz" exact = true stripComponents = 1 -- home/user/.local/share/chezmoi/.chezmoiignore -- .oh-my-zsh/cache -- master/README.md -- # contents of README.md -- master/cache/.gitkeep -- -- www/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue2599.txtar ================================================ # test that chezmoi apply --interactive --verbose does not prompt for empty scripts exec chezmoi apply --interactive --no-tty --verbose -- home/user/.local/share/chezmoi/run_empty.sh -- ================================================ FILE: internal/cmd/testdata/scripts/issue2609.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi apply uses the umask from the configuration file exec chezmoi apply cmpmod 600 $HOME/.file -- home/user/.config/chezmoi/chezmoi.yaml -- umask: 0o077 -- home/user/.local/share/chezmoi/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue2628.txtar ================================================ httpd www # test that .chezmoiexternal.toml.tmpl is read exec chezmoi apply cmp $HOME/.file golden/.file chhome home2/user # test that .chezmoiignore.tmpl is read exec chezmoi apply ! exists $HOME/.file chhome home3/user # test that .chezmoiremove.tmpl is read exec chezmoi apply ! exists $HOME/.file -- golden/.file -- # contents of .file -- home/user/.local/share/chezmoi/.chezmoiexternal.toml.tmpl -- [".file"] type = "file" url = "{{ env "HTTPD_URL" }}/.file" -- home2/user/.local/share/chezmoi/.chezmoiignore.tmpl -- .file -- home2/user/.local/share/chezmoi/dot_file -- # contents of .file -- home3/user/.file -- # contents of .file -- home3/user/.local/share/chezmoi/.chezmoiremove.tmpl -- .file -- www/.file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue2695.txtar ================================================ # test that chezmoi status returns an error when the JSON config file is invalid ! exec chezmoi status stderr 'invalid config' ! stderr 'json.*json' # test that chezmoi doctor warns about invalid JSON config files ! exec chezmoi doctor stdout 'error\s+config-file\s+.*invalid character' chhome home2/user # test that chezmoi status returns an error when the TOML config file is invalid ! exec chezmoi status stderr 'invalid config' ! stderr 'chezmoi\.toml.*chezmoi\.toml' # test that chezmoi doctor warns about invalid TOML config files ! exec chezmoi doctor stdout 'error\s+config-file' chhome home3/user # test that chezmoi status returns an error when the YAML config file is invalid ! exec chezmoi status stderr 'invalid config' ! stderr 'chezmoi\.yaml.*chezmoi\.yaml' # test that chezmoi doctor warns about invalid YAML config files ! exec chezmoi doctor stdout 'error\s+config-file\s+.*string was used where mapping is expected' -- home/user/.config/chezmoi/chezmoi.json -- { "string": unquoted } -- home2/user/.config/chezmoi/chezmoi.toml -- invalid file -- home3/user/.config/chezmoi/chezmoi.yaml -- string ================================================ FILE: internal/cmd/testdata/scripts/issue2752.txtar ================================================ [windows] skip 'uses forward slashes in paths' # test that chezmoi target-path does not include .chezmoiroot in the output exec chezmoi target-path $CHEZMOISOURCEDIR${/}home${/}dot_file stdout ${HOME@R}/\.file -- home/user/.local/share/chezmoi/.chezmoiroot -- home -- home/user/.local/share/chezmoi/home/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue2820.txtar ================================================ # test that chezmoi add refuses to add files in chezmoi's source directory ! exec chezmoi add $CHEZMOISOURCEDIR stderr 'cannot add chezmoi file to chezmoi' # test that chezmoi add refuses to add chezmoi's config file ! exec chezmoi add $CHEZMOICONFIGDIR/chezmoi.toml stderr 'cannot add chezmoi\x27s config file to chezmoi' # test that chezmoi add refuses to add files in chezmoi's cache directory ! exec chezmoi add $HOME/.cache/chezmoi stderr 'cannot add chezmoi file to chezmoi' # test that chezmoi add refuses to add files in chezmoi's source directory when already in that directory cd $CHEZMOISOURCEDIR exists dot_file ! exec chezmoi add dot_file stderr 'cannot add chezmoi file to chezmoi' -- home/user/.cache/chezmoi/.keep -- -- home/user/.config/chezmoi/chezmoi.toml -- -- home/user/.local/share/chezmoi/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue2858.txtar ================================================ # test that chezmoi managed lists managed files in git-repo externals exec chezmoi managed cmp stdout golden/managed # test that chezmoi unmanaged does not list files in git-repo externals exec chezmoi unmanaged cmp stdout golden/unmanaged -- golden/managed -- .dir -- golden/unmanaged -- .local -- home/user/.dir/.git/HEAD -- ref: refs/head/master -- home/user/.dir/file -- # contents of .dir/file -- home/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".dir"] type = "git-repo" url = "https://example.com/user/repo" ================================================ FILE: internal/cmd/testdata/scripts/issue2861.txtar ================================================ # test that chezmoi status shows just-added-then-modified files exec chezmoi add $HOME${/}.file edit $HOME/.file edit $CHEZMOISOURCEDIR/dot_file edit $CHEZMOISOURCEDIR/dot_file exec chezmoi status cmp stdout golden/status -- golden/status -- MM .file -- home/user/.file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue2865.txtar ================================================ [!exec:git] skip 'git not found in $PATH' mkgitconfig # create a commit exec chezmoi git init exec chezmoi git add . exec chezmoi git commit -- --message 'Initial commit' chhome home2/user # test that .chezmoi.sourceDir is set correctly when .chezmoiroot is present (chezmoi init --apply in a clean home directory) mkgitconfig exec chezmoi init --apply file://$WORK/home/user/.local/share/chezmoi stdout '\.chezmoi\.sourceDir=.*/\.local/share/chezmoi/home\r?$' stdout 'CHEZMOI_SOURCE_DIR=.*/\.local/share/chezmoi/home\r?$' # test that .chezmoi.sourceDir is set correctly in config file exec chezmoi execute-template '{{ .testDir }}' stdout '/.local/share/chezmoi/home\r?$' -- home/user/.local/share/chezmoi/.chezmoiroot -- home -- home/user/.local/share/chezmoi/home/.chezmoi.toml.tmpl -- [data] testDir = {{ .chezmoi.sourceDir | quote }} -- home/user/.local/share/chezmoi/home/.chezmoiignore -- {{- if eq .chezmoi.os "windows" }} .chezmoiscripts/echo.sh {{- else }} .chezmoiscripts/echo.ps1 {{- end }} -- home/user/.local/share/chezmoi/home/.chezmoiscripts/run_once_echo.ps1.tmpl -- Write-Host .chezmoi.sourceDir={{ .chezmoi.sourceDir }} Write-Host CHEZMOI_SOURCE_DIR=$Env:CHEZMOI_SOURCE_DIR -- home/user/.local/share/chezmoi/home/.chezmoiscripts/run_once_echo.sh.tmpl -- #!/bin/sh echo .chezmoi.sourceDir={{ .chezmoi.sourceDir }} echo CHEZMOI_SOURCE_DIR=${CHEZMOI_SOURCE_DIR} ================================================ FILE: internal/cmd/testdata/scripts/issue2934.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi sets environment variables for modify_ scripts exec chezmoi apply $HOME${/}.modify grep ^CHEZMOI_SOURCE_DIR=${CHEZMOISOURCEDIR@R}$ $HOME/.modify grep ^CHEZMOI_SOURCE_FILE=modify_dot_modify$ $HOME/.modify chhome home2/user # test that CHEZMOI_SOURCE_FILE environment variable is set when running scripts exec chezmoi apply $HOME${/}script.sh stdout ^CHEZMOI_SOURCE_DIR=${CHEZMOISOURCEDIR@R}$ stdout ^CHEZMOI_SOURCE_FILE=run_script.sh$ -- home/user/.local/share/chezmoi/modify_dot_modify -- #!/bin/sh echo CHEZMOI_SOURCE_DIR=${CHEZMOI_SOURCE_DIR} echo CHEZMOI_SOURCE_FILE=${CHEZMOI_SOURCE_FILE} -- home2/user/.local/share/chezmoi/run_script.sh -- #!/bin/sh echo CHEZMOI_SOURCE_DIR=${CHEZMOI_SOURCE_DIR} echo CHEZMOI_SOURCE_FILE=${CHEZMOI_SOURCE_FILE} ================================================ FILE: internal/cmd/testdata/scripts/issue2937.txtar ================================================ # test that .chezmoiignore prevents git-repo externals from being downloaded exec chezmoi apply ! exists .dir chhome home2/user # test that .chezmoiignore prevents external archives from being downloaded exec chezmoi apply ! exists .dir -- home/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".dir"] type = "archive" url = "https://example.com/does-not-exist.tar.gz" -- home/user/.local/share/chezmoi/.chezmoiignore -- .dir .dir/** -- home2/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".dir"] type = "git-repo" url = "https://example.com/user/repo.git" -- home2/user/.local/share/chezmoi/.chezmoiignore -- .dir .dir/** ================================================ FILE: internal/cmd/testdata/scripts/issue2942.txtar ================================================ # test that .chezmoi.config variables are capitalized correctly exec chezmoi execute-template '{{ .chezmoi.config.keepassxc.command }}' stdout '^keepassxc-cli$' ================================================ FILE: internal/cmd/testdata/scripts/issue2954.txtar ================================================ # test that chezmoi destroy does not delete the source directory when removing a file in an exact directory mkdir $HOME/.dir mkfile $HOME/.dir/test.file exec chezmoi destroy --force $HOME${/}.dir/test.file exists $CHEZMOISOURCEDIR -- home/user/.local/share/chezmoi/exact_dot_dir/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue2964.txtar ================================================ [windows] skip 'UNIX only' chmod 755 $CHEZMOISOURCEDIR/external_dot_dir/executable # test that external_ directories respect the executable bit exec chezmoi apply [umask:002] cmpmod 775 $HOME/.dir/executable [umask:022] cmpmod 755 $HOME/.dir/executable -- home/user/.local/share/chezmoi/external_dot_dir/executable -- # contents of .dir/executable ================================================ FILE: internal/cmd/testdata/scripts/issue2977.txtar ================================================ # test that chezmoi source-path returns an error for file to be removed from exact directories mkdir $HOME/.dir mkfile $HOME/.dir/test.file ! exec chezmoi source-path $HOME/.dir/test.file stderr 'not in source state' -- home/user/.local/share/chezmoi/exact_dot_dir/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue2995.txtar ================================================ # test that chezmoi apply only creates empty files if they have the empty_ attribute exec chezmoi apply ! exists $HOME/new.txt exists $HOME/create-new.txt cmp $HOME/existing.txt golden/existing.txt cmp $HOME/existing-empty.txt golden/existing-empty.txt -- golden/existing-empty.txt -- -- golden/existing.txt -- # contents of existing.txt -- home/user/.local/share/chezmoi/create_empty_create-new.txt -- -- home/user/.local/share/chezmoi/create_empty_existing-empty.txt -- -- home/user/.local/share/chezmoi/create_existing.txt -- -- home/user/.local/share/chezmoi/create_new.txt -- -- home/user/existing-empty.txt -- -- home/user/existing.txt -- # contents of existing.txt ================================================ FILE: internal/cmd/testdata/scripts/issue3005.txtar ================================================ # test that chezmoi --debug init does not fail exec chezmoi --debug init ================================================ FILE: internal/cmd/testdata/scripts/issue3008.txtar ================================================ # test that chezmoi apply does not get confused by multiple files in .chezmoidata exec chezmoi apply -- home/user/.local/share/chezmoi/.chezmoidata/a.yaml -- a: 1 -- home/user/.local/share/chezmoi/.chezmoidata/b.yaml -- b: 2 ================================================ FILE: internal/cmd/testdata/scripts/issue3051.txtar ================================================ # test that chezmoi add respects .chezmoiignore in the presence of protected paths exec chezmoi add -r $HOME${/}.local exists $CHEZMOISOURCEDIR/dot_local/bin/hello.sh -- home/user/.local/bin/hello.sh -- #!/bin/sh -- home/user/.local/share/chezmoi/.chezmoiignore -- .local/share/chezmoi .local/share/chezmoi/** ================================================ FILE: internal/cmd/testdata/scripts/issue3064.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi respects the persistentState config variable exec chezmoi apply exists test.boltdb -- home/user/.config/chezmoi/chezmoi.yaml -- persistentState: test.boltdb -- home/user/.local/share/chezmoi/run_once_script.sh -- #!/bin/sh ================================================ FILE: internal/cmd/testdata/scripts/issue3113.txtar ================================================ [windows] skip 'UNIX only' chmod 777 bin/custom-pager chmod 777 bin/default-pager # test that chezmoi diff uses the default pager by default exec chezmoi diff stdout default-pager chhome home2/user # test that setting diff.pager to a custom pager uses that pager exec chezmoi diff stdout custom-pager chhome home3/user # test that setting diff.pager to the empty string disables the pager exec chezmoi diff stdout diff -- bin/custom-pager -- #!/bin/sh echo custom-pager -- bin/default-pager -- #!/bin/sh echo default-pager -- home/user/.config/chezmoi/chezmoi.yaml -- pager: default-pager -- home/user/.local/share/chezmoi/dot_file -- # contents of .file -- home2/user/.config/chezmoi/chezmoi.yaml -- diff: pager: custom-pager -- home2/user/.local/share/chezmoi/dot_file -- # contents of .file -- home3/user/.config/chezmoi/chezmoi.yaml -- pager: default-pager diff: pager: '' -- home3/user/.local/share/chezmoi/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue3126.txtar ================================================ # test that template executions are independent exec chezmoi apply ! grep bar $HOME/file1 ! grep foo $HOME/file2 -- home/user/.local/share/chezmoi/file1.tmpl -- {{ merge . (dict "key1" "foo") }} -- home/user/.local/share/chezmoi/file2.tmpl -- {{ merge . (dict "key2" "bar") }} ================================================ FILE: internal/cmd/testdata/scripts/issue3127.txtar ================================================ mkdir $CHEZMOISOURCEDIR # test that chezmoi --config=path init --config-path=path writes the initial config into path cp golden/config1.toml $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl exec chezmoi --config=$HOME/config/athome.toml init --config-path=$HOME/config/athome.toml cmp $HOME/config/athome.toml golden/config1.toml # test that chezmoi --config=path init --config-path=path writes an updated config into path cp golden/config2.toml $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl exec chezmoi --config=$HOME/config/athome.toml init --config-path=$HOME/config/athome.toml cmp $HOME/config/athome.toml golden/config2.toml # test that chezmoi --config=path init writes an updated config into path cp golden/config3.toml $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl exec chezmoi --config=$HOME/config/athome.toml init cmp $HOME/config/athome.toml golden/config3.toml -- golden/config1.toml -- [data] email = "mail1@example.com" -- golden/config2.toml -- [data] email = "mail2@example.com" -- golden/config3.toml -- [data] email = "mail3@example.com" ================================================ FILE: internal/cmd/testdata/scripts/issue3163.txtar ================================================ # test that chezmoi supports separate files via templates exec chezmoi apply cmp $HOME/.file1 golden/file1 cmp $HOME/.file2 golden/file2 -- golden/file1 -- # contents of .file1 -- golden/file2 -- # contents of .file2 -- home/user/.local/share/chezmoi/.chezmoitemplates/file2.tmpl -- # {{ . }} -- home/user/.local/share/chezmoi/.file1 -- # contents of .file1 -- home/user/.local/share/chezmoi/dot_file1.tmpl -- {{- include ".file1" -}} -- home/user/.local/share/chezmoi/dot_file2.tmpl -- {{- template "file2.tmpl" "contents of .file2" -}} ================================================ FILE: internal/cmd/testdata/scripts/issue3206.txtar ================================================ # test that chezmoi apply sees .chezmoidata files in a subdirectory when a .chezmoiignore file is present exec chezmoi apply --debug cmp $HOME/.config/expanso/match/greek.yml golden/greek.yml -- golden/greek.yml -- # `propagate_case` allows e.g.: # alpha^ => α # ALPHA^ => Α [or Alpha^ since it's just 1 char] matches: - trigger: "alpha^" replace: "α" propagate_case: true - trigger: "beta^" replace: "β" propagate_case: true - trigger: "chi^" replace: "χ" propagate_case: true - trigger: "delta^" replace: "δ" propagate_case: true - trigger: "epsilon^" replace: "ε" propagate_case: true - trigger: "eta^" replace: "η" propagate_case: true - trigger: "gamma^" replace: "γ" propagate_case: true - trigger: "iota^" replace: "ι" propagate_case: true - trigger: "kappa^" replace: "κ" propagate_case: true - trigger: "lambda^" replace: "λ" propagate_case: true - trigger: "mu^" replace: "μ" propagate_case: true - trigger: "nu^" replace: "ν" propagate_case: true - trigger: "omega^" replace: "ω" propagate_case: true - trigger: "omicron^" replace: "ο" propagate_case: true - trigger: "phi^" replace: "φ" propagate_case: true - trigger: "pi^" replace: "π" propagate_case: true - trigger: "psi^" replace: "ψ" propagate_case: true - trigger: "rho^" replace: "ρ" propagate_case: true - trigger: "sigma^" replace: "σ" propagate_case: true - trigger: "tau^" replace: "τ" propagate_case: true - trigger: "theta^" replace: "θ" propagate_case: true - trigger: "upsilon^" replace: "υ" propagate_case: true - trigger: "xi^" replace: "ξ" propagate_case: true - trigger: "zeta^" replace: "ζ" propagate_case: true -- home/user/.local/share/chezmoi/.chezmoiignore -- -- home/user/.local/share/chezmoi/dot_config/private_expanso/match/.chezmoidata.yaml -- greek_alphabet: {alpha: α, beta: β, chi: χ, delta: δ, epsilon: ε, eta: η, gamma: γ, iota: ι, kappa: κ, lambda: λ, mu: μ, nu: ν, omega: ω, omicron: ο, phi: φ, pi: π, psi: ψ, rho: ρ, sigma: σ, tau: τ, theta: θ, upsilon: υ, xi: ξ, zeta: ζ} -- home/user/.local/share/chezmoi/dot_config/private_expanso/match/greek.yml.tmpl -- # `propagate_case` allows e.g.: # alpha^ => α # ALPHA^ => Α [or Alpha^ since it's just 1 char] matches: {{- range $trigger, $replace := .greek_alphabet }} - trigger: "{{ $trigger }}^" replace: "{{ $replace }}" propagate_case: true {{- end }} ================================================ FILE: internal/cmd/testdata/scripts/issue3232.txtar ================================================ exec tar czf www/archive1.tar.gz archive1 exec tar czf www/archive2.tar.gz archive2 httpd www # test that chezmoi externals allow multiple non-conflicting archives exec chezmoi apply cmp $HOME/.dir/file1 archive1/file1 cmp $HOME/.dir/file2 archive2/file2 chhome home2/user # test that chezmoi externals do not allow multiple conflicting archives ! exec chezmoi apply stderr 'inconsistent state' chhome home3/user # test that chezmoi externals do not allow multiple conflicting archives with different, equivalent, paths ! exec chezmoi apply stderr 'inconsistent state' -- archive1/file1 -- # contents of file1 -- archive2/file2 -- # contents of file2 -- home/user/.local/share/chezmoi/.chezmoiexternals/archive1.toml.tmpl -- [".dir"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive1.tar.gz" stripComponents = 1 -- home/user/.local/share/chezmoi/.chezmoiexternals/archive2.toml.tmpl -- [".dir"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive2.tar.gz" stripComponents = 1 -- home2/user/.local/share/chezmoi/.chezmoiexternals/archive1.toml.tmpl -- [".dir"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive1.tar.gz" stripComponents = 1 -- home2/user/.local/share/chezmoi/.chezmoiexternals/archive2.toml.tmpl -- [".dir"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive1.tar.gz" stripComponents = 1 -- home3/user/.local/share/chezmoi/.chezmoiexternals/archive1.toml.tmpl -- [".dir"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive1.tar.gz" stripComponents = 1 -- home3/user/.local/share/chezmoi/.chezmoiexternals/archive2.toml.tmpl -- [".dir/"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive1.tar.gz" stripComponents = 1 -- www/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue3240.txtar ================================================ [windows] skip 'UNIX only' chmod 777 bin/custom-pager # test that chezmoi diff uses the custom pager if set exec chezmoi diff cmp stdout golden/stdout # test that chezmoi diff --verbose has identical output exec chezmoi diff --verbose cmp stdout golden/stdout # test that chezmoi apply uses the custom pager if set exec chezmoi apply --dry-run --verbose stdout custom-pager -- bin/custom-pager -- #!/bin/sh echo custom-pager -- golden/stdout -- custom-pager -- home/user/.config/chezmoi/chezmoi.toml -- pager = "custom-pager" -- home/user/.local/share/chezmoi/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue3257.txtar ================================================ [windows] skip 'UNIX only' chmod 755 bin/custom-pager # test that chezmoi add invokes the pager when verbose is set exec chezmoi add $HOME${/}.file stdout custom-pager # test chat chezmoi chattr invokes the pager when verbose is set exec chezmoi chattr +private $HOME${/}.file stdout custom-pager # test that chezmoi status does not invoke the pager when verbose is set exec chezmoi status ! stdout custom-pager -- bin/custom-pager -- #!/bin/sh echo custom-pager -- home/user/.config/chezmoi/chezmoi.yaml -- pager: custom-pager verbose: true -- home/user/.file -- # contents of .file -- home/user/.local/share/chezmoi/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue3268.txtar ================================================ [unix] chmod 755 bin/chezmoi-test-command # test that chezmoi sets CHEZMOI_ environment variables exec chezmoi execute-template '{{ output "chezmoi-test-command" }}' stdout 'CHEZMOI_SOURCE_DIR=.*/\.local/share/chezmoi\s?$' chhome home2/user # test that chezmoi sets environment variables from env exec chezmoi execute-template '{{ env "VAR" }}' stdout VALUE chhome home3/user # test that env and scriptEnv cannot both be set ! exec chezmoi execute-template '' stderr 'only one of env or scriptEnv may be set' -- bin/chezmoi-test-command -- #!/bin/sh echo CHEZMOI_SOURCE_DIR=${CHEZMOI_SOURCE_DIR} -- bin/chezmoi-test-command.cmd -- @echo CHEZMOI_SOURCE_DIR=%CHEZMOI_SOURCE_DIR% -- home2/user/.config/chezmoi/chezmoi.json -- { "env": { "VAR": "VALUE" } } -- home3/user/.config/chezmoi/chezmoi.yaml -- env: VAR: VALUE scriptEnv: VAR: VALUE ================================================ FILE: internal/cmd/testdata/scripts/issue3325.txtar ================================================ # test that integer types are preserved in fromJson | toToml template function pipelines exec chezmoi execute-template '{{ "{\"key\":1}" | fromJson | toToml }}' cmp stdout golden/stdout # test that integer types are preserved in the .data section of JSONC config files exec chezmoi execute-template '{{ .data | toToml }}' cmp stdout golden/config.toml # test that integer and floating point types are preserved from .chezmoidata.json files exec chezmoi execute-template '{{ .json | toToml }}' cmp stdout golden/json.toml # test that integer and floating point types are preserved from .chezmoidata.jsonc files exec chezmoi execute-template '{{ .jsonc | toToml }}' cmp stdout golden/jsonc.toml -- golden/config.toml -- dataFloat64 = 1.1 dataInt64 = 2 -- golden/json.toml -- jsonFloat64 = 3.3 jsonInt64 = 4 -- golden/jsonc.toml -- jsoncFloat64 = 5.5 jsoncInt64 = 6 -- golden/stdout -- key = 1 -- home/user/.config/chezmoi/chezmoi.jsonc -- { // Comment "data": { "data": { "dataFloat64": 1.1, "dataInt64": 2, } } } -- home/user/.local/share/chezmoi/.chezmoidata.json -- { "json": { "jsonFloat64": 3.3, "jsonInt64": 4 } } -- home/user/.local/share/chezmoi/.chezmoidata.jsonc -- { // Comment "jsonc": { "jsoncFloat64": 5.5, "jsoncInt64": 6, } } ================================================ FILE: internal/cmd/testdata/scripts/issue3344.txtar ================================================ [!exec:git] skip 'git not found in $PATH' mkgitconfig exec chezmoi init # test that chezmoi generate git-commit-message warns about untracked files cp golden/.file $CHEZMOISOURCEDIR exec chezmoi generate git-commit-message stderr 'chezmoi: warning: untracked files' -- golden/.file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue3349.txtar ================================================ symlink $CHEZMOISOURCEDIR/home/dot_symlink_to_file -> dot_file symlink $CHEZMOISOURCEDIR/home/dot_symlink_to_dir -> dot_dir # test that chezmoi apply follows symlinks to files but not symlinks to directories exec chezmoi apply cmp $HOME/.file golden/.file cmp $HOME/.symlink_to_file golden/.file cmp $HOME/.dir/file golden/.dir/file isdir $HOME/.symlink_to_dir ! exists $HOME/.symlink_to_dir/file -- golden/.dir/file -- # contents of .dir/file -- golden/.file -- # contents of .file -- home/user/.local/share/chezmoi/.chezmoiroot -- home -- home/user/.local/share/chezmoi/home/dot_dir/file -- # contents of .dir/file -- home/user/.local/share/chezmoi/home/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue3371.txtar ================================================ [!exec:git] skip 'git not found in $PATH' [windows] unix2dos golden/.file mkgitconfig # create a dotfile repo with a config file template exec chezmoi init exec chezmoi git add . exec chezmoi git commit -- --message 'Initial commit' chhome home2/user # test that chezmoi init --apply sets environment variables during apply from the config file exec chezmoi init --apply file://$WORK/home/user/.local/share/chezmoi cmp $HOME/.file golden/.file -- golden/.file -- TEST_CONFIG_VAR=test_config_value -- home/user/.local/share/chezmoi/.chezmoi.yaml.tmpl -- env: TEST_CONFIG_VAR: test_config_value -- home/user/.local/share/chezmoi/dot_file.tmpl -- TEST_CONFIG_VAR={{ env "TEST_CONFIG_VAR" }} ================================================ FILE: internal/cmd/testdata/scripts/issue3374.txtar ================================================ # test that chezmoi copes with extra slashes in path arguments exec chezmoi cat $HOME//.file cmp stdout golden/.file -- golden/.file -- # contents of .file -- home/user/.local/share/chezmoi/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue3414.txtar ================================================ exec tar czf www/archive.tar.gz archive httpd www # test that running chezmoi apply twice does not complain about modified files exec chezmoi apply exec chezmoi apply --no-tty -- archive/shell/completion.bash -- # contents of shell/completion.bash -- archive/shell/key-bindings.bash -- # contents of shell/key-bindings.bash -- home/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".bashrc.d/additional/fzf/shell/completion.bash"] type = "archive-file" path = "shell/completion.bash" url = "{{ env "HTTPD_URL" }}/archive.tar.gz" stripComponents = 1 [".bashrc.d/additional/fzf/shell/key-bindings.bash"] type = "archive-file" path = "shell/key-bindings.bash" url = "{{ env "HTTPD_URL" }}/archive.tar.gz" stripComponents = 1 -- home/user/.local/share/chezmoi/exact_dot_bashrc.d/file.sh -- # contents of .bashrc.d/file.sh -- www/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue3415.txtar ================================================ # test that chezmoi apply does not remove remove_ directories if they are not empty exec chezmoi apply exists $HOME/.dir/file # test that chezmoi apply does remove remove_ directories if they are empty rm $HOME/.dir/file exec chezmoi apply ! exists $HOME/.dir chhome home2/user # test that chezmoi apply does not remove remove_ directories if they are not empty, recursively exec chezmoi apply exists $HOME/.dir/subdir/file # test that chezmoi apply does remove remove_ directories if they are empty, recursively rm $HOME/.dir/subdir/file exec chezmoi apply ! exists $HOME/.dir -- home/user/.dir/file -- # contents of .dir/file -- home/user/.local/share/chezmoi/remove_dot_dir/.keep -- -- home2/user/.dir/subdir/file -- # contents of .dir/subdir/file -- home2/user/.local/share/chezmoi/remove_dot_dir/remove_subdir/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue3418.txtar ================================================ # test that chezmoi execute-template --init does read .chezmoitemplates stdin $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl exec chezmoi execute-template stdout 'contents of template' # test that chezmoi execute-template --init does not read .chezmoitemplates stdin $CHEZMOISOURCEDIR/.chezmoi.toml.tmpl ! exec chezmoi execute-template --init ! stdout 'contents of template' -- home/user/.local/share/chezmoi/.chezmoi.toml.tmpl -- {{ template "template" }} -- home/user/.local/share/chezmoi/.chezmoitemplates/template -- # contents of template ================================================ FILE: internal/cmd/testdata/scripts/issue3421.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi runs run_once_ and run_onchange_ scripts only once exec chezmoi apply stdout once stdout onchange exec chezmoi apply ! stdout . # test that chezmoi runs run_once_ scripts after chezmoi state delete-bucket --bucket=scriptState exec chezmoi state delete-bucket --bucket=scriptState exec chezmoi apply stdout once ! stdout onchange # test that chezmoi runs run_once_ scripts after chezmoi state delete-bucket --bucket=entryState exec chezmoi state delete-bucket --bucket=entryState exec chezmoi apply ! stdout once stdout onchange -- home/user/.local/share/chezmoi/run_once_once.sh -- #!/bin/sh echo once -- home/user/.local/share/chezmoi/run_onchange_onchange.sh -- #!/bin/sh echo onchange ================================================ FILE: internal/cmd/testdata/scripts/issue3510.txtar ================================================ [windows] skip 'UNIX only' expandenv $CHEZMOISOURCEDIR/.chezmoiexternal.toml # test that chezmoi apply does not cache the absence of git in $PATH at startup exec chezmoi apply stdout 'using newly-installed git' -- golden/git -- #!/bin/sh echo "using newly-installed git" -- home/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".dir"] type = "git-repo" url = "file://$WORK/repo" -- home/user/.local/share/chezmoi/run_once_before_install-git.sh -- #!/bin/sh mkdir -p $WORK/bin install -m 755 $WORK/golden/git $WORK/bin ================================================ FILE: internal/cmd/testdata/scripts/issue3525.txtar ================================================ # test that chezmoi add does not add files in external_ directories exec chezmoi add $HOME${/}.external/file stderr '.external: skipping entries in external_ directory' # test that chezmoi add does not add files in subdirectories of external_ directories exec chezmoi add $HOME${/}.external/dir/file stderr '.external: skipping entries in external_ directory' -- home/user/.external/dir/file -- # contents of .external/dir/file -- home/user/.external/file -- # contents of .external/file -- home/user/.local/share/chezmoi/external_dot_external/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue3582.txtar ================================================ # test that chezmoi data shows data read from TOML config files exec chezmoi data --format=json stdout '"mode": "file"' stdout '"pager": "my-pager"' stdout '"pager": "my-diff-pager"' stdout '"identity": ".*/my-age-identity"' -- home/user/.config/chezmoi/chezmoi.toml -- encryption = "age" pager = "my-pager" [diff] pager = "my-diff-pager" [age] identity = "my-age-identity" ================================================ FILE: internal/cmd/testdata/scripts/issue3590.txtar ================================================ # test that chezmoi data does not include default sentinels exec chezmoi data ! stdout '"\\u0000"' ================================================ FILE: internal/cmd/testdata/scripts/issue3602.txtar ================================================ # test that the .chezmoi.config.destDir template variable is compatible with the replace template function exec chezmoi execute-template '{{ .chezmoi.config.destDir | replace "abc" "def "}}' ================================================ FILE: internal/cmd/testdata/scripts/issue3630.txtar ================================================ # test that chezmoi add does not traverse into ignored directories chmod 000 $HOME/.dir/private exec chezmoi add $HOME${/}.dir stderr 'warning: ignoring .dir/private' cmp $CHEZMOISOURCEDIR/dot_dir/public/file golden/file -- golden/file -- # contents of .dir/public/file -- home/user/.dir/private/file -- # contents of .dir/private/file -- home/user/.dir/public/file -- # contents of .dir/public/file -- home/user/.local/share/chezmoi/.chezmoiignore -- .dir/private ================================================ FILE: internal/cmd/testdata/scripts/issue3652.txtar ================================================ # test that chezmoi add skips files in external_ directories exec chezmoi apply exists $HOME/.dir/submodule/.git/.keep exec chezmoi add $HOME${/}.dir stderr '.dir/submodule: skipping entries in external_ directory' cmp $CHEZMOISOURCEDIR/dot_dir/file golden/file -- golden/file -- # contents of file -- home/user/.dir/file -- # contents of file -- home/user/.local/share/chezmoi/dot_dir/external_submodule/.git/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue3666.txtar ================================================ # test that chezmoi add creates parent directories exec chezmoi add $HOME${/}.config/helix/themes/ayu_custom.toml exists $CHEZMOISOURCEDIR/dot_config/exact_helix/themes/ayu_custom.toml -- home/user/.config/helix/themes/ayu_custom.toml -- # contents of ayu_custom.toml -- home/user/.local/share/chezmoi/dot_config/exact_helix/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue3693.txtar ================================================ # test that chezmoi apply does not panic when given an external with an empty configuration ! exec chezmoi apply stderr 'missing external type' -- home/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".config"] ================================================ FILE: internal/cmd/testdata/scripts/issue3703.txtar ================================================ httpd www # test that chezmoi apply removes unmanaged files from an exact_ directory containing an external exists $HOME/.local/bin/unmanaged exec chezmoi apply --debug cmp $HOME/.local/bin/file www/file ! exists $HOME/.local/bin/unmanaged -- home/user/.local/bin/unmanaged -- -- home/user/.local/share/chezmoi/.chezmoiexternal.toml.tmpl -- [".local/bin/file"] type = "file" url = "{{ env "HTTPD_URL" }}/file" -- home/user/.local/share/chezmoi/dot_local/exact_bin/.keep -- -- www/file -- # contents of file ================================================ FILE: internal/cmd/testdata/scripts/issue3744.txtar ================================================ [!exec:git] skip 'git not found in $PATH' mkgitconfig # create a repo exec git init --bare $WORK/dotfiles.git exec chezmoi init file://$WORK/dotfiles.git # test that chezmoi add creates and pushes a commit with a custom message template stdin golden/stdin exec chezmoi add --no-tty $HOME${/}.file exec git --git-dir=$WORK/dotfiles.git show HEAD stdout 'Custom commit message' -- golden/stdin -- Custom commit message -- home/user/.config/chezmoi/chezmoi.toml -- [git] autoPush = true commitMessageTemplate = "{{ promptString \"message\" }}" -- home/user/.file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue3772.txtar ================================================ # test that chezmoi add adds a symlink to a file symlink $HOME/a -> test/a exec chezmoi add $HOME/a cmp $CHEZMOISOURCEDIR/symlink_a golden/symlink_a # test that chezmoi add --follow adds a file without the symlink symlink $HOME/b -> test/b exec chezmoi add --follow $HOME/b cmp $CHEZMOISOURCEDIR/b golden/b # test that chezmoi add --follow returns an error when adding a symlink to a directory symlink $HOME/abc -> test ! exec chezmoi add --follow $HOME/abc stderr 'follow and recursive are mutually exclusive for directories' -- golden/b -- # contents of test/b -- golden/symlink_a -- test/a -- home/user/test/a -- # contents of test/a -- home/user/test/b -- # contents of test/b ================================================ FILE: internal/cmd/testdata/scripts/issue3887.txtar ================================================ [!exec:age] skip 'age not found in path' mkageconfig mkgitconfig # add an initial encrypted file exec chezmoi init exec chezmoi add --encrypt ${HOME}${/}.encrypted exec chezmoi git add . exec chezmoi git commit -- -m 'initial commit' . # test that chezmoi edit on an encrypted file with no changes does not change the ciphertext prependline ${CHEZMOICONFIGDIR}/chezmoi.toml 'edit.command = "true"' exec chezmoi edit ${HOME}${/}.encrypted exec chezmoi diff ! stdout . exec chezmoi git diff ! stdout . -- home/user/.encrypted -- plaintext ================================================ FILE: internal/cmd/testdata/scripts/issue3891.txtar ================================================ [!windows] skip 'Windows only' # test that chezmoi re-add does not remove the executable_ attribute on Windows exec chezmoi apply exec chezmoi re-add --debug exists $CHEZMOISOURCEDIR/executable_run.sh ! exists $CHEZMOISOURCEDIR/run.sh -- home/user/.local/share/chezmoi/executable_run.sh -- #!/bin/sh ================================================ FILE: internal/cmd/testdata/scripts/issue3987.txtar ================================================ # test that chezmoi apply --parent-dirs creates parent directories exec chezmoi apply --parent-dirs $HOME/.config/nvim/after/queries/gotmpl/injections.scm exists $HOME/.config/nvim/after/queries/gotmpl/injections.scm -- home/user/.local/share/chezmoi/private_dot_config/nvim/after/queries/gotmpl/injections.scm -- # contents of injections.scm ================================================ FILE: internal/cmd/testdata/scripts/issue4002.txtar ================================================ [windows] skip 'windows line endings confuse cmp and diff' [!exec:git] skip 'git not found in $PATH' mkgitconfig # test that includeTemplate works with a file in .chezmoitemplates with a .chezmoiroot when re-initializing exec chezmoi init cmp ${CHEZMOICONFIGDIR}/chezmoi.toml golden/chezmoi.toml # create a commit exec chezmoi git add . exec chezmoi git commit -- --message 'Initial commit' chhome home2/user # test that includeTemplate works with a file in .chezmoitemplates with a .chezmoiroot with a bare init exec chezmoi init --apply --force file://$WORK/home/user/.local/share/chezmoi cmp ${CHEZMOICONFIGDIR}/chezmoi.toml golden/chezmoi.toml -- golden/chezmoi.toml -- # contents of template -- home/user/.local/share/chezmoi/.chezmoiroot -- home -- home/user/.local/share/chezmoi/home/.chezmoi.toml.tmpl -- {{- includeTemplate "template" -}} -- home/user/.local/share/chezmoi/home/.chezmoitemplates/template -- # contents of template ================================================ FILE: internal/cmd/testdata/scripts/issue4012.txtar ================================================ # test that chezmoi chattr +encrypted prints an error message when run on an external file ! exec chezmoi chattr +encrypted $HOME${/}.file stderr 'is an external' -- home/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".file"] type = "file" url = "https://example.com/.file" ================================================ FILE: internal/cmd/testdata/scripts/issue4024.txtar ================================================ # test that chezmoi merge-all does not generate incorrect "config file template has changed" warnings exec chezmoi init cp ${CHEZMOICONFIGDIR}/chezmoi.toml golden/chezmoi.toml ! stderr . exec chezmoi state get-bucket --bucket=configState cmp stdout golden/state exec chezmoi merge-all ! stderr . -- golden/chezmoi.toml -- [merge] command = "bash" args = [ '-c', 'cp "$1" "$2" && code --new-window --wait --merge "$0" "$1" "$2" "$3"', '{{ .Destination }}', '{{ .Target }}', '{{ .Target }}.base', '{{ .Source }}', ] -- golden/state -- { "configState": { "configTemplateContentsSHA256": "17237711aec7ba710379c223819293b9a9e039d9a4a3ca721259c2337f21fcfe" } } -- home/user/.local/share/chezmoi/.chezmoi.toml.tmpl -- [merge] command = "bash" args = [ '-c', 'cp "$1" "$2" && code --new-window --wait --merge "$0" "$1" "$2" "$3"', '{{ "{{ .Destination }}" }}', '{{ "{{ .Target }}" }}', '{{ "{{ .Target }}" }}.base', '{{ "{{ .Source }}" }}', ] ================================================ FILE: internal/cmd/testdata/scripts/issue4027.txtar ================================================ # test that chezmoi diff --keep-going does not panic when the diff pager command does not exist ! exec chezmoi diff --keep-going ! stderr panic [!windows] stderr '/not/exist: no such file or directory' [windows] stderr '"/not/exist": executable file not found in %PATH%' -- home/user/.config/chezmoi/chezmoi.toml -- [diff] pager = "/not/exist" -- home/user/.local/share/chezmoi/dot_file1 -- # contents of .file1 -- home/user/.local/share/chezmoi/dot_file2 -- # contents of .file2 ================================================ FILE: internal/cmd/testdata/scripts/issue4104.txtar ================================================ # test that chezmoi add --secrets=ignore succeeds when the add.secrets config variable is set to error exec chezmoi add --secrets=ignore $HOME${/}.secret ! stderr . -- home/user/.config/chezmoi/chezmoi.toml -- [add] secrets = "error" -- home/user/.secret -- AWS_ACCESS_KEY_ID=AKIA0123456789ABCDEF ================================================ FILE: internal/cmd/testdata/scripts/issue4181.txtar ================================================ # test that chezmoi doctor finds the specified config file exec chezmoi --config $HOME${/}.second/che.yaml doctor stdout 'ok\s+config-file\s+found ~/\.second/che\.yaml' -- home/user/.config/chezmoi/chezmoi.yaml -- -- home/user/.local/share/chezmoi/.keep -- -- home/user/.second/che.yaml -- ================================================ FILE: internal/cmd/testdata/scripts/issue4315.txtar ================================================ [windows] skip 'UNIX only' exec chmod a+x bin/prog # test that templates are only executed once exec chezmoi apply cmp $HOME/.file golden/.file cmp count golden/count -- bin/prog -- #!/bin/sh echo a >> count echo 1 -- golden/.file -- 1 -- golden/count -- a -- home/user/.local/share/chezmoi/dot_file.tmpl -- {{- output "prog" -}} ================================================ FILE: internal/cmd/testdata/scripts/issue4454.txtar ================================================ # test that chezmoi diff ignores errors from the custom diff command when there are differences in files exec chezmoi diff chhome home2/user # test that chezmoi diff ignores errors from the custom diff command when there are differences in directories exec chezmoi diff chhome home3/user # test that chezmoi diff ignores errors from the custom diff command when there are differences in symlinks exec chezmoi diff -- bin/false.cmd -- @exit 1 -- home/user/.config/chezmoi/chezmoi.toml -- [diff] command = "false" -- home/user/.local/share/chezmoi/dot_file -- # contents of .file -- home2/user/.config/chezmoi/chezmoi.toml -- [diff] command = "false" -- home2/user/.local/share/chezmoi/dot_dir/.keep -- -- home3/user/.config/chezmoi/chezmoi.toml -- [diff] command = "false" -- home3/user/.local/share/chezmoi/symlink_symlink -- target ================================================ FILE: internal/cmd/testdata/scripts/issue4479.txtar ================================================ [!exec:git] skip 'git not found in $PATH' mkgitconfig exec git -C $CHEZMOISOURCEDIR init exec git -C $CHEZMOISOURCEDIR add . exec git -C $CHEZMOISOURCEDIR commit -m 'initial commit' # test that autoCommit does not add or commit files outside the source directory edit $CHEZMOISOURCEDIR/README.md exec chezmoi edit ~/.file exec git -C $CHEZMOISOURCEDIR status stdout 'modified:\s+README\.md' -- home/user/.config/chezmoi/chezmoi.toml -- [git] autoCommit = true -- home/user/.local/share/chezmoi/.chezmoiroot -- home -- home/user/.local/share/chezmoi/README.md -- # README.md -- home/user/.local/share/chezmoi/home/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/issue4496.txtar ================================================ hexdecode www/whitespace.txt.hex exec tar -cf www/archive.tar www/whitespace.txt httpd www # test that externals files containing only whitespace are preserved exec chezmoi apply cmp $HOME/.whitespace www/whitespace.txt cmp $HOME/.dir/whitespace.txt www/whitespace.txt -- home/user/.local/share/chezmoi/.chezmoiexternal.toml.tmpl -- [".whitespace"] type = "file" url = "{{ env "HTTPD_URL" }}/whitespace.txt" [".dir"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive.tar" stripComponents = 1 -- www/whitespace.txt.hex -- 0d0a # "\r\n" ================================================ FILE: internal/cmd/testdata/scripts/issue4500.txtar ================================================ [windows] skip 'UNIX only' [!exec:diff] skip 'diff not found in $PATH' chmod 755 bin/custom-pager # test that chezmoi diff does invoke the diff pager when there is a diff and the command is not "diff" cp golden/dot_file $CHEZMOISOURCEDIR exec chezmoi diff stdout custom-pager chhome home2/user # test that chezmoi diff does invoke the diff pager when there is a diff and the command is "diff" cp golden/dot_file $CHEZMOISOURCEDIR exec chezmoi diff stdout custom-pager -- bin/custom-pager -- #!/bin/sh echo custom-pager -- golden/dot_file -- # contents of .file -- home/user/.config/chezmoi/chezmoi.toml -- [diff] pager = "custom-pager" -- home/user/.local/share/chezmoi/.keep -- -- home2/user/.config/chezmoi/chezmoi.toml -- [diff] command = "diff" pager = "custom-pager" -- home2/user/.local/share/chezmoi/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue4634.txtar ================================================ # test that chezmoi --override-data overrides data exec chezmoi execute-template '{{ .foo }}' stdout ^bar$ exec chezmoi execute-template --override-data '{"foo":"baz"}' '{{ .foo }}' stdout ^baz$ # test that chezmoi --override-data overrides even chezmoi's builtin data exec chezmoi execute-template --override-data '{"chezmoi":{"os":"test"}}' '{{ .chezmoi.os }}' stdout ^test$ # test that chezmoi --override-data-file overrides data from a file exec chezmoi execute-template --override-data-file golden/override.yaml '{{ .foo }}' stdout ^bar$ # that that if both --override-data and --override-data-file are passed then --override-data is preferred exec chezmoi execute-template --override-data '{"foo":"baz"}' --override-data-file golden/override.yaml '{{ .foo }}' stdout ^baz$ -- golden/override.yaml -- foo: bar -- home/user/.config/chezmoi/chezmoi.toml -- [data] foo = "bar" ================================================ FILE: internal/cmd/testdata/scripts/issue4647.txtar ================================================ [!exec:git] skip 'git not found in $PATH' # test that chezmoi init --use-builtin-git=true clones a repository correctly exec chezmoi init --use-builtin-git=true https://github.com/chezmoi/test-dotfiles.git exists $CHEZMOISOURCEDIR/dot_file # undo the last commit exec git -C $CHEZMOISOURCEDIR reset --hard HEAD^ ! exists $CHEZMOISOURCEDIR/dot_file # test that chezmoi update --use-builtin-git=true updates the source directory correctly exec chezmoi update --use-builtin-git=true exists $CHEZMOISOURCEDIR/dot_file # undo the last commit exec git -C $CHEZMOISOURCEDIR reset --hard HEAD^ ! exists $CHEZMOISOURCEDIR/dot_file # modify the source directory cp golden/dot_file2 $CHEZMOISOURCEDIR # test that chezmoi update --use-builtin-git=true updates the source directory correctly exec chezmoi update --use-builtin-git=true exists $CHEZMOISOURCEDIR/dot_file -- golden/dot_file2 -- # contents of .file2 ================================================ FILE: internal/cmd/testdata/scripts/issue4656.txtar ================================================ # test that chezmoi diff shows only three lines of context exec chezmoi diff [umask:002] cmp stdout golden/umask-002.diff [umask:022] cmp stdout golden/umask-022.diff -- golden/umask-002.diff -- diff --git a/.file b/.file index 1cb5bd7432f53fc594236648d89f0c5c4185ba18..0edb85647c20325c18d158529b73bc2105696c97 100664 --- a/.file +++ b/.file @@ -2,6 +2,7 @@ a b c d +e f g h @@ -18,6 +19,7 @@ r s t u +v w x y -- golden/umask-022.diff -- diff --git a/.file b/.file index 1cb5bd7432f53fc594236648d89f0c5c4185ba18..0edb85647c20325c18d158529b73bc2105696c97 100644 --- a/.file +++ b/.file @@ -2,6 +2,7 @@ a b c d +e f g h @@ -18,6 +19,7 @@ r s t u +v w x y -- home/user/.file -- a b c d f g h i j k l m n o p q r s t u w x y z -- home/user/.local/share/chezmoi/dot_file -- a b c d e f g h i j k l m n o p q r s t u v w x y z ================================================ FILE: internal/cmd/testdata/scripts/issue4662.txtar ================================================ [!exec:echo] skip 'echo not found in $PATH' # test that chezmoi init --apply runs apply's pre and post hooks exec chezmoi init --apply cmp stdout golden/init -- golden/init -- pre-apply-hook post-apply-hook -- home/user/.config/chezmoi/chezmoi.toml -- [hooks.apply.pre] command = "echo" args = ["pre-apply-hook"] [hooks.apply.post] command = "echo" args = ["post-apply-hook"] -- home/user/.local/share/chezmoi/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue4703.txtar ================================================ # test that chezmoi init --dry-run does not overwrite the config file exec chezmoi init --dry-run cmp $CHEZMOICONFIGDIR/chezmoi.toml golden/chezmoi.toml -- golden/chezmoi.toml -- # current contents of .config/chezmoi/chezmoi.toml -- home/user/.config/chezmoi/chezmoi.toml -- # current contents of .config/chezmoi/chezmoi.toml -- home/user/.local/share/chezmoi/.chezmoi.toml.tmpl -- # new contents of .config/chezmoi/chezmoi.toml ================================================ FILE: internal/cmd/testdata/scripts/issue4727.txtar ================================================ # test that fromIni | toIni does not mangle strings exec chezmoi execute-template '{{ "a = \"\\\"\"" | fromIni | toIni | fromIni | toIni }}' cmp stdout golden/stdout -- golden/stdout -- a = "\"" ================================================ FILE: internal/cmd/testdata/scripts/issue4743.txtar ================================================ hexdecode home/user/invalid-utf-8.hex hexdecode home/user/boms/utf-16-be.hex # test that chezmoi add --template detects invalid UTF-8 exec chezmoi add --template $HOME${/}invalid-utf-8 stderr 'invalid UTF-8' # test that chezmoi add --template detects byte order marks exec chezmoi add --recursive --template ${HOME}${/}boms stderr 'utf-16-be: detected UTF-16 \(BE\) byte order mark' -- home/user/boms/utf-16-be.hex -- FE FF -- home/user/invalid-utf-8.hex -- C3 28 ================================================ FILE: internal/cmd/testdata/scripts/issue4745.txtar ================================================ mkdir www exec tar czf www/archive.tar.gz archive httpd www # test that chezmoi destroy skips externals and does not delete the source directory exec chezmoi apply exists $HOME/external/file exec chezmoi destroy --force $HOME${/}external stderr 'warning: skipping external external' exists $CHEZMOISOURCEDIR -- archive/file -- # contents of file -- home/user/.local/share/chezmoi/.chezmoiexternal.toml.tmpl -- ["external"] type = "archive" url = "{{ env "HTTPD_URL" }}/archive.tar.gz" stripComponents = 1 ================================================ FILE: internal/cmd/testdata/scripts/issue4787.txtar ================================================ # test that chezmoi forget on a file in an exact_ directory does not delete the source directory exec chezmoi forget --debug --force $HOME${/}.dir${/}file2 stderr 'warning: .dir/file2: ignoring implicitly managed file' exists $CHEZMOISOURCEDIR -- home/user/.dir/file1 -- # contents of .dir/file1 -- home/user/.dir/file2 -- # contents of .dir/file2 -- home/user/.local/share/chezmoi/exact_dot_dir/file1 -- # contents of .dir/file1 ================================================ FILE: internal/cmd/testdata/scripts/issue4796.txtar ================================================ # test that chezmoi execute-template --override-data does not panic when there is no template data exec chezmoi execute-template --override-data '{"foo":"baz"}' '{{ .foo }}' stdout baz ================================================ FILE: internal/cmd/testdata/scripts/issue4816.txtar ================================================ mkageconfig [windows] unix2dos golden/config [windows] unix2dos golden/edited-config mkdir $CHEZMOISOURCEDIR/.chezmoitemplates/dot_ssh # test that chezmoi edit-encrypted works on files in the source directory exec chezmoi encrypt -o $CHEZMOISOURCEDIR/.chezmoitemplates/dot_ssh/config.age golden/config exec chezmoi edit-encrypted $CHEZMOISOURCEDIR/.chezmoitemplates/dot_ssh/config.age exec chezmoi decrypt $CHEZMOISOURCEDIR/.chezmoitemplates/dot_ssh/config.age cmp stdout golden/edited-config -- golden/config -- # contents of config -- golden/edited-config -- # contents of config # edited -- home/user/.local/share/chezmoi/.chezmoitemplates/dot_ssh/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/issue4824.txtar ================================================ # test that chezmoi re-add does not affect templates exec chezmoi apply edit $HOME/dir/a edit $HOME/dir/b exec chezmoi re-add cmp $CHEZMOISOURCEDIR/exact_dir/a golden/edited-a cmp $CHEZMOISOURCEDIR/exact_dir/b.tmpl golden/b.tmpl -- golden/b.tmpl -- # contents of b -- golden/edited-a -- # contents of a # edited -- home/user/.local/share/chezmoi/exact_dir/a -- # contents of a -- home/user/.local/share/chezmoi/exact_dir/b.tmpl -- # contents of b ================================================ FILE: internal/cmd/testdata/scripts/issue4827.txtar ================================================ # test that chezmoi add will add some files with the prefix chezmoi exec chezmoi add $HOME/.local/share/chezmoi.age -- home/user/.local/share/chezmoi.age -- # contents of .local/share/chezmoi.age ================================================ FILE: internal/cmd/testdata/scripts/issue4845.txtar ================================================ # test that chezmoi apply with a file called dot_ in the source directory returns an error ! exec chezmoi apply stderr 'dot_: invalid filename' chhome home2/user # test that chezmoi apply with a directory called literal_ in the source directory returns an error ! exec chezmoi apply stderr 'literal_: invalid directory name' -- home/user/.local/share/chezmoi/dot_ -- -- home2/user/.local/share/chezmoi/literal_/file -- ================================================ FILE: internal/cmd/testdata/scripts/issue4927.txtar ================================================ httpd www # test that chezmoi only downloads externals once if refreshPeriod is zero exec chezmoi apply cmp $HOME/.file golden/.file # sleep is needed to ensure that www/file has a different mtime after being edited # HTTP's Last-Modified headers have only single second resolution sleep 1s edit www/file exec chezmoi apply cmp $HOME/.file golden/.file # test that chezmoi apply re-downloads the file if the cache is cleared rmdir $CHEZMOICACHEDIR modifyfile '[0-9a-f]{64}' '7fd151a85844480c7d5dde3fb06f359ccaa5964871ea63d4e19048f983905124' $CHEZMOISOURCEDIR/.chezmoiexternal.yaml.tmpl exec chezmoi apply cmp $HOME/.file golden/.file-edited exists $CHEZMOICACHEDIR # test that chezmoi apply --refresh-externals=always checks the checksum modifyfile '[0-9a-f]{64}' 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' $CHEZMOISOURCEDIR/.chezmoiexternal.yaml.tmpl ! exec chezmoi apply --refresh-externals=always stderr 'chezmoi: .file: SHA256 mismatch: expected deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef, got 7fd151a85844480c7d5dde3fb06f359ccaa5964871ea63d4e19048f983905124' cmp $HOME/.file golden/.file-edited -- golden/.file -- # contents of file -- golden/.file-edited -- # contents of file # edited -- home/user/.local/share/chezmoi/.chezmoiexternal.yaml.tmpl -- ".file": type: file url: "{{ env "HTTPD_URL" }}/file" checksum: sha256: "989b88bea77292cfdbbb0ae73f66192ed6e4318bd7f3ec5edd809d359be6af43" -- www/file -- # contents of file ================================================ FILE: internal/cmd/testdata/scripts/issue796.txtar ================================================ mkhomedir mksourcedir symlink $CHEZMOISOURCEDIR/dot_file2 -> dot_file exec chezmoi apply --force cmp $HOME/.file2 $HOME/.file ================================================ FILE: internal/cmd/testdata/scripts/keepassxc.txtar ================================================ mockcommand bin/keepassxc-cli # test keepassxcAttachment template function stdin $HOME/input exec chezmoi execute-template --no-tty '{{ keepassxcAttachment "example.com" "attachment" }}' stdout '# contents of attachment' # test keepassxcAttribute template function stdin $HOME/input exec chezmoi execute-template --no-tty '{{ keepassxcAttribute "example.com" "host-name" }}' stdout example\.com$ # test keepassxc template function and that password is only requested once stdin $HOME/input exec chezmoi execute-template --no-tty '{{ (keepassxc "example.com").UserName }}/{{ (keepassxc "example.com").Password }}' stdout examplelogin/examplepassword$ -- bin/keepassxc-cli.yaml -- responses: - args: '--version' response: '2.7.0' - args: 'attachment-export --key-file /secrets.key /secrets.kdbx --quiet --stdout example.com attachment' windowsArgs: 'attachment-export --key-file /secrets.key C:/secrets.kdbx --quiet --stdout example.com attachment' response: '# contents of attachment' - args: 'show --key-file /secrets.key /secrets.kdbx --quiet --show-protected example.com' windowsArgs: 'show --key-file /secrets.key C:/secrets.kdbx --quiet --show-protected example.com' response: | Title: example.com UserName: examplelogin Password: examplepassword URL: Notes: - args: 'show --key-file /secrets.key /secrets.kdbx example.com --attributes host-name --quiet --show-protected' windowsArgs: 'show --key-file /secrets.key C:/secrets.kdbx example.com --attributes host-name --quiet --show-protected' response: 'example.com' default: response: 'keepassxc-cli: invalid command: $*' exitCode: 1 -- home/user/.config/chezmoi/chezmoi.toml -- [keepassxc] args = ["--key-file", "/secrets.key"] database = "/secrets.kdbx" -- home/user/input -- fakepassword ================================================ FILE: internal/cmd/testdata/scripts/keeper.txtar ================================================ mockcommand bin/keeper # test keeper template function exec chezmoi execute-template '{{ (keeper "QOahgRH_dSTvSvhRBqzCzQ").record_uid }}' stdout '^QOahgRH_dSTvSvhRBqzCzQ$' # test keeperDataFields template function exec chezmoi execute-template '{{ index (keeperDataFields "QOahgRH_dSTvSvhRBqzCzQ").password 0 }}' stdout ^mypassword$ # test keeperFindPassword template function exec chezmoi execute-template '{{ keeperFindPassword "Example" }}' stdout ^mypassword$ -- bin/keeper.yaml -- responses: - args: 'get --format=json QOahgRH_dSTvSvhRBqzCzQ --config /path/to/config.json' response: | { "record_uid": "QOahgRH_dSTvSvhRBqzCzQ", "data": { "title": "Example", "type": "login", "fields": [ { "type": "login", "value": [ "mylogin" ] }, { "type": "password", "value": [ "mypassword" ] }, { "type": "url", "value": [] }, { "type": "fileRef", "value": [] }, { "type": "oneTimeCode", "value": [] } ], "custom": [] } } - args: 'find-password Example --config /path/to/config.json' response: 'mypassword' default: response: | Commands: search ... Search the vault. Can use a regular expression. Type 'command -h' to display help on command -- home/user/.config/chezmoi/chezmoi.toml -- [keeper] args = ["--config", "/path/to/config.json"] ================================================ FILE: internal/cmd/testdata/scripts/keepgoing.txtar ================================================ mkhomedir # test that chezmoi diff without --keep-going fails when there is an error ! exec chezmoi diff # test that chezmoi apply without --keep-going fails but still writes the first file ! exec chezmoi apply --force cmp $HOME/1ok golden/1ok ! exists $HOME/2error ! exists $HOME/3ok stderr '2error: template: 2error\.tmpl:2: unclosed action started at 2error\.tmpl:1' # test that chezmoi apply with --keep-going writes all files that it can without errors ! exec chezmoi apply --force --keep-going cmp $HOME/1ok golden/1ok ! exists $HOME/2error cmp $HOME/3ok golden/3ok # FIXME add chezmoi init tests # FIXME add chezmoi update tests -- golden/1ok -- first -- golden/3ok -- last -- home/user/.local/share/chezmoi/1ok -- first -- home/user/.local/share/chezmoi/2error.tmpl -- {{ -- home/user/.local/share/chezmoi/3ok -- last ================================================ FILE: internal/cmd/testdata/scripts/lastpass.txtar ================================================ mockcommand bin/lpass # test lastpass template function exec chezmoi execute-template '{{ (index (lastpass "example.com") 0).password }}' stdout ^examplepassword$ -- bin/lpass.yaml -- responses: - args: '--version' response: 'LastPass CLI v1.3.3.GIT' - args: 'show --json example.com' response: | [ { "id": "0", "name": "example.com", "fullname": "Examples/example.com", "username": "examplelogin", "password": "examplepassword", "last_modified_gmt": "0", "last_touch": "0", "group": "Examples", "url": "", "note": "" } ] default: response: 'lpass: invalid command: $*' exitCode: 1 ================================================ FILE: internal/cmd/testdata/scripts/license.txtar ================================================ # test that chezmoi license prints chezmoi's license exec chezmoi license stdout 'The MIT License' ================================================ FILE: internal/cmd/testdata/scripts/literal.txtar ================================================ # test that chezmoi add can add files that look like files in the source state exec chezmoi add $HOME${/}dot_file $HOME${/}run_script $HOME${/}symlink_symlink $HOME${/}template.tmpl rm $HOME/dot_file $HOME/run_script $HOME/symlink_symlink $HOME/template.tmpl exec chezmoi apply --force ! exists $HOME/.file ! stdout . ! exists $HOME/symlink ! exists $HOME/template cmp $HOME/dot_file golden/dot_file cmp $HOME/run_script golden/run_script cmp $HOME/symlink_symlink golden/symlink_symlink cmp $HOME/template.tmpl golden/template.tmpl -- golden/dot_file -- # contents of dot_file -- golden/run_script -- #!/bin/sh echo contents of run_script -- golden/symlink_symlink -- # contents of symlink_symlink -- golden/template.tmpl -- # contents of template.tmpl -- home/user/dot_file -- # contents of dot_file -- home/user/run_script -- #!/bin/sh echo contents of run_script -- home/user/symlink_symlink -- # contents of symlink_symlink -- home/user/template.tmpl -- # contents of template.tmpl ================================================ FILE: internal/cmd/testdata/scripts/mackupbrew_darwin.txtar ================================================ [!darwin] skip 'Darwin only' # simulate a brew installation of mackup chmod 755 opt/homebrew/Cellar/mackup/0.8.32/libexec/bin/mackup mkdir opt/homebrew/Cellar/mackup/0.8.32/bin opt/homebrew/bin symlink opt/homebrew/Cellar/mackup/0.8.32/bin/mackup -> ../libexec/bin/mackup symlink opt/homebrew/bin/mackup -> ../Cellar/mackup/0.8.32/bin/mackup env PATH=$WORK/opt/homebrew/bin:$PATH # test that chezmoi mackup add adds normal dotfiles exec chezmoi mackup add curl cmp $CHEZMOISOURCEDIR/dot_curlrc golden/dot_curlrc # test that chezmoi mackup add adds XDG configuration files exec chezmoi mackup add vscode cmp $CHEZMOISOURCEDIR/dot_config/Code/User/settings.json golden/settings.json # test that chezmoi mackup add --secrets=error generates an error when adding a file with a secret and does not add the file ! exec chezmoi mackup add --secrets=error wget stderr 'Uncovered a GitHub Personal Access Token, potentially leading to unauthorized repository access and sensitive content exposure\.' stderr 'Detected a Generic API Key, potentially exposing access to various services and sensitive operations\.' ! exists $CHEZMOISOURCEDIR/dot_wgetrc -- golden/dot_curlrc -- # contents of .curlrc -- golden/settings.json -- # contents of .config/Code/User/settings.json -- home/user/.config/Code/User/settings.json -- # contents of .config/Code/User/settings.json -- home/user/.curlrc -- # contents of .curlrc -- home/user/.mackup/curl.cfg -- [application] name = Curl [configuration_files] .netrc .curlrc -- home/user/.wgetrc -- GITHUB_PERSONAL_ACCESS_TOKEN=ghp_0123456789ABCDEF0123456789ABCDEF01234 -- opt/homebrew/Cellar/mackup/0.8.32/libexec/bin/mackup -- # mackup binary -- opt/homebrew/Cellar/mackup/0.8.32/libexec/lib/python3.9/site-packages/mackup/applications/vscode.cfg -- [application] name = Visual Studio Code [configuration_files] Library/Application Support/Code/User/snippets Library/Application Support/Code/User/keybindings.json Library/Application Support/Code/User/settings.json [xdg_configuration_files] Code/User/snippets Code/User/keybindings.json Code/User/settings.json -- opt/homebrew/Cellar/mackup/0.8.32/libexec/lib/python3.9/site-packages/mackup/applications/wget.cfg -- [application] name = Wget [configuration_files] .wgetrc .wget-hsts ================================================ FILE: internal/cmd/testdata/scripts/mackupmacports_darwin.txtar ================================================ [!darwin] skip 'Darwin only' # simulate a macports installation of mackup chmod 755 opt/local/Library/Frameworks/Python.framework/Versions/3.11/bin/mackup mkdir opt/local/bin symlink opt/local/bin/mackup -> ../Library/Frameworks/Python.framework/Versions/3.11/bin/mackup env PATH=$WORK/opt/local/bin:$PATH # test that chezmoi mackup add adds normal dotfiles exec chezmoi mackup add curl cmp $CHEZMOISOURCEDIR/dot_curlrc golden/dot_curlrc # test that chezmoi mackup add adds XDG configuration files exec chezmoi mackup add vscode cmp $CHEZMOISOURCEDIR/dot_config/Code/User/settings.json golden/settings.json -- golden/dot_curlrc -- # contents of .curlrc -- golden/settings.json -- # contents of .config/Code/User/settings.json -- home/user/.config/Code/User/settings.json -- # contents of .config/Code/User/settings.json -- home/user/.curlrc -- # contents of .curlrc -- home/user/.mackup/curl.cfg -- [application] name = Curl [configuration_files] .netrc .curlrc -- opt/local/Library/Frameworks/Python.framework/Versions/3.11/bin/mackup -- # mackup binary -- opt/local/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/mackup/applications/vscode.cfg -- [application] name = Visual Studio Code [configuration_files] Library/Application Support/Code/User/snippets Library/Application Support/Code/User/keybindings.json Library/Application Support/Code/User/settings.json [xdg_configuration_files] Code/User/snippets Code/User/keybindings.json Code/User/settings.json ================================================ FILE: internal/cmd/testdata/scripts/mackuppip_darwin.txtar ================================================ [!darwin] skip 'Darwin only' # simulate a pip installation of mackup chmod 755 usr/local/bin/mackup # version 3.9 of python without mackup mkdir usr/local/lib/python3.9/site-packages env PATH=$WORK/usr/local/bin:$PATH # test that chezmoi mackup add adds normal dotfiles exec chezmoi mackup add curl cmp $CHEZMOISOURCEDIR/dot_curlrc golden/dot_curlrc # test that chezmoi mackup add adds XDG configuration files exec chezmoi mackup add vscode cmp $CHEZMOISOURCEDIR/dot_config/Code/User/settings.json golden/settings.json -- golden/dot_curlrc -- # contents of .curlrc -- golden/settings.json -- # contents of .config/Code/User/settings.json -- home/user/.config/Code/User/settings.json -- # contents of .config/Code/User/settings.json -- home/user/.curlrc -- # contents of .curlrc -- home/user/.mackup/curl.cfg -- [application] name = Curl [configuration_files] .netrc .curlrc -- usr/local/bin/mackup -- # mackup binary -- usr/local/lib/python3.11/site-packages/mackup/applications/vscode.cfg -- [application] name = Visual Studio Code [configuration_files] Library/Application Support/Code/User/snippets Library/Application Support/Code/User/keybindings.json Library/Application Support/Code/User/settings.json [xdg_configuration_files] Code/User/snippets Code/User/keybindings.json Code/User/settings.json ================================================ FILE: internal/cmd/testdata/scripts/mackuppipx_darwin.txtar ================================================ [!darwin] skip 'Darwin only' # simulate a pipx installation of mackup chmod 755 home/.local/pipx/venvs/mackup/bin/mackup mkdir home/.local/bin symlink home/.local/bin/mackup -> ../pipx/venvs/mackup/bin/mackup env PATH=$WORK/home/.local/bin:$PATH # test that chezmoi mackup add adds normal dotfiles exec chezmoi mackup add curl cmp $CHEZMOISOURCEDIR/dot_curlrc golden/dot_curlrc # test that chezmoi mackup add adds XDG configuration files exec chezmoi mackup add vscode cmp $CHEZMOISOURCEDIR/dot_config/Code/User/settings.json golden/settings.json -- golden/dot_curlrc -- # contents of .curlrc -- golden/settings.json -- # contents of .config/Code/User/settings.json -- home/.local/pipx/venvs/mackup/bin/mackup -- # mackup binary -- home/.local/pipx/venvs/mackup/lib/python3.11/site-packages/mackup/applications/vscode.cfg -- [application] name = Visual Studio Code [configuration_files] Library/Application Support/Code/User/snippets Library/Application Support/Code/User/keybindings.json Library/Application Support/Code/User/settings.json [xdg_configuration_files] Code/User/snippets Code/User/keybindings.json Code/User/settings.json -- home/user/.config/Code/User/settings.json -- # contents of .config/Code/User/settings.json -- home/user/.curlrc -- # contents of .curlrc -- home/user/.mackup/curl.cfg -- [application] name = Curl [configuration_files] .netrc .curlrc ================================================ FILE: internal/cmd/testdata/scripts/managed.txtar ================================================ mksourcedir # test chezmoi managed exec chezmoi managed cmp stdout golden/managed # test chezmoi managed --exclude-encrypted exec chezmoi managed --exclude=encrypted cmp stdout golden/managed-exclude-encrypted # test chezmoi managed --exclude=files exec chezmoi managed --exclude=files cmp stdout golden/managed-exclude-files # test chezmoi managed --exclude=files,templates exec chezmoi managed --exclude=files,templates cmp stdout golden/managed-exclude-files-and-templates # test chezmoi managed --include=all exec chezmoi managed --include=all cmp stdout golden/managed-include-all # test chezmoi managed --include=dirs exec chezmoi managed --include=dirs cmp stdout golden/managed-include-dirs # test chezmoi managed --include=encrypted exec chezmoi managed --include=encrypted cmp stdout golden/managed-include-encrypted # test chezmoi managed --include=files exec chezmoi managed --include=files cmp stdout golden/managed-include-files # test chezmoi managed --include=files --exclude=templates exec chezmoi managed --include=files --exclude=templates cmp stdout golden/managed-include-files-exclude-templates # test chezmoi managed --include=symlinks exec chezmoi managed --include=symlinks cmp stdout golden/managed-include-symlinks # test chezmoi managed --include=templates exec chezmoi managed --include=templates cmp stdout golden/managed-include-templates # test chezmoi managed with arguments exec chezmoi managed $HOME${/}.dir $HOME${/}.create cmp stdout golden/managed-with-args # test chezmoi managed with child of managed dir as argument exec chezmoi managed $HOME${/}.dir/subdir cmp stdout golden/managed-in-managed # test chezmoi managed --exclude=dir with arguments exec chezmoi managed --exclude=dirs $HOME${/}.dir $HOME${/}.create cmp stdout golden/managed-with-nodir-args # test chezmoi managed with absent arguments exec chezmoi managed $HOME${/}.dir $HOME${/}.non-exist cmp stdout golden/managed-with-absent-args # test chezmoi managed --path-style=absolute [unix] exec chezmoi managed --path-style=absolute [unix] cmpenv stdout golden/managed-absolute # test chezmoi managed --path-style=source-absolute exec chezmoi managed --path-style=source-absolute cmpenv stdout golden/managed-source-absolute # test chezmoi managed --path-style=source-relative exec chezmoi managed --path-style=source-relative cmp stdout golden/managed-source-relative chhome home2/user # test that chezmoi managed does not evaluate templates exec chezmoi managed --include=all cmp stdout golden/managed2 # test chezmoi managed --path-style=all [!windows] exec chezmoi managed --path-style=all [!windows] cmpenv stdout golden/managed-all.json # test chezmoi managed --path-style=all --format=yaml [!windows] exec chezmoi managed --path-style=all --format=yaml [!windows] cmpenv stdout golden/managed-all.yaml -- golden/managed -- .create .dir .dir/file .dir/subdir .dir/subdir/file .empty .encrypted .executable .file .private .readonly .remove .symlink .template -- golden/managed-absolute -- $WORK/home/user/.create $WORK/home/user/.dir $WORK/home/user/.dir/file $WORK/home/user/.dir/subdir $WORK/home/user/.dir/subdir/file $WORK/home/user/.empty $WORK/home/user/.encrypted $WORK/home/user/.executable $WORK/home/user/.file $WORK/home/user/.private $WORK/home/user/.readonly $WORK/home/user/.remove $WORK/home/user/.symlink $WORK/home/user/.template -- golden/managed-all.json -- { ".create": { "absolute": "$WORK/home2/user/.create", "sourceAbsolute": "$WORK/home2/user/.local/share/chezmoi/create_dot_create.tmpl", "sourceRelative": "create_dot_create.tmpl" }, ".file": { "absolute": "$WORK/home2/user/.file", "sourceAbsolute": "$WORK/home2/user/.local/share/chezmoi/modify_dot_file.tmpl", "sourceRelative": "modify_dot_file.tmpl" }, ".symlink": { "absolute": "$WORK/home2/user/.symlink", "sourceAbsolute": "$WORK/home2/user/.local/share/chezmoi/symlink_dot_symlink.tmpl", "sourceRelative": "symlink_dot_symlink.tmpl" }, ".template": { "absolute": "$WORK/home2/user/.template", "sourceAbsolute": "$WORK/home2/user/.local/share/chezmoi/dot_template.tmpl", "sourceRelative": "dot_template.tmpl" }, "script": { "absolute": "$WORK/home2/user/script", "sourceAbsolute": "$WORK/home2/user/.local/share/chezmoi/run_script.tmpl", "sourceRelative": "run_script.tmpl" } } -- golden/managed-all.yaml -- .create: absolute: $WORK/home2/user/.create sourceAbsolute: $WORK/home2/user/.local/share/chezmoi/create_dot_create.tmpl sourceRelative: create_dot_create.tmpl .file: absolute: $WORK/home2/user/.file sourceAbsolute: $WORK/home2/user/.local/share/chezmoi/modify_dot_file.tmpl sourceRelative: modify_dot_file.tmpl .symlink: absolute: $WORK/home2/user/.symlink sourceAbsolute: $WORK/home2/user/.local/share/chezmoi/symlink_dot_symlink.tmpl sourceRelative: symlink_dot_symlink.tmpl .template: absolute: $WORK/home2/user/.template sourceAbsolute: $WORK/home2/user/.local/share/chezmoi/dot_template.tmpl sourceRelative: dot_template.tmpl script: absolute: $WORK/home2/user/script sourceAbsolute: $WORK/home2/user/.local/share/chezmoi/run_script.tmpl sourceRelative: run_script.tmpl -- golden/managed-exclude-encrypted -- .create .dir .dir/file .dir/subdir .dir/subdir/file .empty .executable .file .private .readonly .remove .symlink .template -- golden/managed-exclude-files -- .dir .dir/subdir .symlink -- golden/managed-exclude-files-and-templates -- .dir .dir/subdir .symlink -- golden/managed-exclude-templates -- .create .dir .dir/file .dir/subdir .dir/subdir/file .empty .encrypted .executable .file .private .readonly .remove .symlink -- golden/managed-in-managed -- .dir/subdir .dir/subdir/file -- golden/managed-include-all -- .create .dir .dir/file .dir/subdir .dir/subdir/file .empty .encrypted .executable .file .private .readonly .remove .symlink .template -- golden/managed-include-dirs -- .dir .dir/subdir -- golden/managed-include-encrypted -- .encrypted -- golden/managed-include-files -- .create .dir/file .dir/subdir/file .empty .encrypted .executable .file .private .readonly .remove .template -- golden/managed-include-files-exclude-templates -- .create .dir/file .dir/subdir/file .empty .encrypted .executable .file .private .readonly .remove -- golden/managed-include-symlinks -- .symlink -- golden/managed-include-templates -- .template -- golden/managed-source-absolute -- ${CHEZMOISOURCEDIR}/create_dot_create ${CHEZMOISOURCEDIR}/dot_dir ${CHEZMOISOURCEDIR}/dot_dir/exact_subdir ${CHEZMOISOURCEDIR}/dot_dir/exact_subdir/file ${CHEZMOISOURCEDIR}/dot_dir/file ${CHEZMOISOURCEDIR}/dot_file ${CHEZMOISOURCEDIR}/dot_remove ${CHEZMOISOURCEDIR}/dot_template.tmpl ${CHEZMOISOURCEDIR}/empty_dot_empty ${CHEZMOISOURCEDIR}/encrypted_dot_encrypted ${CHEZMOISOURCEDIR}/executable_dot_executable ${CHEZMOISOURCEDIR}/private_dot_private ${CHEZMOISOURCEDIR}/readonly_dot_readonly ${CHEZMOISOURCEDIR}/symlink_dot_symlink -- golden/managed-source-relative -- create_dot_create dot_dir dot_dir/exact_subdir dot_dir/exact_subdir/file dot_dir/file dot_file dot_remove dot_template.tmpl empty_dot_empty encrypted_dot_encrypted executable_dot_executable private_dot_private readonly_dot_readonly symlink_dot_symlink -- golden/managed-with-absent-args -- .dir .dir/file .dir/subdir .dir/subdir/file -- golden/managed-with-args -- .create .dir .dir/file .dir/subdir .dir/subdir/file -- golden/managed-with-nodir-args -- .create .dir/file .dir/subdir/file -- golden/managed2 -- .create .file .symlink .template script -- home/user/.local/share/chezmoi/.chezmoiremove -- .remove -- home/user/.local/share/chezmoi/encrypted_dot_encrypted -- -- home2/user/.local/share/chezmoi/create_dot_create.tmpl -- {{ fail "Template should not be executed" }} -- home2/user/.local/share/chezmoi/dot_template.tmpl -- {{ fail "Template should not be executed" }} -- home2/user/.local/share/chezmoi/modify_dot_file.tmpl -- {{ fail "Template should not be executed" }} -- home2/user/.local/share/chezmoi/run_script.tmpl -- {{ fail "Template should not be executed" }} -- home2/user/.local/share/chezmoi/symlink_dot_symlink.tmpl -- {{ fail "Template should not be executed" }} ================================================ FILE: internal/cmd/testdata/scripts/managedtree.txtar ================================================ mksourcedir # test that chezmoi managed --tree produces tree-like output exec chezmoi managed --tree cmp stdout golden/stdout -- golden/stdout -- .create .dir file subdir file .empty .executable .file .private .readonly .remove .symlink .template ================================================ FILE: internal/cmd/testdata/scripts/merge_unix.txtar ================================================ [windows] skip 'UNIX only' mkhomedir mksourcedir # test that chezmoi merge does a three-way merge exec chezmoi merge $HOME${/}.file stdout ^${HOME@R}/\.file\s+${CHEZMOISOURCEDIR@R}/dot_file\s+${WORK@R}/.*/\.file$ # test that chezmoi merge falls back to a two-way merge when the template is invalid # FIXME the following test fails # chezmoi merge $HOME${/}.invalid_template # stdout ^${HOME@R}/\.invalid_template\s+$CHEZMOISOURCEDIR/dot_invalid_template\.tmpl$ chhome home2/user # test that chezmoi merge does a three-way merge with the arguments in the configured order exec chezmoi merge $HOME${/}.file stdout ^${CHEZMOISOURCEDIR@R}/dot_file\s+${HOME@R}/\.file\s+${WORK@R}/.*/\.file$ chhome home3/user # test that chezmoi merge appends the destination, source, and target paths if merge.args does not contain any templates exec chezmoi merge $HOME${/}.file stdout ^arg\s+${HOME@R}/\.file\s+${CHEZMOISOURCEDIR@R}/dot_file\s+${WORK@R}/.*/\.file$ chhome home4/user # test that chezmoi merge respects .chezmoiroot exec chezmoi merge $HOME${/}.file stdout ^${HOME@R}/\.file\s+${CHEZMOISOURCEDIR@R}/home/dot_file\s+${WORK@R}/.*/\.file$ -- home/user/.config/chezmoi/chezmoi.toml -- [merge] command = "echo" -- home/user/.invalid_template -- -- home/user/.local/share/chezmoi/dot_invalid_template.tmpl -- {{ -- home2/user/.config/chezmoi/chezmoi.toml -- [merge] command = "echo" args = ["{{ .Source }}", "{{ .Destination }}", "{{ .Target }}"] -- home2/user/.file -- # destination -- home2/user/.local/share/chezmoi/dot_file -- # source -- home3/user/.config/chezmoi/chezmoi.toml -- [merge] command = "echo" args = ["arg"] -- home3/user/.local/share/chezmoi/dot_file -- # source -- home4/user/.config/chezmoi/chezmoi.yaml -- merge: command: "echo" -- home4/user/.file -- # destination -- home4/user/.local/share/chezmoi/.chezmoiroot -- home -- home4/user/.local/share/chezmoi/home/dot_file -- # source ================================================ FILE: internal/cmd/testdata/scripts/mergeall_unix.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi merge-all does not run the merge command if nothing is modified exec chezmoi merge-all ! stdout . # test that chezmoi merge-all runs the merge command if a file is modified edit $HOME/.file exec chezmoi merge-all stdout ^${HOME@R}/\.file\s+${CHEZMOISOURCEDIR@R}/dot_file\.tmpl\s+${WORK@R}/.*/\.file$ chhome home2/user # test that chezmoi merge-all only merges files exec chezmoi merge-all ! stdout . ! stderr . -- home/user/.config/chezmoi/chezmoi.toml -- [merge] command = "echo" -- home/user/.file -- # contents of .file -- home/user/.local/share/chezmoi/dot_file.tmpl -- {{ "# contents of .file" }} -- home2/user/.local/share/chezmoi/run_script.sh -- #!/bin/sh echo script ================================================ FILE: internal/cmd/testdata/scripts/mergeencryptedage_unix.txtar ================================================ [windows] skip 'UNIX only' [!exec:age] skip 'age not found in $PATH' mkageconfig appendline $CHEZMOICONFIGDIR/chezmoi.toml '[merge]' appendline $CHEZMOICONFIGDIR/chezmoi.toml ' command = "cat"' # test that chezmoi merge works on files encrypted with age exec chezmoi add --encrypt $HOME${/}.dir/file exists $CHEZMOISOURCEDIR/dot_dir/encrypted_file.age edit $HOME${/}.dir/file exec chezmoi merge $HOME${/}.dir/file cmp stdout golden/merge -- golden/merge -- # contents of .dir/file # edited # contents of .dir/file # contents of .dir/file -- home/user/.dir/file -- # contents of .dir/file ================================================ FILE: internal/cmd/testdata/scripts/mergeencryptedgpg_unix.txtar ================================================ [windows] skip 'UNIX only' [!exec:gpg] skip 'gpg not found in $PATH' chmod 755 bin/edit-source mkgpgconfig appendline $CHEZMOICONFIGDIR/chezmoi.toml '[merge]' appendline $CHEZMOICONFIGDIR/chezmoi.toml ' command = "cat"' # test that chezmoi merge transparently decrypts the source cp golden/source $HOME/.file exec chezmoi add --encrypt $HOME${/}.file exec chezmoi chattr +template $HOME${/}.file cp golden/destination $HOME/.file exec chezmoi merge $HOME${/}.file cmp stdout golden/expected chhome home2/user mkgpgconfig appendline $CHEZMOICONFIGDIR/chezmoi.toml '[merge]' appendline $CHEZMOICONFIGDIR/chezmoi.toml ' command = "edit-source"' # test that chezmoi merge transparently re-encrypts the source if it is edited cp golden/source $HOME/.file exec chezmoi add --encrypt $HOME${/}.file exec chezmoi chattr +template $HOME${/}.file cp golden/destination $HOME/.file exec chezmoi merge $HOME${/}.file exec chezmoi cat $HOME${/}.file cmp stdout golden/edited-target -- bin/edit-source -- #!/bin/sh echo "# edited" >> $2 -- golden/destination -- destination -- golden/edited-target -- target # edited -- golden/expected -- destination {{ "target" }} target -- golden/source -- {{ "target" }} ================================================ FILE: internal/cmd/testdata/scripts/modesymlink.txtar ================================================ mkhomedir golden mksourcedir # test that chezmoi apply does not create symlinks by default exec chezmoi apply cmp $HOME/.create golden/.create ! issymlink $HOME/.create cmp $HOME/.dir/file golden/.dir/file ! issymlink $HOME/.dir/file cmp $HOME/.dir/subdir/file golden/.dir/subdir/file ! issymlink $HOME/.dir/subdir/file cmp $HOME/.empty golden/.empty ! issymlink $HOME/.empty cmp $HOME/.executable golden/.executable ! issymlink $HOME/.executable cmp $HOME/.file golden/.file ! issymlink $HOME/.file cmp $HOME/.private golden/.private ! issymlink $HOME/.private ! exists $HOME/.remove cmp $HOME/.template golden/.template ! issymlink $HOME/.template # test that chezmoi apply --mode=symlink creates symlinks where possible exec chezmoi apply --mode=symlink cmp $HOME/.create golden/.create ! issymlink $HOME/.create cmp $HOME/.dir/file golden/.dir/file issymlink $HOME/.dir/file cmp $HOME/.dir/subdir/file golden/.dir/subdir/file issymlink $HOME/.dir/subdir/file cmp $HOME/.empty golden/.empty issymlink $HOME/.empty cmp $HOME/.executable golden/.executable ! issymlink $HOME/.executable cmp $HOME/.file golden/.file issymlink $HOME/.file cmp $HOME/.private golden/.private ! issymlink $HOME/.private ! exists $HOME/.remove cmp $HOME/.template golden/.template ! issymlink $HOME/.template ================================================ FILE: internal/cmd/testdata/scripts/modify_unix.txtar ================================================ [windows] skip 'UNIX only' [!exec:sed] skip 'sed not found in $PATH' [!umask:022] skip 'unsupported umask' cp golden/.modify home/user # test that chezmoi cat prints the modified contents without modifying the file exec chezmoi cat $HOME${/}.modify cmp stdout golden/.modified cmp home/user/.modify golden/.modify # test that chezmoi diff prints the diff without modifying the file exec chezmoi diff cmp stdout golden/diff.diff cmp home/user/.modify golden/.modify # test that chezmoi archive includes the modified file exec chezmoi archive --output=archive.tar exec tar xf archive.tar cmp .modify golden/.modified cmp home/user/.modify golden/.modify # test that chezmoi apply modifies the file exec chezmoi apply --force cmp home/user/.modify golden/.modified chhome home2/user # test that chezmoi cat does not fail or generate output when the target does not exist exec chezmoi cat $HOME${/}.not_exist ! stdout . # test that chezmoi cat exits with an error when the modify script fails ! exec chezmoi cat $HOME${/}.error stderr error # test that chezmoi apply updates file permissions cmpmod 666 $HOME/.file exec chezmoi apply $HOME${/}.file cmpmod 700 $HOME/.file chhome home3/user # test that chezmoi apply always overwrites modified files without --force exec chezmoi add $HOME${/}.modify exec chezmoi apply edit $HOME${/}.modify rm $CHEZMOISOURCEDIR/dot_modify cp home/user/.local/share/chezmoi/modify_dot_modify $CHEZMOISOURCEDIR exec chezmoi apply cmp $HOME${/}.modify golden/.edited-and-modified chhome home4/user # test that modify scripts can be templates exec chezmoi cat $HOME${/}.modify cmp stdout golden/.modified chhome home5/user # test that modify scripts can be modify-templates exec chezmoi cat $HOME${/}.modify cmp stdout golden/.modified chhome home6/user # test that modify scripts can use modify-templates to modify JSON fields exec chezmoi apply --force cmp $HOME/.modify.json golden/.modified.json -- golden/.edited-and-modified -- beginning modified-middle end # edited -- golden/.modified -- beginning modified-middle end -- golden/.modified.json -- {"key1":{"key2":"value","key3":"value3"}} -- golden/.modify -- beginning middle end -- golden/diff.diff -- diff --git a/.modify b/.modify index f91830d4ecd80adfe9a6aea9dca579397aa86921..6b6d41aae5e8d64a54afd8b8ad5a38a3de1e1e35 100644 --- a/.modify +++ b/.modify @@ -1,3 +1,3 @@ beginning -middle +modified-middle end -- home/user/.local/share/chezmoi/modify_dot_modify -- #!/bin/sh sed 's/middle/modified-middle/g' -- home2/user/.file -- # contents of .file -- home2/user/.local/share/chezmoi/modify_dot_error -- #!/bin/sh echo error >2 exit 1 -- home2/user/.local/share/chezmoi/modify_dot_not_exist -- #!/bin/sh cat -- home2/user/.local/share/chezmoi/modify_private_executable_dot_file -- #!/bin/sh cat -- home3/user/.modify -- beginning middle end -- home4/user/.local/share/chezmoi/modify_dot_modify.tmpl -- #!/bin/sh {{ "sed 's/middle/modified-middle/g'" }} -- home4/user/.modify -- beginning middle end -- home5/user/.local/share/chezmoi/modify_dot_modify -- {{- /* chezmoi:modify-template */ -}} {{- .chezmoi.stdin | replaceAllRegex "middle" "modified-middle" -}} -- home5/user/.modify -- beginning middle end -- home6/user/.local/share/chezmoi/modify_dot_modify.json -- {{- /* chezmoi:modify-template */ -}} {{ fromJson .chezmoi.stdin | setValueAtPath "key1.key2" "value" | toJson }} -- home6/user/.modify.json -- {"key1":{"key2":"value2","key3":"value3"}} ================================================ FILE: internal/cmd/testdata/scripts/modify_windows.txtar ================================================ [unix] skip 'Windows only' # test that chezmoi apply modifies a file with a Batch script exec chezmoi apply unix2dos golden/modified cmp $HOME/.file golden/modified chhome home2/user # test that chezmoi apply returns an error when there are multiple modify scripts for the same target ! exec chezmoi apply stderr 'inconsistent state' -- golden/modified -- # modified -- golden/modified-powershell -- # contents of .file # modified -- home/user/.file -- # contents of .file -- home/user/.local/share/chezmoi/modify_dot_file.bat -- @echo off echo # modified -- home2/user/.local/share/chezmoi/modify_dot_file.bat -- -- home2/user/.local/share/chezmoi/modify_dot_file.cmd -- ================================================ FILE: internal/cmd/testdata/scripts/modifyencrypted.txtar ================================================ [windows] skip 'UNIX only' [!exec:age] skip 'age not found in $PATH' # test that chezmoi applies encrypted modify scripts mkageconfig cp golden/.modify $HOME mkdir $CHEZMOISOURCEDIR exec chezmoi encrypt --output=$CHEZMOISOURCEDIR${/}modify_encrypted_dot_modify.age golden/modify.sh grep '-----BEGIN AGE ENCRYPTED FILE-----' $CHEZMOISOURCEDIR/modify_encrypted_dot_modify.age exec chezmoi apply --force cmp $HOME/.modify golden/.modify-modified chhome home2/user # test that chezmoi applies encrypted template modify scripts mkageconfig cp golden/.modify $HOME mkdir $CHEZMOISOURCEDIR exec chezmoi encrypt --output=$CHEZMOISOURCEDIR${/}modify_encrypted_dot_modify.tmpl.age golden/modify.sh.tmpl grep '-----BEGIN AGE ENCRYPTED FILE-----' $CHEZMOISOURCEDIR/modify_encrypted_dot_modify.tmpl.age exec chezmoi apply --force cmp $HOME/.modify golden/.modify-modified -- golden/.modify -- # contents of .modify -- golden/.modify-modified -- # contents of .modify # modified -- golden/modify.sh -- #!/bin/sh cat echo "# modified" -- golden/modify.sh.tmpl -- #!/bin/sh cat echo {{ "# modified" | quote }} ================================================ FILE: internal/cmd/testdata/scripts/modifypython_windows.txtar ================================================ [unix] skip 'Windows only' [!exec:python3] skip 'python3 not found in %PATH%' # test that chezmoi apply modifies a file with python3 exec chezmoi apply unix2dos golden/modified cmp $HOME/.file golden/modified -- golden/modified -- # contents of .file # modified -- home/user/.file -- # contents of .file -- home/user/.local/share/chezmoi/modify_dot_file.py -- import sys sys.stdout.write(sys.stdin.read()) sys.stdout.write("# modified\n") ================================================ FILE: internal/cmd/testdata/scripts/noencryption.txtar ================================================ mkhomedir # test that chezmoi add --encrypt without encryption fails ! exec chezmoi add --encrypt $HOME${/}.encrypted stderr 'encryption not configured' # test that chezmoi apply without encryption fails ! exec chezmoi apply --force stderr '\.encrypted: encryption not configured$' -- home/user/.encrypted -- # contents of .encrypted -- home/user/.local/share/chezmoi/encrypted_dot_encrypted -- ================================================ FILE: internal/cmd/testdata/scripts/nosourcedir.txtar ================================================ # test that chezmoi apply returns an error if the source directory does not exist ! exec chezmoi apply [unix] stderr 'no such file or directory' [windows] stderr 'The system cannot find the path specified' ================================================ FILE: internal/cmd/testdata/scripts/onepassword2.txtar ================================================ mockcommand bin/op # test onepassword template function exec chezmoi execute-template '{{ (onepassword "ExampleLogin").id }}' stdout '^wxcplh5udshnonkzg2n4qx262y$' # test onepassword template function with vault and account exec chezmoi execute-template '{{ (onepassword "ExampleLogin" "vault" "account").id }}' stdout '^wxcplh5udshnonkzg2n4qx262y$' # test onepassword template function with empty vault exec chezmoi execute-template '{{ (onepassword "ExampleLogin" "" "account").id }}' stdout '^wxcplh5udshnonkzg2n4qx262y$' # test onepassword template function with account alias exec chezmoi execute-template '{{ (onepassword "ExampleLogin" "" "chezmoi").id }}' stdout '^wxcplh5udshnonkzg2n4qx262y$' # test onepasswordDetailsFields template function exec chezmoi execute-template '{{ (onepasswordDetailsFields "ExampleLogin").password.value }}' stdout '^L8rm1JXJIE1b8YUDWq7h$' # test onepasswordItemFields template function exec chezmoi execute-template '{{ (onepasswordItemFields "ExampleLogin").exampleLabel.value }}' stdout exampleValue # test onepasswordRead template function exec chezmoi execute-template '{{ onepasswordRead "op://vault/item/field" }}' stdout exampleField # test onepasswordRead template function with account exec chezmoi execute-template '{{ onepasswordRead "op://vault/item/field" "account" }}' stdout exampleAccountField # test onepasswordDocument template function exec chezmoi execute-template '{{ onepasswordDocument "exampleDocument" }}' stdout 'OK-COMPUTER' # test onepasswordDocument template function with vault exec chezmoi execute-template '{{ onepasswordDocument "exampleDocument" "vault" }}' stdout 'OK-VAULT' # test onepasswordDocument template function with vault and account exec chezmoi execute-template '{{ onepasswordDocument "exampleDocument" "vault" "account" }}' stdout 'OK-VAULT-ACCOUNT' # test onepasswordDocument template function with account exec chezmoi execute-template '{{ onepasswordDocument "exampleDocument" "" "account" }}' stdout 'OK-ACCOUNT' # test onepassword template function (insufficient parameters) ! exec chezmoi execute-template '{{ (onepassword).id }}' stderr 'expected 1..3 arguments in account mode, got 0' # test onepassword template function (too many parameters) ! exec chezmoi execute-template '{{ (onepassword "ExampleLogin" "vault" "account" "extra").id }}' stderr 'expected 1..3 arguments in account mode, got 4' # test onepasswordRead template function (too many parameters) ! exec chezmoi execute-template '{{ onepasswordRead "op://vault/item/field" "account" "extra" }}' stderr 'expected 1..2 arguments, got 3' # test failure with OP_SERVICE_ACCOUNT_TOKEN set env OP_SERVICE_ACCOUNT_TOKEN=x ! exec chezmoi execute-template '{{ (onepassword "ExampleLogin").id }}' stderr 'OP_SERVICE_ACCOUNT_TOKEN is set' # test failure with OP_CONNECT_HOST and OP_CONNECT_TOKEN set env OP_SERVICE_ACCOUNT_TOKEN= env OP_CONNECT_HOST=x env OP_CONNECT_TOKEN=y ! exec chezmoi execute-template '{{ (onepassword "ExampleLogin").id }}' stderr 'OP_CONNECT_HOST and OP_CONNECT_TOKEN' -- bin/op.yaml -- responses: - args: '--version' response: '2.0.0' - args: 'item get --format json ExampleLogin --vault vault --account account_uuid' response: '{"id":"wxcplh5udshnonkzg2n4qx262y","title":"ExampleLogin","version":1,"vault":{"id":"tscpxgi6s7c662jtqn3vmw4n5a"},"category":"LOGIN","last_edited_by":"YO4UTYPAD3ZFBNZG5DVAZFBNZM","created_at":"2022-01-17T01:53:50Z","updated_at":"2022-01-17T01:55:35Z","sections":[{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"}],"fields":[{"id":"username","type":"STRING","purpose":"USERNAME","label":"username","value":"exampleuser "},{"id":"password","type":"CONCEALED","purpose":"PASSWORD","label":"password","value":"L8rm1JXJIE1b8YUDWq7h","password_details":{"strength":"EXCELLENT"}},{"id":"notesPlain","type":"STRING","purpose":"NOTES","label":"notesPlain"},{"id":"cqn7oda7wkcsar7rzcr52i2m3u","section":{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"},"type":"STRING","label":"exampleLabel","value":"exampleValue"}],"urls":[{"primary":true,"href":"https://www.example.com/"}]}' - args: 'item get --format json ExampleLogin --account account_uuid' response: '{"id":"wxcplh5udshnonkzg2n4qx262y","title":"ExampleLogin","version":1,"vault":{"id":"tscpxgi6s7c662jtqn3vmw4n5a"},"category":"LOGIN","last_edited_by":"YO4UTYPAD3ZFBNZG5DVAZFBNZM","created_at":"2022-01-17T01:53:50Z","updated_at":"2022-01-17T01:55:35Z","sections":[{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"}],"fields":[{"id":"username","type":"STRING","purpose":"USERNAME","label":"username","value":"exampleuser "},{"id":"password","type":"CONCEALED","purpose":"PASSWORD","label":"password","value":"L8rm1JXJIE1b8YUDWq7h","password_details":{"strength":"EXCELLENT"}},{"id":"notesPlain","type":"STRING","purpose":"NOTES","label":"notesPlain"},{"id":"cqn7oda7wkcsar7rzcr52i2m3u","section":{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"},"type":"STRING","label":"exampleLabel","value":"exampleValue"}],"urls":[{"primary":true,"href":"https://www.example.com/"}]}' - args: '--session thisIsAFakeSessionToken item get --format json ExampleLogin --vault vault --account account_uuid' response: '{"id":"wxcplh5udshnonkzg2n4qx262y","title":"ExampleLogin","version":1,"vault":{"id":"tscpxgi6s7c662jtqn3vmw4n5a"},"category":"LOGIN","last_edited_by":"YO4UTYPAD3ZFBNZG5DVAZFBNZM","created_at":"2022-01-17T01:53:50Z","updated_at":"2022-01-17T01:55:35Z","sections":[{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"}],"fields":[{"id":"username","type":"STRING","purpose":"USERNAME","label":"username","value":"exampleuser "},{"id":"password","type":"CONCEALED","purpose":"PASSWORD","label":"password","value":"L8rm1JXJIE1b8YUDWq7h","password_details":{"strength":"EXCELLENT"}},{"id":"notesPlain","type":"STRING","purpose":"NOTES","label":"notesPlain"},{"id":"cqn7oda7wkcsar7rzcr52i2m3u","section":{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"},"type":"STRING","label":"exampleLabel","value":"exampleValue"}],"urls":[{"primary":true,"href":"https://www.example.com/"}]}' - args: '--session thisIsAFakeSessionToken item get --format json ExampleLogin --account account_uuid' response: '{"id":"wxcplh5udshnonkzg2n4qx262y","title":"ExampleLogin","version":1,"vault":{"id":"tscpxgi6s7c662jtqn3vmw4n5a"},"category":"LOGIN","last_edited_by":"YO4UTYPAD3ZFBNZG5DVAZFBNZM","created_at":"2022-01-17T01:53:50Z","updated_at":"2022-01-17T01:55:35Z","sections":[{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"}],"fields":[{"id":"username","type":"STRING","purpose":"USERNAME","label":"username","value":"exampleuser "},{"id":"password","type":"CONCEALED","purpose":"PASSWORD","label":"password","value":"L8rm1JXJIE1b8YUDWq7h","password_details":{"strength":"EXCELLENT"}},{"id":"notesPlain","type":"STRING","purpose":"NOTES","label":"notesPlain"},{"id":"cqn7oda7wkcsar7rzcr52i2m3u","section":{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"},"type":"STRING","label":"exampleLabel","value":"exampleValue"}],"urls":[{"primary":true,"href":"https://www.example.com/"}]}' - args: 'item get --format json ExampleLogin' response: '{"id":"wxcplh5udshnonkzg2n4qx262y","title":"ExampleLogin","version":1,"vault":{"id":"tscpxgi6s7c662jtqn3vmw4n5a"},"category":"LOGIN","last_edited_by":"YO4UTYPAD3ZFBNZG5DVAZFBNZM","created_at":"2022-01-17T01:53:50Z","updated_at":"2022-01-17T01:55:35Z","sections":[{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"}],"fields":[{"id":"username","type":"STRING","purpose":"USERNAME","label":"username","value":"exampleuser "},{"id":"password","type":"CONCEALED","purpose":"PASSWORD","label":"password","value":"L8rm1JXJIE1b8YUDWq7h","password_details":{"strength":"EXCELLENT"}},{"id":"notesPlain","type":"STRING","purpose":"NOTES","label":"notesPlain"},{"id":"cqn7oda7wkcsar7rzcr52i2m3u","section":{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"},"type":"STRING","label":"exampleLabel","value":"exampleValue"}],"urls":[{"primary":true,"href":"https://www.example.com/"}]}' - args: '--session thisIsAFakeSessionToken item get --format json ExampleLogin' response: '{"id":"wxcplh5udshnonkzg2n4qx262y","title":"ExampleLogin","version":1,"vault":{"id":"tscpxgi6s7c662jtqn3vmw4n5a"},"category":"LOGIN","last_edited_by":"YO4UTYPAD3ZFBNZG5DVAZFBNZM","created_at":"2022-01-17T01:53:50Z","updated_at":"2022-01-17T01:55:35Z","sections":[{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"}],"fields":[{"id":"username","type":"STRING","purpose":"USERNAME","label":"username","value":"exampleuser "},{"id":"password","type":"CONCEALED","purpose":"PASSWORD","label":"password","value":"L8rm1JXJIE1b8YUDWq7h","password_details":{"strength":"EXCELLENT"}},{"id":"notesPlain","type":"STRING","purpose":"NOTES","label":"notesPlain"},{"id":"cqn7oda7wkcsar7rzcr52i2m3u","section":{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"},"type":"STRING","label":"exampleLabel","value":"exampleValue"}],"urls":[{"primary":true,"href":"https://www.example.com/"}]}' - args: '--session thisIsAFakeSessionToken item get --format json ExampleLogin --vault vault' response: '{"id":"wxcplh5udshnonkzg2n4qx262y","title":"ExampleLogin","version":1,"vault":{"id":"tscpxgi6s7c662jtqn3vmw4n5a"},"category":"LOGIN","last_edited_by":"YO4UTYPAD3ZFBNZG5DVAZFBNZM","created_at":"2022-01-17T01:53:50Z","updated_at":"2022-01-17T01:55:35Z","sections":[{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"}],"fields":[{"id":"username","type":"STRING","purpose":"USERNAME","label":"username","value":"exampleuser "},{"id":"password","type":"CONCEALED","purpose":"PASSWORD","label":"password","value":"L8rm1JXJIE1b8YUDWq7h","password_details":{"strength":"EXCELLENT"}},{"id":"notesPlain","type":"STRING","purpose":"NOTES","label":"notesPlain"},{"id":"cqn7oda7wkcsar7rzcr52i2m3u","section":{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"},"type":"STRING","label":"exampleLabel","value":"exampleValue"}],"urls":[{"primary":true,"href":"https://www.example.com/"}]}' - args: '--session thisIsAFakeSessionToken item get --format json ExampleLogin' response: '{"id":"wxcplh5udshnonkzg2n4qx262y","title":"ExampleLogin","version":1,"vault":{"id":"tscpxgi6s7c662jtqn3vmw4n5a"},"category":"LOGIN","last_edited_by":"YO4UTYPAD3ZFBNZG5DVAZFBNZM","created_at":"2022-01-17T01:53:50Z","updated_at":"2022-01-17T01:55:35Z","sections":[{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"}],"fields":[{"id":"username","type":"STRING","purpose":"USERNAME","label":"username","value":"exampleuser "},{"id":"password","type":"CONCEALED","purpose":"PASSWORD","label":"password","value":"L8rm1JXJIE1b8YUDWq7h","password_details":{"strength":"EXCELLENT"}},{"id":"notesPlain","type":"STRING","purpose":"NOTES","label":"notesPlain"},{"id":"cqn7oda7wkcsar7rzcr52i2m3u","section":{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"},"type":"STRING","label":"exampleLabel","value":"exampleValue"}],"urls":[{"primary":true,"href":"https://www.example.com/"}]}' - args: 'account list --format=json' response: '[{"url":"account.1password.com","email":"chezmoi@chezmoi.org","user_uuid":"user_uuid","account_uuid":"account_uuid"}]' - args: 'signin --account account_uuid --raw' response: 'thisIsAFakeSessionToken' - args: 'signin --raw' response: 'thisIsAFakeSessionToken' - args: 'read --no-newline op://vault/item/field' response: 'exampleField' - args: '--session thisIsAFakeSessionToken read --no-newline op://vault/item/field' response: 'exampleField' - args: '--session thisIsAFakeSessionToken read --no-newline op://vault/item/field --account account_uuid' response: 'exampleAccountField' - args: 'document get exampleDocument' response: 'OK-COMPUTER' - args: 'document get exampleDocument --vault vault' response: 'OK-VAULT' - args: '--session thisIsAFakeSessionToken document get exampleDocument' response: 'OK-COMPUTER' - args: '--session thisIsAFakeSessionToken document get exampleDocument --vault vault' response: 'OK-VAULT' - args: '--session thisIsAFakeSessionToken document get exampleDocument --account account_uuid' response: 'OK-ACCOUNT' - args: '--session thisIsAFakeSessionToken document get exampleDocument --vault vault --account account_uuid' response: 'OK-VAULT-ACCOUNT' default: response: '[ERROR] 2020/01/01 00:00:00 unknown command "$*" for "op"' destination: stderr exitCode: 1 ================================================ FILE: internal/cmd/testdata/scripts/onepassword2connect.txtar ================================================ mockcommand bin/op mkhomedir # test that mode is properly set and reported exec chezmoi execute-template '{{ .chezmoi.config.onepassword.mode }}' stdout '^connect$' # test failure without OP_CONNECT_HOST set ! exec chezmoi execute-template '{{ (onepassword "ExampleLogin").id }}' stderr 'OP_CONNECT_HOST' env OP_CONNECT_HOST=x # test failure without OP_CONNECT_TOKEN set ! exec chezmoi execute-template '{{ (onepassword "ExampleLogin").id }}' stderr 'OP_CONNECT_TOKEN' env OP_CONNECT_TOKEN=y # test onepassword template function exec chezmoi execute-template '{{ (onepassword "ExampleLogin").id }}' stdout '^wxcplh5udshnonkzg2n4qx262y$' # test onepassword template function with vault exec chezmoi execute-template '{{ (onepassword "ExampleLogin" "vault").id }}' stdout '^wxcplh5udshnonkzg2n4qx262y$' # test failure onepassword template function with vault and account ! exec chezmoi execute-template '{{ (onepassword "ExampleLogin" "vault" "account").id }}' stderr '1Password account parameters cannot be used in connect mode' # test onepassword template function with empty vault exec chezmoi execute-template '{{ (onepassword "ExampleLogin" "").id }}' stdout '^wxcplh5udshnonkzg2n4qx262y$' # test onepasswordDetailsFields template function exec chezmoi execute-template '{{ (onepasswordDetailsFields "ExampleLogin").password.value }}' stdout '^L8rm1JXJIE1b8YUDWq7h$' # test onepasswordItemFields template function exec chezmoi execute-template '{{ (onepasswordItemFields "ExampleLogin").exampleLabel.value }}' stdout exampleValue # test onepasswordRead template function exec chezmoi execute-template '{{ onepasswordRead "op://vault/item/field" }}' stdout exampleField # test failure onepasswordRead template function with account ! exec chezmoi execute-template '{{ onepasswordRead "op://vault/item/field" "account" }}' stderr '1Password account parameters cannot be used in connect mode' # test failure onepasswordDocument template function ! exec chezmoi execute-template '{{ onepasswordDocument "exampleDocument" }}' stderr 'onepasswordDocument cannot be used in connect mode' # test failure with OP_SERVICE_ACCOUNT_TOKEN set env OP_SERVICE_ACCOUNT_TOKEN=x ! exec chezmoi execute-template '{{ (onepassword "ExampleLogin").id }}' stderr 'OP_SERVICE_ACCOUNT_TOKEN is set' -- bin/op.yaml -- responses: - args: '--version' response: 2.0.0 - args: 'item get --format json ExampleLogin --vault vault --account account_uuid' response: '[ERROR] cannot use accounts with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 - args: 'item get --format json ExampleLogin --account account_uuid' response: '[ERROR] cannot use accounts with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 - args: '--session thisIsAFakeSessionToken item get --format json ExampleLogin --vault vault --account account_uuid' response: '[ERROR] cannot use accounts with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 - args: '--session thisIsAFakeSessionToken item get --format json ExampleLogin --account account_uuid' response: '[ERROR] cannot use accounts with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 - args: 'item get --format json ExampleLogin' response: '{"id":"wxcplh5udshnonkzg2n4qx262y","title":"ExampleLogin","version":1,"vault":{"id":"tscpxgi6s7c662jtqn3vmw4n5a"},"category":"LOGIN","last_edited_by":"YO4UTYPAD3ZFBNZG5DVAZFBNZM","created_at":"2022-01-17T01:53:50Z","updated_at":"2022-01-17T01:55:35Z","sections":[{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"}],"fields":[{"id":"username","type":"STRING","purpose":"USERNAME","label":"username","value":"exampleuser "},{"id":"password","type":"CONCEALED","purpose":"PASSWORD","label":"password","value":"L8rm1JXJIE1b8YUDWq7h","password_details":{"strength":"EXCELLENT"}},{"id":"notesPlain","type":"STRING","purpose":"NOTES","label":"notesPlain"},{"id":"cqn7oda7wkcsar7rzcr52i2m3u","section":{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"},"type":"STRING","label":"exampleLabel","value":"exampleValue"}],"urls":[{"primary":true,"href":"https://www.example.com/"}]}' - args: 'item get --format json ExampleLogin --vault vault' response: '{"id":"wxcplh5udshnonkzg2n4qx262y","title":"ExampleLogin","version":1,"vault":{"id":"tscpxgi6s7c662jtqn3vmw4n5a"},"category":"LOGIN","last_edited_by":"YO4UTYPAD3ZFBNZG5DVAZFBNZM","created_at":"2022-01-17T01:53:50Z","updated_at":"2022-01-17T01:55:35Z","sections":[{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"}],"fields":[{"id":"username","type":"STRING","purpose":"USERNAME","label":"username","value":"exampleuser "},{"id":"password","type":"CONCEALED","purpose":"PASSWORD","label":"password","value":"L8rm1JXJIE1b8YUDWq7h","password_details":{"strength":"EXCELLENT"}},{"id":"notesPlain","type":"STRING","purpose":"NOTES","label":"notesPlain"},{"id":"cqn7oda7wkcsar7rzcr52i2m3u","section":{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"},"type":"STRING","label":"exampleLabel","value":"exampleValue"}],"urls":[{"primary":true,"href":"https://www.example.com/"}]}' - args: '--session thisIsAFakeSessionToken item get --format json ExampleLogin --vault vault' response: '[ERROR] cannot use session tokens with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 - args: '--session thisIsAFakeSessionToken item get --format json ExampleLogin' response: '[ERROR] cannot use session tokens with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 - args: 'account list --format=json' response: '[ERROR] cannot use accounts with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 - args: 'signin --account account_uuid --raw' response: '[ERROR] cannot sign in with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 - args: 'signin --raw' response: '[ERROR] cannot sign in with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 - args: 'read --no-newline op://vault/item/field' response: 'exampleField' - args: '--session thisIsAFakeSessionToken read --no-newline op://vault/item/field' response: '[ERROR] cannot use session tokens with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 - args: '--session thisIsAFakeSessionToken read --no-newline op://vault/item/field --account account_uuid' response: '[ERROR] cannot use session tokens or accounts with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 - args: 'document get exampleDocument' response: '[ERROR] cannot use document get with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 - args: '--session thisIsAFakeSessionToken document get exampleDocument' response: '[ERROR] cannot use document get with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 - args: '--session thisIsAFakeSessionToken document get exampleDocument --vault vault' response: '[ERROR] cannot use document get with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 - args: '--session thisIsAFakeSessionToken document get exampleDocument --account account_uuid' response: '[ERROR] cannot use document get with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 - args: '--session thisIsAFakeSessionToken document get exampleDocument --vault vault --account account_uuid' response: '[ERROR] cannot use document get with OP_CONNECT_HOST and OP_CONNECT_TOKEN set' destination: stderr exitCode: 1 default: response: '[ERROR] 2020/01/01 00:00:00 unknown command "$*" for "op"' destination: stderr exitCode: 1 -- home/user/.config/chezmoi/chezmoi.toml -- [onepassword] mode = "connect" ================================================ FILE: internal/cmd/testdata/scripts/onepassword2service.txtar ================================================ mockcommand bin/op mkhomedir # test that mode is properly set and reported exec chezmoi execute-template '{{ .chezmoi.config.onepassword.mode }}' stdout '^service$' # test failure without OP_SERVICE_ACCOUNT_TOKEN set ! exec chezmoi execute-template '{{ (onepassword "ExampleLogin").id }}' stderr 'OP_SERVICE_ACCOUNT_TOKEN is not set' env OP_SERVICE_ACCOUNT_TOKEN=x # test onepassword template function exec chezmoi execute-template '{{ (onepassword "ExampleLogin").id }}' stdout '^wxcplh5udshnonkzg2n4qx262y$' # test onepassword template function with vault exec chezmoi execute-template '{{ (onepassword "ExampleLogin" "vault").id }}' stdout '^wxcplh5udshnonkzg2n4qx262y$' # test failure onepassword template function with vault and account ! exec chezmoi execute-template '{{ (onepassword "ExampleLogin" "vault" "account").id }}' stderr '1Password account parameters cannot be used in service mode' # test onepassword template function with empty vault exec chezmoi execute-template '{{ (onepassword "ExampleLogin" "").id }}' stdout '^wxcplh5udshnonkzg2n4qx262y$' # test onepasswordDetailsFields template function exec chezmoi execute-template '{{ (onepasswordDetailsFields "ExampleLogin").password.value }}' stdout '^L8rm1JXJIE1b8YUDWq7h$' # test onepasswordItemFields template function exec chezmoi execute-template '{{ (onepasswordItemFields "ExampleLogin").exampleLabel.value }}' stdout exampleValue # test onepasswordRead template function exec chezmoi execute-template '{{ onepasswordRead "op://vault/item/field" }}' stdout exampleField # test failure onepasswordRead template function with account ! exec chezmoi execute-template '{{ onepasswordRead "op://vault/item/field" "account" }}' stderr '1Password account parameters cannot be used in service mode' # test onepasswordDocument template function exec chezmoi execute-template '{{ onepasswordDocument "exampleDocument" }}' stdout 'OK-COMPUTER' # test onepasswordDocument template function with vault exec chezmoi execute-template '{{ onepasswordDocument "exampleDocument" "vault" }}' stdout 'OK-VAULT' # test onepasswordDocument template function with vault and account ! exec chezmoi execute-template '{{ onepasswordDocument "exampleDocument" "vault" "account" }}' stderr '1Password account parameters cannot be used in service mode' # test onepasswordDocument template function with account ! exec chezmoi execute-template '{{ onepasswordDocument "exampleDocument" "" "account" }}' stderr '1Password account parameters cannot be used in service mode' # test failure with OP_CONNECT_HOST and OP_CONNECT_TOKEN set env OP_CONNECT_HOST=x env OP_CONNECT_TOKEN=y ! exec chezmoi execute-template '{{ (onepassword "ExampleLogin").id }}' stderr 'OP_CONNECT_HOST and OP_CONNECT_TOKEN' -- bin/op.yaml -- responses: - args: '--version' response: '2.0.0' - args: 'item get --format json ExampleLogin --vault vault --account account_uuid' response: '[ERROR] cannot use accounts with OP_SERVICE_TOKEN set' destination: stderr exitCode: 1 - args: 'item get --format json ExampleLogin --account account_uuid' response: '[ERROR] cannot use accounts with OP_SERVICE_TOKEN set' destination: stderr exitCode: 1 - args: '--session thisIsAFakeSessionToken item get --format json ExampleLogin --vault vault --account account_uuid' response: '[ERROR] cannot use accounts with OP_SERVICE_TOKEN set' destination: stderr exitCode: 1 - args: '--session thisIsAFakeSessionToken item get --format json ExampleLogin --account account_uuid' response: '[ERROR] cannot use accounts with OP_SERVICE_TOKEN set' destination: stderr exitCode: 1 - args: 'item get --format json ExampleLogin' response: '{"id":"wxcplh5udshnonkzg2n4qx262y","title":"ExampleLogin","version":1,"vault":{"id":"tscpxgi6s7c662jtqn3vmw4n5a"},"category":"LOGIN","last_edited_by":"YO4UTYPAD3ZFBNZG5DVAZFBNZM","created_at":"2022-01-17T01:53:50Z","updated_at":"2022-01-17T01:55:35Z","sections":[{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"}],"fields":[{"id":"username","type":"STRING","purpose":"USERNAME","label":"username","value":"exampleuser "},{"id":"password","type":"CONCEALED","purpose":"PASSWORD","label":"password","value":"L8rm1JXJIE1b8YUDWq7h","password_details":{"strength":"EXCELLENT"}},{"id":"notesPlain","type":"STRING","purpose":"NOTES","label":"notesPlain"},{"id":"cqn7oda7wkcsar7rzcr52i2m3u","section":{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"},"type":"STRING","label":"exampleLabel","value":"exampleValue"}],"urls":[{"primary":true,"href":"https://www.example.com/"}]}' - args: 'item get --format json ExampleLogin --vault vault' response: '{"id":"wxcplh5udshnonkzg2n4qx262y","title":"ExampleLogin","version":1,"vault":{"id":"tscpxgi6s7c662jtqn3vmw4n5a"},"category":"LOGIN","last_edited_by":"YO4UTYPAD3ZFBNZG5DVAZFBNZM","created_at":"2022-01-17T01:53:50Z","updated_at":"2022-01-17T01:55:35Z","sections":[{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"}],"fields":[{"id":"username","type":"STRING","purpose":"USERNAME","label":"username","value":"exampleuser "},{"id":"password","type":"CONCEALED","purpose":"PASSWORD","label":"password","value":"L8rm1JXJIE1b8YUDWq7h","password_details":{"strength":"EXCELLENT"}},{"id":"notesPlain","type":"STRING","purpose":"NOTES","label":"notesPlain"},{"id":"cqn7oda7wkcsar7rzcr52i2m3u","section":{"id":"Section_cdzjhg2jo7jylpyin2f5mbfnhm","label":"Related Items"},"type":"STRING","label":"exampleLabel","value":"exampleValue"}],"urls":[{"primary":true,"href":"https://www.example.com/"}]}' - args: '--session thisIsAFakeSessionToken item get --format json ExampleLogin --vault vault' response: '[ERROR] cannot use session tokens with OP_SERVICE_TOKEN set' destination: stderr exitCode: 1 - args: '--session thisIsAFakeSessionToken item get --format json ExampleLogin' response: '[ERROR] cannot use session tokens with OP_SERVICE_TOKEN set' destination: stderr exitCode: 1 - args: 'account list --format=json' response: '[ERROR] cannot use accounts with OP_SERVICE_TOKEN set' destination: stderr exitCode: 1 - args: 'signin --account account_uuid --raw' response: '[ERROR] cannot sign in with OP_SERVICE_TOKEN set' destination: stderr exitCode: 1 - args: 'signin --raw' response: '[ERROR] cannot sign in with OP_SERVICE_TOKEN set' destination: stderr exitCode: 1 - args: 'document get exampleDocument' response: 'OK-COMPUTER' - args: 'document get exampleDocument --vault vault' response: 'OK-VAULT' - args: '--session thisIsAFakeSessionToken document get exampleDocument' response: '[ERROR] cannot use session tokens with OP_SERVICE_TOKEN set' destination: stderr exitCode: 1 - args: '--session thisIsAFakeSessionToken document get exampleDocument --vault vault' response: 'OK-VAULT' - args: '--session thisIsAFakeSessionToken document get exampleDocument --account account_uuid' response: '[ERROR] cannot use accounts or session tokens with OP_SERVICE_TOKEN set' destination: stderr exitCode: 1 - args: '--session thisIsAFakeSessionToken document get exampleDocument --vault vault --account account_uuid' response: '[ERROR] cannot use accounts or session tokens with OP_SERVICE_TOKEN set' destination: stderr exitCode: 1 - args: 'read --no-newline op://vault/item/field' response: 'exampleField' - args: '--session thisIsAFakeSessionToken read --no-newline op://vault/item/field' response: '[ERROR] cannot use session tokens with OP_SERVICE_TOKEN set' destination: stderr exitCode: 1 - args: '--session thisIsAFakeSessionToken read --no-newline op://vault/item/field --account account_uuid' response: '[ERROR] cannot use session tokens or accounts with OP_SERVICE_TOKEN set' destination: stderr exitCode: 1 default: response: '[ERROR] 2020/01/01 00:00:00 unknown command "$*" for "op"' destination: stderr exitCode: 1 -- home/user/.config/chezmoi/chezmoi.toml -- [onepassword] mode = "service" ================================================ FILE: internal/cmd/testdata/scripts/options.txtar ================================================ # test that --source flag is respected exec chezmoi apply --source=~/.dotfiles cmp $HOME/.file golden/.file chhome home2/user # test that --destination flag is respected mkdir tmp exec chezmoi apply --destination=$WORK/tmp cmp tmp/.file golden/.file chhome home3/user # test that --config flag is respected exec chezmoi apply --config=$HOME/.chezmoi.toml cmp $HOME/tmp/.file golden/.file -- golden/.file -- # contents of .file -- home/user/.dotfiles/dot_file -- # contents of .file -- home2/user/.local/share/chezmoi/dot_file -- # contents of .file -- home3/user/.chezmoi.toml -- sourceDir = "~/.dotfiles" destDir = "~/tmp" -- home3/user/.dotfiles/dot_file -- # contents of .file -- home3/user/tmp/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/pager.txtar ================================================ [windows] skip 'UNIX only' chmod 755 bin/shell # test that chezmoi diff uses args from diff.pager and diff.pagerArgs exec chezmoi diff stdout 'a b c d' chhome home2/user # test that chezmoi diff uses pager and pagerArgs from pager and pagerArgs exec chezmoi diff stdout 'u v x y z' -- bin/shell -- #!/bin/sh echo "$*" -- home/user/.config/chezmoi/chezmoi.toml -- [diff] pager = "echo a b" pagerArgs = ["c", "d"] -- home/user/.local/share/chezmoi/dot_file -- # contents of .file -- home2/user/.config/chezmoi/chezmoi.toml -- pager = "echo u v" pagerArgs = ["x", "y", "z"] -- home2/user/.local/share/chezmoi/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/pass.txtar ================================================ mockcommand bin/pass [windows] unix2dos golden/pass-raw # test pass template function exec chezmoi execute-template '{{ pass "misc/example.com" }}' stdout ^examplepassword$ # test passFields template function exec chezmoi execute-template '{{ (passFields "misc/example.com").login }}' stdout ^examplelogin$ # test pass template function exec chezmoi execute-template '{{ passRaw "misc/example.com" }}' cmp stdout golden/pass-raw -- bin/pass.yaml -- responses: - args: 'show misc/example.com' response: | examplepassword login: examplelogin default: response: 'pass: invalid command: $*' exitCode: 1 -- golden/pass-raw -- examplepassword login: examplelogin ================================================ FILE: internal/cmd/testdata/scripts/passhole.txtar ================================================ mockcommand bin/ph # test passhole template function stdin golden/stdin exec chezmoi execute-template --no-tty '{{ passhole "example.com" "password" }}' stdout examplepassword -- bin/ph.yaml -- responses: - args: '--version' response: '1.9.9' - args: '--password - show --field password example.com' response: 'examplepassword' default: response: 'ph: error: argument command: invalid choice:' exitCode: 1 -- golden/stdin -- fakepassword ================================================ FILE: internal/cmd/testdata/scripts/plugin.txtar ================================================ [unix] chmod 755 bin/chezmoi-plugin # test that chezmoi returns unknown command errors for unknown commands ! exec chezmoi unknown stderr 'unknown command' # test that chezmoi executes plugins exec chezmoi plugin stdout CHEZMOI_COMMAND=plugin stdout CHEZMOI_SOURCE_DIR=${CHEZMOISOURCEDIR@R} -- bin/chezmoi-plugin -- #!/bin/sh echo CHEZMOI_COMMAND=${CHEZMOI_COMMAND} echo CHEZMOI_SOURCE_DIR=${CHEZMOI_SOURCE_DIR} -- bin/chezmoi-plugin.cmd -- @echo CHEZMOI_COMMAND=%CHEZMOI_COMMAND% @echo CHEZMOI_SOURCE_DIR=%CHEZMOI_SOURCE_DIR% ================================================ FILE: internal/cmd/testdata/scripts/protonpass.txtar ================================================ mockcommand bin/pass-cli # test protonPass template function exec chezmoi execute-template '{{ protonPass "pass://FromCLI/My-Wifi/password" | trim }}' stdout ^MyPassword$ # test protonPassJSON template function exec chezmoi execute-template '{{ (protonPassJSON "pass://MyVault/My-Wifi-Item").item.content.content.Wifi.password }}' stdout '^MyWifiPassword$' -- bin/pass-cli.yaml -- responses: - args: 'item view pass://FromCLI/My-Wifi/password' response: MyPassword - args: 'item view pass://MyVault/My-Wifi-Item --output=json' response: | { "item": { "id": "VnYGoUKBHpeDZH_xFEAQkPCGFLy6zYaGI8vnBaJD1fZPVzit724aiUMxIvvkawMjyvS7pEgia9bTX7A6-IBGwQ==", "share_id": "UscrxQg8fVhiptQVc1Y6TIiG21AZwuU1jAIWGsFgv__2moOhwQdiTy4VTXQfYmMMrWPN2ANFZhKGKypRLq914g==", "vault_id": "zE3QmUltEUQVCxSIeXNgu5E0SGBH4_Z3Be-sWj0XCH942FkbsS8aePXdmy_R9qzSBo3-yjDFz7gq9BazoZxdDA==", "content": { "title": "My Wifi", "note": "", "item_uuid": "461e9eb6-3c27-4b68-b8d1-8fdc2684e776", "content": { "Wifi": { "ssid": "My Wifi SSID", "password": "MyWifiPassword", "security": "WPA2", "sections": [] } }, "extra_fields": [] }, "state": "Active", "flags": [], "create_time": "2025-10-27T11:11:22" }, "attachments": [] } - args: '--version' response: 'Proton Pass CLI 1.2.0 (0d71fde)' ================================================ FILE: internal/cmd/testdata/scripts/purge.txtar ================================================ mksourcedir # test that chezmoi purge purges the source dir exists $CHEZMOISOURCEDIR exec chezmoi purge --force ! exists $CHEZMOISOURCEDIR chhome home2/user # test that chezmoi purge purges the config dir exists $CHEZMOICONFIGDIR exec chezmoi purge --force ! exists $CHEZMOICONFIGDIR # test that chezmoi purge purges the cache dir mkdir $HOME/.cache/chezmoi exec chezmoi purge --force ! exists $HOME/.cache/chezmoi -- home2/user/.config/chezmoi/chezmoi.toml -- ================================================ FILE: internal/cmd/testdata/scripts/rbw.txtar ================================================ mockcommand bin/rbw # test rbw template function exec chezmoi execute-template '{{ (rbw "test-entry").data.password }}' stdout ^hunter2$ # test rbw template function with extra args exec chezmoi execute-template '{{ (rbw "test-entry" "--folder" "my-folder").data.password }}' stdout ^correcthorsebatterystaple$ # test rbwFields template function exec chezmoi execute-template '{{ (rbwFields "test-entry").something.value }}' stdout ^secret$ # test rbwFields template function with extra args exec chezmoi execute-template '{{ (rbwFields "test-entry" "--folder" "my-folder").something.value }}' stdout ^enigma$ -- bin/rbw.yaml -- responses: - args: 'get --raw test-entry' response: | { "id": "adf723e1-ab03-4ff3-81aa-f5f3c2b68a5f", "folder": null, "name": "test-entry", "data": { "username": "foo", "password": "hunter2", "totp": null, "uris": [ { "uri": "example.com", "match_type": null } ] }, "fields": [ { "name": "something", "value": "secret" } ], "notes": "blah", "history": [ { "last_used_date": "2022-08-18T23:24:47.994Z", "password": "hunter2" } ] } - args: 'get --raw test-entry --folder my-folder' response: | { "id": "adf723e1-ab03-4ff3-81aa-f5f3c2b68a5f", "folder": null, "name": "test-entry", "data": { "username": "foo", "password": "correcthorsebatterystaple", "totp": null, "uris": [ { "uri": "example.com", "match_type": null } ] }, "fields": [ { "name": "something", "value": "enigma" } ], "notes": "blah", "history": [ { "last_used_date": "2022-08-18T23:24:47.994Z", "password": "hunter2" } ] } default: exitCode: 1 ================================================ FILE: internal/cmd/testdata/scripts/re-add.txtar ================================================ mkhomedir mksourcedir # test that chezmoi re-add adds all modified files exec chezmoi apply --force edit $HOME/.file edit $HOME/.dir/file edit $HOME/.dir/subdir/file exec chezmoi re-add grep '# edited' $CHEZMOISOURCEDIR/dot_file grep '# edited' $CHEZMOISOURCEDIR/dot_dir/file grep '# edited' $CHEZMOISOURCEDIR/dot_dir/exact_subdir/file exec chezmoi diff ! stdout . # test that chezmoi re-add adds only specified targets edit $HOME/.file edit $HOME/.dir/file edit $HOME/.dir/subdir/file exec chezmoi re-add ~/.dir/file grep -count=1 '# edited' $CHEZMOISOURCEDIR/dot_file grep -count=2 '# edited' $CHEZMOISOURCEDIR/dot_dir/file grep -count=1 '# edited' $CHEZMOISOURCEDIR/dot_dir/exact_subdir/file # test that chezmoi re-add --recursive=false does not recurse into subdirectories AND still syncs exact directories # Even with --recursive=false, exact directory sync happens because subdir is exact exec chezmoi re-add --recursive=false ~/.dir/subdir grep -count=2 '# edited' $CHEZMOISOURCEDIR/dot_dir/exact_subdir/file # test that chezmoi re-add is recursive by default edit $HOME/.dir/subdir/file exec chezmoi re-add ~/.dir/subdir grep -count=3 '# edited' $CHEZMOISOURCEDIR/dot_dir/exact_subdir/file ================================================ FILE: internal/cmd/testdata/scripts/readd-exact.txtar ================================================ # test that chezmoi re-add adds new files in exact_ directories exec chezmoi add --exact $HOME${/}test-dir exists $CHEZMOISOURCEDIR/exact_test-dir/test1 cmp $CHEZMOISOURCEDIR/exact_test-dir/test1 golden/test1 # verify no diff after initial add exec chezmoi diff ! stdout . # add a new file to the exact directory in target cp golden/test2 $HOME/test-dir/test2 exec chezmoi re-add exists $CHEZMOISOURCEDIR/exact_test-dir/test2 cmp $CHEZMOISOURCEDIR/exact_test-dir/test2 golden/test2 # verify no diff after adding new file exec chezmoi diff ! stdout . # test that chezmoi re-add removes deleted files from exact_ directories rm $HOME/test-dir/test1 exec chezmoi re-add ! exists $CHEZMOISOURCEDIR/exact_test-dir/test1 # verify no diff after removing file exec chezmoi diff ! stdout . # test that chezmoi re-add with explicit exact directory target works cp golden/test3 $HOME/test-dir/test3 exec chezmoi re-add $HOME/test-dir exists $CHEZMOISOURCEDIR/exact_test-dir/test3 cmp $CHEZMOISOURCEDIR/exact_test-dir/test3 golden/test3 # verify no diff after adding file with explicit target exec chezmoi diff ! stdout . # test nested exact directory with --recursive=false exec chezmoi add $HOME/outer-dir exec chezmoi add --exact $HOME/outer-dir/exact-inner exists $CHEZMOISOURCEDIR/outer-dir/exact_exact-inner/inner1 # add a new file to the nested exact directory in target cp golden/inner2 $HOME/outer-dir/exact-inner/inner2 # re-add with --recursive=false should NOT process nested exact directory, and thereforce should not add new file inner2. exec chezmoi re-add --recursive=false $HOME/outer-dir ! exists $CHEZMOISOURCEDIR/outer-dir/exact_exact-inner/inner2 # re-add with recursive (default) SHOULD process nested exact directory exec chezmoi re-add $HOME/outer-dir exists $CHEZMOISOURCEDIR/outer-dir/exact_exact-inner/inner2 cmp $CHEZMOISOURCEDIR/outer-dir/exact_exact-inner/inner2 golden/inner2 # verify no diff exec chezmoi diff ! stdout . # test nested exact directories (both parent and child are exact) exec chezmoi add --exact $HOME/exact-outer exists $CHEZMOISOURCEDIR/exact_exact-outer/exact_exact-inner/nested1 # add a new file to the nested exact directory cp golden/nested2 $HOME/exact-outer/exact-inner/nested2 # re-add should process both exact directories and add the new file exec chezmoi re-add exists $CHEZMOISOURCEDIR/exact_exact-outer/exact_exact-inner/nested2 cmp $CHEZMOISOURCEDIR/exact_exact-outer/exact_exact-inner/nested2 golden/nested2 # verify no diff exec chezmoi diff ! stdout . -- golden/inner2 -- # contents of inner2 -- golden/nested2 -- # contents of nested2 -- golden/test1 -- # contents of test1 -- golden/test2 -- # contents of test2 -- golden/test3 -- # contents of test3 -- home/user/exact-outer/exact-inner/nested1 -- # contents of nested1 -- home/user/exact-outer/outer1 -- # contents of outer1 -- home/user/outer-dir/exact-inner/inner1 -- # contents of inner1 -- home/user/outer-dir/file1 -- # contents of outer file1 -- home/user/test-dir/test1 -- # contents of test1 ================================================ FILE: internal/cmd/testdata/scripts/readdreencrypt.txtar ================================================ mkageconfig mkdir golden exec chezmoi add --encrypt $HOME${/}.file exists $CHEZMOISOURCEDIR/encrypted_dot_file.age cp $CHEZMOISOURCEDIR/encrypted_dot_file.age golden # test that chezmoi re-add does not re-add unchanged encrypted files exec chezmoi re-add cmp $CHEZMOISOURCEDIR/encrypted_dot_file.age golden/encrypted_dot_file.age # test that chezmoi re-add --re-encrypt does re-encrypt unchanged encrypted files exec chezmoi re-add --force --re-encrypt ! cmp $CHEZMOISOURCEDIR/encrypted_dot_file.age golden/encrypted_dot_file.age -- home/user/.file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/remove.txtar ================================================ # test that chezmoi remove produces an error ! exec chezmoi remove stderr 'Command "remove" is deprecated, use forget or destroy instead' # test that chezmoi rm produces an error ! exec chezmoi rm stderr 'Command "remove" is deprecated, use forget or destroy instead' ================================================ FILE: internal/cmd/testdata/scripts/removedir.txtar ================================================ # test that chezmoi apply removes empty directories, but not non-empty directories, or non-existent directories mkdir $HOME/.empty_dir exec chezmoi apply exists $HOME/.dir ! exists $HOME/.empty_dir chhome home2/user # test that chezmoi apply will remove a directory even if it is actually a file exec chezmoi apply ! exists $HOME/.dir chhome home3/user # test that chezmoi apply removes empty directories recursively mkdir $HOME/.dir/subdir exec chezmoi apply ! exists $HOME/.dir -- home/user/.dir/file -- -- home/user/.local/share/chezmoi/remove_dot_dir/.keep -- -- home/user/.local/share/chezmoi/remove_dot_empty_dir/.keep -- -- home/user/.local/share/chezmoi/remove_dot_non_existent_dir/.keep -- -- home2/user/.dir -- # contents of .dir -- home2/user/.local/share/chezmoi/remove_dot_dir/.keep -- -- home3/user/.local/share/chezmoi/remove_dot_dir/remove_subdir/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/root.txtar ================================================ # test that chezmoi apply uses .chezmoiroot exec chezmoi apply cmp $HOME/.file golden/.file # test that chezmoi add uses .chezmoiroot symlink $HOME/.symlink -> .file exec chezmoi add $HOME${/}.symlink cmp $CHEZMOISOURCEDIR/home/symlink_dot_symlink golden/symlink_dot_symlink [!exec:git] skip 'git not found in $PATH' [windows] skip 'go-git does not support file:// URLs on windows' chhome home2/user mkgitconfig # create a git repo in home2/user/repo exec git -C $HOME/repo init exec git -C $HOME/repo add . exec git -C $HOME/repo commit -m 'Initial commit' # test that chezmoi init uses .chezmoiroot exec chezmoi init --apply file://$HOME/repo exists $CHEZMOICONFIGDIR/chezmoi.toml cmp $HOME/.file golden/.file -- golden/.file -- # contents of .file -- golden/symlink_dot_symlink -- .file -- home/user/.local/share/chezmoi/.chezmoiroot -- home -- home/user/.local/share/chezmoi/home/dot_file -- # contents of .file -- home2/user/repo/.chezmoiroot -- home -- home2/user/repo/home/.chezmoi.toml.tmpl -- -- home2/user/repo/home/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/runscriptdir_unix.txtar ================================================ [windows] skip 'UNIX only' [!umask:022] skip exec chezmoi apply cmpenv stdout golden/apply env $=$ exec chezmoi dump cmp stdout golden/dump.json exec chezmoi archive --output=archive.tar exec tar -tf archive.tar [openbsd] cmp stdout golden/archive-openbsd [!openbsd] cmp stdout golden/archive -- golden/apply -- $HOME${/}dir -- golden/archive -- dir/ dir/script.sh -- golden/archive-openbsd -- dir dir/script.sh -- golden/dump.json -- { "dir": { "type": "dir", "name": "dir", "perm": 493 }, "dir/script.sh": { "type": "script", "name": "dir/script.sh", "contents": "#!/bin/sh\n\npwd\n", "condition": "always" } } -- home/user/.local/share/chezmoi/dir/run_script.sh -- #!/bin/sh pwd ================================================ FILE: internal/cmd/testdata/scripts/script.txtar ================================================ [unix] chmod 755 bin/perl [unix] chmod 755 bin/python3 [unix] chmod 755 bin/ruby [windows] unix2dos golden/stdout # test that chezmoi apply uses python3 and ruby from $PATH instead of the system Python and Ruby exec chezmoi apply cmp stdout golden/stdout -- bin/perl -- #!/bin/sh echo "Hello from fake Perl" -- bin/perl.bat -- @echo Hello from fake Perl -- bin/python3 -- #!/bin/sh echo "Hello from fake Python" -- bin/python3.bat -- @echo Hello from fake Python -- bin/ruby -- #!/bin/sh echo "Hello from fake Ruby" -- bin/ruby.bat -- @echo Hello from fake Ruby -- golden/stdout -- Hello from fake Perl Hello from fake Python Hello from fake Ruby -- home/user/.local/share/chezmoi/run_perl_script.pl -- #!/usr/bin/env perl print("Hello from Perl\n") -- home/user/.local/share/chezmoi/run_python_script.py -- #!/usr/bin/env python3 print("Hello from Python\n") -- home/user/.local/share/chezmoi/run_ruby_script.rb -- #!/usr/bin/env ruby print "Hello from Ruby\n" ================================================ FILE: internal/cmd/testdata/scripts/script_unix.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi status prints that it will run the script exec chezmoi status cmp stdout golden/status # test that chezmoi diff prints the script exec chezmoi diff cmp stdout golden/diff.diff # test that chezmoi diff --script-contents=false prints the script name but not its contents exec chezmoi diff --script-contents=false cmp stdout golden/diff-no-script-contents.diff # test that chezmoi apply runs the script exec chezmoi apply --force stdout ${HOME@R} # test that chezmoi status prints that it will run the script again exec chezmoi status cmp stdout golden/status # test that chezmoi apply runs the script even if it has run before exec chezmoi apply --force stdout ${HOME@R} # test that chezmoi dump includes the script exec chezmoi dump cmp stdout golden/dump.json # test that chezmoi managed includes the script exec chezmoi managed --include=scripts cmpenv stdout golden/managed # test that chezmoi cat writes the contents of the script exec chezmoi cat $HOME${/}script.sh cmp stdout golden/script.sh # test that chezmoi archive includes the script in the archive exec chezmoi archive --format=tar --gzip --output=archive.tar.gz exec tar -tzf archive.tar.gz cmp stdout golden/archive -- golden/archive -- script.sh -- golden/diff-no-script-contents.diff -- diff --git a/script.sh b/script.sh new file mode 100755 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 --- /dev/null +++ b/script.sh -- golden/diff.diff -- diff --git a/script.sh b/script.sh new file mode 100755 index 0000000000000000000000000000000000000000..f9103e018df1bbc178e66b46d8f133f49c85225d --- /dev/null +++ b/script.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +pwd -- golden/dump.json -- { "script.sh": { "type": "script", "name": "script.sh", "contents": "#!/bin/sh\n\npwd\n", "condition": "always" } } -- golden/managed -- script.sh -- golden/script.sh -- #!/bin/sh pwd -- golden/status -- R script.sh -- home/user/.local/share/chezmoi/run_script.sh -- #!/bin/sh pwd ================================================ FILE: internal/cmd/testdata/scripts/script_windows.txtar ================================================ [unix] skip 'Windows only' exec chezmoi apply --force stdout evidence exec chezmoi dump cmp stdout golden/dump.json exec chezmoi managed --include=scripts cmpenv stdout golden/managed exec chezmoi archive --gzip --output=archive.tar.gz exec tar -tzf archive.tar.gz [windows] unix2dos golden/archive cmp stdout golden/archive -- golden/archive -- script.cmd -- golden/dump.json -- { "script.cmd": { "type": "script", "name": "script.cmd", "contents": "echo evidence\n", "condition": "always" } } -- golden/managed -- script.cmd -- home/user/.local/share/chezmoi/run_script.cmd -- echo evidence ================================================ FILE: internal/cmd/testdata/scripts/scriptenv.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi sets environment variables for scripts exec chezmoi apply stdout ^WORK=${WORK@R}$ [darwin] stdout ^CHEZMOI_OS=darwin$ [linux] stdout ^CHEZMOI_OS=linux$ stdout ^CHEZMOI_SOURCE_DIR=${CHEZMOISOURCEDIR@R}/home$ stdout ^CHEZMOI_VERBOSE=$ stdout ^SCRIPTENV_KEY=SCRIPTENV_VALUE$ # test that chezmoi passes along --verbose in scripts exec chezmoi apply --verbose stdout ^WORK=${WORK@R}$ [darwin] stdout ^CHEZMOI_OS=darwin$ [linux] stdout ^CHEZMOI_OS=linux$ stdout ^CHEZMOI_SOURCE_DIR=${CHEZMOISOURCEDIR@R}/home$ stdout ^CHEZMOI_VERBOSE=1$ stdout ^SCRIPTENV_KEY=SCRIPTENV_VALUE$ -- home/user/.config/chezmoi/chezmoi.toml -- [scriptEnv] SCRIPTENV_KEY = "SCRIPTENV_VALUE" -- home/user/.local/share/chezmoi/.chezmoiroot -- home -- home/user/.local/share/chezmoi/home/run_print-variable.sh -- #!/bin/sh echo "WORK=${WORK}" echo "CHEZMOI_OS=${CHEZMOI_OS}" echo "CHEZMOI_SOURCE_DIR=${CHEZMOI_SOURCE_DIR}" echo "SCRIPTENV_KEY=${SCRIPTENV_KEY}" echo "CHEZMOI_VERBOSE=${CHEZMOI_VERBOSE}" ================================================ FILE: internal/cmd/testdata/scripts/scriptinterpreters_unix_pwsh.txtar ================================================ [windows] skip 'Unix only' [!exec:pwsh] skip 'pwsh not found in $PATH' # test: pwsh available, should use pwsh chhome home_pwsh/user exec chezmoi apply cmp stdout golden/stdout_pwsh -- golden/stdout_pwsh -- Hello from PowerShell Core (pwsh) -- home_pwsh/user/.local/share/chezmoi/run_powershell_script.ps1 -- Write-Host 'Hello from PowerShell Core (pwsh)' ================================================ FILE: internal/cmd/testdata/scripts/scriptinterpreters_windows.txtar ================================================ [unix] skip 'Windows only' # test that chezmoi apply runs Batch scripts exec chezmoi apply unix2dos golden/stdout # normalize line endings before comparison cmp stdout golden/stdout chhome home2/user # test that chezmoi apply runs PowerShell scripts exec chezmoi apply unix2dos golden/stdout2 # normalize line endings before comparison cmp stdout golden/stdout2 chhome home3/user # test that interpreters can be overridden exec chezmoi apply unix2dos golden/stdout3 # normalize line endings before comparison cmp stdout golden/stdout3 -- bin/fake-python3.bat -- @echo Hello from fake Python -- golden/stdout -- Hello from Batch (.bat) Hello from Batch (.cmd) -- golden/stdout2 -- Hello from PowerShell -- golden/stdout3 -- Hello from fake Python -- golden/stdout_override -- Hello from overridden PowerShell interpreter -- golden/stdout_powershell -- Hello from Windows PowerShell -- golden/stdout_pwsh -- Hello from PowerShell Core (pwsh) -- home/user/.local/share/chezmoi/run_batch_script.bat -- @echo Hello from Batch (.bat) -- home/user/.local/share/chezmoi/run_cmd_script.cmd -- @echo Hello from Batch (.cmd) -- home2/user/.local/share/chezmoi/run_powershell_script.ps1 -- Write-Host 'Hello from PowerShell' -- home3/user/.config/chezmoi/chezmoi.toml -- [interpreters.py] command = "fake-python3" -- home3/user/.local/share/chezmoi/run_python_script.py -- #!/usr/bin/env python3 # this should never be executed as the interpreter is overridden with # fake-python3.bat in the config file fail() # test: pwsh available, should use pwsh # (mock FindExecutable to return pwsh.exe path) chhome home_pwsh/user exec chezmoi apply cmp stdout golden/stdout_pwsh # test: powershell available, pwsh not, should use powershell # (mock FindExecutable to return powershell.exe path) chhome home_powershell/user exec chezmoi apply cmp stdout golden/stdout_powershell # test: interpreters.ps1 config overrides default selection chhome home_override/user exec chezmoi apply cmp stdout golden/stdout_override -- home_override/user/.config/chezmoi/chezmoi.toml -- [interpreters.ps1] command = "custom-powershell" args = ["-CustomArg"] -- home_override/user/.local/share/chezmoi/run_powershell_script.ps1 -- Write-Host 'Hello from overridden PowerShell interpreter' -- home_powershell/user/.local/share/chezmoi/run_powershell_script.ps1 -- Write-Host 'Hello from Windows PowerShell' -- home_pwsh/user/.local/share/chezmoi/run_powershell_script.ps1 -- Write-Host 'Hello from PowerShell Core (pwsh)' ================================================ FILE: internal/cmd/testdata/scripts/scriptinterpreterstemplate.txtar ================================================ [!exec:python3] skip 'python3 not found in $PATH' # test python3 scripts exec chezmoi apply stdout 'Hello from Python' -- home/user/.local/share/chezmoi/run_python3.py.tmpl -- #!/usr/bin/env python3 print({{ "Hello from Python\n" | quote }}) ================================================ FILE: internal/cmd/testdata/scripts/scriptonce_unix.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi status prints that it will run the script exec chezmoi status cmp stdout golden/status # test that chezmoi diff includes the script exec chezmoi diff cmp stdout golden/diff.diff # test that chezmoi apply runs the script exec chezmoi apply --force stdout ${HOME@R} # test that the script is recorded in the state exec chezmoi state dump stdout bb29fcd5733098d4e391d85d487d84d1d64cf42eae34b53951ae470b98c9ca8d # sha256sum of script contents # test that chezmoi diff no longer includes the script exec chezmoi diff ! stdout . # test that chezmoi status will not print that it will run the script exec chezmoi status ! stdout . # test that chezmoi apply does not run the script a second time and does not prompt exec chezmoi apply ! stdout ${HOME@R} # test that chezmoi apply after the script is modified runs the script a second time and does not prompt edit $CHEZMOISOURCEDIR/run_once_script.sh exec chezmoi apply stdout ${HOME@R} # test that resetting the state causes the next chezmoi apply to run the script exec chezmoi state reset --force exec chezmoi apply --force stdout ${HOME@R} -- golden/diff.diff -- diff --git a/script.sh b/script.sh new file mode 100755 index 0000000000000000000000000000000000000000..f9103e018df1bbc178e66b46d8f133f49c85225d --- /dev/null +++ b/script.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +pwd -- golden/status -- R script.sh -- home/user/.local/share/chezmoi/run_once_script.sh -- #!/bin/sh pwd ================================================ FILE: internal/cmd/testdata/scripts/scriptonce_windows.txtar ================================================ [unix] skip 'Windows only' # test that chezmoi status prints that it will run the script exec chezmoi status cmp stdout golden/status # test that chezmoi apply runs the script exec chezmoi apply --force stdout ${HOME@R} # test that the script is recorded in the state exec chezmoi state dump stdout 4716eebb443f263affd831da48c4af62715fab9dc9e6a813f8bf8992aa53c5b4 # sha256sum of script contents # test that chezmoi status will not print that it will run the script exec chezmoi status ! stdout . # test that chezmoi apply does not run the script a second time exec chezmoi apply --force ! stdout ${HOME@R} # test that resetting the state causes the next chezmoi apply to run the script exec chezmoi state reset --force exec chezmoi apply --force stdout ${HOME@R} -- golden/status -- R script.cmd -- home/user/.local/share/chezmoi/run_once_script.cmd -- @echo %cd% ================================================ FILE: internal/cmd/testdata/scripts/scriptonchange_unix.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi apply runs onchange scripts the first time mkdir $CHEZMOISOURCEDIR cp golden/script-one.sh $CHEZMOISOURCEDIR/run_onchange_script.sh exec chezmoi apply stdout one exec chezmoi state get --bucket=entryState --key=$HOME/script.sh cmp stdout golden/script-one-state.json # test that chezmoi apply does not run onchange scripts when their contents are not changed exec chezmoi apply ! stdout . # test that chezmoi status does not print that it will run onchange scripts when their contents are not changed exec chezmoi status ! stdout . # test that chezmoi status does print that it will run onchange scripts when their contents are changed cp golden/script-two.sh $CHEZMOISOURCEDIR/run_onchange_script.sh exec chezmoi status cmp stdout golden/status # test that chezmoi apply runs onchange scripts when their contents are changed exec chezmoi apply stdout two exec chezmoi state get --bucket=entryState --key=$HOME/script.sh cmp stdout golden/script-two-state.json # test that chezmoi apply runs onchange scripts when their contents are reverted to a previous state cp golden/script-one.sh $CHEZMOISOURCEDIR/run_onchange_script.sh exec chezmoi apply stdout one exec chezmoi state get --bucket=entryState --key=$HOME/script.sh cmp stdout golden/script-one-state.json -- golden/script-one-state.json -- { "type": "script", "contentsSHA256": "a07f0271151ee0271ed379ebbddc5ef49d0f625417c8fe23254179e56f98d2df" } -- golden/script-one.sh -- #!/bin/sh echo one -- golden/script-two-state.json -- { "type": "script", "contentsSHA256": "7c8d714586cecf4f0ffb735ad10334df98428bc5282c0d0a6b78f5c074365159" } -- golden/script-two.sh -- #!/bin/sh echo two -- golden/status -- R script.sh ================================================ FILE: internal/cmd/testdata/scripts/scriptorder_unix.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi apply runs scripts in the correct order symlink home/user/.local/share/chezmoi/.chezmoiscripts/run_before_00-chezmoiscripts-before -> ../.script.sh symlink home/user/.local/share/chezmoi/.chezmoiscripts/run_before_99-chezmoiscripts-before -> ../.script.sh symlink home/user/.local/share/chezmoi/.chezmoiscripts/run_00-chezmoiscripts -> ../.script.sh symlink home/user/.local/share/chezmoi/.chezmoiscripts/run_99-chezmoiscripts -> ../.script.sh symlink home/user/.local/share/chezmoi/.chezmoiscripts/run_after_00-chezmoiscripts-after -> ../.script.sh symlink home/user/.local/share/chezmoi/.chezmoiscripts/run_after_99-chezmoiscripts-after -> ../.script.sh symlink home/user/.local/share/chezmoi/run_before_00-before -> .script.sh symlink home/user/.local/share/chezmoi/run_before_99-before -> .script.sh symlink home/user/.local/share/chezmoi/run_00 -> .script.sh symlink home/user/.local/share/chezmoi/run_99 -> .script.sh symlink home/user/.local/share/chezmoi/run_after_00-after -> .script.sh symlink home/user/.local/share/chezmoi/run_after_99-after -> .script.sh exec chezmoi apply --force cmp stdout golden/apply -- golden/apply -- 00-chezmoiscripts-before 99-chezmoiscripts-before 00-before 99-before 00-chezmoiscripts 99-chezmoiscripts 00 99 00-chezmoiscripts-after 99-chezmoiscripts-after 00-after 99-after -- home/user/.local/share/chezmoi/.chezmoiscripts/.keep -- -- home/user/.local/share/chezmoi/.script.sh -- #!/bin/sh basename=$(basename $0) echo ${basename##*.} ================================================ FILE: internal/cmd/testdata/scripts/scriptorder_windows.txtar ================================================ [unix] skip 'Windows only' unix2dos golden/apply # test that chezmoi apply runs scripts in the correct order symlink home/user/.local/share/chezmoi/run_before_00-before.cmd -> .script.cmd symlink home/user/.local/share/chezmoi/run_before_99-before.cmd -> .script.cmd symlink home/user/.local/share/chezmoi/run_00.cmd -> .script.cmd symlink home/user/.local/share/chezmoi/run_99.cmd -> .script.cmd symlink home/user/.local/share/chezmoi/run_after_00-after.cmd -> .script.cmd symlink home/user/.local/share/chezmoi/run_after_99-after.cmd -> .script.cmd exec chezmoi apply --force cmp stdout golden/apply -- golden/apply -- 00-before 99-before 00 99 00-after 99-after -- home/user/.local/share/chezmoi/.script.cmd -- @echo off set base=%~n0 :: on windows, there's some randomness at the beginning of the filename. remove it. echo %base:*.=% ================================================ FILE: internal/cmd/testdata/scripts/scriptperl.txtar ================================================ [!exec:perl] skip 'perl not found in $PATH' exec chezmoi apply stdout 'Hello from Perl' -- home/user/.local/share/chezmoi/run_script.pl -- #!/usr/bin/env perl print("Hello from Perl\n") ================================================ FILE: internal/cmd/testdata/scripts/scriptpython.txtar ================================================ [!exec:python3] skip 'python3 not found in $PATH' exec chezmoi apply stdout 'Hello from Python' -- home/user/.local/share/chezmoi/run_script.py -- #!/usr/bin/env python3 print("Hello from Python\n") ================================================ FILE: internal/cmd/testdata/scripts/scriptruby.txtar ================================================ [!exec:ruby] skip 'ruby not found in $PATH' exec chezmoi apply stdout 'Hello from Ruby' -- home/user/.local/share/chezmoi/run_script.rb -- #!/usr/bin/env ruby print "Hello from Ruby\n" ================================================ FILE: internal/cmd/testdata/scripts/scriptsdir_unix.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi apply runs scripts in .chezmoiscripts exec chezmoi apply cmp stdout golden/apply chhome home2/user # test that chezmoi apply fails if .chezmoiscripts contains a non-script ! exec chezmoi apply stderr ${CHEZMOISOURCEDIR@R}/\.chezmoiscripts/dot_file:\snot\sa\sscript$ chhome home3/user # test that chezmoi apply fails if .chezmoiscripts contains duplicate targets ! exec chezmoi apply stderr \.chezmoiscripts/script\.sh:\sinconsistent\sstate chhome home4/user # test that chezmoi apply fails if .chezmoiscripts contains any .chezmoi* files ! exec chezmoi apply stderr 'not allowed in \.chezmoiscripts directory' -- golden/apply -- script script in subdir -- home/user/.local/share/chezmoi/.chezmoiscripts/.ignore -- -- home/user/.local/share/chezmoi/.chezmoiscripts/run_script.sh -- #!/bin/sh echo script -- home/user/.local/share/chezmoi/.chezmoiscripts/subdir/run_script.sh -- #!/bin/sh echo script in subdir -- home2/user/.local/share/chezmoi/.chezmoiscripts/dot_file -- -- home3/user/.local/share/chezmoi/.chezmoiscripts/run_once_script.sh -- -- home3/user/.local/share/chezmoi/.chezmoiscripts/run_script.sh -- -- home4/user/.local/share/chezmoi/.chezmoiscripts/.chezmoiignore -- ================================================ FILE: internal/cmd/testdata/scripts/scriptsubdir_unix.txtar ================================================ [windows] skip 'UNIX only' [!umask:022] skip # test that scripts in subdirectories are run in the subdirectory exec chezmoi apply --force cmpenv stdout golden/apply exec chezmoi dump cmp stdout golden/dump.json exec chezmoi archive --gzip --output=archive.tar.gz exec tar -tzf archive.tar.gz [!openbsd] cmp stdout golden/archive [openbsd] cmp stdout golden/archive-openbsd -- golden/apply -- $HOME $HOME/dir $HOME/anotherdir -- golden/archive -- otherdir/script.sh anotherdir/ dir/ dir/script.sh otherdir/ anotherdir/script.sh -- golden/archive-openbsd -- otherdir/script.sh anotherdir dir dir/script.sh otherdir anotherdir/script.sh -- golden/dump.json -- { "anotherdir": { "type": "dir", "name": "anotherdir", "perm": 493 }, "anotherdir/script.sh": { "type": "script", "name": "anotherdir/script.sh", "contents": "#!/bin/sh\n\npwd\n", "condition": "always" }, "dir": { "type": "dir", "name": "dir", "perm": 493 }, "dir/script.sh": { "type": "script", "name": "dir/script.sh", "contents": "#!/bin/sh\n\npwd\n", "condition": "always" }, "otherdir": { "type": "dir", "name": "otherdir", "perm": 493 }, "otherdir/script.sh": { "type": "script", "name": "otherdir/script.sh", "contents": "#!/bin/sh\n\npwd\n", "condition": "always" } } -- home/user/.local/share/chezmoi/anotherdir/run_after_script.sh -- #!/bin/sh pwd -- home/user/.local/share/chezmoi/dir/run_script.sh -- #!/bin/sh pwd -- home/user/.local/share/chezmoi/otherdir/run_before_script.sh -- #!/bin/sh pwd ================================================ FILE: internal/cmd/testdata/scripts/scriptsubdir_windows.txtar ================================================ [unix] skip 'Windows only' unix2dos golden/apply unix2dos golden/archive # test that scripts in subdirectories are run in the subdirectory exec chezmoi apply --force cmpenv stdout golden/apply exec chezmoi dump cmp stdout golden/dump.json exec chezmoi archive --gzip --output=archive.tar.gz exec tar -tzf archive.tar.gz cmp stdout golden/archive -- golden/apply -- $HOME $HOME\dir $HOME\anotherdir -- golden/archive -- otherdir/script.cmd anotherdir/ dir/ dir/script.cmd otherdir/ anotherdir/script.cmd -- golden/dump.json -- { "anotherdir": { "type": "dir", "name": "anotherdir", "perm": 511 }, "anotherdir/script.cmd": { "type": "script", "name": "anotherdir/script.cmd", "contents": "@echo %cd%\n", "condition": "always" }, "dir": { "type": "dir", "name": "dir", "perm": 511 }, "dir/script.cmd": { "type": "script", "name": "dir/script.cmd", "contents": "@echo %cd%\n", "condition": "always" }, "otherdir": { "type": "dir", "name": "otherdir", "perm": 511 }, "otherdir/script.cmd": { "type": "script", "name": "otherdir/script.cmd", "contents": "@echo %cd%\n", "condition": "always" } } -- home/user/.local/share/chezmoi/anotherdir/run_after_script.cmd -- @echo %cd% -- home/user/.local/share/chezmoi/dir/run_script.cmd -- @echo %cd% -- home/user/.local/share/chezmoi/otherdir/run_before_script.cmd -- @echo %cd% ================================================ FILE: internal/cmd/testdata/scripts/scripttempdir.txtar ================================================ [windows] skip 'UNIX only' # test that chezmoi apply with a scriptTempDir set creates a temporary directory and runs scripts from that directory expandenv $CHEZMOICONFIGDIR/chezmoi.toml exec chezmoi apply stdout $WORK/script-tmp/.*script\.sh/run_$ grep $WORK/script-tmp/.*/modify_ $HOME/.file exists $WORK/script-tmp -- home/user/.config/chezmoi/chezmoi.toml -- scriptTempDir = "$WORK/script-tmp" -- home/user/.local/share/chezmoi/modify_dot_file -- #!/bin/sh echo $0/modify_ -- home/user/.local/share/chezmoi/run_script.sh -- #!/bin/sh echo $0/run_ ================================================ FILE: internal/cmd/testdata/scripts/secret.txtar ================================================ [unix] chmod 755 bin/secret [windows] unix2dos bin/secret.cmd # test secret template function exec chezmoi execute-template '{{ secret "password" }}' stdout ^password$ # test secretJSON template function exec chezmoi execute-template '{{ (secretJSON "{\"password\":\"secret\"}").password }}' stdout ^secret$ chhome home2/user # test secret.args exec chezmoi execute-template '{{ secret "password" }}' stdout '^arg password$' -- bin/secret -- #!/bin/sh echo "$*" -- bin/secret.cmd -- @echo off setlocal set out=%* set out=%out:\=% echo %out% endlocal -- home/user/.config/chezmoi/chezmoi.toml -- [secret] command = "secret" -- home2/user/.config/chezmoi/chezmoi.yaml -- secret: args: - "arg" command: "secret" ================================================ FILE: internal/cmd/testdata/scripts/sourcedir.txtar ================================================ exec chezmoi execute-template '{{ .chezmoi.sourceDir }}' stdout '/tmp/user' -- home/user/.config/chezmoi/chezmoi.toml -- sourceDir = "/tmp/user" ================================================ FILE: internal/cmd/testdata/scripts/sourcepath.txtar ================================================ # test that chezmoi source-path returns the source directory exec chezmoi source-path stdout ^${CHEZMOISOURCEDIR@R}$ # test that chezmoi source-path target returns the path to a target's source file exec chezmoi source-path $HOME${/}.file stdout ^${CHEZMOISOURCEDIR@R}/dot_file$ # test that chezmoi source-path returns an error if the target is not in the source state ! exec chezmoi source-path $HOME${/}.newfile stderr 'not managed' # test that chezmoi source-path returns an error if the target is not in the destination directory ! exec chezmoi source-path $WORK${/}etc${/}passwd stderr 'not in destination directory' chhome home2/user # test that chezmoi source-path target returns the path the target's source file when .chezmoiroot is used exec chezmoi source-path $HOME${/}.file stdout /home/dot_file$ -- home/user/.local/share/chezmoi/dot_file -- # contents of .file -- home2/user/.local/share/chezmoi/.chezmoiroot -- home -- home2/user/.local/share/chezmoi/home/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/state.txtar ================================================ [windows] mkdir $CHEZMOICONFIGDIR # FIXME remove this # test that chezmoi state get returns nothing for a non-existing bucket/key exec chezmoi state get --bucket=bucket --key=key ! stdout . # test that chezmoi state set sets a value that can be retrieved with chezmoi state get exec chezmoi state set --bucket=bucket --key=key --value=value exec chezmoi state get --bucket=bucket --key=key stdout ^value$ exec chezmoi state data --format=yaml cmp stdout golden/data.yaml # test that chezmoi state delete deletes a value exec chezmoi state delete --bucket=bucket --key=key ! stdout . exec chezmoi state data --format=yaml cmp stdout golden/data-after-delete.yaml -- golden/data-after-delete.yaml -- bucket: {} -- golden/data.yaml -- bucket: key: value ================================================ FILE: internal/cmd/testdata/scripts/state_unix.txtar ================================================ [windows] skip 'UNIX only' # test that the persistent state is only created on demand exec chezmoi state dump --format=yaml cmp stdout golden/dump.yaml ! exists $CHEZMOICONFIGDIR/chezmoistate.boltdb # test that chezmoi apply updates the persistent state exec chezmoi apply --force exists $CHEZMOICONFIGDIR/chezmoistate.boltdb # test that the persistent state records that script was run exec chezmoi state dump --format=yaml stdout 70396a619400b7f78dbb83ab8ddb76ffe0b8e31557e64bab2ca9677818a52135: stdout runAt: # test that chezmoi get-bucket gets a bucket exec chezmoi state get-bucket --bucket=scriptState stdout "runAt": # test that chezmoi delete-bucket deletes a bucket exec chezmoi state delete-bucket --bucket=scriptState exec chezmoi state dump --format=yaml ! stdout runAt: # test that chezmoi state reset removes the persistent state exec chezmoi --force state reset ! exists $CHEZMOICONFIGDIR/chezmoistate.boltdb # test that the --persistent-state option sets the persistent state file exec chezmoi apply --force stdout script exec chezmoi apply --force --persistent-state=$CHEZMOICONFIGDIR${/}chezmoistate2.boltdb exists $CHEZMOICONFIGDIR${/}chezmoistate2.boltdb stdout script exec chezmoi state dump --format=yaml --persistent-state=$CHEZMOICONFIGDIR${/}chezmoistate2.boltdb stdout 70396a619400b7f78dbb83ab8ddb76ffe0b8e31557e64bab2ca9677818a52135: stdout runAt: -- golden/dump.yaml -- configState: {} entryState: {} gitHubKeysState: {} gitHubLatestReleaseState: {} gitHubReleasesState: {} gitHubTagsState: {} gitHubVersionReleaseState: {} gitRepoExternalState: {} scriptState: {} -- home/user/.local/share/chezmoi/run_once_script.sh -- #!/bin/sh echo script ================================================ FILE: internal/cmd/testdata/scripts/state_windows.txtar ================================================ [unix] skip 'Windows only' # test that the persistent state is only created on demand exec chezmoi state dump --format=yaml cmp stdout golden/dump.yaml ! exists $CHEZMOICONFIGDIR/chezmoistate.boltdb # test that chezmoi apply updates the persistent state exec chezmoi apply --force exists $CHEZMOICONFIGDIR/chezmoistate.boltdb # test that the persistent state records that script was run exec chezmoi state dump --format=yaml stdout c22efac7af1ab6b9b5b74a852250ef7c4f78ebad7d5655bf5db3b5d9d3d2070c: stdout runAt: # test that chezmoi state reset removes the persistent state exec chezmoi --force state reset ! exists $CHEZMOICONFIGDIR/chezmoistate.boltdb -- golden/dump.yaml -- configState: {} entryState: {} gitHubKeysState: {} gitHubLatestReleaseState: {} gitHubReleasesState: {} gitHubTagsState: {} gitHubVersionReleaseState: {} gitRepoExternalState: {} scriptState: {} -- home/user/.local/share/chezmoi/run_once_script.cmd -- :: don't need to actually do anything ================================================ FILE: internal/cmd/testdata/scripts/status.txtar ================================================ # FIXME add more tests mkhomedir golden mksourcedir # test that chezmoi status lists all files to be added exec chezmoi status cmp stdout golden/status # test that the --path-style=absolute works as expected [unix] exec chezmoi status --path-style=absolute [unix] cmpenv stdout golden/status-absolute-flag # test that chezmoi status lists all files to be added exec chezmoi status cmp stdout golden/status # test that chezmoi status omits applied files exec chezmoi apply --force $HOME${/}.file exec chezmoi status cmp stdout golden/status-except-dot-file # test that chezmoi status is empty after apply exec chezmoi apply --force exec chezmoi status ! stdout . # test that chezmoi status identifies files that have been modified in the destination directory edit $HOME/.file exec chezmoi status cmp stdout golden/status-modified-file # test that chezmoi status does not emit status for equivalent modifications edit $CHEZMOISOURCEDIR/dot_file exec chezmoi status ! stdout . # test that the pathStyle config option works as expected [unix] chhome home2/user [unix] mksourcedir [unix] exec chezmoi status [unix] cmpenv stdout golden/status-absolute-config -- golden/status -- A .create A .dir A .dir/file A .dir/subdir A .dir/subdir/file A .empty A .executable A .file A .private A .readonly A .symlink A .template -- golden/status-absolute-config -- A $WORK/home2/user/.create A $WORK/home2/user/.dir A $WORK/home2/user/.dir/file A $WORK/home2/user/.dir/subdir A $WORK/home2/user/.dir/subdir/file A $WORK/home2/user/.empty A $WORK/home2/user/.executable A $WORK/home2/user/.file A $WORK/home2/user/.private A $WORK/home2/user/.readonly A $WORK/home2/user/.symlink A $WORK/home2/user/.template -- golden/status-absolute-flag -- A $WORK/home/user/.create A $WORK/home/user/.dir A $WORK/home/user/.dir/file A $WORK/home/user/.dir/subdir A $WORK/home/user/.dir/subdir/file A $WORK/home/user/.empty A $WORK/home/user/.executable A $WORK/home/user/.file A $WORK/home/user/.private A $WORK/home/user/.readonly A $WORK/home/user/.symlink A $WORK/home/user/.template -- golden/status-except-dot-file -- A .create A .dir A .dir/file A .dir/subdir A .dir/subdir/file A .empty A .executable A .private A .readonly A .symlink A .template -- golden/status-modified-file -- MM .file -- home2/user/.config/chezmoi/chezmoi.toml -- [status] pathStyle = "absolute" ================================================ FILE: internal/cmd/testdata/scripts/symlinks.txtar ================================================ # test that chezmoi apply removes a symlink if the target is empty symlink $HOME/.empty -> .file exec chezmoi apply $HOME${/}.empty ! exists $HOME/.empty # test that chezmoi apply evaluates symlink templates exec chezmoi apply $HOME${/}.template cmp $HOME/.template $HOME/.file # test that chezmoi add --template-symlinks replaces absolute symlinks, pointing to files inside home, with templates symlink $HOME/.symlink_absolute -> $HOME/.dir/subdir/file exec chezmoi add --template-symlinks $HOME${/}.symlink_absolute cmp $CHEZMOISOURCEDIR/symlink_dot_symlink_absolute.tmpl golden/symlink_dot_symlink_absolute.tmpl # test that chezmoi add --template-symlinks replaces absolute symlinks, pointing to files inside the source directory, with templates symlink $HOME/.symlink_source -> $CHEZMOISOURCEDIR/.dir/subdir/file exec chezmoi add --template-symlinks $HOME${/}.symlink_source cmp $CHEZMOISOURCEDIR/symlink_dot_symlink_source.tmpl golden/symlink_dot_symlink_source.tmpl chhome home2/user # test that chezmoi add reads add.templateSymlinks from the config file symlink $HOME/.symlink_absolute -> $HOME/.dir/subdir/file exec chezmoi add --template-symlinks $HOME${/}.symlink_absolute cmp $CHEZMOISOURCEDIR/symlink_dot_symlink_absolute.tmpl golden/symlink_dot_symlink_absolute.tmpl -- golden/symlink_dot_symlink_absolute.tmpl -- {{ .chezmoi.homeDir }}/.dir/subdir/file -- golden/symlink_dot_symlink_source.tmpl -- {{ .chezmoi.sourceDir }}/.dir/subdir/file -- home/user/.file -- # contents of .file -- home/user/.local/share/chezmoi/symlink_dot_empty -- -- home/user/.local/share/chezmoi/symlink_dot_template.tmpl -- {{ ".file" }} -- home2/user/.config/chezmoi/chezmoi.toml -- [add] templateSymlinks = true ================================================ FILE: internal/cmd/testdata/scripts/symlinks_windows.txtar ================================================ [unix] skip 'Windows only' # test that chezmoi apply normalizes symlinks exec chezmoi apply $HOME${/}.symlink_backward_slash $HOME${/}.symlink_forward_slash readlink $HOME/.symlink_backward_slash $HOME\.dir\file readlink $HOME/.symlink_forward_slash $HOME\.dir\file # test that the persistent state matches the actual state exec chezmoi verify -- home/user/.dir/file -- # contents of .dir/file -- home/user/.local/share/chezmoi/symlink_dot_symlink_backward_slash.tmpl -- {{ env "HOME" }}\.dir\file -- home/user/.local/share/chezmoi/symlink_dot_symlink_forward_slash.tmpl -- {{ env "HOME" }}/.dir/file ================================================ FILE: internal/cmd/testdata/scripts/targetpath.txtar ================================================ [windows] skip 'test requires path separator to be forward slash' mksourcedir # test that chezmoi target-path without arguments prints the target directory exec chezmoi target-path stdout ^${HOME@R}$ # test that chezmoi target-path prints the target path of a directory exec chezmoi target-path $CHEZMOISOURCEDIR/dot_dir/exact_subdir stdout ^${HOME@R}/.dir/subdir$ # test that chezmoi target-path prints the target path of a file exec chezmoi target-path $CHEZMOISOURCEDIR/dot_dir/exact_subdir/file stdout ^${HOME@R}/.dir/subdir/file$ # test that chezmoi target-path prints the target path of a script exec chezmoi target-path $CHEZMOISOURCEDIR/run_script stdout ^${HOME@R}/script$ # test that chezmoi target-path prints the target path of a symlink exec chezmoi target-path $CHEZMOISOURCEDIR/symlink_dot_symlink stdout ^${HOME@R}/.symlink$ chhome home2/user # test that chezmoi target-path respects .chezmoiroot exec chezmoi target-path $CHEZMOISOURCEDIR/home/dot_file stdout ^${HOME@R}/\.file$ # test that chezmoi target-path respects .chezmoiscripts in .chezmoiroot exec chezmoi target-path $CHEZMOISOURCEDIR/home/.chezmoiscripts/run_script.sh stdout ^${HOME@R}/\.chezmoiscripts/script\.sh$ -- home/user/.local/share/chezmoi/run_script -- #!/bin/sh -- home2/user/.local/share/chezmoi/.chezmoiroot -- home -- home2/user/.local/share/chezmoi/home/.chezmoiscripts/run_script.sh -- #!/bin/sh -- home2/user/.local/share/chezmoi/home/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/templatedata.txtar ================================================ # test that .chezmoi.sourceDir can be used with joinPath [unix] exec chezmoi execute-template '{{ joinPath .chezmoi.sourceDir ".file" }}' [unix] stdout ${CHEZMOISOURCEDIR@R}/.file # test that .chezmoi.sourceFile is set exec chezmoi cat $HOME${/}.file stdout dot_file.tmpl # test that .chezmoi.kernel is set on linux [linux] exec chezmoi execute-template '{{ .chezmoi.kernel.ostype }}' [linux] stdout Linux chhome home2/user # test that .chezmoidata. and .chezmoitemplates are available in .chezmoiignore exec chezmoi apply exists $HOME/.file1 ! exists $HOME/.file2 chhome home3/user # test that data execute-template ignores template errors exec chezmoi data stdout ok # test that chezmoi execute-template ignores template errors exec chezmoi execute-template '{{ template "template" . }}' stdout ok [unix] chhome home4/user # test that .chezmoi.sourceFile is set [unix] exec chezmoi cat $HOME${/}.file [unix] cmpenv stdout golden/dot_file -- golden/dot_file -- dot_file.tmpl $WORK/home4/user/.file -- home/user/.local/share/chezmoi/dot_file.tmpl -- {{ .chezmoi.sourceFile }} -- home2/user/.local/share/chezmoi/.chezmoidata.toml -- filename = ".file2" -- home2/user/.local/share/chezmoi/.chezmoiignore -- {{ template "ignore" . }} -- home2/user/.local/share/chezmoi/.chezmoitemplates/ignore -- {{ .filename }} -- home2/user/.local/share/chezmoi/dot_file1 -- # contents of .file1 -- home2/user/.local/share/chezmoi/dot_file2 -- # contents of .file2 -- home3/user/.local/share/chezmoi/.chezmoi.toml.tmpl -- {{ "invalid template" -- home3/user/.local/share/chezmoi/.chezmoidata.yaml -- message: ok -- home3/user/.local/share/chezmoi/.chezmoiexternal.toml -- {{ "invalid template" -- home3/user/.local/share/chezmoi/.chezmoiignore -- {{ "invalid template" -- home3/user/.local/share/chezmoi/.chezmoiremove -- {{ "invalid template" -- home3/user/.local/share/chezmoi/.chezmoitemplates/template -- {{ .message }} -- home4/user/.local/share/chezmoi/dot_file.tmpl -- {{ .chezmoi.sourceFile }} {{ .chezmoi.targetFile }} ================================================ FILE: internal/cmd/testdata/scripts/templatedirectives.txtar ================================================ hexdecode golden/encoding.hex # test --left-delimiter and --right-delimiter flags to chezmoi execute-template exec chezmoi execute-template --left-delimiter=[[ --right-delimiter=]] '[[ "ok" ]]' stdout ^ok$ # test that the encoding can be set in files exec chezmoi cat $HOME${/}encoding cmp stdout golden/encoding # test that missing key behavior can be set in files exec chezmoi cat $HOME${/}missing-key cmp stdout golden/missing-key # test that template delimiters can be set in files exec chezmoi cat $HOME${/}nested-template cmp stdout golden/nested-template -- golden/encoding.hex -- fffe # UTF-16 BOM 480065006c006c006f002c00200077006f0072006c00640021000a00 # "Hello, world!\n" -- golden/missing-key -- -- golden/nested-template -- (nested) -- home/user/.local/share/chezmoi/.chezmoitemplates/nested -- # chezmoi:template:left-delimiter=(( right-delimiter=)) ((- . -)) -- home/user/.local/share/chezmoi/encoding.tmpl -- # chezmoi:template:encoding=utf-16-le-bom Hello, world! -- home/user/.local/share/chezmoi/missing-key.tmpl -- # chezmoi:template:missing-key=default {{ .MissingKey }} -- home/user/.local/share/chezmoi/nested-template.tmpl -- # chezmoi:template:left-delimiter=[[ right-delimiter=]] ([[ template "nested" "nested" ]]) ================================================ FILE: internal/cmd/testdata/scripts/templatefuncs.txtar ================================================ [unix] chmod 755 bin/chezmoi-output-test [unix] chmod 755 bin/generate-color-formats [unix] chmod 755 bin/ioreg [unix] chmod 755 bin/executable [windows] unix2dos bin/chezmoi-output-test.cmd symlink $HOME/symlink -> dir # test comment template function exec chezmoi execute-template '{{ "line1\nline2" | comment "# " }}' rmfinalnewline golden/comment cmp stdout golden/comment # test completion template function exec chezmoi execute-template '{{ completion "zsh" }}' stdout '^# zsh completion for chezmoi' # test deleteValueAtPath template function exec chezmoi execute-template '{{ dict "a" (dict "b" (dict "c" 1 "d" 2)) | deleteValueAtPath "a.b.c" | toJson }}' rmfinalnewline golden/deleteValueAtPath cmp stdout golden/deleteValueAtPath # test ensureLinePrefix template function with two arguments exec chezmoi execute-template '{{ "### Heading\nBody\n" | ensureLinePrefix "#" }}' cmp stdout golden/ensureLinePrefix2 # test ensureLinePrefix template function with three arguments exec chezmoi execute-template '{{ "### Heading\nBody\n" | ensureLinePrefix "#" "# " }}' cmp stdout golden/ensureLinePrefix3 # test eqFold template function exec chezmoi execute-template '{{ eqFold "foo" "Foo" "FOO" }}' stdout '^true$' # test that the fromJson template function can deserialize JSON values exec chezmoi execute-template '{{ fromJson "1" }}' stdout '^1$' # test that the fromJson template function can deserialize JSON arrays exec chezmoi execute-template '{{ fromJson "[1, 2]" }}' stdout '^\[1 2\]$' # test fromJsonc template function stdin golden/example.jsonc exec chezmoi execute-template --with-stdin '{{ fromJsonc .chezmoi.stdin | toJson }}' stdout '{"key":1}' # test glob template function exec chezmoi execute-template '{{ glob "*.txt" | join "\n" }}{{ "\n" }}' cmp stdout golden/glob # test hexDecode template function exec chezmoi execute-template '{{ "6578616d706c65" | hexDecode }}' stdout '^example$' # test hexEncode template function exec chezmoi execute-template '{{ "example" | hexEncode }}' stdout '^6578616d706c65$' # test ioreg template function [darwin] exec chezmoi execute-template '{{ index ioreg "IOKitBuildVersion" }}' [darwin] stdout 'Darwin Kernel Version' # test include template function with absolute path exec chezmoi execute-template '{{ joinPath (env "HOME") ".include" | include }}' cmp stdout golden/include-abspath # test include template function with relative paths exec chezmoi execute-template '{{ include ".include" }}' cmp stdout golden/include-relpath # test includeTemplate template function exec chezmoi execute-template '{{ includeTemplate ".template" "data" }}' stdout ^data$ # test includeTemplate template function searches .chezmoitemplates exec chezmoi execute-template '{{ includeTemplate "template" "data" }}' stdout ^data$ # test joinPath template function exec chezmoi execute-template '{{ joinPath "a" "b" }}' stdout a${/}b exec chezmoi execute-template '{{ joinPath .chezmoi.destDir "string" }}' stdout home${/@R}user${/@R}string # test jq template function exec chezmoi execute-template '{{ dict "key" "value" | jq ".key" | first }}' stdout ^value$ # test isExecutable template function positive test case [unix] exec chezmoi execute-template '{{ isExecutable "bin/executable" }}' [windows] exec chezmoi execute-template '{{ isExecutable "bin/executable.cmd" }}' stdout ^true$ # test isExecutable template function negative test case exec chezmoi execute-template '{{ isExecutable "bin/not-executable" }}' stdout ^false$ # test findExecutable template function to find in specified script varargs - success [!windows] exec chezmoi execute-template '{{ findExecutable "echo" (list "/lib" "/bin" "/usr/bin") }}' [!windows] stdout ^/bin/echo$ # test findOneExecutable template function to find in specified script varargs - success [!windows] exec chezmoi execute-template '{{ findOneExecutable (list "chezmoish" "echo") (list "/lib" "/bin" "/usr/bin") }}' [!windows] stdout ^/bin/echo$ # test findExecutable template function to find in specified script varargs - failure [!windows] exec chezmoi execute-template '{{ findExecutable "echo" (list "/lib") }}' [!windows] stdout ^$ # test findExecutable template function to find in specified script - success [!windows] exec chezmoi execute-template '{{ findExecutable "echo" (list "/lib" "/bin" "/usr/bin") }}' [!windows] stdout ^/bin/echo$ # test findExecutable template function to find in specified script - failure [!windows] exec chezmoi execute-template '{{ findExecutable "echo" (list "/lib") }}' [!windows] stdout ^$ # test findExecutable template function to find in specified script - success with extension [windows] exec chezmoi execute-template '{{ findExecutable "git.exe" (list "c:\\windows\\system32" "c:\\windows\\system64" "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0" "C:\\Program Files\\Git\\cmd") }}' [windows] stdout 'git' # test findExecutable template function to find in specified script - success without extension [windows] exec chezmoi execute-template '{{ findExecutable "git" (list "c:\\windows\\system32" "c:\\windows\\system64" "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0" "C:\\Program Files\\Git\\cmd") }}' [windows] stdout 'git' # test findExecutable template function to find in specified script - failure [windows] exec chezmoi execute-template '{{ findExecutable "asdf" (list "c:\\windows\\system32" "c:\\windows\\system64" "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0" "C:\\Program Files\\Git\\cmd") }}' [windows] stdout '^$' # test lookPath template function to find in PATH exec chezmoi execute-template '{{ lookPath "go" }}' stdout go$exe # test lookPath template function to check if file exists exec chezmoi execute-template '{{ lookPath "/non-existing-file" }}' ! stdout . # test lstat template function exec chezmoi execute-template '{{ (joinPath .chezmoi.homeDir "symlink" | lstat).type }}' stdout ^symlink$ # test mozillaInstallHash template function exec chezmoi execute-template '{{ mozillaInstallHash "/Applications/Firefox.app/Contents/MacOS" }}' stdout 2656FF1E876E9973 # test the output and fromJson template functions [unix] exec chezmoi execute-template '{{ $red := output "generate-color-formats" "#ff0000" | fromJson }}{{ $red.rgb.r }}' [unix] stdout '^255$' # test the outputList and fromJson template functions [unix] exec chezmoi execute-template '{{ $red := outputList "generate-color-formats" (list "#ff0000" ) | fromJson }}{{ $red.rgb.r }}' [unix] stdout '^255$' # test that the output function returns an error if the command fails [unix] ! exec chezmoi execute-template '{{ output "false" }}' [unix] stderr 'error calling output: .*/false: exit status 1' # test pruneEmptyDicts template function exec chezmoi execute-template '{{ dict "key1" "value1" "key2" (dict) | pruneEmptyDicts | toJson }}' rmfinalnewline golden/pruneEmptyDicts cmp stdout golden/pruneEmptyDicts # test replaceAllRegex template function exec chezmoi execute-template '{{ "foo bar baz" | replaceAllRegex "ba" "BA" }}' stdout 'foo BAr BAz' # test the abortEmpty template function exec chezmoi execute-template 'before {{ abortEmpty }} after' ! stdout . # test setValueAtPath template function exec chezmoi execute-template '{{ dict | setValueAtPath "key1.key2" "value2" | toJson }}' rmfinalnewline golden/setValueAtPath cmp stdout golden/setValueAtPath # test toIni template function exec chezmoi execute-template '{{ dict "key" "value" "section" (dict "subkey" "subvalue") | toIni }}' cmp stdout golden/toIni # test stat template function exec chezmoi execute-template '{{ (joinPath .chezmoi.homeDir "symlink" | stat).isDir }}' stdout true # test that the output template function returns a command's output exec chezmoi execute-template '{{ output "chezmoi-output-test" "arg" | trim }}' stdout arg # test that the output template function fails if the command fails ! exec chezmoi execute-template '{{ output "false" }}' # test fromToml template function exec chezmoi execute-template '{{ (fromToml "[section]\nkey = \"value\"").section.key }}' stdout '^value$' # test toToml template function exec chezmoi execute-template '{{ dict "key" "value" | toToml }}' stdout '^key = .value.$' # test that the toPrettyJson template function does not escape HTML characters, see https://github.com/golang/go/blob/7a6ddbb425218b2f4866478d0c673ba82c8438e6/src/encoding/json/encode.go#L48-L55 exec chezmoi execute-template '{{ dict "a" (dict "b" "&") | toPrettyJson " " }}' cmp stdout golden/toPrettyJson # test that toStrings flattens nested lists exec chezmoi execute-template '{{ toStrings (list "a" "b" (list "c" "d")) }}' stdout '\[a b c d\]' # test fromYaml template function exec chezmoi execute-template '{{ (fromYaml "key1: value1\nkey2: value2").key2 }}' stdout '^value2$' # test toYaml template function exec chezmoi execute-template '{{ dict "key" "value" | toYaml }}' stdout '^key: value$' # test that the overridden splitList function's output is compatible with quoteList exec chezmoi execute-template '{{ "a b" | splitList " " | quoteList }}' stdout '["a" "b"]' -- bin/chezmoi-output-test -- #!/bin/sh echo "$*" -- bin/chezmoi-output-test.cmd -- @echo off setlocal set out=%* set out=%out:\=% echo %out% endlocal -- bin/executable -- #!/bin/sh -- bin/executable.cmd -- -- bin/generate-color-formats -- #!/bin/sh case "$1" in "#ff0000") cat <" ;; esac -- bin/ioreg -- #!/bin/sh echo '' echo '' echo '' echo '' echo ' IOKitBuildVersion' echo ' Darwin Kernel Version 21.1.0: Wed Oct 13 17:33:24 PDT 2021; root:xnu-8019.41.5~1/RELEASE_ARM64_T8101' echo '' echo '' -- bin/not-executable -- -- golden/comment -- # line1 # line2 -- golden/deleteValueAtPath -- {"a":{"b":{"d":2}}} -- golden/ensureLinePrefix2 -- ### Heading #Body -- golden/ensureLinePrefix3 -- ### Heading # Body -- golden/example.jsonc -- { "key": 1, // Comment } -- golden/expected -- 255 -- golden/glob -- file1.txt file2.txt -- golden/include-abspath -- # contents of .include -- golden/include-relpath -- # contents of .local/share/chezmoi/.include -- golden/pruneEmptyDicts -- {"key1":"value1"} -- golden/setValueAtPath -- {"key1":{"key2":"value2"}} -- golden/toIni -- key = value [section] subkey = subvalue -- golden/toPrettyJson -- { "a": { "b": "&" } } -- home/user/.include -- # contents of .include -- home/user/.local/share/chezmoi/.chezmoitemplates/template -- {{ . }} -- home/user/.local/share/chezmoi/.include -- # contents of .local/share/chezmoi/.include -- home/user/.local/share/chezmoi/.template -- chezmoi:template:left-delimiter=[[ right-delimiter=]] [[ . ]] -- home/user/.local/share/chezmoi/template -- -- home/user/dir/.keep -- -- home/user/file1.txt -- -- home/user/file2.txt -- ================================================ FILE: internal/cmd/testdata/scripts/templatevars.txtar ================================================ exec chezmoi execute-template '{{ .chezmoi.arch }}' [386] stdout 386 [amd64] stdout amd64 [arm] stdout arm [arm64] stdout arm64 [ppc64] stdout ppc64 [ppc64le] stdout ppc64le exec chezmoi execute-template '{{ index .chezmoi.args 1 }}' stdout execute-template exec chezmoi execute-template '{{ .chezmoi.executable }}' stdout [\\/]chezmoi(.exe)?$ [unix] exec chezmoi execute-template '{{ .chezmoi.homeDir }}' [unix] stdout ${HOME@R} exec chezmoi execute-template '{{ .chezmoi.os }}' [darwin] stdout darwin [freebsd] stdout freebsd [linux] stdout linux [openbsd] stdout openbsd [windows] stdout windows exec chezmoi execute-template '{{ .chezmoi.sourceDir }}' stdout ${CHEZMOISOURCEDIR@R} ================================================ FILE: internal/cmd/testdata/scripts/textconv.txtar ================================================ [windows] skip 'UNIX only' [!exec:tr] skip 'tr not found in $PATH' # test that chezmoi diff uses textconv exec chezmoi diff [umask:002] cmp stdout golden/diff-umask-002 [umask:022] cmp stdout golden/diff-umask-022 # test that chezmoi apply uses textconv exec chezmoi apply --verbose [umask:002] cmp stdout golden/diff-umask-002 [umask:022] cmp stdout golden/diff-umask-022 cmp $HOME/file.txt golden/file.txt # test that chezmoi apply uses textconv in interactive diffs edit $HOME/file.txt stdin golden/diff-overwrite exec chezmoi apply --no-tty stdout '^ # CONTENTS OF FILE.TXT' stdout '^-# EDITED' -- golden/diff-overwrite -- diff overwrite -- golden/diff-umask-002 -- diff --git a/file.txt b/file.txt index b0ebb2af412bf3812b0bf8c5d7b950feb8a701be..4d977287915d918f15ef3df13146c1fa58914d30 100664 --- a/file.txt +++ b/file.txt @@ -1 +1 @@ -# PREVIOUS CONTENTS OF FILE.TXT +# CONTENTS OF FILE.TXT -- golden/diff-umask-022 -- diff --git a/file.txt b/file.txt index b0ebb2af412bf3812b0bf8c5d7b950feb8a701be..4d977287915d918f15ef3df13146c1fa58914d30 100644 --- a/file.txt +++ b/file.txt @@ -1 +1 @@ -# PREVIOUS CONTENTS OF FILE.TXT +# CONTENTS OF FILE.TXT -- golden/file.txt -- # contents of file.txt -- home/user/.config/chezmoi/chezmoi.yaml -- textconv: - pattern: '**/*.txt' command: tr args: - a-z - A-Z -- home/user/.local/share/chezmoi/file.txt -- # contents of file.txt -- home/user/file.txt -- # previous contents of file.txt ================================================ FILE: internal/cmd/testdata/scripts/tilde.txtar ================================================ mkhomedir mksourcedir exec chezmoi source-path ~/.file stdout $CHEZMOISOURCEDIR/dot_file ! exec chezmoi source-path ~ stderr 'not managed' ================================================ FILE: internal/cmd/testdata/scripts/transparentencryption.txtar ================================================ # test that chezmoi add --encrypt with transparent encryption adds the encrypted_ attribute but does not encrypt the file exec chezmoi add --encrypt $HOME${/}.file cmp $CHEZMOISOURCEDIR/encrypted_dot_file golden/.file # test that chezmoi cat with transparent encryption returns the ciphertext exec chezmoi cat $HOME${/}.file cmp stdout golden/.file -- golden/.file -- # contents of .file -- home/user/.config/chezmoi/chezmoi.toml -- encryption = "transparent" -- home/user/.file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/umask_unix.txtar ================================================ [windows] skip 'UNIX only' [!umask:022] skip chmod 777 $HOME/.file # test that chezmoi diff prints a diff when a file's permissions are different exec chezmoi diff cmp stdout golden/diff # test that chezmoi verify fails when a file's permissions are different ! exec chezmoi verify # test that chezmoi apply updates file permissions exec chezmoi apply --force exec chezmoi diff ! stdout . exec chezmoi verify # test that chezmoi ignores incorrect permissions in the persistent state if the actual file permissions match and updates the persistent state exec chezmoi state get --bucket=entryState --key=$HOME/.file cmp stdout golden/state-file-get.json exec chezmoi state set --bucket=entryState --key=$HOME/.file --value={"type":"file","mode":436,"contentsSHA256":"634a4dd193c7b3b926d2e08026aa81a416fd41cec52854863b974af422495663"} exec chezmoi apply --verbose ! stdout . exec chezmoi state get --bucket=entryState --key=$HOME/.file cmp stdout golden/state-file-get.json # test that chezmoi ignores incorrect permissions in the persistent state if the actual dir permissions match and updates the persistent state # chezmoi state get --bucket=entryState --key=$HOME/.dir # FIXME understand why un-commenting this and the next line causes the test to fail # cmp stdout golden/state-dir-get exec chezmoi state set --bucket=entryState --key=$HOME/.dir --value={"type":"dir","mode":509} exec chezmoi apply --verbose ! stdout . exec chezmoi state get --bucket=entryState --key=$HOME/.dir cmp stdout golden/state-dir-get.json -- golden/diff -- diff --git a/.file b/.file old mode 100777 new mode 100644 -- golden/state-dir-get.json -- { "type": "dir", "mode": 2147484141 } -- golden/state-file-get.json -- { "type": "file", "mode": 420, "contentsSHA256": "634a4dd193c7b3b926d2e08026aa81a416fd41cec52854863b974af422495663" } -- home/user/.dir/.keep -- -- home/user/.file -- # contents of .file -- home/user/.local/share/chezmoi/dot_dir/.keep -- -- home/user/.local/share/chezmoi/dot_file -- # contents of .file ================================================ FILE: internal/cmd/testdata/scripts/unmanaged.txtar ================================================ mkhomedir mksourcedir exec chezmoi unmanaged cmp stdout golden/unmanaged rm $CHEZMOISOURCEDIR/dot_dir exec chezmoi unmanaged cmp stdout golden/unmanaged-dir rm $CHEZMOISOURCEDIR/dot_file exec chezmoi unmanaged cmp stdout golden/unmanaged-dir-file # test chezmoi unmanaged with arguments exec chezmoi unmanaged $HOME${/}.dir $HOME${/}.file cmp stdout golden/unmanaged-with-args # test chezmoi unmanaged, with child of unmanaged dir as argument exec chezmoi unmanaged $HOME${/}.dir/subdir cmp stdout golden/unmanaged-inside-unmanaged # test chezmoi unmanaged with managed arguments exec chezmoi unmanaged $HOME${/}.create $HOME${/}.file cmp stdout golden/unmanaged-with-some-managed # test that chezmoi unmanaged with absent paths should fail ! exec chezmoi unmanaged $HOME${/}absent-path # test chezmoi unmanaged --path-style=absolute [unix] exec chezmoi unmanaged --path-style=absolute [unix] cmpenv stdout golden/unmanaged-absolute # test chezmoi unmanaged --exclude=dirs exec chezmoi unmanaged --exclude=dirs cmpenv stdout golden/unmanaged-exclude-dirs # test chezmoi unmanaged --include=dirs exec chezmoi unmanaged --include=dirs cmpenv stdout golden/unmanaged-include-dirs # test chezmoi unmanaged --path-style=source-absolute ! exec chezmoi unmanaged --path-style=source-absolute stderr 'flag: invalid value' -- golden/unmanaged -- .local -- golden/unmanaged-absolute -- $WORK/home/user/.dir $WORK/home/user/.file $WORK/home/user/.local -- golden/unmanaged-dir -- .dir .local -- golden/unmanaged-dir-file -- .dir .file .local -- golden/unmanaged-exclude-dirs -- .file -- golden/unmanaged-include-dirs -- .dir .local -- golden/unmanaged-inside-unmanaged -- .dir/subdir -- golden/unmanaged-with-args -- .dir .file -- golden/unmanaged-with-some-managed -- .file ================================================ FILE: internal/cmd/testdata/scripts/unmanagedtree.txtar ================================================ # test that chezmoi unmanaged --tree produces tree-like output exec chezmoi unmanaged --tree cmp stdout golden/stdout -- golden/stdout -- .dir file subdir .local -- home/user/.dir/file -- -- home/user/.dir/subdir/file -- -- home/user/.local/share/chezmoi/dot_dir/.keep -- ================================================ FILE: internal/cmd/testdata/scripts/update.txtar ================================================ [!exec:git] skip 'git not found in $PATH' mkgitconfig mkhomedir golden mkhomedir exec git init --bare $WORK/dotfiles.git exec chezmoi init file://$WORK/dotfiles.git # create a commit exec chezmoi add $HOME${/}.file exec chezmoi git add dot_file exec chezmoi git commit -- --message 'Add dot_file' exec chezmoi git push chhome home2/user # test that chezmoi init --apply inits and applies mkgitconfig exec chezmoi init --apply --force file://$WORK/dotfiles.git cmp $HOME/.file golden/.file chhome home/user # create and push a new commit edit $CHEZMOISOURCEDIR/dot_file exec chezmoi git -- add dot_file exec chezmoi git -- commit -m 'Update dot_file' exec chezmoi git -- push chhome home2/user # test chezmoi update exec chezmoi update grep -count=1 '# edited' $HOME/.file chhome home/user # create and push a new commit edit $CHEZMOISOURCEDIR/dot_file exec chezmoi git -- add dot_file exec chezmoi git -- commit -m 'Update dot_file' exec chezmoi git -- push chhome home2/user # test chezmoi update --apply=false exec chezmoi update --apply=false grep -count=1 '# edited' $HOME/.file exec chezmoi apply --force grep -count=2 '# edited' $HOME/.file # test chezmoi update --init cp golden/.chezmoi.toml.tmpl $CHEZMOISOURCEDIR exec chezmoi update --init exec chezmoi execute-template '{{ .key }}' stdout value -- golden/.chezmoi.toml.tmpl -- [data] key = "value" ================================================ FILE: internal/cmd/testdata/scripts/upgrade.txtar ================================================ [!env:CHEZMOI_GITHUB_TOKEN] skip '$CHEZMOI_GITHUB_TOKEN not set' # test that chezmoi upgrade succeeds exec chezmoi upgrade --executable=$WORK${/}chezmoi$exe --method=replace-executable exec $WORK/chezmoi$exe --version -- chezmoi -- -- chezmoi.exe -- ================================================ FILE: internal/cmd/testdata/scripts/vault.txtar ================================================ mockcommand bin/vault # test vault template function exec chezmoi execute-template '{{ (vault "secret/examplesecret").data.data.password }}' stdout ^examplepassword$ -- bin/vault.yaml -- responses: - args: 'kv get -format=json secret/examplesecret' response: | { "request_id": "d90311b6-2f3f-768e-656c-ce768e773b09", "lease_id": "", "lease_duration": 0, "renewable": false, "data": { "data": { "password": "examplepassword" }, "metadata": { "created_time": "2021-01-11T21:48:46.961974384Z", "deletion_time": "", "destroyed": false, "version": 1 } }, "warnings": null } default: response: 'Usage: vault [args]' exitCode: 127 ================================================ FILE: internal/cmd/testdata/scripts/verify.txtar ================================================ [!umask:022] skip mkhomedir golden mkhomedir mksourcedir # test that chezmoi verify succeeds exec chezmoi verify # test that chezmoi verify fails when a file is added to the source state cp golden/dot_newfile $CHEZMOISOURCEDIR/dot_newfile ! exec chezmoi verify exec chezmoi forget --force $HOME${/}.newfile exec chezmoi verify # test that chezmoi verify fails when a file is edited edit $HOME/.file ! exec chezmoi verify exec chezmoi apply --force $HOME${/}.file exec chezmoi verify # test that chezmoi verify fails when a file is removed from the destination directory rm $HOME/.file ! exec chezmoi verify exec chezmoi apply --force $HOME${/}.file exec chezmoi verify # test that chezmoi verify fails when a directory is removed from the destination directory rm $HOME/.dir ! exec chezmoi verify mkdir $HOME/.dir exec chezmoi apply --force $HOME${/}.dir exec chezmoi verify [windows] stop 'remaining tests use file modes' # test that chezmoi verify fails when a file's permissions are changed chmod 777 $HOME/.file ! exec chezmoi verify exec chezmoi apply --force $HOME${/}.file exec chezmoi verify # test that chezmoi verify fails when a directory's permissions are changed chmod 700 $HOME/.dir ! exec chezmoi verify exec chezmoi apply --force $HOME${/}.dir exec chezmoi verify -- golden/dot_newfile -- # contents of .newfile ================================================ FILE: internal/cmd/testdata/scripts/version.txtar ================================================ exec chezmoi --version stdout 'chezmoi version v2\.0\.0' ================================================ FILE: internal/cmd/testdata/scripts/workingtree.txtar ================================================ [!exec:true] skip 'true not found in $PATH' mkhomedir # test that chezmoi cd creates the working tree if needed ! exists $CHEZMOISOURCEDIR exec chezmoi cd exists $CHEZMOISOURCEDIR exists $CHEZMOISOURCEDIR/home # test that chezmoi add adds a file into the source directory exec chezmoi add $HOME${/}.file cp golden/.file $CHEZMOISOURCEDIR/home/dot_file chhome home2/user # test chezmoi init --working-tree creates the correct directory exec chezmoi init --working-tree=$HOME${/}.dotfiles --source=$HOME${/}.dotfiles${/}home exists $HOME/.dotfiles/.git exists $HOME/.dotfiles/home chhome home3/user # test that chezmoi add returns an error if the source directory is not in the working tree mkhomedir ! exec chezmoi add $HOME${/}.file stderr 'not in' -- golden/.file -- # contents of .file -- home/user/.config/chezmoi/chezmoi.toml -- workingTree = "~/.local/share/chezmoi" sourceDir = "~/.local/share/chezmoi/home" [cd] command = "true" -- home3/user/.config/chezmoi/chezmoi.toml -- workingTree = "~/.local/share/chezmoi/home" sourceDir = "~/.local/share/chezmoi" ================================================ FILE: internal/cmd/textconv.go ================================================ package cmd import ( "bytes" "log/slog" "os" "os/exec" "github.com/bmatcuk/doublestar/v4" "chezmoi.io/chezmoi/internal/chezmoilog" ) type textConvElement struct { Pattern string `json:"pattern" mapstructure:"pattern" yaml:"pattern"` Command string `json:"command" mapstructure:"command" yaml:"command"` Args []string `json:"args" mapstructure:"args" yaml:"args"` } type textConv []*textConvElement func (t textConv) convert(path string, data []byte) ([]byte, bool, error) { var longestPatternElement *textConvElement for _, command := range t { ok, err := doublestar.Match(command.Pattern, path) if err != nil { return nil, false, err } if !ok { continue } if longestPatternElement == nil || len(command.Pattern) > len(longestPatternElement.Pattern) { longestPatternElement = command } } if longestPatternElement == nil { return data, false, nil } cmd := exec.Command(longestPatternElement.Command, longestPatternElement.Args...) cmd.Stdin = bytes.NewReader(data) cmd.Stderr = os.Stderr convertedData, err := chezmoilog.LogCmdOutput(slog.Default(), cmd) if err != nil { return nil, false, err } return convertedData, true, nil } ================================================ FILE: internal/cmd/unmanagedcmd.go ================================================ package cmd import ( "fmt" "io/fs" "slices" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoiset" ) type unmanagedCmdConfig struct { filter *chezmoi.EntryTypeFilter nulPathSeparator bool pathStyle *choiceFlag tree bool } func (c *Config) newUnmanagedCmd() *cobra.Command { unmanagedCmd := &cobra.Command{ GroupID: groupIDAdvanced, Use: "unmanaged [path]...", Short: "List the unmanaged files in the destination directory", Long: mustLongHelp("unmanaged"), Example: example("unmanaged"), Args: cobra.ArbitraryArgs, RunE: c.makeRunEWithSourceState(c.runUnmanagedCmd), Annotations: newAnnotations( persistentStateModeReadWrite, ), } unmanagedCmd.Flags().VarP(c.unmanaged.filter.Exclude, "exclude", "x", "Exclude entry types") unmanagedCmd.Flags().VarP(c.unmanaged.filter.Include, "include", "i", "Include entry types") unmanagedCmd.Flags(). BoolVarP(&c.unmanaged.nulPathSeparator, "nul-path-separator", "0", c.unmanaged.nulPathSeparator, "Use the NUL character as a path separator") unmanagedCmd.Flags().VarP(c.unmanaged.pathStyle, "path-style", "p", "Path style") must(unmanagedCmd.RegisterFlagCompletionFunc("path-style", c.unmanaged.pathStyle.FlagCompletionFunc())) unmanagedCmd.Flags().BoolVarP(&c.unmanaged.tree, "tree", "t", c.unmanaged.tree, "Print paths as a tree") return unmanagedCmd } func (c *Config) runUnmanagedCmd(cmd *cobra.Command, args []string, sourceState *chezmoi.SourceState) error { var absPaths []chezmoi.AbsPath if len(args) == 0 { absPaths = append(absPaths, c.DestDirAbsPath) } else { argsAbsPaths := chezmoiset.New[chezmoi.AbsPath]() for _, arg := range args { argAbsPath, err := chezmoi.NormalizePath(arg) if err != nil { return err } argsAbsPaths.Add(argAbsPath) } absPaths = slices.Sorted(argsAbsPaths.Elements()) } unmanagedRelPaths := chezmoiset.New[chezmoi.RelPath]() walkFunc := func(destAbsPath chezmoi.AbsPath, fileInfo fs.FileInfo, err error) error { if err != nil { c.errorf("%s: %v\n", destAbsPath, err) if fileInfo == nil || fileInfo.IsDir() { return fs.SkipDir } return nil } if destAbsPath == c.DestDirAbsPath { return nil } targetRelPath, err := destAbsPath.TrimDirPrefix(c.DestDirAbsPath) if err != nil { return err } sourceStateEntry := sourceState.Get(targetRelPath) managed := sourceStateEntry != nil ignored := sourceState.Ignore(targetRelPath) included := c.unmanaged.filter.IncludeFileInfo(fileInfo) if !managed && !ignored && included { unmanagedRelPaths.Add(targetRelPath) } if fileInfo.IsDir() { switch { case !managed: return fs.SkipDir case ignored: return fs.SkipDir case sourceStateEntry != nil: if external, ok := sourceStateEntry.Origin().(*chezmoi.External); ok { if external.Type == chezmoi.ExternalTypeGitRepo { return fs.SkipDir } } } } return nil } for _, absPath := range absPaths { if err := chezmoi.Walk(c.destSystem, absPath, walkFunc); err != nil { return err } } elements := slices.Collect(unmanagedRelPaths.Elements()) paths := make([]fmt.Stringer, 0, len(elements)) for relPath := range unmanagedRelPaths { var path fmt.Stringer switch pathStyle := c.unmanaged.pathStyle.String(); pathStyle { case pathStyleAbsolute: path = c.DestDirAbsPath.Join(relPath) case pathStyleRelative: path = relPath default: return fmt.Errorf("%s: invalid path style", pathStyle) } paths = append(paths, path) } return c.writePaths(stringersToStrings(paths), writePathsOptions{ nulPathSeparator: c.unmanaged.nulPathSeparator, tree: c.unmanaged.tree, }) } ================================================ FILE: internal/cmd/unmanagedcmd_test.go ================================================ package cmd import ( "bytes" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestUnmanagedCmd(t *testing.T) { for _, tc := range []struct { name string root any postFunc func(vfs.FS) error args []string expectedOutput string }{ { name: "simple", root: map[string]any{ "/home/user": map[string]any{ ".file": "", ".local/share/chezmoi/dot_file": "# contents of .file\n", ".unmanaged": "", }, }, expectedOutput: chezmoitest.JoinLines( ".local", ".unmanaged", ), }, { name: "nul_path_separator", root: map[string]any{ "/home/user": map[string]any{ ".file": "", ".local/share/chezmoi/dot_file": "# contents of .file\n", ".unmanaged": "", }, }, args: []string{ "-0", }, expectedOutput: ".local\x00.unmanaged\x00", }, { name: "private_subdir", root: map[string]any{ "/home/user": map[string]any{ ".dir": map[string]any{ "subdir/file": "", }, ".local/share/chezmoi/dot_dir/subdir/file": "", }, }, postFunc: func(fileSystem vfs.FS) error { return fileSystem.Chmod("/home/user/.dir", 0) }, args: []string{"--keep-going"}, expectedOutput: chezmoitest.JoinLines( ".local", ), }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { if tc.postFunc != nil { assert.NoError(t, tc.postFunc(fileSystem)) } stdout := &bytes.Buffer{} config := newTestConfig(t, fileSystem, withStdout(stdout)) assert.NoError(t, config.execute(append([]string{"unmanaged"}, tc.args...))) assert.Equal(t, tc.expectedOutput, stdout.String()) }) }) } } ================================================ FILE: internal/cmd/updatecmd.go ================================================ package cmd import ( "errors" "github.com/go-git/go-git/v5" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type updateCmdConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` Args []string `json:"args" mapstructure:"args" yaml:"args"` Apply bool `json:"apply" mapstructure:"apply" yaml:"apply"` RecurseSubmodules bool `json:"recurseSubmodules" mapstructure:"recurseSubmodules" yaml:"recurseSubmodules"` filter *chezmoi.EntryTypeFilter init bool parentDirs bool recursive bool } func (c *Config) newUpdateCmd() *cobra.Command { updateCmd := &cobra.Command{ GroupID: groupIDDaily, Use: "update", Short: "Pull and apply any changes", Long: mustLongHelp("update"), Example: example("update"), Args: cobra.NoArgs, ValidArgsFunction: cobra.NoFileCompletions, RunE: c.runUpdateCmd, Annotations: newAnnotations( modifiesDestinationDirectory, persistentStateModeReadWrite, requiresSourceDirectory, requiresWorkingTree, runsCommands, ), } updateCmd.Flags().BoolVarP(&c.Update.Apply, "apply", "a", c.Update.Apply, "Apply after pulling") updateCmd.Flags().VarP(c.Update.filter.Exclude, "exclude", "x", "Exclude entry types") updateCmd.Flags().VarP(c.Update.filter.Include, "include", "i", "Include entry types") updateCmd.Flags().BoolVar(&c.Update.init, "init", c.Update.init, "Recreate config file from template") updateCmd.Flags().BoolVarP(&c.Update.parentDirs, "parent-dirs", "P", c.Update.parentDirs, "Update all parent directories") updateCmd.Flags(). BoolVar(&c.Update.RecurseSubmodules, "recurse-submodules", c.Update.RecurseSubmodules, "Recursively update submodules") updateCmd.Flags().BoolVarP(&c.Update.recursive, "recursive", "r", c.Update.recursive, "Recurse into subdirectories") return updateCmd } func (c *Config) runUpdateCmd(cmd *cobra.Command, args []string) error { switch { case c.Update.Command != "": if err := c.run(c.WorkingTreeAbsPath, c.Update.Command, c.Update.Args); err != nil { return err } case c.UseBuiltinGit.Value(c.useBuiltinGitAutoFunc): rawWorkingTreeAbsPath, err := c.baseSystem.RawPath(c.WorkingTreeAbsPath) if err != nil { return err } repo, err := git.PlainOpen(rawWorkingTreeAbsPath.String()) if err != nil { return err } wt, err := repo.Worktree() if err != nil { return err } if err := wt.Pull(&git.PullOptions{ RecurseSubmodules: git.DefaultSubmoduleRecursionDepth, }); err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) { return err } default: gitArgs := []string{ "pull", "--autostash", "--rebase", } if c.Update.RecurseSubmodules { gitArgs = append(gitArgs, "--recurse-submodules", ) } if err := c.run(c.WorkingTreeAbsPath, c.Git.Command, gitArgs); err != nil { return err } } if c.Update.Apply { if err := c.applyArgs(cmd.Context(), c.destSystem, c.DestDirAbsPath, args, applyArgsOptions{ cmd: cmd, filter: c.Update.filter, init: c.Update.init, parentDirs: c.Update.parentDirs, recursive: c.Update.recursive, umask: c.Umask, preApplyFunc: c.defaultPreApplyFunc, }); err != nil { return err } } return nil } ================================================ FILE: internal/cmd/upgradecmd.go ================================================ //go:build !noupgrade package cmd import ( "bytes" "context" "crypto/sha256" "encoding/hex" "errors" "fmt" "io" "io/fs" "log/slog" "net/http" "os" "os/exec" "regexp" "runtime" "strings" "github.com/coreos/go-semver/semver" "github.com/google/go-github/v61/github" "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" "chezmoi.io/chezmoi/internal/chezmoilog" ) const ( upgradeMethodBrewUpgrade = "brew-upgrade" upgradeMethodReplaceExecutable = "replace-executable" upgradeMethodSnapRefresh = "snap-refresh" upgradeMethodUpgradePackage = "upgrade-package" upgradeMethodSudoPrefix = "sudo-" upgradeMethodWinGetUpgrade = "winget-upgrade" ) var ( checksumRx = regexp.MustCompile(`\A([0-9a-f]{64})\s+(\S+)\n\z`) errUnsupportedUpgradeMethod = errors.New("unsupported upgrade method") ) type upgradeCmdConfig struct { executable string method string } func (c *Config) newUpgradeCmd() *cobra.Command { upgradeCmd := &cobra.Command{ GroupID: groupIDMigration, Use: "upgrade", Short: "Upgrade chezmoi to the latest released version", Long: mustLongHelp("upgrade"), Example: example("upgrade"), Args: cobra.NoArgs, ValidArgsFunction: cobra.NoFileCompletions, RunE: c.runUpgradeCmd, Annotations: newAnnotations( persistentStateModeNone, runsCommands, ), } upgradeCmd.Flags().StringVar(&c.upgrade.executable, "executable", c.upgrade.method, "Set executable to replace") upgradeCmd.Flags().StringVar(&c.upgrade.method, "method", c.upgrade.method, "Set upgrade method") return upgradeCmd } func (c *Config) runUpgradeCmd(cmd *cobra.Command, args []string) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() var zeroVersion semver.Version if c.version == zeroVersion && !c.force { return errors.New("cannot upgrade dev version to latest released version unless --force is set") } httpClient, err := c.getHTTPClient() if err != nil { return err } client := chezmoi.NewGitHubClient(ctx, httpClient) // Get the latest release. rr, _, err := client.Repositories.GetLatestRelease(ctx, "twpayne", "chezmoi") if err != nil { return err } version, err := semver.NewVersion(strings.TrimPrefix(rr.GetName(), "v")) if err != nil { return err } // If the upgrade is not forced, stop if we're already the latest version. // Print a message and return no error so the command exits with success. if !c.force && !c.version.LessThan(*version) { fmt.Fprintf(c.stdout, "chezmoi: already at the latest version (%s)\n", c.version) return nil } // Determine the upgrade method to use. if c.upgrade.executable == "" { executable, err := os.Executable() if err != nil { return err } c.upgrade.executable = executable } executableAbsPath := chezmoi.NewAbsPath(c.upgrade.executable) method := c.upgrade.method if method == "" { switch method, err = getUpgradeMethod(c.fileSystem, executableAbsPath); { case err != nil: return err case method == "": return fmt.Errorf("%s/%s: cannot determine upgrade method for %s", runtime.GOOS, runtime.GOARCH, executableAbsPath) } } c.logger.Info("upgradeMethod", slog.String("executable", c.upgrade.executable), slog.String("method", method)) // Replace the executable with the updated version. switch method { case upgradeMethodBrewUpgrade: if err := c.brewUpgrade(); err != nil { return err } case upgradeMethodReplaceExecutable: if err := c.replaceExecutable(ctx, executableAbsPath, version, rr); err != nil { return err } case upgradeMethodSnapRefresh: if err := c.snapRefresh(); err != nil { return err } case upgradeMethodUpgradePackage: useSudo := false if err := c.upgradeUNIXPackage(ctx, version, rr, useSudo); err != nil { return err } case upgradeMethodSudoPrefix + upgradeMethodUpgradePackage: useSudo := true if err := c.upgradeUNIXPackage(ctx, version, rr, useSudo); err != nil { return err } case upgradeMethodWinGetUpgrade: if err := c.winGetUpgrade(); err != nil { //nolint:staticcheck return err } default: return fmt.Errorf("%s: invalid method", method) } // Find the executable. If we replaced the executable directly, then use // that, otherwise look in $PATH. path := c.upgrade.executable if method != upgradeMethodReplaceExecutable { path, err = chezmoi.LookPath("chezmoi") if err != nil { return err } } // Execute the new version. chezmoiVersionCmd := exec.Command(path, "--version") chezmoiVersionCmd.Stdin = os.Stdin chezmoiVersionCmd.Stdout = os.Stdout chezmoiVersionCmd.Stderr = os.Stderr return chezmoilog.LogCmdRun(c.logger, chezmoiVersionCmd) } func (c *Config) getChecksums(ctx context.Context, rr *github.RepositoryRelease) (map[string][]byte, error) { name := fmt.Sprintf("chezmoi_%s_checksums.txt", strings.TrimPrefix(rr.GetTagName(), "v")) releaseAsset := getReleaseAssetByName(rr, name) if releaseAsset == nil { return nil, fmt.Errorf("%s: cannot find release asset", name) } data, err := c.downloadURL(ctx, releaseAsset.GetBrowserDownloadURL()) if err != nil { return nil, err } checksums := make(map[string][]byte) for line := range bytes.Lines(data) { m := checksumRx.FindSubmatch(line) if m == nil { return nil, fmt.Errorf("%q: cannot parse checksum", line) } checksums[string(m[2])], _ = hex.DecodeString(string(m[1])) } return checksums, nil } func (c *Config) downloadURL(ctx context.Context, url string) ([]byte, error) { req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody) if err != nil { return nil, err } httpClient, err := c.getHTTPClient() if err != nil { return nil, err } resp, err := chezmoilog.LogHTTPRequest(ctx, c.logger, httpClient, req) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { _ = resp.Body.Close() return nil, fmt.Errorf("%s: %s", url, resp.Status) } data, err := io.ReadAll(resp.Body) if err != nil { return nil, err } if err := resp.Body.Close(); err != nil { return nil, err } return data, nil } func (c *Config) replaceExecutable( ctx context.Context, executableFilenameAbsPath chezmoi.AbsPath, releaseVersion *semver.Version, rr *github.RepositoryRelease, ) (err error) { var archiveFormat chezmoi.ArchiveFormat var archiveName string switch { case runtime.GOOS == "linux" && runtime.GOARCH == "amd64": archiveFormat = chezmoi.ArchiveFormatTarGz // Determine the libc to use. If the executable is dynamically linked // (as indicated by ldd running and returning without error), then use // glibc, otherwise use musl. var libc string if err := exec.Command("ldd", executableFilenameAbsPath.String()).Run(); err == nil { libc = "glibc" } else { libc = "musl" } archiveName = fmt.Sprintf("chezmoi_%s_%s-%s_%s.tar.gz", releaseVersion, runtime.GOOS, libc, runtime.GOARCH) case runtime.GOOS == "linux" && runtime.GOARCH == "386": archiveFormat = chezmoi.ArchiveFormatTarGz archiveName = fmt.Sprintf("chezmoi_%s_%s_i386.tar.gz", releaseVersion, runtime.GOOS) case runtime.GOOS == "windows": archiveFormat = chezmoi.ArchiveFormatZip archiveName = fmt.Sprintf("chezmoi_%s_%s_%s.zip", releaseVersion, runtime.GOOS, runtime.GOARCH) default: archiveFormat = chezmoi.ArchiveFormatTarGz archiveName = fmt.Sprintf("chezmoi_%s_%s_%s.tar.gz", releaseVersion, runtime.GOOS, runtime.GOARCH) } releaseAsset := getReleaseAssetByName(rr, archiveName) if releaseAsset == nil { return fmt.Errorf("%s: cannot find release asset", archiveName) } var archiveData []byte if archiveData, err = c.downloadURL(ctx, releaseAsset.GetBrowserDownloadURL()); err != nil { return err } if err := c.verifyChecksum(ctx, rr, releaseAsset.GetName(), archiveData); err != nil { return err } // Extract the executable from the archive. var executableData []byte executableName := "chezmoi" if runtime.GOOS == "windows" { executableName += ".exe" } walkArchiveFunc := func(name string, info fs.FileInfo, r io.Reader, linkname string) error { if name == executableName { var err error executableData, err = io.ReadAll(r) if err != nil { return err } return fs.SkipAll } return nil } if err := chezmoi.WalkArchive(archiveData, archiveFormat, walkArchiveFunc); err != nil { return err } if executableData == nil { return fmt.Errorf("%s: cannot find executable in archive", archiveName) } // Replace the executable. if runtime.GOOS == "windows" { if err := c.baseSystem.Rename(executableFilenameAbsPath, executableFilenameAbsPath.Append(".old")); err != nil { return err } } return c.baseSystem.WriteFile(executableFilenameAbsPath, executableData, 0o755) } func (c *Config) verifyChecksum(ctx context.Context, rr *github.RepositoryRelease, name string, data []byte) error { checksums, err := c.getChecksums(ctx, rr) if err != nil { return err } expectedChecksum, ok := checksums[name] if !ok { return fmt.Errorf("%s: checksum not found", name) } checksum := sha256.Sum256(data) if !bytes.Equal(checksum[:], expectedChecksum) { format := "%s: checksum failed (want %s, got %s)" return fmt.Errorf(format, name, hex.EncodeToString(expectedChecksum), hex.EncodeToString(checksum[:])) } return nil } // getReleaseAssetByName returns the release asset from rr with the given name. func getReleaseAssetByName(rr *github.RepositoryRelease, name string) *github.ReleaseAsset { for i, ra := range rr.Assets { if ra.GetName() == name { return rr.Assets[i] } } return nil } ================================================ FILE: internal/cmd/upgradecmd_test.go ================================================ //go:build !noupgrade && unix package cmd import ( "testing" "github.com/alecthomas/assert/v2" "github.com/coreos/go-semver/semver" "github.com/twpayne/go-vfs/v5" ) func TestConfigGetPackageFilename(t *testing.T) { for _, tc := range []struct { packageType string arch string expected string }{ { packageType: packageTypeAPK, arch: "amd64", expected: "chezmoi_2.0.0_linux_amd64.apk", }, { packageType: packageTypeDEB, arch: "386", expected: "chezmoi_2.0.0_linux_i386.deb", }, { packageType: packageTypeDEB, arch: "amd64", expected: "chezmoi_2.0.0_linux_amd64.deb", }, { packageType: packageTypeDEB, arch: "arm", expected: "chezmoi_2.0.0_linux_armel.deb", }, { packageType: packageTypeDEB, arch: "arm64", expected: "chezmoi_2.0.0_linux_arm64.deb", }, { packageType: packageTypeRPM, arch: "386", expected: "chezmoi-2.0.0-i686.rpm", }, { packageType: packageTypeRPM, arch: "amd64", expected: "chezmoi-2.0.0-x86_64.rpm", }, { packageType: packageTypeRPM, arch: "arm", expected: "chezmoi-2.0.0-armhfp.rpm", }, { packageType: packageTypeRPM, arch: "arm64", expected: "chezmoi-2.0.0-aarch64.rpm", }, { packageType: packageTypeRPM, arch: "ppc64", expected: "chezmoi-2.0.0-ppc64.rpm", }, { packageType: packageTypeRPM, arch: "ppc64le", expected: "chezmoi-2.0.0-ppc64le.rpm", }, } { t.Run(tc.expected, func(t *testing.T) { c := newTestConfig(t, vfs.EmptyFS{}) version := semver.Must(semver.NewVersion("2.0.0")) actual, err := c.getPackageFilename(tc.packageType, version, "linux", tc.arch) assert.NoError(t, err) assert.Equal(t, tc.expected, actual) }) } } ================================================ FILE: internal/cmd/upgradecmd_unix.go ================================================ //go:build !noupgrade && unix package cmd import ( "context" "errors" "fmt" "io/fs" "os" "runtime" "strings" "github.com/coreos/go-semver/semver" "github.com/google/go-github/v61/github" vfs "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoi" ) const ( packageTypeNone = "" packageTypeAPK = "apk" packageTypeAUR = "aur" packageTypeDEB = "deb" packageTypeRPM = "rpm" ) var ( packageTypeByID = map[string]string{ "alpine": packageTypeAPK, "amzn": packageTypeRPM, "arch": packageTypeAUR, "centos": packageTypeRPM, "fedora": packageTypeRPM, "opensuse": packageTypeRPM, "debian": packageTypeDEB, "rhel": packageTypeRPM, "sles": packageTypeRPM, "ubuntu": packageTypeDEB, } archReplacements = map[string]map[string]string{ packageTypeDEB: { "386": "i386", "arm": "armel", }, packageTypeRPM: { "amd64": "x86_64", "386": "i686", "arm": "armhfp", "arm64": "aarch64", }, } ) func (c *Config) brewUpgrade() error { return c.run(chezmoi.EmptyAbsPath, "brew", []string{"upgrade", "chezmoi"}) } func (c *Config) getPackageFilename(packageType string, version *semver.Version, goOS, arch string) (string, error) { if archReplacement, ok := archReplacements[packageType][arch]; ok { arch = archReplacement } switch packageType { case packageTypeAPK: return fmt.Sprintf("chezmoi_%s_%s_%s.apk", version, goOS, arch), nil case packageTypeDEB: return fmt.Sprintf("chezmoi_%s_%s_%s.deb", version, goOS, arch), nil case packageTypeRPM: return fmt.Sprintf("chezmoi-%s-%s.rpm", version, arch), nil default: return "", fmt.Errorf("%s: unsupported package type", packageType) } } func (c *Config) snapRefresh() error { return c.run(chezmoi.EmptyAbsPath, "snap", []string{"refresh", "chezmoi"}) } func (c *Config) upgradeUNIXPackage( ctx context.Context, version *semver.Version, rr *github.RepositoryRelease, useSudo bool, ) error { switch runtime.GOOS { case "linux": // Determine the package type and architecture. packageType, err := getPackageType(c.baseSystem) if err != nil { return err } // chezmoi does not build and distribute AUR packages, so instead rely // on pacman and the community package. if packageType == packageTypeAUR { var args []string if useSudo { args = append(args, "sudo") } args = append(args, "pacman", "-S", "--needed", "chezmoi") return c.run(chezmoi.EmptyAbsPath, args[0], args[1:]) } // Find the release asset. packageFilename, err := c.getPackageFilename(packageType, version, runtime.GOOS, runtime.GOARCH) if err != nil { return err } releaseAsset := getReleaseAssetByName(rr, packageFilename) if releaseAsset == nil { return fmt.Errorf("%s: cannot find release asset", packageFilename) } // Create a temporary directory for the package. tempDirAbsPath, err := c.tempDir("chezmoi") if err != nil { return err } data, err := c.downloadURL(ctx, releaseAsset.GetBrowserDownloadURL()) if err != nil { return err } if err := c.verifyChecksum(ctx, rr, releaseAsset.GetName(), data); err != nil { return err } packageAbsPath := tempDirAbsPath.JoinString(releaseAsset.GetName()) if err := c.baseSystem.WriteFile(packageAbsPath, data, 0o644); err != nil { return err } // Install the package from disk. var args []string if useSudo { args = append(args, "sudo") } switch packageType { case packageTypeAPK: args = append(args, "apk", "--allow-untrusted", packageAbsPath.String()) case packageTypeDEB: args = append(args, "dpkg", "-i", packageAbsPath.String()) case packageTypeRPM: args = append(args, "rpm", "-U", packageAbsPath.String()) } return c.run(chezmoi.EmptyAbsPath, args[0], args[1:]) default: return fmt.Errorf("%s: unsupported GOOS", runtime.GOOS) } } func (c *Config) winGetUpgrade() error { return errUnsupportedUpgradeMethod } // getPackageType returns the distributions package type based on is OS release. func getPackageType(system chezmoi.System) (string, error) { osRelease, err := chezmoi.OSRelease(system.UnderlyingFS()) if err != nil { return packageTypeNone, err } if id, ok := osRelease["ID"].(string); ok { if packageType, ok := packageTypeByID[id]; ok { return packageType, nil } } if idLikes, ok := osRelease["ID_LIKE"].(string); ok { for id := range strings.SplitSeq(idLikes, " ") { if packageType, ok := packageTypeByID[id]; ok { return packageType, nil } } } err = fmt.Errorf( "could not determine package type (ID=%q, ID_LIKE=%q)", osRelease["ID"], osRelease["ID_LIKE"], ) return packageTypeNone, err } // getUpgradeMethod attempts to determine the method by which chezmoi can be // upgraded by looking at how it was installed. func getUpgradeMethod(fileSystem vfs.Stater, executableAbsPath chezmoi.AbsPath) (string, error) { switch { case runtime.GOOS == "darwin" && strings.Contains(executableAbsPath.String(), "/homebrew/"): return upgradeMethodBrewUpgrade, nil case runtime.GOOS == "linux" && strings.Contains(executableAbsPath.String(), "/.linuxbrew/"): return upgradeMethodBrewUpgrade, nil } // If the executable is in the user's home directory, then always use // replace-executable. switch userHomeDir, err := chezmoi.UserHomeDir(); { case errors.Is(err, fs.ErrNotExist): case err != nil: return "", err default: switch executableInUserHomeDir, err := vfs.Contains(fileSystem, executableAbsPath.String(), userHomeDir); { case errors.Is(err, fs.ErrNotExist): case err != nil: return "", err case executableInUserHomeDir: return upgradeMethodReplaceExecutable, nil } } // If the executable is in the system's temporary directory, then always use // replace-executable. if executableIsInTempDir, err := vfs.Contains(fileSystem, executableAbsPath.String(), os.TempDir()); err != nil { return "", err } else if executableIsInTempDir { return upgradeMethodReplaceExecutable, nil } switch runtime.GOOS { case "darwin": return upgradeMethodReplaceExecutable, nil case "freebsd": return upgradeMethodReplaceExecutable, nil case "linux": if ok, _ := vfs.Contains(fileSystem, executableAbsPath.String(), "/snap"); ok { return upgradeMethodSnapRefresh, nil } fileInfo, err := fileSystem.Stat(executableAbsPath.String()) if err != nil { return "", err } uid := os.Getuid() switch fileInfoUID(fileInfo) { case 0: method := upgradeMethodUpgradePackage if uid != 0 { if _, err := chezmoi.LookPath("sudo"); err == nil { method = upgradeMethodSudoPrefix + method } } return method, nil case uid: return upgradeMethodReplaceExecutable, nil default: return "", fmt.Errorf("%s: cannot upgrade executable owned by non-current non-root user", executableAbsPath) } case "openbsd": return upgradeMethodReplaceExecutable, nil default: return "", nil } } ================================================ FILE: internal/cmd/upgradecmd_windows.go ================================================ //go:build !noupgrade package cmd import ( "context" "errors" "io/fs" "os" "path/filepath" "github.com/coreos/go-semver/semver" "github.com/google/go-github/v61/github" vfs "github.com/twpayne/go-vfs/v5" "chezmoi.io/chezmoi/internal/chezmoi" ) type InstallBehavior struct { PortablePackageUserRoot string `json:"portablePackageUserRoot"` PortablePackageMachineRoot string `json:"portablePackageMachineRoot"` } func (ib *InstallBehavior) Values() []string { return []string{ ib.PortablePackageUserRoot, ib.PortablePackageMachineRoot, } } type WinGetSettings struct { InstallBehavior InstallBehavior `json:"installBehavior"` } func (c *Config) brewUpgrade() error { return errUnsupportedUpgradeMethod } // isWinGetInstall determines if executableAbsPath contains a WinGet // installation path. func isWinGetInstall(fileSystem vfs.Stater, executableAbsPath string) (bool, error) { realExecutableAbsPath := executableAbsPath fi, err := os.Lstat(executableAbsPath) if err != nil { return false, err } if fi.Mode().Type() == fs.ModeSymlink { realExecutableAbsPath, err = os.Readlink(executableAbsPath) if err != nil { return false, err } } winGetSettings := WinGetSettings{ InstallBehavior: InstallBehavior{ PortablePackageUserRoot: os.ExpandEnv(`${LOCALAPPDATA}\Microsoft\WinGet\Packages\`), PortablePackageMachineRoot: os.ExpandEnv(`${PROGRAMFILES}\WinGet\Packages\`), }, } settingsPaths := []string{ os.ExpandEnv(`${LOCALAPPDATA}\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\settings.json`), os.ExpandEnv(`${LOCALAPPDATA}\Microsoft\WinGet\Settings\settings.json`), } for _, settingsPath := range settingsPaths { if _, err := os.Stat(settingsPath); err == nil { winGetSettingsContents, err := os.ReadFile(settingsPath) if err == nil { if err := chezmoi.FormatJSONC.Unmarshal(winGetSettingsContents, &winGetSettings); err != nil { return false, err } } } } for _, path := range winGetSettings.InstallBehavior.Values() { path = filepath.Clean(path) if path == "." { continue } if ok, _ := vfs.Contains(fileSystem, realExecutableAbsPath, path); ok { return true, nil } } return false, nil } func (c *Config) snapRefresh() error { return errUnsupportedUpgradeMethod } func (c *Config) upgradeUNIXPackage(_ context.Context, _ *semver.Version, _ *github.RepositoryRelease, _ bool) error { return errUnsupportedUpgradeMethod } func (c *Config) winGetUpgrade() error { //nolint:staticcheck return errors.New( "upgrade command is not currently supported for WinGet installations. chezmoi can still be upgraded via WinGet by running `winget upgrade --id twpayne.chezmoi --source winget`", ) } // getUpgradeMethod attempts to determine the method by which chezmoi can be // upgraded by looking at how it was installed. func getUpgradeMethod(fileSystem vfs.Stater, executableAbsPath chezmoi.AbsPath) (string, error) { if ok, err := isWinGetInstall(fileSystem, executableAbsPath.String()); err != nil { return "", err } else if ok { return upgradeMethodWinGetUpgrade, nil } // If the executable is in the user's home directory, then always use // replace-executable. switch userHomeDir, err := chezmoi.UserHomeDir(); { case errors.Is(err, fs.ErrNotExist): case err != nil: return "", err default: switch executableInUserHomeDir, err := vfs.Contains(fileSystem, executableAbsPath.String(), userHomeDir); { case errors.Is(err, fs.ErrNotExist): case err != nil: return "", err case executableInUserHomeDir: return upgradeMethodReplaceExecutable, nil } } // If the executable is in the system's temporary directory, then always use // replace-executable. if executableIsInTempDir, err := vfs.Contains(fileSystem, executableAbsPath.String(), os.TempDir()); err != nil { return "", err } else if executableIsInTempDir { return upgradeMethodReplaceExecutable, nil } return "", nil } ================================================ FILE: internal/cmd/util.go ================================================ package cmd import ( "fmt" "strings" "unicode" "chezmoi.io/chezmoi/internal/chezmoiset" ) var ( wellKnownAbbreviations = chezmoiset.New( "ANSI", "CPE", "ID", "URL", ) choicesYesNoAllQuit = []string{ "yes", "no", "all", "quit", } choicesYesNoQuit = []string{ "yes", "no", "quit", } choicesOverwrite = []string{ "overwrite", "all-overwrite", "skip", "quit", } ) // camelCaseToUpperSnakeCase converts a string in camelCase to UPPER_SNAKE_CASE. func camelCaseToUpperSnakeCase(s string) string { if s == "" { return "" } runes := []rune(s) var wordBoundaries []int for i, r := range runes[1:] { if unicode.IsLower(runes[i]) && unicode.IsUpper(r) { wordBoundaries = append(wordBoundaries, i+1) } } if len(wordBoundaries) == 0 { return strings.ToUpper(s) } wordBoundaries = append(wordBoundaries, len(runes)) words := make([]string, len(wordBoundaries)) prevWordBoundary := 0 for i, wordBoundary := range wordBoundaries { word := string(runes[prevWordBoundary:wordBoundary]) words[i] = strings.ToUpper(word) prevWordBoundary = wordBoundary } return strings.Join(words, "_") } // englishList returns ss formatted as a list, including an Oxford comma. func englishList(ss []string) string { switch n := len(ss); n { case 0: return "" case 1: return ss[0] case 2: return ss[0] + " and " + ss[1] default: return strings.Join(ss[:n-1], ", ") + ", and " + ss[n-1] } } // englishListWithNoun returns ss formatted as an English list, including an // Oxford comma. func englishListWithNoun(ss []string, singular, plural string) string { if len(ss) == 1 { return ss[0] + " " + singular } if plural == "" { plural = pluralize(singular) } switch n := len(ss); n { case 0: return "no " + plural default: return englishList(ss) + " " + plural } } // pluralize returns the English plural form of singular. func pluralize(singular string) string { if prefix, found := strings.CutSuffix(singular, "y"); found { return prefix + "ies" } return singular + "s" } // stringersToStrings converts a slice of fmt.Stringers to a list of strings. func stringersToStrings[T fmt.Stringer](ss []T) []string { result := make([]string, len(ss)) for i, s := range ss { result[i] = s.String() } return result } // titleFirst returns s with its first rune converted to title case. func titleFirst(s string) string { if s == "" { return s } runes := []rune(s) return string(append([]rune{unicode.ToTitle(runes[0])}, runes[1:]...)) } // upperSnakeCaseToCamelCase converts a string in UPPER_SNAKE_CASE to // camelCase. func upperSnakeCaseToCamelCase(s string) string { words := strings.Split(s, "_") for i, word := range words { if i == 0 { words[i] = strings.ToLower(word) } else if !wellKnownAbbreviations.Contains(word) { words[i] = titleFirst(strings.ToLower(word)) } } return strings.Join(words, "") } // upperSnakeCaseToCamelCaseMap returns m with all keys converted from // UPPER_SNAKE_CASE to camelCase. func upperSnakeCaseToCamelCaseMap[V any](m map[string]V) map[string]V { result := make(map[string]V) for k, v := range m { result[upperSnakeCaseToCamelCase(k)] = v } return result } ================================================ FILE: internal/cmd/util_test.go ================================================ package cmd import ( "testing" "github.com/alecthomas/assert/v2" ) func TestCamelCaseToUpperSnakeCase(t *testing.T) { for _, tc := range []struct { s string expected string }{ { "", "", }, { "camel", "CAMEL", }, { "camelCase", "CAMEL_CASE", }, { "bugReportURL", "BUG_REPORT_URL", }, } { t.Run(tc.s, func(t *testing.T) { actual := camelCaseToUpperSnakeCase(tc.s) assert.Equal(t, tc.expected, actual) }) } } func TestEnglishList(t *testing.T) { for _, tc := range []struct { ss []string expected string }{ { expected: "", }, { ss: []string{"first"}, expected: "first", }, { ss: []string{"first", "second"}, expected: "first and second", }, { ss: []string{"first", "second", "third"}, expected: "first, second, and third", }, { ss: []string{"first", "second", "third", "fourth"}, expected: "first, second, third, and fourth", }, } { actual := englishList(tc.ss) assert.Equal(t, tc.expected, actual) } } func TestEnglishListWithNoun(t *testing.T) { for _, tc := range []struct { ss []string singular string plural string expected string }{ { singular: "item", expected: "no items", }, { ss: []string{"first"}, singular: "item", expected: "first item", }, { ss: []string{"first", "second"}, singular: "item", expected: "first and second items", }, { ss: []string{"first", "second", "third"}, singular: "item", expected: "first, second, and third items", }, { ss: []string{"first", "second", "third", "fourth"}, singular: "item", expected: "first, second, third, and fourth items", }, { singular: "entry", expected: "no entries", }, { ss: []string{"first"}, singular: "entry", expected: "first entry", }, { ss: []string{"first", "second"}, singular: "entry", expected: "first and second entries", }, { ss: []string{"first", "second", "third"}, singular: "entry", expected: "first, second, and third entries", }, { ss: []string{"first", "second", "third", "fourth"}, singular: "entry", expected: "first, second, third, and fourth entries", }, { ss: []string{"first"}, singular: "person", plural: "people", expected: "first person", }, { ss: []string{"first", "second", "third"}, singular: "person", plural: "people", expected: "first, second, and third people", }, } { actual := englishListWithNoun(tc.ss, tc.singular, tc.plural) assert.Equal(t, tc.expected, actual) } } func TestUpperSnakeCaseToCamelCaseMap(t *testing.T) { actual := upperSnakeCaseToCamelCaseMap(map[string]any{ "BUG_REPORT_URL": "", "ID": "", }) assert.Equal(t, map[string]any{ "bugReportURL": "", "id": "", }, actual) } ================================================ FILE: internal/cmd/util_unix.go ================================================ //go:build unix package cmd import ( "io/fs" "os" "strings" "syscall" "chezmoi.io/chezmoi/internal/chezmoi" ) const defaultEditor = "vi" func fileInfoUID(info fs.FileInfo) int { return int(info.Sys().(*syscall.Stat_t).Uid) //nolint:forcetypeassert } // getPS1Interpreter returns the appropriate Interpreter for PowerShell // scripts (.ps1) on Unix-like systems. It uses the provided findExecutable // function to check for the presence of 'pwsh'. If 'pwsh' is not found, it // returns an empty Interpreter, indicating no suitable interpreter is // available. func getPS1Interpreter(findExecutable func([]string, []string) (string, error)) chezmoi.Interpreter { paths := strings.Split(os.Getenv("PATH"), string(os.PathListSeparator)) if pwshPath, _ := findExecutable([]string{"pwsh"}, paths); pwshPath != "" { return chezmoi.Interpreter{ Command: "pwsh", Args: []string{"-NoLogo", "-File"}, } } return chezmoi.Interpreter{} } func windowsVersion() (map[string]any, error) { return nil, nil } ================================================ FILE: internal/cmd/util_windows.go ================================================ package cmd import ( "fmt" "os" "strings" "golang.org/x/sys/windows/registry" "chezmoi.io/chezmoi/internal/chezmoi" ) const defaultEditor = "notepad.exe" // getPS1Interpreter returns the appropriate Interpreter for PowerShell // scripts (.ps1) on Windows systems. It uses the provided findExecutable // function to check for the presence of 'pwsh'. If 'pwsh' is not found, it // returns an empty Interpreter, indicating no suitable interpreter is // available. func getPS1Interpreter(findExecutable func([]string, []string) (string, error)) chezmoi.Interpreter { paths := strings.Split(os.Getenv("PATH"), string(os.PathListSeparator)) var interpreter chezmoi.Interpreter if pwshPath, _ := findExecutable([]string{"pwsh.exe", "pwsh"}, paths); pwshPath != "" { interpreter = chezmoi.Interpreter{ Command: "pwsh", Args: []string{"-NoLogo", "-File"}, } } else if powershellPath, _ := findExecutable([]string{"powershell.exe", "powershell"}, paths); powershellPath != "" { interpreter = chezmoi.Interpreter{ Command: "powershell", Args: []string{"-NoLogo", "-File"}, } } return interpreter } func windowsVersion() (map[string]any, error) { registryKey, err := registry.OpenKey( registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE, ) if err != nil { return nil, fmt.Errorf("registry.OpenKey: %w", err) } windowsVersion := make(map[string]any) for _, name := range []string{ "CurrentBuild", "CurrentVersion", "DisplayVersion", "EditionID", "ProductName", } { if value, _, err := registryKey.GetStringValue(name); err == nil { key := strings.ToLower(name[:1]) + name[1:] windowsVersion[key] = value } } for _, name := range []string{ "CurrentMajorVersionNumber", "CurrentMinorVersionNumber", } { if value, _, err := registryKey.GetIntegerValue(name); err == nil { key := strings.ToLower(name[:1]) + name[1:] windowsVersion[key] = value } } return windowsVersion, nil } ================================================ FILE: internal/cmd/vaulttemplatefuncs.go ================================================ package cmd import ( "encoding/json" "os" "os/exec" "chezmoi.io/chezmoi/internal/chezmoilog" ) type vaultConfig struct { Command string `json:"command" mapstructure:"command" yaml:"command"` cache map[string]any } func (c *Config) vaultTemplateFunc(key string) any { if data, ok := c.Vault.cache[key]; ok { return data } args := []string{"kv", "get", "-format=json", key} cmd := exec.Command(c.Vault.Command, args...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr output, err := chezmoilog.LogCmdOutput(c.logger, cmd) if err != nil { panic(newCmdOutputError(cmd, output, err)) } var data any must(json.Unmarshal(output, &data)) if c.Vault.cache == nil { c.Vault.cache = make(map[string]any) } c.Vault.cache[key] = data return data } ================================================ FILE: internal/cmd/verifycmd.go ================================================ package cmd import ( "github.com/spf13/cobra" "chezmoi.io/chezmoi/internal/chezmoi" ) type verifyCmdConfig struct { Exclude *chezmoi.EntryTypeSet `json:"exclude" mapstructure:"exclude" yaml:"exclude"` include *chezmoi.EntryTypeSet init bool parentDirs bool recursive bool } func (c *Config) newVerifyCmd() *cobra.Command { verifyCmd := &cobra.Command{ GroupID: groupIDAdvanced, Use: "verify [target]...", Short: "Exit with success if the destination state matches the target state, fail otherwise", Long: mustLongHelp("verify"), Example: example("verify"), ValidArgsFunction: c.targetValidArgs, RunE: c.runVerifyCmd, Annotations: newAnnotations( persistentStateModeReadMockWrite, requiresSourceDirectory, ), } verifyCmd.Flags().VarP(c.Verify.Exclude, "exclude", "x", "Exclude entry types") verifyCmd.Flags().VarP(c.Verify.include, "include", "i", "Include entry types") verifyCmd.Flags().BoolVar(&c.Verify.init, "init", c.Verify.init, "Recreate config file from template") verifyCmd.Flags().BoolVarP(&c.Verify.parentDirs, "parent-dirs", "P", c.Verify.parentDirs, "Verify all parent directories") verifyCmd.Flags().BoolVarP(&c.Verify.recursive, "recursive", "r", c.Verify.recursive, "Recurse into subdirectories") return verifyCmd } func (c *Config) runVerifyCmd(cmd *cobra.Command, args []string) error { errorOnWriteSystem := chezmoi.NewErrorOnWriteSystem(c.destSystem, chezmoi.ExitCodeError(1)) return c.applyArgs(cmd.Context(), errorOnWriteSystem, c.DestDirAbsPath, args, applyArgsOptions{ cmd: cmd, filter: chezmoi.NewEntryTypeFilter(c.Verify.include.Bits(), c.Verify.Exclude.Bits()), init: c.Verify.init, parentDirs: c.Verify.parentDirs, recursive: c.Verify.recursive, umask: c.Umask, }) } ================================================ FILE: internal/cmd/verifycmd_test.go ================================================ package cmd import ( "io/fs" "testing" "github.com/alecthomas/assert/v2" "github.com/twpayne/go-vfs/v5" "github.com/twpayne/go-vfs/v5/vfst" "chezmoi.io/chezmoi/internal/chezmoitest" ) func TestVerifyCmd(t *testing.T) { for _, tc := range []struct { name string root any expectedErr error }{ { name: "empty", root: map[string]any{ "/home/user/.local/share/chezmoi": &vfst.Dir{ Perm: fs.ModePerm &^ chezmoitest.Umask, }, }, }, { name: "file", root: map[string]any{ "/home/user": map[string]any{ ".bashrc": &vfst.File{ Contents: []byte("# contents of .bashrc\n"), Perm: 0o666 &^ chezmoitest.Umask, }, ".local/share/chezmoi/dot_bashrc": "# contents of .bashrc\n", }, }, }, } { t.Run(tc.name, func(t *testing.T) { chezmoitest.WithTestFS(t, tc.root, func(fileSystem vfs.FS) { assert.Equal(t, tc.expectedErr, newTestConfig(t, fileSystem).execute([]string{"verify"})) }) }) } } ================================================ FILE: internal/cmds/execute-template/main.go ================================================ // execute-template executes a template with optional data. package main import ( "bytes" "context" "errors" "flag" "fmt" "io/fs" "net/http" "os" "os/exec" "path" "regexp" "strings" "text/template" "github.com/go-sprout/sprout" sproutencoding "github.com/go-sprout/sprout/registry/encoding" sproutmaps "github.com/go-sprout/sprout/registry/maps" sproutnumeric "github.com/go-sprout/sprout/registry/numeric" sproutregexp "github.com/go-sprout/sprout/registry/regexp" sproutsemver "github.com/go-sprout/sprout/registry/semver" sproutslices "github.com/go-sprout/sprout/registry/slices" sproutstd "github.com/go-sprout/sprout/registry/std" sproutstrings "github.com/go-sprout/sprout/registry/strings" sprouttime "github.com/go-sprout/sprout/registry/time" "github.com/goccy/go-yaml" "github.com/google/go-github/v61/github" "github.com/google/renameio/v2/maybe" "chezmoi.io/chezmoi/internal/chezmoi" ) var ( templateDataFilename = flag.String("data", "", "data filename") outputFilename = flag.String("output", "", "output filename") nonLetterRx = regexp.MustCompile(`[^a-z]+`) ) type gitHubClient struct { ctx context.Context //nolint:containedctx client *github.Client } func newGitHubClient(ctx context.Context) *gitHubClient { return &gitHubClient{ ctx: ctx, client: chezmoi.NewGitHubClient(ctx, http.DefaultClient), } } func (c *gitHubClient) gitHubListReleases(ownerRepo string) []*github.RepositoryRelease { owner, repo, ok := strings.Cut(ownerRepo, "/") if !ok { panic(fmt.Errorf("%s: not a owner/repo", ownerRepo)) } var allRepositoryReleases []*github.RepositoryRelease opts := &github.ListOptions{ PerPage: 100, } for { repositoryReleases, resp, err := c.client.Repositories.ListReleases(c.ctx, owner, repo, opts) if err != nil { panic(err) } allRepositoryReleases = append(allRepositoryReleases, repositoryReleases...) if resp.NextPage == 0 { break } opts.Page = resp.NextPage } return allRepositoryReleases } func (c *gitHubClient) gitHubLatestRelease(ownerRepo string) *github.RepositoryRelease { owner, repo, ok := strings.Cut(ownerRepo, "/") if !ok { panic(fmt.Errorf("%s: not a owner/repo", ownerRepo)) } rr, _, err := c.client.Repositories.GetLatestRelease(c.ctx, owner, repo) if err != nil { panic(err) } return rr } func slugify(s string) string { s = strings.ToLower(s) s = nonLetterRx.ReplaceAllString(s, "-") s = strings.Trim(s, "-") return s } func run() error { flag.Parse() var templateData any if *templateDataFilename != "" { dataBytes, err := os.ReadFile(*templateDataFilename) if err != nil { return err } if err := yaml.Unmarshal(dataBytes, &templateData); err != nil { return err } } if flag.NArg() == 0 { return errors.New("no arguments") } templateName := path.Base(flag.Arg(0)) buffer := &bytes.Buffer{} funcMap := sprout.New( sprout.WithRegistries( sproutencoding.NewRegistry(), sproutmaps.NewRegistry(), sproutnumeric.NewRegistry(), sproutregexp.NewRegistry(), sproutsemver.NewRegistry(), sproutslices.NewRegistry(), sproutstd.NewRegistry(), sproutstrings.NewRegistry(), sprouttime.NewRegistry(), ), ).Build() gitHubClient := newGitHubClient(context.Background()) funcMap["exists"] = func(name string) bool { switch _, err := os.Stat(name); { case err == nil: return true case errors.Is(err, fs.ErrNotExist): return false default: panic(err) } } funcMap["gitHubLatestRelease"] = gitHubClient.gitHubLatestRelease funcMap["gitHubListReleases"] = gitHubClient.gitHubListReleases funcMap["gitHubTimestampFormat"] = func(layout string, timestamp github.Timestamp) string { return timestamp.Format(layout) } funcMap["output"] = func(name string, args ...string) string { cmd := exec.Command(name, args...) cmd.Stderr = os.Stderr out, err := cmd.Output() if err != nil { panic(err) } return string(out) } funcMap["replaceAllRegex"] = func(expr, repl, s string) string { return regexp.MustCompile(expr).ReplaceAllString(s, repl) } funcMap["slugify"] = slugify tmpl, err := template.New(templateName).Funcs(funcMap).ParseFiles(flag.Args()...) if err != nil { return err } if err := tmpl.Execute(buffer, templateData); err != nil { return err } if *outputFilename == "" { if _, err := os.Stdout.Write(buffer.Bytes()); err != nil { return err } } else if data, err := os.ReadFile(*outputFilename); err != nil || !bytes.Equal(data, buffer.Bytes()) { if err := maybe.WriteFile(*outputFilename, buffer.Bytes(), 0o644); err != nil { return err } } return nil } func main() { if err := run(); err != nil { fmt.Println(err) os.Exit(1) } } ================================================ FILE: internal/cmds/execute-template/main_test.go ================================================ package main import ( "testing" "github.com/alecthomas/assert/v2" ) func TestSlugify(t *testing.T) { for _, tc := range []struct { s string expected string }{ { s: "add", expected: "add", }, { s: "*command*`.post.args`", expected: "command-post-args", }, } { t.Run(tc.s, func(t *testing.T) { assert.Equal(t, tc.expected, slugify(tc.s)) }) } } ================================================ FILE: internal/cmds/generate-commit/main.go ================================================ // generate-commit generates the COMMIT file. package main import ( "bytes" "flag" "fmt" "log" "os" "os/exec" "strings" ) func main() { output := flag.String("o", "", "output file") flag.Parse() b := strings.Builder{} sha, err := exec.Command("git", "rev-parse", "HEAD").Output() if err != nil { log.Fatal(err) } b.Write(bytes.TrimSpace(sha)) if err := exec.Command("git", "diff-index", "--quiet", "HEAD").Run(); err != nil { b.WriteString("-dirty") } commit := b.String() if *output != "" { err := os.WriteFile(*output, []byte(commit), 0o644) if err != nil { log.Fatal(err) } } else { fmt.Println(commit) } } ================================================ FILE: internal/cmds/generate-helps/helps.go.tmpl ================================================ // Code generated by chezmoi.io/chezmoi/internal/cmds/generate-helps. DO NOT EDIT. package cmd import ( "chezmoi.io/chezmoi/internal/chezmoiset" ) type help struct { longHelp string example string longFlags chezmoiset.Set[string] shortFlags chezmoiset.Set[string] } var helps = map[string]*help{ {{- range $key, $value := . }} "{{ $key }}": { {{- if $value.LongHelp }} longHelp: "" + {{ $value.LongHelp | splitAndQuoteLines }}, {{- end }} {{- if $value.Example }} example: "" + {{ $value.Example | splitAndQuoteLines }}, {{- end }} {{- if $value.LongFlags }} longFlags: chezmoiset.New({{ $value.LongFlags | quoteElementsOnePerLine }}), {{- end }} {{- if $value.ShortFlags }} shortFlags: chezmoiset.New({{ $value.ShortFlags | quoteElementsOnePerLine }}), {{- end }} }, {{- end }} } ================================================ FILE: internal/cmds/generate-helps/main.go ================================================ // generate-helps generates internal/cmd/helps.gen.go. package main import ( "bytes" _ "embed" "flag" "fmt" "go/format" "os" "regexp" "slices" "strconv" "strings" "text/template" "github.com/charmbracelet/glamour" "github.com/charmbracelet/glamour/styles" "chezmoi.io/chezmoi/assets/chezmoi.io/docs/reference/commands" "chezmoi.io/chezmoi/internal/chezmoiset" ) //go:embed helps.go.tmpl var helpsGoTmpl string type help struct { LongHelp string Example string LongFlags chezmoiset.Set[string] ShortFlags chezmoiset.Set[string] } var ( unwantedEscapeSequenceRx = regexp.MustCompile(`\x1b\[[01]m`) linkRx = regexp.MustCompile(`(?m)\[(.*?)]\[(.*?)]`) helpFlagsRx = regexp.MustCompile("^### (?:`-([0-9A-Za-z])`, )?`--([-0-9A-Za-z]+)`") trailingSpaceRx = regexp.MustCompile(` +\n`) output = flag.String("o", "", "output") ) func run() error { flag.Parse() dirEntries, err := commands.FS.ReadDir(".") if err != nil { return err } longHelpStyleConfig := styles.ASCIIStyleConfig longHelpStyleConfig.Code.BlockPrefix = "" longHelpStyleConfig.Code.BlockSuffix = "" longHelpStyleConfig.Emph.BlockPrefix = "" longHelpStyleConfig.Emph.BlockSuffix = "" longHelpStyleConfig.H2.Prefix = "" longHelpTermRenderer, err := glamour.NewTermRenderer( glamour.WithStyles(longHelpStyleConfig), glamour.WithWordWrap(80), ) if err != nil { return err } exampleStyleConfig := styles.ASCIIStyleConfig exampleStyleConfig.Code.BlockPrefix = "" exampleStyleConfig.Code.BlockSuffix = "" exampleStyleConfig.Document.Margin = nil exampleTermRenderer, err := glamour.NewTermRenderer( glamour.WithStyles(exampleStyleConfig), glamour.WithWordWrap(80), ) if err != nil { return err } helps := make(map[string]*help) for _, dirEntry := range dirEntries { if dirEntry.Name() == "index.md" { continue } command := strings.TrimSuffix(dirEntry.Name(), ".md") data, err := commands.FS.ReadFile(dirEntry.Name()) if err != nil { return err } help, err := extractHelp(command, data, longHelpTermRenderer, exampleTermRenderer) if err != nil { return err } helps[command] = help } funcMap := template.FuncMap{ "quoteElementsOnePerLine": func(s chezmoiset.Set[string]) string { if s.IsEmpty() { return "" } elements := slices.Sorted(s.Elements()) quotedElementLines := make([]string, len(elements)) for i, element := range elements { quotedElementLines[i] = "\n" + strconv.Quote(element) + "," } return strings.Join(quotedElementLines, "") + "\n" }, "splitAndQuoteLines": func(s string) string { lines := strings.Split(s, "\n") quotedLines := make([]string, len(lines)) for i, line := range lines { if i == len(lines)-1 { quotedLines[i] = strconv.Quote(line) } else { quotedLines[i] = strconv.Quote(line + "\n") } } return strings.Join(quotedLines, " +\n") }, } helpsGoTemplate, err := template.New("helps.go.tmpl").Funcs(funcMap).Parse(helpsGoTmpl) if err != nil { return err } var buffer bytes.Buffer if err := helpsGoTemplate.ExecuteTemplate(&buffer, "helps.go.tmpl", helps); err != nil { return err } outputBytes := buffer.Bytes() if formattedOutput, err := format.Source(outputBytes); err == nil { outputBytes = formattedOutput } if *output == "" || *output == "-" { if _, err := os.Stdout.Write(outputBytes); err != nil { return err } } else { if err := os.WriteFile(*output, outputBytes, 0o666); err != nil { return err } } return nil } func main() { if err := run(); err != nil { fmt.Println(err) os.Exit(1) } } // extractHelp returns the helps parse from r. func extractHelp(command string, data []byte, longHelpTermRenderer, exampleTermRenderer *glamour.TermRenderer) (*help, error) { type stateType int const ( stateReadTitle stateType = iota stateInLongHelp stateInOptions stateInExamples stateInNotes stateInAdmonition stateInUnknownSection ) state := stateReadTitle var longHelpLines []string var exampleLines []string longFlags := chezmoiset.New[string]() shortFlags := chezmoiset.New[string]() stateChange := func(line string, state *stateType) bool { switch { case line == "## Flags" || line == "## Common flags": *state = stateInOptions return true case line == "## Examples": *state = stateInExamples return true case line == "## Notes": *state = stateInNotes return true case strings.HasPrefix(line, "## "): *state = stateInUnknownSection return true } return false } for line := range strings.SplitSeq(string(data), "\n") { switch state { case stateReadTitle: titleRx, err := regexp.Compile("# `" + command + "`") if err != nil { return nil, err } if !titleRx.MatchString(line) { return nil, fmt.Errorf("expected title for '%s'", command) } state = stateInLongHelp case stateInLongHelp: switch { case stateChange(line, &state): // Do nothing. case strings.HasPrefix(line, "!!! "): state = stateInAdmonition default: longHelpLines = append(longHelpLines, line) } case stateInExamples: if !stateChange(line, &state) { exampleLines = append(exampleLines, line) } case stateInOptions: if !stateChange(line, &state) { if m := helpFlagsRx.FindStringSubmatch(line); m != nil { if m[1] != "" { shortFlags.Add(m[1]) } longFlags.Add(m[2]) } } default: stateChange(line, &state) } } longHelp, err := renderLines(longHelpLines, longHelpTermRenderer) if err != nil { return nil, err } example, err := renderLines(exampleLines, exampleTermRenderer) if err != nil { return nil, err } return &help{ LongHelp: longHelp, Example: example, LongFlags: longFlags, ShortFlags: shortFlags, }, nil } // renderLines renders lines, trimming extraneous whitespace. func renderLines(lines []string, termRenderer *glamour.TermRenderer) (string, error) { in := strings.Join(lines, "\n") // Replace links with their anchor text only. This should be possible by // using Conceal in the style, but this currently has no effect. See // https://github.com/charmbracelet/glamour/issues/389. in = linkRx.ReplaceAllString(in, "$1") // For some reason, the above regular expression does not work if the link // spans multiple lines. Fix this with a horrible hack for upgrade.md. in = strings.ReplaceAll(in, "[rate\nlimiting][rate]", "rate limiting") renderedLines, err := termRenderer.Render(in) if err != nil { return "", err } // For some reason, glamour adds escape sequences for the first data line of // each table. Remove these. renderedLines = unwantedEscapeSequenceRx.ReplaceAllString(renderedLines, "") renderedLines = trailingSpaceRx.ReplaceAllString(renderedLines, "\n") renderedLines = strings.Trim(renderedLines, "\n") return renderedLines, nil } ================================================ FILE: internal/cmds/generate-install.sh/install.sh.tmpl ================================================ #!/bin/sh # chezmoi install script # contains code from and inspired by # https://github.com/client9/shlib # https://github.com/goreleaser/godownloader set -e BINDIR="${BINDIR:-{{ .BinDir }}}" TAGARG=latest LOG_LEVEL=2 tmpdir="$(mktemp -d)" trap 'rm -rf -- "${tmpdir}"' EXIT trap 'exit' INT TERM usage() { this="${1}" cat <&2 return 1 ;; esac } get_libc() { if is_command ldd; then case "$(ldd --version 2>&1 | tr '[:upper:]' '[:lower:]')" in *glibc* | *"gnu libc"*) # If the version of glibc is too old then use the statically-linked # musl version instead. chezmoi releases are built on GitHub Actions # ubuntu-22.04 runners, which have glibc version 2.35. minimum_glibc_version=2.35 glibc_version="$(ldd --version 2>&1 | awk '$1 == "ldd" { print $NF }')" # shellcheck disable=SC2046,SC2183 minimum_glibc_version_string="$(printf "%03d%03d" $(echo "${minimum_glibc_version}" | tr "." " "))" # shellcheck disable=SC2046,SC2183 glibc_version_string="$(printf "%03d%03d" $(echo "${glibc_version}" | tr "." " "))" log_info "found glibc version ${glibc_version}" if [ "${glibc_version_string}" -lt "${minimum_glibc_version_string}" ]; then printf musl return fi printf glibc return ;; *musl*) printf musl return ;; esac fi if is_command getconf; then case "$(getconf GNU_LIBC_VERSION 2>&1)" in *glibc*) printf glibc return ;; esac fi log_crit "unable to determine libc" 1>&2 exit 1 } real_tag() { tag="${1}" log_debug "checking GitHub for tag ${tag}" release_url="https://github.com/twpayne/chezmoi/releases/${tag}" json="$(http_get "${release_url}" "Accept: application/json")" if [ -z "${json}" ]; then log_err "real_tag error retrieving GitHub release ${tag}" return 1 fi real_tag="$(printf '%s\n' "${json}" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')" if [ -z "${real_tag}" ]; then log_err "real_tag error determining real tag of GitHub release ${tag}" return 1 fi if [ -z "${real_tag}" ]; then return 1 fi log_debug "found tag ${real_tag} for ${tag}" printf '%s' "${real_tag}" } http_get() { tmpfile="$(mktemp)" http_download "${tmpfile}" "${1}" "${2}" || return 1 body="$(cat "${tmpfile}")" rm -f "${tmpfile}" printf '%s\n' "${body}" } http_download_curl() { local_file="${1}" source_url="${2}" header="${3}" if [ -z "${header}" ]; then code="$(curl -w '%{http_code}' -fsSL -o "${local_file}" "${source_url}")" else code="$(curl -w '%{http_code}' -fsSL -H "${header}" -o "${local_file}" "${source_url}")" fi if [ "${code}" != "200" ]; then log_debug "http_download_curl received HTTP status ${code}" return 1 fi return 0 } http_download_wget() { local_file="${1}" source_url="${2}" header="${3}" if [ -z "${header}" ]; then wget -q -O "${local_file}" "${source_url}" || return 1 else wget -q --header "${header}" -O "${local_file}" "${source_url}" || return 1 fi } http_download() { log_debug "http_download ${2}" if is_command curl; then http_download_curl "${@}" || return 1 return elif is_command wget; then http_download_wget "${@}" || return 1 return fi log_crit "http_download unable to find wget or curl" return 1 } hash_sha256() { target="${1}" if is_command sha256sum; then hash="$(sha256sum "${target}")" || return 1 printf '%s' "${hash}" | cut -d ' ' -f 1 elif is_command shasum; then hash="$(shasum -a 256 "${target}" 2>/dev/null)" || return 1 printf '%s' "${hash}" | cut -d ' ' -f 1 elif is_command sha256; then hash="$(sha256 -q "${target}" 2>/dev/null)" || return 1 printf '%s' "${hash}" | cut -d ' ' -f 1 elif is_command openssl; then hash="$(openssl dgst -sha256 "${target}")" || return 1 printf '%s' "${hash}" | cut -d ' ' -f a else log_crit "hash_sha256 unable to find command to compute SHA256 hash" return 1 fi } hash_sha256_verify() { target="${1}" checksums="${2}" basename="${target##*/}" want="$(grep "${basename}\$" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1)" if [ -z "${want}" ]; then log_err "hash_sha256_verify unable to find checksum for ${target} in ${checksums}" return 1 fi got="$(hash_sha256 "${target}")" if [ "${want}" != "${got}" ]; then log_err "hash_sha256_verify checksum for ${target} did not verify ${want} vs ${got}" return 1 fi } untar() { tarball="${1}" case "${tarball}" in *.tar.gz | *.tgz) tar -xzf "${tarball}" ;; *.tar) tar -xf "${tarball}" ;; *.zip) unzip -- "${tarball}" ;; *) log_err "untar unknown archive format for ${tarball}" return 1 ;; esac } is_command() { type "${1}" >/dev/null 2>&1 } log_debug() { [ 3 -le "${LOG_LEVEL}" ] || return 0 printf 'debug %s\n' "${*}" 1>&2 } log_info() { [ 2 -le "${LOG_LEVEL}" ] || return 0 printf 'info %s\n' "${*}" 1>&2 } log_err() { [ 1 -le "${LOG_LEVEL}" ] || return 0 printf 'error %s\n' "${*}" 1>&2 } log_crit() { [ 0 -le "${LOG_LEVEL}" ] || return 0 printf 'critical %s\n' "${*}" 1>&2 } main "${@}" ================================================ FILE: internal/cmds/generate-install.sh/main.go ================================================ // generate-install.sh generates assets/scripts/install.sh and // assets/scripts/install-local-bin.sh. package main import ( "cmp" _ "embed" "encoding/json" "flag" "fmt" "os" "os/exec" "slices" "text/template" "github.com/goccy/go-yaml" "chezmoi.io/chezmoi/internal/chezmoiset" ) var ( binDir = flag.String("b", "bin", "binary directory") output = flag.String("o", "", "output") //go:embed install.sh.tmpl installShTmpl string ) type platform struct { GOOS string GOARCH string } func newPlatform(goos, goarch string) platform { return platform{ GOOS: goos, GOARCH: goarch, } } func (p platform) String() string { return p.GOOS + "/" + p.GOARCH } type platformValue struct { platform CgoSupported bool } type platformSet map[platform]platformValue func goToolDistList() (platformSet, error) { cmd := exec.Command("go", "tool", "dist", "list", "-json") cmd.Stderr = os.Stderr data, err := cmd.Output() if err != nil { return nil, err } var platformValues []platformValue if err := json.Unmarshal(data, &platformValues); err != nil { return nil, err } result := make(platformSet) for _, pv := range platformValues { result[pv.platform] = pv } return result, nil } func run() error { flag.Parse() // Read goreleaser config. data, err := os.ReadFile(".config/goreleaser.yaml") if err != nil { return err } var goreleaserConfig struct { Builds []struct { GOOS []string `yaml:"goos"` GOARCH []string `yaml:"goarch"` Ignore []platform `yaml:"ignore"` } `yaml:"builds"` } if err := yaml.Unmarshal(data, &goreleaserConfig); err != nil { return err } // Read list of supported platforms. supportedPlatforms, err := goToolDistList() if err != nil { return err } // Exclude unsupported platforms. delete(supportedPlatforms, newPlatform("windows", "arm64")) // Build set of platforms. allPlatforms := chezmoiset.New[platform]() for _, build := range goreleaserConfig.Builds { buildPlatforms := chezmoiset.New[platform]() for _, goos := range build.GOOS { for _, goarch := range build.GOARCH { platform := newPlatform(goos, goarch) if _, ok := supportedPlatforms[platform]; ok { buildPlatforms.Add(platform) } } } buildPlatforms.Remove(build.Ignore...) allPlatforms.AddSet(buildPlatforms) } // Sort platforms. sortedPlatforms := slices.SortedFunc(allPlatforms.Elements(), func(a, b platform) int { return cmp.Compare(a.String(), b.String()) }) // Generate install.sh. installShTemplate, err := template.New("install.sh.tmpl").Parse(installShTmpl) if err != nil { return err } var outputFile *os.File if *output == "" || *output == "-" { outputFile = os.Stdout } else { outputFile, err = os.Create(*output) if err != nil { return err } defer outputFile.Close() } return installShTemplate.ExecuteTemplate(outputFile, "install.sh.tmpl", struct { BinDir string Platforms []platform }{ BinDir: *binDir, Platforms: sortedPlatforms, }) } func main() { if err := run(); err != nil { fmt.Println(err) os.Exit(1) } } ================================================ FILE: internal/cmds/generate-install.sh/main_test.go ================================================ package main import ( "testing" "github.com/alecthomas/assert/v2" ) func TestGoToolDistList(t *testing.T) { _, err := goToolDistList() assert.NoError(t, err) } ================================================ FILE: internal/cmds/generate-license/license.go.tmpl ================================================ package cmd var license = "" + {{ . | splitAndQuoteLines }} ================================================ FILE: internal/cmds/generate-license/main.go ================================================ // generate-license generates internal/cmd/license.gen.go. package main // FIXME merge this with internal/cmds/generate-helps import ( "bytes" _ "embed" "flag" "fmt" "go/format" "os" "strconv" "strings" "text/template" "unicode" "github.com/charmbracelet/glamour" "github.com/charmbracelet/glamour/styles" "chezmoi.io/chezmoi/assets/chezmoi.io/docs" ) //go:embed license.go.tmpl var licenseGoTmpl string var output = flag.String("o", "", "output") func run() error { flag.Parse() renderer, err := glamour.NewTermRenderer( glamour.WithStyles(styles.ASCIIStyleConfig), glamour.WithWordWrap(80), ) if err != nil { return err } licenseMarkdown := strings.TrimPrefix(docs.License, "# License\n\n") license, err := renderer.Render(licenseMarkdown) if err != nil { return err } licenseGoTemplate, err := template.New("license.go.tmpl").Funcs(template.FuncMap{ "splitAndQuoteLines": func(s string) string { lines := strings.Split(s, "\n") quotedLines := make([]string, len(lines)) for i, line := range lines { line = strings.TrimRightFunc(line, unicode.IsSpace) if i == len(lines)-1 { quotedLines[i] = strconv.Quote(line) } else { quotedLines[i] = strconv.Quote(line + "\n") } } return strings.Join(quotedLines, " +\n") }, }).Parse(licenseGoTmpl) if err != nil { return err } var buffer bytes.Buffer if err := licenseGoTemplate.ExecuteTemplate(&buffer, "license.go.tmpl", license); err != nil { return err } outputBytes := buffer.Bytes() if formattedOutput, err := format.Source(outputBytes); err == nil { outputBytes = formattedOutput } if *output == "" || *output == "-" { if _, err := os.Stdout.Write(outputBytes); err != nil { return err } } else { if err := os.WriteFile(*output, outputBytes, 0o666); err != nil { return err } } return nil } func main() { if err := run(); err != nil { fmt.Println(err) os.Exit(1) } } ================================================ FILE: internal/cmds/hexencode/main.go ================================================ // hexencode encodes stdin as hex. It is designed for use with the hexdecode // testscript function. package main import ( "encoding/hex" "fmt" "io" "os" ) func run() error { data, err := io.ReadAll(os.Stdin) if err != nil { return err } for i := 0; i < len(data); i += 40 { line := data[i:min(i+40, len(data))] if _, err := fmt.Println(hex.EncodeToString(line)); err != nil { return err } } return nil } func main() { if err := run(); err != nil { fmt.Println(err) os.Exit(1) } } ================================================ FILE: internal/cmds/lint-commit-messages/main.go ================================================ // lint-commit-messages lints commit messages. package main import ( "bufio" "bytes" "fmt" "os" "os/exec" "regexp" "strings" ) var commitRx = regexp.MustCompile(`\A([0-9a-f]{40}) (chore(?:\([\w\-]+\))?|docs|feat|fix): ([A-Z]|bump)`) func run() error { args := append([]string{"log", "--format=oneline"}, os.Args[1:]...) cmd := exec.Command("git", args...) cmd.Stdin = os.Stdin cmd.Stderr = os.Stderr output, err := cmd.Output() if err != nil { return fmt.Errorf("git: %w", err) } var invalidCommitMessages []string scanner := bufio.NewScanner(bytes.NewReader(output)) for scanner.Scan() { commitMessage := scanner.Text() if !commitRx.MatchString(commitMessage) { invalidCommitMessages = append(invalidCommitMessages, commitMessage) } } if err := scanner.Err(); err != nil { return fmt.Errorf("scanner: %w", err) } if len(invalidCommitMessages) != 0 { return fmt.Errorf("invalid commit messages:\n%s", strings.Join(invalidCommitMessages, "\n")) } return nil } func main() { if err := run(); err != nil { fmt.Println(err) os.Exit(1) } } ================================================ FILE: internal/cmds/lint-commit-messages/main_test.go ================================================ package main import ( "strings" "testing" "github.com/alecthomas/assert/v2" ) func TestCommitRx(t *testing.T) { prefix := strings.Repeat("0", 40) + " " for s, match := range map[string]bool{ "chore(deps): Text": true, "chore(deps-dev): Text": true, "chore(deps): bump": true, "chore: Text": true, "docs: Text": true, "feat: Text": true, "fix: Text": true, "fixup!": false, "snapshot": false, } { assert.Equal(t, match, commitRx.MatchString(prefix+s)) } } ================================================ FILE: internal/cmds/lint-txtar/main.go ================================================ // lint-txtar lints txtar files. package main import ( "errors" "flag" "fmt" "os" "slices" "strings" "github.com/rogpeppe/go-internal/txtar" "chezmoi.io/chezmoi/internal/chezmoierrors" "chezmoi.io/chezmoi/internal/chezmoiset" ) var write = flag.Bool("w", false, "rewrite archives") func lintFilenames(archiveFilename string, archive *txtar.Archive) error { var errs []error filenames := chezmoiset.New[string]() for _, file := range archive.Files { if file.Name == "" { errs = append(errs, fmt.Errorf("%s: empty filename", archiveFilename)) } else { if filenames.Contains(file.Name) { errs = append(errs, fmt.Errorf("%s: %s: duplicate filename", archiveFilename, file.Name)) } filenames.Add(file.Name) } } return errors.Join(errs...) } func sortFilesFunc(file1, file2 txtar.File) int { fileComponents1 := strings.Split(file1.Name, "/") fileComponents2 := strings.Split(file2.Name, "/") return slices.Compare(fileComponents1, fileComponents2) } func tidyTxtar(archiveFilename string) error { archive, err := txtar.ParseFile(archiveFilename) if err != nil { return err } if err := lintFilenames(archiveFilename, archive); err != nil { return err } if slices.IsSortedFunc(archive.Files, sortFilesFunc) { return nil } if *write { slices.SortFunc(archive.Files, sortFilesFunc) return os.WriteFile(archiveFilename, txtar.Format(archive), 0o666) } return fmt.Errorf("%s: files are not sorted", archiveFilename) } func run() error { flag.Parse() errs := make([]error, 0, flag.NArg()) for _, arg := range flag.Args() { errs = append(errs, tidyTxtar(arg)) } return chezmoierrors.Combine(errs...) } func main() { if err := run(); err != nil { fmt.Println(err) os.Exit(1) } } ================================================ FILE: internal/cmds/lint-whitespace/main.go ================================================ // lint-whitespace lints files for whitespace. package main import ( "bytes" "fmt" "io/fs" "net/http" "os" "regexp" "strings" "chezmoi.io/chezmoi/internal/chezmoierrors" ) var ( ignoreRxs = []*regexp.Regexp{ regexp.MustCompile(`\.svg\z`), regexp.MustCompile(`\A\.git\z`), regexp.MustCompile(`\A\.idea\z`), regexp.MustCompile(`\A\.jj\z`), regexp.MustCompile(`\A\.ruff_cache\z`), regexp.MustCompile(`\A\.vagrant\z`), regexp.MustCompile(`\A\.venv\z`), regexp.MustCompile(`\A\.vscode\z`), regexp.MustCompile(`\ACOMMIT\z`), regexp.MustCompile(`\Aassets/chezmoi\.io/site\z`), regexp.MustCompile(`\Aassets/scripts/install\.ps1\z`), regexp.MustCompile(`\Acompletions/chezmoi\.ps1\z`), regexp.MustCompile(`\Adist\z`), } crlfLineEndingRx = regexp.MustCompile(`\r\z`) trailingWhitespaceRx = regexp.MustCompile(`\s+\z`) ) func lintData(filename string, data []byte) error { if !strings.HasPrefix(http.DetectContentType(data), "text/") { return nil } lines := bytes.Split(data, []byte{'\n'}) var errs []error for i, line := range lines { switch { case crlfLineEndingRx.Match(line): errs = append(errs, fmt.Errorf("::error file=%s,line=%d::CRLF line ending", filename, i+1)) case trailingWhitespaceRx.Match(line): errs = append(errs, fmt.Errorf("::error file=%s,line=%d::trailing whitespace", filename, i+1)) } } if len(data) > 0 && len(lines[len(lines)-1]) != 0 { errs = append(errs, fmt.Errorf("::error file=%s,line=%d::no newline at end of file", filename, len(lines)+1)) } return chezmoierrors.Combine(errs...) } func lintFile(filename string) error { data, err := os.ReadFile(filename) if err != nil { return err } return lintData(filename, data) } func run() error { var lintErrs []error if err := fs.WalkDir(os.DirFS("."), ".", func(path string, dirEntry fs.DirEntry, err error) error { if err != nil { return err } for _, rx := range ignoreRxs { if rx.MatchString(path) { if dirEntry.IsDir() { return fs.SkipDir } return nil } } if dirEntry.Type().IsRegular() { lintErrs = append(lintErrs, lintFile(path)) } return nil }); err != nil { return err } return chezmoierrors.Combine(lintErrs...) } func main() { if err := run(); err != nil { fmt.Println(err) os.Exit(1) } } ================================================ FILE: internal/cmds/lint-whitespace/main_test.go ================================================ package main import ( "strconv" "testing" "github.com/alecthomas/assert/v2" ) func TestLintData(t *testing.T) { for i, tc := range []struct { data []byte expectedErrStr string }{ { data: nil, expectedErrStr: "", }, { data: []byte("package main\n"), expectedErrStr: "", }, { data: []byte("package main\r\n"), expectedErrStr: "CRLF line ending", }, { data: []byte("package main \n"), expectedErrStr: "trailing whitespace", }, { data: []byte("package main"), expectedErrStr: "no newline at end of file", }, } { t.Run(strconv.Itoa(i), func(t *testing.T) { actualErr := lintData("main.go", tc.data) if tc.expectedErrStr == "" { assert.NoError(t, actualErr) } else { assert.Contains(t, actualErr.Error(), tc.expectedErrStr) } }) } } ================================================ FILE: main.go ================================================ //go:generate go tool chezmoi completion bash -o completions/chezmoi-completion.bash //go:generate go tool chezmoi completion fish -o completions/chezmoi.fish //go:generate go tool chezmoi completion powershell -o completions/chezmoi.ps1 //go:generate go tool chezmoi completion zsh -o completions/chezmoi.zsh //go:generate go tool generate-helps -o internal/cmd/helps.gen.go //go:generate go tool generate-install.sh -o assets/scripts/install.sh //go:generate go tool generate-install.sh -b .local/bin -o assets/scripts/install-local-bin.sh //go:generate go tool generate-license -o internal/cmd/license.gen.go // chezmoi manages your dotfiles across multiple machines, securely. package main import ( "os" "chezmoi.io/chezmoi/internal/cmd" ) var ( version string commit string date string builtBy string ) func main() { if exitCode := cmd.Main(cmd.VersionInfo{ Version: version, Commit: commit, Date: date, BuiltBy: builtBy, }, os.Args[1:]); exitCode != 0 { os.Exit(exitCode) } } ================================================ FILE: main_test.go ================================================ package main import ( "testing" "github.com/alecthomas/assert/v2" "chezmoi.io/chezmoi/internal/cmd" ) func TestMain(t *testing.T) { versionInfo := cmd.VersionInfo{ Version: version, Commit: commit, Date: date, BuiltBy: builtBy, } args := []string{"--version"} assert.Equal(t, 0, cmd.Main(versionInfo, args)) } ================================================ FILE: pyproject.toml ================================================ [project] name = "chezmoi" version = "2.59.1" classifiers = ["Private :: Do Not Upload"] requires-python = ">=3.14" license = { text = "MIT" } dependencies = [ "mkdocs>=1.6.1", "mkdocs-material>=9.7.5", "mkdocs-mermaid2-plugin>=1.2.3", "pymdown-extensions>=10.21", "ruamel.yaml>=0.19.1", "ruff>=0.15.6", "taskipy>=1.14.1", ] [tool.taskipy.tasks] build-docs = { cmd = "mkdocs build -f assets/chezmoi.io/mkdocs.yml" } serve-docs = { cmd = "mkdocs serve -f assets/chezmoi.io/mkdocs.yml" } deploy-docs = { cmd = "cd assets/chezmoi.io && mkdocs gh-deploy" } lint = { cmd = "ruff check" } format = { cmd = "ruff format" } pycheck = { cmd = "task lint && task format --diff" } pyfix = { cmd = "task lint --fix && task format" } format-yaml = { cmd = "./assets/scripts/format-yaml.py" } [tool.ruff] include = [ "pyproject.toml", "assets/chezmoi.io/docs/hooks.py", "assets/chezmoi.io/docs/shortcodes.py", "assets/scripts/format-yaml.py", ] target-version = "py314" [tool.ruff.lint] extend-select = [ "A", "B", "E", "F", "I", "W", "ANN", "COM", "FA", "UP", "PL", "PTH", "SIM", ] ignore = ["ANN003", "COM812"] [tool.ruff.format] quote-style = "single" [tool.uv.workspace] members = ["chezmoi"]