/// This handles the common case of setting the entire var to a set of values. static int set_var_array(const wchar_t *cmd, set_cmd_opts_t &opts, const wchar_t *varname, wcstring_list_t &new_values, int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) { UNUSED(cmd); UNUSED(parser); UNUSED(streams); if (opts.prepend || opts.append) { if (opts.prepend) { for (int i = 0; i < argc; i++) new_values.push_back(argv[i]); } auto var_str = parser.vars().get(varname, ENV_DEFAULT); wcstring_list_t var_array; if (var_str) var_str->to_list(var_array); new_values.insert(new_values.end(), var_array.begin(), var_array.end()); if (opts.append) { for (int i = 0; i < argc; i++) new_values.push_back(argv[i]); } } else { for (int i = 0; i < argc; i++) new_values.push_back(argv[i]); } return STATUS_CMD_OK; }
// IEEE 1003.1 says nothing about what it means for two strings to be "algebraically equal". For // example, should we interpret 0x10 as 0, 10, or 16? Here we use only base 10 and use wcstoll, // which allows for leading + and -, and whitespace. This is consistent, albeit a bit more lenient // since we allow trailing whitespace, with other implementations such as bash. static bool parse_number(const wcstring &arg, long long *out, wcstring_list_t &errors) { *out = fish_wcstoll(arg.c_str()); if (errno) { errors.push_back(format_string(_(L"invalid integer '%ls'"), arg.c_str())); } return !errno; }
static bool binary_primary_evaluate(test_expressions::token_t token, const wcstring &left, const wcstring &right, wcstring_list_t &errors) { using namespace test_expressions; long long left_num, right_num; switch (token) { case test_string_equal: return left == right; case test_string_not_equal: return left != right; case test_number_equal: return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num == right_num; case test_number_not_equal: return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num != right_num; case test_number_greater: return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num > right_num; case test_number_greater_equal: return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num >= right_num; case test_number_lesser: return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num < right_num; case test_number_lesser_equal: return parse_number(left, &left_num) && parse_number(right, &right_num) && left_num <= right_num; default: errors.push_back(format_string(L"Unknown token type in %s", __func__)); return false; } }
bool unary_operator::evaluate(wcstring_list_t &errors) { if (token == test_bang) { assert(subject.get()); return !subject->evaluate(errors); } errors.push_back(format_string(L"Unknown token type in %s", __func__)); return false; }
bool combining_expression::evaluate(wcstring_list_t &errors) { switch (token) { case test_combine_and: case test_combine_or: { /* One-element case */ if (subjects.size() == 1) return subjects.at(0)->evaluate(errors); /* Evaluate our lists, remembering that AND has higher precedence than OR. We can visualize this as a sequence of OR expressions of AND expressions. */ assert(combiners.size() + 1 == subjects.size()); assert(! subjects.empty()); size_t idx = 0, max = subjects.size(); bool or_result = false; while (idx < max) { if (or_result) { /* Short circuit */ break; } /* Evaluate a stream of AND starting at given subject index. It may only have one element. */ bool and_result = true; for (; idx < max; idx++) { /* Evaluate it, short-circuiting */ and_result = and_result && subjects.at(idx)->evaluate(errors); /* If the combiner at this index (which corresponding to how we combine with the next subject) is not AND, then exit the loop */ if (idx + 1 < max && combiners.at(idx) != test_combine_and) { idx++; break; } } /* OR it in */ or_result = or_result || and_result; } return or_result; } default: errors.push_back(format_string(L"Unknown token type in %s", __func__)); return BUILTIN_TEST_FAIL; } }
bool unary_operator::evaluate(wcstring_list_t &errors) { switch (token) { case test_bang: assert(subject.get()); return ! subject->evaluate(errors); default: errors.push_back(format_string(L"Unknown token type in %s", __func__)); return false; } }
/** Put exported or unexported variables in a string list */ void env_universal_common_get_names(wcstring_list_t &lst, int show_exported, int show_unexported) { env_var_table_t::const_iterator iter; for (iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter) { const wcstring& key = iter->first; const var_uni_entry_t *e = iter->second; if ((e->exportv && show_exported) || (!e->exportv && show_unexported)) { lst.push_back(key); } } }
/** Read lines of input from the specified file, unescape them and insert them into the specified list. */ static void read_array( FILE* file, wcstring_list_t &comp ) { std::vector<char> buffer; int c; wchar_t *wcs; while( !feof( file ) ) { buffer.clear(); while( 1 ) { c = getc( file ); if( c == EOF ) { break; } if( c == '\n' ) { break; } buffer.push_back(static_cast<char>(c)); } if( ! buffer.empty() ) { buffer.push_back(0); wcs = str2wcs( &buffer.at(0) ); if( wcs ) { wcstring tmp = wcs; if (unescape_string(tmp, 0)) { comp.push_back(tmp); } free( wcs ); } } } }
bool combining_expression::evaluate(wcstring_list_t &errors) { if (token == test_combine_and || token == test_combine_or) { assert(!subjects.empty()); //!OCLINT(multiple unary operator) assert(combiners.size() + 1 == subjects.size()); // One-element case. if (subjects.size() == 1) return subjects.at(0)->evaluate(errors); // Evaluate our lists, remembering that AND has higher precedence than OR. We can // visualize this as a sequence of OR expressions of AND expressions. size_t idx = 0, max = subjects.size(); bool or_result = false; while (idx < max) { if (or_result) { // short circuit break; } // Evaluate a stream of AND starting at given subject index. It may only have one // element. bool and_result = true; for (; idx < max; idx++) { // Evaluate it, short-circuiting. and_result = and_result && subjects.at(idx)->evaluate(errors); // If the combiner at this index (which corresponding to how we combine with the // next subject) is not AND, then exit the loop. if (idx + 1 < max && combiners.at(idx) != test_combine_and) { idx++; break; } } // OR it in. or_result = or_result || and_result; } return or_result; } errors.push_back(format_string(L"Unknown token type in %s", __func__)); return BUILTIN_TEST_FAIL; }
static bool unary_primary_evaluate(test_expressions::token_t token, const wcstring &arg, wcstring_list_t &errors) { using namespace test_expressions; struct stat buf; long long num; switch (token) { case test_filetype_b: { // "-b", for block special files return !wstat(arg, &buf) && S_ISBLK(buf.st_mode); } case test_filetype_c: { // "-c", for character special files return !wstat(arg, &buf) && S_ISCHR(buf.st_mode); } case test_filetype_d: { // "-d", for directories return !wstat(arg, &buf) && S_ISDIR(buf.st_mode); } case test_filetype_e: { // "-e", for files that exist return !wstat(arg, &buf); } case test_filetype_f: { // "-f", for for regular files return !wstat(arg, &buf) && S_ISREG(buf.st_mode); } case test_filetype_G: { // "-G", for check effective group id return !wstat(arg, &buf) && getegid() == buf.st_gid; } case test_filetype_g: { // "-g", for set-group-id return !wstat(arg, &buf) && (S_ISGID & buf.st_mode); } case test_filetype_h: // "-h", for symbolic links case test_filetype_L: { // "-L", same as -h return !lwstat(arg, &buf) && S_ISLNK(buf.st_mode); } case test_filetype_O: { // "-O", for check effective user id return !wstat(arg, &buf) && geteuid() == buf.st_uid; } case test_filetype_p: { // "-p", for FIFO return !wstat(arg, &buf) && S_ISFIFO(buf.st_mode); } case test_filetype_S: { // "-S", socket return !wstat(arg, &buf) && S_ISSOCK(buf.st_mode); } case test_filesize_s: { // "-s", size greater than zero return !wstat(arg, &buf) && buf.st_size > 0; } case test_filedesc_t: { // "-t", whether the fd is associated with a terminal return parse_number(arg, &num, errors) && num == (int)num && isatty((int)num); } case test_fileperm_r: { // "-r", read permission return !waccess(arg, R_OK); } case test_fileperm_u: { // "-u", whether file is setuid return !wstat(arg, &buf) && (S_ISUID & buf.st_mode); } case test_fileperm_w: { // "-w", whether file write permission is allowed return !waccess(arg, W_OK); } case test_fileperm_x: { // "-x", whether file execute/search is allowed return !waccess(arg, X_OK); } case test_string_n: { // "-n", non-empty string return !arg.empty(); } case test_string_z: { // "-z", true if length of string is 0 return arg.empty(); } default: { errors.push_back(format_string(L"Unknown token type in %s", __func__)); return false; } } }