├── .gitignore ├── library ├── aws.phar ├── bashrc-darwin.sh ├── imos-lambda.php ├── ninestream.php └── git-prompt.sh ├── test ├── imos-bashrc.sh ├── imos-package-archive.sh ├── imos-stat.sh ├── imos-passgen_test.sh ├── imos-bashrc_test.sh ├── imos-package-run_test.sh ├── imos-crypt.sh ├── imos-package-run.sh ├── imos-start_test.sh ├── imos-start.sh ├── imos-pokemon_test.sh ├── imos-stat_test.sh ├── imos-package-archive_test.sh ├── imos-install.sh ├── imofs.sh ├── imos-package-download_test.sh ├── imos-install_test.sh ├── imofs_test.sh ├── imos-package-upload_test.sh └── imos-pokemon.sh ├── data ├── installed-password.aes256 ├── homebrew-api-token.sh.aes256 ├── bashrc ├── aws.aes256 ├── LaunchDaemons │ └── jp.imoz.imos-start.plist ├── ninelet.aes256 ├── variance │ ├── Psyduck │ │ └── test.tar.aes256 │ └── Moltres │ │ └── office_2011.tar.aes256 └── pokemon.txt ├── Makefile ├── imos-aws-credentials ├── imos-aws ├── ssh-for-git ├── imos-lock ├── imos-lambda ├── tool ├── update ├── setup └── update-readme ├── stelnet ├── LICENSE ├── ninestream ├── imos-passgen ├── imos-docker-run ├── imos-diskdiff ├── imos-start ├── imos-stat ├── ninelet ├── imos-bashrc ├── imos-variables ├── imos-crypt ├── imos-archive ├── imos-pokemon ├── imos-install ├── imofs ├── README.md └── imos-package /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /library/aws.phar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imos/bin/master/library/aws.phar -------------------------------------------------------------------------------- /test/imos-bashrc.sh: -------------------------------------------------------------------------------- 1 | source "$(dirname "${BASH_SOURCE}")"/../imos-bashrc || exit 1 2 | eval "echo \"${PS1}\"" > '/dev/null' 3 | -------------------------------------------------------------------------------- /data/installed-password.aes256: -------------------------------------------------------------------------------- 1 | U2FsdGVkX18BIzxre14yM2OuDrLwSRFEfeFNWNXbVT/Iidb4HF0u5rKdJ8R8ROFW 2 | Uy2jS8pVu2JNffxn5WW4HE0oCi9k6ZsRQiEyy+j1+C0= 3 | -------------------------------------------------------------------------------- /test/imos-package-archive.sh: -------------------------------------------------------------------------------- 1 | echo "# of args: $#" 2 | if [ "$#" -ne 0 ]; then 3 | for argument in "$@"; do 4 | echo "${argument}" 5 | done 6 | fi 7 | -------------------------------------------------------------------------------- /data/homebrew-api-token.sh.aes256: -------------------------------------------------------------------------------- 1 | U2FsdGVkX18IU0MKoonxNOcWMjvjPuk+frsrpIJwTzWngHzobqiwsRJkczdWrdMW 2 | 2Ssf4E0QFxN18IDa608pMiHXvIn/hsFjm2OGm1Xeq0GvXwTRXLpk5DwgZVbsVYGt 3 | -------------------------------------------------------------------------------- /data/bashrc: -------------------------------------------------------------------------------- 1 | if [ -f ~/bin/imos-bashrc ]; then 2 | source ~/bin/imos-bashrc 3 | elif [ -f /usr/local/imos/bin/imos-bashrc ]; then 4 | source /usr/local/imos/bin/imos-bashrc 5 | fi 6 | -------------------------------------------------------------------------------- /library/bashrc-darwin.sh: -------------------------------------------------------------------------------- 1 | edit() { 2 | if [ "${#}" -ne 0 ]; then 3 | open -a 'Visual Studio Code' "${@}" 4 | else 5 | LOG ERROR 'Wrong number of arguments.' 6 | fi 7 | } 8 | -------------------------------------------------------------------------------- /test/imos-stat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "$(dirname "${BASH_SOURCE}")"/../imosh || exit 1 4 | 5 | stat() { 6 | echo "$*" 7 | } 8 | 9 | source "$(dirname "${BASH_SOURCE}")"/../imos-stat || exit 1 10 | -------------------------------------------------------------------------------- /data/aws.aes256: -------------------------------------------------------------------------------- 1 | U2FsdGVkX19F9BUh1Qtm9a4Xaxinrs/KNfL0VjgBeeonE4jHTRPj5U147R1YVX3Z 2 | G59W25we4PpkbcAL7vb5fbVWLVPEqWekrEXxRnbefjHJIvFJJM63wT9/SCJfeawr 3 | s+AqKMIlAAGulFl/G6y4h8fLawR6Mbs28cA4wQlS3rKtR4XWAkUN3V741n8eDSga 4 | -------------------------------------------------------------------------------- /test/imos-passgen_test.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | SSH_TTY= "$(dirname "${BASH_SOURCE}")"/../imos-passgen 3 | } 4 | 5 | test::imos-passgen() { 6 | EXPECT_EQ 'zKIQMRw8' "$(echo $'foo\nbar' | run)" 7 | EXPECT_EQ 'J7GedYjG' "$(echo $'hoge\npiyo' | run)" 8 | } 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | readme: 2 | tool/update-readme 3 | .PHONY: readme 4 | 5 | update: readme 6 | tool/update 7 | .PHONY: update 8 | 9 | test: 10 | bash --version 11 | env 12 | bash -c shopt 13 | @if ! ./imosh test/*_test.sh; then exit 1; fi 14 | .PHONY: test 15 | -------------------------------------------------------------------------------- /test/imos-bashrc_test.sh: -------------------------------------------------------------------------------- 1 | test::imos-bashrc() { 2 | PS1='$' POKEMON='Pikachu' UNAME='Darwin' \ 3 | "$(dirname "${BASH_SOURCE}")"/imos-bashrc.sh 4 | PS1='$' POKEMON='Pikachu' UNAME='Linux' \ 5 | "$(dirname "${BASH_SOURCE}")"/imos-bashrc.sh 6 | PS1='$' POKEMON='Pikachu' UNAME='Unknown' \ 7 | "$(dirname "${BASH_SOURCE}")"/imos-bashrc.sh 8 | } 9 | -------------------------------------------------------------------------------- /imos-aws-credentials: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-aws-credentials shows AWS credentials. 3 | # 4 | # Usage: 5 | # imos-aws-credentials 6 | 7 | source "$(dirname "${BASH_SOURCE}")"/imos-variables || exit 1 8 | eval "${IMOSH_INIT}" 9 | 10 | imos-crypt --decrypt --installed_password \ 11 | "${IMOS_BIN}/data/aws.aes256" "${TMPDIR}/credentials" 12 | cat "${TMPDIR}/credentials" 13 | -------------------------------------------------------------------------------- /test/imos-package-run_test.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | "$(dirname "${BASH_SOURCE}")/imos-package-run.sh" "$@" & 3 | wait "$!" || echo "Exit status: $?" 4 | } 5 | 6 | test::imos-package::run() { 7 | EXPECT_EQ 'foo bar' "$(run --command='echo foo bar')" 8 | EXPECT_EQ 'Exit status: 134' "$(run --command='kill -SIGABRT $$')" 9 | EXPECT_EQ 'Exit status: 137' "$(run --command='kill -SIGKILL $$')" 10 | } 11 | -------------------------------------------------------------------------------- /imos-aws: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-aws calls aws with imos credentails. 3 | # 4 | # Usage: 5 | # imos-aws options... 6 | 7 | source "$(dirname "${BASH_SOURCE}")"/imos-variables || exit 1 8 | IMOSH_PREDICATE=1 eval "${IMOSH_INIT}" 9 | 10 | mkdir "${TMPDIR}/.aws" 11 | sudo imos-aws-credentials > "${TMPDIR}/.aws/credentials" 12 | { 13 | sub::println '[default]' 14 | sub::println 'output = json' 15 | sub::println 'region = ap-northeast-1' 16 | } > "${TMPDIR}/.aws/config" 17 | HOME="${TMPDIR}" aws "$@" 18 | -------------------------------------------------------------------------------- /ssh-for-git: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ssh-for-git runs ssh especially for git. 3 | # 4 | # ssh-for-git uses the GIT_SSH_IDENTITY environment variable to specify an 5 | # identity file. Plus, it specifies StrictHostKeyChecking=no so that git is 6 | # not blocked for known_hosts registration. 7 | # 8 | # Usage: 9 | # GIT_SSH=ssh-for-git GIT_SSH_IDENTITY= \ 10 | # git 11 | # 12 | # Caveat: 13 | # GIT_SSH_IDENTITY must be specified as an environment variable. 14 | 15 | set -e -u 16 | ssh -i "${GIT_SSH_IDENTITY}" -o StrictHostKeyChecking=no "$@" 17 | -------------------------------------------------------------------------------- /test/imos-crypt.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | bash "$(dirname "${BASH_SOURCE}")"/../imos-crypt \ 3 | --password=PASSW0RD \ 4 | --alsologtostderr="${FLAGS_alsologtostderr}" \ 5 | --logtostderr="${FLAGS_logtostderr}" \ 6 | "$@" 7 | } 8 | 9 | test::imos-crypt() { 10 | sub::print 'foobar' > "${TMPDIR}/input" 11 | run --encrypt "${TMPDIR}/input" "${TMPDIR}/encrypted" 12 | EXPECT_NE 'foobar' \ 13 | "$(cat "${TMPDIR}/encrypted")" 14 | run --decrypt "${TMPDIR}/encrypted" "${TMPDIR}/decrypted" 15 | EXPECT_EQ 'foobar' \ 16 | "$(cat "${TMPDIR}/decrypted")" 17 | } 18 | -------------------------------------------------------------------------------- /test/imos-package-run.sh: -------------------------------------------------------------------------------- 1 | source "$(dirname "${BASH_SOURCE}")"/../imos-package || exit 1 2 | 3 | imos-package::download() { 4 | LOG INFO "imos-package::download $*" 5 | if [ "$#" -eq 1 ]; then 6 | CHECK [ "${1}" = '0123456789abcdef0123456789abcdef' ] 7 | CHECK [ "${FLAGS_command}" != '' ] 8 | CHECK [ "${FLAGS_output}" != '' ] 9 | sub::println "${FLAGS_command}" >"${FLAGS_output}" 10 | else 11 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 12 | fi 13 | } 14 | 15 | eval "${IMOSH_INIT}" 16 | CHECK [ "$#" -eq 0 ] 17 | imos-package::run 0123456789abcdef0123456789abcdef "$@" 18 | -------------------------------------------------------------------------------- /imos-lock: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-lock is a lock script. 3 | # 4 | # Usage: 5 | # imos-lock key 6 | 7 | source "$(dirname "${BASH_SOURCE}")"/imos-variables || exit 1 8 | DEFINE_string endpoint 'http://lock.imoz.jp/lock' 'End point of a lock service.' 9 | eval "${IMOSH_INIT}" 10 | 11 | if [ "${#}" -ne 1 ]; then 12 | LOG FATAL "imos-lock requires no arguments." 13 | fi 14 | KEY="${1}" 15 | OWNER="$(whoami)@$(hostname):$$.${RANDOM}" 16 | LOG INFO "Locking ${KEY} as ${OWNER}..." 17 | 18 | lock() { 19 | "${FLAGS_endpoint}?key=${KEY}&owner=${OWNER}&duration=10" 20 | } 21 | 22 | main() { 23 | "${KEY}" 24 | check 25 | "start::$(sub::strtolower "${UNAME}")" 26 | } 27 | 28 | main 29 | -------------------------------------------------------------------------------- /test/imos-start_test.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | bash "$(dirname "${BASH_SOURCE}")"/imos-start.sh "$@" 3 | } 4 | 5 | test::imos-start::darwin() { 6 | export IMOS_ROOT="${TMPDIR}/root" 7 | mkdir -p "${IMOS_ROOT}/Users/Guest/Pictures/test" 8 | mkdir -p "${IMOS_ROOT}/Users/foo/Pictures/test" 9 | mkdir -p "${IMOS_ROOT}/usr/local/imos/config" 10 | sub::print 'PASSW0RD' > "${IMOS_ROOT}/usr/local/imos/config/installed-password" 11 | UNAME=Darwin run 12 | EXPECT_TRUE [ ! -L "${IMOS_ROOT}/Users/Guest/Pictures" ] 13 | EXPECT_EQ "${IMOS_ROOT}/Volumes/Arceus/Users/foo/Pictures" \ 14 | "$(readlink "${IMOS_ROOT}/Users/foo/Pictures")" 15 | EXPECT_EQ 'hoge' "$(cat "${IMOS_ROOT}/foo/bar")" 16 | } 17 | -------------------------------------------------------------------------------- /test/imos-start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "$(dirname "${BASH_SOURCE}")"/../imos-variables || exit 1 4 | 5 | whoami() { 6 | LOG INFO "Command: whoami $*" 7 | if [ "$#" -eq 0 ]; then 8 | echo root 9 | else 10 | LOG FATAL "Wrong number of arguments: $#" 11 | fi 12 | } 13 | 14 | imos-pokemon() { 15 | LOG INFO "Command: imos-pokemon $*" 16 | echo 'Psyduck' 17 | } 18 | 19 | mkdir -p "${TMPDIR}/scutil" 20 | scutil() { 21 | if [ "${#}" -eq 3 ]; then 22 | CHECK [ "${1}" = '--set' ] 23 | sub::println "${3}" > "${TMPDIR}/scutil/${2}" 24 | else 25 | LOG FATAL "Wrong number of arguments: ${#}" 26 | fi 27 | } 28 | 29 | source "$(dirname "${BASH_SOURCE}")"/../imos-start || exit 1 30 | -------------------------------------------------------------------------------- /imos-lambda: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-lambda 3 | # 4 | # Usage: 5 | # imos-lambda 6 | 7 | source "$(dirname "${BASH_SOURCE}")"/imosh || exit 1 8 | DEFINE_string input '' 'Input.' 9 | DEFINE_string object '' 'Object.' 10 | DEFINE_string function 'exec' 'Function name.' 11 | DEFINE_string bucket 'lambda.imoz.jp' 'Bucket name to read from/write to.' 12 | IMOSH_PREDICATE=1 eval "${IMOSH_INIT}" 13 | 14 | IMOS_BIN="$(cd "$(dirname "${BASH_SOURCE}")"; pwd)" 15 | 16 | if [ "${FLAGS_input}" != '' ]; then 17 | export IMOS_LAMBDA_INPUT="${FLAGS_input}" 18 | fi 19 | if [ "${FLAGS_object}" != '' ]; then 20 | export IMOS_LAMBDA_OBJECT="${FLAGS_object}" 21 | fi 22 | 23 | IMOS_LAMBDA_FUNCTION="${FLAGS_function}" \ 24 | IMOS_LAMBDA_BUCKET='lambda.imoz.jp' \ 25 | php "${IMOS_BIN}/library/imos-lambda.php" "$@" 26 | -------------------------------------------------------------------------------- /test/imos-pokemon_test.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | bash "$(dirname "${BASH_SOURCE}")"/imos-pokemon.sh \ 3 | --nocache \ 4 | --cache_file= \ 5 | --pokemon_error_level=FATAL \ 6 | --alsologtostderr="${FLAGS_alsologtostderr}" \ 7 | --logtostderr="${FLAGS_logtostderr}" \ 8 | "$@" 9 | } 10 | 11 | test::imos-pokemon() { 12 | # Test only when Darwin-specific commands exist. 13 | if [ "${UNAME}" = 'Darwin' ]; then 14 | EXPECT_EQ 'Pikachu' "$(UNAME=Darwin run)" 15 | EXPECT_EQ '25' "$(UNAME=Darwin run --id)" 16 | fi 17 | 18 | EXPECT_EQ 'Dunsparce' "$(UNAME=Linux run)" 19 | EXPECT_EQ '206' "$(UNAME=Linux run --id)" 20 | 21 | echo 'Pikachu' > "${TMPDIR}/POKEMON_NAME" 22 | EXPECT_EQ 'Pikachu' "$( 23 | UNAME=Darwin run --cache --cache_file="${TMPDIR}/POKEMON_NAME")" 24 | } 25 | -------------------------------------------------------------------------------- /data/LaunchDaemons/jp.imoz.imos-start.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Disabled 6 | 7 | KeepAlive 8 | 9 | EnvironmentVariables 10 | 11 | IMOSH_FLAGS_alsologtostderr 12 | 1 13 | 14 | Label 15 | jp.imoz.imos-start 16 | ProgramArguments 17 | 18 | /bin/bash 19 | /usr/local/imos/bin/imos-start 20 | --alsologtostderr 21 | 22 | RunAtLoad 23 | 24 | StandardOutPath 25 | /tmp/jp.imoz.imos-start.log.STDOUT 26 | StandardErrorPath 27 | /tmp/jp.imoz.imos-start.log.STDERR 28 | 29 | 30 | -------------------------------------------------------------------------------- /test/imos-stat_test.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | bash "$(dirname "${BASH_SOURCE}")"/imos-stat.sh \ 3 | --use_native_uname=false \ 4 | --alsologtostderr="${FLAGS_alsologtostderr}" \ 5 | --logtostderr="${FLAGS_logtostderr}" \ 6 | "$@" 7 | } 8 | 9 | test::imos-stat::darwin() { 10 | export UNAME=Darwin 11 | local options='' 12 | 13 | options="$(run --format=%g:%u /file)" 14 | EXPECT_EQ '-f %g:%u /file' "${options}" 15 | 16 | options="$(run --format=%G:%U /file)" 17 | EXPECT_EQ '-f %Sg:%Su /file' "${options}" 18 | } 19 | 20 | test::imos-stat::linux() { 21 | export UNAME=Linux 22 | local options='' 23 | 24 | options="$(run --format=%g:%u /file)" 25 | EXPECT_EQ '--format %g:%u /file' "${options}" 26 | 27 | options="$(run --format=%G:%U /file)" 28 | EXPECT_EQ '--format %G:%U /file' "${options}" 29 | } 30 | 31 | test::imos-stat() { 32 | EXPECT_EQ '2312' "$( 33 | "$(dirname "${BASH_SOURCE}")/../imos-stat" \ 34 | --format=%s "$(dirname "${BASH_SOURCE}")/../imos-stat")" 35 | } 36 | -------------------------------------------------------------------------------- /tool/update: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Update scripts inside the bin directory. 3 | # 4 | # This script should not use imosh so that we can revert imosh even if imosh is 5 | # buggy. 6 | 7 | GIT_URL='https://raw.githubusercontent.com' 8 | TARGETS=( 9 | "imosh@${GIT_URL}/imos/imosh/master/imosh" 10 | "library/git-prompt.sh@${GIT_URL}/git/git/master/contrib/completion/git-prompt.sh" 11 | ) 12 | 13 | cd "$(dirname "${BASH_SOURCE}")/.." 14 | 15 | update() { 16 | if [ "$#" -eq 2 ]; then 17 | local command="$1" 18 | local url="$2" 19 | curl "${url}" > "${command}" 20 | chmod +x "${command}" 21 | else 22 | echo 'update requires at least two arguments.' >&2 23 | exit 1 24 | fi 25 | } 26 | 27 | main() { 28 | local target='' 29 | for target in "${TARGETS[@]}"; do 30 | IFS='@' eval "local args=(\${target})" 31 | if [ "${#}" -eq 1 ]; then 32 | if [ "${1}" != "${args[0]}" ]; then 33 | continue 34 | fi 35 | fi 36 | update "${args[@]}" 37 | done 38 | } 39 | 40 | main "${@}" 41 | -------------------------------------------------------------------------------- /test/imos-package-archive_test.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | local output_file='' 3 | func::tmpfile output_file 4 | pushd "$(dirname "${BASH_SOURCE}")" >'/dev/null' 5 | ../imos-package archive --output "${output_file}" "$@" 6 | popd >'/dev/null' 7 | if [ "${#run_options[*]}" -ne 0 ]; then 8 | "${output_file}" "${run_options[@]}" 9 | else 10 | "${output_file}" 11 | fi 12 | } 13 | 14 | test::imos-package::archive() { 15 | local run_options=() 16 | local sample_command='./imos-package-archive.sh' 17 | EXPECT_EQ 'foo bar' "$(run --command='echo foo bar')" 18 | EXPECT_EQ 'foo bar' "$(run --command='echo foo bar' --file_size=10000000)" 19 | EXPECT_DEATH "$(run --command='echo foo bar' --file_size=1000)" 20 | EXPECT_EQ '# of args: 0' "$(run --command="${sample_command}")" 21 | run_options=('foo bar') 22 | EXPECT_EQ $'# of args: 1\nfoo bar' "$(run --command="${sample_command}")" 23 | run_options=('foo' 'bar') 24 | EXPECT_EQ $'# of args: 2\nfoo\nbar' "$(run --command="${sample_command}")" 25 | EXPECT_EQ $'# of args: 0' "$( 26 | run --command="${sample_command}" --noextra_arguments)" 27 | } 28 | -------------------------------------------------------------------------------- /stelnet: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # stelnet is an SSL client. 3 | # 4 | # stelnet is used to communicate wit hanother host using the SSL protocol. 5 | # stelnet requires the openssl command. If port is not specified, 443 (HTTPS) 6 | # is used. 7 | # 8 | # Usage: 9 | # stelnet host [port] 10 | 11 | source "$(dirname "${BASH_SOURCE}")"/imosh || exit 1 12 | DEFINE_bool crlf true 'Use CRLF instead for new lines.' 13 | DEFINE_bool ssl3 true 'Use SSL3.' 14 | DEFINE_bool debug false 'Show debug information.' 15 | eval "${IMOSH_INIT}" 16 | 17 | main() { 18 | local host="$1" 19 | local port="$2" 20 | 21 | local options=(-host "${host}" -port "${port}") 22 | if (( FLAGS_crlf )); then options+=(-crlf); fi 23 | if (( FLAGS_ssl3 )); then options+=(-ssl3); fi 24 | if (( FLAGS_ssl3 )); then options+=(-ssl3); fi 25 | if (( FLAGS_debug )); then 26 | openssl s_client "${options[@]}" 27 | else 28 | openssl s_client -quiet "${options[@]}" 2>/dev/null 29 | fi 30 | } 31 | 32 | if [ "${#}" -eq 2 ]; then 33 | main "$@" 34 | elif [ "${#}" -eq 1 ]; then 35 | main "$@" 443 36 | else 37 | LOG FATAL 'stelnet requires one or two arguments.' 38 | fi 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Kentaro IMAJO 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /test/imos-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "$(dirname "${BASH_SOURCE}")"/../imosh || exit 1 4 | 5 | whoami() { 6 | LOG INFO "Command: whoami $*" 7 | if [ "$#" -eq 0 ]; then 8 | echo root 9 | else 10 | LOG FATAL "Wrong number of arguments: $#" 11 | fi 12 | } 13 | 14 | imos-pokemon() { 15 | LOG INFO "Command: imos-pokemon $*" 16 | echo 'Pikachu' 17 | } 18 | 19 | imos-crypt() { 20 | LOG INFO "Command: imos-crypt $*" 21 | } 22 | 23 | LAUNCHCTL_DIRECTORY="${TMPDIR}/launchctl" 24 | mkdir -p "${LAUNCHCTL_DIRECTORY}" 25 | echo $'PID\tStatus\tLabel' > "${LAUNCHCTL_DIRECTORY}/HEADER" 26 | echo $'17\t0\tcom.apple.syslogd' > "${LAUNCHCTL_DIRECTORY}/com.apple.syslogd" 27 | echo $'-\t0\tjp.imoz.foo' > "${LAUNCHCTL_DIRECTORY}/jp.imoz.foo" 28 | launchctl() { 29 | case "${1}" in 30 | list) 31 | cat "${LAUNCHCTL_DIRECTORY}"/* 32 | ;; 33 | load) 34 | CHECK [ -f "${2}" ] 35 | ;; 36 | remove) 37 | CHECK [ -f "${LAUNCHCTL_DIRECTORY}/${2}" ] 38 | rm "${LAUNCHCTL_DIRECTORY}/${2}" 39 | ;; 40 | *) 41 | LOG FATAL "Unknown launchctl command: ${1}";; 42 | esac 43 | } 44 | 45 | source "$(dirname "${BASH_SOURCE}")"/../imos-install || exit 1 46 | -------------------------------------------------------------------------------- /test/imofs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "$(dirname "${BASH_SOURCE}")"/../imosh || exit 1 4 | 5 | whoami() { 6 | LOG INFO "Command: whoami $*" 7 | if [ "$#" -eq 0 ]; then 8 | echo root 9 | else 10 | LOG FATAL "Wrong number of arguments: $#" 11 | fi 12 | } 13 | 14 | service() { 15 | LOG INFO "Command: service $*" 16 | if [ "$#" -eq 2 ]; then 17 | local service="$1" 18 | local command="$2" 19 | case "${command}" in 20 | status) 21 | if ! sub::isset "SERVICE_${service}"; then 22 | LOG ERROR "Unrecognized service: ${service}" 23 | return 1 24 | fi 25 | if (( SERVICE_${service} )); then 26 | return 0 27 | fi 28 | return 2 29 | ;; 30 | start) 31 | func::let "SERVICE_${service}" 1 32 | ;; 33 | stop) 34 | func::let "SERVICE_${service}" 0 35 | ;; 36 | esac 37 | else 38 | LOG FATAL "Wrong number of arguments: $#" 39 | fi 40 | } 41 | 42 | SERVICE_mysqld=1 43 | 44 | rsync() { 45 | CHECK --message='mysqld must be turned off during rsync.' \ 46 | [ "${SERVICE_mysqld}" -eq 0 ] 47 | command -p rsync "$@" 48 | } 49 | 50 | source "$(dirname "${BASH_SOURCE}")"/../imofs || exit 1 51 | 52 | CHECK --message='mysqld is not restored.' [ "${SERVICE_mysqld}" -eq 1 ] 53 | -------------------------------------------------------------------------------- /ninestream: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ninestream runs a command on a ninelet. 3 | # 4 | # Usage: 5 | # ninelet [options] args... 6 | 7 | source "$(dirname "${BASH_SOURCE}")"/imos-variables || exit 1 8 | DEFINE_string worker '' 'Command as a worker.' 9 | DEFINE_string controller '' 'Command as a controller.' 10 | DEFINE_int replicas 0 'The number of replicas.' 11 | DEFINE_bool worker_stderr true "Outputs workers' stderr." 12 | DEFINE_bool debug true 'Outputs I/O from the controller.' 13 | DEFINE_bool interactive false 'Enables interactive mode.' 14 | eval "${IMOSH_INIT}" 15 | 16 | main() { 17 | { 18 | echo "set controller.debug ${FLAGS_debug}" 19 | echo "set worker.stderr ${FLAGS_worker_stderr}" 20 | if [ "${FLAGS_worker}" != '' ]; then 21 | if [ "${FLAGS_replicas}" -lt 1 ]; then 22 | LOG FATAL '--replicas must be larger than 0.' 23 | fi 24 | echo "run ${FLAGS_replicas} ${FLAGS_worker}" 25 | fi 26 | if [ "${FLAGS_controller}" != '' ]; then 27 | echo "exec ${FLAGS_controller}" 28 | elif (( ! FLAGS_interactive )); then 29 | LOG FATAL '--controller or --interactive must be specified.' 30 | fi 31 | } > "${TMPDIR}/ninestream_init.txt" 32 | NINESTREAM_INIT="${TMPDIR}/ninestream_init.txt" \ 33 | php "${IMOS_BIN}/library/ninestream.php" 34 | exit "${PIPESTATUS[1]}" 35 | } 36 | 37 | main "$@" 38 | -------------------------------------------------------------------------------- /tool/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -u 4 | 5 | update_profile() { 6 | grep -v 'usr/local/imos/bin' '/etc/profile' > '/tmp/profile' 7 | echo 'if [[ ! "${PATH}" =~ (:|^)'\''/usr/local/imos/bin'\''(:|$) ]]; then export PATH="/usr/local/imos/bin:${PATH}"; fi' >> '/tmp/profile' 8 | cat '/tmp/profile' > '/etc/profile' 9 | } 10 | 11 | setup_for_mac() { 12 | echo '/usr/local/imos/bin' > '/etc/paths.d/imos-bin' 13 | update_profile 14 | } 15 | 16 | setup_for_linux() { 17 | update_profile 18 | } 19 | 20 | if [ "$(whoami)" != 'root' ]; then 21 | echo 'root privilege is required.' 22 | exit 1 23 | fi 24 | 25 | OS="$(uname -s)" 26 | case "${OS}" in 27 | Darwin) 28 | setup_for_mac 29 | ;; 30 | Linux) 31 | if ! type git >/dev/null 2>/dev/null; then 32 | if type apt-get >/dev/null 2>/dev/null; then 33 | apt-get -y install git 34 | elif type yum >/dev/null 2>/dev/null; then 35 | yum -y install git 36 | else 37 | echo 'git command is not found.' 38 | fi 39 | fi 40 | setup_for_linux 41 | ;; 42 | *) 43 | echo "Unsupported system: ${OS}" >&2 44 | exit 1 45 | ;; 46 | esac 47 | 48 | if [ -d '/usr/local/imos/bin' ]; then 49 | rm -r '/usr/local/imos/bin' 50 | fi 51 | mkdir -p '/usr/local/imos/bin' 52 | git clone --depth 1 'https://github.com/imos/bin' '/usr/local/imos/bin' 53 | 54 | -------------------------------------------------------------------------------- /imos-passgen: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-passgen is a password generator. 3 | # 4 | # imos-passgen generates a password based on a master password and a domain, and 5 | # copies a password to the clip board if it is not from SSH. 6 | # 7 | # Web interface version is available at: http://imoz.jp/password.html 8 | # 9 | # Usage: 10 | # imos-passgen 11 | 12 | hash() { 13 | local seed="${1}" 14 | local hash="$( 15 | tr -d '[:space:]' <<<"${seed}" | \ 16 | openssl md5 -binary | \ 17 | base64 | \ 18 | tr -d -c '[:alnum:]')" 19 | echo "${hash:0:8}" 20 | } 21 | 22 | main() { 23 | while :; do 24 | IFS=$'\n' read -r -sp 'Master password: ' password 25 | if [ ! -t 0 ]; then 26 | break 27 | fi 28 | echo 29 | read -p "Password hash is $(hash "${password}"). Right? [Y/n] " yesno 30 | case "${yesno}" in 31 | [Nn]*) continue;; 32 | *) break;; 33 | esac 34 | done 35 | 36 | read -p 'Domain: ' domain 37 | domain="$(echo "${domain}" | tr '[A-Z]' '[a-z]')" 38 | seed="${password}@${domain}" 39 | 40 | password="$(hash ${seed})" 41 | if [ "${SSH_TTY+set}" == '' ]; then 42 | if which pbcopy >/dev/null 2>/dev/null; then 43 | echo -n "${password}" | pbcopy 44 | echo 'Password was copied to your clipboard.' 45 | exit 0 46 | fi 47 | fi 48 | 49 | echo "${password}" 50 | } 51 | 52 | if [ "${#}" -eq 0 ]; then 53 | main 54 | else 55 | echo 'imos-passgen does not need arguments.' >&2 56 | exit 1 57 | fi 58 | -------------------------------------------------------------------------------- /imos-docker-run: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-archive archives/dearchives files. 3 | # 4 | # Usage: 5 | # imos-crypt [options] [input output] ... 6 | 7 | source "$(dirname "${BASH_SOURCE}")"/imos-variables || exit 1 8 | DEFINE_string container 'ephemeral' 'Container image to use.' 9 | DEFINE_string command 'su -' 'Command to run inside docker.' 10 | DEFINE_string volume '/:/host' 'Volumes to attach.' 11 | DEFINE_bool interactive true 'Enable interactive mode.' 12 | DEFINE_bool rebuild false 'Re-build a base image.' 13 | eval "${IMOSH_INIT}" 14 | 15 | rebuild() { 16 | CHECK [ "${FLAGS_container}" = 'ephemeral' ] 17 | cat << EOM > "${TMPDIR}/Dockerfile" 18 | FROM ubuntu:14.04 19 | ENV DEBIAN_FRONTEND noninteractive 20 | RUN apt-get update -qq && apt-get -y install git man 21 | RUN mkdir -p /usr/local/imos 22 | RUN echo $(date) 23 | RUN git clone https://github.com/imos/bin /usr/local/imos/bin 24 | RUN echo 'source /usr/local/imos/bin/data/bashrc' >'/root/.bash_aliases' 25 | EOM 26 | docker build --tag="${FLAGS_container}" "${TMPDIR}" 27 | } 28 | 29 | main() { 30 | if [ "$#" -eq 0 ]; then 31 | if (( FLAGS_rebuild )); then 32 | rebuild 33 | fi 34 | DOCKER_FLAGS=() 35 | if (( FLAGS_interactive )); then 36 | DOCKER_FLAGS+=(--interactive --rm --tty) 37 | fi 38 | if [ "${FLAGS_volume}" != '' ]; then 39 | DOCKER_FLAGS+=(--volume="${FLAGS_volume}") 40 | fi 41 | docker run "${DOCKER_FLAGS[@]}" "${FLAGS_container}" ${FLAGS_command} 42 | else 43 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 44 | fi 45 | } 46 | 47 | main "$@" 48 | -------------------------------------------------------------------------------- /imos-diskdiff: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-diskdiff compares disk volumes. 3 | # 4 | # imos-diskdiff compares disk volumes and list different files. 5 | # 6 | # Usage: 7 | # imos-diskdiff 8 | 9 | source "$(dirname "${BASH_SOURCE}")"/imos-variables || exit 1 10 | DEFINE_string source '/Volumes/Ditto/' 'Source volume.' 11 | DEFINE_string target '/' 'Target volume.' 12 | eval "${IMOSH_INIT}" 13 | 14 | EXCLUDES=( 15 | '.dbfseventsd' 16 | '.DocumentRevisions-V100/' 17 | '.fseventsd/' 18 | '.Spotlight-V100/' 19 | '.Trashes/' 20 | '.VolumeIcon.icns' 21 | 'dev/' 22 | 'Library/Application Support/com.bombich.ccc/' 23 | 'net/' 24 | 'private/tmp/' 25 | 'private/var/folders/' 26 | 'private/var/log/' 27 | 'private/var/root/' 28 | 'private/var/run/' 29 | 'private/var/spool/cups/' 30 | 'private/var/spool/postfix/' 31 | 'private/var/tmp/' 32 | 'System/Library/Caches/' 33 | 'System/Library/CoreServices/' 34 | 'tmp/' 35 | 'Users/*/.Trash/' 36 | 'Users/*/Library/Application Support/com.bombich.ccc/' 37 | 'Users/*/Library/Saved Application State/' 38 | 'Volumes/' 39 | ) 40 | 41 | check() { 42 | if [ "$(whoami)" != 'root' ]; then 43 | LOG FATAL 'root privilege is required' 44 | fi 45 | } 46 | 47 | call_rsync() { 48 | local options=(rsync -av --delete --dry-run) 49 | if [ "${#EXCLUDES[@]}" -ne 0 ]; then 50 | for exclude in "${EXCLUDES[@]}"; do 51 | options+=(--exclude="${exclude}") 52 | done 53 | fi 54 | "${options[@]}" "${FLAGS_source}" "${FLAGS_target}" 55 | } 56 | 57 | main() { 58 | check 59 | call_rsync 60 | } 61 | 62 | main 63 | -------------------------------------------------------------------------------- /test/imos-package-download_test.sh: -------------------------------------------------------------------------------- 1 | IMOSH_TESTING=1 2 | source "$(dirname "${BASH_SOURCE}")"/../imos-package || exit 1 3 | 4 | curl() { 5 | LOG INFO "Args: $*" 6 | local ARGS_silent=0 ARGS_fail=0 ARGS_output=0 7 | eval "${IMOSH_PARSE_ARGUMENTS}" 8 | 9 | if [ "$#" -eq 2 ]; then 10 | ASSERT_EQ 1 "${ARGS_fail}" 11 | ASSERT_EQ 1 "${ARGS_output}" 12 | case "${2}" in 13 | 'https://s3-ap-northeast-1.amazonaws.com/imos-package/ephemeral/b026324c6904b2a9cb4b88d6d61c81d1') 14 | sub::println '1' >"${1}";; 15 | 'https://s3-ap-northeast-1.amazonaws.com/imos-package/ephemeral/26ab0db90d72e28ad0ba1e22ee510510') 16 | sub::println '2' >"${1}";; 17 | 'https://s3-ap-northeast-1.amazonaws.com/imos-package/ephemeral/6d7fce9fee471194aa8b5b6e47267f03') 18 | sub::println '3' >"${1}";; 19 | 'https://s3-ap-northeast-1.amazonaws.com/imos-package/ephemeral/9518ab157cbf0ecdee5048934bfb878a') 20 | { 21 | sub::println 'b026324c6904b2a9cb4b88d6d61c81d1' 22 | sub::println '26ab0db90d72e28ad0ba1e22ee510510' 23 | sub::println '6d7fce9fee471194aa8b5b6e47267f03' 24 | } >"${1}";; 25 | *) 26 | return 22;; 27 | esac 28 | else 29 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 30 | fi 31 | } 32 | 33 | run() { 34 | imos-package::download "$@" 35 | } 36 | 37 | test::imos-package::download() { 38 | FLAGS_fragments_directory="${TMPDIR}/fragments" 39 | FLAGS_output="${TMPDIR}/output" 40 | 41 | run '9518ab157cbf0ecdee5048934bfb878a' 42 | EXPECT_EQ "$(sub::println '1'; sub::println '2'; sub::println '3')" \ 43 | "$(cat "${FLAGS_output}")" 44 | } 45 | -------------------------------------------------------------------------------- /test/imos-install_test.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | bash "$(dirname "${BASH_SOURCE}")"/imos-install.sh "$@" 3 | } 4 | 5 | test::imos-install::darwin() { 6 | export IMOS_ROOT="${TMPDIR}/root" 7 | mkdir -p "${IMOS_ROOT}" 8 | mkdir -p "${IMOS_ROOT}/Volumes/Arceus" 9 | mkdir -p "${IMOS_ROOT}/Users/Guest/Desktop/test" 10 | mkdir -p "${IMOS_ROOT}/Users/foo/Desktop/test" 11 | touch "${IMOS_ROOT}/Users/foo/Desktop/.localized" 12 | mkdir -p "${IMOS_ROOT}/Users/foo/Library/Caches/test" 13 | mkdir -p "${IMOS_ROOT}/Users/bar" 14 | mkdir -p "${IMOS_ROOT}/Library/Caches/test" 15 | mkdir -p "${IMOS_ROOT}/var/vm" 16 | mkdir -p "${IMOS_ROOT}/Library/LaunchDaemons" 17 | UNAME=Darwin run 18 | EXPECT_EQ "${IMOS_ROOT}/Volumes/Arceus" \ 19 | "$(readlink "${IMOS_ROOT}/storage")" 20 | EXPECT_EQ "${IMOS_ROOT}/storage/Users/foo/Desktop" \ 21 | "$(readlink "${IMOS_ROOT}/Users/foo/Desktop")" 22 | EXPECT_TRUE [ -d "${IMOS_ROOT}/Users/foo/Desktop/test" ] 23 | EXPECT_TRUE [ -f "${IMOS_ROOT}/Users/foo/Desktop/.localized" ] 24 | EXPECT_EQ "${IMOS_ROOT}/storage/Users/foo/Downloads" \ 25 | "$(readlink "${IMOS_ROOT}/Users/foo/Downloads")" 26 | EXPECT_EQ "${IMOS_ROOT}/storage/cache/Users/foo/Library/Caches" \ 27 | "$(readlink "${IMOS_ROOT}/Users/foo/Library/Caches")" 28 | EXPECT_EQ "${IMOS_ROOT}/storage/Users/foo/.bash_history" \ 29 | "$(readlink "${IMOS_ROOT}/Users/foo/.bash_history")" 30 | EXPECT_EQ "${IMOS_ROOT}/storage/cache/Library/Caches" \ 31 | "$(readlink "${IMOS_ROOT}/Library/Caches")" 32 | # EXPECT_EQ "${IMOS_ROOT}/storage/cache/var/vm" \ 33 | # "$(readlink "${IMOS_ROOT}/var/vm")" 34 | EXPECT_TRUE \ 35 | [ -f "${IMOS_ROOT}/Library/LaunchDaemons/jp.imoz.imos-start.plist" ] 36 | } 37 | -------------------------------------------------------------------------------- /test/imofs_test.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | bash "$(dirname "${BASH_SOURCE}")"/imofs.sh \ 3 | --target_directory="${TARGET}" \ 4 | --backup_directory="${BACKUP}" \ 5 | --source_directory="${SOURCE}" \ 6 | --alsologtostderr="${FLAGS_alsologtostderr}" \ 7 | --logtostderr="${FLAGS_logtostderr}" \ 8 | "$@" 9 | } 10 | 11 | test::imofs() { 12 | local TARGET="${TMPDIR}/imofs" 13 | local BACKUP="${TMPDIR}/imofs/backup" 14 | local SOURCE="${TMPDIR}/imofs/repository" 15 | 16 | mkdir -p "${TARGET}/etc" 17 | echo 'aaa' > "${TARGET}/etc/aaa" 18 | echo 'bbb' > "${TARGET}/etc/bbb" 19 | echo 'ccc' > "${TARGET}/etc/ccc" 20 | 21 | run backup 22 | 23 | EXPECT_EQ 'aaa' "$(cat "${BACKUP}/etc/aaa")" 24 | EXPECT_EQ 'bbb' "$(cat "${BACKUP}/etc/bbb")" 25 | EXPECT_EQ 'ccc' "$(cat "${BACKUP}/etc/ccc")" 26 | EXPECT_EQ 'mysqld' "$(cat "${BACKUP}/imofs/services")" 27 | 28 | echo 'BBBBB' > "${TARGET}/etc/bbb" 29 | rm "${TARGET}/etc/ccc" 30 | 31 | run restore 32 | 33 | EXPECT_EQ 'aaa' "$(cat "${TARGET}/etc/aaa")" 34 | EXPECT_EQ 'bbb' "$(cat "${TARGET}/etc/bbb")" 35 | EXPECT_EQ 'ccc' "$(cat "${TARGET}/etc/ccc")" 36 | 37 | echo 'BBBBB' > "${TARGET}/etc/bbb" 38 | rm "${TARGET}/etc/ccc" 39 | 40 | run backup 41 | 42 | EXPECT_EQ 'aaa' "$(cat "${BACKUP}/etc/aaa")" 43 | EXPECT_EQ 'BBBBB' "$(cat "${BACKUP}/etc/bbb")" 44 | EXPECT_FALSE [ -f "${BACKUP}/etc/ccc" ] 45 | EXPECT_EQ 'mysqld' "$(cat "${BACKUP}/imofs/services")" 46 | 47 | mkdir -p "${SOURCE}/etc" 48 | echo 'AAAAA' > "${SOURCE}/etc/aaa" 49 | { echo '#!/bin/bash'; echo 'touch etc/foo'; } > "${SOURCE}/deploy" 50 | 51 | run deploy 52 | 53 | EXPECT_EQ 'AAAAA' "$(cat "${TARGET}/etc/aaa")" 54 | EXPECT_EQ 'BBBBB' "$(cat "${TARGET}/etc/bbb")" 55 | EXPECT_FALSE [ -f "${TARGET}/etc/ccc" ] 56 | EXPECT_TRUE [ -f "${TARGET}/etc/foo" ] 57 | } 58 | -------------------------------------------------------------------------------- /tool/update-readme: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Update README.md. 3 | 4 | source "$(dirname "${BASH_SOURCE}")/../imosh" || exit 1 5 | 6 | DEFINE_string output "$(dirname "${BASH_SOURCE}")/../README.md" \ 7 | 'README file to output.' 8 | 9 | eval "${IMOSH_INIT}" 10 | 11 | 12 | init_readme() { 13 | cat << 'EOM' 14 | # imos-bin 15 | bin directory used by imos. 16 | 17 | This repository is tested on drone.io (https://drone.io/github.com/imos/bin). 18 | 19 | ## How to Install/Update 20 | Install this repository to /usr/local/imos/bin and configure ${PATH}: 21 | ```sh 22 | curl https://raw.githubusercontent.com/imos/bin/master/tool/setup | sudo bash 23 | ``` 24 | 25 | Note that you can run the command multiple times safely. 26 | 27 | ## Commands 28 | EOM 29 | } 30 | 31 | add_readme() { 32 | local file="${1}" 33 | 34 | echo '-----' 35 | echo "### ${file}" 36 | if grep 'eval "\${IMOSH_INIT}"$' "${file}" >/dev/null; then 37 | "./${file}" --help_format=markdown 2>&1 | while IFS= read -r line; do 38 | case "${line}" in 39 | '#'*) sub::println; sub::println "###${line}";; 40 | *) sub::println "${line}";; 41 | esac 42 | done 43 | else 44 | sub::usage --format=markdown --notitle --markdown_heading='##' "${file}" 45 | fi 46 | echo 47 | } 48 | 49 | main() { 50 | local targets=() 51 | local target='' 52 | init_readme 53 | for target in *; do 54 | if [ -x "${target}" -a ! -d "${target}" ]; then 55 | targets+=("${target}") 56 | fi 57 | done 58 | local readme_all='' 59 | for target in "${targets[@]}"; do 60 | local readme="$(add_readme "${target}")" 61 | local lines=() 62 | func::trim readme 63 | func::explode lines $'\n' "${readme}" 64 | if [ "${#lines[*]}" -gt 2 ]; then 65 | echo "* [${target}](#${target}) ... ${lines[2]}" 66 | fi 67 | readme_all+="${readme}"$'\n\n' 68 | done 69 | echo 70 | echo "${readme_all}" 71 | } 72 | 73 | main > "${FLAGS_output}" 74 | -------------------------------------------------------------------------------- /imos-start: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-start is a start-up script. 3 | # 4 | # imos-start is a script that should run just after boot programs. This script 5 | # requires the root privilege. 6 | # 7 | # Usage: 8 | # imos-start 9 | 10 | source "$(dirname "${BASH_SOURCE}")"/imos-variables || exit 1 11 | eval "${IMOSH_INIT}" 12 | 13 | POKEMON="$(imos-pokemon --nocache)" 14 | 15 | check() { 16 | if [ "$(whoami)" != 'root' ]; then 17 | LOG FATAL 'root privilege is required' 18 | fi 19 | } 20 | 21 | start::darwin() { 22 | LOG INFO "Initializing host names with ${POKEMON}..." 23 | scutil --set ComputerName "${POKEMON}" 24 | scutil --set LocalHostName "${POKEMON}" 25 | scutil --set HostName "${POKEMON}.local" 26 | 27 | if [ "${POKEMON}" != 'Ditto' ]; then 28 | LOG INFO 'Installing pictures...' 29 | local users=() 30 | func::imos_get_users users 31 | local user='' 32 | if [ "${#users[*]}" -ne 0 ]; then 33 | for user in "${users[@]}"; do 34 | if [ ! -L "${IMOS_ROOT}/Users/${user}/Pictures" ]; then 35 | LOG INFO "Replacing ${user}'s Pictures directory..." 36 | rm -R -f "${IMOS_ROOT}/Users/${user}/Pictures" 37 | ln -s "${IMOS_ROOT}/Users/${user}/${POKEMON}/Pictures" \ 38 | "${IMOS_ROOT}/Users/${user}/Pictures" 39 | fi 40 | done 41 | fi 42 | 43 | local variance_directory="$( 44 | dirname "${BASH_SOURCE}")/data/variance/${POKEMON}" 45 | if [ -d "${variance_directory}" ]; then 46 | LOG INFO 'Installing variances...' 47 | pushd "${variance_directory}" > '/dev/null' 48 | for file in *.tar.aes256; do 49 | LOG INFO "Deploying ${file}..." 50 | func::str_replace file '.aes256' '' 51 | imos-crypt --decrypt --installed_password \ 52 | "${file}.aes256" "${TMPDIR}/${file}" 53 | pushd "${IMOS_ROOT}/" > '/dev/null' 54 | tar xvf "${TMPDIR}/${file}" 55 | popd > '/dev/null' 56 | done 57 | popd > '/dev/null' 58 | fi 59 | fi 60 | } 61 | 62 | start::linux() { 63 | # There is nothing to do for linux. 64 | : 65 | } 66 | 67 | main() { 68 | check 69 | "start::$(sub::strtolower "${UNAME}")" 70 | } 71 | 72 | if [ "${#}" -eq 0 ]; then 73 | main 74 | else 75 | LOG FATAL "imos-start requires no arguments." 76 | fi 77 | -------------------------------------------------------------------------------- /library/imos-lambda.php: -------------------------------------------------------------------------------- 1 | 'default', 34 | 'region' => $_ENV['IMOS_LAMBDA_REGION'] ?: 'ap-northeast-1', 35 | 'version' => '2015-03-31', 36 | )); 37 | 38 | $result = $client->Invoke([ 39 | 'FunctionName' => FUNCTION_NAME, 40 | 'Payload' => json_encode($request), 41 | 'Version' => '2015-03-31']); 42 | 43 | $data = json_decode($result['Payload']->getContents(), true); 44 | 45 | if (isset($data['error']) && !is_null($data['error'])) { 46 | fwrite(STDERR, json_encode($data['error']) . "\n"); 47 | } 48 | fwrite(STDOUT, $data['stdout']); 49 | fwrite(STDERR, $data['stderr']); 50 | if (isset($data['code']) && $data['code'] != 0) { 51 | fwrite(STDERR, 'Return: ' . $data['code'] . "\n"); 52 | } 53 | if (isset($data['signal']) && $data['signal'] != 0) { 54 | fwrite(STDERR, 'Signal: ' . $data['signal'] . "\n"); 55 | } 56 | if (isset($data['output'])) { 57 | fwrite(STDERR, 'Output: ' . $data['output'] . "\n"); 58 | } 59 | if (isset($data['elapsed_time'])) { 60 | fwrite(STDERR, "Elapsed time: " . $data['elapsed_time'] . " ms\n"); 61 | 62 | $info = $client->GetFunctionConfiguration(['FunctionName' => FUNCTION_NAME]); 63 | $request_price = 0.000002; // Price / request. 64 | $base_price = 0.00001667; // 1GB RAM / sec. 65 | $usdjpy = 119.6; // 1 USD / JPY. 66 | $price = 67 | ($info['MemorySize'] / 1024 * $base_price / 10 * 68 | ceil($data['elapsed_time'] / 100) + $request_price) * $usdjpy; 69 | fprintf(STDERR, "Price: %.4f JPY\n", $price); 70 | } 71 | -------------------------------------------------------------------------------- /data/ninelet.aes256: -------------------------------------------------------------------------------- 1 | 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 36 | 55SxQTRU1Us2cgdWzioYpA== 37 | -------------------------------------------------------------------------------- /imos-stat: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-stat is a command for stat's compatibility. 3 | # 4 | # imos-stat displays information about a file. GNU's stat and BSD's stat use 5 | # different format, so imos-stat converts GNU's format to BSD's format if 6 | # necessary. 7 | # 8 | # Usage: 9 | # imos-stat --format= 10 | 11 | source "$(dirname "${BASH_SOURCE}")"/imosh || exit 1 12 | DEFINE_bool use_native_uname true 'Use native UNAME instead.' 13 | DEFINE_string format '' 'Format.' 14 | eval "${IMOSH_INIT}" 15 | 16 | if (( FLAGS_use_native_uname )); then 17 | UNAME="$(uname)" 18 | fi 19 | 20 | replace() { 21 | func::str_replace format "$@" 22 | } 23 | 24 | stat-format() { 25 | if [ "${#}" -eq 2 ]; then 26 | local format="${1}" 27 | local os="${2}" 28 | func::str_replace format '%' $'\x02' 29 | case "${os}" in 30 | Darwin) 31 | replace $'\x02a' '%p' # access rights in octal 32 | replace $'\x02A' '%Sp' # access rights in human readable form 33 | replace $'\x02b' '%b' # number of blocks allocated 34 | replace $'\x02B' '%k' # the size in bytes of each block reported by %b 35 | replace $'\x02g' '%g' # group ID of owner 36 | replace $'\x02G' '%Sg' # group name of owner 37 | replace $'\x02i' '%i' # inode number 38 | replace $'\x02s' '%z' # total size, in bytes 39 | replace $'\x02U' '%Su' # user name of owner 40 | replace $'\x02u' '%u' # user ID of owner 41 | replace $'\x02W' '%B' # time of file birth, seconds since Epoch 42 | replace $'\x02w' '%SB' # time of file birth, human-readable 43 | replace $'\x02X' '%a' # time of last access, seconds since Epoch 44 | replace $'\x02x' '%Sa' # time of last access, human-readable 45 | replace $'\x02Y' '%m' # time of last modification, seconds since Epoch 46 | replace $'\x02y' '%Sm' # time of last modification, human-readable 47 | replace $'\x02Z' '%c' # time of last change, seconds since Epoch 48 | replace $'\x02z' '%Sc' # time of last change, human-readable 49 | ;; 50 | esac 51 | replace $'\x02' '%' 52 | sub::println "${format}" 53 | else 54 | LOG ERROR "Wrong number of arguments: $#" 55 | fi 56 | } 57 | 58 | main() { 59 | local format="$(stat-format "${FLAGS_format}" "${UNAME}")" 60 | LOG INFO "Format: ${format}" 61 | case "${UNAME}" in 62 | Darwin) 63 | stat -f "${format}" "$@";; 64 | Linux) 65 | stat --format "${format}" "$@";; 66 | *) 67 | LOG FATAL "Unsupported UNAME: ${UNAME}";; 68 | esac 69 | } 70 | 71 | main "$@" 72 | -------------------------------------------------------------------------------- /ninelet: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ninelet runs a command on a ninelet. 3 | # 4 | # Usage: 5 | # ninelet [options] args... 6 | 7 | source "$(dirname "${BASH_SOURCE}")"/imos-variables || exit 1 8 | DEFINE_bool debug false 'Enables debug mode.' 9 | DEFINE_bool tty false 'Enables tty.' 10 | DEFINE_string ninelet '' 'Ninelet target.' 11 | DEFINE_string ninelet_list \ 12 | 'http://imoz.jp/data/ninecluster/ninecluster.txt' \ 13 | 'URL containing a list of ninelet ports.' 14 | IMOSH_PREDICATE=1 eval "${IMOSH_INIT}" 15 | 16 | fetch_ninelet() { 17 | if [ "${FLAGS_ninelet_list:0:7}" == 'http://' -o \ 18 | "${FLAGS_ninelet_list:0:8}" == 'https://' ]; then 19 | curl --silent "${FLAGS_ninelet_list}" 20 | else 21 | cat "${FLAGS_ninelet_list}" 22 | fi 23 | } 24 | 25 | run() { 26 | LOG INFO "Command: $*" 27 | imos-crypt --decrypt --installed_password \ 28 | "${IMOS_BIN}/data/ninelet.aes256" "${TMPDIR}/id_rsa" 29 | chmod 0700 "${TMPDIR}/id_rsa" 30 | local args=(ssh -i "${TMPDIR}/id_rsa" 31 | -o 'StrictHostKeyChecking no' 32 | -o 'UserKnownHostsFile /dev/null' 33 | -o 'ConnectTimeout 1') 34 | if (( FLAGS_tty )); then 35 | args+=(-t -t) 36 | fi 37 | local timeout="$(( SECONDS + 10 ))" 38 | local targets=() 39 | local host='' port='' 40 | if [ "${FLAGS_ninelet}" == '' ]; then 41 | while IFS=: read host port; do 42 | targets+=("${RANDOM}:-p ${port} ninelet@${host}") 43 | done < <(fetch_ninelet) 44 | func::sort targets 45 | else 46 | host="${FLAGS_ninelet%:*}" 47 | port="${FLAGS_ninelet##*:}" 48 | if [ "${port}" == "${FLAGS_ninelet}" ]; then 49 | port=2210 50 | fi 51 | targets+=("${RANDOM}:-p ${port} ninelet@${host}") 52 | fi 53 | local target='' 54 | for target in "${targets[@]}"; do 55 | target="${target#*:}" 56 | local status=0 57 | "${args[@]}" ${target} "$@" || status="$?" 58 | LOG INFO "Status: ${status}" 59 | if [ "${status}" -ne 255 ]; then 60 | exit "${status}" 61 | fi 62 | if (( timeout < SECONDS )); then 63 | exit 255 64 | fi 65 | done 66 | LOG FATAL 'No available ninelet server is found.' 67 | } 68 | 69 | main() { 70 | local command='' 71 | for arg in "$@"; do 72 | func::escapeshellarg arg 73 | command+=" ${arg}" 74 | done 75 | 76 | if [ "${command}" == '' ]; then 77 | run 78 | elif (( FLAGS_debug )); then 79 | run "${command}" 80 | else 81 | command="echo BEGIN >&2; ${command}" 82 | mkfifo "${TMPDIR}/stderr" 83 | grep --line-buffered -A 1000000000 BEGIN <"${TMPDIR}/stderr" | \ 84 | tail -n +2 >&2 & 85 | run "${command}" 2>"${TMPDIR}/stderr" 86 | fi 87 | } 88 | 89 | main "$@" 90 | -------------------------------------------------------------------------------- /imos-bashrc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-bashrc is a bash init script. 3 | # 4 | # imos-bashrc initializes PS1 and sources init files. 5 | # 6 | # Usage: 7 | # source /usr/local/imos/bin/imos-bashrc 8 | 9 | # Exit immediatelly if PS1 is not set. 10 | if [ "${PS1}" = '' ]; then return 2>&- || exit; fi 11 | 12 | source "$(dirname "${BASH_SOURCE}")"/imos-variables 13 | source "$(dirname "${BASH_SOURCE}")"/library/git-prompt.sh 14 | 15 | __bashrc::show_extra_ps1() { 16 | case "$(sub::strtolower "${POKEMON}")" in 17 | chikorita) 18 | if ! klist -s >/dev/null 2>/dev/null; then 19 | echo kinit 20 | fi 21 | ;; 22 | esac 23 | } 24 | 25 | __bashrc::set_ps1() { 26 | local git='\[\033[36m\]$(__git_ps1 "%s:" 2>/dev/null)\[\033[0m\]' 27 | local dir='\[\033[1;35m\]\w\[\033[0m\]' 28 | local brace_open='\[\033[34m\][\[\033[0m\]' 29 | local brace_close='\[\033[34m\]]\[\033[0m\]' 30 | local block1="${brace_open}${git}${dir}${brace_close}" 31 | 32 | local date='\[\033[38;5;238m\](${USER}@' 33 | if [ "${POKEMON-}" = '' ]; then 34 | date+="$(hostname -s) " 35 | else 36 | date+="${POKEMON} " 37 | fi 38 | date+='$(TZ=Asia/Tokyo date +"%Y-%m-%d %H:%M:%S %Z") ' 39 | date+='/ $(TZ=US/Pacific date +"%H:%M:%S %Z")' 40 | date+=')\[\033[0m\]' 41 | local block2="${date}" 42 | 43 | local extra='\[\033[31m\]$(__bashrc::show_extra_ps1 2>/dev/null)\[\033[0m\]' 44 | local block3="${extra}" 45 | 46 | local ps1="${block1} ${block2}\[\033[0m\] ${block3}\n" 47 | ps1+='$(if [ "${USER-}" = "root" ]; then echo "#"; else echo "\$"; fi) ' 48 | export PS1="${ps1}" 49 | } 50 | 51 | __bashrc::init() { 52 | export USER="$(whoami)" 53 | __bashrc::set_ps1 54 | local target="$(dirname "${BASH_SOURCE}")/library/bashrc-$(sub::strtolower "${UNAME}").sh" 55 | if [ -f "${target}" ]; then 56 | source "${target}" 57 | fi 58 | shopt -u histappend 59 | export HISTSIZE=999999 60 | export HISTCONTROL=ignorespace 61 | } 62 | 63 | __bashrc::init 64 | 65 | export GOPATH="${HOME}/go" 66 | export PATH="${PATH}:/usr/local/opt/go/libexec/bin:${GOPATH}/bin:${HOME}/.cargo/bin" 67 | 68 | if [ -f ~/google-cloud-sdk/path.bash.inc ]; then 69 | source ~/google-cloud-sdk/path.bash.inc 70 | fi 71 | if [ -f ~/google-cloud-sdk/completion.bash.inc ]; then 72 | source ~/google-cloud-sdk/completion.bash.inc 73 | fi 74 | for bashrc in /usr/local/imos/bashrc/*.sh; do 75 | if [ -f "${bashrc}" ]; then 76 | source "$bashrc" 77 | fi 78 | done 79 | if [ -f ~/.cargo/env ]; then 80 | source ~/.cargo/env 81 | fi 82 | 83 | export PATH="${HOME}/.pyenv/shims:${PATH}" 84 | export PATH="${HOME}/bin:${PATH}" 85 | 86 | if [ -d /usr/local/rustc/src ]; then 87 | export RUST_SRC_PATH=/usr/local/rustc/src 88 | fi 89 | -------------------------------------------------------------------------------- /imos-variables: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-variables is a source file initializing variables. 3 | # 4 | # imos-variables initializes variables. Thus, imos-variables must be called 5 | # using the source built-in command. This command prepends the bin directory 6 | # where imos-variables exists to the PATH environment vairable. 7 | # 8 | # Usage: 9 | # source imos-variables 10 | # 11 | # Variables: 12 | # - IMOS_STORAGE 13 | # IMOS_STORAGE is a persistent directory's absolute path. Its default 14 | # value is /Volumes/Arceus in Mac OSX and /storage in other operating 15 | # systems. 16 | # - IMOS_RESOURCE 17 | # IMOS_RESOURCE is a directory containing resource files for imos scripts. 18 | # Its default value is an absolute path that is ../resource from the 19 | # directory where the imos-variables command exists. 20 | 21 | # Return if imos-variables is already loaded. 22 | if [ "${__IMOS_VARIABLES_IS_LOADED+loaded}" = 'loaded' ]; then return; fi 23 | __IMOS_VARIABLES_IS_LOADED=1 24 | source "$(dirname "${BASH_SOURCE}")"/imosh || exit 1 25 | 26 | IMOS_BIN="$(cd "$(dirname "${BASH_SOURCE}")"; pwd)" 27 | export PATH="${IMOS_BIN}:${PATH}" 28 | export POKEMON="$(imos-pokemon)" 29 | 30 | if ! sub::isset IMOS_ROOT; then 31 | IMOS_ROOT='' 32 | fi 33 | 34 | if ! sub::isset IMOS_STORAGE; then 35 | IMOS_STORAGE="${IMOS_ROOT}/storage" 36 | fi 37 | 38 | if ! sub::isset IMOS_RESOURCE; then 39 | if [ -d "${IMOS_BIN}/../resource" ]; then 40 | IMOS_RESOURCE="$(cd "${IMOS_BIN}/../resource"; pwd)" 41 | fi 42 | fi 43 | 44 | if ! sub::isset IMOS_USERS; then 45 | case "${UNAME}" in 46 | Darwin) IMOS_USERS="${IMOS_ROOT}/Users";; 47 | Linux) IMOS_USERS="${IMOS_ROOT}/home";; 48 | *) IMOS_USERS="${IMOS_ROOT}/home";; 49 | esac 50 | fi 51 | 52 | func::imos_get_users() { 53 | if [ "${#}" -eq 1 ]; then 54 | local __imos_get_users_variable="${1}" 55 | local __imos_get_users_users=() 56 | case "${UNAME}" in 57 | Darwin) 58 | pushd "${IMOS_ROOT}/Users" > '/dev/null' 59 | for user in *; do 60 | case "${user}" in 61 | Guest|Shared|provision|ninetan) :;; 62 | *) 63 | if [ -d "${user}" ]; then 64 | __imos_get_users_users+=("${user}") 65 | fi 66 | ;; 67 | esac 68 | done 69 | popd > '/dev/null' 70 | ;; 71 | *) 72 | pushd "${IMOS_ROOT}/home" > '/dev/null' 73 | for user in *; do 74 | if [ -d "${user}" ]; then 75 | __imos_get_users_users+=("${user}") 76 | fi 77 | done 78 | popd > '/dev/null' 79 | ;; 80 | esac 81 | if [ "${#__imos_get_users_users}" -eq 0 ]; then 82 | eval "${__imos_get_users_variable}=()" 83 | else 84 | eval "${__imos_get_users_variable}=(\"\${__imos_get_users_users[@]}\")" 85 | fi 86 | else 87 | LOG ERROR "Wrong number of arguments: ${#}" 88 | fi 89 | } 90 | -------------------------------------------------------------------------------- /data/variance/Psyduck/test.tar.aes256: -------------------------------------------------------------------------------- 1 | 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 44 | lcHqOmhMXmreofhmYHQFJQ== 45 | -------------------------------------------------------------------------------- /imos-crypt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-crypt encrypts/decrypts files. 3 | # 4 | # imos-crypt encrypts/decrypts files using an embedded password by default. 5 | # The embedded password is encrypted with a master password that should be 6 | # given by imos. The installed_password flag makes imos-crypt use the 7 | # installed password, which should be installed by imos-start. The password 8 | # flag forces the script to use the password given by the flag. This script 9 | # uses AES-256-CBC to encrypt/decrypt. 10 | # 11 | # Usage: 12 | # imos-crypt [options] [input output] ... 13 | 14 | source "$(dirname "${BASH_SOURCE}")"/imos-variables || exit 1 15 | DEFINE_string password '' 'Password to use instead.' 16 | DEFINE_bool installed_password false 'Use the installed password instead.' 17 | DEFINE_bool encrypt false 'Encrypt files.' 18 | DEFINE_bool decrypt false 'Decrypt files.' 19 | eval "${IMOSH_INIT}" 20 | 21 | get_password() { 22 | if [ "${FLAGS_password}" != '' ]; then 23 | sub::println "${FLAGS_password}" 24 | return 25 | fi 26 | if (( FLAGS_installed_password )); then 27 | local password_file="${IMOS_ROOT}/usr/local/imos/config/installed-password" 28 | mkdir -p "$(dirname "${password_file}")" 29 | if [ ! -f "${password_file}" ]; then 30 | if [ "$(whoami)" != 'root' ]; then 31 | LOG FATAL 'root privilege is required to install a password.' 32 | fi 33 | imos-crypt --decrypt \ 34 | "$(dirname "${BASH_SOURCE}")/data/installed-password.aes256" \ 35 | "${password_file}" 36 | chmod 600 "${password_file}" 37 | fi 38 | if [ -r "${password_file}" ]; then 39 | sub::println "$(cat "${password_file}")" 40 | else 41 | sub::println "$(sudo cat "${password_file}")" 42 | fi 43 | return 44 | fi 45 | local seed=() password='' 46 | while :; do 47 | seed=( 48 | 'U2FsdGVkX19rXUkSvOYoQ3HKqa4Xi7DXp2A/Fj4MxOeIU3CPuU10sDNAStBH86Sp' 49 | 'T03CKk8WxUNuNrDqqO1p7qhyS2uMKZBgWCBljIk+wiGGpof6qo0o6wHZTIb1PSzt' 50 | 'txeMgaVLKiHSb0WmPOlmFqiolTBBkE3sVmniwJbSw6G0FKiZHNPkuSENJhUN8Z74' 51 | 'tcjrCxGvGpB3QhgxbvU55Q==') 52 | password="$( 53 | for line in "${seed[@]}"; do echo "${line}"; done | \ 54 | openssl enc -aes-256-cbc -d -base64 || true)" 55 | if [ "${password:0:4}" = 'mT25' ]; then break; fi 56 | LOG ERROR 'Wrong password.' 57 | done 58 | sub::println "${password}" 59 | } 60 | 61 | main() { 62 | if (( ! FLAGS_encrypt && ! FLAGS_decrypt )); then 63 | LOG FATAL 'Either of the encrypt flag or the decrypt flag is required.' 64 | fi 65 | if (( "${#}" % 2 != 0 )); then 66 | LOG FATAL 'The number of arguments must be an even number.' 67 | fi 68 | local password="$(get_password)" 69 | while (( "${#}" != 0 )); do 70 | local args=( 71 | openssl enc -aes-256-cbc -base64 -pass "pass:${password}" 72 | -in "${1}" -out "${2}") 73 | shift; shift 74 | if (( FLAGS_encrypt )); then 75 | "${args[@]}" 76 | elif (( FLAGS_decrypt )); then 77 | "${args[@]}" -d 78 | else 79 | LOG FATAL 'Nothing to do.' 80 | fi 81 | done 82 | } 83 | 84 | main "$@" 85 | -------------------------------------------------------------------------------- /imos-archive: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-archive archives/dearchives files. 3 | # 4 | # Usage: 5 | # imos-crypt [options] [input output] ... 6 | 7 | source "$(dirname "${BASH_SOURCE}")"/imos-variables || exit 1 8 | DEFINE_int file_size 0 'Maximum size of a generated archive in bytes.' 9 | DEFINE_bool pass_working_directory false \ 10 | "Pass a caller's working directory to the program." \ 11 | "Otherwise, a callee's working directory is under the package." 12 | DEFINE_string output '' 'Path to output the archive to.' 13 | DEFINE_string command '' 'Command to run.' 14 | DEFINE_bool extra_arguments true 'Append extra arguments.' 15 | IMOSH_PREDICATE=1 eval "${IMOSH_INIT}" 16 | 17 | create_tar() { 18 | if [ "$#" -eq 1 ]; then 19 | local package_directory="${TMPDIR}/package" 20 | mkdir -p "${package_directory}" 21 | ln -s "$(pwd)" "${package_directory}/bin" 22 | if [ "${FLAGS_command}" != '' ]; then 23 | local command_file="${package_directory}/COMMAND" 24 | { 25 | sub::println '#!/bin/bash' 26 | if (( ! FLAGS_pass_working_directory )); then 27 | sub::println 'cd "$(dirname "${BASH_SOURCE}")/bin"' 28 | fi 29 | if (( FLAGS_extra_arguments )); then 30 | sub::println "${FLAGS_command} \"\$@\"" 31 | else 32 | sub::println "${FLAGS_command}" 33 | fi 34 | } > "${command_file}" 35 | chmod +x "${command_file}" 36 | fi 37 | pushd "${package_directory}" >/dev/null 38 | local line='' 39 | local tar_out="${TMPDIR}/tar_out" 40 | local tar_log="${TMPDIR}/tar_log" 41 | mkfifo "${tar_out}" "${tar_log}" 42 | tar zcvfh - * >"${tar_out}" 2>"${tar_log}" & 43 | local tar_pid="$!" 44 | while IFS= read -r line; do 45 | LOG INFO "Archiving: ${line}" 46 | done <"${tar_log}" & 47 | local log_pid="$!" 48 | if (( FLAGS_file_size <= 0 )); then 49 | cat <"${tar_out}" >"${1}" 50 | else 51 | head -c "$(( FLAGS_file_size + 1 ))" <"${tar_out}" > "${1}" 52 | kill "${log_pid}" 2>/dev/null || true 53 | kill "${tar_pid}" 2>/dev/null || true 54 | local file_size="$(wc -c < "${1}")" 55 | if (( file_size > FLAGS_file_size )); then 56 | LOG FATAL "File size is too big: --file_size=${FLAGS_file_size}" 57 | fi 58 | fi 59 | popd >/dev/null 60 | else 61 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 62 | fi 63 | } 64 | 65 | archive_tar() { 66 | if [ "$#" -eq 2 ]; then 67 | local command='#!/bin/bash 68 | set -e -u 69 | package_directory="${TMPDIR}/package.${RANDOM}.${RANDOM}.${RANDOM}.${RANDOM}.${RANDOM}/package" 70 | mkdir -p "${package_directory}" 71 | cat "${BASH_SOURCE}" | tail -n +NUM_OF_LINES | { 72 | pushd "${package_directory}" >/dev/null 73 | tar zxvf - >/dev/null 2>/dev/null 74 | popd >/dev/null 75 | } 76 | "${package_directory}/COMMAND" "$@" 77 | exit 0 78 | ' 79 | local command_lines=() 80 | func::explode command_lines $'\n' "${command}" 81 | func::str_replace command 'NUM_OF_LINES' "${#command_lines[*]}" 82 | { 83 | sub::print "${command}" 84 | cat "${1}" 85 | } > "${2}" 86 | chmod +x "${2}" 87 | else 88 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 89 | fi 90 | } 91 | 92 | main() { 93 | if [ "${FLAGS_output}" = '' ]; then 94 | LOG FATAL '--output flag msut be specified.' 95 | fi 96 | if [ "$#" -eq 0 ]; then 97 | if [ "${FLAGS_command}" = '' ]; then 98 | LOG FATAL '--command flag or arguments must be specified.' 99 | fi 100 | else 101 | if [ "${FLAGS_command}" != '' ]; then 102 | LOG FATAL 'Both of --command flag and arguments are specified.' 103 | fi 104 | local arguments=("$@") 105 | func::array_map arguments INPLACE func::escapeshellarg 106 | func::implode FLAGS_command ' ' arguments 107 | fi 108 | local tar_file="${TMPDIR}/package.tar" 109 | local sar_file="${TMPDIR}/package.sar" 110 | create_tar "${tar_file}" 111 | archive_tar "${tar_file}" "${sar_file}" 112 | cp "${sar_file}" "${FLAGS_output}" 113 | } 114 | 115 | main "$@" 116 | -------------------------------------------------------------------------------- /imos-pokemon: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-pokemon prints the current volume's Pokémon name/ID. 3 | # 4 | # For Mac OSX, this command uses a disk volume's name to determine Pokémon 5 | # ID. For linux OS, this command uses a primary IP address. 6 | # 7 | # Usage: 8 | # imos-pokemon [options] 9 | # 10 | # Files: 11 | # - /tmp/POKEMON_NAME 12 | # Cache file to store a Pokémon name. 13 | # - ./data/pokemon.txt 14 | # Mapping from Pokémon ID to Pokémon name. 15 | 16 | source "$(dirname "${BASH_SOURCE}")"/imosh || exit 1 17 | DEFINE_bool cache true 'Use cache.' 18 | DEFINE_string cache_file '/tmp/POKEMON_NAME' \ 19 | 'File to cache a Pokémon name' 20 | DEFINE_bool id false 'Show pokemon ID instead.' 21 | DEFINE_string pokemon_mapping_file \ 22 | "$(dirname "${BASH_SOURCE}")/data/pokemon.txt" \ 23 | 'File to map a Pokémon ID to a Pokémon name.' 24 | DEFINE_string pokemon_error_level 'ERROR' \ 25 | 'Error level used when resolution partially fails.' 26 | eval "${IMOSH_INIT}" 27 | 28 | pokemon::darwin::system_profiler() { 29 | sub::println \ 30 | "$(system_profiler -xml -detailLevel full SPSoftwareDataType | 31 | plutil -extract '0._items.0.boot_volume' xml1 -o - - | 32 | xpath '/plist/string/text()' 2>/dev/null)" 33 | } 34 | 35 | pokemon::darwin::diskutil() { 36 | local device="$(df / | grep /dev/ | awk '{ print $1 }')" 37 | sub::println \ 38 | "$(diskutil info -plist "${device}" | 39 | plutil -extract VolumeName xml1 -o - - | 40 | xpath '/plist/string/text()' 2>/dev/null)" 41 | } 42 | 43 | pokemon::darwin::volume() { 44 | cd /Volumes 45 | for disk in * Pikachu; do 46 | if [ "$(readlink "${disk}")" = '/' ]; then 47 | sub::println "${disk}" 48 | return 49 | fi 50 | done 51 | return 1 52 | } 53 | 54 | pokemon::darwin() { 55 | local commands=( 56 | pokemon::darwin::system_profiler 57 | pokemon::darwin::diskutil 58 | pokemon::darwin::volume 59 | ) 60 | 61 | for command in "${commands[@]}"; do 62 | local name="$("${command}")" 63 | if [ "${name}" = '' ]; then 64 | LOG ERROR "${command} failed and returned an empty value." 65 | else 66 | LOG INFO "${command} infers ${name}" 67 | sub::println "${name}" 68 | fi 69 | done >"${TMPDIR}/commands.txt" 70 | if [ "$(wc -l < "${TMPDIR}/commands.txt")" -ne 3 -o \ 71 | "$(uniq "${TMPDIR}/commands.txt" | wc -l)" -ne 1 ]; then 72 | LOG "${FLAGS_pokemon_error_level}" 'some command failed.' 73 | head -n 1 "${TMPDIR}/commands.txt" 74 | return 75 | fi 76 | sort "${TMPDIR}/commands.txt" | uniq -c | sort -nr | \ 77 | awk '{ print $2 }' | head -n 1 78 | } 79 | 80 | pokemon::linux() { 81 | if sub::isset NINECLUSTER; then 82 | case "${NINECLUSTER}" in 83 | 'ninemaster') 84 | sub::println 'Dugtrio';; 85 | 'ninelet') 86 | sub::println 'Diglett';; 87 | *) 88 | LOG "${FLAGS_pokemon_error_level}" 'resolution failed.' 89 | sub::println 'Unown';; 90 | esac 91 | return 92 | fi 93 | case "$(hostname -i)" in 94 | '153.121.64.206') 95 | sub::println 'Dunsparce';; 96 | 172.*) 97 | # Docker container should be Unown. 98 | sub::println 'Unown';; 99 | *) 100 | case "$(hostname -s)" in 101 | 'oddish-imos') 102 | sub::println 'Oddish';; 103 | 'cyndaquil-imos') 104 | sub::println 'Cyndaquil';; 105 | *) 106 | LOG "${FLAGS_pokemon_error_level}" 'resolution failed.' 107 | sub::println 'Unown';; 108 | esac 109 | esac 110 | } 111 | 112 | if sub::isset IMOSH_TESTING; then 113 | POKEMON_NAME=Psyduck 114 | else 115 | : "${POKEMON_NAME:=}" 116 | if (( FLAGS_cache )) && [ -f "${FLAGS_cache_file}" ]; then 117 | func::file_get_contents POKEMON_NAME "${FLAGS_cache_file}" 118 | func::trim POKEMON_NAME 119 | fi 120 | if [ "${POKEMON_NAME}" = '' ]; then 121 | POKEMON_NAME="$(pokemon::"$(sub::strtolower "${UNAME}")")" 122 | if [ "${FLAGS_cache_file}" != '' ]; then 123 | if [ ! -f "${FLAGS_cache_file}" ]; then 124 | sub::println "${POKEMON_NAME}" > "${FLAGS_cache_file}" || true 125 | fi 126 | fi 127 | fi 128 | fi 129 | LOG INFO "pokemon name is ${POKEMON_NAME}" 130 | 131 | POKEMON_ID="$( 132 | grep "${POKEMON_NAME}\$" "${FLAGS_pokemon_mapping_file}" | 133 | awk '{ print $1 }')" 134 | LOG INFO "pokemon ID is ${POKEMON_ID}" 135 | 136 | if (( FLAGS_id )); then 137 | sub::println "${POKEMON_ID}" 138 | else 139 | sub::println "${POKEMON_NAME}" 140 | fi 141 | 142 | if [ "${POKEMON_ID}" = '' -o \ 143 | "$(tr -d '[0-9]' <<<"${POKEMON_ID}")" != '' ]; then 144 | exit 1 145 | fi 146 | -------------------------------------------------------------------------------- /imos-install: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-install is a script to configure imos-bin. 3 | # 4 | # imos-install configures user directories and installs an installed 5 | # password if necessary. This script requires the root privilege. 6 | # 7 | # Usage: 8 | # imos-install 9 | 10 | source "$(dirname "${BASH_SOURCE}")"/imos-variables || exit 1 11 | eval "${IMOSH_INIT}" 12 | 13 | POKEMON="$(imos-pokemon --nocache)" 14 | 15 | check() { 16 | if [ "$(whoami)" != 'root' ]; then 17 | LOG FATAL 'root privilege is required' 18 | fi 19 | } 20 | 21 | backup_and_move() { 22 | if [ "${#}" -eq 2 ]; then 23 | local source="${1}" 24 | local destination="${2}" 25 | LOG INFO "Moving ${source} to ${destination}..." 26 | if [ -L "${source}" ]; then 27 | if [ "$(readlink "${source}")" != "${destination}" ]; then 28 | rm "${source}" 29 | ln -s "${destination}" "${source}" 30 | else 31 | LOG INFO 'There is nothing to do.' 32 | fi 33 | else 34 | if [ -a "${source}" ]; then 35 | if [ -a "${destination}" ]; then 36 | mv "${destination}" "${destination}.$(date '+%Y%m%d-%H%M%S')" 37 | fi 38 | mkdir -p "$(dirname "${destination}")" 39 | mv "${source}" "${destination}" 40 | fi 41 | ln -s "${destination}" "${source}" || LOG ERROR \ 42 | "Failed to create a symbolic link: ${source} to ${destination}." 43 | fi 44 | else 45 | LOG FATAL "Wrong number of arguments." 46 | fi 47 | } 48 | 49 | register_launchdaemons() { 50 | local daemons_directory="${IMOS_ROOT}/Library/LaunchDaemons" 51 | if [ ! -d "${daemons_directory}" ]; then 52 | LOG ERROR "LaunchDaemons directory is not found: ${daemons_directory}" 53 | return 54 | fi 55 | # Unregister imoz jobs first. 56 | local job='' 57 | while read -r -d$'\n' job; do 58 | LOG INFO "Unregistering an imoz job: ${job}" 59 | launchctl remove "${job}" 60 | done < <(launchctl list | cut -d$'\t' -f 3 | grep '^jp.imoz.') 61 | # Install launch daemons. 62 | pushd "$(dirname "${BASH_SOURCE}")/data/LaunchDaemons" >'/dev/null' 63 | for job in jp.imoz.*; do 64 | if [ -f "${daemons_directory}/${job}" ]; then 65 | rm "${daemons_directory}/${job}" 66 | fi 67 | if [ -f "${job}" ]; then 68 | cat "${job}" > "${daemons_directory}/${job}" 69 | chmod 644 "${daemons_directory}/${job}" 70 | fi 71 | done 72 | popd >/dev/null 73 | # Reload launch daemons. 74 | pushd "${daemons_directory}" >'/dev/null' 75 | for job in jp.imoz.*; do 76 | if [ -f "${job}" ]; then 77 | launchctl load "${job}" 78 | fi 79 | done 80 | popd >'/dev/null' 81 | } 82 | 83 | imos-install::darwin() { 84 | if [ ! -L "${IMOS_STORAGE}" -a ! -a "${IMOS_STORAGE}" ]; then 85 | local arceus="${IMOS_ROOT}/Volumes/Arceus" 86 | if [ ! -d "${arceus}" ]; then 87 | LOG FATAL "Arceus is not found: ${arceus}" 88 | fi 89 | ln -s "${arceus}" "${IMOS_STORAGE}" 90 | fi 91 | LOG INFO 'Install a password...' 92 | imos-crypt --installed_password --encrypt 93 | LOG INFO 'Preparing user files...' 94 | pushd "${IMOS_USERS}" >/dev/null 95 | local user='' 96 | for user in *; do 97 | case "${user}" in 98 | Guest|Shared|provision|ninetan) :;; 99 | *) 100 | local owner="$(imos-stat --format='%u:%g' "${user}")" 101 | local storage_home="${IMOS_STORAGE}/Users/${user}" 102 | mkdir -p "${storage_home}" 103 | chown "${owner}" "${storage_home}" 104 | chmod 755 "${storage_home}" 105 | local directories=( 106 | '.dropbox' 'Desktop' 'Documents' 'Downloads' 'Dropbox' 107 | 'Movies' 'Music') 108 | local directory='' 109 | for directory in "${directories[@]}"; do 110 | backup_and_move "${user}/${directory}" "${storage_home}/${directory}" 111 | done 112 | local files=('.bash_history') 113 | local file='' 114 | for file in "${files[@]}"; do 115 | backup_and_move "${user}/${file}" "${storage_home}/${file}" 116 | done 117 | backup_and_move "${user}/Library/Caches" \ 118 | "${IMOS_STORAGE}/cache/Users/${user}/Library/Caches" 119 | esac 120 | done 121 | popd >/dev/null 122 | # backup_and_move "${IMOS_ROOT}/var/vm" "${IMOS_STORAGE}/cache/var/vm" 123 | backup_and_move "${IMOS_ROOT}/Library/Caches" \ 124 | "${IMOS_STORAGE}/cache/Library/Caches" 125 | register_launchdaemons 126 | 127 | LOG INFO 'Installing Homebrew API token...' 128 | rm -rf /usr/local/imos/bashrc 129 | mkdir -p /usr/local/imos/bashrc 130 | imos-crypt --installed_password --decrypt \ 131 | "${IMOS_BIN}/data/homebrew-api-token.sh.aes256" \ 132 | "${IMOS_ROOT}/usr/local/imos/bashrc/homebrew-api-token.sh" 133 | } 134 | 135 | imos-install::linux() { 136 | # There is nothing to do for linux. 137 | : 138 | } 139 | 140 | main() { 141 | check 142 | "imos-install::$(sub::strtolower "${UNAME}")" 143 | } 144 | 145 | if [ "${#}" -eq 0 ]; then 146 | main 147 | else 148 | LOG FATAL "imos-install requires no arguments." 149 | fi 150 | -------------------------------------------------------------------------------- /imofs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imofs is a command-line tool to backup/restore files and services. 3 | # 4 | # This command is useful especially for programming contests giving a disk 5 | # image, and imofs backs up the initial state and restores almost everything. 6 | # Directories to backup/restore are specified as TARGETS, and services to 7 | # backup/restore are specified as SERVICES. 8 | # 9 | # Usage: 10 | # imofs [options] command 11 | # 12 | # Command: 13 | # - restore 14 | # Restore files and services from FLAGS_backup_directory to 15 | # FLAGS_target_directory. 16 | # - backup 17 | # Backup files and services from FLAGS_target_directory to 18 | # FLAGS_backup_directory. 19 | # - deploy 20 | # Restore files and services from FLAGS_backup_directory to 21 | # FLAGS_target_directory, and deploy files from FLAGS_source_directory to 22 | # FLAGS_target_directory. 23 | # 24 | # Configurations: 25 | # - /backup/imofs/services 26 | # Services to backup/restore. This file is updated when you call 27 | # backup command. 28 | # - /backup/imofs/targets 29 | # Directories to backup/restore. A directory should not end with "/". 30 | 31 | source "$(dirname "${BASH_SOURCE}")"/imosh || exit 1 32 | DEFINE_string target_directory '' 'Directory to backup/restore.' 33 | DEFINE_string backup_directory '/backup' 'Directory to store backups.' 34 | DEFINE_string source_directory '.' 'Source directory to deploy from.' 35 | DEFINE_bool dry_run --alias=n false 'Show what will be copied.' 36 | eval "${IMOSH_INIT}" 37 | 38 | # Top directories to backup. 39 | TARGETS=(bin etc home lib lib64 local opt root sbin usr var) 40 | # Services to stop before operating. 41 | SERVICES=(mysqld) 42 | 43 | stop_services() { 44 | for service in "${SERVICES[@]}" \ 45 | $(cat "${FLAGS_backup_directory}/imofs/services"); do 46 | if service "${service}" status >/dev/null 2>/dev/null; then 47 | ACTIVE_SERVICES+=("${service}") 48 | echo "Stopping ${service}..." >&2 49 | service "${service}" stop 50 | fi 51 | done 52 | } 53 | 54 | restart_services() { 55 | local services=() 56 | if [ "${#ACTIVE_SERVICES[*]}" -ne 0 ]; then 57 | services+=("${ACTIVE_SERVICES[@]}") 58 | fi 59 | services+=($(cat "${FLAGS_backup_directory}/imofs/services")) 60 | for service in "${services[@]}"; do 61 | if ! service "${service}" status >/dev/null 2>/dev/null; then 62 | echo "Starting ${service}..." >&2 63 | service "${service}" start 64 | fi 65 | done 66 | } 67 | 68 | call_rsync() { 69 | local rsync_options=(rsync) 70 | if (( FLAGS_dry_run )); then 71 | rsync_options+=(--dry-run) 72 | fi 73 | "${rsync_options[@]}" "$@" 74 | } 75 | 76 | imofs::restore() { 77 | if [ ! -d "${FLAGS_backup_directory}" ]; then 78 | echo 'There is no backup.' >&2 79 | exit 80 | fi 81 | stop_services 82 | for target in "${TARGETS[@]}"; do 83 | if [ -d "${FLAGS_backup_directory}/${target}/" ]; then 84 | call_rsync -av --delete \ 85 | "${FLAGS_backup_directory}/${target}/" \ 86 | "${FLAGS_target_directory}/${target}/" 87 | fi 88 | done 89 | } 90 | 91 | imofs::backup() { 92 | mkdir -p "${FLAGS_backup_directory}/imofs" 93 | echo 'Backing up services...' >&2 94 | if (( ! FLAGS_dry_run )); then 95 | for service in "${SERVICES[@]}"; do 96 | if service "${service}" status >/dev/null 2>/dev/null; then 97 | echo "${service}" >> "${FLAGS_backup_directory}/imofs/services" 98 | fi 99 | done 100 | sort "${FLAGS_backup_directory}/imofs/services" | \ 101 | uniq > "${FLAGS_backup_directory}/imofs/services.tmp" 102 | cat "${FLAGS_backup_directory}/imofs/services.tmp" \ 103 | > "${FLAGS_backup_directory}/imofs/services" 104 | rm "${FLAGS_backup_directory}/imofs/services.tmp" 105 | fi 106 | stop_services 107 | for target in "${TARGETS[@]}"; do 108 | if [ -d "${FLAGS_target_directory}/${target}" ]; then 109 | call_rsync -av --delete \ 110 | "${FLAGS_target_directory}/${target}/" \ 111 | "${FLAGS_backup_directory}/${target}/" 112 | fi 113 | done 114 | } 115 | 116 | imofs::deploy() { 117 | imofs::restore 118 | call_rsync -av "${FLAGS_source_directory}/" "${FLAGS_target_directory}/" 119 | pushd "${FLAGS_target_directory}/" 120 | if [ -f './deploy' ]; then 121 | chmod +x './deploy' 122 | ./deploy & 123 | if ! wait $!; then 124 | LOG ERROR 'deploy program failed.' 125 | fi 126 | fi 127 | popd 128 | } 129 | 130 | main() { 131 | local command="$1"; shift 132 | 133 | if [ -f "${FLAGS_backup_directory}/imofs/targets" ]; then 134 | TARGETS+=($(cat "${FLAGS_backup_directory}/imofs/targets")) 135 | fi 136 | if type "imofs::${command}" >/dev/null 2>/dev/null; then 137 | "imofs::${command}" 138 | restart_services 139 | else 140 | echo "No such command: ${command}" >&2 141 | exit 1 142 | fi 143 | } 144 | 145 | # Initialize variables. 146 | ACTIVE_SERVICES=() 147 | if [ "$#" -eq 1 ] ; then 148 | if [ "$(whoami)" != 'root' ]; then 149 | LOG FATAL 'root privilege is required.' 150 | fi 151 | main "$1" 152 | else 153 | func::exit 'Command is required.' 154 | fi 155 | -------------------------------------------------------------------------------- /test/imos-package-upload_test.sh: -------------------------------------------------------------------------------- 1 | IMOSH_TESTING=1 2 | source "$(dirname "${BASH_SOURCE}")"/../imos-package || exit 1 3 | 4 | curl() { 5 | LOG INFO "Args: $*" 6 | local ARGS_silent=0 ARGS_fail=0 ARGS_head=0 7 | eval "${IMOSH_PARSE_ARGUMENTS}" 8 | 9 | if [ "$#" -eq 1 ]; then 10 | ASSERT_EQ 1 "${ARGS_fail}" 11 | ASSERT_EQ 1 "${ARGS_head}" 12 | if [ "${1}" = \ 13 | "https://s3-ap-northeast-1.amazonaws.com/imos-package/foobar" ]; then 14 | cat << 'EOM' 15 | HTTP/1.1 200 OK 16 | Date: Wed, 07 Jan 2015 16:25:55 GMT 17 | Last-Modified: Fri, 25 Jul 2014 04:32:39 GMT 18 | ETag: "foobar" 19 | Content-Type: application/octet-stream 20 | Content-Length: 897 21 | Server: AmazonS3 22 | 23 | EOM 24 | else 25 | return 22 26 | fi 27 | else 28 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 29 | fi 30 | } 31 | 32 | imos-aws-credentials() { 33 | LOG INFO 'Mocking: imos-aws-credentials' 34 | } 35 | 36 | imos-aws() { 37 | sub::println "$@" >>"${TMPDIR}/imos-aws" 38 | } 39 | 40 | # override 41 | create_working_directory() { 42 | LOG INFO "Mocking: create_working_directory $*" 43 | if [ "$#" -eq 1 ]; then 44 | func::let "${1}" "${WORKING_DIRECTORY}" 45 | if [ -d "${WORKING_DIRECTORY}" ]; then 46 | rm -R "${WORKING_DIRECTORY}" 47 | fi 48 | else 49 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 50 | fi 51 | } 52 | 53 | run() { 54 | true >"${TMPDIR}/imos-aws" 55 | imos-package::upload "$@" >'/dev/null' 56 | cat "${TMPDIR}/imos-aws" | \ 57 | stream::str_replace "${WORKING_DIRECTORY}" 'WORKING_DIRECTORY' 58 | } 59 | 60 | test::imos-package-upload::rfc2date() { 61 | EXPECT_EQ '2014-01-08' "$(rfc2date 'Sun, 08 Jan 2014 10:11:12 GMT')" 62 | } 63 | 64 | test::imos-package-upload::mdate() { 65 | EXPECT_EQ '2014-07-25' "$(mdate foobar)" 66 | EXPECT_EQ '' "$(mdate foofoo)" 67 | } 68 | 69 | test::imos-package-upload() { 70 | local target="${TMPDIR}/target" 71 | local WORKING_DIRECTORY="${TMPDIR}/WORKING_DIRECTORY" 72 | touch "${target}" 73 | EXPECT_EQ "$( 74 | echo s3api put-object \ 75 | --bucket imos-package \ 76 | --key ephemeral/d41d8cd98f00b204e9800998ecf8427e \ 77 | --content-md5 1B2M2Y8AsgTpgAmY7PhCfg== \ 78 | --body WORKING_DIRECTORY/d41d8cd98f00b204e9800998ecf8427e 79 | echo s3api put-object \ 80 | --bucket imos-package \ 81 | --key ephemeral/227bc609651f929e367c3b2b79e09d5b \ 82 | --content-md5 InvGCWUfkp42fDsreeCdWw== \ 83 | --body WORKING_DIRECTORY/227bc609651f929e367c3b2b79e09d5b)" \ 84 | "$(run "${target}")" 85 | seq 100 >"${target}" 86 | EXPECT_EQ "$( 87 | echo s3api put-object \ 88 | --bucket imos-package \ 89 | --key ephemeral/d632eba71107bf7bc3ec423eab256d78 \ 90 | --content-md5 1jLrpxEHv3vD7EI+qyVteA== \ 91 | --body WORKING_DIRECTORY/d632eba71107bf7bc3ec423eab256d78 92 | echo s3api put-object \ 93 | --bucket imos-package \ 94 | --key ephemeral/6fe45f263f52b46265af688d4e26a3d6 \ 95 | --content-md5 b+RfJj9StGJlr2iNTiaj1g== \ 96 | --body WORKING_DIRECTORY/6fe45f263f52b46265af688d4e26a3d6)" \ 97 | "$(run "${target}")" 98 | EXPECT_EQ 'd632eba71107bf7bc3ec423eab256d78' \ 99 | "$(cat "${WORKING_DIRECTORY}/d632eba71107bf7bc3ec423eab256d78" | \ 100 | stream::md5)" 101 | EXPECT_EQ '6fe45f263f52b46265af688d4e26a3d6' \ 102 | "$(cat "${WORKING_DIRECTORY}/6fe45f263f52b46265af688d4e26a3d6" | \ 103 | stream::md5)" 104 | EXPECT_EQ 'd632eba71107bf7bc3ec423eab256d78' \ 105 | "$(cat "${WORKING_DIRECTORY}/6fe45f263f52b46265af688d4e26a3d6")" 106 | FLAGS_fragment_size=100 107 | EXPECT_EQ "$( 108 | { 109 | echo s3api put-object \ 110 | --bucket imos-package \ 111 | --key ephemeral/b8465f50d9579a17a918285548090783 \ 112 | --content-md5 uEZfUNlXmhepGChVSAkHgw== \ 113 | --body WORKING_DIRECTORY/b8465f50d9579a17a918285548090783 114 | echo s3api put-object \ 115 | --bucket imos-package \ 116 | --key ephemeral/bcab5aa38bd1777c2cdbc1b79cee353f \ 117 | --content-md5 vKtao4vRd3ws28G3nO41Pw== \ 118 | --body WORKING_DIRECTORY/bcab5aa38bd1777c2cdbc1b79cee353f 119 | echo s3api put-object \ 120 | --bucket imos-package \ 121 | --key ephemeral/c4095b9c7c0a5d8dc6472ecb3fb7395e \ 122 | --content-md5 xAlbnHwKXY3GRy7LP7c5Xg== \ 123 | --body WORKING_DIRECTORY/c4095b9c7c0a5d8dc6472ecb3fb7395e 124 | echo s3api put-object \ 125 | --bucket imos-package \ 126 | --key ephemeral/0982e972bf2e287852c958582b0e4364 \ 127 | --content-md5 CYLpcr8uKHhSyVhYKw5DZA== \ 128 | --body WORKING_DIRECTORY/0982e972bf2e287852c958582b0e4364 129 | } | sort)" \ 130 | "$(run "${target}" | sort)" 131 | EXPECT_EQ 'd632eba71107bf7bc3ec423eab256d78' \ 132 | "$({ 133 | cat "${WORKING_DIRECTORY}/c4095b9c7c0a5d8dc6472ecb3fb7395e" 134 | cat "${WORKING_DIRECTORY}/b8465f50d9579a17a918285548090783" 135 | cat "${WORKING_DIRECTORY}/bcab5aa38bd1777c2cdbc1b79cee353f" 136 | } | \ 137 | stream::md5)" 138 | EXPECT_EQ "$( 139 | echo 'c4095b9c7c0a5d8dc6472ecb3fb7395e' 140 | echo 'b8465f50d9579a17a918285548090783' 141 | echo 'bcab5aa38bd1777c2cdbc1b79cee353f')" \ 142 | "$(cat "${WORKING_DIRECTORY}/0982e972bf2e287852c958582b0e4364")" 143 | } 144 | -------------------------------------------------------------------------------- /test/imos-pokemon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "$(dirname "${BASH_SOURCE}")"/../imosh || exit 1 4 | 5 | system_profiler() { 6 | if [ "$*" = '-xml -detailLevel full SPSoftwareDataType' ]; then 7 | cat <<'EOM' 8 | 9 | 10 | 11 | 12 | 13 | _SPCommandLineArguments 14 | 15 | /usr/sbin/system_profiler 16 | -nospawn 17 | -xml 18 | SPSoftwareDataType 19 | -detailLevel 20 | full 21 | 22 | _SPCompletionInterval 23 | 0.0058029890060424805 24 | _SPResponseTime 25 | 0.12743896245956421 26 | _dataType 27 | SPSoftwareDataType 28 | _detailLevel 29 | -2 30 | _items 31 | 32 | 33 | _name 34 | os_overview 35 | boot_mode 36 | normal_boot 37 | boot_volume 38 | Pikachu 39 | kernel_version 40 | Darwin 14.0.0 41 | local_host_name 42 | Ditto 43 | os_version 44 | OS X 10.10 (14A389) 45 | secure_vm 46 | secure_vm_enabled 47 | uptime 48 | up 0:0:53:48 49 | user_name 50 | imos (imos) 51 | 52 | 53 | _parentDataType 54 | SPRootDataType 55 | _properties 56 | 57 | boot_mode 58 | 59 | _detailLevel 60 | 0 61 | _order 62 | 32 63 | 64 | boot_volume 65 | 66 | _detailLevel 67 | 0 68 | _order 69 | 30 70 | _suppressLocalization 71 | YES 72 | 73 | kernel_version 74 | 75 | _order 76 | 20 77 | 78 | local_host_name 79 | 80 | _detailLevel 81 | 0 82 | _order 83 | 35 84 | 85 | os_version 86 | 87 | _order 88 | 10 89 | 90 | secure_vm 91 | 92 | _detailLevel 93 | 0 94 | _order 95 | 50 96 | 97 | server_configuration 98 | 99 | _detailLevel 100 | 0 101 | _order 102 | 12 103 | 104 | user_name 105 | 106 | _detailLevel 107 | 0 108 | _order 109 | 40 110 | 111 | volumes 112 | 113 | _detailLevel 114 | 0 115 | 116 | 117 | _timeStamp 118 | 2014-10-26T14:03:27Z 119 | _versionInfo 120 | 121 | com.apple.SystemProfiler.SPOSReporter 122 | 915 123 | 124 | 125 | 126 | 127 | EOM 128 | else 129 | LOG FATAL "Unknown flags: $*" 130 | fi 131 | } 132 | 133 | df() { 134 | if [ "$*" = '/' ]; then 135 | cat <<'EOM' 136 | Filesystem 512-blocks Used Available Capacity iused ifree %iused Mounted on 137 | /dev/disk0s4 154971144 24244328 130214816 16% 3094539 16276852 16% / 138 | EOM 139 | else 140 | LOG FATAL "Unknown flags: $*" 141 | fi 142 | } 143 | 144 | diskutil() { 145 | if [ "$*" = 'info -plist /dev/disk0s4' ]; then 146 | cat <<'EOM' 147 | 148 | 149 | 150 | 151 | Bootable 152 | 153 | BusProtocol 154 | PCI 155 | CanBeMadeBootable 156 | 157 | CanBeMadeBootableRequiresDestroy 158 | 159 | Content 160 | Apple_HFS 161 | DeviceBlockSize 162 | 512 163 | DeviceIdentifier 164 | disk0s4 165 | DeviceNode 166 | /dev/disk0s4 167 | DeviceTreePath 168 | IODeviceTree:/PCI0@0/RP06@1C,5/SSD0@0/PRT0@0/PMP@0 169 | DiskUUID 170 | 3BF55C01-92BC-4714-80C7-D7EE0B280E9F 171 | Ejectable 172 | 173 | FilesystemName 174 | Journaled HFS+ 175 | FilesystemType 176 | hfs 177 | FilesystemUserVisibleName 178 | Mac OS Extended (Journaled) 179 | FreeSpace 180 | 66661707776 181 | GlobalPermissionsEnabled 182 | 183 | IOKitSize 184 | 79345225728 185 | Internal 186 | 187 | JournalOffset 188 | 2453504 189 | JournalSize 190 | 8388608 191 | MediaName 192 | Pikachu 193 | MediaType 194 | Generic 195 | MountPoint 196 | / 197 | ParentWholeDisk 198 | disk0 199 | RAIDMaster 200 | 201 | RAIDSlice 202 | 203 | RecoveryDeviceIdentifier 204 | disk0s5 205 | SMARTStatus 206 | Verified 207 | SolidState 208 | 209 | SupportsGlobalPermissionsDisable 210 | 211 | SystemImage 212 | 213 | TotalSize 214 | 79345225728 215 | VolumeAllocationBlockSize 216 | 4096 217 | VolumeName 218 | Pikachu 219 | VolumeUUID 220 | 1B66B181-9AA9-3478-A247-072F82162FD5 221 | WholeDisk 222 | 223 | Writable 224 | 225 | WritableMedia 226 | 227 | WritableVolume 228 | 229 | 230 | 231 | EOM 232 | else 233 | LOG FATAL "Unknown flags: $*" 234 | fi 235 | } 236 | 237 | readlink() { 238 | if [ "${#}" -eq 1 ]; then 239 | local path="$(pwd)/${1}" 240 | if [ "${path}" = '/Volumes/Pikachu' ]; then 241 | sub::println '/' 242 | else 243 | sub::println "${path}" 244 | fi 245 | else 246 | LOG FATAL "Wrong number of arguments." 247 | fi 248 | } 249 | 250 | hostname() { 251 | if [ "$*" = '-i' ]; then 252 | sub::println '153.121.64.206' 253 | else 254 | LOG FATAL "Unknown flags: $*" 255 | fi 256 | } 257 | 258 | unset IMOSH_TESTING 259 | source "$(dirname "${BASH_SOURCE}")"/../imos-pokemon || exit 1 260 | -------------------------------------------------------------------------------- /data/pokemon.txt: -------------------------------------------------------------------------------- 1 | 1 Bulbasaur 2 | 2 Ivysaur 3 | 3 Venusaur 4 | 4 Charmander 5 | 5 Charmeleon 6 | 6 Charizard 7 | 7 Squirtle 8 | 8 Wartortle 9 | 9 Blastoise 10 | 10 Caterpie 11 | 11 Metapod 12 | 12 Butterfree 13 | 13 Weedle 14 | 14 Kakuna 15 | 15 Beedrill 16 | 16 Pidgey 17 | 17 Pidgeotto 18 | 18 Pidgeot 19 | 19 Rattata 20 | 20 Raticate 21 | 21 Spearow 22 | 22 Fearow 23 | 23 Ekans 24 | 24 Arbok 25 | 25 Pikachu 26 | 26 Raichu 27 | 27 Sandshrew 28 | 28 Sandslash 29 | 29 Nidoran\u2640 30 | 30 Nidorina 31 | 31 Nidoqueen 32 | 32 Nidoran\u2642 33 | 33 Nidorino 34 | 34 Nidoking 35 | 35 Clefairy 36 | 36 Clefable 37 | 37 Vulpix 38 | 38 Ninetales 39 | 39 Jigglypuff 40 | 40 Wigglytuff 41 | 41 Zubat 42 | 42 Golbat 43 | 43 Oddish 44 | 44 Gloom 45 | 45 Vileplume 46 | 46 Paras 47 | 47 Parasect 48 | 48 Venonat 49 | 49 Venomoth 50 | 50 Diglett 51 | 51 Dugtrio 52 | 52 Meowth 53 | 53 Persian 54 | 54 Psyduck 55 | 55 Golduck 56 | 56 Mankey 57 | 57 Primeape 58 | 58 Growlithe 59 | 59 Arcanine 60 | 60 Poliwag 61 | 61 Poliwhirl 62 | 62 Poliwrath 63 | 63 Abra 64 | 64 Kadabra 65 | 65 Alakazam 66 | 66 Machop 67 | 67 Machoke 68 | 68 Machamp 69 | 69 Bellsprout 70 | 70 Weepinbell 71 | 71 Victreebel 72 | 72 Tentacool 73 | 73 Tentacruel 74 | 74 Geodude 75 | 75 Graveler 76 | 76 Golem 77 | 77 Ponyta 78 | 78 Rapidash 79 | 79 Slowpoke 80 | 80 Slowbro 81 | 81 Magnemite 82 | 82 Magneton 83 | 83 Farfetch'd 84 | 84 Doduo 85 | 85 Dodrio 86 | 86 Seel 87 | 87 Dewgong 88 | 88 Grimer 89 | 89 Muk 90 | 90 Shellder 91 | 91 Cloyster 92 | 92 Gastly 93 | 93 Haunter 94 | 94 Gengar 95 | 95 Onix 96 | 96 Drowzee 97 | 97 Hypno 98 | 98 Krabby 99 | 99 Kingler 100 | 100 Voltorb 101 | 101 Electrode 102 | 102 Exeggcute 103 | 103 Exeggutor 104 | 104 Cubone 105 | 105 Marowak 106 | 106 Hitmonlee 107 | 107 Hitmonchan 108 | 108 Lickitung 109 | 109 Koffing 110 | 110 Weezing 111 | 111 Rhyhorn 112 | 112 Rhydon 113 | 113 Chansey 114 | 114 Tangela 115 | 115 Kangaskhan 116 | 116 Horsea 117 | 117 Seadra 118 | 118 Goldeen 119 | 119 Seaking 120 | 120 Staryu 121 | 121 Starmie 122 | 122 Mr. Mime 123 | 123 Scyther 124 | 124 Jynx 125 | 125 Electabuzz 126 | 126 Magmar 127 | 127 Pinsir 128 | 128 Tauros 129 | 129 Magikarp 130 | 130 Gyarados 131 | 131 Lapras 132 | 132 Ditto 133 | 133 Eevee 134 | 134 Vaporeon 135 | 135 Jolteon 136 | 136 Flareon 137 | 137 Porygon 138 | 138 Omanyte 139 | 139 Omastar 140 | 140 Kabuto 141 | 141 Kabutops 142 | 142 Aerodactyl 143 | 143 Snorlax 144 | 144 Articuno 145 | 145 Zapdos 146 | 146 Moltres 147 | 147 Dratini 148 | 148 Dragonair 149 | 149 Dragonite 150 | 150 Mewtwo 151 | 151 Mew 152 | 152 Chikorita 153 | 153 Bayleef 154 | 154 Meganium 155 | 155 Cyndaquil 156 | 156 Quilava 157 | 157 Typhlosion 158 | 158 Totodile 159 | 159 Croconaw 160 | 160 Feraligatr 161 | 161 Sentret 162 | 162 Furret 163 | 163 Hoothoot 164 | 164 Noctowl 165 | 165 Ledyba 166 | 166 Ledian 167 | 167 Spinarak 168 | 168 Ariados 169 | 169 Crobat 170 | 170 Chinchou 171 | 171 Lanturn 172 | 172 Pichu 173 | 173 Cleffa 174 | 174 Igglybuff 175 | 175 Togepi 176 | 176 Togetic 177 | 177 Natu 178 | 178 Xatu 179 | 179 Mareep 180 | 180 Flaaffy 181 | 181 Ampharos 182 | 182 Bellossom 183 | 183 Marill 184 | 184 Azumarill 185 | 185 Sudowoodo 186 | 186 Politoed 187 | 187 Hoppip 188 | 188 Skiploom 189 | 189 Jumpluff 190 | 190 Aipom 191 | 191 Sunkern 192 | 192 Sunflora 193 | 193 Yanma 194 | 194 Wooper 195 | 195 Quagsire 196 | 196 Espeon 197 | 197 Umbreon 198 | 198 Murkrow 199 | 199 Slowking 200 | 200 Misdreavus 201 | 201 Unown 202 | 202 Wobbuffet 203 | 203 Girafarig 204 | 204 Pineco 205 | 205 Forretress 206 | 206 Dunsparce 207 | 207 Gligar 208 | 208 Steelix 209 | 209 Snubbull 210 | 210 Granbull 211 | 211 Qwilfish 212 | 212 Scizor 213 | 213 Shuckle 214 | 214 Heracross 215 | 215 Sneasel 216 | 216 Teddiursa 217 | 217 Ursaring 218 | 218 Slugma 219 | 219 Magcargo 220 | 220 Swinub 221 | 221 Piloswine 222 | 222 Corsola 223 | 223 Remoraid 224 | 224 Octillery 225 | 225 Delibird 226 | 226 Mantine 227 | 227 Skarmory 228 | 228 Houndour 229 | 229 Houndoom 230 | 230 Kingdra 231 | 231 Phanpy 232 | 232 Donphan 233 | 233 Porygon2 234 | 234 Stantler 235 | 235 Smeargle 236 | 236 Tyrogue 237 | 237 Hitmontop 238 | 238 Smoochum 239 | 239 Elekid 240 | 240 Magby 241 | 241 Miltank 242 | 242 Blissey 243 | 243 Raikou 244 | 244 Entei 245 | 245 Suicune 246 | 246 Larvitar 247 | 247 Pupitar 248 | 248 Tyranitar 249 | 249 Lugia 250 | 250 Ho-Oh 251 | 251 Celebi 252 | 252 Treecko 253 | 253 Grovyle 254 | 254 Sceptile 255 | 255 Torchic 256 | 256 Combusken 257 | 257 Blaziken 258 | 258 Mudkip 259 | 259 Marshtomp 260 | 260 Swampert 261 | 261 Poochyena 262 | 262 Mightyena 263 | 263 Zigzagoon 264 | 264 Linoone 265 | 265 Wurmple 266 | 266 Silcoon 267 | 267 Beautifly 268 | 268 Cascoon 269 | 269 Dustox 270 | 270 Lotad 271 | 271 Lombre 272 | 272 Ludicolo 273 | 273 Seedot 274 | 274 Nuzleaf 275 | 275 Shiftry 276 | 276 Taillow 277 | 277 Swellow 278 | 278 Wingull 279 | 279 Pelipper 280 | 280 Ralts 281 | 281 Kirlia 282 | 282 Gardevoir 283 | 283 Surskit 284 | 284 Masquerain 285 | 285 Shroomish 286 | 286 Breloom 287 | 287 Slakoth 288 | 288 Vigoroth 289 | 289 Slaking 290 | 290 Nincada 291 | 291 Ninjask 292 | 292 Shedinja 293 | 293 Whismur 294 | 294 Loudred 295 | 295 Exploud 296 | 296 Makuhita 297 | 297 Hariyama 298 | 298 Azurill 299 | 299 Nosepass 300 | 300 Skitty 301 | 301 Delcatty 302 | 302 Sableye 303 | 303 Mawile 304 | 304 Aron 305 | 305 Lairon 306 | 306 Aggron 307 | 307 Meditite 308 | 308 Medicham 309 | 309 Electrike 310 | 310 Manectric 311 | 311 Plusle 312 | 312 Minun 313 | 313 Volbeat 314 | 314 Illumise 315 | 315 Roselia 316 | 316 Gulpin 317 | 317 Swalot 318 | 318 Carvanha 319 | 319 Sharpedo 320 | 320 Wailmer 321 | 321 Wailord 322 | 322 Numel 323 | 323 Camerupt 324 | 324 Torkoal 325 | 325 Spoink 326 | 326 Grumpig 327 | 327 Spinda 328 | 328 Trapinch 329 | 329 Vibrava 330 | 330 Flygon 331 | 331 Cacnea 332 | 332 Cacturne 333 | 333 Swablu 334 | 334 Altaria 335 | 335 Zangoose 336 | 336 Seviper 337 | 337 Lunatone 338 | 338 Solrock 339 | 339 Barboach 340 | 340 Whiscash 341 | 341 Corphish 342 | 342 Crawdaunt 343 | 343 Baltoy 344 | 344 Claydol 345 | 345 Lileep 346 | 346 Cradily 347 | 347 Anorith 348 | 348 Armaldo 349 | 349 Feebas 350 | 350 Milotic 351 | 351 Castform 352 | 352 Kecleon 353 | 353 Shuppet 354 | 354 Banette 355 | 355 Duskull 356 | 356 Dusclops 357 | 357 Tropius 358 | 358 Chimecho 359 | 359 Absol 360 | 360 Wynaut 361 | 361 Snorunt 362 | 362 Glalie 363 | 363 Spheal 364 | 364 Sealeo 365 | 365 Walrein 366 | 366 Clamperl 367 | 367 Huntail 368 | 368 Gorebyss 369 | 369 Relicanth 370 | 370 Luvdisc 371 | 371 Bagon 372 | 372 Shelgon 373 | 373 Salamence 374 | 374 Beldum 375 | 375 Metang 376 | 376 Metagross 377 | 377 Regirock 378 | 378 Regice 379 | 379 Registeel 380 | 380 Latias 381 | 381 Latios 382 | 382 Kyogre 383 | 383 Groudon 384 | 384 Rayquaza 385 | 385 Jirachi 386 | 386 Deoxys 387 | 387 Turtwig 388 | 388 Grotle 389 | 389 Torterra 390 | 390 Chimchar 391 | 391 Monferno 392 | 392 Infernape 393 | 393 Piplup 394 | 394 Prinplup 395 | 395 Empoleon 396 | 396 Starly 397 | 397 Staravia 398 | 398 Staraptor 399 | 399 Bidoof 400 | 400 Bibarel 401 | 401 Kricketot 402 | 402 Kricketune 403 | 403 Shinx 404 | 404 Luxio 405 | 405 Luxray 406 | 406 Budew 407 | 407 Roserade 408 | 408 Cranidos 409 | 409 Rampardos 410 | 410 Shieldon 411 | 411 Bastiodon 412 | 412 Burmy 413 | 413 Wormadam 414 | 414 Mothim 415 | 415 Combee 416 | 416 Vespiquen 417 | 417 Pachirisu 418 | 418 Buizel 419 | 419 Floatzel 420 | 420 Cherubi 421 | 421 Cherrim 422 | 422 Shellos 423 | 423 Gastrodon 424 | 424 Ambipom 425 | 425 Drifloon 426 | 426 Drifblim 427 | 427 Buneary 428 | 428 Lopunny 429 | 429 Mismagius 430 | 430 Honchkrow 431 | 431 Glameow 432 | 432 Purugly 433 | 433 Chingling 434 | 434 Stunky 435 | 435 Skuntank 436 | 436 Bronzor 437 | 437 Bronzong 438 | 438 Bonsly 439 | 439 Mime Jr. 440 | 440 Happiny 441 | 441 Chatot 442 | 442 Spiritomb 443 | 443 Gible 444 | 444 Gabite 445 | 445 Garchomp 446 | 446 Munchlax 447 | 447 Riolu 448 | 448 Lucario 449 | 449 Hippopotas 450 | 450 Hippowdon 451 | 451 Skorupi 452 | 452 Drapion 453 | 453 Croagunk 454 | 454 Toxicroak 455 | 455 Carnivine 456 | 456 Finneon 457 | 457 Lumineon 458 | 458 Mantyke 459 | 459 Snover 460 | 460 Abomasnow 461 | 461 Weavile 462 | 462 Magnezone 463 | 463 Lickilicky 464 | 464 Rhyperior 465 | 465 Tangrowth 466 | 466 Electivire 467 | 467 Magmortar 468 | 468 Togekiss 469 | 469 Yanmega 470 | 470 Leafeon 471 | 471 Glaceon 472 | 472 Gliscor 473 | 473 Mamoswine 474 | 474 Porygon-Z 475 | 475 Gallade 476 | 476 Probopass 477 | 477 Dusknoir 478 | 478 Froslass 479 | 479 Rotom 480 | 480 Uxie 481 | 481 Mesprit 482 | 482 Azelf 483 | 483 Dialga 484 | 484 Palkia 485 | 485 Heatran 486 | 486 Regigigas 487 | 487 Giratina 488 | 488 Cresselia 489 | 489 Phione 490 | 490 Manaphy 491 | 491 Darkrai 492 | 492 Shaymin 493 | 493 Arceus 494 | 494 Victini 495 | 495 Snivy 496 | 496 Servine 497 | 497 Serperior 498 | 498 Tepig 499 | 499 Pignite 500 | 500 Emboar 501 | 501 Oshawott 502 | 502 Dewott 503 | 503 Samurott 504 | 504 Patrat 505 | 505 Watchog 506 | 506 Lillipup 507 | 507 Herdier 508 | 508 Stoutland 509 | 509 Purrloin 510 | 510 Liepard 511 | 511 Pansage 512 | 512 Simisage 513 | 513 Pansear 514 | 514 Simisear 515 | 515 Panpour 516 | 516 Simipour 517 | 517 Munna 518 | 518 Musharna 519 | 519 Pidove 520 | 520 Tranquill 521 | 521 Unfezant 522 | 522 Blitzle 523 | 523 Zebstrika 524 | 524 Roggenrola 525 | 525 Boldore 526 | 526 Gigalith 527 | 527 Woobat 528 | 528 Swoobat 529 | 529 Drilbur 530 | 530 Excadrill 531 | 531 Audino 532 | 532 Timburr 533 | 533 Gurdurr 534 | 534 Conkeldurr 535 | 535 Tympole 536 | 536 Palpitoad 537 | 537 Seismitoad 538 | 538 Throh 539 | 539 Sawk 540 | 540 Sewaddle 541 | 541 Swadloon 542 | 542 Leavanny 543 | 543 Venipede 544 | 544 Whirlipede 545 | 545 Scolipede 546 | 546 Cottonee 547 | 547 Whimsicott 548 | 548 Petilil 549 | 549 Lilligant 550 | 550 Basculin 551 | 551 Sandile 552 | 552 Krokorok 553 | 553 Krookodile 554 | 554 Darumaka 555 | 555 Darmanitan 556 | 556 Maractus 557 | 557 Dwebble 558 | 558 Crustle 559 | 559 Scraggy 560 | 560 Scrafty 561 | 561 Sigilyph 562 | 562 Yamask 563 | 563 Cofagrigus 564 | 564 Tirtouga 565 | 565 Carracosta 566 | 566 Archen 567 | 567 Archeops 568 | 568 Trubbish 569 | 569 Garbodor 570 | 570 Zorua 571 | 571 Zoroark 572 | 572 Minccino 573 | 573 Cinccino 574 | 574 Gothita 575 | 575 Gothorita 576 | 576 Gothitelle 577 | 577 Solosis 578 | 578 Duosion 579 | 579 Reuniclus 580 | 580 Ducklett 581 | 581 Swanna 582 | 582 Vanillite 583 | 583 Vanillish 584 | 584 Vanilluxe 585 | 585 Deerling 586 | 586 Sawsbuck 587 | 587 Emolga 588 | 588 Karrablast 589 | 589 Escavalier 590 | 590 Foongus 591 | 591 Amoonguss 592 | 592 Frillish 593 | 593 Jellicent 594 | 594 Alomomola 595 | 595 Joltik 596 | 596 Galvantula 597 | 597 Ferroseed 598 | 598 Ferrothorn 599 | 599 Klink 600 | 600 Klang 601 | 601 Klinklang 602 | 602 Tynamo 603 | 603 Eelektrik 604 | 604 Eelektross 605 | 605 Elgyem 606 | 606 Beheeyem 607 | 607 Litwick 608 | 608 Lampent 609 | 609 Chandelure 610 | 610 Axew 611 | 611 Fraxure 612 | 612 Haxorus 613 | 613 Cubchoo 614 | 614 Beartic 615 | 615 Cryogonal 616 | 616 Shelmet 617 | 617 Accelgor 618 | 618 Stunfisk 619 | 619 Mienfoo 620 | 620 Mienshao 621 | 621 Druddigon 622 | 622 Golett 623 | 623 Golurk 624 | 624 Pawniard 625 | 625 Bisharp 626 | 626 Bouffalant 627 | 627 Rufflet 628 | 628 Braviary 629 | 629 Vullaby 630 | 630 Mandibuzz 631 | 631 Heatmor 632 | 632 Durant 633 | 633 Deino 634 | 634 Zweilous 635 | 635 Hydreigon 636 | 636 Larvesta 637 | 637 Volcarona 638 | 638 Cobalion 639 | 639 Terrakion 640 | 640 Virizion 641 | 641 Tornadus 642 | 642 Thundurus 643 | 643 Reshiram 644 | 644 Zekrom 645 | 645 Landorus 646 | 646 Kyurem 647 | -------------------------------------------------------------------------------- /library/ninestream.php: -------------------------------------------------------------------------------- 1 | '1', 5 | 'controller.debug' => '0', 6 | ]; 7 | 8 | class Stream { 9 | public function __construct($command) { 10 | global $CONFIG; 11 | $GLOBALS['STREAM_ID'] = -1; 12 | if (is_null($command)) { 13 | $this->stdin = STDOUT; 14 | $this->stdout = STDIN; 15 | $this->stderr = STDERR; 16 | return; 17 | } 18 | $spec = [['pipe', 'r'], ['pipe', 'w']]; 19 | if (boolval($CONFIG['worker.stderr'])) { 20 | $spec[] = STDERR; 21 | } else { 22 | $spec[] = ['file', '/dev/null', 'w']; 23 | } 24 | $this->process = proc_open($command, $spec, $pipes); 25 | if ($this->process === FALSE) { 26 | trigger_error("Failed to run command: $command"); 27 | $this->process = NULL; 28 | return; 29 | } 30 | foreach ($pipes as $pipe) { 31 | stream_set_blocking($pipe, FALSE); 32 | } 33 | $this->stdin = $pipes[0]; 34 | $this->stdout = $pipes[1]; 35 | $this->stderr = STDERR; 36 | } 37 | 38 | public function __destruct() { 39 | $this->Close(); 40 | } 41 | 42 | public function KillProcesses($pid) { 43 | posix_kill($pid, @constant('SIGSTOP') ?: 17); 44 | $output = []; 45 | exec('ps -axo ppid,pid', $output); 46 | $pids = [$pid]; 47 | foreach ($output as $line) { 48 | $line = explode(' ', trim(preg_replace('%\s+%', ' ', $line)), 2); 49 | if (count($line) != 2) continue; 50 | if (intval($line[0]) == $pid) { 51 | $this->KillProcesses(intval($line[1])); 52 | } 53 | } 54 | posix_kill($pid, @constant('SIGTERM') ?: 15); 55 | posix_kill($pid, @constant('SIGCONT') ?: 19); 56 | } 57 | 58 | public function Kill() { 59 | if (is_null($this->process)) return; 60 | for ($i = 0; $i < 10; $i++) { 61 | $status = proc_get_status($this->process); 62 | if (!$status['running']) return; 63 | if ($i == 0) { 64 | $this->KillProcesses($status['pid']); 65 | } 66 | usleep(1000 << $i); 67 | } 68 | posix_kill($status['pid'], @constant('SIGKILL') ?: 9); 69 | } 70 | 71 | public function Close() { 72 | $this->Kill(); 73 | if (!is_null($this->stdin) && $this->stdin != STDOUT) { 74 | fclose($this->stdin); 75 | $this->stdin = NULL; 76 | } 77 | if (!is_null($this->stdout) && $this->stdout != STDIN) { 78 | fclose($this->stdout); 79 | $this->stdout = NULL; 80 | } 81 | if (!is_null($this->stderr) && $this->stderr != STDERR) { 82 | fclose($this->stderr); 83 | $this->stderr = NULL; 84 | } 85 | if (!is_null($this->process)) { 86 | proc_close($this->process); 87 | $this->process = NULL; 88 | } 89 | } 90 | 91 | public function PrintLine($message) { 92 | fwrite($this->stdin, rtrim($message, "\r\n") . "\n"); 93 | fflush($this->stdin); 94 | } 95 | 96 | public $process = NULL; 97 | public $stdin = NULL; 98 | public $stdout = NULL; 99 | public $stderr = NULL; 100 | } 101 | 102 | class NineStream { 103 | public function __destruct() { 104 | foreach ($this->streams as $stream) { 105 | $stream->Close(); 106 | } 107 | $this->streams = []; 108 | } 109 | 110 | public function Start() { 111 | global $CONFIG; 112 | $this->streams = [-2 => new Stream(NULL)]; 113 | if (getenv('NINESTREAM_INIT') !== FALSE) { 114 | $init_commands = trim(file_get_contents(getenv('NINESTREAM_INIT'))); 115 | foreach (explode("\n", $init_commands) as $command) { 116 | $this->Command($command); 117 | } 118 | } 119 | while (TRUE) { 120 | $stdin = $this->streams[-2]->stdout; 121 | $stdout = $this->streams[-2]->stdin; 122 | if (@posix_isatty($stdin) && @posix_isatty($stdout)) { 123 | fwrite(STDOUT, '> '); 124 | fflush(STDOUT); 125 | } 126 | $buffer = ''; 127 | while (TRUE) { 128 | stream_set_blocking($stdin, TRUE); 129 | $buffer .= fgets($stdin); 130 | if (substr($buffer, -1) == "\n" || feof($stdin)) { 131 | break; 132 | } 133 | } 134 | $buffer = rtrim($buffer, "\r\n"); 135 | if ($stdout != STDOUT && boolval($CONFIG['controller.debug'])) { 136 | fwrite(STDERR, "> $buffer\n"); 137 | fflush(STDERR); 138 | } 139 | if (preg_match('%^exit(?:| (\d+))$%', $buffer, $match)) { 140 | exit(isset($match[1]) ? intval($match[1]) : 0); 141 | } 142 | if ($buffer == '' && feof($stdin)) { 143 | fwrite(STDERR, "Controller stream exited without calling exit.\n"); 144 | fflush(STDERR); 145 | exit(1); 146 | } 147 | $result = strtr($this->Command($buffer), ["\r" => '', "\n" => '']); 148 | fwrite($stdout, "$result\n"); 149 | fflush($stdout); 150 | if (isset($this->streams[-3])) { 151 | unset($this->streams[-3]); 152 | } 153 | if ($stdout != STDOUT && boolval($CONFIG['controller.debug'])) { 154 | fwrite(STDERR, "$result\n"); 155 | fflush(STDERR); 156 | } 157 | } 158 | } 159 | 160 | public function Command($command) { 161 | global $CONFIG; 162 | if (trim($command) == '') { 163 | return 'BAD_REQUEST Empty command.'; 164 | } 165 | $args = explode(' ', $command); 166 | $command = array_shift($args); 167 | $buffer = ''; 168 | switch (strtolower($command)) { 169 | case 'help': 170 | $commands = ['set', 'get', 'config', 'run', 'exec', 171 | 'stream', 'write', 'writeall', 'close', 'closeall']; 172 | sort($commands); 173 | array_unshift($commands, 'OK'); 174 | return implode(' ', $commands); 175 | case 'set': 176 | if (count($args) < 2) { 177 | return 'BAD_REQUEST run requires 2+ arguments, but ' . 178 | count($args) . '.'; 179 | } 180 | $key = array_shift($args); 181 | $value = implode(' ', $args); 182 | if (!isset($CONFIG[$key])) { 183 | return "NOT_FOUND No such a config: $key"; 184 | } 185 | $CONFIG[$key] = $value; 186 | return 'OK'; 187 | case 'get': 188 | if (count($args) != 1) { 189 | return 'BAD_REQUEST get requires exactly one argument, but ' . 190 | count($args) . '.'; 191 | } 192 | $key = array_shift($args); 193 | if (!isset($CONFIG[$key])) { 194 | return "NOT_FOUND No such a config: $key"; 195 | } 196 | return "OK $CONFIG[$key]"; 197 | case 'config': 198 | $pattern = '*'; 199 | if (count($args) > 0) { 200 | $pattern = implode(' ', $args); 201 | } 202 | $pattern = '/^' . strtr(preg_quote($pattern, '/'), 203 | ['\\*' => '.*', '\\?' => '.']) . '$/'; 204 | $keys = ['OK']; 205 | foreach (array_keys($CONFIG) as $key) { 206 | if (preg_match($pattern, $key)) { 207 | $keys[] = $key; 208 | } 209 | } 210 | return implode(' ', $keys); 211 | case 'run': 212 | if (count($args) < 2) { 213 | return 'BAD_REQUEST run requires 2+ arguments, but ' . 214 | count($args) . '.'; 215 | } 216 | $count = array_shift($args); 217 | if (!preg_match('%^\d{0,6}$%', $count)) { 218 | return 'BAD_REQUEST run\'s first argument must be an integer, ' . 219 | "but $count."; 220 | } 221 | $count = intval($count); 222 | $stream_command = implode(' ', $args); 223 | for ($i = 0; $i < $count; $i++) { 224 | $stream = new Stream($stream_command); 225 | if (is_null($stream->process)) { 226 | return "INTERNAL_ERROR Failed to run the command: $stream_command"; 227 | } 228 | $this->streams[] = $stream; 229 | } 230 | return 'OK'; 231 | case 'exec': 232 | if (count($args) < 1) { 233 | return 'BAD_REQUEST run requires 1+ arguments, but ' . 234 | count($args) . '.'; 235 | } 236 | if (!isset($this->streams[-1])) { 237 | $this->streams[-1] = new Stream(NULL); 238 | } 239 | $this->streams[-3] = $this->streams[-2]; 240 | $this->streams[-2] = new Stream(implode(' ', $args)); 241 | return 'OK'; 242 | case 'stream': 243 | $streams = ['OK']; 244 | foreach (array_keys($this->streams) as $stream_id) { 245 | if ($stream_id < 0) continue; 246 | $streams[] = $stream_id; 247 | } 248 | return implode(' ', $streams); 249 | case 'read': 250 | if (count($args) > 1) { 251 | return 'BAD_REQUEST read exactly takes 0 or 1 argument.'; 252 | } 253 | if (count($args) == 0) { 254 | $timeout = 0; 255 | } else { 256 | $timeout = array_shift($args); 257 | if (!is_numeric($timeout)) { 258 | return 'BAD_REQUEST timeout must be a number.'; 259 | } 260 | $timeout = floatval($timeout); 261 | if ($timeout < 0) $timeout = NULL; 262 | } 263 | while (TRUE) { 264 | $reads = []; 265 | foreach ($this->streams as $stream_id => $stream) { 266 | if ($stream_id < -1) continue; 267 | $reads[] = $stream->stdout; 268 | } 269 | if (count($reads) == 0) { 270 | return 'NOT_FOUND No processes are running.'; 271 | } 272 | stream_select($reads, $writes = [], $excepts = [], $timeout); 273 | if (count($reads) == 0) { 274 | return 'NO_CONTENT'; 275 | } 276 | $read = array_pop($reads); 277 | foreach ($this->streams as $id => $stream) { 278 | if ($read == $stream->stdout) break; 279 | } 280 | if (!isset($this->buffers[$id])) { 281 | $this->buffers[$id] = ''; 282 | } 283 | $this->buffers[$id] .= fgets($read); 284 | if (substr($this->buffers[$id], -1) == "\n") break; 285 | if (feof($read)) { 286 | if ($buffer == '') { 287 | unset($this->streams[$id]); 288 | continue; 289 | } 290 | break; 291 | } 292 | } 293 | $buffer = $this->buffers[$id]; 294 | $this->buffers[$id] = ''; 295 | if (feof($read)) { 296 | unset($this->streams[$id]); 297 | } 298 | return "OK $id " . rtrim($buffer, "\r\n"); 299 | case 'write': 300 | if (count($args) < 2) { 301 | return 'BAD_REQUEST write requires 2+ arguments, but ' . 302 | count($args) . '.'; 303 | } 304 | $stream_id = array_shift($args); 305 | if (!preg_match('%^-?\d{0,6}$%', $stream_id)) { 306 | return 'BAD_REQUEST write\'s first argument must be an integer, ' . 307 | "but $stream_id."; 308 | } 309 | $stream_id = intval($stream_id); 310 | $data = implode(' ', $args); 311 | if (!isset($this->streams[$stream_id])) { 312 | return "NOT_FOUND No such stream: $stream_id"; 313 | } 314 | $this->streams[$stream_id]->PrintLine($data); 315 | return 'OK'; 316 | case 'writeall': 317 | if (count($args) < 1) { 318 | return 'BAD_REQUEST writeall requires 1+ arguments.'; 319 | } 320 | $data = implode(' ', $args); 321 | foreach ($this->streams as $stream_id => $stream) { 322 | if ($stream_id < 0) continue; 323 | $stream->PrintLine($data); 324 | } 325 | return 'OK'; 326 | case 'close': 327 | if (count($args) < 1) { 328 | return 'BAD_REQUEST close requires exactly 1 argument.'; 329 | } 330 | $stream_id = array_shift($args); 331 | if (!preg_match('%^\d{0,6}$%', $stream_id)) { 332 | return 'BAD_REQUEST close\'s first argument must be an integer, ' . 333 | "but $stream_id."; 334 | } 335 | $stream_id = intval($stream_id); 336 | if (!isset($this->streams[$stream_id])) { 337 | return "NOT_FOUND No such stream: $stream_id"; 338 | } 339 | $this->streams[$stream_id]->Close(); 340 | unset($this->streams[$stream_id]); 341 | return 'OK'; 342 | case 'closeall': 343 | foreach ($this->streams as $stream_id => $stream) { 344 | if ($stream_id < 0) continue; 345 | $stream->Close(); 346 | unset($this->streams[$stream_id]); 347 | } 348 | return 'OK'; 349 | } 350 | return "NOT_IMPLEMENTED Not implemented command: $command"; 351 | } 352 | 353 | public $buffers = []; 354 | public $streams = []; 355 | } 356 | 357 | $nine_stream = new NineStream(); 358 | $nine_stream->Start(); 359 | -------------------------------------------------------------------------------- /data/variance/Moltres/office_2011.tar.aes256: -------------------------------------------------------------------------------- 1 | U2FsdGVkX18cs0mB/lImrOwFJgK1epSlcXFYTbSksTq1ftGEkLYY793bG6GiXKDh 2 | I/ib3JPxyqzujTNH39tMIvDHozfPlEY9AaQuEdqffZRx+RokUE+ePe+X2Kl3mmiP 3 | VlUkFXldPrPQvaRzwY0mYF/U2BheE5mGeHVyM8FTO4NCOD+BHLBZpmX/humzH87/ 4 | eKMPcvCCQcLGgU14KZJAJzPqEBu8IZDwbClkJOIf8alYP8g2BxKDTn+6bk5Fm9mz 5 | 6RQzynBNJINlUNXhdqi6DqOc3M8m/SulaCiGx9cMxYMMWKU9FK4MmPra0VcU1q4H 6 | qTEkRQWBnwoCmLsv+D9iun+4S0uQQfrrugvIjq2eWa8f3djqvYH/ByyV0g0kA22l 7 | ofBxCBwmuZIYZgHUpdKDpb8Ku+ubU5QyApH94eOLD+NWu2NQ1UF8s1KzYxmv/xbp 8 | lZJUwix92x3cvu9GC025Ln//aoZE/TwXuc1AmNJ9YIKL+zJBWx9WvORyfM/HdTfq 9 | F6xAlf1UlF0MR74IVRCvcc3lXtHyPYYr3uNfTvKqwFVPg8Gdn+9asu37fDRSpwwB 10 | kt1yYcG9WClu8eTtkTN7r5WUPVqPP0tb7sVoqWXcy/VxIoJ8FtF8oP5WSrKKmNSW 11 | dCWrDZu9QJwXF9qyNpZ40AcRt1Cw1d7kUJbQbWkC6mVaSPjFze+PWjaH2NbBtuNY 12 | PWdDKnuBMSzwIloE3S6E7maUZVqnftZYseUQTdoA7qiug0LB0LQIHf8gQYqwvRpL 13 | hCKAAksf/Glma7KNuRlKBefVNChvx1XIWfaEkTXEfSFS8kG9S47R1XaRzm2lumIB 14 | 1WLah4oHwbYwpPt36xCIsojlE8DuXU3Y6XXvWvUccavm5iAv959R4juuvLZP9P79 15 | J+gwe8SN9jXzStizOKWFWi9EaqljIoQbN5qTQvYkZrTNrHic+RtPkx7P/LZ49AWk 16 | RstA4po/UY1JP93Y+O53cWOy3FFuiRDhBXnkDUG4GCHnGzHOcT2dZ8yz1Nonp/sJ 17 | oz4iylrX8Sp0dGUyWM+HNR6ozkwluUrx7MoYtb1kQ34dPE20MUbB9996USpqJG0t 18 | VqTrndSPTPO+2RP6hkvylL1I96oMt/6QobJWxjlM0nWagHNRm+1kGHpE7TNc8GYE 19 | P9PFUD5DWf14OUKiYe1tS5N32xB1nCeAeIExYxCk7UyEd9a9/UShcr7lkQIRErnN 20 | fmVJH3C/9JEShKuDoiMzud9Z0RyREqmPMbSx2XZ9Yvivht5fF6vEvKB5nQ5RPJCV 21 | ygPLEO4ZzMXHUdXlkfnRUoI9Q5GlLse5HfZy97zN3ARMsiXHLUzAnfsjYQAlsI5U 22 | bV29IdOhZC3b5AA7sWheDQqtxSoxpuftypeQ/w5Tg/u3PIqEAMsUhZBo31nGrRwh 23 | Nztbg+j5G1MsYUwKlNP0me9+0uEh12+dFEUkw7ghfYTBRoX6g8J6oxF1STAndULu 24 | hjw9MNWe+dqWHjV6b0cuak07a/zw+9i4JOQ3Zyc59kKsoTlMsT5cdFAu8yM1h9nT 25 | B98zQbb7LN4lw67WnkMFrE/rEKrKgqvwbG27Gnv1rxMDZf2tH/FM0Q9RvsTfBlZ4 26 | 20oV5iOYQlatu4CvVjbpqI5YyoolD7osB2mhJTdlK4OfFqVaXMqxuw/1oK9PSmnc 27 | uO0iE+zNxoqeL1bxS9wpcLGNOZFo3oUoWMRJOZbADpgAPrJOWXRQ5HsXlqtGxSrq 28 | Pkq/1onmnRj2nga3U1CSw8waohWPweuCG0CoBD2v9jkQAzgy5FJV0J2Ms3SoC+Gp 29 | YZMg2A4sUCDNFy1lvJNNLjJ6izobkLjFaCyDCICXtQkWoCyAenQi6HP2ml73RHsW 30 | wptae5TCaOfLiEIhJ2YRPFCaVzXz3cwu1HiHLZWiUv/Cl5psnZlZ/3QaqZtCMcN8 31 | oj1EsTl0lFA8V7pCCchc4KTZYXw0dK6JzZfAsLtnWGaRQnfFHQMqhLRXZFm+4qfh 32 | G93eFQLTmNxBpmY3g3WcK0zYkYAvjrqtVvDMirrOkq1aSKYabFoUyfLZiZvMw2ig 33 | /3N55MCa+2C8smYN15oeLwsiG3ToKGZnc4C64k8cwVkE24A2JrdDfPfMDOLU7IYk 34 | nWn2DSYCwF8t9ItErpzHasjPthtOkGS4NOrY1Hw1rx6PvqQG+vslRUNMx5/1Pk6w 35 | EOm6AkUrr/LHDALA3r+QYgEiuvdb4A4FAwRV7FNeK0GK/mjvSZLutKE+Leja6F3o 36 | KkJo8FIZEHdZkX+ZIBLP1YABvWG751rhLQwOMkHp1AfPZcPI6ANO/gLrdS0EVkfj 37 | nCH2EbzjwNgw6PdpJ3aR3+iJQl0Z+0a013gbEfWToEq+GDMRCsdeABD9U0dM5G/W 38 | 8au73H67U1HxXPipoJ4FWq40C6Cc3mwKD4QCoAtQI0bSCK+TwfznkTL8zg6gjAOs 39 | Xw/PzQ2621FnPSqhyStSh5dLKAk2NWAmK0aotSfCGrN+Dn5GNWaQe8PO/wODv1Lq 40 | T0rAwQTcyntnWB+LphHcuXfteGsExUwUMLd4uvKXTPi61LDvs5/IrPaQVGAZ+w9q 41 | 5kZL2XmMHFgCqrTM8QDwMjEnWhBoChyw1NYJdrhTbkxpdZF8SJ2EqDayAQ9X52hV 42 | g4lKIHWUecSVwWXyzbDtBD9kmXAZzjpsgMwFe2kgNYo6D4g3kwKO2bIGqLSCQ5Wg 43 | kAlos0BrqKBtWabMzx6QsGJew8rPGapA+9vy8q41eNvnL3rnB3bLuiEo7Bm5akMz 44 | Uces1AZ69gXZQawnlNeQCfxh9hqfVKdQu2A1rH4CLPIVBweuTl/wFCQqoCznjz1/ 45 | 6WG/CXq9GB0Q4q9NvWaCDwlDn+73eL8GO9mneUHwo62StN6LC4T3iceFQx95A2n9 46 | b5rA1JMLpxVskJribQzRLJJHijwWK/VyT6pdvqkVMlTI9xR5G3+72UEgLavlROuW 47 | zDx7TDtguh/t/ThM9Gd5sQthnq5yyQocZ+KHpW5TnKnhD5hTMqKBD2f1K3atpQ3l 48 | NreJq2dvXHXmZUzTyo3zXcb+ryFII1LQACmQCVGv5Y7sR5y345wHgIShwq0EIoXb 49 | ZfsSofIaGNsy9oB3kbQVbRgxbc31b0KfzxxSwQn3i/5PUfUOFVyBmILJW/FVY2IQ 50 | jVRugQKEgtFKQsoUat3xuL+n31zu3XwLo2RTOa4G+5yyvWLqBwgcqDjhV2Sx9p05 51 | oEwYjYBI3bwNL7N5qpy0e4ly38BQhlsWkRA5wJ2WXz1sSTJI1+E5XGN1xTIcxFHQ 52 | LScQoNPCNsUL+WJGwyZpgkOfx4JqF+BVKF2JcaCyU5NuDSxaSCNWM4pPYsRmej17 53 | W44qo/IyXsDZjXaMo4EmzPTVrtcRRpVwrqI5VW8O8IBGWW1RCVEeLwV4/Kd3EmCF 54 | XB4h6Iasl1aKr361MNw9+NCCv0uM/6XsSOf5l2ITEqmbx+yaIusiTxNUt4Nrat+t 55 | nDzd70BomSH/O5ereIqsDEWVTkCsvbLlDiESjMbNY3lWUaMo6UtIbV03K+1bpmqa 56 | g6UXwQR0jD5nV3MwFY2Ns6OTBkqUnZSBb6g+dYs49ttkpKVc3WtLc8mvW+HCfCTJ 57 | g2uKfuI0XpW0ML45WZrZZuKqOuiyLMMgMfnjCdwZOl27W9v76raG+cPfpA+QXDJT 58 | Z5jmxRaS8Xpd0WXzf4NH6KExQLzlvxiVK4iEtlfL5rTwBBrKwwE9+dsgtn7BQesc 59 | G7+wqtj6F48sC2Zt1ND8QgPJKmUBhqChI24nUcNISFMytTiOMHLlo09/aNoWJ2NN 60 | QBkvmRQcGNC4+XixsD/6Isi1tavzNqyOSSeNrfgpRXOWQsd+yuPChLl7c0IZPoeD 61 | kVxZfHslA5Xaa0Edjmosv4jysmMq2Aeg56dOaKxAx80osVqkhuKVp2g5Fbp34TB0 62 | YXdUGw3luRFiFF6NtADIr7PhoTlCMYDsG3uO/sezteU5CYoyUek6ewomFmIJ1P0T 63 | /dbCkaBshtyNiNHb+9vTl+tNaAOKPyoXvOFu5yAUeVSL8tgbzON7MrwMhzSDMyoJ 64 | Pi7bDd8nlJXO6YKbYXAQAfvHC/Kvdh8Rm4InyEufI/sPZ1tZ6I8NrvMTj4mNxuDX 65 | LIiP21FBLBwkljvKgfiay/4YenH+rCk9CASEefdUx3y65rJR3dtvi230YkO1eZKx 66 | SLtqb7maNmqsIBvKnHCUI0n+yYJRDW/480xZOo3ix8vf7J5bY68Eyd/QudNf7Raa 67 | CUoksQ3l3NnIlmA3pVmUTlQNbMrpERhQCeQKIL//SNELKeBvagh2JF3d1OL13nYJ 68 | T/ZZDIHbDnOGZcaRcDo3aM8SgB2XQAK3HdK1ChzvIo8Cz1kPhB9sDP1XUsHgwQ2l 69 | wZOUFba4k/HGD08CNDrquYggbVuycAGxGS6iaU7vrsdQlqUnu93+1zVrmpLjKfJq 70 | NHTcYeYnV4ja/B1s7et6IbBK3YaBybr3xAS/C1TCfEJnzOOF+kRmNYjgVYAgdbf9 71 | cs1/0y1OazSoRlEPkwsVNiFC4B+i20Prai1etJDS08hDVqgryyt7+HD2UoVmiuot 72 | ccPqJmVbRyq+aaQ7LtVkMhVM9wEs8FeEzyjHvu+g6zSbXoN3drgsav6lCbskasFG 73 | lPWWgZHwejajVqJe7ZoogVUZjl+bd83eOaCeLzghza2ev5MvO/QIzUHawteHbDGa 74 | COMmQOYJWaEM6+8W2IJ0uC7XQiKZqqNVrvM06DiK9/KiySj3CFsSQgP+19AocB1k 75 | eK8f0u3qEtLYi6gHub68Dw2PYvw7pEcfdNU+JazVqwuGGYuyrOP3u6OlQHU/qhh5 76 | 0x5RJXMgrFoVZ+nd4/0PZoylhhO78EttmZ8HvAJARiZeP0eM5lmBGvt+05nQhn15 77 | ZoPyD4qHc/5CSas2acSSLRKLUGcmivk8pUTbZnjO8ZEH5Z1dHM9oEJNajDdi6m2n 78 | s3pbZNyVrxOEoT8DOblDFnR+DNmMqycZIpyRBDRQlPq/vWHJvgSVFRnavJ2YnTW+ 79 | 9ZEWqdVmAekYI4piHGIFE3IPirT6SYlL0RhZTVSUFYE4vY2aAFYMO30HF46+RCSX 80 | T88bn1Hh7/zyLdov6Q2iFllbcpx6jWyoeJe5G1awyn5baSFQk64DOho7oFp7WSzG 81 | huDYWhCRpYSPfVNP0i72MO23ZPTjybeK+iSjochlp+9es6LMowap4Ui1DAzomBRI 82 | LP308E+xThuacf5KgUGZSLKjZ6trSMFftDMYek0CzHa2mvhBG0w3EkbwkqBVVa9u 83 | 9Tm5GPysWYmAvajv0N+t4J1wqVUZ8sMWs4WdBsKwP3eCydHJL948nQlCVkedObcV 84 | aFX1weS1ssqMU/CyMXYaPBtyu6bXpM2vY3MtBBDP6GwGXPieAzr3xYJSnK8nu/SU 85 | frpGvT04Y4mjm/acrsqgnwkG6/c+Z4BCTHIN/HHo2u9bn0KEXqhzwvOPJcbtpae9 86 | rrik5liwqAhd+leXURHxttvZ3cLFUfOnBJdFlsXiUBa9joeQwReiXIOQpdbwZoFW 87 | bDhwmWufJ1UejWqaCwaAbPZGZEF/+sxpTyHBHJnRp8hYn0a3jzVuXfPTlGhhlIUK 88 | E9vzOm9Ggp652XzpfbJdp8h4RBNY/BCdMCJr9gZCyPM6WMHc9J7eVjV0B5LrThYB 89 | j8e+NgKtzQi0/IOCb+eYpKWkbdeFpzxPKRszRBl98TA3P5Qb7Hac5syreLt2+R1d 90 | tdrf9j6VNRPcGRIhefX01J30K8ME9xKTsvIA1wQjnrNMC02MLPBg7ndrwTb4cFoL 91 | iySSichrSoX3j8gUAqRAZ8y0c4+97k8qzCUFx0xIWG/NvKTvm9i5yhgwT3tIlkr4 92 | K86kuOscb/Vzl2KKyVS/EtIqQayEcSsBGK07SJBqC5R2XZes1v07AT7I5BlfW5ms 93 | jBWOIcF/VTR0FE1G4jscwzkXLFosbQ9u+cDsMaxXk2fWaSUz/w7Mzjffl6z7eAKb 94 | LsonKgX1bKNeudzrDvNVvy6rxmOeSs3xyNtapWdWjBMnKZlgB17X/F49cZfzrNxt 95 | R0I51QkK1s0BHvK6FwZNw7OeaVtZZ9rgt1OWun3TEsFF6lWpX3Cxh0WLPclvCx2w 96 | Warr7hKu2PesAbrD1veOwY9hQ3a4VxcwAaHLuD/6+3oYRczgDfxc1/SUMvSsTeZO 97 | O9thhZDJfB/v1VTiKipbu5DPp08QyjLTEayirca1h234Z9fZ2hBa4w7q908SG2u3 98 | k59aalPrkFucjzATL0il+VizJ7E2NL/0z0FAZlJ5WYXSGyjctOquOsIGdDmpDavd 99 | eGR9Zy2I0cITUk19PGreSpKV9GAcZJSyaFNBvGE6w3uVoAvsZTKq1mmMGMFNibHO 100 | FwxmCr6d2bFtS+9nRspVvulz28KHVtmT63SD8odv/+k+1NNUiWIG7Lu01GyWfhE1 101 | e5PH/5LUIN0YHkNiBSp5qwumE2NNhfh+fOgubqZQoCCWIsBGziSNhXmnYMxxh61c 102 | j7TjHujZx9XDm3mLLGKxe9/f8cPb/ypt4cqomq+hZ5/Zc5AkkYTxK/E0Ee3iKcri 103 | RUAyxUFb+DSobQtqkN9j34fx23F2JUqQ6PNSnaTcpDktx9vLR9XBqoyMYusKxNwW 104 | ccvaeOfDYo47J7rhF+yK2fz0d4NG5UFhBFhtN6fuOIr3vWHuqrUJ8siCGhZNJAhw 105 | heyURUG1P7g6r+5Nwn2YcmpIo9BVqVlGMbfxwGAxqzKlNEY3fP6a6XVB+Cvu4Fup 106 | ieArQDS0g9IsITwoKm4Ko+kO8r2b+xJxIngKaCB95sBLg4uuU4A2Bac2p5uRwJwZ 107 | fJSeC/0rfCJqhNs9jS+PJw7uq4ytCnl0uu7Uj62yl8LiBIOePnsSQ/ubhQT6UYb1 108 | Hwh20Y7Ty7ihT0xpJFwnlF/ZFaTlnkeo0khZoyTpoYnmU0O8CXOpEGKSjDgQC8Fa 109 | X2INc7dHuPLlCuONqK753DCuipb5gJV9630xOOxlQMCaCKUvNh//qZFTPyo9CMr+ 110 | 2ZiHzP5JHwepYW3SqL5+ULXIXFQOc2FEM2Xv8IcDUXOyYmxzhb2vAGZ2YlU3FPbq 111 | i0Yq2JYr+bsUQCPatocVF0uRrRnQv2yZPtdgAQtccYs5XRqljQPb3P2pY2zM7c7b 112 | u6+SF2S+GKrgt0jSb6a6UfOuGp+puZN7joIcGFxzr/+p+lxMlkA94ecyoqaOrfRe 113 | UOz4Ks++B0Fncelosvet+lbrC44HwasGnzKARE2mVbTb5X+EeTQpmuo4eiaIXQJd 114 | KG01Z6WT2lkbBLg2AMprk65XKfELvX/9mt5nHzgizY9Q8W4DSFqc0rNlY1lmkszc 115 | 9Hz5raNCmfKMqc1EEFMel9aaA/2hp+z/E/x4GyFfG8xeg2psKYzRs4tfvHxSIFSQ 116 | hL3SjMY0XrgwfQFm+8/uCYEStb7okNyRTAbdiEqmdPLSmg4LvVAsVc1y0ZsiVxrq 117 | rC1ysuUmU8UWJkeriQqdkQhUpb8qcN/B4NR1P85jbaBdbWCUeQ+pvA78Xfxlasa/ 118 | ZXWqyDrWbhxYr3/lpqPzhhE3Sl00PKRu+arIogkeGL7niSHyPlWZrbqtPqnAlF6M 119 | dLvTEYWgidpxHq1w4/qvPc2uZ2wiGhcz3Pw7N0VMbzyQc3puPqTsPVvv9CKuvkb1 120 | lWpV5ACzK90nITmyht1pBJ4yoSXHv+gQSsmOs86bvT3fZOWPPQDXCurOr781xTxk 121 | nezs99Q5B8KpI5Hci7mDWVfOqsLKzao/to6KtGBc38aHt8J+zRwpOeXx4QZk2LIB 122 | VlgKOKQFu9lEPK6u9G+rQPi6o54iq15gSTmPBCVKCmZ8amY2qafQMqebt9laHANA 123 | 5qVZBZzEhYv2QeoYJA1Gfec95Ui+8QKMk+mC+xNcMcDJA0FX9Jic2pe5jLJf0EWL 124 | z0nkb/+g+pdT9VSzOtlcl98X8MDCablSl9Qm40pKq1tH5B2+fTHvKvdqPJRpGqJo 125 | sXLtRQpDrGPdvomc2PnUyn2RsDCQ0TtjD9+RbFNPTMlEH4ZehtiGpgPaEsFeF2SN 126 | AfypADXRPX7N3B1bD+jHgDaYDVvixISb5lrj1ufwsnROGuu9gzjDfeG64a2iaeKT 127 | +FcOHrpeeJr9z/nXc87U1PjCk1tCdp+NEZ3MsbsbtaoN3Vy4TsO6pf7AUSQImaD0 128 | JHPt3BnATzV7nMHvk7V2LX/QEwRGQRo6vIHo6HR7TafbDHuaCVPd6XV0MFb8P3FT 129 | Crl55z2kt8/b6dXLiGnI7loBZDSF5FhY+8apAF1FPl+2wHAUINLpxDpoYcvcSLcL 130 | jsE2BEIoonJvx3pE5pStH1ptpIxGTClvLhp/V9saVnGHg7LCAovgxw7QKmf+OFyD 131 | rbkSr6wXLarAaYMQI3wBbm9etUaBYnFFd2rrGCzmvqS819gsncLpSZzyHYOzIGiz 132 | mjFfeuE5v7n4ni6HkIOu9OAPWcTQMlRe5bMMdhbNNvDvpi0CeKz9GurkK5rOjCCA 133 | rJz8YG2ACxNw1AQ2grAp3ZK3aA0Il7OVjQsZ0lb676UxwMDxdSyM9JKGuPrDOUoE 134 | ykcD3ryAGxuEyMDEUl+hOIXEtda9HAn+hehhfcrHP9Wo2zq/Jblph6/cVm8BP06N 135 | hrco61qt/+OkpuxV8Gf+Ds1ERwayXQrwI0cH32udaE7OjxGI+W2xb6LpYifXJxje 136 | 0WCBnsDHLnL7HE1Ct1amv6xDdzyX15ogvugs7nmo9J2C80eU5qWQ3h8Qq0dl8KvX 137 | RgIZ2cpBL+JwPAp2hxWNkqj6p3GwYFu/zVfThhJYF/Z9b1XL0nHfyeFYk2SYzwtX 138 | mHJSJSGTF6XlycG626kkjvjE8pdxcCN4qWEZFJ1HCFtRLsPOADTxzgYOfeCcsCMR 139 | hceKFzBvRCBG2el8DVdUaCNb+ANyeHuW3NLfiMA4jXN6hE8zq7GAf/GbNeKwbPIs 140 | 1Ia4c/4Vu0j1mdAGdf86lG9Hk3rK6a9Vc4ZhJ10zfP65Iz1ODfAzcUQNiWAW1o6n 141 | fY0AmjI98xGzurtRUcTdyrFj/VXZYjcLHvROlnAJGpFTyE9Ng40aPataZLNavFdp 142 | HOhvimqd6QlpzHYMXp/Byuf0LtMjhTvpuPry58xk344dYO1wcPALKFuPt6jQ6Bqb 143 | WCe5HosfbgGEZRnD0KBPHOPNTUTfoCZX+j3elN/AmqleF7/LfO044rmNhcpiIc0D 144 | 4UjsgQghAb8uqjlikKiMaUdYQkGv9Gu2WGKu1oLoEnqyOli2RpqsWUOnX8RczQ1P 145 | eQLXN5WqsEdGQ+rEkLZz8CZ7iyIlclEC/kUZ35Hd7LJP+xnIcrLqjDAkcjCOdxLJ 146 | uKGihNaL/8r/7DnYvEbv0P+LdSMX23VWppUxialIgyyG6LkLQnfe4HhwobAxQh6c 147 | S+AfrJsqZOj0GtlVxI5Vsjtpqp3C+n7TX8CXL8b5JmJlR0/1YjqUI9oMym6XU3rD 148 | JXLPyAWVqW6+ZFkWgOAoiks1Z4L+TUik6NGfVc+/Mq+y4KTzILskeZFlcIQO3Dqw 149 | vAfOSEbXNBygeR/FCH6H0Vxtq82/724ypv+ea4iDMC+E1TGvGTzSGHwlphtGYDfD 150 | ESnoiBjO1cf5EgNN0hL7GGssMzZSFuehgF6WTMu6jEyAM3BSUiMBzytZFED/5CGR 151 | yHy+GYVSWIZ4pBV/8qqiPflVlvYNv7ZEecINTlUy/FmmHSTL03XJz7Sor1BEiKCp 152 | easN2xSxG5RA2UUwghIlf762Lybf5JMHaot2yHGqY3FbvTtSQpnOcYV86FzZWWLM 153 | KzGde2n7ZjvQwC8E4KxG1QTTxCmkRNPMUEAhTXyFZS4J2dkyJuA7sjPY2DeVDU1v 154 | Mb+rtrgFIgeFVLwirP5+NGOdAlqjEKth8W46JU1vXw6WLqxchEhFNODYXN2YZj3T 155 | nJaoaVr3e5ssVSbe5DpfNqFzVtLwaLWY/Z9SQF/XLkVOAllWehUKbxOPRpLhCVYJ 156 | 43Azes9plpqIfsrJrnSE1AEH1xbRfOuAmC39yaiVdQ2VmQu7WZI4DYeBtNNJBKnd 157 | kIV5q7Gzli0DCLglcR3PqqtzhCmR8UzeWOEPsyGSjXY6AuI4jW4Thv/XSre5iSGU 158 | 6Ji4RuLE48wyqzO6BUutQUP7JuPDPDDbAHudeZ/m+oF0nEoEgjGPiTj+F/RoYA9m 159 | 1H9sL8nfoemn+9mrtro+GZi/Zue97haSsGo/uG3YkHzPaP3t4ghanjVGtSA6ITaK 160 | OP9SRFrUYBpV5NRV6PnqhGZ+sxheKE9tmOgjSrpwG5+A1KLHCIzfoLCTC7ET+kBn 161 | ex7Usl86H1uWt8el18yjjHp5e64JPX1ntDfWkWWFWkV/1oTGZWbucU31mFBcTRR8 162 | Rie+oOpishY2zj7Fp5esBRXVHo0VqRQXe3w/lw5j+Rnb4iaf/4zpk5YeFVc9uHOr 163 | OxYZoHgJrshOpfSfVoNFfsehIBLHVdzEbe16MzkxQcm1Y4IP43H37fK3NTf08yyi 164 | Nn0Is6eEAPr5kZ45T5Tl7xqMWgu6vpkhrmH0vv/VkpXUoItcHigqgxAC93KCPvHh 165 | WcdJ/k1u9JmmmLuCEsp7ERHG8178AIuJSglBhp6psDSfkDEqxyqPYyFTbnFlD2Xt 166 | TnUrFOYT0E8csA5VNYo6OP1cNlPakWLVdb0qUqBrjJgJqwhDaRNyHOWcUZJx1oAL 167 | bLLB266tYw9flIqlMmJBKXanmzU518fgbkrgNcxuxCNumSHIq0gXg57fQydjYiic 168 | 1F4VtnWNcoUn9zUQkn8f/c8AoiMp3OuisTqcyj3dnTBDq5lypHwKzrCyJnM6nPQR 169 | qaJ9bfBQKL6QZ94zJeG87XeLBNSKDdqBhPbATLOCn3bI0IbeaIz8wy/87LGSjdwe 170 | hOI39iOsbwSn4fg23xpCD5yLM5nRLfokN/4WtDAlcJgSTuFaXL0p8fcinUoKezEC 171 | K16oIIlHkjhCmtFktKFSJ1YaL+9YgiJbKhKof22uNlJZzL5bBwEwFEtax9OI3e4c 172 | GBLUX12k7SFYg/8G3EzDtgbw5b6sishMhbEIC9QYzSkcMNxavz16FvKPkCIyjjXN 173 | ubJv8Z2Z6uEW+DQ4NzRZzDgN9u0Aec15HeWmDocEcoAUq7SISFK/6Wu48P2omAXj 174 | bVA8gpXB2nRUQvFvOoHzD07eJNbTCYiJ2deFo3p1YmpbOVYjEJShUKnO5MsjWmsl 175 | IIWmsS6h8HMaqGOXW4Z8rgnaYA7wTAJ9BWxFIrMuoZnwES1KKd7Vy+Kdhrwalh4j 176 | HrfUsN6FrAp4b5TxV+cyclxE0SM5Ikvmwsvyy4tovIMs4aMTe64qG+iHDkpIazCy 177 | 6pzqDfudlsyWHOyQLqJeT2iwKTEyFWAR0BL2INJeBa47h3fHJ7gWSofv47iM7vMv 178 | 06K5ylDNF5m7BaaUeVA9Au/RMqlJG9BvW+cEXhrGVapgQsudJ/X8PX1hLQsn1Ji/ 179 | HA6b72AMtRXUIN1oeLgqk18jMYpcZThkY2dn7MqEVOyYtiyP7i55yKZvK0/4VBYQ 180 | jLJeM1G9YsEFkA6HBfaEzFzCf1XTCmwmDiizpMR1YHtktpi6ho3uc8FgpGzmgE0c 181 | IG1FtNinav+bVu0ftTePg+od6ljWt7G456/s7fUEIiTYFe0MlGIpE/quRVYvHBkA 182 | 16Triy1y03Hgcd0+TkXTSHVNr50kGSpv3b7wUc8AJhFWEt3/HJ/u8zQ4Oiy3pL+5 183 | G/+LK3XIhoVD1cMdwscBkMDp67K8BL91phLQDcK/e22bSErrIlLt4n3TaVP6Ysmq 184 | 2lic96MKzScr4paWfdQl1Ox7BUUzb+0HUIuP8w1UYyXrLOM2LzLRSSDIClWLIV7i 185 | V6F/tCHumNlbg8WJ+n7DXkM97lWJkpgcY28L8WeGCQEu75+0HSvcsKhaXtXzFQHz 186 | W+ksGKW3IyxSPRKNMtxnR0GTwBQnf91fz+AAbu5vrT4zw7s4LJ5soCCaJdDEVVlx 187 | bXB8ii8nAuwYb/chgo1a4pXLA/7e19OED65j5/6Xo8P3vH1qZx1Dd2tolk6b8ZyB 188 | MTzmhz+5/hP82MP0W3ux62dpvZBfAyB4nDcmg37QHQzUehYm7pkzQkKH3XGre5PD 189 | 2zZ/85uwJcciHaA4EFEktqPFb8giEy3Gg5RuBmtO8G2kPRTKWkZjPsx785B3Naj0 190 | pmg+LhzYZUdrZDeqrkkCeIw8h0jVYVb57Es2qNvUo3YEUEiBwxgYyWIec9gO2AER 191 | IJbaOmgPZCe7W53lUbLN6UqcqOuY7Rd0V1aq+mnXIXn6l8mw+uQzXDcfVbCbddBI 192 | 7wx4esnGtM7UQjWVu1yF45n7RnuWYy4cmRwFLHnqGWXCPIais1EYI07D/0liu2Fc 193 | wOjEHAlqms9eetptoHRwlX4ZAjAa3uzKZ8Mg/MP5+EEOy5Hic04V/Pv7fU4/IK/K 194 | kouQycHJtIhBSqM8cDtrGLGiBs35fp1M7WflRKD6XvQu7GeDdqxAnleB50iMPMr0 195 | RFBFlNSX3V8c3x+NDTqphc24HCTS4w5LGtSyRh+gFrEGG8DymEG3r074LrlEQZg4 196 | U1YnFjoHZcEv1cHgx9Qt734X5vgAoiv9j4EN6+yr9C0Rn/5ijpjT0mKHDUjs1Cx7 197 | upNa3SuXFJaUOQ9i6r2KS8+1CrSq6bDp2xdAVAlX6Lj0+7LcrgVeH7R676k5u8Ga 198 | ziMYGRvreJQkQzyVbBLzKlY1DNn9W2gvK1SM2oB7jBAIE92oj+8+uwR0/UhCnwDI 199 | HoIIcvg9QgrqwNdI/JzvClbcKoEOXDvUd4uUs4QtYqjlsAY7OEXLfh6rAjsxPI9T 200 | b1PZ0DryI04wCClZgSX3Eteb0ViPqL5I7olrdQGT67Na3hLvaIwv1C8tcyYcOTPu 201 | 7go9+OgTwCknOkZ4uW/fYiWYjCgZtGK3ONssG0nOpt0hdS9J8Oi3dlz7YiSLu9qd 202 | w2zxer7nh8L8Q288/jQPcjhZS4SOBdLI55wWPQifMnmQxu9bisFHbyjXJvwbc5Qy 203 | B4IL4Doy2aEDTn/9u3gvnYSyOJz0uW3ccgiXGO2JfeVXHnS+c/d6XFQZ/YFApq+H 204 | y+NeUmS4Il9g2pL1iS70Qg== 205 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # imos-bin 2 | bin directory used by imos. 3 | 4 | This repository is tested on drone.io (https://drone.io/github.com/imos/bin). 5 | 6 | ## How to Install/Update 7 | Install this repository to /usr/local/imos/bin and configure ${PATH}: 8 | ```sh 9 | curl https://raw.githubusercontent.com/imos/bin/master/tool/setup | sudo bash 10 | ``` 11 | 12 | Note that you can run the command multiple times safely. 13 | 14 | ## Commands 15 | * [imofs](#imofs) ... imofs is a command-line tool to backup/restore files and services. 16 | * [imos-archive](#imos-archive) ... imos-archive archives/dearchives files. 17 | * [imos-aws](#imos-aws) ... imos-aws calls aws with imos credentails. 18 | * [imos-aws-credentials](#imos-aws-credentials) ... imos-aws-credentials shows AWS credentials. 19 | * [imos-bashrc](#imos-bashrc) ... imos-bashrc is a bash init script. 20 | * [imos-crypt](#imos-crypt) ... imos-crypt encrypts/decrypts files. 21 | * [imos-diskdiff](#imos-diskdiff) ... imos-diskdiff compares disk volumes. 22 | * [imos-docker-run](#imos-docker-run) ... imos-archive archives/dearchives files. 23 | * [imos-install](#imos-install) ... imos-install is a script to configure imos-bin. 24 | * [imos-package](#imos-package) ... imos-package manages binary packages. 25 | * [imos-passgen](#imos-passgen) ... imos-passgen is a password generator. 26 | * [imos-pokemon](#imos-pokemon) ... imos-pokemon prints the current volume's Pokémon name/ID. 27 | * [imos-start](#imos-start) ... imos-start is a start-up script. 28 | * [imos-stat](#imos-stat) ... imos-stat is a command for stat's compatibility. 29 | * [imos-variables](#imos-variables) ... imos-variables is a source file initializing variables. 30 | * [imosh](#imosh) ... imosh is a utility library for BASH. 31 | * [ssh-for-git](#ssh-for-git) ... ssh-for-git runs ssh especially for git. 32 | * [stelnet](#stelnet) ... stelnet is an SSL client. 33 | 34 | ----- 35 | ### imofs 36 | imofs is a command-line tool to backup/restore files and services. 37 | 38 | This command is useful especially for programming contests giving a disk 39 | image, and imofs backs up the initial state and restores almost everything. 40 | Directories to backup/restore are specified as TARGETS, and services to 41 | backup/restore are specified as SERVICES. 42 | 43 | 44 | #### Usage 45 | ```sh 46 | imofs [options] command 47 | ``` 48 | 49 | 50 | 51 | #### Command 52 | * restore 53 | * Restore files and services from FLAGS_backup_directory to 54 | FLAGS_target_directory. 55 | * backup 56 | * Backup files and services from FLAGS_target_directory to 57 | FLAGS_backup_directory. 58 | * deploy 59 | * Restore files and services from FLAGS_backup_directory to 60 | FLAGS_target_directory, and deploy files from FLAGS_source_directory to 61 | FLAGS_target_directory. 62 | 63 | 64 | #### Configurations 65 | * /backup/imofs/services 66 | * Services to backup/restore. This file is updated when you call 67 | backup command. 68 | * /backup/imofs/targets 69 | * Directories to backup/restore. A directory should not end with "/". 70 | 71 | 72 | 73 | #### Options 74 | 75 | ##### main options 76 | * --backup_directory='/backup' 77 | * Directory to store backups. 78 | * --dry_run=false 79 | * Show what will be copied. (Alias: --n) 80 | * --source_directory='.' 81 | * Source directory to deploy from. 82 | * --target_directory='' 83 | * Directory to backup/restore. 84 | 85 | ----- 86 | ### imos-archive 87 | imos-archive archives/dearchives files. 88 | 89 | 90 | #### Usage 91 | ```sh 92 | imos-crypt [options] [input output] ... 93 | ``` 94 | 95 | 96 | 97 | 98 | #### Options 99 | 100 | ##### main options 101 | * --command='' 102 | * Command to run. 103 | * --extra_arguments=true 104 | * Append extra arguments. 105 | * --file_size=0 106 | * Maximum size of a generated archive in bytes. 107 | * --output='' 108 | * Path to output the archive to. 109 | * --pass_working_directory=false 110 | * Pass a caller's working directory to the program. Otherwise, a callee's working directory is under the package. 111 | 112 | ----- 113 | ### imos-aws 114 | imos-aws calls aws with imos credentails. 115 | 116 | ### Usage 117 | ```sh 118 | imos-aws options... 119 | ``` 120 | 121 | ----- 122 | ### imos-aws-credentials 123 | imos-aws-credentials shows AWS credentials. 124 | 125 | 126 | #### Usage 127 | ```sh 128 | imos-aws-credentials 129 | ``` 130 | 131 | 132 | 133 | 134 | #### Options 135 | 136 | ----- 137 | ### imos-bashrc 138 | imos-bashrc is a bash init script. 139 | 140 | imos-bashrc initializes PS1 and sources init files. 141 | 142 | ### Usage 143 | ```sh 144 | source /usr/local/imos/bin/imos-bashrc 145 | ``` 146 | 147 | ----- 148 | ### imos-crypt 149 | imos-crypt encrypts/decrypts files. 150 | 151 | imos-crypt encrypts/decrypts files using an embedded password by default. 152 | The embedded password is encrypted with a master password that should be 153 | given by imos. The installed_password flag makes imos-crypt use the 154 | installed password, which should be installed by imos-start. The password 155 | flag forces the script to use the password given by the flag. This script 156 | uses AES-256-CBC to encrypt/decrypt. 157 | 158 | 159 | #### Usage 160 | ```sh 161 | imos-crypt [options] [input output] ... 162 | ``` 163 | 164 | 165 | 166 | 167 | #### Options 168 | 169 | ##### main options 170 | * --decrypt=false 171 | * Decrypt files. 172 | * --encrypt=false 173 | * Encrypt files. 174 | * --installed_password=false 175 | * Use the installed password instead. 176 | * --password='' 177 | * Password to use instead. 178 | 179 | ----- 180 | ### imos-diskdiff 181 | imos-diskdiff compares disk volumes. 182 | 183 | imos-diskdiff compares disk volumes and list different files. 184 | 185 | 186 | #### Usage 187 | ```sh 188 | imos-diskdiff 189 | ``` 190 | 191 | 192 | 193 | 194 | #### Options 195 | 196 | ##### main options 197 | * --source='/Volumes/Ditto/' 198 | * Source volume. 199 | * --target='/' 200 | * Target volume. 201 | 202 | ----- 203 | ### imos-docker-run 204 | imos-archive archives/dearchives files. 205 | 206 | 207 | #### Usage 208 | ```sh 209 | imos-crypt [options] [input output] ... 210 | ``` 211 | 212 | 213 | 214 | 215 | #### Options 216 | 217 | ##### main options 218 | * --command='su -' 219 | * Command to run inside docker. 220 | * --container='ephemeral' 221 | * Container image to use. 222 | * --interactive=true 223 | * Enable interactive mode. 224 | * --rebuild=false 225 | * Re-build a base image. 226 | * --volume='/:/host' 227 | * Volumes to attach. 228 | 229 | ----- 230 | ### imos-install 231 | imos-install is a script to configure imos-bin. 232 | 233 | imos-install configures user directories and installs an installed 234 | password if necessary. This script requires the root privilege. 235 | 236 | 237 | #### Usage 238 | ```sh 239 | imos-install 240 | ``` 241 | 242 | 243 | 244 | 245 | #### Options 246 | 247 | ----- 248 | ### imos-package 249 | imos-package manages binary packages. 250 | 251 | 252 | #### Usage 253 | ```sh 254 | imos-package command [options...] 255 | ``` 256 | 257 | 258 | 259 | #### Command 260 | * help 261 | * Shows help. 262 | * create 263 | * Archives files under the current directory with a command, and uploads 264 | the archive as a binary package. 265 | * run 266 | * Fetches a package and runs it. 267 | * archive 268 | * Archives files under the current directory with a command. 269 | * upload 270 | * Uploads a file to the cloud server. 271 | * download 272 | * Downloads a file from the cloud server. 273 | 274 | 275 | #### `create` command 276 | * `imos-package create ./a.out --foo --bar` 277 | * Archives files under the current directory with a command: 278 | `./a.out --foo --bar`, and uploads the archive. 279 | * `imos-package create --lifetime=permanent bash foo.sh` 280 | * Archives files under the current directory with a command: 281 | `bash foo.sh`, and uploads the archive as a permanent archive. 282 | 283 | 284 | #### `upload` command 285 | Uploads a file to the cloud server. If the file's size is bigger than 1MB 286 | (specified by --fragment_size flag), the file will be split into fragments 287 | so as to speed up download/upload processes. 288 | * `imos-package upload ./foo` 289 | * Uploads `./foo` to the cloud server, and prints its file ID. 290 | 291 | 292 | #### `download` command 293 | Downloads a file from the cloud server and save it to the path specified by 294 | --output flag. 295 | 296 | 297 | #### Dependencies 298 | * create 299 | * Depends on archive and upload. 300 | * run 301 | * Depends on download. 302 | 303 | 304 | 305 | #### Options 306 | 307 | ##### main options 308 | * --bucket='imos-package' 309 | * Bucket name. 310 | * --host='s3-ap-northeast-1.amazonaws.com' 311 | * Host name. 312 | * --output='' 313 | * Path to output a file on. archive and download commands use this flag. 314 | * --threads=64 315 | * The number of threads to upload with. 316 | 317 | ##### ARCHIVE options 318 | * --command='' 319 | * Command to run. 320 | * --extra_arguments=true 321 | * Append extra arguments. 322 | * --file_size=30000000 323 | * Maximum size of a generated archive in bytes. 324 | * --pass_working_directory=false 325 | * Pass a caller's working directory to the program. Otherwise, a callee's working directory is under the package. 326 | 327 | ##### DOWNLOAD options 328 | * --fragments_directory='/var/tmp/fragments' 329 | * Cache directory. 330 | 331 | ##### UPLOAD options 332 | * --alias='' 333 | * Alias name of the package. 334 | * --fragment_size=1048576 335 | * Limit of fragment size. 336 | * --lifetime='ephemeral' 337 | * Lifetime of fragments. ephemeral's lifetime is 7 days. Re-uploading the same fragments will prolong their lifetime. 338 | 339 | ----- 340 | ### imos-passgen 341 | imos-passgen is a password generator. 342 | 343 | imos-passgen generates a password based on a master password and a domain, and 344 | copies a password to the clip board if it is not from SSH. 345 | 346 | Web interface version is available at: http://imoz.jp/password.html 347 | 348 | ### Usage 349 | ```sh 350 | imos-passgen 351 | ``` 352 | 353 | ----- 354 | ### imos-pokemon 355 | imos-pokemon prints the current volume's Pokémon name/ID. 356 | 357 | For Mac OSX, this command uses a disk volume's name to determine Pokémon 358 | ID. For linux OS, this command uses a primary IP address. 359 | 360 | 361 | #### Usage 362 | ```sh 363 | imos-pokemon [options] 364 | ``` 365 | 366 | 367 | 368 | #### Files 369 | * /tmp/POKEMON_NAME 370 | * Cache file to store a Pokémon name. 371 | * ./data/pokemon.txt 372 | * Mapping from Pokémon ID to Pokémon name. 373 | 374 | 375 | 376 | #### Options 377 | 378 | ##### main options 379 | * --cache=true 380 | * Use cache. 381 | * --cache_file='/tmp/POKEMON_NAME' 382 | * File to cache a Pokémon name 383 | * --id=false 384 | * Show pokemon ID instead. 385 | * --pokemon_error_level='ERROR' 386 | * Error level used when resolution partially fails. 387 | * --pokemon_mapping_file='./data/pokemon.txt' 388 | * File to map a Pokémon ID to a Pokémon name. 389 | 390 | ----- 391 | ### imos-start 392 | imos-start is a start-up script. 393 | 394 | imos-start is a script that should run just after boot programs. This script 395 | requires the root privilege. 396 | 397 | 398 | #### Usage 399 | ```sh 400 | imos-start 401 | ``` 402 | 403 | 404 | 405 | 406 | #### Options 407 | 408 | ----- 409 | ### imos-stat 410 | imos-stat is a command for stat's compatibility. 411 | 412 | imos-stat displays information about a file. GNU's stat and BSD's stat use 413 | different format, so imos-stat converts GNU's format to BSD's format if 414 | necessary. 415 | 416 | 417 | #### Usage 418 | ```sh 419 | imos-stat --format= 420 | ``` 421 | 422 | 423 | 424 | 425 | #### Options 426 | 427 | ##### main options 428 | * --format='' 429 | * Format. 430 | * --use_native_uname=true 431 | * Use native UNAME instead. 432 | 433 | ----- 434 | ### imos-variables 435 | imos-variables is a source file initializing variables. 436 | 437 | imos-variables initializes variables. Thus, imos-variables must be called 438 | using the source built-in command. This command prepends the bin directory 439 | where imos-variables exists to the PATH environment vairable. 440 | 441 | ### Usage 442 | ```sh 443 | source imos-variables 444 | ``` 445 | 446 | 447 | ### Variables 448 | * IMOS_STORAGE 449 | * IMOS_STORAGE is a persistent directory's absolute path. Its default 450 | value is /Volumes/Arceus in Mac OSX and /storage in other operating 451 | systems. 452 | * IMOS_RESOURCE 453 | * IMOS_RESOURCE is a directory containing resource files for imos scripts. 454 | Its default value is an absolute path that is ../resource from the 455 | directory where the imos-variables command exists. 456 | 457 | ----- 458 | ### imosh 459 | imosh is a utility library for BASH. 460 | 461 | For more details, see https://github.com/imos/imosh 462 | 463 | ----- 464 | ### ssh-for-git 465 | ssh-for-git runs ssh especially for git. 466 | 467 | ssh-for-git uses the GIT_SSH_IDENTITY environment variable to specify an 468 | identity file. Plus, it specifies StrictHostKeyChecking=no so that git is 469 | not blocked for known_hosts registration. 470 | 471 | ### Usage 472 | ```sh 473 | GIT_SSH=ssh-for-git GIT_SSH_IDENTITY= \ 474 | git 475 | ``` 476 | 477 | 478 | ### Caveat 479 | GIT_SSH_IDENTITY must be specified as an environment variable. 480 | 481 | ----- 482 | ### stelnet 483 | stelnet is an SSL client. 484 | 485 | stelnet is used to communicate wit hanother host using the SSL protocol. 486 | stelnet requires the openssl command. If port is not specified, 443 (HTTPS) 487 | is used. 488 | 489 | 490 | #### Usage 491 | ```sh 492 | stelnet host [port] 493 | ``` 494 | 495 | 496 | 497 | 498 | #### Options 499 | 500 | ##### main options 501 | * --crlf=true 502 | * Use CRLF instead for new lines. 503 | * --debug=false 504 | * Show debug information. 505 | * --ssl3=true 506 | * Use SSL3. 507 | 508 | 509 | -------------------------------------------------------------------------------- /imos-package: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # imos-package manages binary packages. 3 | # 4 | # Usage: 5 | # imos-package command [options...] 6 | # 7 | # Command: 8 | # - help 9 | # Shows help. 10 | # - create 11 | # Archives files under the current directory with a command, and uploads 12 | # the archive as a binary package. 13 | # - run 14 | # Fetches a package and runs it. 15 | # - archive 16 | # Archives files under the current directory with a command. 17 | # - upload 18 | # Uploads a file to the cloud server. 19 | # - download 20 | # Downloads a file from the cloud server. 21 | # 22 | # `create` command: 23 | # - `imos-package create ./a.out --foo --bar` 24 | # Archives files under the current directory with a command: 25 | # `./a.out --foo --bar`, and uploads the archive. 26 | # - `imos-package create --lifetime=permanent bash foo.sh` 27 | # Archives files under the current directory with a command: 28 | # `bash foo.sh`, and uploads the archive as a permanent archive. 29 | # 30 | # `upload` command: 31 | # Uploads a file to the cloud server. If the file's size is bigger than 1MB 32 | # (specified by --fragment_size flag), the file will be split into fragments 33 | # so as to speed up download/upload processes. 34 | # - `imos-package upload ./foo` 35 | # Uploads `./foo` to the cloud server, and prints its file ID. 36 | # 37 | # `download` command: 38 | # Downloads a file from the cloud server and save it to the path specified by 39 | # --output flag. 40 | # 41 | # Dependencies: 42 | # - create 43 | # Depends on archive and upload. 44 | # - run 45 | # Depends on download. 46 | 47 | source "$(dirname "${BASH_SOURCE}")/imos-variables" || exit 1 48 | # Common flags. 49 | DEFINE_string bucket 'imos-package' 'Bucket name.' 50 | DEFINE_string host 's3-ap-northeast-1.amazonaws.com' 'Host name.' 51 | DEFINE_int threads 64 'The number of threads to upload with.' 52 | DEFINE_string output '' \ 53 | 'Path to output a file on. archive and download commands use this flag.' 54 | 55 | # Archive options. 56 | DEFINE_int --group='archive' file_size 30000000 \ 57 | 'Maximum size of a generated archive in bytes.' 58 | DEFINE_bool --group='archive' pass_working_directory false \ 59 | "Pass a caller's working directory to the program." \ 60 | "Otherwise, a callee's working directory is under the package." 61 | DEFINE_string --group='archive' command '' 'Command to run.' 62 | DEFINE_bool --group='archive' extra_arguments true 'Append extra arguments.' 63 | 64 | # Upload options. 65 | DEFINE_string --group='upload' alias '' \ 66 | 'Alias name of the package.' 67 | DEFINE_int --group='upload' fragment_size "$(( 1024 * 1024 ))" \ 68 | 'Limit of fragment size.' 69 | DEFINE_string --group='upload' lifetime 'ephemeral' \ 70 | "Lifetime of fragments. ephemeral's lifetime is 7 days." \ 71 | 'Re-uploading the same fragments will prolong their lifetime.' 72 | 73 | # Download options. 74 | DEFINE_string --group='download' fragments_directory '/var/tmp/fragments' \ 75 | 'Cache directory.' 76 | 77 | ################################################################################ 78 | # archive command 79 | ################################################################################ 80 | create_tar() { 81 | if [ "$#" -eq 1 ]; then 82 | local package_directory="${TMPDIR}/package" 83 | mkdir -p "${package_directory}" 84 | ln -s "$(pwd)" "${package_directory}/bin" 85 | if [ "${FLAGS_command}" != '' ]; then 86 | local command_file="${package_directory}/COMMAND" 87 | { 88 | sub::println '#!/bin/bash' 89 | if (( ! FLAGS_pass_working_directory )); then 90 | sub::println 'cd "$(dirname "${BASH_SOURCE}")/bin"' 91 | fi 92 | if (( FLAGS_extra_arguments )); then 93 | sub::println "${FLAGS_command} \"\$@\"" 94 | else 95 | sub::println "${FLAGS_command}" 96 | fi 97 | } > "${command_file}" 98 | chmod +x "${command_file}" 99 | fi 100 | pushd "${package_directory}" >/dev/null 101 | local line='' 102 | local tar_out="${TMPDIR}/tar_out" 103 | local tar_log="${TMPDIR}/tar_log" 104 | mkfifo "${tar_out}" "${tar_log}" 105 | tar zcvfh - * >"${tar_out}" 2>"${tar_log}" & 106 | local tar_pid="$!" 107 | while IFS= read -r line; do 108 | LOG INFO "Archiving: ${line}" 109 | done <"${tar_log}" & 110 | local log_pid="$!" 111 | if (( FLAGS_file_size <= 0 )); then 112 | cat <"${tar_out}" >"${1}" 113 | else 114 | head -c "$(( FLAGS_file_size + 1 ))" <"${tar_out}" > "${1}" 115 | kill "${log_pid}" 2>/dev/null || true 116 | kill "${tar_pid}" 2>/dev/null || true 117 | local file_size="$(wc -c < "${1}")" 118 | if (( file_size > FLAGS_file_size )); then 119 | ls -lAR . >&2 120 | LOG FATAL "File size ($file_size bytes) is bigger than limit:" \ 121 | "--file_size=${FLAGS_file_size}" 122 | fi 123 | fi 124 | popd >/dev/null 125 | else 126 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 127 | fi 128 | } 129 | 130 | archive_tar() { 131 | if [ "$#" -eq 2 ]; then 132 | local command='#!/bin/bash 133 | set -e -u 134 | package_directory="${TMPDIR:-/tmp}/package.${RANDOM}.${RANDOM}.${RANDOM}.${RANDOM}.${RANDOM}/package" 135 | mkdir -p "${package_directory}" 136 | cat "${BASH_SOURCE}" | tail -n +NUM_OF_LINES | { 137 | pushd "${package_directory}" >/dev/null 138 | tar zxvf - >/dev/null 2>/dev/null 139 | popd >/dev/null 140 | } 141 | "${package_directory}/COMMAND" "$@" 142 | exit 0 143 | ' 144 | local command_lines=() 145 | func::explode command_lines $'\n' "${command}" 146 | func::str_replace command 'NUM_OF_LINES' "${#command_lines[*]}" 147 | { 148 | sub::print "${command}" 149 | cat "${1}" 150 | } > "${2}" 151 | chmod +x "${2}" 152 | else 153 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 154 | fi 155 | } 156 | 157 | imos-package::archive() { 158 | CHECK [ "${FLAGS_output}" != '' ] 159 | if [ "$#" -eq 0 ]; then 160 | if [ "${FLAGS_command}" = '' ]; then 161 | LOG FATAL '--command flag or arguments must be specified.' 162 | fi 163 | else 164 | if [ "${FLAGS_command}" != '' ]; then 165 | LOG FATAL 'Both of --command flag and arguments are specified.' 166 | fi 167 | local arguments=("$@") 168 | func::array_map arguments INPLACE func::escapeshellarg 169 | func::implode FLAGS_command ' ' arguments 170 | fi 171 | local tar_file="${TMPDIR}/package.tar" 172 | local sar_file="${TMPDIR}/package.sar" 173 | create_tar "${tar_file}" 174 | archive_tar "${tar_file}" "${sar_file}" 175 | cp "${sar_file}" "${FLAGS_output}" 176 | } 177 | 178 | ################################################################################ 179 | # create command 180 | ################################################################################ 181 | imos-package::create() { 182 | if [ "$#" -ge 1 ]; then 183 | local archive_file="${TMPDIR}/archive" 184 | FLAGS_output="${archive_file}" imos-package::archive "$@" 185 | imos-package::upload "${archive_file}" 186 | else 187 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 188 | fi 189 | } 190 | 191 | ################################################################################ 192 | # run command 193 | ################################################################################ 194 | imos-package::run() { 195 | if [ "$#" -ge 1 ]; then 196 | local package_binary="${TMPDIR}/${1}" 197 | FLAGS_output="${package_binary}" imos-package::download "${1}" 198 | chmod +x "${package_binary}" 199 | shift 200 | "${package_binary}" "$@" & 201 | local pid="$!" 202 | local exit_status=0 203 | wait "${pid}" || exit_status="$?" 204 | if [ "${exit_status}" -lt 128 ]; then 205 | exit "${exit_status}" 206 | else 207 | kill -"$(( exit_status - 128 ))" "${IMOSH_ROOT_PID}" 208 | fi 209 | else 210 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 211 | fi 212 | } 213 | 214 | ################################################################################ 215 | # upload command 216 | ################################################################################ 217 | rfc2date() { 218 | if [ "$#" -eq 1 ]; then 219 | case "${UNAME}" in 220 | 'Linux') 221 | LC_ALL=en_US.UTF-8 TZ=GMT date --date="${1}" +'%Y-%m-%d';; 222 | *) 223 | LC_ALL=en_US.UTF-8 TZ=GMT \ 224 | date -j -f '%a, %d %b %Y %H:%M:%S %Z' "${1}" +'%Y-%m-%d';; 225 | esac 226 | else 227 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 228 | fi 229 | } 230 | 231 | mdate() { 232 | if [ "$#" -eq 1 ]; then 233 | local mtime="$( 234 | curl --silent --fail --head \ 235 | "https://${FLAGS_host}/${FLAGS_bucket}/${1}" | \ 236 | grep -i last-modified | cut -d: -f2-)" 237 | func::trim mtime 238 | if [ "${mtime}" != '' ]; then 239 | rfc2date "${mtime}" 240 | fi 241 | else 242 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 243 | fi 244 | } 245 | 246 | # For testing. This function will be overridden in tests. 247 | create_working_directory() { 248 | if [ "$#" -eq 1 ]; then 249 | func::tmpfile "${1}" 250 | else 251 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 252 | fi 253 | } 254 | 255 | touch_object() { 256 | if [ "$#" -eq 1 ]; then 257 | imos-aws s3api copy-object \ 258 | --bucket "${FLAGS_bucket}" \ 259 | --key "${1}" \ 260 | --copy-source "${FLAGS_bucket}/${1}" \ 261 | --metadata-directive REPLACE \ 262 | >'/dev/null' 2>'/dev/null' || return "$?" 263 | else 264 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 265 | fi 266 | } 267 | 268 | upload() { 269 | if [ "$#" -eq 2 ]; then 270 | CHECK [ -f "${2}" ] 271 | local mdate="$(mdate "${FLAGS_lifetime}/${1}")" 272 | local today="$(date +'%Y-%m-%d')" 273 | if [ "${mdate}" = "${today}" ]; then 274 | LOG INFO "${1} is up-to-date." 275 | elif [ "${mdate}" != '' ]; then 276 | LOG INFO "Touching ${1}..." 277 | touch_object "${FLAGS_lifetime}/${1}" 278 | else 279 | LOG INFO "Uploading ${1}..." 280 | imos-aws s3api put-object \ 281 | --bucket "${FLAGS_bucket}" \ 282 | --key "${FLAGS_lifetime}/${1}" \ 283 | --content-md5 "$(sub::print "${1}" | stream::hex2bin | base64)" \ 284 | --body "${2}" \ 285 | >'/dev/null' 286 | fi 287 | else 288 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 289 | fi 290 | } 291 | 292 | package::split() { 293 | if [ "$#" -ge 1 ]; then 294 | local fragments_directory='' 295 | create_working_directory fragments_directory 296 | mkdir "${fragments_directory}" 297 | LOG INFO "Fragments directory: ${fragments_directory}" 298 | if [ -s "${1}" ]; then 299 | split -a 3 -b "${FLAGS_fragment_size}" "${1}" "${fragments_directory}/" 300 | else 301 | touch "${fragments_directory}/empty" 302 | fi 303 | if ! imos-aws-credentials >'/dev/null'; then 304 | LOG FATAL 'Failed to get AWS credentials.' 305 | fi 306 | local list_file="${fragments_directory}/list" 307 | local file='' 308 | local files=() 309 | for file in "${fragments_directory}/"*; do 310 | local md5="$(cat "${file}" | stream::md5)" 311 | LOG INFO "Fragment: ${md5}" 312 | if [ -f "${fragments_directory}/${md5}" ]; then 313 | rm "${file}" 314 | else 315 | mv "${file}" "${fragments_directory}/${md5}" 316 | files+=("${md5}") 317 | fi 318 | sub::println "${md5}" >> "${list_file}" 319 | done 320 | for md5 in "${files[@]}"; do 321 | sub::throttle "${FLAGS_threads}" 322 | upload "${md5}" "${fragments_directory}/${md5}" & 323 | done 324 | local md5="$(cat "${list_file}" | stream::md5)" 325 | mv "${list_file}" "${fragments_directory}/${md5}" 326 | list_file="${fragments_directory}/${md5}" 327 | LOG INFO "$(ls -lA "${fragments_directory}/")" 328 | LOG INFO "List file: ${md5}" 329 | if [ "${FLAGS_alias}" != '' ]; then 330 | imos-aws s3api put-object \ 331 | --bucket "${FLAGS_bucket}" \ 332 | --key "${FLAGS_lifetime}/${FLAGS_alias}" \ 333 | --body "${list_file}" \ 334 | >'/dev/null' & 335 | fi 336 | upload "${md5}" "${list_file}" 337 | wait 338 | sub::println "${md5}" 339 | else 340 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 341 | fi 342 | } 343 | 344 | imos-package::upload() { 345 | if [ "${FLAGS_lifetime}" != 'ephemeral' -a \ 346 | "${FLAGS_lifetime}" != 'permanent' ]; then 347 | LOG FATAL "Invalid lifetime: ${FLAGS_lifetime}" 348 | fi 349 | local file='' 350 | for file in "$@"; do 351 | package::split "${file}" 352 | done 353 | } 354 | 355 | ################################################################################ 356 | # download command 357 | ################################################################################ 358 | # Downloads a file from the cloud server. 359 | # 360 | # Usage: 361 | # download_fragment input_path output_path 362 | download_fragment() { 363 | if [ "$#" -eq 2 ]; then 364 | LOG INFO "Downloading ${1}..." 365 | local destination='' 366 | func::tmpfile destination 367 | curl --silent --fail --output "${destination}" \ 368 | "https://${FLAGS_host}/${FLAGS_bucket}/ephemeral/${1}" || \ 369 | curl --silent --fail --output "${destination}" \ 370 | "https://${FLAGS_host}/${FLAGS_bucket}/permanent/${1}" || \ 371 | LOG FATAL "Failed to find ${1}." 372 | mv -f "${destination}" "${2}" 373 | else 374 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 375 | fi 376 | } 377 | 378 | fetch_fragment() { 379 | if [ "$#" -ge 1 ]; then 380 | local destination="${TMPDIR}/${1}" 381 | sub::println "${destination}" 382 | if [ "${#1}" -ne 32 ] || 383 | ! sub::greg_match '+([0-9a-f])' "${1}"; then 384 | download_fragment "${1}" "${destination}" 385 | return 386 | fi 387 | local cache='' 388 | if [ "${FLAGS_fragments_directory}" != '' ]; then 389 | mkdir -m 0777 -p "${FLAGS_fragments_directory}" 390 | cache="${FLAGS_fragments_directory}/${1}" 391 | fi 392 | if [ "${cache}" != '' -a -f "${cache}" ]; then 393 | cat "${cache}" >"${destination}" 394 | fi 395 | if [ -f "${destination}" ] && \ 396 | [ "$(cat "${destination}" | stream::md5)" = "${1}" ]; then 397 | LOG INFO "${1} was loaded from cache." 398 | return 399 | fi 400 | download_fragment "${1}" "${destination}" 401 | if [ "$(cat "${destination}" | stream::md5)" != "${1}" ]; then 402 | LOG FATAL "Fragment ${1} is broken." 403 | fi 404 | if [ "${cache}" != '' ]; then 405 | cat "${destination}" >"${cache}" 406 | fi 407 | else 408 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 409 | fi 410 | } 411 | 412 | imos-package::download() { 413 | if [ "$#" -eq 1 ]; then 414 | local list_file="$(fetch_fragment "${1}")" 415 | local output_file='' 416 | local fragment='' 417 | func::tmpfile output_file 418 | LOG INFO "List file: ${list_file}" 419 | while IFS= read -r fragment; do 420 | LOG INFO "Fragment: ${fragment}" 421 | if sub::greg_match '+([0-9a-f])' "${fragment}"; then 422 | cat "$(fetch_fragment "${fragment}")" >>"${output_file}" 423 | fi 424 | done <"${list_file}" 425 | if [ "${FLAGS_output}" != '' ]; then 426 | cat "${output_file}" >"${FLAGS_output}" 427 | fi 428 | else 429 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 430 | fi 431 | } 432 | 433 | ################################################################################ 434 | # bootstrap 435 | ################################################################################ 436 | if sub::is_main; then 437 | IMOSH_PREDICATE=2 eval "${IMOSH_INIT}" 438 | LOG INFO "Arguments: $*" 439 | if [ "$#" -eq 0 ] || [ "${1}" = 'help' ]; then 440 | imosh::help 441 | exit 442 | fi 443 | COMMAND="${1}" 444 | shift 445 | case "${COMMAND}" in 446 | 'create'|'run'|'archive'|'download'|'upload') 447 | "imos-package::${COMMAND}" "$@";; 448 | *) 449 | LOG FATAL "Unknown command: ${COMMAND}";; 450 | esac 451 | fi 452 | -------------------------------------------------------------------------------- /library/git-prompt.sh: -------------------------------------------------------------------------------- 1 | # bash/zsh git prompt support 2 | # 3 | # Copyright (C) 2006,2007 Shawn O. Pearce 4 | # Distributed under the GNU General Public License, version 2.0. 5 | # 6 | # This script allows you to see repository status in your prompt. 7 | # 8 | # To enable: 9 | # 10 | # 1) Copy this file to somewhere (e.g. ~/.git-prompt.sh). 11 | # 2) Add the following line to your .bashrc/.zshrc: 12 | # source ~/.git-prompt.sh 13 | # 3a) Change your PS1 to call __git_ps1 as 14 | # command-substitution: 15 | # Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ ' 16 | # ZSH: setopt PROMPT_SUBST ; PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ ' 17 | # the optional argument will be used as format string. 18 | # 3b) Alternatively, for a slightly faster prompt, __git_ps1 can 19 | # be used for PROMPT_COMMAND in Bash or for precmd() in Zsh 20 | # with two parameters,
 and , which are strings
 21 | #        you would put in $PS1 before and after the status string
 22 | #        generated by the git-prompt machinery.  e.g.
 23 | #        Bash: PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
 24 | #          will show username, at-sign, host, colon, cwd, then
 25 | #          various status string, followed by dollar and SP, as
 26 | #          your prompt.
 27 | #        ZSH:  precmd () { __git_ps1 "%n" ":%~$ " "|%s" }
 28 | #          will show username, pipe, then various status string,
 29 | #          followed by colon, cwd, dollar and SP, as your prompt.
 30 | #        Optionally, you can supply a third argument with a printf
 31 | #        format string to finetune the output of the branch status
 32 | #
 33 | # The repository status will be displayed only if you are currently in a
 34 | # git repository. The %s token is the placeholder for the shown status.
 35 | #
 36 | # The prompt status always includes the current branch name.
 37 | #
 38 | # In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty value,
 39 | # unstaged (*) and staged (+) changes will be shown next to the branch
 40 | # name.  You can configure this per-repository with the
 41 | # bash.showDirtyState variable, which defaults to true once
 42 | # GIT_PS1_SHOWDIRTYSTATE is enabled.
 43 | #
 44 | # You can also see if currently something is stashed, by setting
 45 | # GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed,
 46 | # then a '$' will be shown next to the branch name.
 47 | #
 48 | # If you would like to see if there're untracked files, then you can set
 49 | # GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're untracked
 50 | # files, then a '%' will be shown next to the branch name.  You can
 51 | # configure this per-repository with the bash.showUntrackedFiles
 52 | # variable, which defaults to true once GIT_PS1_SHOWUNTRACKEDFILES is
 53 | # enabled.
 54 | #
 55 | # If you would like to see the difference between HEAD and its upstream,
 56 | # set GIT_PS1_SHOWUPSTREAM="auto".  A "<" indicates you are behind, ">"
 57 | # indicates you are ahead, "<>" indicates you have diverged and "="
 58 | # indicates that there is no difference. You can further control
 59 | # behaviour by setting GIT_PS1_SHOWUPSTREAM to a space-separated list
 60 | # of values:
 61 | #
 62 | #     verbose       show number of commits ahead/behind (+/-) upstream
 63 | #     name          if verbose, then also show the upstream abbrev name
 64 | #     legacy        don't use the '--count' option available in recent
 65 | #                   versions of git-rev-list
 66 | #     git           always compare HEAD to @{upstream}
 67 | #     svn           always compare HEAD to your SVN upstream
 68 | #
 69 | # By default, __git_ps1 will compare HEAD to your SVN upstream if it can
 70 | # find one, or @{upstream} otherwise.  Once you have set
 71 | # GIT_PS1_SHOWUPSTREAM, you can override it on a per-repository basis by
 72 | # setting the bash.showUpstream config variable.
 73 | #
 74 | # If you would like to see more information about the identity of
 75 | # commits checked out as a detached HEAD, set GIT_PS1_DESCRIBE_STYLE
 76 | # to one of these values:
 77 | #
 78 | #     contains      relative to newer annotated tag (v1.6.3.2~35)
 79 | #     branch        relative to newer tag or branch (master~4)
 80 | #     describe      relative to older annotated tag (v1.6.3.1-13-gdd42c2f)
 81 | #     default       exactly matching tag
 82 | #
 83 | # If you would like a colored hint about the current dirty state, set
 84 | # GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
 85 | # the colored output of "git status -sb" and are available only when
 86 | # using __git_ps1 for PROMPT_COMMAND or precmd.
 87 | 
 88 | # check whether printf supports -v
 89 | __git_printf_supports_v=
 90 | printf -v __git_printf_supports_v -- '%s' yes >/dev/null 2>&1
 91 | 
 92 | # stores the divergence from upstream in $p
 93 | # used by GIT_PS1_SHOWUPSTREAM
 94 | __git_ps1_show_upstream ()
 95 | {
 96 | 	local key value
 97 | 	local svn_remote svn_url_pattern count n
 98 | 	local upstream=git legacy="" verbose="" name=""
 99 | 
100 | 	svn_remote=()
101 | 	# get some config options from git-config
102 | 	local output="$(git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' 2>/dev/null | tr '\0\n' '\n ')"
103 | 	while read -r key value; do
104 | 		case "$key" in
105 | 		bash.showupstream)
106 | 			GIT_PS1_SHOWUPSTREAM="$value"
107 | 			if [[ -z "${GIT_PS1_SHOWUPSTREAM}" ]]; then
108 | 				p=""
109 | 				return
110 | 			fi
111 | 			;;
112 | 		svn-remote.*.url)
113 | 			svn_remote[$((${#svn_remote[@]} + 1))]="$value"
114 | 			svn_url_pattern="$svn_url_pattern\\|$value"
115 | 			upstream=svn+git # default upstream is SVN if available, else git
116 | 			;;
117 | 		esac
118 | 	done <<< "$output"
119 | 
120 | 	# parse configuration values
121 | 	for option in ${GIT_PS1_SHOWUPSTREAM}; do
122 | 		case "$option" in
123 | 		git|svn) upstream="$option" ;;
124 | 		verbose) verbose=1 ;;
125 | 		legacy)  legacy=1  ;;
126 | 		name)    name=1 ;;
127 | 		esac
128 | 	done
129 | 
130 | 	# Find our upstream
131 | 	case "$upstream" in
132 | 	git)    upstream="@{upstream}" ;;
133 | 	svn*)
134 | 		# get the upstream from the "git-svn-id: ..." in a commit message
135 | 		# (git-svn uses essentially the same procedure internally)
136 | 		local -a svn_upstream
137 | 		svn_upstream=($(git log --first-parent -1 \
138 | 					--grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null))
139 | 		if [[ 0 -ne ${#svn_upstream[@]} ]]; then
140 | 			svn_upstream=${svn_upstream[${#svn_upstream[@]} - 2]}
141 | 			svn_upstream=${svn_upstream%@*}
142 | 			local n_stop="${#svn_remote[@]}"
143 | 			for ((n=1; n <= n_stop; n++)); do
144 | 				svn_upstream=${svn_upstream#${svn_remote[$n]}}
145 | 			done
146 | 
147 | 			if [[ -z "$svn_upstream" ]]; then
148 | 				# default branch name for checkouts with no layout:
149 | 				upstream=${GIT_SVN_ID:-git-svn}
150 | 			else
151 | 				upstream=${svn_upstream#/}
152 | 			fi
153 | 		elif [[ "svn+git" = "$upstream" ]]; then
154 | 			upstream="@{upstream}"
155 | 		fi
156 | 		;;
157 | 	esac
158 | 
159 | 	# Find how many commits we are ahead/behind our upstream
160 | 	if [[ -z "$legacy" ]]; then
161 | 		count="$(git rev-list --count --left-right \
162 | 				"$upstream"...HEAD 2>/dev/null)"
163 | 	else
164 | 		# produce equivalent output to --count for older versions of git
165 | 		local commits
166 | 		if commits="$(git rev-list --left-right "$upstream"...HEAD 2>/dev/null)"
167 | 		then
168 | 			local commit behind=0 ahead=0
169 | 			for commit in $commits
170 | 			do
171 | 				case "$commit" in
172 | 				"<"*) ((behind++)) ;;
173 | 				*)    ((ahead++))  ;;
174 | 				esac
175 | 			done
176 | 			count="$behind	$ahead"
177 | 		else
178 | 			count=""
179 | 		fi
180 | 	fi
181 | 
182 | 	# calculate the result
183 | 	if [[ -z "$verbose" ]]; then
184 | 		case "$count" in
185 | 		"") # no upstream
186 | 			p="" ;;
187 | 		"0	0") # equal to upstream
188 | 			p="=" ;;
189 | 		"0	"*) # ahead of upstream
190 | 			p=">" ;;
191 | 		*"	0") # behind upstream
192 | 			p="<" ;;
193 | 		*)	    # diverged from upstream
194 | 			p="<>" ;;
195 | 		esac
196 | 	else
197 | 		case "$count" in
198 | 		"") # no upstream
199 | 			p="" ;;
200 | 		"0	0") # equal to upstream
201 | 			p=" u=" ;;
202 | 		"0	"*) # ahead of upstream
203 | 			p=" u+${count#0	}" ;;
204 | 		*"	0") # behind upstream
205 | 			p=" u-${count%	0}" ;;
206 | 		*)	    # diverged from upstream
207 | 			p=" u+${count#*	}-${count%	*}" ;;
208 | 		esac
209 | 		if [[ -n "$count" && -n "$name" ]]; then
210 | 			__git_ps1_upstream_name=$(git rev-parse \
211 | 				--abbrev-ref "$upstream" 2>/dev/null)
212 | 			if [ $pcmode = yes ] && [ $ps1_expanded = yes ]; then
213 | 				p="$p \${__git_ps1_upstream_name}"
214 | 			else
215 | 				p="$p ${__git_ps1_upstream_name}"
216 | 				# not needed anymore; keep user's
217 | 				# environment clean
218 | 				unset __git_ps1_upstream_name
219 | 			fi
220 | 		fi
221 | 	fi
222 | 
223 | }
224 | 
225 | # Helper function that is meant to be called from __git_ps1.  It
226 | # injects color codes into the appropriate gitstring variables used
227 | # to build a gitstring.
228 | __git_ps1_colorize_gitstring ()
229 | {
230 | 	if [[ -n ${ZSH_VERSION-} ]]; then
231 | 		local c_red='%F{red}'
232 | 		local c_green='%F{green}'
233 | 		local c_lblue='%F{blue}'
234 | 		local c_clear='%f'
235 | 	else
236 | 		# Using \[ and \] around colors is necessary to prevent
237 | 		# issues with command line editing/browsing/completion!
238 | 		local c_red='\[\e[31m\]'
239 | 		local c_green='\[\e[32m\]'
240 | 		local c_lblue='\[\e[1;34m\]'
241 | 		local c_clear='\[\e[0m\]'
242 | 	fi
243 | 	local bad_color=$c_red
244 | 	local ok_color=$c_green
245 | 	local flags_color="$c_lblue"
246 | 
247 | 	local branch_color=""
248 | 	if [ $detached = no ]; then
249 | 		branch_color="$ok_color"
250 | 	else
251 | 		branch_color="$bad_color"
252 | 	fi
253 | 	c="$branch_color$c"
254 | 
255 | 	z="$c_clear$z"
256 | 	if [ "$w" = "*" ]; then
257 | 		w="$bad_color$w"
258 | 	fi
259 | 	if [ -n "$i" ]; then
260 | 		i="$ok_color$i"
261 | 	fi
262 | 	if [ -n "$s" ]; then
263 | 		s="$flags_color$s"
264 | 	fi
265 | 	if [ -n "$u" ]; then
266 | 		u="$bad_color$u"
267 | 	fi
268 | 	r="$c_clear$r"
269 | }
270 | 
271 | __git_eread ()
272 | {
273 | 	f="$1"
274 | 	shift
275 | 	test -r "$f" && read "$@" <"$f"
276 | }
277 | 
278 | # __git_ps1 accepts 0 or 1 arguments (i.e., format string)
279 | # when called from PS1 using command substitution
280 | # in this mode it prints text to add to bash PS1 prompt (includes branch name)
281 | #
282 | # __git_ps1 requires 2 or 3 arguments when called from PROMPT_COMMAND (pc)
283 | # in that case it _sets_ PS1. The arguments are parts of a PS1 string.
284 | # when two arguments are given, the first is prepended and the second appended
285 | # to the state string when assigned to PS1.
286 | # The optional third parameter will be used as printf format string to further
287 | # customize the output of the git-status string.
288 | # In this mode you can request colored hints using GIT_PS1_SHOWCOLORHINTS=true
289 | __git_ps1 ()
290 | {
291 | 	local pcmode=no
292 | 	local detached=no
293 | 	local ps1pc_start='\u@\h:\w '
294 | 	local ps1pc_end='\$ '
295 | 	local printf_format=' (%s)'
296 | 
297 | 	case "$#" in
298 | 		2|3)	pcmode=yes
299 | 			ps1pc_start="$1"
300 | 			ps1pc_end="$2"
301 | 			printf_format="${3:-$printf_format}"
302 | 		;;
303 | 		0|1)	printf_format="${1:-$printf_format}"
304 | 		;;
305 | 		*)	return
306 | 		;;
307 | 	esac
308 | 
309 | 	# ps1_expanded:  This variable is set to 'yes' if the shell
310 | 	# subjects the value of PS1 to parameter expansion:
311 | 	#
312 | 	#   * bash does unless the promptvars option is disabled
313 | 	#   * zsh does not unless the PROMPT_SUBST option is set
314 | 	#   * POSIX shells always do
315 | 	#
316 | 	# If the shell would expand the contents of PS1 when drawing
317 | 	# the prompt, a raw ref name must not be included in PS1.
318 | 	# This protects the user from arbitrary code execution via
319 | 	# specially crafted ref names.  For example, a ref named
320 | 	# 'refs/heads/$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' might cause the
321 | 	# shell to execute 'sudo rm -rf /' when the prompt is drawn.
322 | 	#
323 | 	# Instead, the ref name should be placed in a separate global
324 | 	# variable (in the __git_ps1_* namespace to avoid colliding
325 | 	# with the user's environment) and that variable should be
326 | 	# referenced from PS1.  For example:
327 | 	#
328 | 	#     __git_ps1_foo=$(do_something_to_get_ref_name)
329 | 	#     PS1="...stuff...\${__git_ps1_foo}...stuff..."
330 | 	#
331 | 	# If the shell does not expand the contents of PS1, the raw
332 | 	# ref name must be included in PS1.
333 | 	#
334 | 	# The value of this variable is only relevant when in pcmode.
335 | 	#
336 | 	# Assume that the shell follows the POSIX specification and
337 | 	# expands PS1 unless determined otherwise.  (This is more
338 | 	# likely to be correct if the user has a non-bash, non-zsh
339 | 	# shell and safer than the alternative if the assumption is
340 | 	# incorrect.)
341 | 	#
342 | 	local ps1_expanded=yes
343 | 	[ -z "$ZSH_VERSION" ] || [[ -o PROMPT_SUBST ]] || ps1_expanded=no
344 | 	[ -z "$BASH_VERSION" ] || shopt -q promptvars || ps1_expanded=no
345 | 
346 | 	local repo_info rev_parse_exit_code
347 | 	repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
348 | 		--is-bare-repository --is-inside-work-tree \
349 | 		--short HEAD 2>/dev/null)"
350 | 	rev_parse_exit_code="$?"
351 | 
352 | 	if [ -z "$repo_info" ]; then
353 | 		if [ $pcmode = yes ]; then
354 | 			#In PC mode PS1 always needs to be set
355 | 			PS1="$ps1pc_start$ps1pc_end"
356 | 		fi
357 | 		return
358 | 	fi
359 | 
360 | 	local short_sha
361 | 	if [ "$rev_parse_exit_code" = "0" ]; then
362 | 		short_sha="${repo_info##*$'\n'}"
363 | 		repo_info="${repo_info%$'\n'*}"
364 | 	fi
365 | 	local inside_worktree="${repo_info##*$'\n'}"
366 | 	repo_info="${repo_info%$'\n'*}"
367 | 	local bare_repo="${repo_info##*$'\n'}"
368 | 	repo_info="${repo_info%$'\n'*}"
369 | 	local inside_gitdir="${repo_info##*$'\n'}"
370 | 	local g="${repo_info%$'\n'*}"
371 | 
372 | 	local r=""
373 | 	local b=""
374 | 	local step=""
375 | 	local total=""
376 | 	if [ -d "$g/rebase-merge" ]; then
377 | 		__git_eread "$g/rebase-merge/head-name" b
378 | 		__git_eread "$g/rebase-merge/msgnum" step
379 | 		__git_eread "$g/rebase-merge/end" total
380 | 		if [ -f "$g/rebase-merge/interactive" ]; then
381 | 			r="|REBASE-i"
382 | 		else
383 | 			r="|REBASE-m"
384 | 		fi
385 | 	else
386 | 		if [ -d "$g/rebase-apply" ]; then
387 | 			__git_eread "$g/rebase-apply/next" step
388 | 			__git_eread "$g/rebase-apply/last" total
389 | 			if [ -f "$g/rebase-apply/rebasing" ]; then
390 | 				__git_eread "$g/rebase-apply/head-name" b
391 | 				r="|REBASE"
392 | 			elif [ -f "$g/rebase-apply/applying" ]; then
393 | 				r="|AM"
394 | 			else
395 | 				r="|AM/REBASE"
396 | 			fi
397 | 		elif [ -f "$g/MERGE_HEAD" ]; then
398 | 			r="|MERGING"
399 | 		elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
400 | 			r="|CHERRY-PICKING"
401 | 		elif [ -f "$g/REVERT_HEAD" ]; then
402 | 			r="|REVERTING"
403 | 		elif [ -f "$g/BISECT_LOG" ]; then
404 | 			r="|BISECTING"
405 | 		fi
406 | 
407 | 		if [ -n "$b" ]; then
408 | 			:
409 | 		elif [ -h "$g/HEAD" ]; then
410 | 			# symlink symbolic ref
411 | 			b="$(git symbolic-ref HEAD 2>/dev/null)"
412 | 		else
413 | 			local head=""
414 | 			if ! __git_eread "$g/HEAD" head; then
415 | 				if [ $pcmode = yes ]; then
416 | 					PS1="$ps1pc_start$ps1pc_end"
417 | 				fi
418 | 				return
419 | 			fi
420 | 			# is it a symbolic ref?
421 | 			b="${head#ref: }"
422 | 			if [ "$head" = "$b" ]; then
423 | 				detached=yes
424 | 				b="$(
425 | 				case "${GIT_PS1_DESCRIBE_STYLE-}" in
426 | 				(contains)
427 | 					git describe --contains HEAD ;;
428 | 				(branch)
429 | 					git describe --contains --all HEAD ;;
430 | 				(describe)
431 | 					git describe HEAD ;;
432 | 				(* | default)
433 | 					git describe --tags --exact-match HEAD ;;
434 | 				esac 2>/dev/null)" ||
435 | 
436 | 				b="$short_sha..."
437 | 				b="($b)"
438 | 			fi
439 | 		fi
440 | 	fi
441 | 
442 | 	if [ -n "$step" ] && [ -n "$total" ]; then
443 | 		r="$r $step/$total"
444 | 	fi
445 | 
446 | 	local w=""
447 | 	local i=""
448 | 	local s=""
449 | 	local u=""
450 | 	local c=""
451 | 	local p=""
452 | 
453 | 	if [ "true" = "$inside_gitdir" ]; then
454 | 		if [ "true" = "$bare_repo" ]; then
455 | 			c="BARE:"
456 | 		else
457 | 			b="GIT_DIR!"
458 | 		fi
459 | 	elif [ "true" = "$inside_worktree" ]; then
460 | 		if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
461 | 		   [ "$(git config --bool bash.showDirtyState)" != "false" ]
462 | 		then
463 | 			git diff --no-ext-diff --quiet --exit-code || w="*"
464 | 			if [ -n "$short_sha" ]; then
465 | 				git diff-index --cached --quiet HEAD -- || i="+"
466 | 			else
467 | 				i="#"
468 | 			fi
469 | 		fi
470 | 		if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ] &&
471 | 		   git rev-parse --verify --quiet refs/stash >/dev/null
472 | 		then
473 | 			s="$"
474 | 		fi
475 | 
476 | 		if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
477 | 		   [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
478 | 		   git ls-files --others --exclude-standard --error-unmatch -- '*' >/dev/null 2>/dev/null
479 | 		then
480 | 			u="%${ZSH_VERSION+%}"
481 | 		fi
482 | 
483 | 		if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
484 | 			__git_ps1_show_upstream
485 | 		fi
486 | 	fi
487 | 
488 | 	local z="${GIT_PS1_STATESEPARATOR-" "}"
489 | 
490 | 	# NO color option unless in PROMPT_COMMAND mode
491 | 	if [ $pcmode = yes ] && [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
492 | 		__git_ps1_colorize_gitstring
493 | 	fi
494 | 
495 | 	b=${b##refs/heads/}
496 | 	if [ $pcmode = yes ] && [ $ps1_expanded = yes ]; then
497 | 		__git_ps1_branch_name=$b
498 | 		b="\${__git_ps1_branch_name}"
499 | 	fi
500 | 
501 | 	local f="$w$i$s$u"
502 | 	local gitstring="$c$b${f:+$z$f}$r$p"
503 | 
504 | 	if [ $pcmode = yes ]; then
505 | 		if [ "${__git_printf_supports_v-}" != yes ]; then
506 | 			gitstring=$(printf -- "$printf_format" "$gitstring")
507 | 		else
508 | 			printf -v gitstring -- "$printf_format" "$gitstring"
509 | 		fi
510 | 		PS1="$ps1pc_start$gitstring$ps1pc_end"
511 | 	else
512 | 		printf -- "$printf_format" "$gitstring"
513 | 	fi
514 | }
515 | 


--------------------------------------------------------------------------------