├── .gitignore ├── library ├── 90-flags │ ├── 90-fi.sh │ ├── 00-define.sh │ └── 50-variables.sh ├── 10-func │ ├── array │ │ ├── README.md │ │ ├── array.sh │ │ ├── array_is_empty.sh │ │ ├── in_array.sh │ │ ├── count.sh │ │ ├── is_array.sh │ │ ├── array_values.sh │ │ ├── array_keys.sh │ │ ├── array_unique.sh │ │ ├── sort.sh │ │ └── array_map.sh │ ├── strings │ │ ├── README.md │ │ ├── strcpy.sh │ │ ├── print.sh │ │ ├── addslashes.sh │ │ ├── println.sh │ │ ├── trim.sh │ │ ├── ltrim.sh │ │ ├── explode.sh │ │ ├── escapeshellarg.sh │ │ ├── md5_file.sh │ │ ├── ord.sh │ │ ├── bin2hex.sh │ │ ├── base64_encode.sh │ │ ├── md5.sh │ │ ├── sha1.sh │ │ ├── rtrim.sh │ │ ├── base64_decode.sh │ │ ├── substr.sh │ │ ├── hex2bin.sh │ │ ├── str_replace.sh │ │ ├── implode.sh │ │ ├── strtolower.sh │ │ └── strtoupper.sh │ ├── datetime │ │ ├── README.md │ │ └── time.sh │ ├── filesystem │ │ ├── README.md │ │ ├── readarray.sh │ │ ├── tmpfile.sh │ │ ├── readline.sh │ │ ├── fgets.sh │ │ └── file_get_contents.sh │ ├── var │ │ ├── README.md │ │ ├── let.sh │ │ ├── strval.sh │ │ ├── isset.sh │ │ ├── floatval.sh │ │ ├── intval.sh │ │ ├── enumval.sh │ │ ├── boolval.sh │ │ └── cast.sh │ ├── logging │ │ ├── README.md │ │ ├── DEPRECATED.sh │ │ └── CHECK.sh │ ├── math │ │ ├── README.md │ │ └── rand.sh │ ├── misc │ │ ├── README.md │ │ ├── atexit.sh │ │ ├── exec.sh │ │ ├── throttle.sh │ │ ├── exit.sh │ │ └── usage.sh │ ├── info │ │ ├── README.md │ │ ├── is_main.sh │ │ ├── getmypid.sh │ │ └── getchildpids.sh │ ├── testing │ │ ├── README.md │ │ ├── ASSERT_TRUE.sh │ │ ├── ASSERT_FALSE.sh │ │ ├── ASSERT_ALIVE.sh │ │ ├── ASSERT_DEATH.sh │ │ ├── ASSERT_EQ.sh │ │ ├── ASSERT_NE.sh │ │ ├── EXPECT_ALIVE.sh │ │ ├── EXPECT_DEATH.sh │ │ ├── EXPECT_TRUE.sh │ │ ├── EXPECT_FALSE.sh │ │ ├── FAILURE.sh │ │ ├── EXPECT_EQ.sh │ │ ├── EXPECT_NE.sh │ │ ├── EXPECT_LT.sh │ │ ├── EXPECT_GT.sh │ │ ├── EXPECT_LE.sh │ │ ├── EXPECT_GE.sh │ │ └── test_functions.sh │ ├── greg │ │ ├── README.md │ │ ├── greg_match.sh │ │ ├── ereg_match.sh │ │ ├── greg_split.sh │ │ └── greg_replace.sh │ └── funchand │ │ └── function_exists.sh ├── 60-constants │ ├── uname.sh │ ├── pid.sh │ ├── state.sh │ ├── color.sh │ └── flag.sh ├── 40-variables │ └── imosh.sh ├── 99-footer │ └── footer.sh ├── 70-handler │ ├── error.sh │ ├── signal.sh │ └── exit.sh ├── 50-reload │ └── reload.sh ├── 80-setup │ ├── log.sh │ └── mktemp.sh ├── 00-header │ └── header.sh └── 10-imosh │ ├── stack_trace.sh │ └── arguments.sh ├── test ├── variable │ └── uname.sh ├── func │ ├── ASSERT_FALSE_test.sh │ ├── ASSERT_TRUE_test.sh │ ├── ASSERT_EQ_test.sh │ ├── ASSERT_NE_test.sh │ ├── exit_test.sh │ ├── function_exists_test.sh │ ├── greg_split_test.sh │ ├── md5_test.sh │ ├── print_test.sh │ ├── greg_match_test.sh │ ├── sha1_test.sh │ ├── array_is_empty_test.sh │ ├── println_test.sh │ ├── EXPECT_TRUE_test.sh │ ├── bin2hex_test.sh │ ├── EXPECT_DEATH_test.sh │ ├── EXPECT_EQ_test.sh │ ├── EXPECT_FALSE_test.sh │ ├── EXPECT_NE_test.sh │ ├── let_test.sh │ ├── time_test.sh │ ├── cast_test.sh │ ├── getmypid_test.sh │ ├── strcpy_test.sh │ ├── ord_test.sh │ ├── is_array_test.sh │ ├── getchildpids_test.sh │ ├── md5_file_test.sh │ ├── strval_test.sh │ ├── readarray_test.sh │ ├── intval_test.sh │ ├── floatval_test.sh │ ├── array_keys_test.sh │ ├── escapeshellarg_test.sh │ ├── boolval_test.sh │ ├── greg_replace_test.sh │ ├── addslashes_test.sh │ ├── tmpfile_test.sh │ ├── trim_test.sh │ ├── implode_test.sh │ ├── ltrim_test.sh │ ├── rtrim_test.sh │ ├── explode_test.sh │ ├── base64_encode_test.sh │ ├── isset_test.sh │ ├── array_values_test.sh │ ├── base64_decode_test.sh │ ├── date_test.sh │ ├── in_array_test.sh │ ├── str_replace_test.sh │ ├── hex2bin_test.sh │ ├── file_get_contents_test.sh │ ├── rand_test.sh │ ├── fgets_test.sh │ ├── strtolower_test.sh │ ├── strtoupper_test.sh │ ├── array_map_test.sh │ ├── substr_test.sh │ ├── readline_test.sh │ ├── sort_test.sh │ ├── strtotime_test.sh │ └── array_unique_test.sh ├── parse_arguments_test.sh ├── script │ ├── fd.sh │ └── exit.sh ├── extra_flags_test.sh ├── log_test.sh ├── flags.sh ├── bash_bug.sh └── help_test.sh ├── doc ├── info │ ├── is_main.sh.md │ ├── getmypid.sh.md │ └── getchildpids.sh.md ├── logging │ ├── CHECK.sh.md │ ├── DEPRECATED.sh.md │ └── LOG.sh.md ├── testing │ ├── test_functions.sh.md │ ├── FAILURE.sh.md │ ├── ASSERT_TRUE.sh.md │ ├── EXPECT_TRUE.sh.md │ ├── ASSERT_FALSE.sh.md │ ├── EXPECT_FALSE.sh.md │ ├── ASSERT_ALIVE.sh.md │ ├── ASSERT_DEATH.sh.md │ ├── EXPECT_ALIVE.sh.md │ ├── EXPECT_DEATH.sh.md │ ├── EXPECT_LT.sh.md │ ├── ASSERT_EQ.sh.md │ ├── EXPECT_EQ.sh.md │ ├── ASSERT_NE.sh.md │ ├── EXPECT_GT.sh.md │ ├── EXPECT_NE.sh.md │ ├── EXPECT_LE.sh.md │ └── EXPECT_GE.sh.md ├── datetime │ ├── date.sh.md │ ├── strtotime.sh.md │ └── time.sh.md ├── var │ ├── let.sh.md │ ├── boolval.sh.md │ ├── cast.sh.md │ ├── strval.sh.md │ ├── enumval.sh.md │ ├── intval.sh.md │ ├── floatval.sh.md │ └── isset.sh.md ├── array │ ├── array.sh.md │ ├── in_array.sh.md │ ├── is_array.sh.md │ ├── count.sh.md │ ├── sort.sh.md │ ├── array_is_empty.sh.md │ ├── array_values.sh.md │ ├── array_keys.sh.md │ ├── array_unique.sh.md │ └── array_map.sh.md ├── filesystem │ ├── readline.sh.md │ ├── readarray.sh.md │ ├── tmpfile.sh.md │ ├── fgets.sh.md │ └── file_get_contents.sh.md ├── misc │ ├── atexit.sh.md │ ├── exec.sh.md │ ├── throttle.sh.md │ ├── usage.sh.md │ └── exit.sh.md ├── strings │ ├── explode.sh.md │ ├── md5_file.sh.md │ ├── strcpy.sh.md │ ├── addslashes.sh.md │ ├── escapeshellarg.sh.md │ ├── md5.sh.md │ ├── sha1.sh.md │ ├── ltrim.sh.md │ ├── trim.sh.md │ ├── base64_decode.sh.md │ ├── base64_encode.sh.md │ ├── ord.sh.md │ ├── print.sh.md │ ├── strtolower.sh.md │ ├── strtoupper.sh.md │ ├── println.sh.md │ ├── bin2hex.sh.md │ ├── hex2bin.sh.md │ ├── rtrim.sh.md │ ├── str_replace.sh.md │ ├── substr.sh.md │ └── implode.sh.md ├── greg │ ├── ereg_match.sh.md │ ├── greg_match.sh.md │ ├── greg_replace.sh.md │ └── greg_split.sh.md ├── funchand │ └── function_exists.sh.md └── math │ └── rand.sh.md ├── tool ├── print-flag-variables.sh └── update-readme.sh ├── LICENSE └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /library/90-flags/90-fi.sh: -------------------------------------------------------------------------------- 1 | fi 2 | -------------------------------------------------------------------------------- /library/10-func/array/README.md: -------------------------------------------------------------------------------- 1 | # Arrays 2 | -------------------------------------------------------------------------------- /library/10-func/strings/README.md: -------------------------------------------------------------------------------- 1 | # Strings 2 | -------------------------------------------------------------------------------- /library/10-func/datetime/README.md: -------------------------------------------------------------------------------- 1 | # Date/Time 2 | -------------------------------------------------------------------------------- /library/10-func/filesystem/README.md: -------------------------------------------------------------------------------- 1 | # Filesystem 2 | -------------------------------------------------------------------------------- /library/10-func/var/README.md: -------------------------------------------------------------------------------- 1 | # Variable handling 2 | -------------------------------------------------------------------------------- /library/10-func/logging/README.md: -------------------------------------------------------------------------------- 1 | # Logging Functions 2 | -------------------------------------------------------------------------------- /library/10-func/math/README.md: -------------------------------------------------------------------------------- 1 | # Mathematical Functions 2 | -------------------------------------------------------------------------------- /library/10-func/misc/README.md: -------------------------------------------------------------------------------- 1 | # Miscellaneous Functions 2 | -------------------------------------------------------------------------------- /library/10-func/info/README.md: -------------------------------------------------------------------------------- 1 | # imosh Options and Information 2 | -------------------------------------------------------------------------------- /library/10-func/testing/README.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | Functions only for testing. 3 | -------------------------------------------------------------------------------- /library/60-constants/uname.sh: -------------------------------------------------------------------------------- 1 | if ! sub::isset UNAME; then 2 | UNAME="$(uname)" 3 | fi 4 | -------------------------------------------------------------------------------- /library/60-constants/pid.sh: -------------------------------------------------------------------------------- 1 | # Process ID that imosh initially runs on. 2 | readonly IMOSH_ROOT_PID="$$" 3 | -------------------------------------------------------------------------------- /library/40-variables/imosh.sh: -------------------------------------------------------------------------------- 1 | # IMOSH_PREDICATE declares the number of predicates. 2 | unset IMOSH_PREDICATE 3 | -------------------------------------------------------------------------------- /test/variable/uname.sh: -------------------------------------------------------------------------------- 1 | test::uname() { 2 | EXPECT_TRUE sub::isset UNAME 3 | EXPECT_NE '' "${UNAME}" 4 | } 5 | -------------------------------------------------------------------------------- /library/60-constants/state.sh: -------------------------------------------------------------------------------- 1 | # Variable not to output stack trace multiple times when exiting. 2 | __IMOSH_STACK_TRACED=0 3 | -------------------------------------------------------------------------------- /library/99-footer/footer.sh: -------------------------------------------------------------------------------- 1 | __IMOSH_IS_LOADED=1 2 | 3 | if sub::is_main; then 4 | eval "${IMOSH_INIT}" 5 | imosh::test_files "$@" 6 | fi 7 | -------------------------------------------------------------------------------- /doc/info/is_main.sh.md: -------------------------------------------------------------------------------- 1 | # is_main 2 | is_main -- Returns 0 iff caller is in the main script. 3 | 4 | ## Usage 5 | ```sh 6 | // 1. Command form. 7 | void sub::is_main() 8 | ``` 9 | -------------------------------------------------------------------------------- /library/10-func/greg/README.md: -------------------------------------------------------------------------------- 1 | # Regular Expressions (Glob-Compatible) 2 | For more details of GREG pattern, see [http://mywiki.wooledge.org/glob](http://mywiki.wooledge.org/glob). 3 | -------------------------------------------------------------------------------- /test/func/ASSERT_FALSE_test.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | ASSERT_FALSE "$@" 2>'/dev/null' 105>'/dev/null' 3 | } 4 | 5 | test::ASSERT_FALSE() { 6 | EXPECT_ALIVE run false 7 | EXPECT_DEATH run true 8 | } 9 | -------------------------------------------------------------------------------- /test/func/ASSERT_TRUE_test.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | ASSERT_TRUE "$@" 2>'/dev/null' 105>'/dev/null' 3 | } 4 | 5 | test::ASSERT_TRUE() { 6 | EXPECT_ALIVE run true 7 | EXPECT_DEATH run false 8 | } 9 | -------------------------------------------------------------------------------- /test/func/ASSERT_EQ_test.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | ASSERT_EQ "$@" 2>'/dev/null' 105>'/dev/null' 3 | } 4 | 5 | test::ASSERT_EQ() { 6 | EXPECT_ALIVE run 'abc' 'abc' 7 | EXPECT_DEATH run 'abc' 'def' 8 | } 9 | -------------------------------------------------------------------------------- /test/func/ASSERT_NE_test.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | ASSERT_NE "$@" 2>'/dev/null' 105>'/dev/null' 3 | } 4 | 5 | test::ASSERT_NE() { 6 | EXPECT_ALIVE run 'abc' 'def' 7 | EXPECT_DEATH run 'abc' 'abc' 8 | } 9 | -------------------------------------------------------------------------------- /doc/logging/CHECK.sh.md: -------------------------------------------------------------------------------- 1 | # CHECK 2 | CHECK -- checks if a command succeeds. 3 | 4 | CHECK fails with a fatal error if a command fails. 5 | 6 | ## Usage 7 | ```sh 8 | void CHECK(string command...) 9 | ``` 10 | -------------------------------------------------------------------------------- /doc/testing/test_functions.sh.md: -------------------------------------------------------------------------------- 1 | # test_file 2 | test_file -- Tests a file. 3 | 4 | test_file tests test cases in a file. 5 | 6 | ## Usage 7 | ```sh 8 | void imosh::test_file(string file_path) 9 | ``` 10 | -------------------------------------------------------------------------------- /library/70-handler/error.sh: -------------------------------------------------------------------------------- 1 | __imosh::error_handler() { 2 | local exit_code="$?" 3 | __IMOSH_STACK_TRACED=1 4 | imosh::stack_trace "Exit code: ${exit_code}" 5 | } 6 | 7 | trap '__imosh::error_handler' ERR 8 | -------------------------------------------------------------------------------- /doc/datetime/date.sh.md: -------------------------------------------------------------------------------- 1 | # date 2 | date -- Format a local time/date. 3 | 4 | date formats a local time/date. 5 | 6 | ## Usage 7 | ```sh 8 | void func::date(string* output, string format, int time) 9 | ``` 10 | -------------------------------------------------------------------------------- /doc/logging/DEPRECATED.sh.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | DEPRECATED -- Declares as deprecated. 3 | 4 | DEPRECATED displays an error message and a stack trace. 5 | 6 | ## Usage 7 | ```sh 8 | void DEPRECATED() 9 | ``` 10 | -------------------------------------------------------------------------------- /doc/testing/FAILURE.sh.md: -------------------------------------------------------------------------------- 1 | # FAILURE 2 | FAILURE -- Declares a test case failed. 3 | 4 | FAILURE sets the test case as failed and shows a stack trace. 5 | 6 | ## Usage 7 | ```sh 8 | void FAILURE() 9 | ``` 10 | -------------------------------------------------------------------------------- /test/func/exit_test.sh: -------------------------------------------------------------------------------- 1 | test::func_exit() { 2 | EXPECT_EQ 123 "$( 3 | "${SHELL}" "test/script/exit.sh" \ 4 | --status=123 --depth=2 --exit_depth=1 2>/dev/null; 5 | echo "$?"; 6 | sleep 5)" 7 | } 8 | -------------------------------------------------------------------------------- /test/func/function_exists_test.sh: -------------------------------------------------------------------------------- 1 | test::function_exists() { 2 | EXPECT_TRUE sub::function_exists test::function_exists 3 | EXPECT_TRUE sub::function_exists cd 4 | EXPECT_FALSE sub::function_exists no_such_function 5 | } 6 | -------------------------------------------------------------------------------- /doc/var/let.sh.md: -------------------------------------------------------------------------------- 1 | # let 2 | let -- Assigns a value into a variable. 3 | 4 | Assigns value into *destination. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | func::let(string* destination, string value) 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/var/boolval.sh.md: -------------------------------------------------------------------------------- 1 | # boolval 2 | boolval -- Casts a variable as a boolean value. 3 | 4 | Casts variable as a boolean value. If it fails, returns 1. 5 | 6 | ## Usage 7 | ```sh 8 | bool func::boolval(string* variable) 9 | ``` 10 | -------------------------------------------------------------------------------- /library/50-reload/reload.sh: -------------------------------------------------------------------------------- 1 | # Return if imosh is reloaded. Operations in 50+ run level may have 2 | # destructive operations, so they are skipped in reloading. 3 | if [ "${__IMOSH_IS_LOADED+loaded}" = 'loaded' ]; then 4 | return 5 | fi 6 | -------------------------------------------------------------------------------- /doc/testing/ASSERT_TRUE.sh.md: -------------------------------------------------------------------------------- 1 | # ASSERT_TRUE 2 | ASSERT_TRUE -- Asserts a command succeeds. 3 | 4 | ASSERT_TRUE asserts the command succeeds (returns 0). 5 | 6 | ## Usage 7 | ```sh 8 | void ASSERT_TRUE(string arguments,...) 9 | ``` 10 | -------------------------------------------------------------------------------- /doc/testing/EXPECT_TRUE.sh.md: -------------------------------------------------------------------------------- 1 | # EXPECT_TRUE 2 | EXPECT_TRUE -- Expects a command succeeds. 3 | 4 | EXPECT_TRUE expects the command succeeds (returns 0). 5 | 6 | ## Usage 7 | ```sh 8 | void EXPECT_TRUE(string arguments,...) 9 | ``` 10 | -------------------------------------------------------------------------------- /doc/info/getmypid.sh.md: -------------------------------------------------------------------------------- 1 | # getmypid 2 | getmypid -- Gets the current process ID. 3 | 4 | ## Usage 5 | ```sh 6 | // 1. Function form. 7 | void func::getmypid(int* variable) 8 | // 2. Command form. 9 | void sub::getmypid() > output 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/logging/LOG.sh.md: -------------------------------------------------------------------------------- 1 | # LOG 2 | LOG -- Logs a message. 3 | 4 | LOG logs a message with a timestamp, the current process ID and a file 5 | position. 6 | 7 | ## Usage 8 | ```sh 9 | void LOG(string log_level, string message...) 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/array/array.sh.md: -------------------------------------------------------------------------------- 1 | # array 2 | array -- Creates an array. 3 | 4 | array creates an array from a string with IFS separators. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form 9 | void func::array(string[]* result, string input) 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/filesystem/readline.sh.md: -------------------------------------------------------------------------------- 1 | # readline 2 | readline -- Gets a line. 3 | 4 | readline reads a line and sets its content to LINE and its trailing new line 5 | to NEWLINE. 6 | 7 | ## Usage 8 | ```sh 9 | void func::readline() < input 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/misc/atexit.sh.md: -------------------------------------------------------------------------------- 1 | # atexit 2 | atexit -- Registers a function on shutdown. 3 | 4 | atexit registers a function to be excuted on shutdown. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Command form. 9 | void sub::atexit(string command) 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/array/in_array.sh.md: -------------------------------------------------------------------------------- 1 | # in_array 2 | in_array -- Checks if a value exists in an array. 3 | 4 | Searches haystack for needle. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Command form. 9 | bool sub::in_array(string needle, string[]* haystack) 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/array/is_array.sh.md: -------------------------------------------------------------------------------- 1 | # in_array 2 | in_array -- Finds whether a variable is an array. 3 | 4 | Finds whether the given variable is an array. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Command form. 9 | bool sub::is_array(string[]* variable) 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/testing/ASSERT_FALSE.sh.md: -------------------------------------------------------------------------------- 1 | # ASSERT_FALSE 2 | ASSERT_FALSE -- Expects a command fails. 3 | 4 | ASSERT_FALSE expects the command fails (returns a non-zero value). 5 | 6 | ## Usage 7 | ```sh 8 | void ASSERT_FALSE(string arguments,...) 9 | ``` 10 | -------------------------------------------------------------------------------- /doc/testing/EXPECT_FALSE.sh.md: -------------------------------------------------------------------------------- 1 | # EXPECT_FALSE 2 | EXPECT_FALSE -- Expects a command fails. 3 | 4 | EXPECT_FALSE expects the command fails (returns a non-zero value). 5 | 6 | ## Usage 7 | ```sh 8 | void EXPECT_FALSE(string arguments,...) 9 | ``` 10 | -------------------------------------------------------------------------------- /test/func/greg_split_test.sh: -------------------------------------------------------------------------------- 1 | test::func_greg_split() { 2 | local variable=() 3 | 4 | func::greg_split variable $'[ \t\n]' $'a b\tc' 5 | EXPECT_EQ 3 "${#variable[*]}" 6 | func::implode variable ',' variable 7 | EXPECT_EQ 'a,b,c' "${variable}" 8 | } 9 | -------------------------------------------------------------------------------- /test/func/md5_test.sh: -------------------------------------------------------------------------------- 1 | test::sub_md5() { 2 | EXPECT_EQ 'd41d8cd98f00b204e9800998ecf8427e' "$(sub::md5 '')" 3 | EXPECT_EQ 'acbd18db4cc2f85cedef654fccc4a4d8' "$(sub::md5 'foo')" 4 | EXPECT_EQ '68b329da9893e34099c7d8ad5cb9c940' "$(sub::md5 $'\n')" 5 | } 6 | -------------------------------------------------------------------------------- /doc/strings/explode.sh.md: -------------------------------------------------------------------------------- 1 | # explode 2 | explode -- Splits a string by a substring. 3 | 4 | Splits a string by string. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::explode(string* variable, string delimiter, string value) 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/datetime/strtotime.sh.md: -------------------------------------------------------------------------------- 1 | # strtotime 2 | strtotime -- Parses a datetime text into a UNIX timestamp. 3 | 4 | strtotime parses a datetime text into a UNIX timestamp. 5 | 6 | ## Usage 7 | ```sh 8 | void func::strtotime(int* time, string time_text) 9 | ``` 10 | -------------------------------------------------------------------------------- /doc/info/getchildpids.sh.md: -------------------------------------------------------------------------------- 1 | # getchildpids 2 | getchildpids -- Gets child process IDs. 3 | 4 | ## Usage 5 | ```sh 6 | // 1. Function form. 7 | void func::getchildpids(int[]* variable) 8 | // 2. Command form. 9 | void sub::getchildpids() > output 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/testing/ASSERT_ALIVE.sh.md: -------------------------------------------------------------------------------- 1 | # ASSERT_ALIVE 2 | ASSERT_ALIVE -- Asserts a command successfully dies. 3 | 4 | ASSERT_ALIVE asserts that a command dies with a zero return value. 5 | 6 | ## Usage 7 | ```sh 8 | void ASSERT_ALIVE(string arguments,...) 9 | ``` 10 | -------------------------------------------------------------------------------- /doc/filesystem/readarray.sh.md: -------------------------------------------------------------------------------- 1 | # readarray 2 | readarray -- Reads a line as an array. 3 | 4 | readarray reads a line and sets its content to LINE and its trailing new line 5 | to NEWLINE. 6 | 7 | ## Usage 8 | ```sh 9 | void func::readarray() < input 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/testing/ASSERT_DEATH.sh.md: -------------------------------------------------------------------------------- 1 | # ASSERT_DEATH 2 | ASSERT_DEATH -- Asserts a command unsuccessfully dies. 3 | 4 | ASSERT_DEATH asserts that a command dies with a non-zero return value. 5 | 6 | ## Usage 7 | ```sh 8 | void ASSERT_DEATH(string arguments,...) 9 | ``` 10 | -------------------------------------------------------------------------------- /doc/testing/EXPECT_ALIVE.sh.md: -------------------------------------------------------------------------------- 1 | # EXPECT_ALIVE 2 | EXPECT_ALIVE -- Expects a command successfully exits. 3 | 4 | EXPECT_ALIVE expects that a command exits with a zero return value. 5 | 6 | ## Usage 7 | ```sh 8 | void EXPECT_ALIVE(string arguments,...) 9 | ``` 10 | -------------------------------------------------------------------------------- /doc/testing/EXPECT_DEATH.sh.md: -------------------------------------------------------------------------------- 1 | # EXPECT_DEATH 2 | EXPECT_DEATH -- Expects a command unsuccessfully dies. 3 | 4 | EXPECT_DEATH expects that a command dies with a non-zero return value. 5 | 6 | ## Usage 7 | ```sh 8 | void EXPECT_DEATH(string arguments,...) 9 | ``` 10 | -------------------------------------------------------------------------------- /test/func/print_test.sh: -------------------------------------------------------------------------------- 1 | test::func_print() { 2 | EXPECT_EQ '686f6765' "$(sub::print 'hoge' | stream::bin2hex)" 3 | EXPECT_EQ '686f67650a' "$(sub::print $'hoge\n' | stream::bin2hex)" 4 | EXPECT_EQ '01020304' "$(sub::print $'\x01\x02\x03\x04' | stream::bin2hex)" 5 | } 6 | -------------------------------------------------------------------------------- /doc/datetime/time.sh.md: -------------------------------------------------------------------------------- 1 | # time 2 | time -- Returns current Unix timestamp. 3 | 4 | time returns current Unix timestamp. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::time(string* result) 10 | // 2. Command form. 11 | void sub::time() > output 12 | ``` 13 | -------------------------------------------------------------------------------- /doc/misc/exec.sh.md: -------------------------------------------------------------------------------- 1 | # exec 2 | exec -- Executes an external program. 3 | 4 | func::exec executes an external program and sets its output to the variable. 5 | 6 | ## Usage 7 | ```sh 8 | // Function form. 9 | void func::exec(string* output, string arguments...) 10 | ``` 11 | -------------------------------------------------------------------------------- /test/func/greg_match_test.sh: -------------------------------------------------------------------------------- 1 | test::sub_greg_match() { 2 | EXPECT_TRUE sub::greg_match '*def*' 'abcdefghi' 3 | EXPECT_FALSE sub::greg_match '*xyz*' 'abcdefghi' 4 | EXPECT_TRUE sub::greg_match '*([a-z])' 'abcdefghi' 5 | EXPECT_FALSE sub::greg_match '*([a-z])' 'abc123ghi' 6 | } 7 | -------------------------------------------------------------------------------- /test/func/sha1_test.sh: -------------------------------------------------------------------------------- 1 | test::sha1() { 2 | EXPECT_EQ 'da39a3ee5e6b4b0d3255bfef95601890afd80709' "$(sub::sha1 '')" 3 | EXPECT_EQ '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' "$(sub::sha1 'foo')" 4 | EXPECT_EQ 'adc83b19e793491b1c6ea0fd8b46cd9f32e592fc' "$(sub::sha1 $'\n')" 5 | } 6 | -------------------------------------------------------------------------------- /library/10-func/testing/ASSERT_TRUE.sh: -------------------------------------------------------------------------------- 1 | # ASSERT_TRUE -- Asserts a command succeeds. 2 | # 3 | # ASSERT_TRUE asserts the command succeeds (returns 0). 4 | # 5 | # Usage: 6 | # void ASSERT_TRUE(string arguments,...) 7 | ASSERT_TRUE() { 8 | __ASSERT EXPECT_TRUE "$@" 9 | } 10 | -------------------------------------------------------------------------------- /test/func/array_is_empty_test.sh: -------------------------------------------------------------------------------- 1 | test::array_is_empty() { 2 | local array1=() 3 | local array2=('') 4 | local array3=('foo') 5 | 6 | EXPECT_TRUE sub::array_is_empty array1 7 | EXPECT_FALSE sub::array_is_empty array2 8 | EXPECT_FALSE sub::array_is_empty array3 9 | } 10 | -------------------------------------------------------------------------------- /test/func/println_test.sh: -------------------------------------------------------------------------------- 1 | test::func_println() { 2 | EXPECT_EQ '686f67650a' "$(sub::println 'hoge' | stream::bin2hex)" 3 | EXPECT_EQ '686f67650a0a' "$(sub::println $'hoge\n' | stream::bin2hex)" 4 | EXPECT_EQ '010203040a' "$(sub::println $'\x01\x02\x03\x04' | stream::bin2hex)" 5 | } 6 | -------------------------------------------------------------------------------- /doc/greg/ereg_match.sh.md: -------------------------------------------------------------------------------- 1 | # ereg_match 2 | ereg_match -- Checks if a string matches an EREG pattern. 3 | 4 | ereg_match checks if a string matches an EREG pattern. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Command form. 9 | bool sub::ereg_match(string pattern, string subject) 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/greg/greg_match.sh.md: -------------------------------------------------------------------------------- 1 | # greg_match 2 | greg_match -- Checks if a string matches a GREG pattern. 3 | 4 | greg_match checks if a string matches a GREG pattern. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Command form. 9 | bool sub::greg_match(string pattern, string subject) 10 | ``` 11 | -------------------------------------------------------------------------------- /library/10-func/testing/ASSERT_FALSE.sh: -------------------------------------------------------------------------------- 1 | # ASSERT_FALSE -- Expects a command fails. 2 | # 3 | # ASSERT_FALSE expects the command fails (returns a non-zero value). 4 | # 5 | # Usage: 6 | # void ASSERT_FALSE(string arguments,...) 7 | ASSERT_FALSE() { 8 | __ASSERT EXPECT_FALSE "$@" 9 | } 10 | -------------------------------------------------------------------------------- /test/func/EXPECT_TRUE_test.sh: -------------------------------------------------------------------------------- 1 | test::EXPECT_TRUE() { 2 | EXPECT_TRUE true 2>'/dev/null' 105>'/dev/null' 3 | ASSERT_TRUE [ "${IMOSH_TEST_IS_FAILED}" -eq 0 ] 4 | EXPECT_TRUE false 2>'/dev/null' 105>'/dev/null' 5 | ASSERT_TRUE [ "${IMOSH_TEST_IS_FAILED}" -ne 0 ] 6 | IMOSH_TEST_IS_FAILED=0 7 | } 8 | -------------------------------------------------------------------------------- /test/func/bin2hex_test.sh: -------------------------------------------------------------------------------- 1 | test::func_bin2hex() { 2 | EXPECT_EQ '686f6765' "$(sub::print 'hoge' | stream::bin2hex)" 3 | EXPECT_EQ 'e697a5e69cace8aa9e' "$(sub::print '日本語' | stream::bin2hex)" 4 | 5 | local variable 6 | func::bin2hex variable 'hoge' 7 | EXPECT_EQ '686f6765' "${variable}" 8 | } 9 | -------------------------------------------------------------------------------- /doc/strings/md5_file.sh.md: -------------------------------------------------------------------------------- 1 | # md5 2 | md5 -- Calculates the MD5 hash of a given file. 3 | 4 | ## Usage 5 | ```sh 6 | // 1. Function form. 7 | void func::md5_file(string* variable, string filename) 8 | // 2. Command form. 9 | void sub::md5_file(string filename, bool binary = false) > hash 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/testing/EXPECT_LT.sh.md: -------------------------------------------------------------------------------- 1 | # EXPECT_LT 2 | EXPECT_LT -- Expects first one is less than second one. 3 | 4 | EXPECT_LT expects first one is less than second one. 5 | 6 | ## Usage 7 | ```sh 8 | void EXPECT_LT(string target, string actual) 9 | void EXPECT_STRLT(string target, string actual) 10 | ``` 11 | -------------------------------------------------------------------------------- /test/func/EXPECT_DEATH_test.sh: -------------------------------------------------------------------------------- 1 | test::EXPECT_DEATH() { 2 | EXPECT_DEATH exit 1 2>'/dev/null' 105>'/dev/null' 3 | ASSERT_TRUE [ "${IMOSH_TEST_IS_FAILED}" -eq 0 ] 4 | EXPECT_DEATH true 2>'/dev/null' 105>'/dev/null' 5 | ASSERT_TRUE [ "${IMOSH_TEST_IS_FAILED}" -ne 0 ] 6 | IMOSH_TEST_IS_FAILED=0 7 | } 8 | -------------------------------------------------------------------------------- /test/func/EXPECT_EQ_test.sh: -------------------------------------------------------------------------------- 1 | test::EXPECT_EQ() { 2 | EXPECT_EQ 'abc' 'abc' 2>'/dev/null' 105>'/dev/null' 3 | ASSERT_TRUE [ "${IMOSH_TEST_IS_FAILED}" -eq 0 ] 4 | EXPECT_EQ 'abc' 'def' 2>'/dev/null' 105>'/dev/null' 5 | ASSERT_TRUE [ "${IMOSH_TEST_IS_FAILED}" -ne 0 ] 6 | IMOSH_TEST_IS_FAILED=0 7 | } 8 | -------------------------------------------------------------------------------- /test/func/EXPECT_FALSE_test.sh: -------------------------------------------------------------------------------- 1 | test::EXPECT_FALSE() { 2 | EXPECT_FALSE false 2>'/dev/null' 105>'/dev/null' 3 | ASSERT_TRUE [ "${IMOSH_TEST_IS_FAILED}" -eq 0 ] 4 | EXPECT_FALSE true 2>'/dev/null' 105>'/dev/null' 5 | ASSERT_TRUE [ "${IMOSH_TEST_IS_FAILED}" -ne 0 ] 6 | IMOSH_TEST_IS_FAILED=0 7 | } 8 | -------------------------------------------------------------------------------- /test/func/EXPECT_NE_test.sh: -------------------------------------------------------------------------------- 1 | test::EXPECT_NE() { 2 | EXPECT_NE 'abc' 'def' 2>'/dev/null' 105>'/dev/null' 3 | ASSERT_TRUE [ "${IMOSH_TEST_IS_FAILED}" -eq 0 ] 4 | EXPECT_NE 'abc' 'abc' 2>'/dev/null' 105>'/dev/null' 5 | ASSERT_TRUE [ "${IMOSH_TEST_IS_FAILED}" -ne 0 ] 6 | IMOSH_TEST_IS_FAILED=0 7 | } 8 | -------------------------------------------------------------------------------- /doc/strings/strcpy.sh.md: -------------------------------------------------------------------------------- 1 | # strcpy 2 | strcpy -- Copies a string from a variable to another variable. 3 | 4 | Assigns the content of a variable specified as source into destination. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::strcpy(string* destination, string *source) 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/testing/ASSERT_EQ.sh.md: -------------------------------------------------------------------------------- 1 | # ASSERT_EQ 2 | ASSERT_EQ -- Asserts two arguments are equal. 3 | 4 | ASSERT_EQ asserts two arguments are equal. 5 | 6 | ## Usage 7 | ```sh 8 | void ASSERT_EQ(string expected, string actual) 9 | ``` 10 | 11 | 12 | ## Alias 13 | ASSERT_STREQ is an alias of ASSERT_EQ. 14 | -------------------------------------------------------------------------------- /doc/testing/EXPECT_EQ.sh.md: -------------------------------------------------------------------------------- 1 | # EXPECT_EQ 2 | EXPECT_EQ -- Expects two arguments are equal. 3 | 4 | EXPECT_EQ expects two arguments are equal. 5 | 6 | ## Usage 7 | ```sh 8 | void EXPECT_EQ(string expected, string actual) 9 | ``` 10 | 11 | 12 | ## Alias 13 | EXPECT_STREQ is an alias of EXPECT_EQ. 14 | -------------------------------------------------------------------------------- /library/10-func/testing/ASSERT_ALIVE.sh: -------------------------------------------------------------------------------- 1 | # ASSERT_ALIVE -- Asserts a command successfully dies. 2 | # 3 | # ASSERT_ALIVE asserts that a command dies with a zero return value. 4 | # 5 | # Usage: 6 | # void ASSERT_ALIVE(string arguments,...) 7 | ASSERT_ALIVE() { 8 | __ASSERT EXPECT_ALIVE "$@" 9 | } 10 | -------------------------------------------------------------------------------- /doc/strings/addslashes.sh.md: -------------------------------------------------------------------------------- 1 | # addslashes 2 | addslashes -- Quotes a string with backslahses. 3 | 4 | Quotes string with backslashes. Single quote, double quote and backslash in 5 | subject are escaped. 6 | 7 | ## Usage 8 | ```sh 9 | // 1. Function form. 10 | void func::addslashes(string* subject) 11 | ``` 12 | -------------------------------------------------------------------------------- /doc/strings/escapeshellarg.sh.md: -------------------------------------------------------------------------------- 1 | # escapeshellarg 2 | escapeshellarg -- Escapes a variable as a shell argument. 3 | 4 | escapeshellarg escapes variable's content so as to use it as a shell argument. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::escapeshellarg(string* variable) 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/testing/ASSERT_NE.sh.md: -------------------------------------------------------------------------------- 1 | # ASSERT_NE 2 | ASSERT_NE -- Asserts two arguments are not equal. 3 | 4 | ASSERT_NE asserts two arguments are not equal. 5 | 6 | ## Usage 7 | ```sh 8 | void ASSERT_NE(string target, string actual) 9 | ``` 10 | 11 | 12 | ## Alias 13 | ASSERT_STRNE is an alias of ASSERT_NE. 14 | -------------------------------------------------------------------------------- /doc/testing/EXPECT_GT.sh.md: -------------------------------------------------------------------------------- 1 | # EXPECT_GT 2 | EXPECT_GT -- Expects first one is greater than second one. 3 | 4 | EXPECT_GT expects first one is greater than second one. 5 | 6 | ## Usage 7 | ```sh 8 | void EXPECT_GT(string target, string actual) 9 | void EXPECT_STRGT(string target, string actual) 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/testing/EXPECT_NE.sh.md: -------------------------------------------------------------------------------- 1 | # EXPECT_NE 2 | EXPECT_NE -- Expects two arguments are not equal. 3 | 4 | EXPECT_NE expects two arguments are not equal. 5 | 6 | ## Usage 7 | ```sh 8 | void EXPECT_NE(string target, string actual) 9 | ``` 10 | 11 | 12 | ## Alias 13 | EXPECT_STRNE is an alias of EXPECT_NE. 14 | -------------------------------------------------------------------------------- /library/10-func/testing/ASSERT_DEATH.sh: -------------------------------------------------------------------------------- 1 | # ASSERT_DEATH -- Asserts a command unsuccessfully dies. 2 | # 3 | # ASSERT_DEATH asserts that a command dies with a non-zero return value. 4 | # 5 | # Usage: 6 | # void ASSERT_DEATH(string arguments,...) 7 | ASSERT_DEATH() { 8 | __ASSERT EXPECT_DEATH "$@" 9 | } 10 | -------------------------------------------------------------------------------- /test/func/let_test.sh: -------------------------------------------------------------------------------- 1 | test::func_let() { 2 | local destination 3 | 4 | func::let destination 'test' 5 | EXPECT_EQ 'test' "${destination}" 6 | 7 | func::let destination $'abc\x00\x01\x02\x03\x04\x05\x06\x07\x08xyz' 8 | EXPECT_EQ $'abc\x00\x01\x02\x03\x04\x05\x06\x07\x08xyz' "${destination}" 9 | } 10 | -------------------------------------------------------------------------------- /doc/filesystem/tmpfile.sh.md: -------------------------------------------------------------------------------- 1 | # tmpfile 2 | tmpfile -- Creates a temporary file. 3 | 4 | tmpfile creates a temporary file with a unique name under ${TMPDIR}. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::tmpfile(string* path) 10 | // 2. Command form. 11 | void sub::tmpfile() > path 12 | ``` 13 | -------------------------------------------------------------------------------- /library/80-setup/log.sh: -------------------------------------------------------------------------------- 1 | # Close descriptors for logs beforehand for BASH3's bug. 2 | exec 101>&- 102>&- 103>&- 104>&- 3 | # Open descriptors for LOG without calling init_log. 4 | exec 101>/dev/null 102>/dev/null 103>/dev/null 104>/dev/null 5 | # Redirect log output to the current STDERR. 6 | exec 105>&- 7 | exec 105>&2 8 | -------------------------------------------------------------------------------- /doc/array/count.sh.md: -------------------------------------------------------------------------------- 1 | # count 2 | count -- Counts the number of elements. 3 | 4 | count counts the number of elements of an array. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form 9 | void func::count(int* result, string[]* values) 10 | // 2. Command form 11 | void sub::count(string[]* values) > result 12 | ``` 13 | -------------------------------------------------------------------------------- /doc/testing/EXPECT_LE.sh.md: -------------------------------------------------------------------------------- 1 | # EXPECT_LE 2 | EXPECT_LE -- Expects first one is less than or equal to second one. 3 | 4 | EXPECT_LE expects first one is less than or equal to second one. 5 | 6 | ## Usage 7 | ```sh 8 | void EXPECT_LE(string target, string actual) 9 | void EXPECT_STRLE(string target, string actual) 10 | ``` 11 | -------------------------------------------------------------------------------- /doc/testing/EXPECT_GE.sh.md: -------------------------------------------------------------------------------- 1 | # EXPECT_GE 2 | EXPECT_GE -- Expects first one is greater than or equal to second one. 3 | 4 | EXPECT_GE expects first one is greater than or equal to second one. 5 | 6 | ## Usage 7 | ```sh 8 | void EXPECT_GE(string target, string actual) 9 | void EXPECT_STRGE(string target, string actual) 10 | ``` 11 | -------------------------------------------------------------------------------- /test/func/time_test.sh: -------------------------------------------------------------------------------- 1 | test::time() { 2 | # The current time should be between 2015-01-01 and 2100-01-01. 3 | EXPECT_LT 1420038000 "$(sub::time)" 4 | EXPECT_GT 4102412400 "$(sub::time)" 5 | 6 | local result=0 7 | func::time result 8 | EXPECT_LT 1420038000 "${result}" 9 | EXPECT_GT 4102412400 "${result}" 10 | } 11 | -------------------------------------------------------------------------------- /doc/var/cast.sh.md: -------------------------------------------------------------------------------- 1 | # cast 2 | cast -- Casts a variable. 3 | 4 | Casts variable into a specified type. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | bool func::cast(variant* variable, string type) 10 | // 2. Function form. (Dies if conversion fails.) 11 | void func::cast_or_die(variant* variable, string type) 12 | ``` 13 | -------------------------------------------------------------------------------- /doc/strings/md5.sh.md: -------------------------------------------------------------------------------- 1 | # md5 2 | md5 -- Calculates a MD5 hash. 3 | 4 | ## Usage 5 | ```sh 6 | // 1. Function form. 7 | void func::md5(string* variable, string data) 8 | // 2. Command form. 9 | void sub::md5(string data, bool binary = false) > hash 10 | // 3. Stream form. 11 | void stream::md5(bool binary = false) < input > hash 12 | ``` 13 | -------------------------------------------------------------------------------- /test/func/cast_test.sh: -------------------------------------------------------------------------------- 1 | test::func_cast() { 2 | local variable 3 | 4 | variable=' 12345 ' 5 | EXPECT_TRUE func::cast variable STRING 6 | EXPECT_EQ ' 12345 ' "${variable}" 7 | EXPECT_TRUE func::cast variable INT 8 | EXPECT_EQ 12345 "${variable}" 9 | EXPECT_TRUE func::cast variable BOOL 10 | EXPECT_EQ 1 "${variable}" 11 | } 12 | -------------------------------------------------------------------------------- /tool/print-flag-variables.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # print-flag-variables prints flag variables in imosh. 3 | 4 | IMOSH_USE_DEFINE_FLAGS=1 5 | source "$(dirname "${BASH_SOURCE}")"/../imosh || exit 1 6 | 7 | sub::println '# This is automatically generated by print-flag-variables.' 8 | declare | grep ^FLAGS 9 | declare | grep ^__IMOSH_FLAGS 10 | -------------------------------------------------------------------------------- /doc/strings/sha1.sh.md: -------------------------------------------------------------------------------- 1 | # sha1 2 | sha1 -- Calculates a MD5 hash. 3 | 4 | ## Usage 5 | ```sh 6 | // 1. Function form. 7 | void func::sha1(string* variable, string data) 8 | // 2. Command form. 9 | void sub::sha1(string data, bool binary = false) > hash 10 | // 3. Stream form. 11 | void stream::sha1(bool binary = false) < input > hash 12 | ``` 13 | -------------------------------------------------------------------------------- /test/func/getmypid_test.sh: -------------------------------------------------------------------------------- 1 | test::func_getmypid() { 2 | local pid='' 3 | func::getmypid pid 4 | EXPECT_NE '' "${pid}" 5 | EXPECT_TRUE sub::greg_match '[1-9]*([0-9])' "${pid}" 6 | 7 | local child_pid="$(func::getmypid pid; echo "${pid}")" 8 | EXPECT_NE "${pid}" "${child_pid}" 9 | EXPECT_TRUE sub::greg_match '[1-9]*([0-9])' "${child_pid}" 10 | } 11 | -------------------------------------------------------------------------------- /doc/strings/ltrim.sh.md: -------------------------------------------------------------------------------- 1 | # ltrim 2 | ltrim -- Strips whitespace(s) from the beginning of a string. 3 | 4 | Strips whitespace (or other characters) from the beginning of a string. 5 | 6 | ## Usage 7 | ```sh 8 | // 1-a. Function form. 9 | void func::ltrim(string* variable) 10 | // 1-b. Function form. 11 | void func::ltrim(string* variable, string input) 12 | ``` 13 | -------------------------------------------------------------------------------- /doc/strings/trim.sh.md: -------------------------------------------------------------------------------- 1 | # trim 2 | trim -- Strips whitespaces from both sides. 3 | 4 | trim strips whitespaces (or other characters) from the beginning and end of a 5 | string. 6 | 7 | ## Usage 8 | ```sh 9 | // 1-a. Function format. 10 | void func::trim(string* output, string input) 11 | // 1-b. Function format. 12 | void func::trim(string* variable) 13 | ``` 14 | -------------------------------------------------------------------------------- /test/func/strcpy_test.sh: -------------------------------------------------------------------------------- 1 | test::func_strcpy() { 2 | local source destination 3 | 4 | source='test' 5 | func::strcpy destination source 6 | EXPECT_EQ 'test' "${destination}" 7 | 8 | source=$'abc\x00\x01\x02\x03\x04\x05\x06\x07\x08xyz' 9 | func::strcpy destination source 10 | EXPECT_EQ $'abc\x00\x01\x02\x03\x04\x05\x06\x07\x08xyz' "${destination}" 11 | } 12 | -------------------------------------------------------------------------------- /doc/strings/base64_decode.sh.md: -------------------------------------------------------------------------------- 1 | # base64_decode 2 | base64_decode -- Decodes data with MIME base64. 3 | 4 | ## Usage 5 | ```sh 6 | // 1. Function form. 7 | void func::base64_decode(string* variable, string data) 8 | // 2. Command form. 9 | void sub::base64_decode(string data) > output 10 | // 3. Stream form. 11 | void stream::base64_decode() < input > output 12 | ``` 13 | -------------------------------------------------------------------------------- /doc/strings/base64_encode.sh.md: -------------------------------------------------------------------------------- 1 | # base64_encode 2 | base64_encode -- Encodes data with MIME base64. 3 | 4 | ## Usage 5 | ```sh 6 | // 1. Function form. 7 | void func::base64_encode(string* variable, string data) 8 | // 2. Command form. 9 | void sub::base64_encode(string data) > output 10 | // 3. Stream form. 11 | void stream::base64_encode() < input > output 12 | ``` 13 | -------------------------------------------------------------------------------- /doc/strings/ord.sh.md: -------------------------------------------------------------------------------- 1 | # ord 2 | ord -- Gets a character's ASCII code. 3 | 4 | Sets ASCII value of character to variable. 5 | 6 | ## Usage 7 | ```sh 8 | // 1-a. Function form. 9 | void func::ord(string* variable, string character) 10 | // 1-b. Function form. 11 | void func::ord(string* variable) 12 | // 2. Command form. 13 | void sub::ord(string character) > output 14 | ``` 15 | -------------------------------------------------------------------------------- /library/10-func/var/let.sh: -------------------------------------------------------------------------------- 1 | # let -- Assigns a value into a variable. 2 | # 3 | # Assigns value into *destination. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # func::let(string* destination, string value) 8 | func::let() { 9 | if [ "$#" -eq 2 ]; then 10 | eval "${1}=\"\${2}\"" 11 | else 12 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 13 | fi 14 | } 15 | -------------------------------------------------------------------------------- /doc/var/strval.sh.md: -------------------------------------------------------------------------------- 1 | # strval 2 | strval -- Casts a variable as a string value. 3 | 4 | Casts variable into string type. 5 | 6 | ## Usage 7 | ```sh 8 | // 1-a. Function from. 9 | void func::strval(string* output, string input) 10 | // 1-b. Inplace function form. 11 | void func::strval(string* variable) 12 | // 2. Command form. 13 | bool sub::strval(string input) > output 14 | ``` 15 | -------------------------------------------------------------------------------- /test/func/ord_test.sh: -------------------------------------------------------------------------------- 1 | test::func_ord() { 2 | local variable='A' 3 | local result='' 4 | 5 | variable='A' 6 | func::ord result "${variable}" 7 | EXPECT_EQ 65 "${result}" 8 | 9 | variable='ABC' 10 | func::ord result "${variable}" 11 | EXPECT_EQ 65 "${result}" 12 | 13 | variable=$'\n' 14 | func::ord result "${variable}" 15 | EXPECT_EQ 10 "${result}" 16 | } 17 | -------------------------------------------------------------------------------- /doc/strings/print.sh.md: -------------------------------------------------------------------------------- 1 | # print 2 | print -- Prints a message. 3 | 4 | Print message to the standard output. While "echo" consumes flags, 5 | print does not consume any flags, so this is theoretically safe. 6 | 7 | ## Usage 8 | ```sh 9 | // DEPRECATED 10 | void func::print(string message...) > output 11 | // 1. Command form. 12 | void sub::print(string message...) > output 13 | ``` 14 | -------------------------------------------------------------------------------- /doc/strings/strtolower.sh.md: -------------------------------------------------------------------------------- 1 | # strtolower 2 | strtolower -- Makes a string lowercase. 3 | 4 | strtolower makes a string lowercase. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::strtolower(string* variable) 10 | // 2. Subroutine form. 11 | void sub::strtolower(string input) > output 12 | // 3. Stream form. 13 | void stream::strtolower() < input > output 14 | ``` 15 | -------------------------------------------------------------------------------- /doc/strings/strtoupper.sh.md: -------------------------------------------------------------------------------- 1 | # strtoupper 2 | strtoupper -- Makes a string uppercase. 3 | 4 | strtoupper makes a string uppercase. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::strtoupper(string* variable) 10 | // 2. Subroutine form. 11 | void sub::strtoupper(string input) > output 12 | // 3. Stream form. 13 | void stream::strtoupper() < input > output 14 | ``` 15 | -------------------------------------------------------------------------------- /library/10-func/logging/DEPRECATED.sh: -------------------------------------------------------------------------------- 1 | # DEPRECATED -- Declares as deprecated. 2 | # 3 | # DEPRECATED displays an error message and a stack trace. 4 | # 5 | # Usage: 6 | # void DEPRECATED() 7 | DEPRECATED() { 8 | if [ "$#" -eq 0 ]; then 9 | LOG ERROR "$(imosh::stack_trace 'This is deprecated.' 2>&1)" 10 | else 11 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 12 | fi 13 | } 14 | -------------------------------------------------------------------------------- /library/10-func/testing/ASSERT_EQ.sh: -------------------------------------------------------------------------------- 1 | # ASSERT_EQ -- Asserts two arguments are equal. 2 | # 3 | # ASSERT_EQ asserts two arguments are equal. 4 | # 5 | # Usage: 6 | # void ASSERT_EQ(string expected, string actual) 7 | # 8 | # Alias: 9 | # ASSERT_STREQ is an alias of ASSERT_EQ. 10 | ASSERT_EQ() { 11 | __ASSERT EXPECT_EQ "$@" 12 | } 13 | 14 | ASSERT_STREQ() { 15 | ASSERT_EQ "$@" 16 | } 17 | -------------------------------------------------------------------------------- /doc/array/sort.sh.md: -------------------------------------------------------------------------------- 1 | # sort 2 | sort -- Sorts elements. 3 | 4 | sort sorts elements. The function form sorts elements in a variable in place. 5 | The stream form applies sort to every line. Every line is treated as 6 | elements. 7 | 8 | ## Usage 9 | ```sh 10 | // 1. Function form. 11 | void func::sort(string[]* variable) 12 | // 2. Stream form. 13 | void stream::sort() < input > output 14 | ``` 15 | -------------------------------------------------------------------------------- /doc/misc/throttle.sh.md: -------------------------------------------------------------------------------- 1 | # throttle 2 | throttle -- Throttles by the number of child processes. 3 | 4 | throttle waits that the number of the child processes is less than a limit. 5 | Firstly, throttle waits for 0.1 second, and the n-th retry (n < 10) waits for 6 | n * 0.1 seconds. The n-th retry (n >= 10) waits for 1 second. 7 | 8 | ## Usage 9 | ```sh 10 | void sub::throttle(int limit) 11 | ``` 12 | -------------------------------------------------------------------------------- /library/10-func/testing/ASSERT_NE.sh: -------------------------------------------------------------------------------- 1 | # ASSERT_NE -- Asserts two arguments are not equal. 2 | # 3 | # ASSERT_NE asserts two arguments are not equal. 4 | # 5 | # Usage: 6 | # void ASSERT_NE(string target, string actual) 7 | # 8 | # Alias: 9 | # ASSERT_STRNE is an alias of ASSERT_NE. 10 | ASSERT_NE() { 11 | __ASSERT EXPECT_NE "$@" 12 | } 13 | 14 | ASSERT_STRNE() { 15 | ASSERT_NE "$@" 16 | } 17 | -------------------------------------------------------------------------------- /library/00-header/header.sh: -------------------------------------------------------------------------------- 1 | # Enables error checking if imosh is called in a script. 2 | if [ "${-//i/}" == "${-}" ]; then 3 | # Make a script fail when 4 | # - a command returns non-zero value (-e). 5 | # - an undefined variable is referred (-u). 6 | set -e -u 7 | # Return if imosh is already loaded. 8 | if [ "${__IMOSH_IS_LOADED+loaded}" = 'loaded' ]; then 9 | return 10 | fi 11 | fi 12 | -------------------------------------------------------------------------------- /doc/array/array_is_empty.sh.md: -------------------------------------------------------------------------------- 1 | # array_is_empty 2 | array_is_empty -- Checks if an array is empty. 3 | 4 | array_is_empty returns true iff a given array is empty. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Command form. 9 | bool sub::array_is_empty(string[]* variable) 10 | ``` 11 | 12 | 13 | ## Example 14 | ```sh 15 | array=() 16 | if sub::array_is_empty array; then 17 | echo 'array is empty.' 18 | fi 19 | ``` 20 | -------------------------------------------------------------------------------- /doc/var/enumval.sh.md: -------------------------------------------------------------------------------- 1 | # enumval 2 | enumval -- Casts a variable as an enum value. 3 | 4 | Casts variable into enum type. If it fails, returns 1. 5 | 6 | ## Usage 7 | ```sh 8 | // 1-a. Function from. 9 | bool func::enumval(string* output, string input) 10 | // 1-b. Inplace function form. 11 | bool func::enumval(string* variable) 12 | // 2. Command form. 13 | bool sub::enumval(string input) > output 14 | ``` 15 | -------------------------------------------------------------------------------- /doc/var/intval.sh.md: -------------------------------------------------------------------------------- 1 | # intval 2 | intval -- Casts a variable as an integer value. 3 | 4 | Casts variable into integer type. If it fails, returns 1. 5 | 6 | ## Usage 7 | ```sh 8 | // 1-a. Function from. 9 | bool func::intval(string* output, string input) 10 | // 1-b. Inplace function form. 11 | bool func::intval(string* variable) 12 | // 2. Command form. 13 | bool sub::intval(string input) > output 14 | ``` 15 | -------------------------------------------------------------------------------- /doc/var/floatval.sh.md: -------------------------------------------------------------------------------- 1 | # floatval 2 | floatval -- Casts a variable as a float value. 3 | 4 | Casts variable into float type. If it fails, returns 1. 5 | 6 | ## Usage 7 | ```sh 8 | // 1-a. Function form. 9 | bool func::floatval(string* output, string input) 10 | // 1-b. Inplace function form. 11 | bool func::floatval(string* variable) 12 | // 2. Command form. 13 | bool sub::floatval(string input) > output 14 | ``` 15 | -------------------------------------------------------------------------------- /doc/strings/println.sh.md: -------------------------------------------------------------------------------- 1 | # println 2 | println -- Prints a message with a new line. 3 | 4 | Print message to the standard output with a new line. While "echo" consumes 5 | flags, println does not consume any flags, so this is theoretically safe. 6 | 7 | ## Usage 8 | ```sh 9 | // DEPRECATED. 10 | void func::println(string message...) > output 11 | // 1. Command form. 12 | void sub::println(string message...) > output 13 | ``` 14 | -------------------------------------------------------------------------------- /library/10-func/misc/atexit.sh: -------------------------------------------------------------------------------- 1 | # atexit -- Registers a function on shutdown. 2 | # 3 | # atexit registers a function to be excuted on shutdown. 4 | # 5 | # Usage: 6 | # // 1. Command form. 7 | # void sub::atexit(string command) 8 | sub::atexit() { 9 | if [ "$#" -eq 1 ]; then 10 | sub::println "${1}" >> "${__IMOSH_CORE_TMPDIR}/atexit.sh" 11 | else 12 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 13 | fi 14 | } 15 | -------------------------------------------------------------------------------- /test/func/is_array_test.sh: -------------------------------------------------------------------------------- 1 | test::in_array() { 2 | local array1=() 3 | local array2=('') 4 | local array3=('foo') 5 | local string1='' 6 | local string2='foo' 7 | 8 | EXPECT_TRUE sub::is_array array1 9 | EXPECT_TRUE sub::is_array array2 10 | EXPECT_TRUE sub::is_array array3 11 | 12 | EXPECT_FALSE sub::is_array array3[0] 13 | EXPECT_FALSE sub::is_array string1 14 | EXPECT_FALSE sub::is_array string2 15 | } 16 | -------------------------------------------------------------------------------- /test/parse_arguments_test.sh: -------------------------------------------------------------------------------- 1 | test::parse_arguments() { 2 | local ARGS_foo= ARGS_bar= 3 | set -- --foo arg1 --bar=$'abc\'def -- ghi;jkl \n mno"=pqr \t stu' arg2 4 | eval "${IMOSH_PARSE_ARGUMENTS}" 5 | EXPECT_EQ 2 "${#IMOSH_ARGV[@]}" 6 | EXPECT_EQ arg1 "${IMOSH_ARGV[0]}" 7 | EXPECT_EQ arg2 "${IMOSH_ARGV[1]}" 8 | EXPECT_EQ 1 "${ARGS_foo}" 9 | EXPECT_EQ $'abc\'def -- ghi;jkl \n mno"=pqr \t stu' "${ARGS_bar}" 10 | } 11 | -------------------------------------------------------------------------------- /doc/greg/greg_replace.sh.md: -------------------------------------------------------------------------------- 1 | # greg_replace 2 | greg_replace -- Replace a GREG pattern with a string. 3 | 4 | greg_replace replaces substrings matching a pattern with a string. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::greg_replace(string* subject, string pattern, string replace) 10 | // 2. Command form. 11 | void sub::greg_replace( 12 | string subject, string pattern, string replace) > output 13 | ``` 14 | -------------------------------------------------------------------------------- /doc/var/isset.sh.md: -------------------------------------------------------------------------------- 1 | # isset 2 | isset -- Checks if a variable exists. 3 | 4 | Returns true iff variable exists. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::isset(bool* result, variant* variable) 10 | // 2. Command form. 11 | bool func::isset(variant* variable) 12 | ``` 13 | 14 | 15 | ## CAVEATS 16 | func::isset returns true for uninitialized variables in BASH 3, and returns 17 | false for them in BASH 4. 18 | -------------------------------------------------------------------------------- /library/80-setup/mktemp.sh: -------------------------------------------------------------------------------- 1 | # Prepares a temporary directory and sets its path to TMPDIR. 2 | TMPDIR="${TMPDIR:-/tmp}" 3 | TMPDIR="${TMPDIR%/}" 4 | export TMPDIR="${TMPDIR}/imosh.${RANDOM}.${RANDOM}.${RANDOM}.${RANDOM}" 5 | export IMOSH_TMPDIR="${TMPDIR}" 6 | export __IMOSH_CORE_TMPDIR="${TMPDIR}/.imosh" 7 | if ! mkdir -p "${__IMOSH_CORE_TMPDIR}"; then 8 | LOG FATAL "Failed to create a temporary directory: ${__IMOSH_CORE_TMPDIR}" 9 | fi 10 | -------------------------------------------------------------------------------- /doc/greg/greg_split.sh.md: -------------------------------------------------------------------------------- 1 | # greg_split 2 | greg_split -- Splits a string with a GREG pattern. 3 | 4 | greg_split splits a string with a GREG pattern. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::greg_split(string[]* variable, string pattern, string value) 10 | // 2. Command form. 11 | void sub::greg_split(string pattern, string value) 12 | ``` 13 | 14 | 15 | ## Caveat 16 | greg_split does not support \x02 for value. 17 | -------------------------------------------------------------------------------- /doc/filesystem/fgets.sh.md: -------------------------------------------------------------------------------- 1 | # fgets 2 | fgets -- Gets a line from STDIN. 3 | 4 | fgets reads a line and regards it as a result. fgets does not strip a 5 | trailing new line. The function form sets the result to a variable. The 6 | subroutine form outputs the result to the standard output. 7 | 8 | ## Usage 9 | ```sh 10 | // 1. Function form. 11 | bool func::fgets(string* variable) 12 | // 2. Subroutine form. 13 | bool sub::fgets() > line 14 | ``` 15 | -------------------------------------------------------------------------------- /doc/funchand/function_exists.sh.md: -------------------------------------------------------------------------------- 1 | # function_exists 2 | function_exists -- Returns true iff the given function has been defined. 3 | 4 | Checks the list of defined functions, both built-in (internal) and 5 | user-defined, for function_name. 6 | 7 | ## Usage 8 | ```sh 9 | // 1. Command form. 10 | bool sub::function_exists(string function_name) 11 | ``` 12 | 13 | 14 | ## Examples 15 | ```sh 16 | EXPECT_TRUE sub::function_exists func::array 17 | ``` 18 | -------------------------------------------------------------------------------- /library/10-func/strings/strcpy.sh: -------------------------------------------------------------------------------- 1 | # strcpy -- Copies a string from a variable to another variable. 2 | # 3 | # Assigns the content of a variable specified as source into destination. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::strcpy(string* destination, string *source) 8 | func::strcpy() { 9 | if [ "$#" -eq 2 ]; then 10 | eval "${1}=\"\${${2}}\"" 11 | else 12 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 13 | fi 14 | } 15 | -------------------------------------------------------------------------------- /library/10-func/testing/EXPECT_ALIVE.sh: -------------------------------------------------------------------------------- 1 | # EXPECT_ALIVE -- Expects a command successfully exits. 2 | # 3 | # EXPECT_ALIVE expects that a command exits with a zero return value. 4 | # 5 | # Usage: 6 | # void EXPECT_ALIVE(string arguments,...) 7 | EXPECT_ALIVE() { 8 | "$@" & 9 | if ! wait "$!"; then 10 | LOG ERROR 'Command died unexpectedly:' "$@" 11 | FAILURE 12 | return 13 | fi 14 | LOG INFO 'Command did not die as expected:' "$@" 15 | } 16 | -------------------------------------------------------------------------------- /test/script/fd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source "$(dirname "${BASH_SOURCE}")"/../../imosh || exit 1 4 | DEFINE_int 'descriptor' 1 'File descriptor to show a message to.' 5 | DEFINE_bool 'close' false 'Close the descriptor beforehand.' 6 | eval "${IMOSH_INIT}" 7 | 8 | if (( FLAGS_close )); then 9 | eval "exec ${FLAGS_descriptor}>&-" 10 | fi 11 | eval "exec ${FLAGS_descriptor}>/dev/null" 12 | eval "echo 'This message should not be shown.' >&${FLAGS_descriptor}" 13 | -------------------------------------------------------------------------------- /doc/math/rand.sh.md: -------------------------------------------------------------------------------- 1 | # rand 2 | rand -- Generates a random integer. 3 | 4 | rand generates a random integer. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::rand(int* variable, int minimum, int maximum) 10 | void func::rand(int* variable, int maximum) 11 | void func::rand(int* variable) 12 | // 2. Command form. 13 | void sub::rand(int minimum, int maximum) > output 14 | void sub::rand(int maximum) > output 15 | void sub::rand() > output 16 | ``` 17 | -------------------------------------------------------------------------------- /library/10-func/testing/EXPECT_DEATH.sh: -------------------------------------------------------------------------------- 1 | # EXPECT_DEATH -- Expects a command unsuccessfully dies. 2 | # 3 | # EXPECT_DEATH expects that a command dies with a non-zero return value. 4 | # 5 | # Usage: 6 | # void EXPECT_DEATH(string arguments,...) 7 | EXPECT_DEATH() { 8 | "$@" & 9 | if wait "$!"; then 10 | LOG ERROR 'Command did not die unexpectedly:' "$@" 11 | FAILURE 12 | return 13 | fi 14 | LOG INFO 'Command died as expected:' "$@" 15 | } 16 | -------------------------------------------------------------------------------- /test/func/getchildpids_test.sh: -------------------------------------------------------------------------------- 1 | test::func_getchildpids() { 2 | local pids=() 3 | func::getchildpids pids 4 | EXPECT_TRUE [ "${#pids[*]}" -le 1 ] 5 | 6 | local child_pids=() 7 | for i in {1..10}; do 8 | sleep 10 & 9 | child_pids+=("$!") 10 | done 11 | func::getchildpids pids 12 | EXPECT_TRUE [ "${#pids[*]}" -ge 10 -a "${#pids[*]}" -le 11 ] 13 | 14 | for child_pid in "${child_pids[@]}"; do 15 | kill -9 "${child_pid}" 16 | done 17 | } 18 | -------------------------------------------------------------------------------- /doc/strings/bin2hex.sh.md: -------------------------------------------------------------------------------- 1 | # bin2hex 2 | bin2hex -- Converts a binary string into hexadecimal representation. 3 | 4 | Converts binary data into hexadecimal representation. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::bin2hex(string* hexadecimal_output, string binary_input) 10 | // 2. Command form. 11 | void sub::bin2hex(string binary_input) > hexadecimal_output 12 | // 3. Stream form. 13 | void stream::bin2hex() < binary_input > hexadecimal_output 14 | ``` 15 | -------------------------------------------------------------------------------- /doc/strings/hex2bin.sh.md: -------------------------------------------------------------------------------- 1 | # hex2bin 2 | hex2bin -- Decodes a hexadecimally encoded binary string. 3 | 4 | Decodes a hexadecimally encoded binary string. 5 | 6 | ## Usage 7 | ```sh 8 | // 1-a. Function form. 9 | void func::hex2bin(string* output, string input) 10 | // 1-b. Function form. 11 | void func::hex2bin(string* variable) 12 | // 2. Command form. 13 | void sub::hex2bin(string input) > output 14 | // 3. Stream form. 15 | void stream::hex2bin() < input > output 16 | ``` 17 | -------------------------------------------------------------------------------- /library/10-func/testing/EXPECT_TRUE.sh: -------------------------------------------------------------------------------- 1 | # EXPECT_TRUE -- Expects a command succeeds. 2 | # 3 | # EXPECT_TRUE expects the command succeeds (returns 0). 4 | # 5 | # Usage: 6 | # void EXPECT_TRUE(string arguments,...) 7 | EXPECT_TRUE() { 8 | if ! "$@"; then 9 | sub::println ' Actual: false' >&2 10 | sub::println 'Expected: true' >&2 11 | LOG ERROR 'EXPECT_TRUE failed:' "$@" 12 | FAILURE 13 | return 14 | fi 15 | LOG INFO 'EXPECT_TRUE passes:' "$@" 16 | } 17 | -------------------------------------------------------------------------------- /doc/strings/rtrim.sh.md: -------------------------------------------------------------------------------- 1 | # rtrim 2 | rtrim -- Strips whitespace(s) from the end of a string. 3 | 4 | Strips whitespace (or other characters) from the end of a string. 5 | 6 | ## Usage 7 | ```sh 8 | // 1-a. Function form. 9 | void func::rtrim(string* output, string input) 10 | // 1-b. Function form. 11 | void func::rtrim(string* variable) 12 | // 2. Command form. 13 | void sub::rtrim(string value) > output 14 | // 3. Stream form. 15 | void stream::rtrim() < input > output 16 | ``` 17 | -------------------------------------------------------------------------------- /test/func/md5_file_test.sh: -------------------------------------------------------------------------------- 1 | test::md5_file() { 2 | touch "${TMPDIR}/md5_file" 3 | EXPECT_EQ 'd41d8cd98f00b204e9800998ecf8427e' \ 4 | "$(sub::md5_file "${TMPDIR}/md5_file")" 5 | 6 | echo -n 'foo' >"${TMPDIR}/md5_file" 7 | EXPECT_EQ 'acbd18db4cc2f85cedef654fccc4a4d8' \ 8 | "$(sub::md5_file "${TMPDIR}/md5_file")" 9 | 10 | local result='' 11 | func::md5_file result "${TMPDIR}/md5_file" 12 | EXPECT_EQ 'acbd18db4cc2f85cedef654fccc4a4d8' "${result}" 13 | } 14 | -------------------------------------------------------------------------------- /library/10-func/testing/EXPECT_FALSE.sh: -------------------------------------------------------------------------------- 1 | # EXPECT_FALSE -- Expects a command fails. 2 | # 3 | # EXPECT_FALSE expects the command fails (returns a non-zero value). 4 | # 5 | # Usage: 6 | # void EXPECT_FALSE(string arguments,...) 7 | EXPECT_FALSE() { 8 | if "$@"; then 9 | sub::println ' Actual: true' >&2 10 | sub::println 'Expected: false' >&2 11 | LOG ERROR 'EXPECT_FALSE failed:' "$@" 12 | FAILURE 13 | return 14 | fi 15 | LOG INFO 'EXPECT_FALSE passes:' "$@" 16 | } 17 | -------------------------------------------------------------------------------- /test/func/strval_test.sh: -------------------------------------------------------------------------------- 1 | test::func_strval() { 2 | local variable 3 | 4 | variable=12345 5 | func::strval variable 6 | EXPECT_EQ 12345 "${variable}" 7 | 8 | variable=-12345678901234567890 9 | func::strval variable 10 | EXPECT_EQ -12345678901234567890 "${variable}" 11 | 12 | variable=' -123.456 ' 13 | func::strval variable 14 | EXPECT_EQ ' -123.456 ' "${variable}" 15 | 16 | variable='abc' 17 | func::strval variable 18 | EXPECT_EQ 'abc' "${variable}" 19 | } 20 | -------------------------------------------------------------------------------- /test/func/readarray_test.sh: -------------------------------------------------------------------------------- 1 | test::func_readarray() { 2 | local variable='' 3 | local LINE=() NEWLINE='' 4 | 5 | sub::print $'abc\tdef\tghi\n' > "${TMPDIR}/test" 6 | exec < "${TMPDIR}/test" 7 | IFS=$' \t\n' EXPECT_TRUE func::readarray 8 | func::implode variable ',' LINE 9 | LOG INFO "LINE: ${LINE[*]}" 10 | EXPECT_EQ 'abc,def,ghi' "${variable}" 11 | EXPECT_EQ $'\n' "${NEWLINE}" 12 | EXPECT_FALSE func::readarray 13 | EXPECT_EQ 0 "${#LINE[*]}" 14 | EXPECT_EQ '' "${NEWLINE}" 15 | } 16 | -------------------------------------------------------------------------------- /doc/strings/str_replace.sh.md: -------------------------------------------------------------------------------- 1 | # str_replace 2 | str_replace -- Replaces a substring with another substring. 3 | 4 | Replace search with replace in *subject. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::str_replace(string* subject, string search, string replace) 10 | // 2. Command form. 11 | void sub::str_replace( 12 | string input, string search, string replace) > output 13 | // 3. Stream form. 14 | void stream::str_replace(string search, string replace) < input > output 15 | ``` 16 | -------------------------------------------------------------------------------- /library/10-func/info/is_main.sh: -------------------------------------------------------------------------------- 1 | # is_main -- Returns 0 iff caller is in the main script. 2 | # 3 | # Usage: 4 | # // 1. Command form. 5 | # void sub::is_main() 6 | sub::is_main() { 7 | if [ "$#" -eq 0 ]; then 8 | local __is_main_depth="${#BASH_SOURCE[*]}" 9 | if [ "${BASH_SOURCE[1]}" = \ 10 | "${BASH_SOURCE[$((__is_main_depth - 1))]}" ]; then 11 | return 0 12 | else 13 | return 1 14 | fi 15 | else 16 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 17 | fi 18 | } 19 | -------------------------------------------------------------------------------- /doc/array/array_values.sh.md: -------------------------------------------------------------------------------- 1 | # array_values 2 | array_values -- Copies elements from an array to an array. 3 | 4 | array_values copies elements in an array variable into an array variable. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::array_values(string[]* output, string[]* input) 10 | // 2. Command form. 11 | void sub::array_values(string[]* input) > output 12 | ``` 13 | 14 | 15 | ## Examples 16 | ```sh 17 | array=(foo bar) 18 | EXPECT_EQ 'foo,bar' "$(IFS=, sub::array_values array)" 19 | ``` 20 | -------------------------------------------------------------------------------- /test/func/intval_test.sh: -------------------------------------------------------------------------------- 1 | test::func_intval() { 2 | local variable 3 | 4 | variable=12345 5 | func::intval variable 6 | EXPECT_EQ 12345 "${variable}" 7 | 8 | variable=-12345678901234567890 9 | func::intval variable 10 | EXPECT_EQ -12345678901234567890 "${variable}" 11 | 12 | variable=' -123.456 ' 13 | func::intval variable 14 | EXPECT_EQ -123 "${variable}" 15 | 16 | variable='abc' 17 | if func::intval variable; then 18 | LOG FATAL 'abc cannot be cast to an integer.' 19 | fi 20 | } 21 | -------------------------------------------------------------------------------- /doc/strings/substr.sh.md: -------------------------------------------------------------------------------- 1 | # substr 2 | substr -- Returns a substring. 3 | 4 | substr returns a substring of a string specfied by start and length. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::substr( 10 | string* output, string input, string start, string length) 11 | void func::substr( 12 | string* output, string input, string start) 13 | // 2. Command form. 14 | void sub::substr(string input, string start, string length) > output 15 | void sub::substr(string input, string start) > output 16 | ``` 17 | -------------------------------------------------------------------------------- /library/10-func/strings/print.sh: -------------------------------------------------------------------------------- 1 | # print -- Prints a message. 2 | # 3 | # Print message to the standard output. While "echo" consumes flags, 4 | # print does not consume any flags, so this is theoretically safe. 5 | # 6 | # Usage: 7 | # // DEPRECATED 8 | # void func::print(string message...) > output 9 | # // 1. Command form. 10 | # void sub::print(string message...) > output 11 | func::print() { 12 | DEPRECATED 13 | printf "%s" "$*" 14 | } 15 | 16 | sub::print() { 17 | IFS=' ' eval 'printf "%s" "$*"' 18 | } 19 | -------------------------------------------------------------------------------- /library/10-func/filesystem/readarray.sh: -------------------------------------------------------------------------------- 1 | # readarray -- Reads a line as an array. 2 | # 3 | # readarray reads a line and sets its content to LINE and its trailing new line 4 | # to NEWLINE. 5 | # 6 | # Usage: 7 | # void func::readarray() < input 8 | func::readarray() { 9 | if [ "$#" -eq 0 ]; then 10 | NEWLINE='' 11 | LINE='' 12 | if ! func::readline; then 13 | LINE=() 14 | return 1 15 | fi 16 | func::array LINE "${LINE}" 17 | else 18 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 19 | fi 20 | } 21 | -------------------------------------------------------------------------------- /library/10-func/testing/FAILURE.sh: -------------------------------------------------------------------------------- 1 | # FAILURE -- Declares a test case failed. 2 | # 3 | # FAILURE sets the test case as failed and shows a stack trace. 4 | # 5 | # Usage: 6 | # void FAILURE() 7 | FAILURE() { 8 | IMOSH_TEST_IS_FAILED=1 9 | imosh::stack_trace --skip_imosh '*** Check failure ***' 10 | } 11 | 12 | __ASSERT() { 13 | local last_state="${IMOSH_TEST_IS_FAILED}" 14 | IMOSH_TEST_IS_FAILED=0 15 | "$@" 16 | if (( IMOSH_TEST_IS_FAILED )); then 17 | exit 1 18 | fi 19 | IMOSH_TEST_IS_FAILED="${last_state}" 20 | } 21 | -------------------------------------------------------------------------------- /test/func/floatval_test.sh: -------------------------------------------------------------------------------- 1 | test::func_floatval() { 2 | local variable 3 | 4 | variable=12345 5 | func::floatval variable 6 | EXPECT_EQ 12345 "${variable}" 7 | 8 | variable=-12345678901234567890 9 | func::floatval variable 10 | EXPECT_EQ -12345678901234567890 "${variable}" 11 | 12 | variable=' -123.456 ' 13 | func::floatval variable 14 | EXPECT_EQ -123.456 "${variable}" 15 | 16 | variable='abc' 17 | if func::floatval variable; then 18 | LOG FATAL 'abc cannot be cast to a float value.' 19 | fi 20 | } 21 | -------------------------------------------------------------------------------- /test/func/array_keys_test.sh: -------------------------------------------------------------------------------- 1 | test::func_array_keys() { 2 | local input=('a' 'b' 'c') 3 | local variable=() 4 | 5 | func::array_keys variable input 6 | EXPECT_EQ '0,1,2' "$(sub::implode ',' variable)" 7 | EXPECT_EQ '0 1 2' "$(sub::array_keys variable)" 8 | 9 | unset input[1] 10 | func::array_keys variable input 11 | EXPECT_EQ '0,2' "$(sub::implode ',' variable)" 12 | 13 | input=() 14 | func::array_keys variable input 15 | EXPECT_EQ '' "$(sub::implode ',' variable)" 16 | EXPECT_EQ '' "$(sub::array_keys variable)" 17 | } 18 | -------------------------------------------------------------------------------- /doc/array/array_keys.sh.md: -------------------------------------------------------------------------------- 1 | # array_keys 2 | array_keys -- Gets an array's keys. 3 | 4 | array_keys gets an array's keys. 5 | 6 | ## Usage 7 | ```sh 8 | // 1. Function form. 9 | void func::array_keys(string[]* output, string[]* input) 10 | // 2. Command form. 11 | void sub::array_keys(string[]* input) > output 12 | ``` 13 | 14 | 15 | ## Examples 16 | ```sh 17 | array=([0]=abc [2]=def) 18 | EXPECT_EQ '0,2' "$(IFS=, sub::array_keys array)" 19 | func::array_keys result array 20 | EXPECT_EQ '0' "${result[0]}" 21 | EXPECT_EQ '2' "${result[1]}" 22 | ``` 23 | -------------------------------------------------------------------------------- /library/10-func/array/array.sh: -------------------------------------------------------------------------------- 1 | # array -- Creates an array. 2 | # 3 | # array creates an array from a string with IFS separators. 4 | # 5 | # Usage: 6 | # // 1. Function form 7 | # void func::array(string[]* result, string input) 8 | func::array() { 9 | if [ "$#" -eq 2 ]; then 10 | local __array_ifs="${IFS}" 11 | func::str_replace __array_ifs '[' '\[' 12 | func::str_replace __array_ifs ']' '\]' 13 | func::greg_split "${1}" "[${__array_ifs}]" "${2}" 14 | else 15 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 16 | fi 17 | } 18 | -------------------------------------------------------------------------------- /test/func/escapeshellarg_test.sh: -------------------------------------------------------------------------------- 1 | test::func_escapeshellarg() { 2 | local variable 3 | 4 | variable='' 5 | func::escapeshellarg variable 6 | EXPECT_EQ "''" "${variable}" 7 | 8 | variable='test' 9 | func::escapeshellarg variable 10 | EXPECT_EQ "test" "${variable}" 11 | 12 | variable='abc,01234-foo.bar/baz' 13 | func::escapeshellarg variable 14 | EXPECT_EQ "abc,01234-foo.bar/baz" "${variable}" 15 | 16 | variable="abc def'ghi\\jkl" 17 | func::escapeshellarg variable 18 | EXPECT_EQ "'abc def'\\''ghi\\jkl'" "${variable}" 19 | } 20 | -------------------------------------------------------------------------------- /doc/misc/usage.sh.md: -------------------------------------------------------------------------------- 1 | # usage 2 | usage -- Shows a usage message. 3 | 4 | usage shows a usage message based on a header comment. A header comment 5 | consists of consecutive comment lines. Comment lines starting with "#!" are 6 | ignored. 7 | 8 | ## Usage 9 | ```sh 10 | void sub::usage(string file) > output 11 | ``` 12 | 13 | 14 | ## Options 15 | * --format=text 16 | * Select one fromat from text/markdown/groff. 17 | * --title=true 18 | * Treat the first line as title. 19 | * --markdown_heading='' 20 | * Prepend a string to every heading. 21 | -------------------------------------------------------------------------------- /library/10-func/strings/addslashes.sh: -------------------------------------------------------------------------------- 1 | # addslashes -- Quotes a string with backslahses. 2 | # 3 | # Quotes string with backslashes. Single quote, double quote and backslash in 4 | # subject are escaped. 5 | # 6 | # Usage: 7 | # // 1. Function form. 8 | # void func::addslashes(string* subject) 9 | func::addslashes() { 10 | if [ "$#" -eq 1 ]; then 11 | func::str_replace "${1}" '\' '\\' 12 | func::str_replace "${1}" "'" "\\'" 13 | func::str_replace "${1}" '"' '\"' 14 | else 15 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 16 | fi 17 | } 18 | -------------------------------------------------------------------------------- /test/func/boolval_test.sh: -------------------------------------------------------------------------------- 1 | test::func_boolval() { 2 | local variable 3 | 4 | variable=12345 5 | EXPECT_TRUE func::boolval variable 6 | EXPECT_EQ 1 "${variable}" 7 | 8 | variable=0 9 | EXPECT_TRUE func::boolval variable 10 | EXPECT_EQ 0 "${variable}" 11 | 12 | variable=true 13 | EXPECT_TRUE func::boolval variable 14 | EXPECT_EQ 1 "${variable}" 15 | 16 | variable=false 17 | EXPECT_TRUE func::boolval variable 18 | EXPECT_EQ 0 "${variable}" 19 | 20 | variable='hoge' 21 | EXPECT_FALSE func::boolval variable 22 | EXPECT_EQ 0 "${variable}" 23 | } 24 | -------------------------------------------------------------------------------- /library/10-func/strings/println.sh: -------------------------------------------------------------------------------- 1 | # println -- Prints a message with a new line. 2 | # 3 | # Print message to the standard output with a new line. While "echo" consumes 4 | # flags, println does not consume any flags, so this is theoretically safe. 5 | # 6 | # Usage: 7 | # // DEPRECATED. 8 | # void func::println(string message...) > output 9 | # // 1. Command form. 10 | # void sub::println(string message...) > output 11 | func::println() { 12 | DEPRECATED 13 | printf "%s\n" "$*" 14 | } 15 | 16 | sub::println() { 17 | IFS=' ' eval 'printf "%s\n" "$*"' 18 | } 19 | -------------------------------------------------------------------------------- /test/func/greg_replace_test.sh: -------------------------------------------------------------------------------- 1 | test::func_greg_replace() { 2 | local variable 3 | 4 | variable='abc def ghi' 5 | func::greg_replace variable '[[:space:]]' 'x' 6 | EXPECT_EQ 'abcxdefxghi' "${variable}" 7 | 8 | variable='abc def ghi' 9 | func::greg_replace variable '+( )' 'xyz' 10 | EXPECT_EQ 'abcxyzdefxyzghi' "${variable}" 11 | 12 | variable='abcdefghi' 13 | func::greg_replace variable '[beh]' 'x' 14 | EXPECT_EQ 'axcdxfgxi' "${variable}" 15 | 16 | variable=$'abc\tdef\nghi' 17 | func::greg_replace variable '[[:cntrl:][:print:]]' 'x' 18 | EXPECT_EQ 'xxxxxxxxxxx' "${variable}" 19 | } 20 | -------------------------------------------------------------------------------- /library/10-func/array/array_is_empty.sh: -------------------------------------------------------------------------------- 1 | # array_is_empty -- Checks if an array is empty. 2 | # 3 | # array_is_empty returns true iff a given array is empty. 4 | # 5 | # Usage: 6 | # // 1. Command form. 7 | # bool sub::array_is_empty(string[]* variable) 8 | # 9 | # Example: 10 | # array=() 11 | # if sub::array_is_empty array; then 12 | # echo 'array is empty.' 13 | # fi 14 | sub::array_is_empty() { 15 | if [ "$#" -eq 1 ]; then 16 | if eval "[ \"\${#${1}[*]}\" -eq 0 ]"; then return 0; fi 17 | return 1 18 | else 19 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 20 | fi 21 | } 22 | -------------------------------------------------------------------------------- /test/func/addslashes_test.sh: -------------------------------------------------------------------------------- 1 | test::func_addslashes() { 2 | local variable 3 | 4 | variable="abc\"def\\ghi'jkl" 5 | func::addslashes variable 6 | EXPECT_EQ "abc\\\"def\\\\ghi\\'jkl" "${variable}" 7 | 8 | variable='abcdef' 9 | func::addslashes variable 10 | EXPECT_EQ 'abcdef' "${variable}" 11 | 12 | variable='' 13 | func::addslashes variable 14 | EXPECT_EQ '' "${variable}" 15 | 16 | variable=' ' 17 | func::addslashes variable 18 | EXPECT_EQ ' ' "${variable}" 19 | 20 | variable=$'abc\tdef\nghi' 21 | func::addslashes variable 22 | EXPECT_EQ $'abc\tdef\nghi' "${variable}" 23 | } 24 | -------------------------------------------------------------------------------- /test/func/tmpfile_test.sh: -------------------------------------------------------------------------------- 1 | test::func_tmpfile() { 2 | local files=() 3 | local tmpfile='' 4 | 5 | # Test tmpfile's destribution. 6 | for i in {1..100}; do 7 | func::tmpfile tmpfile 8 | files+=("${tmpfile}") 9 | done 10 | func::array_unique files 11 | EXPECT_EQ 100 "${#files[@]}" 12 | 13 | func::tmpfile tmpfile 14 | echo foo > "${tmpfile}" 15 | EXPECT_EQ 'foo' "$(cat "${tmpfile}")" 16 | } 17 | 18 | test::sub_tmpfile() { 19 | local files=() 20 | local tmpfile='' 21 | 22 | tmpfile="$(sub::tmpfile)" 23 | echo bar > "${tmpfile}" 24 | EXPECT_EQ 'bar' "$(cat "${tmpfile}")" 25 | } 26 | -------------------------------------------------------------------------------- /library/10-func/datetime/time.sh: -------------------------------------------------------------------------------- 1 | # time -- Returns current Unix timestamp. 2 | # 3 | # time returns current Unix timestamp. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::time(string* result) 8 | # // 2. Command form. 9 | # void sub::time() > output 10 | func::time() { 11 | if [ "$#" -eq 1 ]; then 12 | func::exec "${1}" date +'%s' 13 | func::rtrim "${1}" 14 | else 15 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 16 | fi 17 | } 18 | 19 | sub::time() { 20 | if [ "$#" -eq 0 ]; then 21 | date +'%s' 22 | else 23 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 24 | fi 25 | } 26 | -------------------------------------------------------------------------------- /library/60-constants/color.sh: -------------------------------------------------------------------------------- 1 | # Color definitions. A shell script should restore terminal's original color 2 | # using IMOSH_STYLE_DEFAULT when it changes color or style. 3 | 4 | readonly IMOSH_STYLE_DEFAULT=$'\033[0m' 5 | readonly IMOSH_COLOR_DEFAULT=$'\033[0;39m' 6 | readonly IMOSH_COLOR_BLACK=$'\033[0;30m' 7 | readonly IMOSH_COLOR_RED=$'\033[0;31m' 8 | readonly IMOSH_COLOR_GREEN=$'\033[0;32m' 9 | readonly IMOSH_COLOR_YELLOW=$'\033[0;33m' 10 | readonly IMOSH_COLOR_BLUE=$'\033[0;34m' 11 | readonly IMOSH_COLOR_MAGENTA=$'\033[0;35m' 12 | readonly IMOSH_COLOR_CYAN=$'\033[0;36m' 13 | readonly IMOSH_COLOR_WHITE=$'\033[0;37m' 14 | -------------------------------------------------------------------------------- /library/10-func/funchand/function_exists.sh: -------------------------------------------------------------------------------- 1 | # function_exists -- Returns true iff the given function has been defined. 2 | # 3 | # Checks the list of defined functions, both built-in (internal) and 4 | # user-defined, for function_name. 5 | # 6 | # Usage: 7 | # // 1. Command form. 8 | # bool sub::function_exists(string function_name) 9 | # 10 | # Examples: 11 | # EXPECT_TRUE sub::function_exists func::array 12 | sub::function_exists() { 13 | if [ "$#" -eq 1 ]; then 14 | if ! type -t "${1}" >'/dev/null'; then 15 | return 1 16 | fi 17 | else 18 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 19 | fi 20 | } 21 | -------------------------------------------------------------------------------- /library/10-func/array/in_array.sh: -------------------------------------------------------------------------------- 1 | # in_array -- Checks if a value exists in an array. 2 | # 3 | # Searches haystack for needle. 4 | # 5 | # Usage: 6 | # // 1. Command form. 7 | # bool sub::in_array(string needle, string[]* haystack) 8 | sub::in_array() { 9 | if [ "$#" -eq 2 ]; then 10 | if sub::array_is_empty "${2}"; then 11 | return 1 12 | fi 13 | eval " 14 | for value in \"\${${2}[@]}\"; do 15 | if [ \"\${value}\" == \"\${1}\" ]; then 16 | return 17 | fi 18 | done" 19 | return 1 20 | else 21 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 22 | fi 23 | } 24 | -------------------------------------------------------------------------------- /library/10-func/strings/trim.sh: -------------------------------------------------------------------------------- 1 | # trim -- Strips whitespaces from both sides. 2 | # 3 | # trim strips whitespaces (or other characters) from the beginning and end of a 4 | # string. 5 | # 6 | # Usage: 7 | # // 1-a. Function format. 8 | # void func::trim(string* output, string input) 9 | # // 1-b. Function format. 10 | # void func::trim(string* variable) 11 | func::trim() { 12 | if [ "$#" -eq 1 ]; then 13 | func::rtrim "${1}" 14 | func::ltrim "${1}" 15 | elif [ "$#" -eq 2 ]; then 16 | func::let "${1}" "${2}" 17 | func::trim "${1}" 18 | else 19 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 20 | fi 21 | } 22 | -------------------------------------------------------------------------------- /library/10-func/misc/exec.sh: -------------------------------------------------------------------------------- 1 | # exec -- Executes an external program. 2 | # 3 | # func::exec executes an external program and sets its output to the variable. 4 | # 5 | # Usage: 6 | # // Function form. 7 | # void func::exec(string* output, string arguments...) 8 | func::exec() { 9 | if [ "$#" -gt 1 ]; then 10 | local __func_exec_variable="${1}" 11 | shift 12 | local __func_exec_output='' 13 | func::tmpfile __func_exec_output 14 | "$@" >"${__func_exec_output}" 15 | func::file_get_contents "${__func_exec_variable}" "${__func_exec_output}" 16 | else 17 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 18 | fi 19 | } 20 | -------------------------------------------------------------------------------- /library/10-func/strings/ltrim.sh: -------------------------------------------------------------------------------- 1 | # ltrim -- Strips whitespace(s) from the beginning of a string. 2 | # 3 | # Strips whitespace (or other characters) from the beginning of a string. 4 | # 5 | # Usage: 6 | # // 1-a. Function form. 7 | # void func::ltrim(string* variable) 8 | # // 1-b. Function form. 9 | # void func::ltrim(string* variable, string input) 10 | func::ltrim() { 11 | if [ "$#" -eq 1 ]; then 12 | eval "${1}=\"\${${1}#\"\${${1}%%[![:space:]]*}\"}\"" 13 | elif [ "$#" -eq 2 ]; then 14 | func::let "${1}" "${2}" 15 | func::ltrim "${1}" 16 | else 17 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 18 | fi 19 | } 20 | -------------------------------------------------------------------------------- /library/10-func/testing/EXPECT_EQ.sh: -------------------------------------------------------------------------------- 1 | # EXPECT_EQ -- Expects two arguments are equal. 2 | # 3 | # EXPECT_EQ expects two arguments are equal. 4 | # 5 | # Usage: 6 | # void EXPECT_EQ(string expected, string actual) 7 | # 8 | # Alias: 9 | # EXPECT_STREQ is an alias of EXPECT_EQ. 10 | EXPECT_EQ() { 11 | if [ "$#" -eq 2 ]; then 12 | if [ "$1" != "$2" ]; then 13 | echo " Actual: $2" >&2 14 | echo "Expected: $1" >&2 15 | FAILURE 16 | fi 17 | LOG INFO "EXPECT_EQ passes: '$1' == '$2'" 18 | else 19 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 20 | fi 21 | } 22 | 23 | EXPECT_STREQ() { 24 | EXPECT_EQ "$@" 25 | } 26 | -------------------------------------------------------------------------------- /library/10-func/testing/EXPECT_NE.sh: -------------------------------------------------------------------------------- 1 | # EXPECT_NE -- Expects two arguments are not equal. 2 | # 3 | # EXPECT_NE expects two arguments are not equal. 4 | # 5 | # Usage: 6 | # void EXPECT_NE(string target, string actual) 7 | # 8 | # Alias: 9 | # EXPECT_STRNE is an alias of EXPECT_NE. 10 | EXPECT_NE() { 11 | if [ "$#" -eq 2 ]; then 12 | if [ "$1" = "$2" ]; then 13 | echo "Actual: $2" >&2 14 | echo "Target: $1" >&2 15 | FAILURE 16 | fi 17 | LOG INFO "EXPECT_NE passes: '$1' != '$2'" 18 | else 19 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 20 | fi 21 | } 22 | 23 | EXPECT_STRNE() { 24 | EXPECT_NE "$@" 25 | } 26 | -------------------------------------------------------------------------------- /library/10-func/array/count.sh: -------------------------------------------------------------------------------- 1 | # count -- Counts the number of elements. 2 | # 3 | # count counts the number of elements of an array. 4 | # 5 | # Usage: 6 | # // 1. Function form 7 | # void func::count(int* result, string[]* values) 8 | # // 2. Command form 9 | # void sub::count(string[]* values) > result 10 | func::count() { 11 | if [ "$#" -eq 2 ]; then 12 | func::strcpy "${1}" "#${2}[*]" 13 | else 14 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 15 | fi 16 | } 17 | 18 | sub::count() { 19 | if [ "$#" -eq 1 ]; then 20 | eval "sub::println \"\${#${1}[*]}\"" 21 | else 22 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 23 | fi 24 | } 25 | -------------------------------------------------------------------------------- /library/10-func/greg/greg_match.sh: -------------------------------------------------------------------------------- 1 | # greg_match -- Checks if a string matches a GREG pattern. 2 | # 3 | # greg_match checks if a string matches a GREG pattern. 4 | # 5 | # Usage: 6 | # // 1. Command form. 7 | # bool sub::greg_match(string pattern, string subject) 8 | sub::greg_match() { 9 | if ! shopt extglob > /dev/null; then 10 | shopt -s extglob 11 | local __greg_match_result=0 12 | sub::greg_match "$@" || __greg_match_result="$?" 13 | shopt -u extglob 14 | return "${__greg_match_result}" 15 | elif [ "$#" -eq 2 ]; then 16 | [[ "${2}" = ${1} ]] || return 1 17 | else 18 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 19 | fi 20 | } 21 | -------------------------------------------------------------------------------- /library/10-func/greg/ereg_match.sh: -------------------------------------------------------------------------------- 1 | # ereg_match -- Checks if a string matches an EREG pattern. 2 | # 3 | # ereg_match checks if a string matches an EREG pattern. 4 | # 5 | # Usage: 6 | # // 1. Command form. 7 | # bool sub::ereg_match(string pattern, string subject) 8 | sub::ereg_match() { 9 | if [ "$#" -eq 2 ]; then 10 | if [[ "${2}" =~ $1 ]]; then 11 | return 0 12 | else 13 | return 1 14 | fi 15 | elif [ "$#" -eq 3 ]; then 16 | if [[ "${2}" =~ $1 ]]; then 17 | func::array_values "${3}" BASH_REMATCH 18 | return 0 19 | else 20 | return 1 21 | fi 22 | else 23 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 24 | fi 25 | } 26 | -------------------------------------------------------------------------------- /doc/misc/exit.sh.md: -------------------------------------------------------------------------------- 1 | # exit, die 2 | exit, die -- Kills the current script. 3 | 4 | sub::exit kills all the subprocesses of the current program. If an integer 5 | argument is given, sub::exit exits with the status. Otherwise, sub::exit 6 | shows the given message and exits with status 0. sub::die shows a stack 7 | trace and delegates arguments to sub::exit. 8 | 9 | ## Usage 10 | ```sh 11 | // 1-a. Command form with a message. 12 | void sub::exit(string message) 13 | // 1-b. Command form with a status. 14 | void sub::exit(int status = 0) 15 | // 1-c. Command form with a message. 16 | void sub::die(string message) 17 | // 1-d. Command form with a status. 18 | void sub::die(int status = 0) 19 | ``` 20 | -------------------------------------------------------------------------------- /library/10-func/array/is_array.sh: -------------------------------------------------------------------------------- 1 | # in_array -- Finds whether a variable is an array. 2 | # 3 | # Finds whether the given variable is an array. 4 | # 5 | # Usage: 6 | # // 1. Command form. 7 | # bool sub::is_array(string[]* variable) 8 | sub::is_array() { 9 | if [ "$#" -eq 1 ]; then 10 | if [ "${1%]}" != "${1}" ]; then 11 | return 1 12 | fi 13 | while :; do 14 | local key=0 15 | func::rand key 2147483647 16 | if sub::isset "${1}[$key]"; then 17 | continue 18 | fi 19 | if unset "${1}[$key]"; then 20 | return 0 21 | fi 22 | return 1 23 | done 24 | else 25 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 26 | fi 27 | } 28 | -------------------------------------------------------------------------------- /library/70-handler/signal.sh: -------------------------------------------------------------------------------- 1 | __imosh::signal_handler() { 2 | local signal="$1" 3 | trap - "${signal}" 4 | local pid='' 5 | func::getmypid pid 6 | if [ "${pid}" = "${IMOSH_ROOT_PID}" ]; then 7 | __sub::exit "${pid}" "${pid}" 8 | if [ -f "${__IMOSH_CORE_TMPDIR}/EXIT" ]; then 9 | exit "$(cat "${__IMOSH_CORE_TMPDIR}/EXIT")" 10 | fi 11 | fi 12 | LOG ERROR "$(imosh::stack_trace "Terminated by signal: ${signal}" 2>&1)" 13 | kill -s "${signal}" "${pid}" 14 | } 15 | 16 | if [ "${-//i/}" == "${-}" ]; then 17 | for signal in SIGHUP SIGINT SIGPIPE SIGTERM \ 18 | SIGXCPU SIGXFSZ SIGUSR1 SIGUSR2; do 19 | trap "__imosh::signal_handler ${signal}" "${signal}" 20 | done 21 | fi 22 | -------------------------------------------------------------------------------- /test/func/trim_test.sh: -------------------------------------------------------------------------------- 1 | test::func_trim() { 2 | local variable='' 3 | 4 | variable=' abc def ' 5 | func::trim variable 6 | EXPECT_EQ 'abc def' "${variable}" 7 | 8 | variable=$'\t\n\r \t\n\rabc def ' 9 | func::trim variable 10 | EXPECT_EQ 'abc def' "${variable}" 11 | 12 | variable='abc def' 13 | func::trim variable 14 | EXPECT_EQ 'abc def' "${variable}" 15 | 16 | variable='' 17 | func::trim variable 18 | EXPECT_EQ '' "${variable}" 19 | 20 | variable=' ' 21 | func::trim variable 22 | EXPECT_EQ '' "${variable}" 23 | 24 | variable=' ' 25 | func::trim variable 26 | EXPECT_EQ '' "${variable}" 27 | 28 | variable=$'\t\n\r \t\n\r' 29 | func::trim variable 30 | EXPECT_EQ '' "${variable}" 31 | } 32 | -------------------------------------------------------------------------------- /doc/strings/implode.sh.md: -------------------------------------------------------------------------------- 1 | # implode 2 | implode -- Joins array elements with a string. 3 | 4 | func::implode joins `pieces` with `glue`. 5 | *Stream form* uses the IFS environment variable as an input separator and 6 | processes line by line. 7 | 8 | ## Usage 9 | ```sh 10 | // 1. Function form. 11 | void func::implode(string* variable, string glue, string[]* pieces) 12 | // 2. Command form. 13 | void sub::implode(string glue, string[]* pieces) > result 14 | void sub::implode(string[]* pieces) > result 15 | // 3. Stream form. 16 | void stream::implode(string glue) < input > output 17 | ``` 18 | 19 | 20 | ## Aliases 21 | func::join, sub::join and stream::join are aliases of func::implode, 22 | sub::implode and stream::implode respectively. 23 | -------------------------------------------------------------------------------- /test/func/implode_test.sh: -------------------------------------------------------------------------------- 1 | test::func_implode() { 2 | local values=() 3 | 4 | values=(a b c) 5 | func::implode variable ' ' values 6 | EXPECT_EQ 'a b c' "${variable}" 7 | 8 | func::implode variable 'xxx' values 9 | EXPECT_EQ 'axxxbxxxc' "${variable}" 10 | 11 | values=(';' ' ' $'\n' '\' '') 12 | func::implode variable ' ' values 13 | EXPECT_EQ $'; \n \\ ' "${variable}" 14 | 15 | func::implode variable 'xxx' values 16 | EXPECT_EQ $';xxx xxx\nxxx\\xxx' "${variable}" 17 | 18 | # Command form. 19 | values=(a b c) 20 | EXPECT_EQ 'a,b,c' "$(sub::implode ',' values)" 21 | 22 | # Stream form. 23 | IFS=' \t\n' EXPECT_EQ $'a,b,c\nd,e\nf' \ 24 | "$(stream::implode ',' <<<$'a b c\nd\te\nf')" 25 | } 26 | -------------------------------------------------------------------------------- /test/func/ltrim_test.sh: -------------------------------------------------------------------------------- 1 | test::func_ltrim() { 2 | local variable='' 3 | 4 | variable=' abc def ' 5 | func::ltrim variable 6 | EXPECT_EQ 'abc def ' "${variable}" 7 | 8 | variable=$'\t\n\r \t\n\rabc def ' 9 | func::ltrim variable 10 | EXPECT_EQ 'abc def ' "${variable}" 11 | 12 | variable='abc def' 13 | func::ltrim variable 14 | EXPECT_EQ 'abc def' "${variable}" 15 | 16 | variable='' 17 | func::ltrim variable 18 | EXPECT_EQ '' "${variable}" 19 | 20 | variable=' ' 21 | func::ltrim variable 22 | EXPECT_EQ '' "${variable}" 23 | 24 | variable=' ' 25 | func::ltrim variable 26 | EXPECT_EQ '' "${variable}" 27 | 28 | variable=$'\t\n\r \t\n\r' 29 | func::ltrim variable 30 | EXPECT_EQ '' "${variable}" 31 | } 32 | -------------------------------------------------------------------------------- /test/func/rtrim_test.sh: -------------------------------------------------------------------------------- 1 | test::func_rtrim() { 2 | local variable='' 3 | 4 | variable=' abc def ' 5 | func::rtrim variable 6 | EXPECT_EQ ' abc def' "${variable}" 7 | 8 | variable=$' abc def\t\n\r \t\n\r' 9 | func::rtrim variable 10 | EXPECT_EQ ' abc def' "${variable}" 11 | 12 | variable='abc def' 13 | func::rtrim variable 14 | EXPECT_EQ 'abc def' "${variable}" 15 | 16 | variable='' 17 | func::rtrim variable 18 | EXPECT_EQ '' "${variable}" 19 | 20 | variable=' ' 21 | func::rtrim variable 22 | EXPECT_EQ '' "${variable}" 23 | 24 | variable=' ' 25 | func::rtrim variable 26 | EXPECT_EQ '' "${variable}" 27 | 28 | variable=$'\t\n\r \t\n\r' 29 | func::rtrim variable 30 | EXPECT_EQ '' "${variable}" 31 | } 32 | -------------------------------------------------------------------------------- /library/10-func/filesystem/tmpfile.sh: -------------------------------------------------------------------------------- 1 | # tmpfile -- Creates a temporary file. 2 | # 3 | # tmpfile creates a temporary file with a unique name under ${TMPDIR}. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::tmpfile(string* path) 8 | # // 2. Command form. 9 | # void sub::tmpfile() > path 10 | func::tmpfile() { 11 | if [ "$#" -eq 1 ]; then 12 | func::let "${1}" \ 13 | "${TMPDIR}/tmpfile.${RANDOM}.${RANDOM}.${RANDOM}.${RANDOM}.${RANDOM}" 14 | else 15 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 16 | fi 17 | } 18 | 19 | sub::tmpfile() { 20 | if [ "$#" -eq 0 ]; then 21 | local tmpfile='' 22 | func::tmpfile tmpfile 23 | sub::println "${tmpfile}" 24 | else 25 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 26 | fi 27 | } 28 | -------------------------------------------------------------------------------- /library/10-func/strings/explode.sh: -------------------------------------------------------------------------------- 1 | # explode -- Splits a string by a substring. 2 | # 3 | # Splits a string by string. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::explode(string* variable, string delimiter, string value) 8 | func::explode() { 9 | if [ "$#" -eq 3 ]; then 10 | local __explode_value="${3}" 11 | if [ "${2}" != $'\x02' ]; then 12 | func::str_replace __explode_value "${2}" $'\x02' 13 | fi 14 | local __explode_result=() 15 | local __explode_term='' 16 | while IFS= read -r -d $'\x02' __explode_term; do 17 | __explode_result+=("${__explode_term}") 18 | done <<<"${__explode_value}"$'\x02' 19 | func::array_values "${1}" __explode_result 20 | else 21 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 22 | fi 23 | } 24 | -------------------------------------------------------------------------------- /library/10-func/filesystem/readline.sh: -------------------------------------------------------------------------------- 1 | # readline -- Gets a line. 2 | # 3 | # readline reads a line and sets its content to LINE and its trailing new line 4 | # to NEWLINE. 5 | # 6 | # Usage: 7 | # void func::readline() < input 8 | func::readline() { 9 | if [ "$#" -eq 0 ]; then 10 | local line='' 11 | NEWLINE='' 12 | LINE='' 13 | if ! func::fgets line; then 14 | return 1 15 | fi 16 | if [ "${line:$((${#line} - 2))}" = $'\r\n' ]; then 17 | NEWLINE=$'\r\n' 18 | LINE="${line%$'\r\n'}" 19 | elif [ "${line:$((${#line} - 1))}" = $'\n' ]; then 20 | NEWLINE=$'\n' 21 | LINE="${line%$'\n'}" 22 | else 23 | NEWLINE='' 24 | LINE="${line}" 25 | fi 26 | else 27 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 28 | fi 29 | } 30 | -------------------------------------------------------------------------------- /test/func/explode_test.sh: -------------------------------------------------------------------------------- 1 | test::func_explode() { 2 | local values=() 3 | local actual='' 4 | 5 | func::explode values ' ' 'abc def ghi' 6 | func::implode actual ',' values 7 | EXPECT_EQ 'abc,def,ghi' "${actual}" 8 | 9 | func::explode values 'xyz' $'ab\ncxyzde\tfxyzxyzgh i' 10 | func::implode actual ',' values 11 | EXPECT_EQ $'ab\nc,de\tf,,gh i' "${actual}" 12 | 13 | func::explode values ' ' '' 14 | func::implode actual ',' values 15 | EXPECT_EQ '' "${actual}" 16 | EXPECT_EQ 1 "${#values[*]}" 17 | 18 | func::explode values ' ' ' ' 19 | func::implode actual ',' values 20 | EXPECT_EQ ',' "${actual}" 21 | EXPECT_EQ 2 "${#values[*]}" 22 | 23 | func::explode values ';' '\\;*;?;' 24 | func::implode actual ',' values 25 | EXPECT_EQ '\\,*,?,' "${actual}" 26 | } 27 | -------------------------------------------------------------------------------- /doc/array/array_unique.sh.md: -------------------------------------------------------------------------------- 1 | # array_unique 2 | array_unique -- Removes duplicated elements from an array variable. 3 | 4 | array_unique sorts elements first and removes duplicated elements. Function 5 | form applies array_unique to the given variable. Stream form applies 6 | array_unique to every line. Every line is treated as elements. 7 | 8 | ## Usage 9 | ```sh 10 | // 1. Function form. 11 | void func::array_unique(string[]* variable) 12 | // 2. Stream form. 13 | void stream::array_unique() < input > output 14 | ``` 15 | 16 | 17 | ## Examples 18 | ```sh 19 | a=(c b a b) 20 | func::array_unique a 21 | echo "${a[@]}" # => a b c 22 | ``` 23 | 24 | 25 | ```sh 26 | echo c b a b | stream::array_unique # => a b c 27 | ``` 28 | 29 | 30 | ```sh 31 | echo c,b,a,b | IFS=, stream::array_unique # => a,b,c 32 | ``` 33 | -------------------------------------------------------------------------------- /test/func/base64_encode_test.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | local expected="${1}" 3 | local input="${2}" 4 | 5 | local result='' 6 | func::base64_encode result "${input}" 7 | EXPECT_EQ "${expected}" "${result}" 8 | 9 | local result='' 10 | local tmpfile='' 11 | func::tmpfile tmpfile 12 | sub::base64_encode "${input}" >"${tmpfile}" 13 | func::file_get_contents result "${tmpfile}" 14 | EXPECT_EQ "${expected}"$'\n' "${result}" 15 | 16 | local result='' 17 | func::file_get_contents result "${tmpfile}" 18 | sub::print "${input}" | stream::base64_encode >"${tmpfile}" 19 | func::file_get_contents result "${tmpfile}" 20 | EXPECT_EQ "${expected}" "${result}" 21 | } 22 | 23 | test::base64_encode() { 24 | run '' '' 25 | run 'Zg==' 'f' 26 | run 'Zm9v' 'foo' 27 | run 'aG9nZWhvZ2U=' 'hogehoge' 28 | } 29 | -------------------------------------------------------------------------------- /test/func/isset_test.sh: -------------------------------------------------------------------------------- 1 | test::func_isset() { 2 | if sub::isset undefined_variable; then 3 | LOG FATAL "undefined_variable should return false" 4 | fi 5 | local defined_variable=value 6 | if ! sub::isset defined_variable; then 7 | LOG FATAL "defined_variable should return true" 8 | fi 9 | defined_variable= 10 | if ! sub::isset defined_variable; then 11 | LOG FATAL "defined_variable should return true" 12 | fi 13 | local null_variable= 14 | if ! sub::isset null_variable; then 15 | LOG FATAL "null_variable should return true" 16 | fi 17 | # isset's behavior for uninitialized variables is different in BASH versions. 18 | # local uninitialized_variable 19 | # if ! sub::isset uninitialized_variable; then 20 | # LOG FATAL "uninitialized_variable should return true" 21 | # fi 22 | } 23 | -------------------------------------------------------------------------------- /library/10-func/misc/throttle.sh: -------------------------------------------------------------------------------- 1 | # throttle -- Throttles by the number of child processes. 2 | # 3 | # throttle waits that the number of the child processes is less than a limit. 4 | # Firstly, throttle waits for 0.1 second, and the n-th retry (n < 10) waits for 5 | # n * 0.1 seconds. The n-th retry (n >= 10) waits for 1 second. 6 | # 7 | # Usage: 8 | # void sub::throttle(int limit) 9 | sub::throttle() { 10 | if [ "$#" -eq 1 ]; then 11 | local pids=() 12 | local sleep=0 13 | 14 | while :; do 15 | func::getchildpids pids 16 | if [ "${#pids[*]}" -lt "${1}" ]; then 17 | break 18 | fi 19 | if (( sleep += 1, sleep < 10 )); then 20 | sleep "0.${sleep}" 21 | else 22 | sleep 1 23 | fi 24 | done 25 | else 26 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 27 | fi 28 | } 29 | -------------------------------------------------------------------------------- /test/script/exit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test sub::exit. 3 | 4 | source "$(dirname "${BASH_SOURCE}")"/../../imosh || exit 1 5 | DEFINE_int 'status' 0 'Status code to exit.' 6 | DEFINE_int 'depth' 0 'Max depth of subprocesses.' 7 | DEFINE_int 'exit_depth' 0 'Depth of a subprocess calling sub::exit.' 8 | eval "${IMOSH_INIT}" 9 | 10 | f() { 11 | local depth="$1" 12 | local primary="$2" 13 | LOG INFO "Function: f ${depth} ${primary}" 14 | if [ "${depth}" -gt 0 ]; then 15 | f "$(( depth - 1 ))" 0 & 16 | f "$(( depth - 1 ))" "${primary}" & 17 | fi 18 | if (( primary && depth == FLAGS_exit_depth )); then 19 | sleep 0.1 20 | LOG INFO "Calling sub::exit ${FLAGS_status}" 21 | sub::exit "${FLAGS_status}" 22 | exit 23 | fi 24 | sleep 5 25 | echo failure 26 | } 27 | 28 | f "${FLAGS_depth}" 1 & 29 | sleep 5 30 | exit 1 31 | -------------------------------------------------------------------------------- /test/func/array_values_test.sh: -------------------------------------------------------------------------------- 1 | test::array_values() { 2 | local array1=() 3 | local array2=('') 4 | local array3=('foo') 5 | local array4=('foo' 'bar') 6 | 7 | EXPECT_EQ '' "$(IFS=, sub::array_values array1)" 8 | EXPECT_EQ '' "$(IFS=, sub::array_values array2)" 9 | EXPECT_EQ 'foo' "$(IFS=, sub::array_values array3)" 10 | EXPECT_EQ 'foo,bar' "$(IFS=, sub::array_values array4)" 11 | 12 | func::array_values result array1 13 | EXPECT_EQ '0' "${#result[*]}" 14 | func::array_values result array2 15 | EXPECT_EQ '1' "${#result[*]}" 16 | EXPECT_EQ '' "${result[0]}" 17 | func::array_values result array3 18 | EXPECT_EQ '1' "${#result[*]}" 19 | EXPECT_EQ 'foo' "${result[0]}" 20 | func::array_values result array4 21 | EXPECT_EQ '2' "${#result[*]}" 22 | EXPECT_EQ 'foo' "${result[0]}" 23 | EXPECT_EQ 'bar' "${result[1]}" 24 | } 25 | -------------------------------------------------------------------------------- /library/10-func/strings/escapeshellarg.sh: -------------------------------------------------------------------------------- 1 | # escapeshellarg -- Escapes a variable as a shell argument. 2 | # 3 | # escapeshellarg escapes variable's content so as to use it as a shell argument. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::escapeshellarg(string* variable) 8 | func::escapeshellarg() { 9 | if [ "$#" -eq 1 ]; then 10 | local __escapeshellarg_value='' 11 | func::strcpy __escapeshellarg_value "${1}" 12 | if [ "${__escapeshellarg_value}" == '' ]; then 13 | func::let "${1}" "''" 14 | return 15 | fi 16 | if [ "${__escapeshellarg_value//[[:alnum:]\,\-\.\_\/\:\@]/}" == '' ]; then 17 | return 18 | fi 19 | func::str_replace __escapeshellarg_value "'" "'\\''" 20 | func::let "${1}" "'${__escapeshellarg_value}'" 21 | else 22 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 23 | fi 24 | } 25 | -------------------------------------------------------------------------------- /test/func/base64_decode_test.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | local expected="${1}" 3 | local input="${2}" 4 | 5 | local result='' 6 | func::base64_decode result "${input}" 7 | EXPECT_EQ "${expected}" "${result}" 8 | 9 | local result='' 10 | local tmpfile='' 11 | func::tmpfile tmpfile 12 | sub::base64_decode "${input}" >"${tmpfile}" 13 | func::file_get_contents result "${tmpfile}" 14 | EXPECT_EQ "${expected}" "${result}" 15 | 16 | local result='' 17 | func::file_get_contents result "${tmpfile}" 18 | sub::print "${input}" | stream::base64_decode >"${tmpfile}" 19 | func::file_get_contents result "${tmpfile}" 20 | EXPECT_EQ "${expected}" "${result}" 21 | } 22 | 23 | test::base64_decode() { 24 | run '' '' 25 | run 'f' 'Zg==' 26 | run 'f' 'Zg' 27 | run 'foo' 'Zm9v' 28 | run 'hogehoge' 'aG9nZWhvZ2U=' 29 | run 'hogehoge' 'aG9nZWhvZ2U' 30 | } 31 | -------------------------------------------------------------------------------- /library/10-func/logging/CHECK.sh: -------------------------------------------------------------------------------- 1 | # CHECK -- checks if a command succeeds. 2 | # 3 | # CHECK fails with a fatal error if a command fails. 4 | # 5 | # Usage: 6 | # void CHECK(string command...) 7 | CHECK() { 8 | IFS=' ' eval 'local __CHECK_message="Check failure: $*"' 9 | if [ "$#" -ge 1 ] && [ "${1:0:10}" = '--message=' ]; then 10 | __CHECK_message="${1:10}" 11 | shift 12 | fi 13 | 14 | if [ "$#" -ge 1 ]; then 15 | local __CHECK_invert="${1}" 16 | 17 | if [ "${__CHECK_invert}" = '!' ]; then 18 | shift 19 | if "$@"; then 20 | LOG ERROR 'Check failure: !' "$@" 21 | LOG FATAL "${__CHECK_message}" 22 | fi 23 | else 24 | if ! "$@"; then 25 | LOG ERROR 'Check failure:' "$@" 26 | LOG FATAL "${__CHECK_message}" 27 | fi 28 | fi 29 | else 30 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 31 | fi 32 | } 33 | -------------------------------------------------------------------------------- /library/10-func/var/strval.sh: -------------------------------------------------------------------------------- 1 | # strval -- Casts a variable as a string value. 2 | # 3 | # Casts variable into string type. 4 | # 5 | # Usage: 6 | # // 1-a. Function from. 7 | # void func::strval(string* output, string input) 8 | # // 1-b. Inplace function form. 9 | # void func::strval(string* variable) 10 | # // 2. Command form. 11 | # bool sub::strval(string input) > output 12 | func::strval() { 13 | if [ "$#" -eq 2 ]; then 14 | func::let "${1}" "${2}" 15 | elif [ "$#" -eq 1 ]; then 16 | func::strcpy "${1}" "${1}" 17 | else 18 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 19 | fi 20 | } 21 | 22 | sub::strval() { 23 | if [ "$#" -eq 1 ]; then 24 | local __strval_value="${1}" 25 | func::strval __strval_value || return "$?" 26 | sub::println "${__strval_value}" 27 | else 28 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 29 | fi 30 | } 31 | -------------------------------------------------------------------------------- /test/func/date_test.sh: -------------------------------------------------------------------------------- 1 | test::date() { 2 | EXPECT_EQ '2006-01-02 15:04:05' \ 3 | "$(TZ=UTC sub::date 'Y-m-d H:i:s' 1136214245)" 4 | EXPECT_EQ '2006-01-03 00:04:05' \ 5 | "$(TZ=Asia/Tokyo sub::date 'Y-m-d H:i:s' 1136214245)" 6 | EXPECT_EQ 'January 3, 2006, 12:04 AM' \ 7 | "$(TZ=Asia/Tokyo sub::date 'F j, Y, g:i a' 1136214245)" 8 | EXPECT_EQ '00:01:05 m is month' \ 9 | "$(TZ=Asia/Tokyo sub::date 'H:m:s \m \i\s\ \m\o\n\t\h' 1136214245)" 10 | EXPECT_EQ '2006-01-03T00:04:05+09:00' \ 11 | "$(TZ=Asia/Tokyo sub::date 'c' 1136214245)" 12 | EXPECT_EQ 'Tue, 03 Jan 2006 00:04:05 +0900' \ 13 | "$(TZ=Asia/Tokyo sub::date 'r' 1136214245)" 14 | EXPECT_EQ '1136214245' \ 15 | "$(TZ=Asia/Tokyo sub::date 'U' 1136214245)" 16 | 17 | EXPECT_LT 1422807963 "$(sub::date 'U')" 18 | EXPECT_GT 9999999999 "$(sub::date 'U')" 19 | } 20 | -------------------------------------------------------------------------------- /test/func/in_array_test.sh: -------------------------------------------------------------------------------- 1 | test::in_array() { 2 | local array1=() 3 | local array2=('') 4 | local array3=('foo') 5 | local array4=('foo' 'bar') 6 | 7 | EXPECT_FALSE sub::in_array '' array1 8 | EXPECT_TRUE sub::in_array '' array2 9 | EXPECT_FALSE sub::in_array '' array3 10 | EXPECT_FALSE sub::in_array '' array4 11 | 12 | EXPECT_FALSE sub::in_array 'foo' array1 13 | EXPECT_FALSE sub::in_array 'foo' array2 14 | EXPECT_TRUE sub::in_array 'foo' array3 15 | EXPECT_TRUE sub::in_array 'foo' array4 16 | 17 | EXPECT_FALSE sub::in_array 'bar' array1 18 | EXPECT_FALSE sub::in_array 'bar' array2 19 | EXPECT_FALSE sub::in_array 'bar' array3 20 | EXPECT_TRUE sub::in_array 'bar' array4 21 | 22 | EXPECT_FALSE sub::in_array 'baz' array1 23 | EXPECT_FALSE sub::in_array 'baz' array2 24 | EXPECT_FALSE sub::in_array 'baz' array3 25 | EXPECT_FALSE sub::in_array 'baz' array4 26 | } 27 | -------------------------------------------------------------------------------- /library/10-imosh/stack_trace.sh: -------------------------------------------------------------------------------- 1 | # Usage: 2 | # imosh::stack_trace [--skip_imosh] [message...] 3 | # 4 | # Shows a stack trace. Arguments are used as a message. 5 | imosh::stack_trace() { 6 | local ARGS_skip_imosh=0 7 | eval "${IMOSH_PARSE_ARGUMENTS}" 8 | 9 | local max_depth="${#BASH_LINENO[*]}" 10 | local i=0 11 | if [ "$*" = '' ]; then 12 | echo 'imosh::stack_trace is called' >&2 13 | else 14 | echo "$*" >&2 15 | fi 16 | while (( i < max_depth - 1 )); do 17 | if [ "${BASH_SOURCE[$((i+1))]}" != "${BASH_SOURCE[0]}" ]; then 18 | break 19 | fi 20 | (( i += 1 )) || true 21 | done 22 | while (( i < max_depth - 1 )); do 23 | local lineno="${BASH_LINENO[$((i))]}" 24 | local file="${BASH_SOURCE[$((i+1))]}" 25 | local function="${FUNCNAME[$((i+1))]}" 26 | echo " at ${function} (${file}:${lineno})" >&2 27 | (( i += 1 )) || true 28 | done 29 | } 30 | -------------------------------------------------------------------------------- /test/extra_flags_test.sh: -------------------------------------------------------------------------------- 1 | get_flag() { 2 | "${BASH}" test/flags.sh --show_argv "$@" 2>/dev/null & 3 | if ! wait $!; then sub::print invalid; fi 4 | } 5 | 6 | test::extra_flags() { 7 | local pids=() 8 | ASSERT_EQ 'foo' "$(get_flag foo)" & 9 | pids+=("$!") 10 | ASSERT_EQ 'foo' "$(get_flag -- foo)" & 11 | pids+=("$!") 12 | ASSERT_EQ '-- foo' "$(get_flag -- -- foo)" & 13 | pids+=("$!") 14 | ASSERT_EQ '-- -- foo' "$(get_flag -- -- -- foo)" & 15 | pids+=("$!") 16 | ASSERT_EQ 'foo bar' "$(get_flag foo -- bar)" & 17 | pids+=("$!") 18 | ASSERT_EQ 'foo -- bar' "$(get_flag foo -- -- bar)" & 19 | pids+=("$!") 20 | 21 | ASSERT_EQ 'invalid' "$(get_flag --foo)" & 22 | pids+=("$!") 23 | ASSERT_EQ '--foo' "$(get_flag -- --foo)" & 24 | pids+=("$!") 25 | 26 | for pid in "${pids[@]}"; do 27 | if ! wait "${pid}"; then 28 | IMOSH_TEST_IS_FAILED=1 29 | fi 30 | done 31 | } 32 | -------------------------------------------------------------------------------- /test/func/str_replace_test.sh: -------------------------------------------------------------------------------- 1 | test::func_str_replace() { 2 | local variable 3 | 4 | variable='abc def ghi' 5 | func::str_replace variable ' ' 'x' 6 | EXPECT_EQ 'abcxdefxghi' "${variable}" 7 | 8 | variable='abc def ghi' 9 | func::str_replace variable ' ' 'xyz' 10 | EXPECT_EQ 'abcxyzdefxyzghi' "${variable}" 11 | 12 | variable='abcdefghi' 13 | func::str_replace variable 'def' 'x' 14 | EXPECT_EQ 'abcxghi' "${variable}" 15 | 16 | variable='aaaaaaaa' 17 | func::str_replace variable 'aaa' 'bbb' 18 | EXPECT_EQ 'bbbbbbaa' "${variable}" 19 | 20 | variable='abcdefghi' 21 | func::str_replace variable 'x' 'y' 22 | EXPECT_EQ 'abcdefghi' "${variable}" 23 | 24 | variable=$'abc\ndef' 25 | func::str_replace variable $'\n' ' ' 26 | EXPECT_EQ 'abc def' "${variable}" 27 | 28 | variable='abc/def/ghi' 29 | func::str_replace variable '/' '//' 30 | EXPECT_EQ 'abc//def//ghi' "${variable}" 31 | } 32 | -------------------------------------------------------------------------------- /library/60-constants/flag.sh: -------------------------------------------------------------------------------- 1 | readonly IMOSH_PARSE_ARGUMENTS=' 2 | local IMOSH_ARGV IMOSH_ARGS 3 | imosh::internal::parse_args arg "$@" 4 | if [ "${#IMOSH_ARGS[*]}" -ne 0 ]; then 5 | local __imosh_parse_arguments_arg="" 6 | for __imosh_parse_arguments_arg in "${IMOSH_ARGS[@]}"; do 7 | eval "local ${__imosh_parse_arguments_arg}" 8 | done 9 | fi 10 | if [ "${#IMOSH_ARGV[*]}" -ne 0 ]; then 11 | set -- "${IMOSH_ARGV[@]}" 12 | else 13 | set -- 14 | fi' 15 | 16 | readonly IMOSH_WRONG_NUMBER_OF_ARGUMENTS=' 17 | LOG ERROR "Wrong number of arguments: $#" 18 | return 1' 19 | 20 | readonly IMOSH_INIT=' 21 | set -e -u 22 | imosh::internal::init "$@" 23 | if [ "${#IMOSH_ARGV[*]}" -ne 0 ]; then 24 | set -- "${IMOSH_ARGV[@]}" 25 | else 26 | set -- 27 | fi' 28 | 29 | __IMOSH_FLAGS=() 30 | __IMOSH_FLAGS_ALIASES=() 31 | __IMOSH_FLAGS_MULTIALIASES=() 32 | -------------------------------------------------------------------------------- /library/10-func/info/getmypid.sh: -------------------------------------------------------------------------------- 1 | # getmypid -- Gets the current process ID. 2 | # 3 | # Usage: 4 | # // 1. Function form. 5 | # void func::getmypid(int* variable) 6 | # // 2. Command form. 7 | # void sub::getmypid() > output 8 | func::getmypid() { 9 | if [ "$#" -eq 1 ]; then 10 | if sub::isset BASHPID; then 11 | func::let "${1}" "${BASHPID}" 12 | else 13 | local __getmypid_pid_file='' 14 | func::tmpfile __getmypid_pid_file 15 | "${SHELL}" -c 'echo "${PPID}"' > "${__getmypid_pid_file}" 16 | read -r -d $'\n' "${1}" < "${__getmypid_pid_file}" 17 | fi 18 | else 19 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 20 | fi 21 | } 22 | 23 | sub::getmypid() { 24 | if [ "$#" -eq 0 ]; then 25 | local __getmypid_variable 26 | func::getmypid __getmypid_variable 27 | sub::println "${__getmypid_variable}" 28 | else 29 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 30 | fi 31 | } 32 | -------------------------------------------------------------------------------- /library/10-func/strings/md5_file.sh: -------------------------------------------------------------------------------- 1 | # md5 -- Calculates the MD5 hash of a given file. 2 | # 3 | # Usage: 4 | # // 1. Function form. 5 | # void func::md5_file(string* variable, string filename) 6 | # // 2. Command form. 7 | # void sub::md5_file(string filename, bool binary = false) > hash 8 | func::md5_file() { 9 | if [ "$#" -eq 2 ]; then 10 | local __md5_file_variable="${1}" 11 | shift 12 | func::exec "${__md5_file_variable}" sub::md5_file "$@" 13 | func::rtrim "${__md5_file_variable}" 14 | else 15 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 16 | fi 17 | } 18 | 19 | sub::md5_file() { 20 | if [ "$#" -eq 2 ]; then 21 | if [ "${2}" -eq 0 ]; then 22 | sub::md5_file "${1}" 1 | stream::bin2hex 23 | else 24 | openssl md5 -binary "${1}" 25 | fi 26 | elif [ "$#" -eq 1 ]; then 27 | sub::md5_file "$@" 0 28 | else 29 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 30 | fi 31 | } 32 | -------------------------------------------------------------------------------- /test/log_test.sh: -------------------------------------------------------------------------------- 1 | output_log() { 2 | if (( FLAGS_logtostderr )); then 3 | FLAGS_alsologtostderr=1 4 | FLAGS_logtostderr=0 5 | fi 6 | __IMOSH_LOGGING=1 7 | __IMOSH_LOG_PID=12345 LOG "$@" 105>/dev/null 8 | } 9 | 10 | test::log::info() { 11 | message="$(output_log INFO 'message' 101>&1 2>/dev/null)" 12 | EXPECT_EQ 'I0000 00:00:00.000000 00000 log_test.sh:0] message' \ 13 | "${message//[0-9]/0}" 14 | } 15 | 16 | test::log::warning() { 17 | message="$(output_log WARNING 'message' \ 18 | 101>/dev/null 102>&1 2>/dev/null)" 19 | EXPECT_EQ 'W0000 00:00:00.000000 00000 log_test.sh:0] message' \ 20 | "${message//[0-9]/0}" 21 | } 22 | 23 | test::log::error() { 24 | message="$(output_log ERROR 'message' \ 25 | 101>/dev/null 102>/dev/null 103>&1 2>/dev/null)" 26 | EXPECT_EQ 'E0000 00:00:00.000000 00000 log_test.sh:0] message' \ 27 | "${message//[0-9]/0}" 28 | } 29 | -------------------------------------------------------------------------------- /library/90-flags/00-define.sh: -------------------------------------------------------------------------------- 1 | if sub::isset IMOSH_USE_DEFINE_FLAGS && (( IMOSH_USE_DEFINE_FLAGS )); then 2 | DEFINE_bool --group=imosh --alias=h help false \ 3 | 'Print this help message and exit.' 4 | DEFINE_bool --group=imosh helpfull false \ 5 | 'Print all the help message.' 6 | DEFINE_bool --group=imosh 'alsologtostderr' false \ 7 | 'Log messages go to stderr in addition to logfiles.' 8 | DEFINE_bool --group=imosh 'logtostderr' false \ 9 | 'Log messages go to stderr instead of logfiles.' 10 | DEFINE_string --group=imosh 'log_dir' '' \ 11 | 'Directory to output log files. Output no files if this flag is empty.' 12 | DEFINE_string --group=imosh 'stacktrace_threshold' 'FATAL' \ 13 | 'Threshold to show stacktrace.' 14 | DEFINE_string --group=imosh 'help_format' '' \ 15 | 'Help format to output.' 16 | DEFINE_string --group=imosh 'imosh_test' '' \ 17 | 'Test files to test.' 18 | else 19 | : 20 | -------------------------------------------------------------------------------- /test/func/hex2bin_test.sh: -------------------------------------------------------------------------------- 1 | test::func_hex2bin() { 2 | local variable='' 3 | 4 | # void func::hex2bin(string* output, string input) 5 | func::hex2bin variable '686f6765' 6 | EXPECT_EQ 'hoge' "${variable}" 7 | 8 | # void func::hex2bin(string* variable) 9 | variable='686f6765' 10 | func::hex2bin variable 11 | EXPECT_EQ 'hoge' "${variable}" 12 | 13 | # void stream::hex2bin() < input > output 14 | EXPECT_EQ 'hoge' "$(echo '686f6765' | stream::hex2bin)" 15 | 16 | # Japanese characters. 17 | variable='e697a5e69cace8aa9e' 18 | func::hex2bin variable 19 | EXPECT_EQ '日本語' "${variable}" 20 | 21 | # Spaces are ignored. 22 | variable='e6 97 a5 e6 9c ac e8 aa 9e' 23 | func::hex2bin variable 24 | EXPECT_EQ '日本語' "${variable}" 25 | 26 | # Spaces including new lines are also ignored. 27 | variable='e6 97 a5 28 | e6 9c ac 29 | e8 aa 9e' 30 | func::hex2bin variable 31 | EXPECT_EQ '日本語' "${variable}" 32 | } 33 | -------------------------------------------------------------------------------- /library/10-func/strings/ord.sh: -------------------------------------------------------------------------------- 1 | # ord -- Gets a character's ASCII code. 2 | # 3 | # Sets ASCII value of character to variable. 4 | # 5 | # Usage: 6 | # // 1-a. Function form. 7 | # void func::ord(string* variable, string character) 8 | # // 1-b. Function form. 9 | # void func::ord(string* variable) 10 | # // 2. Command form. 11 | # void sub::ord(string character) > output 12 | func::ord() { 13 | if [ "$#" -eq 2 ]; then 14 | func::let "${1}" "$(printf '%d' \'"${2}")" 15 | elif [ "$#" -eq 1 ]; then 16 | local __ord_value='' 17 | func::strcpy __ord_value "${2}" 18 | func::ord "${1}" "${__ord_value}" 19 | else 20 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 21 | fi 22 | } 23 | 24 | sub::ord() { 25 | if [ "$#" -eq 1 ]; then 26 | local __ord_variable='' 27 | func::ord __ord_variable "${1}" 28 | func::println "${__ord_variable}" 29 | else 30 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 31 | fi 32 | } 33 | -------------------------------------------------------------------------------- /test/func/file_get_contents_test.sh: -------------------------------------------------------------------------------- 1 | test::func_file_get_contents() { 2 | local variable='' 3 | 4 | sub::print 'abc' > "${TMPDIR}/test" 5 | func::file_get_contents variable "${TMPDIR}/test" 6 | EXPECT_EQ 'abc' "${variable}" 7 | 8 | sub::print $'abc def\nghi jkl\n' > "${TMPDIR}/test" 9 | func::file_get_contents variable "${TMPDIR}/test" 10 | EXPECT_EQ $'abc def\nghi jkl\n' "${variable}" 11 | 12 | sub::print '' > "${TMPDIR}/test" 13 | func::file_get_contents variable "${TMPDIR}/test" 14 | EXPECT_EQ '' "${variable}" 15 | } 16 | 17 | test::sub_file_get_contents() { 18 | sub::print 'abc' > "${TMPDIR}/test" 19 | EXPECT_EQ 'abc' "$(sub::file_get_contents "${TMPDIR}/test")" 20 | } 21 | 22 | test::stream_file_get_contents() { 23 | sub::print 'abc' > "${TMPDIR}/test1" 24 | sub::print 'def' > "${TMPDIR}/test2" 25 | EXPECT_EQ 'abcdef' "$( 26 | { echo "${TMPDIR}/test1"; echo "${TMPDIR}/test2"; } | \ 27 | stream::file_get_contents)" 28 | } 29 | -------------------------------------------------------------------------------- /test/func/rand_test.sh: -------------------------------------------------------------------------------- 1 | test::func_rand() { 2 | local values=() value=0 3 | for i in {1..100}; do 4 | func::rand value 5 | if [ 0 -le "${value}" -a "${value}" -le 2147483647 ]; then 6 | values+=("${value}") 7 | else 8 | LOG FATAL 'func::rand should be between 0 and 2147483647 inclusive:' \ 9 | "${value}" 10 | fi 11 | done 12 | func::array_unique values 13 | if [ "${#values[@]}" -lt 98 ]; then 14 | LOG FATAL "func::rand's distribution may be bad: ${#values[@]}" 15 | fi 16 | values=() 17 | for i in {1..100}; do 18 | func::rand value 0 4 19 | if [ 0 -le "${value}" -a "${value}" -le 4 ]; then 20 | values+=("${value}") 21 | else 22 | LOG FATAL "func::rand(0, 4) should be between 0 and 4 inclusive: ${value}" 23 | fi 24 | done 25 | func::array_unique values 26 | # This should pass in 99.999999898148%. 27 | if [ "${#values[@]}" -ne 5 ]; then 28 | LOG FATAL "func::rand's distribution may be bad: ${#values[@]}" 29 | fi 30 | } 31 | -------------------------------------------------------------------------------- /library/10-func/greg/greg_split.sh: -------------------------------------------------------------------------------- 1 | # greg_split -- Splits a string with a GREG pattern. 2 | # 3 | # greg_split splits a string with a GREG pattern. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::greg_split(string[]* variable, string pattern, string value) 8 | # // 2. Command form. 9 | # void sub::greg_split(string pattern, string value) 10 | # 11 | # Caveat: 12 | # greg_split does not support \x02 for value. 13 | func::greg_split() { 14 | if [ "$#" -eq 3 ]; then 15 | local __greg_split_value="${3}" 16 | func::greg_replace __greg_split_value "${2}" $'\x02' 17 | func::explode "${1}" $'\x02' "${__greg_split_value}" 18 | else 19 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 20 | fi 21 | } 22 | 23 | sub::greg_split() { 24 | if [ "$#" -eq 2 ]; then 25 | local __greg_split_variable=() 26 | func::greg_split __greg_split_variable "${1}" "${2}" 27 | sub::implode __greg_split_variable 28 | else 29 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 30 | fi 31 | } 32 | -------------------------------------------------------------------------------- /test/func/fgets_test.sh: -------------------------------------------------------------------------------- 1 | test::func_fgets() { 2 | local variable='' 3 | 4 | sub::print $'abc\ndef\n' > "${TMPDIR}/test" 5 | exec < "${TMPDIR}/test" 6 | EXPECT_TRUE func::fgets variable 7 | EXPECT_EQ $'abc\n' "${variable}" 8 | EXPECT_TRUE func::fgets variable 9 | EXPECT_EQ $'def\n' "${variable}" 10 | EXPECT_FALSE func::fgets variable 11 | EXPECT_EQ '' "${variable}" 12 | 13 | sub::print $'abc\ndef' > "${TMPDIR}/test" 14 | exec < "${TMPDIR}/test" 15 | EXPECT_TRUE func::fgets variable 16 | EXPECT_EQ $'abc\n' "${variable}" 17 | EXPECT_TRUE func::fgets variable 18 | EXPECT_EQ 'def' "${variable}" 19 | EXPECT_FALSE func::fgets variable 20 | EXPECT_EQ '' "${variable}" 21 | 22 | sub::print $'abc\r\ndef\r\n' > "${TMPDIR}/test" 23 | exec < "${TMPDIR}/test" 24 | EXPECT_TRUE func::fgets variable 25 | EXPECT_EQ $'abc\r\n' "${variable}" 26 | EXPECT_TRUE func::fgets variable 27 | EXPECT_EQ $'def\r\n' "${variable}" 28 | EXPECT_FALSE func::fgets variable 29 | EXPECT_EQ '' "${variable}" 30 | } 31 | -------------------------------------------------------------------------------- /library/10-func/strings/bin2hex.sh: -------------------------------------------------------------------------------- 1 | # bin2hex -- Converts a binary string into hexadecimal representation. 2 | # 3 | # Converts binary data into hexadecimal representation. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::bin2hex(string* hexadecimal_output, string binary_input) 8 | # // 2. Command form. 9 | # void sub::bin2hex(string binary_input) > hexadecimal_output 10 | # // 3. Stream form. 11 | # void stream::bin2hex() < binary_input > hexadecimal_output 12 | func::bin2hex() { 13 | if [ "$#" -eq 2 ]; then 14 | func::let "${1}" "$(sub::print "${2}" | stream::bin2hex)" 15 | else 16 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 17 | fi 18 | } 19 | 20 | sub::bin2hex() { 21 | if [ "$#" -eq 1 ]; then 22 | stream::bin2hex <<<"${1}" 23 | else 24 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 25 | fi 26 | } 27 | 28 | stream::bin2hex() { 29 | if [ "$#" -eq 0 ]; then 30 | od -An -tx1 | tr -d ' \n' 31 | else 32 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 33 | fi 34 | } 35 | -------------------------------------------------------------------------------- /library/10-func/testing/EXPECT_LT.sh: -------------------------------------------------------------------------------- 1 | # EXPECT_LT -- Expects first one is less than second one. 2 | # 3 | # EXPECT_LT expects first one is less than second one. 4 | # 5 | # Usage: 6 | # void EXPECT_LT(string target, string actual) 7 | # void EXPECT_STRLT(string target, string actual) 8 | EXPECT_LT() { 9 | if [ "$#" -eq 2 ]; then 10 | if [ "$1" -ge "$2" ]; then 11 | echo "Actual: $2" >&2 12 | echo "Target: $1" >&2 13 | FAILURE 14 | fi 15 | LOG INFO "EXPECT_LT passes: '$1' < '$2'" 16 | else 17 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 18 | fi 19 | } 20 | 21 | EXPECT_STRLT() { 22 | if [ "$#" -eq 2 ]; then 23 | if [ "$1" \>= "$2" ]; then 24 | echo "Actual: $2" >&2 25 | echo "Target: $1" >&2 26 | FAILURE 27 | fi 28 | LOG INFO "EXPECT_STRLT passes: '$1' < '$2'" 29 | else 30 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 31 | fi 32 | } 33 | 34 | ASSERT_LT() { 35 | __ASSERT EXPECT_LT "$@" 36 | } 37 | 38 | ASSERT_STRLT() { 39 | __ASSERT EXPECT_STRLT "$@" 40 | } 41 | -------------------------------------------------------------------------------- /library/10-func/var/isset.sh: -------------------------------------------------------------------------------- 1 | # isset -- Checks if a variable exists. 2 | # 3 | # Returns true iff variable exists. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::isset(bool* result, variant* variable) 8 | # // 2. Command form. 9 | # bool func::isset(variant* variable) 10 | # 11 | # CAVEATS: 12 | # func::isset returns true for uninitialized variables in BASH 3, and returns 13 | # false for them in BASH 4. 14 | func::isset() { 15 | if [ "$#" -eq 2 ]; then 16 | if declare -p "${2}" &>/dev/null; then 17 | func::let "${1}" 1 18 | else 19 | func::let "${1}" 0 20 | fi 21 | elif [ "$#" -eq 1 ]; then 22 | DEPRECATED 23 | sub::isset "$@" || return "$?" 24 | else 25 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 26 | fi 27 | } 28 | 29 | sub::isset() { 30 | if [ "$#" -eq 1 ]; then 31 | local __isset_return=0 32 | func::isset __isset_return "${1}" 33 | (( __isset_return )) || return 1 34 | else 35 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 36 | fi 37 | } 38 | -------------------------------------------------------------------------------- /library/10-func/testing/EXPECT_GT.sh: -------------------------------------------------------------------------------- 1 | # EXPECT_GT -- Expects first one is greater than second one. 2 | # 3 | # EXPECT_GT expects first one is greater than second one. 4 | # 5 | # Usage: 6 | # void EXPECT_GT(string target, string actual) 7 | # void EXPECT_STRGT(string target, string actual) 8 | EXPECT_GT() { 9 | if [ "$#" -eq 2 ]; then 10 | if [ "$1" -le "$2" ]; then 11 | echo "Actual: $2" >&2 12 | echo "Target: $1" >&2 13 | FAILURE 14 | fi 15 | LOG INFO "EXPECT_GT passes: '$1' > '$2'" 16 | else 17 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 18 | fi 19 | } 20 | 21 | EXPECT_STRGT() { 22 | if [ "$#" -eq 2 ]; then 23 | if [ "$1" \<= "$2" ]; then 24 | echo "Actual: $2" >&2 25 | echo "Target: $1" >&2 26 | FAILURE 27 | fi 28 | LOG INFO "EXPECT_STRGT passes: '$1' > '$2'" 29 | else 30 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 31 | fi 32 | } 33 | 34 | ASSERT_GT() { 35 | __ASSERT EXPECT_GT "$@" 36 | } 37 | 38 | ASSERT_STRGT() { 39 | __ASSERT EXPECT_STRGT "$@" 40 | } 41 | -------------------------------------------------------------------------------- /library/10-func/array/array_values.sh: -------------------------------------------------------------------------------- 1 | # array_values -- Copies elements from an array to an array. 2 | # 3 | # array_values copies elements in an array variable into an array variable. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::array_values(string[]* output, string[]* input) 8 | # // 2. Command form. 9 | # void sub::array_values(string[]* input) > output 10 | # 11 | # Examples: 12 | # array=(foo bar) 13 | # EXPECT_EQ 'foo,bar' "$(IFS=, sub::array_values array)" 14 | func::array_values() { 15 | if [ "$#" -eq 2 ]; then 16 | if sub::array_is_empty "${2}"; then 17 | eval "${1}=()" 18 | else 19 | eval "${1}=(\"\${${2}[@]}\")" 20 | fi 21 | else 22 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 23 | fi 24 | } 25 | 26 | sub::array_values() { 27 | if [ "$#" -eq 1 ]; then 28 | local __array_values_values=() 29 | func::array_values __array_values_values "${1}" 30 | sub::implode __array_values_values 31 | else 32 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 33 | fi 34 | } 35 | -------------------------------------------------------------------------------- /library/10-func/testing/EXPECT_LE.sh: -------------------------------------------------------------------------------- 1 | # EXPECT_LE -- Expects first one is less than or equal to second one. 2 | # 3 | # EXPECT_LE expects first one is less than or equal to second one. 4 | # 5 | # Usage: 6 | # void EXPECT_LE(string target, string actual) 7 | # void EXPECT_STRLE(string target, string actual) 8 | EXPECT_LE() { 9 | if [ "$#" -eq 2 ]; then 10 | if [ "$1" -gt "$2" ]; then 11 | echo "Actual: $2" >&2 12 | echo "Target: $1" >&2 13 | FAILURE 14 | fi 15 | LOG INFO "EXPECT_LE passes: '$1' <= '$2'" 16 | else 17 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 18 | fi 19 | } 20 | 21 | EXPECT_STRLE() { 22 | if [ "$#" -eq 2 ]; then 23 | if [ "$1" \> "$2" ]; then 24 | echo "Actual: $2" >&2 25 | echo "Target: $1" >&2 26 | FAILURE 27 | fi 28 | LOG INFO "EXPECT_STRLE passes: '$1' <= '$2'" 29 | else 30 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 31 | fi 32 | } 33 | 34 | ASSERT_LE() { 35 | __ASSERT EXPECT_LE "$@" 36 | } 37 | 38 | ASSERT_STRLE() { 39 | __ASSERT EXPECT_STRLE "$@" 40 | } 41 | -------------------------------------------------------------------------------- /library/10-func/testing/EXPECT_GE.sh: -------------------------------------------------------------------------------- 1 | # EXPECT_GE -- Expects first one is greater than or equal to second one. 2 | # 3 | # EXPECT_GE expects first one is greater than or equal to second one. 4 | # 5 | # Usage: 6 | # void EXPECT_GE(string target, string actual) 7 | # void EXPECT_STRGE(string target, string actual) 8 | EXPECT_GE() { 9 | if [ "$#" -eq 2 ]; then 10 | if [ "$1" -lt "$2" ]; then 11 | echo "Actual: $2" >&2 12 | echo "Target: $1" >&2 13 | FAILURE 14 | fi 15 | LOG INFO "EXPECT_GE passes: '$1' >= '$2'" 16 | else 17 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 18 | fi 19 | } 20 | 21 | EXPECT_STRGE() { 22 | if [ "$#" -eq 2 ]; then 23 | if [ "$1" \< "$2" ]; then 24 | echo "Actual: $2" >&2 25 | echo "Target: $1" >&2 26 | FAILURE 27 | fi 28 | LOG INFO "EXPECT_STRGE passes: '$1' >= '$2'" 29 | else 30 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 31 | fi 32 | } 33 | 34 | ASSERT_GE() { 35 | __ASSERT EXPECT_GE "$@" 36 | } 37 | 38 | ASSERT_STRGE() { 39 | __ASSERT EXPECT_STRGE "$@" 40 | } 41 | -------------------------------------------------------------------------------- /library/10-func/greg/greg_replace.sh: -------------------------------------------------------------------------------- 1 | # greg_replace -- Replace a GREG pattern with a string. 2 | # 3 | # greg_replace replaces substrings matching a pattern with a string. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::greg_replace(string* subject, string pattern, string replace) 8 | # // 2. Command form. 9 | # void sub::greg_replace( 10 | # string subject, string pattern, string replace) > output 11 | func::greg_replace() { 12 | if ! shopt extglob >/dev/null; then 13 | local __greg_replace_status=0 14 | shopt -s extglob 15 | func::greg_replace "$@" 16 | shopt -u extglob 17 | elif [ "$#" -eq 3 ]; then 18 | eval "${1}=\"\${${1}//\${2}/\${3}}\"" 19 | else 20 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 21 | fi 22 | } 23 | 24 | sub::greg_replace() { 25 | if [ "$#" -eq 3 ]; then 26 | local __greg_replace_subject="${1}" 27 | func::greg_replace __greg_replace_subject "${2}" "${3}" 28 | sub::println "${__greg_replace_subject}" 29 | else 30 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 31 | fi 32 | } 33 | -------------------------------------------------------------------------------- /library/10-func/var/floatval.sh: -------------------------------------------------------------------------------- 1 | # floatval -- Casts a variable as a float value. 2 | # 3 | # Casts variable into float type. If it fails, returns 1. 4 | # 5 | # Usage: 6 | # // 1-a. Function form. 7 | # bool func::floatval(string* output, string input) 8 | # // 1-b. Inplace function form. 9 | # bool func::floatval(string* variable) 10 | # // 2. Command form. 11 | # bool sub::floatval(string input) > output 12 | func::floatval() { 13 | if [ "$#" -eq 2 ]; then 14 | if [[ ! "${2}" =~ ^[[:space:]]*(-?[0-9]+(\.[0-9]+)?) ]]; then 15 | return 1 16 | fi 17 | func::let "${1}" "${BASH_REMATCH[1]}" 18 | elif [ "$#" -eq 1 ]; then 19 | eval "func::floatval \"\${1}\" \"\${${1}}\" || return \"\$?\"" 20 | else 21 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 22 | fi 23 | } 24 | 25 | sub::floatval() { 26 | if [ "$#" -eq 1 ]; then 27 | local __floatval_value="${1}" 28 | func::floatval __floatval_value || return "$?" 29 | sub::println "${__floatval_value}" 30 | else 31 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 32 | fi 33 | } 34 | -------------------------------------------------------------------------------- /library/10-func/var/intval.sh: -------------------------------------------------------------------------------- 1 | # intval -- Casts a variable as an integer value. 2 | # 3 | # Casts variable into integer type. If it fails, returns 1. 4 | # 5 | # Usage: 6 | # // 1-a. Function from. 7 | # bool func::intval(string* output, string input) 8 | # // 1-b. Inplace function form. 9 | # bool func::intval(string* variable) 10 | # // 2. Command form. 11 | # bool sub::intval(string input) > output 12 | func::intval() { 13 | if [ "$#" -eq 2 ]; then 14 | if [[ ! "${2}" =~ ^[[:space:]]*(-?[0-9]+) ]]; then 15 | func::let "${1}" 0 16 | return 1 17 | fi 18 | func::let "${1}" "${BASH_REMATCH[1]}" 19 | elif [ "$#" -eq 1 ]; then 20 | eval "func::intval \"\${1}\" \"\${${1}}\" || return \"\$?\"" 21 | else 22 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 23 | fi 24 | } 25 | 26 | sub::intval() { 27 | if [ "$#" -eq 1 ]; then 28 | local __intval_value="${1}" 29 | func::intval __intval_value || return "$?" 30 | sub::println "${__intval_value}" 31 | else 32 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 33 | fi 34 | } 35 | -------------------------------------------------------------------------------- /library/10-func/array/array_keys.sh: -------------------------------------------------------------------------------- 1 | # array_keys -- Gets an array's keys. 2 | # 3 | # array_keys gets an array's keys. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::array_keys(string[]* output, string[]* input) 8 | # // 2. Command form. 9 | # void sub::array_keys(string[]* input) > output 10 | # 11 | # Examples: 12 | # array=([0]=abc [2]=def) 13 | # EXPECT_EQ '0,2' "$(IFS=, sub::array_keys array)" 14 | # func::array_keys result array 15 | # EXPECT_EQ '0' "${result[0]}" 16 | # EXPECT_EQ '2' "${result[1]}" 17 | func::array_keys() { 18 | if [ "$#" -eq 2 ]; then 19 | if eval "[ \"\${#${2}[*]}\" -eq 0 ]"; then 20 | eval "${1}=()" 21 | else 22 | eval "${1}=(\"\${!${2}[@]}\")" 23 | fi 24 | else 25 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 26 | fi 27 | } 28 | 29 | sub::array_keys() { 30 | if [ "$#" -eq 1 ]; then 31 | local __array_keys_values=() 32 | func::array_keys __array_keys_values "${1}" 33 | sub::implode __array_keys_values 34 | else 35 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 36 | fi 37 | } 38 | -------------------------------------------------------------------------------- /library/10-func/var/enumval.sh: -------------------------------------------------------------------------------- 1 | # enumval -- Casts a variable as an enum value. 2 | # 3 | # Casts variable into enum type. If it fails, returns 1. 4 | # 5 | # Usage: 6 | # // 1-a. Function from. 7 | # bool func::enumval(string* output, string input) 8 | # // 1-b. Inplace function form. 9 | # bool func::enumval(string* variable) 10 | # // 2. Command form. 11 | # bool sub::enumval(string input) > output 12 | func::enumval() { 13 | if [ "$#" -eq 2 ]; then 14 | if sub::in_array "${2}" __imosh_enum_values; then 15 | func::let "${1}" "${2}" 16 | else 17 | func::let "${1}" "${__imosh_enum_values[0]}" 18 | return 1 19 | fi 20 | elif [ "$#" -eq 1 ]; then 21 | eval "func::enumval \"\${1}\" \"\${${1}}\" || return \"\$?\"" 22 | else 23 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 24 | fi 25 | } 26 | 27 | sub::enumval() { 28 | if [ "$#" -eq 1 ]; then 29 | local __enumval_value="${1}" 30 | func::enumval __enumval_value || return "$?" 31 | sub::println "${__enumval_value}" 32 | else 33 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 34 | fi 35 | } 36 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /library/10-func/info/getchildpids.sh: -------------------------------------------------------------------------------- 1 | # getchildpids -- Gets child process IDs. 2 | # 3 | # Usage: 4 | # // 1. Function form. 5 | # void func::getchildpids(int[]* variable) 6 | # // 2. Command form. 7 | # void sub::getchildpids() > output 8 | func::getchildpids() { 9 | if [ "$#" -eq 1 ]; then 10 | local __getchildpids_mypid='' 11 | func::getmypid __getchildpids_mypid 12 | local __getchildpids_pid='' __getchildpids_ppid='' 13 | local __getchildpids_result=() 14 | while IFS=$' \t\n' \ 15 | read -r -d $'\n' __getchildpids_ppid __getchildpids_pid; do 16 | if [ "${__getchildpids_ppid}" = "${__getchildpids_mypid}" ]; then 17 | __getchildpids_result+=("${__getchildpids_pid}") 18 | fi 19 | done < <(ps -o ppid,pid) 20 | func::array_values "${1}" __getchildpids_result 21 | else 22 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 23 | fi 24 | } 25 | 26 | sub::getchildpids() { 27 | if [ "$#" -eq 0 ]; then 28 | local __getchildpids_variable=() 29 | func::getchildpids __getchildpids_variable 30 | sub::implode __getchildpids_variable 31 | else 32 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 33 | fi 34 | } 35 | -------------------------------------------------------------------------------- /library/10-func/strings/base64_encode.sh: -------------------------------------------------------------------------------- 1 | # base64_encode -- Encodes data with MIME base64. 2 | # 3 | # Usage: 4 | # // 1. Function form. 5 | # void func::base64_encode(string* variable, string data) 6 | # // 2. Command form. 7 | # void sub::base64_encode(string data) > output 8 | # // 3. Stream form. 9 | # void stream::base64_encode() < input > output 10 | func::base64_encode() { 11 | if [ "$#" -eq 2 ]; then 12 | local __base64_encode_file='' 13 | func::tmpfile __base64_encode_file 14 | sub::print "${2}" | stream::base64_encode > "${__base64_encode_file}" 15 | func::file_get_contents "${1}" "${__base64_encode_file}" 16 | else 17 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 18 | fi 19 | } 20 | 21 | sub::base64_encode() { 22 | if [ "$#" -eq 1 ]; then 23 | sub::print "${1}" | stream::base64_encode 24 | sub::println 25 | else 26 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 27 | fi 28 | } 29 | 30 | stream::base64_encode() { 31 | if [ "$#" -eq 0 ]; then 32 | # Remove newlines for compatibility. 33 | openssl enc -e -base64 | tr -cd '[:print:]' 34 | else 35 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 36 | fi 37 | } 38 | -------------------------------------------------------------------------------- /doc/filesystem/file_get_contents.sh.md: -------------------------------------------------------------------------------- 1 | # file_get_contents 2 | file_get_contents -- Reads an entire file into a string. 3 | 4 | The function form reads an entire file and sets its contents to the 5 | variable. The subroutine form reads an entire file and outputs its contents 6 | to the standard output. The stream form reads a file name for each line and 7 | outputs its contents to the standard output. 8 | 9 | ## Usage 10 | ```sh 11 | // 1. Function form. 12 | func::file_get_contents(string* variable, string filename) 13 | // 2. Subroutine form. 14 | sub::file_get_contents(string filename) > output 15 | // 3. Stream form. 16 | stream::file_get_contents() < input > output 17 | ``` 18 | 19 | 20 | ## Examples 21 | ```sh 22 | sub::print hello > "${TMPDIR}/foo" 23 | func::file_get_contents variable "${TMPDIR}/foo" 24 | echo "${variable}" # => hello 25 | ``` 26 | 27 | 28 | ```sh 29 | sub::print hello > "${TMPDIR}/foo" 30 | sub::file_get_contents "${TMPDIR}/foo" # => hello 31 | ``` 32 | 33 | 34 | ```sh 35 | sub::print hello > "${TMPDIR}/foo" 36 | sub::print world > "${TMPDIR}/bar" 37 | { echo "${TMPDIR}/foo"; echo "${TMPDIR}/bar"; } | \ 38 | stream::file_get_contents # => helloworld 39 | ``` 40 | -------------------------------------------------------------------------------- /library/10-func/strings/md5.sh: -------------------------------------------------------------------------------- 1 | # md5 -- Calculates a MD5 hash. 2 | # 3 | # Usage: 4 | # // 1. Function form. 5 | # void func::md5(string* variable, string data) 6 | # // 2. Command form. 7 | # void sub::md5(string data, bool binary = false) > hash 8 | # // 3. Stream form. 9 | # void stream::md5(bool binary = false) < input > hash 10 | func::md5() { 11 | if [ "$#" -eq 2 ]; then 12 | local __md5_file='' 13 | func::tmpfile __md5_file 14 | sub::md5 "${2}" 0 > "${__md5_file}" 15 | func::file_get_contents "${1}" "${__md5_file}" 16 | else 17 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 18 | fi 19 | } 20 | 21 | sub::md5() { 22 | if [ "$#" -eq 2 ]; then 23 | sub::print "${1}" | stream::md5 "${2}" 24 | elif [ "$#" -eq 1 ]; then 25 | sub::md5 "$@" 0 26 | else 27 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 28 | fi 29 | } 30 | 31 | stream::md5() { 32 | if [ "$#" -eq 1 ]; then 33 | if [ "${1}" -eq 0 ]; then 34 | stream::md5 1 | stream::bin2hex 35 | else 36 | openssl md5 -binary 37 | fi 38 | elif [ "$#" -eq 0 ]; then 39 | stream::md5 0 40 | else 41 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 42 | fi 43 | } 44 | -------------------------------------------------------------------------------- /library/10-func/strings/sha1.sh: -------------------------------------------------------------------------------- 1 | # sha1 -- Calculates a MD5 hash. 2 | # 3 | # Usage: 4 | # // 1. Function form. 5 | # void func::sha1(string* variable, string data) 6 | # // 2. Command form. 7 | # void sub::sha1(string data, bool binary = false) > hash 8 | # // 3. Stream form. 9 | # void stream::sha1(bool binary = false) < input > hash 10 | func::sha1() { 11 | if [ "$#" -eq 2 ]; then 12 | local __sha1_file='' 13 | func::tmpfile __sha1_file 14 | sub::sha1 "${2}" 0 > "${__sha1_file}" 15 | func::file_get_contents "${1}" "${__sha1_file}" 16 | else 17 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 18 | fi 19 | } 20 | 21 | sub::sha1() { 22 | if [ "$#" -eq 2 ]; then 23 | sub::print "${1}" | stream::sha1 "${2}" 24 | elif [ "$#" -eq 1 ]; then 25 | sub::sha1 "$@" 0 26 | else 27 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 28 | fi 29 | } 30 | 31 | stream::sha1() { 32 | if [ "$#" -eq 1 ]; then 33 | if [ "${1}" -eq 0 ]; then 34 | stream::sha1 1 | stream::bin2hex 35 | else 36 | openssl sha1 -binary 37 | fi 38 | elif [ "$#" -eq 0 ]; then 39 | stream::sha1 0 40 | else 41 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 42 | fi 43 | } 44 | -------------------------------------------------------------------------------- /library/10-func/var/boolval.sh: -------------------------------------------------------------------------------- 1 | # boolval -- Casts a variable as a boolean value. 2 | # 3 | # Casts variable as a boolean value. If it fails, returns 1. 4 | # 5 | # Usage: 6 | # bool func::boolval(string* variable) 7 | func::boolval() { 8 | if [ "$#" -eq 2 ]; then 9 | func::let "${1}" 0 10 | local __boolval_value="${2}" 11 | func::trim __boolval_value 12 | if [ "${__boolval_value}" = '' ] || \ 13 | [[ "${__boolval_value}" =~ ^F|f|[Ff]alse$ ]]; then 14 | return 15 | elif [[ "${__boolval_value}" =~ ^T|t|[Tt]rue$ ]]; then 16 | func::let "${1}" 1 17 | else 18 | func::intval __boolval_value || return "$?" 19 | if (( __boolval_value != 0 )); then 20 | func::let "${1}" 1 21 | fi 22 | fi 23 | elif [ "$#" -eq 1 ]; then 24 | eval "func::boolval \"\${1}\" \"\${${1}}\" || return \"\$?\"" 25 | else 26 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 27 | fi 28 | } 29 | 30 | sub::boolval() { 31 | if [ "$#" -eq 1 ]; then 32 | local __boolval_sub_value="${1}" 33 | func::booval __boolval_sub_value || return "$?" 34 | sub::println "${__boolval_sub_value}" 35 | else 36 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 37 | fi 38 | } 39 | -------------------------------------------------------------------------------- /test/flags.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # A script to test imosh flags. 3 | 4 | source "$(dirname "${BASH_SOURCE}")"/../imosh || exit 1 5 | 6 | DEFINE_string 'flag' '' 'Flag name to show.' 7 | DEFINE_bool 'show_argv' false 'Output extra argv.' 8 | DEFINE_string 'string' 'default' 'String flag.' 9 | DEFINE_int --alias=i 'int' 100 'Integer flag.' 10 | DEFINE_bool 'bool' false 'Boolean flag.' 11 | DEFINE_multiint 'multiint' --alias=m 1,10,100 'Multiple integers flag.' 12 | DEFINE_list 'list' 'a,b,c' 'Multiple strings flag.' 13 | DEFINE_enum 'enum' 'bar' --values='foo,bar' 'Enum flag.' 14 | 15 | IMOSH_PREDICATE="${IMOSH_TEST_PREDICATE:--1}" eval "${IMOSH_INIT}" 16 | 17 | if (( FLAGS_show_argv )); then 18 | echo "$@" 19 | exit 20 | fi 21 | 22 | if [ "${FLAGS_flag}" == '' ]; then 23 | LOG FATAL '--flag must be specified.' 24 | fi 25 | if [ "${FLAGS_flag}" = 'list' -o "${FLAGS_flag:0:5}" = 'multi' ]; then 26 | func::array_values values "FLAGS_${FLAGS_flag}" 27 | if [ "${#values[*]}" -ne 0 ]; then 28 | for value in "${values[@]}"; do 29 | sub::println "${value}" 30 | done 31 | else 32 | sub::println 'EMPTY' 33 | fi 34 | else 35 | eval "sub::println 'FLAGS_${FLAGS_flag}='\"\${FLAGS_${FLAGS_flag}}\"" 36 | fi 37 | -------------------------------------------------------------------------------- /test/func/strtolower_test.sh: -------------------------------------------------------------------------------- 1 | run_testcase() { 2 | local expected="$1" 3 | local input="$2" 4 | 5 | # 1. Function form. 6 | local variable="${input}" 7 | func::strtolower variable 8 | EXPECT_EQ "${expected}" "${variable}" 9 | 10 | # 2. Subroutine form. 11 | EXPECT_EQ "${expected}" "$(sub::strtolower "${input}")" 12 | 13 | # 3. Stream form. 14 | EXPECT_EQ "${expected}" "$(sub::print "${input}" | stream::strtolower)" 15 | } 16 | 17 | test::func_strtolower() { 18 | run_testcase 'abc def ghi 123 ひらがな 漢字 カタカナ' \ 19 | 'ABC def Ghi 123 ひらがな 漢字 カタカナ' 20 | 21 | run_testcase \ 22 | 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789' \ 23 | 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' 24 | 25 | # Symbols. Symbols do not change. 26 | run_testcase $' !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' \ 27 | $' !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' 28 | run_testcase $'\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0e\x0f\x10' \ 29 | $'\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0e\x0f\x10' 30 | run_testcase $'\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1e\x1f\x7f' \ 31 | $'\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1e\x1f\x7f' 32 | } 33 | -------------------------------------------------------------------------------- /test/func/strtoupper_test.sh: -------------------------------------------------------------------------------- 1 | run_testcase() { 2 | local expected="$1" 3 | local input="$2" 4 | 5 | # 1. Function form. 6 | local variable="${input}" 7 | func::strtoupper variable 8 | EXPECT_EQ "${expected}" "${variable}" 9 | 10 | # 2. Subroutine form. 11 | EXPECT_EQ "${expected}" "$(sub::strtoupper "${input}")" 12 | 13 | # 3. Stream form. 14 | EXPECT_EQ "${expected}" "$(sub::print "${input}" | stream::strtoupper)" 15 | } 16 | 17 | test::func_strtouppper() { 18 | run_testcase 'ABC DEF GHI 123 ひらがな 漢字 カタカナ' \ 19 | 'ABC def Ghi 123 ひらがな 漢字 カタカナ' 20 | 21 | run_testcase \ 22 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' \ 23 | 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' 24 | 25 | # Symbols. Symbols do not change. 26 | run_testcase $' !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' \ 27 | $' !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' 28 | run_testcase $'\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0e\x0f\x10' \ 29 | $'\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0e\x0f\x10' 30 | run_testcase $'\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1e\x1f\x7f' \ 31 | $'\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1e\x1f\x7f' 32 | } 33 | -------------------------------------------------------------------------------- /library/10-func/strings/rtrim.sh: -------------------------------------------------------------------------------- 1 | # rtrim -- Strips whitespace(s) from the end of a string. 2 | # 3 | # Strips whitespace (or other characters) from the end of a string. 4 | # 5 | # Usage: 6 | # // 1-a. Function form. 7 | # void func::rtrim(string* output, string input) 8 | # // 1-b. Function form. 9 | # void func::rtrim(string* variable) 10 | # // 2. Command form. 11 | # void sub::rtrim(string value) > output 12 | # // 3. Stream form. 13 | # void stream::rtrim() < input > output 14 | func::rtrim() { 15 | if [ "$#" -eq 1 ]; then 16 | eval "${1}=\"\${${1}%\"\${${1}##*[![:space:]]}\"}\"" 17 | elif [ "$#" -eq 2 ]; then 18 | func::let "${1}" "${2}" 19 | func::rtrim "${1}" 20 | else 21 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 22 | fi 23 | } 24 | 25 | sub::rtrim() { 26 | if [ "$#" -eq 1 ]; then 27 | local __rtrim_value="$1" 28 | func::rtrim __rtrim_value 29 | sub::println "${__rtrim_value}" 30 | else 31 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 32 | fi 33 | } 34 | 35 | stream::rtrim() { 36 | if [ "$#" -eq 0 ]; then 37 | local LINE='' NEWLINE='' 38 | while func::readline; do 39 | func::rtrim LINE 40 | sub::print "${LINE}${NEWLINE}" 41 | done 42 | else 43 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 44 | fi 45 | } 46 | -------------------------------------------------------------------------------- /test/func/array_map_test.sh: -------------------------------------------------------------------------------- 1 | run_test_case() { 2 | local type="$1" 3 | local variable=() 4 | 5 | variable=("${INPUT[@]}") 6 | func::array_map variable "$@" 7 | if [ "${type}" = 'COMMAND' ]; then 8 | EXPECT_EQ "${EXPECTED}" "$(sub::implode $'' variable)" 9 | else 10 | EXPECT_EQ "${EXPECTED}" "$(sub::implode $'\n' variable)" 11 | fi 12 | 13 | variable=("${INPUT[@]}") 14 | local actual="$( 15 | sub::implode $'\n' variable | 16 | stream::array_map "$@")" 17 | EXPECT_EQ "${EXPECTED}" "${actual}" 18 | } 19 | 20 | test::func_array_map() { 21 | local variable=() 22 | 23 | # Test for array functions (e.g. func::sort). 24 | INPUT=('def,abc,ghi' '1,3,2,5,4') 25 | EXPECTED=$'abc,def,ghi\n1,2,3,4,5' 26 | IFS=',' run_test_case ARRAY func::sort 27 | 28 | # Test for regular functions (e.g. func::bin2hex). 29 | INPUT=('a,b,c' '1 2 3') 30 | EXPECTED=$'612c622c63\n3120322033' 31 | run_test_case FUNCTION func::bin2hex 32 | 33 | # Test for inplace functions (e.g. func::str_replace). 34 | INPUT=('abcbd' 'bcdbcb') 35 | EXPECTED=$'aBCbd\nBCdBCb' 36 | run_test_case INPLACE func::str_replace 'bc' 'BC' 37 | 38 | # Test for command functions (e.g. sub::strtoupper). 39 | INPUT=('abcbd' 'bcdbcb') 40 | EXPECTED=$'ABCBD\nBCDBCB' 41 | run_test_case COMMAND sub::strtoupper 42 | } 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BASH=/bin/bash 2 | 3 | concat: 4 | @echo '#!/bin/bash' >imosh 5 | @echo '# imosh is a utility library for BASH.' >>imosh 6 | @echo '#' >>imosh 7 | @echo '# For more details, see https://github.com/imos/imosh' >>imosh 8 | @echo '' >>imosh 9 | @echo "IMOSH_VERSION='$$(git log --pretty=format:'%ci (%h)' library | head -n 1)'" >>imosh 10 | @echo '' >>imosh 11 | @for directory in library/*; do \ 12 | for file in "$${directory}"/*.sh "$${directory}"/*/*.sh; do \ 13 | if [ -f "$${file}" ]; then \ 14 | cat "$${file}"; \ 15 | echo; \ 16 | fi; \ 17 | done; \ 18 | done >> imosh 19 | @chmod +x imosh 20 | @chmod +x tool/update-readme.sh 21 | @./tool/update-readme.sh 22 | @IMOSH_USE_DEFINE_FLAGS=1 \ 23 | ./tool/print-flag-variables.sh > library/90-flags/50-variables.sh 24 | .PHONY: concat 25 | 26 | all: info bug test 27 | 28 | info: 29 | $(BASH) --version 30 | env 31 | $(BASH) -c shopt 32 | .PHONY: info 33 | 34 | bug: 35 | -@$(BASH) ./imosh test/bash_bug.sh 36 | .PHONY: bug 37 | 38 | test: concat 39 | @if ! $(BASH) ./imosh test/*_test.sh test/*/*_test.sh; then exit 1; fi 40 | .PHONY: test 41 | 42 | test/%: concat test/%_test.sh 43 | ./imosh test/$*_test.sh 44 | 45 | benchmark: concat 46 | @time $(BASH) -c "for i in {1..10}; do $(BASH) -c '. ./imosh'; done" 47 | .PHONY: benchmark 48 | -------------------------------------------------------------------------------- /library/70-handler/exit.sh: -------------------------------------------------------------------------------- 1 | __imosh::exit_handler::atexit() { 2 | # Stop checking exit statuses because this is inside exit_handler, and it is 3 | # impossible to output a stacktrace for an irregular exit status. 4 | set +e 5 | if [ -f "${__IMOSH_CORE_TMPDIR}/atexit.sh" ]; then 6 | source "${__IMOSH_CORE_TMPDIR}/atexit.sh" 7 | fi 8 | } 9 | 10 | __imosh::exit_handler() { 11 | local exit_code="$?" 12 | 13 | # Revert the EXIT trap to prevent an infinite loop. 14 | trap - EXIT 15 | 16 | # Output a stacktrace if the script does not output a stacktrace for the 17 | # error. (signal_handler may output a stacktrace before calling this.) 18 | if (( exit_code && ! __IMOSH_STACK_TRACED )); then 19 | imosh::stack_trace "Error status: ${exit_code}" 20 | fi 21 | 22 | # Check the current process is not the root process so as not to remove the 23 | # root process's temporary directory. 24 | local pid='' 25 | func::getmypid pid 26 | if [ "${pid}" != "${IMOSH_ROOT_PID}" ]; then 27 | LOG INFO 'Finalizing a child process...' 28 | return 29 | fi 30 | 31 | __imosh::exit_handler::atexit & 32 | if ! wait "$!"; then 33 | LOG ERROR 'Failed to run atexit entirely.' 34 | fi 35 | rm -R -f "${IMOSH_TMPDIR}" 36 | 37 | # Close log pipes. 38 | exec 101>&- 102>&- 103>&- 104>&- 39 | } 40 | 41 | trap '__imosh::exit_handler' EXIT 42 | -------------------------------------------------------------------------------- /test/func/substr_test.sh: -------------------------------------------------------------------------------- 1 | run_testcase() { 2 | local expected="$1"; shift 3 | 4 | local variable='' 5 | func::substr variable "$@" 6 | EXPECT_EQ "${expected}" "${variable}" 7 | EXPECT_EQ "${expected}" "$(sub::substr "$@")" 8 | } 9 | 10 | test::substr() { 11 | run_testcase 'bc' 'abc' -2 12 | run_testcase '' 'abc' -2 -3 13 | run_testcase '' 'abc' -2 -2 14 | run_testcase 'b' 'abc' -2 -1 15 | run_testcase '' 'abc' -2 0 16 | run_testcase 'b' 'abc' -2 1 17 | run_testcase 'bc' 'abc' -2 2 18 | run_testcase 'bc' 'abc' -2 3 19 | 20 | run_testcase 'abc' 'abc' 0 21 | run_testcase '' 'abc' 0 -4 22 | run_testcase '' 'abc' 0 -3 23 | run_testcase 'a' 'abc' 0 -2 24 | run_testcase 'ab' 'abc' 0 -1 25 | run_testcase '' 'abc' 0 0 26 | run_testcase 'a' 'abc' 0 1 27 | run_testcase 'abc' 'abc' 0 3 28 | run_testcase 'abc' 'abc' 0 5 29 | 30 | run_testcase 'bc' 'abc' 1 31 | run_testcase '' 'abc' 1 -3 32 | run_testcase '' 'abc' 1 -2 33 | run_testcase 'b' 'abc' 1 -1 34 | run_testcase '' 'abc' 1 0 35 | run_testcase 'b' 'abc' 1 1 36 | run_testcase 'bc' 'abc' 1 3 37 | 38 | run_testcase '' 'abc' 3 39 | run_testcase '' 'abc' 5 40 | 41 | run_testcase '' 'abc' 9 -9 42 | run_testcase '' 'abc' 9 -1 43 | run_testcase '' 'abc' 9 0 44 | run_testcase '' 'abc' 9 9 45 | } 46 | -------------------------------------------------------------------------------- /library/10-func/filesystem/fgets.sh: -------------------------------------------------------------------------------- 1 | # fgets -- Gets a line from STDIN. 2 | # 3 | # fgets reads a line and regards it as a result. fgets does not strip a 4 | # trailing new line. The function form sets the result to a variable. The 5 | # subroutine form outputs the result to the standard output. 6 | # 7 | # Usage: 8 | # // 1. Function form. 9 | # bool func::fgets(string* variable) 10 | # // 2. Subroutine form. 11 | # bool sub::fgets() > line 12 | func::fgets() { 13 | if [ "$#" -eq 1 ]; then 14 | local __fgets_buffer='' 15 | local __fgets_line='' 16 | 17 | func::let "${1}" '' 18 | # Check if EOF. 19 | if ! IFS= read -r -n 1 -d '' __fgets_buffer; then 20 | return 1 21 | fi 22 | __fgets_line+="${__fgets_buffer}" 23 | if [ "${__fgets_buffer}" = $'\n' ]; then 24 | func::strcpy "${1}" __fgets_buffer 25 | return 26 | fi 27 | if IFS= read -r -d $'\n' __fgets_buffer; then 28 | __fgets_buffer+=$'\n' 29 | fi 30 | __fgets_line+="${__fgets_buffer}" 31 | func::strcpy "${1}" __fgets_line 32 | else 33 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 34 | fi 35 | } 36 | 37 | sub::fgets() { 38 | if [ "$#" -eq 0 ]; then 39 | local variable='' 40 | if ! func::fgets variable; then return 1; fi 41 | sub::print "${variable}" 42 | else 43 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 44 | fi 45 | } 46 | -------------------------------------------------------------------------------- /test/func/readline_test.sh: -------------------------------------------------------------------------------- 1 | test::func_readline() { 2 | local variable='' 3 | local LINE='' NEWLINE='' 4 | 5 | sub::print $'abc\ndef\n' > "${TMPDIR}/test" 6 | exec < "${TMPDIR}/test" 7 | EXPECT_TRUE func::readline 8 | EXPECT_EQ 'abc' "${LINE}" 9 | EXPECT_EQ $'\n' "${NEWLINE}" 10 | EXPECT_TRUE func::readline 11 | EXPECT_EQ 'def' "${LINE}" 12 | EXPECT_EQ $'\n' "${NEWLINE}" 13 | EXPECT_FALSE func::readline 14 | EXPECT_EQ '' "${LINE}" 15 | EXPECT_EQ '' "${NEWLINE}" 16 | 17 | sub::print $'abc\n\ndef' > "${TMPDIR}/test" 18 | exec < "${TMPDIR}/test" 19 | EXPECT_TRUE func::readline 20 | EXPECT_EQ 'abc' "${LINE}" 21 | EXPECT_EQ $'\n' "${NEWLINE}" 22 | EXPECT_TRUE func::readline 23 | EXPECT_EQ '' "${LINE}" 24 | EXPECT_EQ $'\n' "${NEWLINE}" 25 | EXPECT_TRUE func::readline 26 | EXPECT_EQ 'def' "${LINE}" 27 | EXPECT_EQ '' "${NEWLINE}" 28 | EXPECT_FALSE func::readline 29 | EXPECT_EQ '' "${LINE}" 30 | EXPECT_EQ '' "${NEWLINE}" 31 | 32 | sub::print $'abc\r\n\r\ndef\r\n' > "${TMPDIR}/test" 33 | exec < "${TMPDIR}/test" 34 | EXPECT_TRUE func::readline 35 | EXPECT_EQ 'abc' "${LINE}" 36 | EXPECT_EQ $'\r\n' "${NEWLINE}" 37 | EXPECT_TRUE func::readline 38 | EXPECT_EQ '' "${LINE}" 39 | EXPECT_EQ $'\r\n' "${NEWLINE}" 40 | EXPECT_TRUE func::readline 41 | EXPECT_EQ 'def' "${LINE}" 42 | EXPECT_EQ $'\r\n' "${NEWLINE}" 43 | EXPECT_FALSE func::readline 44 | EXPECT_EQ '' "${LINE}" 45 | EXPECT_EQ '' "${NEWLINE}" 46 | } 47 | -------------------------------------------------------------------------------- /library/10-func/strings/base64_decode.sh: -------------------------------------------------------------------------------- 1 | # base64_decode -- Decodes data with MIME base64. 2 | # 3 | # Usage: 4 | # // 1. Function form. 5 | # void func::base64_decode(string* variable, string data) 6 | # // 2. Command form. 7 | # void sub::base64_decode(string data) > output 8 | # // 3. Stream form. 9 | # void stream::base64_decode() < input > output 10 | func::base64_decode() { 11 | if [ "$#" -eq 2 ]; then 12 | local __base64_decode_file='' 13 | func::tmpfile __base64_decode_file 14 | sub::print "${2}" | stream::base64_decode >"${__base64_decode_file}" 15 | func::file_get_contents "${1}" "${__base64_decode_file}" 16 | else 17 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 18 | fi 19 | } 20 | 21 | sub::base64_decode() { 22 | if [ "$#" -eq 1 ]; then 23 | sub::print "${1}" | stream::base64_decode 24 | else 25 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 26 | fi 27 | } 28 | 29 | stream::base64_decode() { 30 | if [ "$#" -eq 0 ]; then 31 | local __base64_decode_file='' 32 | func::tmpfile __base64_decode_file 33 | tr -cd '[:alnum:]/+' >"${__base64_decode_file}" 34 | wc -c <"${__base64_decode_file}" >"${__base64_decode_file}.count" 35 | local __base64_decode_count=0 36 | func::file_get_contents \ 37 | __base64_decode_count "${__base64_decode_file}.count" 38 | local __base64_decode_suffix='====' 39 | sub::print "${__base64_decode_suffix:0:$(( 3 - (__base64_decode_count + 3) % 4 ))}" >>"${__base64_decode_file}" 40 | base64 --decode -i "${__base64_decode_file}" 41 | else 42 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 43 | fi 44 | } 45 | -------------------------------------------------------------------------------- /library/10-func/math/rand.sh: -------------------------------------------------------------------------------- 1 | # rand -- Generates a random integer. 2 | # 3 | # rand generates a random integer. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::rand(int* variable, int minimum, int maximum) 8 | # void func::rand(int* variable, int maximum) 9 | # void func::rand(int* variable) 10 | # // 2. Command form. 11 | # void sub::rand(int minimum, int maximum) > output 12 | # void sub::rand(int maximum) > output 13 | # void sub::rand() > output 14 | func::rand() { 15 | if [ "$#" -eq 3 ]; then 16 | local __rand_value=0 17 | __func::rand "${2}" "${3}" 18 | func::let "${1}" "${__rand_value}" 19 | elif [ "$#" -eq 2 ]; then 20 | func::rand "${1}" 0 "${2}" 21 | elif [ "$#" -eq 1 ]; then 22 | func::rand "${1}" 2147483647 23 | else 24 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 25 | fi 26 | } 27 | 28 | sub::rand() { 29 | if [ "$#" -le 2 ]; then 30 | local __rand_variable=0 31 | func::rand __rand_variable "$@" 32 | sub::println "${__rand_variable}" 33 | else 34 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 35 | fi 36 | } 37 | 38 | __func::rand() { 39 | local min="${1}" max="${2}" range=0 40 | if [ "${min}" -eq "${max}" ]; then 41 | __rand_value="${min}" 42 | return 43 | elif [ "${min}" -gt "${max}" ]; then 44 | local min="${max}" max="${min}" 45 | fi 46 | (( __rand_value = RANDOM ^ (RANDOM << 8) ^ (RANDOM << 16) ^ (RANDOM << 24) ^ 47 | (RANDOM << 32) ^ (RANDOM << 40) ^ (RANDOM << 48) ^ (RANDOM << 56), 48 | range = max - min + 1, 49 | __rand_value = min + (__rand_value % range + range) % range )) || true 50 | } 51 | -------------------------------------------------------------------------------- /library/10-func/strings/substr.sh: -------------------------------------------------------------------------------- 1 | # substr -- Returns a substring. 2 | # 3 | # substr returns a substring of a string specfied by start and length. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::substr( 8 | # string* output, string input, string start, string length) 9 | # void func::substr( 10 | # string* output, string input, string start) 11 | # // 2. Command form. 12 | # void sub::substr(string input, string start, string length) > output 13 | # void sub::substr(string input, string start) > output 14 | func::substr() { 15 | if [ "$#" -eq 4 ]; then 16 | local __substr_size="${#2}" 17 | local __substr_start="${3}" 18 | local __substr_length="${4}" 19 | (( __substr_start = ( __substr_start < 0 ) 20 | ? __substr_size + __substr_start : __substr_start, 21 | __substr_start = ( __substr_start < 0 ) ? 0 : __substr_start, 22 | __substr_length = ( __substr_length < 0 ) 23 | ? __substr_size + __substr_length - __substr_start 24 | : __substr_length, 25 | __substr_length = ( __substr_length < 0 ) ? 0 : __substr_length 26 | )) || true 27 | func::let "${1}" "${2:${__substr_start}:${__substr_length}}" 28 | elif [ "$#" -eq 3 ]; then 29 | func::substr "${1}" "${2}" "${3}" "${#2}" 30 | else 31 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 32 | fi 33 | } 34 | 35 | sub::substr() { 36 | if [ "$#" -eq 3 -o "$#" -eq 2 ]; then 37 | local __substr_value='' 38 | func::substr __substr_value "$@" 39 | sub::println "${__substr_value}" 40 | else 41 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 42 | fi 43 | } 44 | -------------------------------------------------------------------------------- /library/10-func/array/array_unique.sh: -------------------------------------------------------------------------------- 1 | # array_unique -- Removes duplicated elements from an array variable. 2 | # 3 | # array_unique sorts elements first and removes duplicated elements. Function 4 | # form applies array_unique to the given variable. Stream form applies 5 | # array_unique to every line. Every line is treated as elements. 6 | # 7 | # Usage: 8 | # // 1. Function form. 9 | # void func::array_unique(string[]* variable) 10 | # // 2. Stream form. 11 | # void stream::array_unique() < input > output 12 | # 13 | # Examples: 14 | # a=(c b a b) 15 | # func::array_unique a 16 | # echo "${a[@]}" # => a b c 17 | # 18 | # echo c b a b | stream::array_unique # => a b c 19 | # 20 | # echo c,b,a,b | IFS=, stream::array_unique # => a,b,c 21 | func::array_unique() { 22 | if [ "$#" -eq 1 ]; then 23 | if sub::array_is_empty "${1}"; then return; fi 24 | local __array_unique_values=() 25 | func::array_values __array_unique_values "${1}" 26 | func::sort __array_unique_values 27 | local __array_unique_i="${#__array_unique_values[*]}" 28 | while (( __array_unique_i -= 1 )); do 29 | if [ "${__array_unique_values[$(( __array_unique_i - 1 ))]}" = \ 30 | "${__array_unique_values[${__array_unique_i}]}" ]; then 31 | unset "__array_unique_values[${__array_unique_i}]" 32 | fi 33 | done 34 | func::array_values "${1}" __array_unique_values 35 | else 36 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 37 | fi 38 | } 39 | 40 | stream::array_unique() { 41 | if [ "$#" -eq 0 ]; then 42 | stream::array_map ARRAY func::array_unique 43 | else 44 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 45 | fi 46 | } 47 | -------------------------------------------------------------------------------- /doc/array/array_map.sh.md: -------------------------------------------------------------------------------- 1 | # array_map 2 | array_map -- Applies a callback to elements. 3 | 4 | array_map applies a callback to every element. stream::array_map applies a 5 | callback to every line's elements. 6 | stream::array_map supports the following functions: array, function, inplace 7 | and command. 8 | 9 | ## Usage 10 | ```sh 11 | // 1. Function form. 12 | void func::array_map(string[]* variable, string type, 13 | string callback [, string arguments...]) 14 | // 2. Stream form. 15 | void stream::array_map( 16 | string type, string callback [, string arguments...]) 17 | < input > output 18 | ``` 19 | 20 | 21 | ## Type 22 | * ARRAY 23 | * Reads every line as an array, and applies a callback to every line. 24 | * FUNCTION 25 | * Reads every line as a string and applies a callback. A callback should be 26 | a function format like: function(string* output, input). 27 | * INPLACE 28 | * Reads every line as a string and applies a callback. A callback should be 29 | an inplace format like: function(string* input_and_output). 30 | * COMMAND 31 | * Reads every line as a string and applies a callback. A callback should be 32 | a command format like: function(string input) > output. 33 | 34 | ## Examples 35 | ```sh 36 | input=('abc' 'DeF' '012') 37 | func::array_map input INPLACE func::strtoupper 38 | echo "${input[@]}" # => ABC DEF 012 39 | ``` 40 | 41 | 42 | ```sh 43 | sub::print $'def,abc,ghi\n1,3,2,5,4' | \ 44 | IFS=',' stream::array_map ARRAY func::sort 45 | # => abc,def,ghi\n1,2,3,4,5 46 | ``` 47 | 48 | 49 | ```sh 50 | sub::print $'abcbd\nbcdbcb' | \ 51 | stream::array_map INPLACE func::str_replace 'bc' 'BC' 52 | # => aBCbd\nBCdBCb 53 | ``` 54 | -------------------------------------------------------------------------------- /library/10-func/filesystem/file_get_contents.sh: -------------------------------------------------------------------------------- 1 | # file_get_contents -- Reads an entire file into a string. 2 | # 3 | # The function form reads an entire file and sets its contents to the 4 | # variable. The subroutine form reads an entire file and outputs its contents 5 | # to the standard output. The stream form reads a file name for each line and 6 | # outputs its contents to the standard output. 7 | # 8 | # Usage: 9 | # // 1. Function form. 10 | # func::file_get_contents(string* variable, string filename) 11 | # // 2. Subroutine form. 12 | # sub::file_get_contents(string filename) > output 13 | # // 3. Stream form. 14 | # stream::file_get_contents() < input > output 15 | # 16 | # Examples: 17 | # sub::print hello > "${TMPDIR}/foo" 18 | # func::file_get_contents variable "${TMPDIR}/foo" 19 | # echo "${variable}" # => hello 20 | # 21 | # sub::print hello > "${TMPDIR}/foo" 22 | # sub::file_get_contents "${TMPDIR}/foo" # => hello 23 | # 24 | # sub::print hello > "${TMPDIR}/foo" 25 | # sub::print world > "${TMPDIR}/bar" 26 | # { echo "${TMPDIR}/foo"; echo "${TMPDIR}/bar"; } | \ 27 | # stream::file_get_contents # => helloworld 28 | func::file_get_contents() { 29 | if [ "$#" -eq 2 ]; then 30 | IFS= read -r -d '' "${1}" < "${2}" || true 31 | else 32 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 33 | fi 34 | } 35 | 36 | sub::file_get_contents() { 37 | if [ "$#" -eq 1 ]; then 38 | cat < "${1}" 39 | else 40 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 41 | fi 42 | } 43 | 44 | stream::file_get_contents() { 45 | if [ "$#" -eq 0 ]; then 46 | stream::array_map COMMAND cat 47 | else 48 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 49 | fi 50 | } 51 | -------------------------------------------------------------------------------- /library/10-func/var/cast.sh: -------------------------------------------------------------------------------- 1 | # cast -- Casts a variable. 2 | # 3 | # Casts variable into a specified type. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # bool func::cast(variant* variable, string type) 8 | # // 2. Function form. (Dies if conversion fails.) 9 | # void func::cast_or_die(variant* variable, string type) 10 | func::cast() { 11 | if [ "$#" -eq 2 ]; then 12 | if [ "${2}" = 'LIST' ]; then 13 | func::cast "${1}" 'MULTISTRING' || return "$?" 14 | return 15 | fi 16 | case "${2}" in 17 | 'MULTI'*) 18 | local __cast_values=() 19 | local __cast_element_type="${2:5}" 20 | func::array_values __cast_values "${1}" 21 | if [ "${#__cast_values[*]}" -ne 0 ]; then 22 | local __cast_index=0 23 | for __cast_index in "${!__cast_values[@]}"; do 24 | local __cast_value="${__cast_values[${__cast_index}]}" 25 | func::cast __cast_value "${__cast_element_type}" || return "$?" 26 | __cast_values["${__cast_index}"]="${__cast_value}" 27 | done 28 | fi 29 | func::array_values "${1}" __cast_values 30 | ;; 31 | 'INT') func::intval "${1}" || return "$?";; 32 | 'ENUM') func::enumval "${1}" || return "$?";; 33 | 'FLOAT') func::floatval "${1}" || return "$?";; 34 | 'STRING') func::strval "${1}" || return "$?";; 35 | 'BOOL') func::boolval "${1}" || return "$?";; 36 | 'VARIANT') return 0;; 37 | *) LOG FATAL "Unknown type: ${2}";; 38 | esac 39 | else 40 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 41 | fi 42 | } 43 | 44 | func::cast_or_die() { 45 | if ! func::cast "${@}"; then 46 | IFS=' ' eval 'LOG FATAL "Type mismatch: ${*}"' 47 | fi 48 | } 49 | -------------------------------------------------------------------------------- /library/10-func/array/sort.sh: -------------------------------------------------------------------------------- 1 | # sort -- Sorts elements. 2 | # 3 | # sort sorts elements. The function form sorts elements in a variable in place. 4 | # The stream form applies sort to every line. Every line is treated as 5 | # elements. 6 | # 7 | # Usage: 8 | # // 1. Function form. 9 | # void func::sort(string[]* variable) 10 | # // 2. Stream form. 11 | # void stream::sort() < input > output 12 | func::sort() { 13 | if [ "$#" -eq 1 ]; then 14 | local __sort_name="${1}" 15 | if eval "[ \"\${#${__sort_name}[*]}\" -lt 2 ]"; then return; fi 16 | local __sort_values=() 17 | func::array_values __sort_values "${__sort_name}" 18 | __func::quick_sort 19 | func::array_values "${__sort_name}" __sort_values 20 | else 21 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 22 | fi 23 | } 24 | 25 | stream::sort() { 26 | if [ "$#" -eq 0 ]; then 27 | stream::array_map ARRAY func::sort 28 | else 29 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 30 | fi 31 | } 32 | 33 | __func::quick_sort() { 34 | local size="${#__sort_values[@]}" 35 | local pivot="${__sort_values[$(( size / 2 ))]}" 36 | local values1=() values2=() values3=() 37 | 38 | for value in "${__sort_values[@]}"; do 39 | if [ "${value}" \< "${pivot}" ]; then 40 | values1+=("${value}") 41 | elif [ "${value}" \> "${pivot}" ]; then 42 | values3+=("${value}") 43 | else 44 | values2+=("${value}") 45 | fi 46 | done 47 | func::sort values1 48 | func::sort values3 49 | __sort_values=() 50 | if ! sub::array_is_empty values1; then __sort_values+=("${values1[@]}"); fi 51 | if ! sub::array_is_empty values2; then __sort_values+=("${values2[@]}"); fi 52 | if ! sub::array_is_empty values3; then __sort_values+=("${values3[@]}"); fi 53 | } 54 | -------------------------------------------------------------------------------- /library/10-func/strings/hex2bin.sh: -------------------------------------------------------------------------------- 1 | # hex2bin -- Decodes a hexadecimally encoded binary string. 2 | # 3 | # Decodes a hexadecimally encoded binary string. 4 | # 5 | # Usage: 6 | # // 1-a. Function form. 7 | # void func::hex2bin(string* output, string input) 8 | # // 1-b. Function form. 9 | # void func::hex2bin(string* variable) 10 | # // 2. Command form. 11 | # void sub::hex2bin(string input) > output 12 | # // 3. Stream form. 13 | # void stream::hex2bin() < input > output 14 | func::hex2bin() { 15 | if [ "$#" -eq 2 ]; then 16 | local __hex2bin_result='' 17 | __func::hex2bin <<<"${2}" 18 | func::let "${1}" "${__hex2bin_result}" 19 | elif [ "$#" -eq 1 ]; then 20 | local __hex2bin_data='' 21 | func::strcpy __hex2bin_data "${1}" 22 | func::hex2bin "${1}" "${__hex2bin_data}" 23 | else 24 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 25 | fi 26 | } 27 | 28 | sub::hex2bin() { 29 | if [ "$#" -eq 1 ]; then 30 | stream::hex2bin <<<"${1}" 31 | else 32 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 33 | fi 34 | } 35 | 36 | stream::hex2bin() { 37 | if [ "$#" -eq 0 ]; then 38 | local __hex2bin_variable='' 39 | __func::hex2bin 40 | else 41 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 42 | fi 43 | } 44 | 45 | __func::hex2bin() { 46 | local __hex2bin_char='' __hex2bin_buffer='' 47 | while read -n 1 __hex2bin_char; do 48 | case "${__hex2bin_char}" in 49 | [0-9a-fA-F]) __hex2bin_buffer+="${__hex2bin_char}";; 50 | *) continue;; 51 | esac 52 | if [ "${#__hex2bin_buffer}" -eq 2 ]; then 53 | if ! sub::isset __hex2bin_result; then 54 | printf "\\x${__hex2bin_buffer}" 55 | else 56 | eval "__hex2bin_result+=\$'\\x${__hex2bin_buffer}'" 57 | fi 58 | __hex2bin_buffer='' 59 | fi 60 | done 61 | } 62 | -------------------------------------------------------------------------------- /test/func/sort_test.sh: -------------------------------------------------------------------------------- 1 | test::func_sort() { 2 | local values=() 3 | EXPECT_TRUE func::sort values 4 | 5 | local values=('a') 6 | EXPECT_TRUE func::sort values 7 | EXPECT_EQ "a" "$(echo "${values[@]}")" 8 | 9 | local values=('b' 'a') 10 | EXPECT_TRUE func::sort values 11 | EXPECT_EQ "a b" "$(echo "${values[@]}")" 12 | 13 | local values=(w a c m d i n v y u q j g r x t z k h s e l o f b p) 14 | EXPECT_TRUE func::sort values 15 | EXPECT_EQ "a b c d e f g h i j k l m n o p q r s t u v w x y z" \ 16 | "$(echo "${values[@]}")" 17 | 18 | local values=(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20) 19 | EXPECT_TRUE func::sort values 20 | EXPECT_EQ "1 10 11 12 13 14 15 16 17 18 19 2 20 3 4 5 6 7 8 9" \ 21 | "$(echo "${values[@]}")" 22 | 23 | local i=1 24 | local c1=$'\x01' 25 | local values=($'\x1a' $'\x11' "${c1}" $'\x16' $'\x09' $'\x03' $'\x1c' $'\x05' 26 | $'\x1d' $'\x1b' $'\x0a' $'\x19' $'\x1e' $'\x0e' $'\x15' $'\x0f' 27 | $'\x0d' $'\x08' $'\x04' $'\x13' $'\x02' $'\x07' $'\x1f' $'\x0c' 28 | $'\x17' $'\x18' $'\x20' $'\x10' $'\x06' $'\x0b' $'\x12' $'\x14') 29 | IFS= EXPECT_TRUE func::sort values 30 | local expected='' 31 | expected+=$'\x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 ' 32 | expected+=$'\x09 \x0a \x0b \x0c \x0d \x0e \x0f \x10 ' 33 | expected+=$'\x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 ' 34 | expected+=$'\x19 \x1a \x1b \x1c \x1d \x1e \x1f \x20' 35 | EXPECT_EQ "$(sub::bin2hex "${expected}")" \ 36 | "$(sub::bin2hex "$(sub::implode ' ' values)")" 37 | 38 | IFS= EXPECT_TRUE func::sort values 39 | EXPECT_EQ "$(sub::bin2hex "${expected}")" \ 40 | "$(sub::bin2hex "$(sub::implode ' ' values)")" 41 | } 42 | 43 | test::stream_sort() { 44 | EXPECT_EQ \ 45 | $'a b c\nd e e f' \ 46 | "$(sub::print $'c b a\nf\te\te\td\n' | stream::sort)" 47 | } 48 | -------------------------------------------------------------------------------- /library/10-func/strings/str_replace.sh: -------------------------------------------------------------------------------- 1 | # str_replace -- Replaces a substring with another substring. 2 | # 3 | # Replace search with replace in *subject. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::str_replace(string* subject, string search, string replace) 8 | # // 2. Command form. 9 | # void sub::str_replace( 10 | # string input, string search, string replace) > output 11 | # // 3. Stream form. 12 | # void stream::str_replace(string search, string replace) < input > output 13 | func::str_replace() { 14 | if [ "$#" -eq 3 ]; then 15 | eval "${1}=\"\${${1}//\"\${2}\"/\${3}}\"" 16 | else 17 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 18 | fi 19 | } 20 | 21 | sub::str_replace() { 22 | if [ "$#" -eq 3 ]; then 23 | local __str_replace_value="${1}" 24 | func::str_replace __str_replace_value "${2}" "${3}" 25 | sub::println "${__str_replace_value}" 26 | else 27 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 28 | fi 29 | } 30 | 31 | stream::str_replace() { 32 | if [ "$#" -eq 2 ]; then 33 | local __str_replace_search="${1}" 34 | local __str_replace_replace="${2}" 35 | func::str_replace __str_replace_search '\' '\\' 36 | func::str_replace __str_replace_search '/' '\/' 37 | func::str_replace __str_replace_search '.' '\.' 38 | func::str_replace __str_replace_search '*' '\*' 39 | func::str_replace __str_replace_search '^' '\^' 40 | func::str_replace __str_replace_search '$' '\$' 41 | func::str_replace __str_replace_search '[' '\[' 42 | func::str_replace __str_replace_search ']' '\]' 43 | func::str_replace __str_replace_replace '\' '\\' 44 | func::str_replace __str_replace_replace '/' '\/' 45 | func::str_replace __str_replace_replace '&' '\&' 46 | sed -e "s/${__str_replace_search}/${__str_replace_replace}/g" 47 | else 48 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 49 | fi 50 | } 51 | -------------------------------------------------------------------------------- /library/10-func/strings/implode.sh: -------------------------------------------------------------------------------- 1 | # implode -- Joins array elements with a string. 2 | # 3 | # func::implode joins `pieces` with `glue`. 4 | # *Stream form* uses the IFS environment variable as an input separator and 5 | # processes line by line. 6 | # 7 | # Usage: 8 | # // 1. Function form. 9 | # void func::implode(string* variable, string glue, string[]* pieces) 10 | # // 2. Command form. 11 | # void sub::implode(string glue, string[]* pieces) > result 12 | # void sub::implode(string[]* pieces) > result 13 | # // 3. Stream form. 14 | # void stream::implode(string glue) < input > output 15 | # 16 | # Aliases: 17 | # func::join, sub::join and stream::join are aliases of func::implode, 18 | # sub::implode and stream::implode respectively. 19 | func::implode() { 20 | # 1. Function form. 21 | if [ "$#" -eq 3 ]; then 22 | local __implode_pieces=() 23 | func::array_values __implode_pieces "${3}" 24 | local __implode_size="${#__implode_pieces[*]}" 25 | local __implode_i=0 26 | local __implode_result='' 27 | while (( __implode_i < __implode_size )); do 28 | if (( __implode_i != 0 )); then 29 | __implode_result+="${2}" 30 | fi 31 | __implode_result+="${__implode_pieces[${__implode_i}]}" 32 | (( __implode_i += 1 )) || true 33 | done 34 | func::let "${1}" "${__implode_result}" 35 | elif [ "$#" -eq 2 ]; then 36 | DEPRECATED 37 | sub::implode "$@" 38 | elif [ "$#" -eq 1 ]; then 39 | DEPRECATED 40 | stream::implode "$@" 41 | else 42 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 43 | fi 44 | } 45 | 46 | sub::implode() { 47 | if [ "$#" -eq 2 ]; then 48 | local __implode_output='' 49 | func::implode __implode_output "${1}" "${2}" 50 | sub::println "${__implode_output}" 51 | elif [ "$#" -eq 1 ]; then 52 | sub::implode "${IFS:0:1}" "${1}" 53 | else 54 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 55 | fi 56 | } 57 | 58 | stream::implode() { 59 | if [ "$#" -eq 1 ]; then 60 | local LINE=() 61 | while func::readarray; do 62 | sub::implode "${1}" LINE 63 | done 64 | else 65 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 66 | fi 67 | } 68 | 69 | func::join() { func::implode "$@"; } 70 | sub::join() { sub::implode "$@"; } 71 | stream::join() { stream::implode "$@"; } 72 | -------------------------------------------------------------------------------- /library/90-flags/50-variables.sh: -------------------------------------------------------------------------------- 1 | # This is automatically generated by print-flag-variables. 2 | FLAGS_alsologtostderr=0 3 | FLAGS_h=0 4 | FLAGS_help=0 5 | FLAGS_help_format= 6 | FLAGS_helpfull=0 7 | FLAGS_imosh_test= 8 | FLAGS_log_dir= 9 | FLAGS_logtostderr=0 10 | FLAGS_stacktrace_threshold=FATAL 11 | __IMOSH_FLAGS=([0]="IMOSH:help" [1]="IMOSH:helpfull" [2]="IMOSH:alsologtostderr" [3]="IMOSH:logtostderr" [4]="IMOSH:log_dir" [5]="IMOSH:stacktrace_threshold" [6]="IMOSH:help_format" [7]="IMOSH:imosh_test") 12 | __IMOSH_FLAGS_ALIASES=([0]="help:h") 13 | __IMOSH_FLAGS_DEFAULT_alsologtostderr=--alsologtostderr=false 14 | __IMOSH_FLAGS_DEFAULT_help=--help=false 15 | __IMOSH_FLAGS_DEFAULT_help_format='--help_format='\'''\''' 16 | __IMOSH_FLAGS_DEFAULT_helpfull=--helpfull=false 17 | __IMOSH_FLAGS_DEFAULT_imosh_test='--imosh_test='\'''\''' 18 | __IMOSH_FLAGS_DEFAULT_log_dir='--log_dir='\'''\''' 19 | __IMOSH_FLAGS_DEFAULT_logtostderr=--logtostderr=false 20 | __IMOSH_FLAGS_DEFAULT_stacktrace_threshold=--stacktrace_threshold=FATAL 21 | __IMOSH_FLAGS_DESCRIPTION_alsologtostderr='Log messages go to stderr in addition to logfiles.' 22 | __IMOSH_FLAGS_DESCRIPTION_help='Print this help message and exit. (Alias: --h)' 23 | __IMOSH_FLAGS_DESCRIPTION_help_format='Help format to output.' 24 | __IMOSH_FLAGS_DESCRIPTION_helpfull='Print all the help message.' 25 | __IMOSH_FLAGS_DESCRIPTION_imosh_test='Test files to test.' 26 | __IMOSH_FLAGS_DESCRIPTION_log_dir='Directory to output log files. Output no files if this flag is empty.' 27 | __IMOSH_FLAGS_DESCRIPTION_logtostderr='Log messages go to stderr instead of logfiles.' 28 | __IMOSH_FLAGS_DESCRIPTION_stacktrace_threshold='Threshold to show stacktrace.' 29 | __IMOSH_FLAGS_IS_DEFAULT_alsologtostderr=1 30 | __IMOSH_FLAGS_IS_DEFAULT_h=1 31 | __IMOSH_FLAGS_IS_DEFAULT_help=1 32 | __IMOSH_FLAGS_IS_DEFAULT_help_format=1 33 | __IMOSH_FLAGS_IS_DEFAULT_helpfull=1 34 | __IMOSH_FLAGS_IS_DEFAULT_imosh_test=1 35 | __IMOSH_FLAGS_IS_DEFAULT_log_dir=1 36 | __IMOSH_FLAGS_IS_DEFAULT_logtostderr=1 37 | __IMOSH_FLAGS_IS_DEFAULT_stacktrace_threshold=1 38 | __IMOSH_FLAGS_MULTIALIASES=() 39 | __IMOSH_FLAGS_TYPE_alsologtostderr=BOOL 40 | __IMOSH_FLAGS_TYPE_h=BOOL 41 | __IMOSH_FLAGS_TYPE_help=BOOL 42 | __IMOSH_FLAGS_TYPE_help_format=STRING 43 | __IMOSH_FLAGS_TYPE_helpfull=BOOL 44 | __IMOSH_FLAGS_TYPE_imosh_test=STRING 45 | __IMOSH_FLAGS_TYPE_log_dir=STRING 46 | __IMOSH_FLAGS_TYPE_logtostderr=BOOL 47 | __IMOSH_FLAGS_TYPE_stacktrace_threshold=STRING 48 | -------------------------------------------------------------------------------- /test/bash_bug.sh: -------------------------------------------------------------------------------- 1 | # CAVEATS: 2 | # Some versions of BASH 4 have a bug around string comparation using double 3 | # brackets. 4 | test::compare_string() { 5 | # BASH 4 fails. 6 | EXPECT_TRUE eval "[[ ' b' < 'a' ]]" 7 | EXPECT_TRUE eval "[ ' b' \\< 'a' ]" 8 | # BASH 4 fails. 9 | EXPECT_TRUE eval "[[ \$'\n' < ' ' ]]" 10 | EXPECT_TRUE eval "[ \$'\n' \\< ' ' ]" 11 | # BASH 4 fails. 12 | EXPECT_TRUE eval "[[ 'A' < 'a' ]]" 13 | EXPECT_TRUE eval "[ 'A' \\< 'a' ]" 14 | EXPECT_TRUE eval "[[ 'a' < 'aa' ]]" 15 | EXPECT_TRUE eval "[ 'a' \\< 'aa' ]" 16 | } 17 | 18 | # CAVEATS: 19 | # BASH 3 has a bug: $'\x01' in array is treated as $'\x01\x01'. 20 | test::soh_in_array() { 21 | local values=($'\x01') 22 | local expected='01' 23 | local actual='' 24 | 25 | func::implode actual '' values 26 | func::bin2hex actual "${actual}" 27 | # BASH 3 fails. SOH cannot be treated in array correctly. 28 | EXPECT_EQ "${expected}" "${actual}" 29 | } 30 | 31 | # CAVEATS: 32 | # BASH 4 does not actually prepare a variable while BASH 3 prepares a 33 | # variable. 34 | test::isset() { 35 | local variable 36 | 37 | # BASH 4 fails. local does not prepare a variable. 38 | EXPECT_ALIVE eval ": \"\${variable}\"" 39 | 40 | local nullstr_variable='' 41 | EXPECT_ALIVE eval ": \"\${variable}\"" 42 | } 43 | 44 | # CAVEATS: 45 | # BASH cannot expand an empty array. 46 | test::empty_array() { 47 | local empty_array=() 48 | 49 | # BASH fails. 50 | EXPECT_ALIVE eval 'variable=("${empty_array[@]}")' 51 | } 52 | 53 | # CAVEATS: 54 | # BASH does not reopen a descriptor if its number is not smaller than 10 and 55 | # its parent process has already opened the descriptor. 56 | test::file_descriptor() { 57 | local message='' descriptor=0 file='' 58 | for descriptor in 3 9 10 100 200; do 59 | eval "exec ${descriptor}>&-" 60 | func::tmpfile file 61 | eval "exec ${descriptor}>\"\${file}\"" 62 | "${BASH}" 'test/script/fd.sh' --descriptor="${descriptor}" 63 | func::file_get_contents message "${file}" 64 | # BASH fails if descriptor is not smaller than 10. 65 | EXPECT_EQ '' "${message}" 66 | done 67 | for descriptor in 3 9 10 100 200; do 68 | eval "exec ${descriptor}>&-" 69 | func::tmpfile file 70 | eval "exec ${descriptor}>\"\${file}\"" 71 | "${BASH}" 'test/script/fd.sh' --descriptor="${descriptor}" --close 72 | func::file_get_contents message "${file}" 73 | EXPECT_EQ '' "${message}" 74 | done 75 | } 76 | -------------------------------------------------------------------------------- /test/func/strtotime_test.sh: -------------------------------------------------------------------------------- 1 | run_date() { 2 | EXPECT_EQ '1136160000' "$(TZ=UTC sub::strtotime "${1}")" 3 | EXPECT_EQ '1136127600' "$(TZ=Asia/Tokyo sub::strtotime "${1}")" 4 | } 5 | 6 | run_datetime() { 7 | EXPECT_EQ '1136214245' "$(TZ=UTC sub::strtotime "${1}")" 8 | EXPECT_EQ '1136181845' "$(TZ=Asia/Tokyo sub::strtotime "${1}")" 9 | } 10 | 11 | run_fixed_datetime() { 12 | EXPECT_EQ '1136214245' "$(TZ=UTC sub::strtotime "${1}")" 13 | EXPECT_EQ '1136214245' "$(TZ=Asia/Tokyo sub::strtotime "${1}")" 14 | } 15 | 16 | test::strtotime() { 17 | # Proposed new HTTP format. 18 | run_date '02 Jan 2006' 19 | # Old RFC850 HTTP format. 20 | run_date '02-Jan-06' 21 | # Broken RFC850 HTTP format. 22 | run_date '02-Jan-2006' 23 | # Common logfile format. 24 | run_date '02/Jan/2006' 25 | # ISO 8601 compact date format. 26 | run_date '20060102' 27 | # ISO 8601 date format. 28 | run_date '2006-01-02' 29 | # UNIX ls format. 30 | run_date 'Jan 2 2006' 31 | # English date format. 32 | run_date 'Jan 2, 2006' 33 | # Japanese date format. 34 | run_date '2006/01/02' 35 | 36 | # ISO 8601 format without timezone. 37 | run_datetime '2006-01-02 15:04:05' 38 | # ISO 8601 format with a T separator. 39 | run_datetime '2006-01-02T15:04:05' 40 | # Compact datetime format. 41 | run_datetime '20060102150405' 42 | # HTTP format without timezone. 43 | run_datetime 'Mon, 02 Jan 2006 15:04:05' 44 | run_datetime '02 Jan 2006 15:04:05' 45 | # Japanese datetime format. 46 | run_datetime '2006/01/02 15:04:05' 47 | 48 | # HTTP format. 49 | run_fixed_datetime 'Mon, 02 Jan 2006 15:04:05 GMT' 50 | run_fixed_datetime 'Mon, 02 Jan 2006 15:04:05 +0000' 51 | run_fixed_datetime '02 Jan 2006 15:04:05 GMT' 52 | run_fixed_datetime 'Tue, 03 Jan 2006 00:04:05 +0900' 53 | run_fixed_datetime 'Tue, 03 Jan 2006 00:04:05 JST' 54 | # Old RFC850 HTTP format. 55 | run_fixed_datetime 'Monday, 02-Jan-06 15:04:05 GMT' 56 | run_fixed_datetime 'Monday, 02-Jan-2006 15:04:05 GMT' 57 | # Common logfile format. 58 | run_fixed_datetime '03/Jan/2006:00:04:05 +0900' 59 | # ISO 8601 format. 60 | run_fixed_datetime '2006-01-02 15:04:05 +0000' 61 | run_fixed_datetime '2006-01-03 00:04:05 +0900' 62 | run_fixed_datetime '2006-01-02T15:04:05Z' 63 | run_fixed_datetime '2006-01-03T00:04:05+0900' 64 | run_fixed_datetime '2006-01-03T00:04:05+09:00' 65 | # UNIX timestamp format. 66 | run_fixed_datetime '1136214245' 67 | run_fixed_datetime '@1136214245' 68 | } 69 | -------------------------------------------------------------------------------- /test/func/array_unique_test.sh: -------------------------------------------------------------------------------- 1 | test::func_array_unique() { 2 | local values=() 3 | EXPECT_TRUE func::array_unique values 4 | 5 | local values=(w a c m d i n v y u q j g r x t z k h s e l o f b p) 6 | func::array_unique values 7 | EXPECT_EQ "a b c d e f g h i j k l m n o p q r s t u v w x y z" \ 8 | "$(echo "${values[@]}")" 9 | 10 | local values=(w a c m d i n v y u q j g r x t z k h s e l o f b p 11 | a b c d e e e e e e e e e e e e e e e e e e e e e e) 12 | func::array_unique values 13 | EXPECT_EQ "a b c d e f g h i j k l m n o p q r s t u v w x y z" \ 14 | "$(echo "${values[@]}")" 15 | 16 | local values=(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20) 17 | func::array_unique values 18 | EXPECT_EQ "1 10 11 12 13 14 15 16 17 18 19 2 20 3 4 5 6 7 8 9" \ 19 | "$(echo "${values[@]}")" 20 | 21 | local i=1 22 | local c1=$'\x01' 23 | local values=($'\x1a' $'\x11' "${c1}" $'\x16' $'\x09' $'\x03' $'\x1c' $'\x05' 24 | $'\x1d' $'\x1b' $'\x0a' $'\x19' $'\x1e' $'\x0e' $'\x15' $'\x0f' 25 | $'\x0d' $'\x08' $'\x04' $'\x13' $'\x02' $'\x07' $'\x1f' $'\x0c' 26 | $'\x17' $'\x18' $'\x20' $'\x10' $'\x06' $'\x0b' $'\x12' $'\x14') 27 | func::array_unique values 28 | local expected='' 29 | expected+=$'\x01 \x02 \x03 \x04 \x05 \x06 \x07 \x08 ' 30 | expected+=$'\x09 \x0a \x0b \x0c \x0d \x0e \x0f \x10 ' 31 | expected+=$'\x11 \x12 \x13 \x14 \x15 \x16 \x17 \x18 ' 32 | expected+=$'\x19 \x1a \x1b \x1c \x1d \x1e \x1f \x20' 33 | EXPECT_EQ "$(sub::bin2hex "${expected}")" \ 34 | "$(sub::bin2hex "$(sub::implode ' ' values)")" 35 | 36 | func::array_unique values 37 | EXPECT_EQ "$(sub::bin2hex "${expected}")" \ 38 | "$(sub::bin2hex "$(sub::implode ' ' values)")" 39 | 40 | local values=($'\x1a' $'\x11' "${c1}" $'\x16' $'\x09' $'\x03' $'\x1c' $'\x05' 41 | $'\x1d' $'\x1b' $'\x0a' $'\x19' $'\x1e' $'\x0e' $'\x15' $'\x0f' 42 | $'\x0d' $'\x08' $'\x04' $'\x13' $'\x02' $'\x07' $'\x1f' $'\x0c' 43 | $'\x17' $'\x18' $'\x20' $'\x10' $'\x06' $'\x0b' $'\x12' $'\x14' 44 | $'\x1a' $'\x11' "${c1}" $'\x16' $'\x09' $'\x03' $'\x1c' $'\x05' 45 | $'\x1d' $'\x1b' $'\x0a' $'\x19' $'\x1e' $'\x0e' $'\x15' $'\x0f' 46 | $'\x0d' $'\x08' $'\x04' $'\x13' $'\x02' $'\x07' $'\x1f' $'\x0c' 47 | $'\x17' $'\x18' $'\x20' $'\x10' $'\x06' $'\x0b' $'\x12' $'\x14') 48 | func::array_unique values 49 | } 50 | 51 | test::stream_array_unique() { 52 | EXPECT_EQ $'a b c\n\nx' "$(echo $'c b a b c\n\nx x x' | stream::array_unique)" 53 | } 54 | -------------------------------------------------------------------------------- /test/help_test.sh: -------------------------------------------------------------------------------- 1 | run() { 2 | IMOSH_FLAGS_alsologtostderr=0 IMOSH_FLAGS_logtostderr=0 \ 3 | IMOSH_FLAGS_disown_php=0 \ 4 | bash test/flags.sh "$@" 5 | } 6 | 7 | test::help() { 8 | local pids=() 9 | local expected_message="A script to test imosh flags. 10 | 11 | OPTIONS: 12 | MAIN OPTIONS: 13 | --bool=false 14 | Boolean flag. 15 | --enum=bar 16 | Enum flag. 17 | --flag='' 18 | Flag name to show. 19 | --int=100 20 | Integer flag. (Alias: --i) 21 | --list=a,b,c 22 | Multiple strings flag. 23 | --multiint=1,10,100 24 | Multiple integers flag. (Alias: --m) 25 | --show_argv=false 26 | Output extra argv. 27 | --string=default 28 | String flag." 29 | ASSERT_EQ "${expected_message}" "$(run --help 2>&1)" & 30 | pids+=("$!") 31 | ASSERT_EQ "${expected_message}" "$(run -h 2>&1)" & 32 | pids+=("$!") 33 | 34 | # There should be no output to the standard output. 35 | ASSERT_EQ '' "$(run --help 2>/dev/null)" & 36 | pids+=("$!") 37 | 38 | for pid in "${pids[@]}"; do 39 | if ! wait "${pid}"; then 40 | IMOSH_TEST_IS_FAILED=1 41 | fi 42 | done 43 | } 44 | 45 | test::help_groff() { 46 | local expected_message='.TH flags.sh 1 47 | 48 | .SH DESCRIPTION 49 | A script to test imosh flags. 50 | 51 | .SH OPTIONS 52 | .SS MAIN OPTIONS 53 | .TP 54 | \fB--bool=false\fP 55 | Boolean flag. 56 | 57 | .TP 58 | \fB--enum=bar\fP 59 | Enum flag. 60 | 61 | .TP 62 | \fB--flag='\'\''\fP 63 | Flag name to show. 64 | 65 | .TP 66 | \fB--int=100\fP 67 | Integer flag. (Alias: --i) 68 | 69 | .TP 70 | \fB--list=a,b,c\fP 71 | Multiple strings flag. 72 | 73 | .TP 74 | \fB--multiint=1,10,100\fP 75 | Multiple integers flag. (Alias: --m) 76 | 77 | .TP 78 | \fB--show_argv=false\fP 79 | Output extra argv. 80 | 81 | .TP 82 | \fB--string=default\fP 83 | String flag.' 84 | ASSERT_EQ "${expected_message}" "$(run --help_format=groff 2>&1)" 85 | } 86 | 87 | test::help_markdown() { 88 | local expected_message='A script to test imosh flags. 89 | 90 | # Options 91 | ## main options 92 | * --bool=false 93 | * Boolean flag. 94 | * --enum=bar 95 | * Enum flag. 96 | * --flag='\'\'' 97 | * Flag name to show. 98 | * --int=100 99 | * Integer flag. (Alias: --i) 100 | * --list=a,b,c 101 | * Multiple strings flag. 102 | * --multiint=1,10,100 103 | * Multiple integers flag. (Alias: --m) 104 | * --show_argv=false 105 | * Output extra argv. 106 | * --string=default 107 | * String flag.' 108 | ASSERT_EQ "${expected_message}" "$(run --help_format=markdown 2>&1)" 109 | } 110 | -------------------------------------------------------------------------------- /library/10-func/strings/strtolower.sh: -------------------------------------------------------------------------------- 1 | # strtolower -- Makes a string lowercase. 2 | # 3 | # strtolower makes a string lowercase. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::strtolower(string* variable) 8 | # // 2. Subroutine form. 9 | # void sub::strtolower(string input) > output 10 | # // 3. Stream form. 11 | # void stream::strtolower() < input > output 12 | func::strtolower() { 13 | if [ "$#" -eq 1 ]; then 14 | local __strtolower_variable="${1}" 15 | local __strtolower_value='' 16 | func::strcpy __strtolower_value "${__strtolower_variable}" 17 | # This is faster than tr for short strings. 18 | # TODO(imos): Use ${variable,,} instead once Mac OSX supports BASH 4. 19 | __strtolower_value="${__strtolower_value//A/a}" 20 | __strtolower_value="${__strtolower_value//B/b}" 21 | __strtolower_value="${__strtolower_value//C/c}" 22 | __strtolower_value="${__strtolower_value//D/d}" 23 | __strtolower_value="${__strtolower_value//E/e}" 24 | __strtolower_value="${__strtolower_value//F/f}" 25 | __strtolower_value="${__strtolower_value//G/g}" 26 | __strtolower_value="${__strtolower_value//H/h}" 27 | __strtolower_value="${__strtolower_value//I/i}" 28 | __strtolower_value="${__strtolower_value//J/j}" 29 | __strtolower_value="${__strtolower_value//K/k}" 30 | __strtolower_value="${__strtolower_value//L/l}" 31 | __strtolower_value="${__strtolower_value//M/m}" 32 | __strtolower_value="${__strtolower_value//N/n}" 33 | __strtolower_value="${__strtolower_value//O/o}" 34 | __strtolower_value="${__strtolower_value//P/p}" 35 | __strtolower_value="${__strtolower_value//Q/q}" 36 | __strtolower_value="${__strtolower_value//R/r}" 37 | __strtolower_value="${__strtolower_value//S/s}" 38 | __strtolower_value="${__strtolower_value//T/t}" 39 | __strtolower_value="${__strtolower_value//U/u}" 40 | __strtolower_value="${__strtolower_value//V/v}" 41 | __strtolower_value="${__strtolower_value//W/w}" 42 | __strtolower_value="${__strtolower_value//X/x}" 43 | __strtolower_value="${__strtolower_value//Y/y}" 44 | __strtolower_value="${__strtolower_value//Z/z}" 45 | func::let "${__strtolower_variable}" "${__strtolower_value}" 46 | else 47 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 48 | fi 49 | } 50 | 51 | sub::strtolower() { 52 | if [ "$#" -eq 1 ]; then 53 | local value="${1}" 54 | func::strtolower value 55 | sub::println "${value}" 56 | else 57 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 58 | fi 59 | } 60 | 61 | stream::strtolower() { 62 | if [ "$#" -eq 0 ]; then 63 | tr '[A-Z]' '[a-z]' 64 | else 65 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 66 | fi 67 | } 68 | -------------------------------------------------------------------------------- /library/10-func/strings/strtoupper.sh: -------------------------------------------------------------------------------- 1 | # strtoupper -- Makes a string uppercase. 2 | # 3 | # strtoupper makes a string uppercase. 4 | # 5 | # Usage: 6 | # // 1. Function form. 7 | # void func::strtoupper(string* variable) 8 | # // 2. Subroutine form. 9 | # void sub::strtoupper(string input) > output 10 | # // 3. Stream form. 11 | # void stream::strtoupper() < input > output 12 | func::strtoupper() { 13 | if [ "$#" -eq 1 ]; then 14 | local __strtoupper_variable="${1}" 15 | local __strtoupper_value='' 16 | func::strcpy __strtoupper_value "${__strtoupper_variable}" 17 | # This is faster than tr for short strings. 18 | # TODO(imos): Use ${variable^^} instead once Mac OSX supports BASH 4. 19 | __strtoupper_value="${__strtoupper_value//a/A}" 20 | __strtoupper_value="${__strtoupper_value//b/B}" 21 | __strtoupper_value="${__strtoupper_value//c/C}" 22 | __strtoupper_value="${__strtoupper_value//d/D}" 23 | __strtoupper_value="${__strtoupper_value//e/E}" 24 | __strtoupper_value="${__strtoupper_value//f/F}" 25 | __strtoupper_value="${__strtoupper_value//g/G}" 26 | __strtoupper_value="${__strtoupper_value//h/H}" 27 | __strtoupper_value="${__strtoupper_value//i/I}" 28 | __strtoupper_value="${__strtoupper_value//j/J}" 29 | __strtoupper_value="${__strtoupper_value//k/K}" 30 | __strtoupper_value="${__strtoupper_value//l/L}" 31 | __strtoupper_value="${__strtoupper_value//m/M}" 32 | __strtoupper_value="${__strtoupper_value//n/N}" 33 | __strtoupper_value="${__strtoupper_value//o/O}" 34 | __strtoupper_value="${__strtoupper_value//p/P}" 35 | __strtoupper_value="${__strtoupper_value//q/Q}" 36 | __strtoupper_value="${__strtoupper_value//r/R}" 37 | __strtoupper_value="${__strtoupper_value//s/S}" 38 | __strtoupper_value="${__strtoupper_value//t/T}" 39 | __strtoupper_value="${__strtoupper_value//u/U}" 40 | __strtoupper_value="${__strtoupper_value//v/V}" 41 | __strtoupper_value="${__strtoupper_value//w/W}" 42 | __strtoupper_value="${__strtoupper_value//x/X}" 43 | __strtoupper_value="${__strtoupper_value//y/Y}" 44 | __strtoupper_value="${__strtoupper_value//z/Z}" 45 | func::let "${__strtoupper_variable}" "${__strtoupper_value}" 46 | else 47 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 48 | fi 49 | } 50 | 51 | sub::strtoupper() { 52 | if [ "$#" -eq 1 ]; then 53 | local value="${1}" 54 | func::strtoupper value 55 | sub::println "${value}" 56 | else 57 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 58 | fi 59 | } 60 | 61 | stream::strtoupper() { 62 | if [ "$#" -eq 0 ]; then 63 | tr '[a-z]' '[A-Z]' 64 | else 65 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 66 | fi 67 | } 68 | -------------------------------------------------------------------------------- /library/10-func/misc/exit.sh: -------------------------------------------------------------------------------- 1 | # exit, die -- Kills the current script. 2 | # 3 | # sub::exit kills all the subprocesses of the current program. If an integer 4 | # argument is given, sub::exit exits with the status. Otherwise, sub::exit 5 | # shows the given message and exits with status 0. sub::die shows a stack 6 | # trace and delegates arguments to sub::exit. 7 | # 8 | # Usage: 9 | # // 1-a. Command form with a message. 10 | # void sub::exit(string message) 11 | # // 1-b. Command form with a status. 12 | # void sub::exit(int status = 0) 13 | # // 1-c. Command form with a message. 14 | # void sub::die(string message) 15 | # // 1-d. Command form with a status. 16 | # void sub::die(int status = 0) 17 | sub::exit() { 18 | if [ "$#" -eq 1 ]; then 19 | local status=0 20 | if [[ "${1}" =~ ^[0-9]+$ ]]; then 21 | status="${1}" 22 | else 23 | local message="${1}" 24 | func::rtrim message 25 | sub::println "${message}" 26 | fi 27 | sub::print "${status}" > "${__IMOSH_CORE_TMPDIR}/EXIT" 28 | # First, kill all the childs of the current process so that no child 29 | # processes spawn new processes. 30 | __sub::exit 31 | # Then, try to kill other processes under the root process except this 32 | # process. 33 | __sub::exit "${IMOSH_ROOT_PID}" 34 | # Send a TERM signal to the root process. 35 | kill -TERM "${IMOSH_ROOT_PID}" 2> '/dev/null' || true 36 | # If this is the root process, following commands will not be executed. 37 | # Exit immediately if this is the root process. 38 | exit "${status}" 39 | elif [ "$#" -eq 0 ]; then 40 | sub::exit 0 41 | else 42 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 43 | fi 44 | } 45 | 46 | sub::die() { 47 | imosh::stack_trace "*** imosh::die stack trace: ***" 48 | sub::exit "$@" 49 | } 50 | 51 | # __sub::exit -- Kills subprocesses. 52 | # 53 | # __sub::exit kills pid's subprocesses. 54 | # 55 | # Usage: 56 | # void __sub::exit::kill(int pid = $$, int caller_pid = $$) 57 | __sub::exit() { 58 | if [ "$#" -eq 2 ]; then 59 | if [ "${1}" != "${2}" -a "${1}" != "${IMOSH_ROOT_PID}" ]; then 60 | kill -STOP "${1}" 2>/dev/null || true 61 | fi 62 | local tmpfile='' 63 | local ppid=0 64 | local pid=0 65 | func::tmpfile tmpfile 66 | ps ax -o ppid,pid > "${tmpfile}" 67 | while IFS=$' \t\n' read -r ppid pid; do 68 | if [ "${ppid}" = "${1}" ]; then 69 | __sub::exit "${pid}" "${2}" 70 | fi 71 | done < "${tmpfile}" 72 | if [ "${1}" != "${2}" -a "${1}" != "${IMOSH_ROOT_PID}" ]; then 73 | kill -KILL "${1}" 2>/dev/null || true 74 | fi 75 | elif [ "$#" -eq 1 ]; then 76 | local pid=0 77 | func::getmypid pid 78 | __sub::exit "${1}" "${pid}" 79 | elif [ "$#" -eq 0 ]; then 80 | local pid=0 81 | func::getmypid pid 82 | __sub::exit "${pid}" "${pid}" 83 | else 84 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 85 | fi 86 | } 87 | -------------------------------------------------------------------------------- /library/10-func/testing/test_functions.sh: -------------------------------------------------------------------------------- 1 | # test_file -- Tests a file. 2 | # 3 | # test_file tests test cases in a file. 4 | # 5 | # Usage: 6 | # void imosh::test_file(string file_path) 7 | imosh::test_case() { 8 | if [ "$#" -eq 1 ]; then 9 | IMOSH_TEST_IS_FAILED=0 10 | "test::${1}" 11 | (( ! IMOSH_TEST_IS_FAILED )) || exit 1 12 | else 13 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 14 | fi 15 | } 16 | 17 | imosh::test_file() { 18 | if [ "$#" -eq 1 ]; then 19 | sub::print "${IMOSH_COLOR_GREEN}[==========]" >&2 20 | sub::println "${IMOSH_STYLE_DEFAULT} Running tests for ${1}." >&2 21 | IMOSH_TESTING=0 source "${1}" || exit 1 22 | ( ( declare -F | grep 'test::' ) || true ) >"${IMOSH_TMPDIR}/test_func" 23 | if [ "$(cat "${IMOSH_TMPDIR}/test_func")" == '' ]; then 24 | LOG FATAL "${1} has no test." 25 | fi 26 | exec 3>&2 27 | while read line; do 28 | function="${line##*test::}" 29 | sub::print "${IMOSH_COLOR_GREEN}[ RUN ]" >&2 30 | sub::println "${IMOSH_STYLE_DEFAULT} ${function}" >&2 31 | { 32 | time -p { 33 | imosh::test_case "${function}" 2>&1 & 34 | wait $! 35 | } 36 | } 2>"${IMOSH_TMPDIR}/time" \ 37 | 1>"${IMOSH_TMPDIR}/stdout" & 38 | if wait $!; then 39 | func::file_get_contents time "${IMOSH_TMPDIR}/time" 40 | func::greg_replace time '+([[:space:]])' ' ' 41 | func::trim time 42 | sub::print "${IMOSH_COLOR_GREEN}[ OK ]" >&2 43 | sub::println "${IMOSH_STYLE_DEFAULT} ${function} (${time})" >&2 44 | else 45 | cat "${IMOSH_TMPDIR}/stdout" 46 | func::file_get_contents time "${IMOSH_TMPDIR}/time" 47 | func::greg_replace time '+([[:space:]])' ' ' 48 | func::trim time 49 | sub::print "${IMOSH_COLOR_RED}[ FAILED ]" >&2 50 | sub::println "${IMOSH_STYLE_DEFAULT} ${function} (${time})" >&2 51 | IMOSH_TEST_IS_FAILED=1 52 | fi 53 | done <"${IMOSH_TMPDIR}/test_func" 54 | else 55 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 56 | fi 57 | } 58 | 59 | imosh::test_files() { 60 | local IMOSH_TEST_IS_FAILED=0 61 | if [ "$#" -eq 1 ]; then 62 | imosh::test_file "${1}" 63 | else 64 | local ppid=() 65 | local files=() 66 | local file='' 67 | local index=0 68 | for file in "$@"; do 69 | sub::throttle 4 70 | "${BASH}" "${BASH_SOURCE}" "${file}" \ 71 | >"${IMOSH_TMPDIR}/test_index_${index}.stdout" \ 72 | 2>"${IMOSH_TMPDIR}/test_index_${index}.stderr" & 73 | ppid+=("$!") 74 | files+=("${file}") 75 | (( index += 1 )) 76 | done 77 | index=0 78 | while [ "${index}" -lt "${#ppid[*]}" ]; do 79 | if ! wait "${ppid[${index}]}"; then 80 | IMOSH_TEST_IS_FAILED=1 81 | LOG ERROR "${files[${index}]} failed." 82 | fi 83 | cat "${IMOSH_TMPDIR}/test_index_${index}.stdout" 84 | cat "${IMOSH_TMPDIR}/test_index_${index}.stderr" >&2 85 | sub::println 86 | (( index += 1 )) 87 | done 88 | fi 89 | if (( IMOSH_TEST_IS_FAILED )); then 90 | exit 1 91 | fi 92 | } 93 | -------------------------------------------------------------------------------- /library/10-func/misc/usage.sh: -------------------------------------------------------------------------------- 1 | # usage -- Shows a usage message. 2 | # 3 | # usage shows a usage message based on a header comment. A header comment 4 | # consists of consecutive comment lines. Comment lines starting with "#!" are 5 | # ignored. 6 | # 7 | # Usage: 8 | # void sub::usage(string file) > output 9 | # 10 | # Options: 11 | # - --format=text 12 | # Select one fromat from text/markdown/groff. 13 | # - --title=true 14 | # Treat the first line as title. 15 | # - --markdown_heading='' 16 | # Prepend a string to every heading. 17 | sub::usage() { 18 | local ARGS_format='text' ARGS_title=1 ARGS_markdown_heading='' 19 | eval "${IMOSH_PARSE_ARGUMENTS}" 20 | 21 | if [ "$#" -eq 1 ]; then 22 | local usage='' line='' first_line="${ARGS_title}" is_buffered=0 23 | while IFS= read -r line; do 24 | case "${line}" in 25 | '#!'*) continue;; 26 | '# '*) usage+="${line:2}"$'\n';; 27 | '#'*) usage+="${line:1}"$'\n';; 28 | *) break;; 29 | esac 30 | done < "${1}" 31 | func::trim usage 32 | [ "${usage}" != '' ] || return 33 | if [ "${ARGS_format}" = 'text' ]; then 34 | sub::print "${usage}"$'\n\n' 35 | return 36 | fi 37 | while :; do 38 | (( is_buffered )) || IFS= read -r line || break 39 | is_buffered=0 40 | func::rtrim line 41 | # Show title. 42 | if (( first_line )); then 43 | case "${ARGS_format}" in 44 | 'groff') sub::println ".TH ${line} 1";; 45 | 'markdown') sub::println "${ARGS_markdown_heading} ${line}";; 46 | esac 47 | first_line=0 48 | # Show section title. 49 | elif sub::greg_match '*:' "${line}"; then 50 | local title="${line%:}" 51 | func::strtoupper title 52 | case "${ARGS_format}" in 53 | 'groff') sub::println ".SH ${title}";; 54 | 'markdown') sub::println "${ARGS_markdown_heading}# ${line%:}";; 55 | esac 56 | # Show code. 57 | elif [ "${line:0:4}" = ' ' ]; then 58 | case "${ARGS_format}" in 59 | 'groff') sub::println '.Bd -literal -offset indent';; 60 | 'markdown') sub::println '```sh';; 61 | esac 62 | while [ "${line:0:4}" = ' ' ]; do 63 | case "${ARGS_format}" in 64 | 'groff') sub::println "${line#' '}";; 65 | 'markdown') sub::println "${line#' '}";; 66 | esac 67 | IFS= read -r line || break 68 | done 69 | case "${ARGS_format}" in 70 | 'groff') sub::println '.Ed';; 71 | 'markdown') sub::print $'```\n\n';; 72 | esac 73 | is_buffered=1 74 | # Show an item. 75 | elif sub::greg_match '*( )- *' "${line}"; then 76 | func::ltrim line 77 | CHECK [ "${line:0:2}" = '- ' ] 78 | line="${line:2}" 79 | case "${ARGS_format}" in 80 | 'groff') sub::println $'.TP\n'".B ${line}";; 81 | 'markdown') sub::println "* ${line}";; 82 | esac 83 | local markdown_indent=' * ' 84 | while IFS= read -r line; do 85 | if sub::greg_match '*( )- *' "${line}" || \ 86 | sub::greg_match '*([[:space:]])' "${line}"; then 87 | break 88 | fi 89 | func::ltrim line 90 | case "${ARGS_format}" in 91 | 'groff') sub::println "${line}";; 92 | 'markdown') sub::println "${markdown_indent}${line}" 93 | markdown_indent=' ';; 94 | esac 95 | done 96 | is_buffered=1 97 | else 98 | sub::println "${line}" 99 | fi 100 | done <<<"${usage}" 101 | sub::println 102 | else 103 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 104 | fi 105 | } 106 | -------------------------------------------------------------------------------- /tool/update-readme.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # update-readme -- Updates README.md and documents. 3 | # 4 | # update-readme updates README.md and documents based on comments in source 5 | # files. 6 | 7 | IMOSH_ROOT="$(cd "$(dirname "${BASH_SOURCE}")"/..; pwd)" 8 | source ./imosh || exit 1 9 | DEFINE_string root_directory "${IMOSH_ROOT}" "Repository's root directory." 10 | eval "${IMOSH_INIT}" 11 | 12 | show_introduction() { 13 | local line='' 14 | while IFS= read -r line; do 15 | echo "${line}" 16 | if [ "${line}" = '' ]; then 17 | break 18 | fi 19 | done < "${FLAGS_root_directory}/README.md" 20 | echo 21 | } 22 | 23 | # process_usage -- Reads a file and writes a document page. 24 | # 25 | # process_file reads a script file and writes a document page using the heading 26 | # comment of the script file. Additionally, process_file outputs a document 27 | # link to readme_toc_output with a link to readme_link. This command appends 28 | # texts to files. 29 | # 30 | # Usage: 31 | # void process_usage( 32 | # string usage, string readme_output = '', 33 | # string readme_link = '', string readme_toc_output = '') 34 | process_usage() { 35 | if [ "$#" -eq 3 ]; then 36 | local usage="$1" 37 | local readme_output="$2" 38 | local readme_link="$3" 39 | 40 | func::rtrim usage 41 | local lines=() 42 | func::explode lines $'\n\n' "${usage}" 43 | CHECK [ "${#lines[*]}" -gt 1 ] 44 | local first_line="${lines[0]#'#'}" 45 | local words=() 46 | func::explode words '--' "${first_line}" 47 | local title="${words[0]}" 48 | unset words[0] 49 | if [ "${#words[*]}" -gt 0 ]; then 50 | local description="${words[*]}" 51 | else 52 | local description='' 53 | fi 54 | func::trim title 55 | func::trim description 56 | if [ "${description}" = '' ]; then 57 | sub::println "* [${title}](${readme_link})" 58 | lines[0]="# ${title}" 59 | else 60 | sub::println "* [${title}](${readme_link}) -- ${description}" 61 | lines[0]="# ${title}"$'\n'"${title} -- ${description}" 62 | fi 63 | func::implode usage $'\n\n' lines 64 | if [ "${readme_output}" != '' ]; then 65 | sub::println "${usage}" >> "${readme_output}" 66 | fi 67 | elif [ "$#" -eq 2 ]; then 68 | process_usage "$@" '' 69 | elif [ "$#" -eq 1 ]; then 70 | process_usage "$@" '' 71 | else 72 | LOG FATAL "Wrong number of arguments: $#" 73 | fi 74 | } 75 | 76 | process_file() { 77 | if [ "$#" -eq 3 ]; then 78 | local input_file="$1" 79 | local output_file="$2" 80 | local readme_link="$3" 81 | 82 | local usage="$( 83 | sub::usage --format=markdown --title \ 84 | --markdown_heading='#' "${input_file}")" 85 | process_usage "${usage}" "${output_file}" "${readme_link}" 86 | else 87 | LOG FATAL "Wrong number of arguments: $#" 88 | fi 89 | } 90 | 91 | show_readme() { 92 | show_introduction 93 | cd "${FLAGS_root_directory}" 94 | rm -r 'doc' 95 | mkdir 'doc' 96 | echo '# Functions' 97 | pushd 'library' > '/dev/null' 98 | for directory in *-func; do 99 | if [ ! -d "${directory}" ]; then continue; fi 100 | pushd "${directory}" > '/dev/null' 101 | for subdirectory in *; do 102 | if [ ! -d "${subdirectory}" ]; then continue; fi 103 | if [ -f "${subdirectory}/README.md" ]; then 104 | sub::print '#' 105 | cat "${subdirectory}/README.md" 106 | local title="$( 107 | head -n 1 "${subdirectory}/README.md" | sed -e 's/#//')" 108 | func::trim title 109 | fi 110 | sub::println 111 | pushd "${subdirectory}" > '/dev/null' 112 | mkdir -p "${FLAGS_root_directory}/doc/${subdirectory}" 113 | for file in *.sh; do 114 | process_file "${file}" \ 115 | "${FLAGS_root_directory}/doc/${subdirectory}/${file}.md" \ 116 | "doc/${subdirectory}/${file}.md" 117 | done 118 | sub::println 119 | popd > '/dev/null' 120 | done 121 | popd > '/dev/null' 122 | done 123 | popd > '/dev/null' 124 | } 125 | 126 | show_readme >"${TMPDIR}/README.md" 127 | cat "${TMPDIR}/README.md" > "${FLAGS_root_directory}/README.md" 128 | -------------------------------------------------------------------------------- /library/10-imosh/arguments.sh: -------------------------------------------------------------------------------- 1 | # Parses arguments without getopt. 2 | imosh::internal::parse_args() { 3 | local class_name="$1"; shift 4 | 5 | local upper_class_name="${class_name}" 6 | func::strtoupper upper_class_name 7 | local arg arg_name arg_value 8 | IMOSH_ARGV=() 9 | IMOSH_ARGS=() 10 | while [ "$#" != '0' ]; do 11 | if sub::isset IMOSH_PREDICATE && 12 | [ "${upper_class_name}" = 'FLAG' ] && 13 | [ "${#IMOSH_ARGV[*]}" -eq "${IMOSH_PREDICATE}" ]; then 14 | IMOSH_ARGV+=("$@") 15 | break 16 | fi 17 | local arg="$1" 18 | shift 19 | if [ "${arg:0:1}" != '-' ]; then 20 | IMOSH_ARGV+=("${arg}") 21 | continue 22 | fi 23 | if [[ "${arg}" =~ ^-[0-9] ]]; then 24 | IMOSH_ARGV+=("${arg}") 25 | continue 26 | fi 27 | if [ "${arg}" = '--' ]; then 28 | IMOSH_ARGV+=("$@") 29 | break 30 | fi 31 | case "${arg}" in 32 | --*) arg="${arg:2}";; 33 | -*) arg="${arg:1}";; 34 | esac 35 | arg_name="${arg%%=*}" 36 | if [[ ! "${arg_name}" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then 37 | LOG FATAL "${class_name} name is bad: ${arg_name}" 38 | fi 39 | arg_value="${arg:${#arg_name}}" 40 | # If the argument does not have "=", it should be a boolean flag or it 41 | # should consume its next argument. 42 | if [ "${arg_value:0:1}" = '=' ]; then 43 | arg_value="${arg_value:1}" 44 | # Preprocess for boolean flags (e.g. --name, --noname) and separated flags 45 | # (e.g. --name value). 46 | else 47 | # If it is a negative boolean flag (--noname). 48 | if [ "${arg_name:0:2}" = 'no' ] && 49 | sub::isset "${upper_class_name}S_${arg_name:2}" && 50 | ( [ "${class_name}" != 'flag' ] || 51 | [ "$(imosh::internal::flag_type "${arg_name:2}")" = 'BOOL' ] ); then 52 | arg_name="${arg_name:2}" 53 | arg_value=0 54 | else 55 | if ! sub::isset "${upper_class_name}S_${arg_name}"; then 56 | LOG FATAL "no such bool ${class_name} is defined:" \ 57 | "(${upper_class_name}S_)${arg_name}" 58 | fi 59 | if [ "${class_name}" != 'flag' ] || 60 | [ "$(imosh::internal::flag_type "${arg_name}")" = 'BOOL' ]; then 61 | arg_value=1 62 | else 63 | if [ "$#" -eq 0 ]; then 64 | LOG FATAL "the ${arg_name} flag requires a value" 65 | fi 66 | arg_value="${1}" 67 | shift 68 | fi 69 | fi 70 | fi 71 | if [ "${class_name}" = 'flag' ]; then 72 | local type="$(imosh::internal::flag_type "${arg_name}")" 73 | local single_type="${type}" 74 | if [ "${type:0:5}" = 'MULTI' ]; then 75 | single_type="${type:5}" 76 | fi 77 | if [ "${type}" = 'LIST' ]; then 78 | # TODO(imos): Support delimiter. 79 | func::explode arg_value ',' "${arg_value}" 80 | fi 81 | if [ "${single_type}" = 'ENUM' ]; then 82 | local __imosh_enum_values=() 83 | func::array_values \ 84 | __imosh_enum_values "__IMOSH_FLAGS_ENUM_VALUES_${arg_name}" 85 | fi 86 | CHECK --message="FLAGS_${arg_name} is invalid: ${arg_value}" \ 87 | func::cast arg_value "${single_type}" 88 | local is_default=0 89 | func::strcpy 'is_default' "__IMOSH_FLAGS_IS_DEFAULT_${arg_name}" 90 | func::let "__IMOSH_FLAGS_IS_DEFAULT_${arg_name}" '0' 91 | if [ "${type}" = 'LIST' ]; then 92 | # Set values here. FLAGS_* are global variables, and this does not 93 | # cause scope issues. 94 | func::array_values "FLAGS_${arg_name}" arg_value 95 | continue 96 | fi 97 | if [ "${type:0:5}" = 'MULTI' ]; then 98 | if (( is_default )); then 99 | eval "FLAGS_${arg_name}=(\"\${arg_value}\")" 100 | else 101 | eval "FLAGS_${arg_name}+=(\"\${arg_value}\")" 102 | fi 103 | continue 104 | fi 105 | fi 106 | if ! sub::isset "${upper_class_name}S_${arg_name}"; then 107 | LOG FATAL "No such ${class_name} is defined: ${arg_name}" 108 | fi 109 | func::str_replace arg_value "'" "'\\''" 110 | IMOSH_ARGS+=("${upper_class_name}S_${arg_name}='${arg_value}'") 111 | done 112 | } 113 | -------------------------------------------------------------------------------- /library/10-func/array/array_map.sh: -------------------------------------------------------------------------------- 1 | # array_map -- Applies a callback to elements. 2 | # 3 | # array_map applies a callback to every element. stream::array_map applies a 4 | # callback to every line's elements. 5 | # stream::array_map supports the following functions: array, function, inplace 6 | # and command. 7 | # 8 | # Usage: 9 | # // 1. Function form. 10 | # void func::array_map(string[]* variable, string type, 11 | # string callback [, string arguments...]) 12 | # // 2. Stream form. 13 | # void stream::array_map( 14 | # string type, string callback [, string arguments...]) 15 | # < input > output 16 | # 17 | # Type: 18 | # - ARRAY 19 | # Reads every line as an array, and applies a callback to every line. 20 | # - FUNCTION 21 | # Reads every line as a string and applies a callback. A callback should be 22 | # a function format like: function(string* output, input). 23 | # - INPLACE 24 | # Reads every line as a string and applies a callback. A callback should be 25 | # an inplace format like: function(string* input_and_output). 26 | # - COMMAND 27 | # Reads every line as a string and applies a callback. A callback should be 28 | # a command format like: function(string input) > output. 29 | # 30 | # Examples: 31 | # input=('abc' 'DeF' '012') 32 | # func::array_map input INPLACE func::strtoupper 33 | # echo "${input[@]}" # => ABC DEF 012 34 | # 35 | # sub::print $'def,abc,ghi\n1,3,2,5,4' | \ 36 | # IFS=',' stream::array_map ARRAY func::sort 37 | # # => abc,def,ghi\n1,2,3,4,5 38 | # 39 | # sub::print $'abcbd\nbcdbcb' | \ 40 | # stream::array_map INPLACE func::str_replace 'bc' 'BC' 41 | # # => aBCbd\nBCdBCb 42 | func::array_map() { 43 | if [ "$#" -ge 3 ]; then 44 | if sub::array_is_empty "${1}"; then return; fi 45 | local __array_map_variable="${1}"; shift 46 | local __array_map_type="${1}"; shift 47 | local __array_map_callback="${1}"; shift 48 | CHECK sub::function_exists "${__array_map_callback}" 49 | 50 | local __array_map_keys=() 51 | local __array_map_key='' 52 | func::array_keys __array_map_keys "${__array_map_variable}" 53 | case "${__array_map_type}" in 54 | 'ARRAY') 55 | local __array_map_value='' 56 | local __array_map_array_value=() 57 | for __array_map_key in "${__array_map_keys[@]}"; do 58 | func::strcpy __array_map_value \ 59 | "${__array_map_variable}[${__array_map_key}]" 60 | func::array __array_map_array_value "${__array_map_value}" 61 | "${__array_map_callback}" __array_map_array_value "$@" 62 | func::let "${__array_map_variable}[${__array_map_key}]" \ 63 | "${__array_map_array_value[*]}" 64 | done 65 | ;; 66 | 'FUNCTION') 67 | local __array_map_value='' 68 | for __array_map_key in "${__array_map_keys[@]}"; do 69 | func::strcpy __array_map_value \ 70 | "${__array_map_variable}[${__array_map_key}]" 71 | "${__array_map_callback}" \ 72 | "${__array_map_variable}[${__array_map_key}]" \ 73 | "${__array_map_value}" "$@" 74 | done 75 | ;; 76 | 'INPLACE') 77 | local __array_map_value='' 78 | for __array_map_key in "${__array_map_keys[@]}"; do 79 | "${__array_map_callback}" \ 80 | "${__array_map_variable}[${__array_map_key}]" "$@" 81 | done 82 | ;; 83 | 'COMMAND') 84 | local __array_map_value='' 85 | local __array_map_tmpfile='' 86 | func::tmpfile __array_map_tmpfile 87 | for __array_map_key in "${__array_map_keys[@]}"; do 88 | func::strcpy __array_map_value \ 89 | "${__array_map_variable}[${__array_map_key}]" 90 | "${__array_map_callback}" \ 91 | "${__array_map_value}" "$@" > "${__array_map_tmpfile}" 92 | func::file_get_contents \ 93 | "${__array_map_variable}[${__array_map_key}]" \ 94 | "${__array_map_tmpfile}" 95 | done 96 | ;; 97 | *) 98 | LOG ERROR "Unknown array_map type: ${__array_map_type}" 99 | return 2 100 | ;; 101 | esac 102 | else 103 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 104 | fi 105 | } 106 | 107 | stream::array_map() { 108 | if [ "$#" -ge 2 ]; then 109 | local __array_map_type="${1}" 110 | local LINE='' NEWLINE='' 111 | while func::readline; do 112 | local __array_map_line=("${LINE}") 113 | func::array_map __array_map_line "$@" 114 | if [ "${__array_map_type}" = 'COMMAND' ]; then 115 | sub::print "${__array_map_line[0]}" 116 | else 117 | sub::print "${__array_map_line[0]}${NEWLINE}" 118 | fi 119 | done 120 | else 121 | eval "${IMOSH_WRONG_NUMBER_OF_ARGUMENTS}" 122 | fi 123 | } 124 | --------------------------------------------------------------------------------