diff --git a/builtin.cpp b/builtin.cpp index 8b8b8d5c2..930d10be2 100644 --- a/builtin.cpp +++ b/builtin.cpp @@ -1728,7 +1728,7 @@ static int builtin_pwd(parser_t &parser, wchar_t **argv) } } -/* This is nearly identical to builtin_function, and is intended to be the successor (with no block manipulation, no function/end split) */ +/** Adds a function to the function set. It calls into function.cpp to perform any heavy lifting. */ int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstring &contents, wcstring *out_err) { assert(out_err != NULL); @@ -2034,338 +2034,6 @@ int define_function(parser_t &parser, const wcstring_list_t &c_args, const wcstr return res; } -/** - The function builtin, used for providing subroutines. - It calls various functions from function.c to perform any heavy lifting. -*/ -static int builtin_function(parser_t &parser, wchar_t **argv) -{ - /* Hack hack hack - with the new parser, this is only invoked for help */ - if (parser_use_ast()) - { - builtin_print_help(parser, argv[0], stdout_buffer); - return STATUS_BUILTIN_OK; - } - - int argc = builtin_count_args(argv); - int res=STATUS_BUILTIN_OK; - wchar_t *desc=0; - std::vector events; - std::auto_ptr named_arguments(NULL); - - wchar_t *name = 0; - bool shadows = true; - - woptind=0; - - function_def_block_t * const fdb = new function_def_block_t(); - parser.push_block(fdb); - - const struct woption long_options[] = - { - { L"description", required_argument, 0, 'd' }, - { L"on-signal", required_argument, 0, 's' }, - { L"on-job-exit", required_argument, 0, 'j' }, - { L"on-process-exit", required_argument, 0, 'p' }, - { L"on-variable", required_argument, 0, 'v' }, - { L"on-event", required_argument, 0, 'e' }, - { L"help", no_argument, 0, 'h' }, - { L"argument-names", no_argument, 0, 'a' }, - { L"no-scope-shadowing", no_argument, 0, 'S' }, - { 0, 0, 0, 0 } - }; - - while (1 && (!res)) - { - int opt_index = 0; - - int opt = wgetopt_long(argc, - argv, - L"d:s:j:p:v:e:haS", - long_options, - &opt_index); - if (opt == -1) - break; - - switch (opt) - { - case 0: - if (long_options[opt_index].flag != 0) - break; - append_format(stderr_buffer, - BUILTIN_ERR_UNKNOWN, - argv[0], - long_options[opt_index].name); - - res = 1; - break; - - case 'd': - desc=woptarg; - break; - - case 's': - { - int sig = wcs2sig(woptarg); - - if (sig < 0) - { - append_format(stderr_buffer, - _(L"%ls: Unknown signal '%ls'\n"), - argv[0], - woptarg); - res=1; - break; - } - events.push_back(event_t::signal_event(sig)); - break; - } - - case 'v': - { - if (wcsvarname(woptarg)) - { - append_format(stderr_buffer, - _(L"%ls: Invalid variable name '%ls'\n"), - argv[0], - woptarg); - res=STATUS_BUILTIN_ERROR; - break; - } - - events.push_back(event_t::variable_event(woptarg)); - break; - } - - - case 'e': - { - events.push_back(event_t::generic_event(woptarg)); - break; - } - - case 'j': - case 'p': - { - pid_t pid; - wchar_t *end; - event_t e(EVENT_ANY); - - if ((opt == 'j') && - (wcscasecmp(woptarg, L"caller") == 0)) - { - int job_id = -1; - - if (is_subshell) - { - size_t block_idx = 0; - - /* Find the outermost substitution block */ - for (block_idx = 0; ; block_idx++) - { - const block_t *b = parser.block_at_index(block_idx); - if (b == NULL || b->type() == SUBST) - break; - } - - /* Go one step beyond that, to get to the caller */ - const block_t *caller_block = parser.block_at_index(block_idx + 1); - if (caller_block != NULL && caller_block->job != NULL) - { - job_id = caller_block->job->job_id; - } - } - - if (job_id == -1) - { - append_format(stderr_buffer, - _(L"%ls: Cannot find calling job for event handler\n"), - argv[0]); - res=1; - } - else - { - e.type = EVENT_JOB_ID; - e.param1.job_id = job_id; - } - - } - else - { - errno = 0; - pid = fish_wcstoi(woptarg, &end, 10); - if (errno || !end || *end) - { - append_format(stderr_buffer, - _(L"%ls: Invalid process id %ls\n"), - argv[0], - woptarg); - res=1; - break; - } - - - e.type = EVENT_EXIT; - e.param1.pid = (opt=='j'?-1:1)*abs(pid); - } - if (res) - { - /* nothing */ - } - else - { - events.push_back(e); - } - break; - } - - case 'a': - if (named_arguments.get() == NULL) - named_arguments.reset(new wcstring_list_t); - break; - - case 'S': - shadows = 0; - break; - - case 'h': - parser.pop_block(); - parser.push_block(new fake_block_t()); - builtin_print_help(parser, argv[0], stdout_buffer); - return STATUS_BUILTIN_OK; - - case '?': - builtin_unknown_option(parser, argv[0], argv[woptind-1]); - res = 1; - break; - - } - - } - - if (!res) - { - - if (argc == woptind) - { - append_format(stderr_buffer, - _(L"%ls: Expected function name\n"), - argv[0]); - res=1; - } - else if (wcsfuncname(argv[woptind])) - { - append_format(stderr_buffer, - _(L"%ls: Illegal function name '%ls'\n"), - argv[0], - argv[woptind]); - - res=1; - } - else if (parser_keywords_is_reserved(argv[woptind])) - { - - append_format(stderr_buffer, - _(L"%ls: The name '%ls' is reserved,\nand can not be used as a function name\n"), - argv[0], - argv[woptind]); - - res=1; - } - else if (! wcslen(argv[woptind])) - { - append_format(stderr_buffer, _(L"%ls: No function name given\n"), argv[0]); - } - else - { - - name = argv[woptind++]; - - if (named_arguments.get()) - { - while (woptind < argc) - { - if (wcsvarname(argv[woptind])) - { - append_format(stderr_buffer, - _(L"%ls: Invalid variable name '%ls'\n"), - argv[0], - argv[woptind]); - res = STATUS_BUILTIN_ERROR; - break; - } - - named_arguments->push_back(argv[woptind++]); - } - } - else if (woptind != argc) - { - append_format(stderr_buffer, - _(L"%ls: Expected one argument, got %d\n"), - argv[0], - argc); - res=1; - - } - } - } - - if (res) - { - size_t i; - size_t chars=0; - - builtin_print_help(parser, argv[0], stderr_buffer); - const wchar_t *cfa = _(L"Current functions are: "); - stderr_buffer.append(cfa); - chars += wcslen(cfa); - - wcstring_list_t names = function_get_names(0); - sort(names.begin(), names.end()); - - for (i=0; i (size_t)common_get_width()) - { - chars = 0; - stderr_buffer.push_back(L'\n'); - } - - stderr_buffer.append(nxt); - stderr_buffer.append(L" "); - } - stderr_buffer.push_back(L'\n'); - - parser.pop_block(); - parser.push_block(new fake_block_t()); - } - else - { - function_data_t &d = fdb->function_data; - - d.name = name; - if (desc) - d.description = desc; - d.events.swap(events); - d.shadows = shadows; - if (named_arguments.get()) - d.named_arguments.swap(*named_arguments); - - for (size_t i=0; itok_pos = parser.get_pos(); - parser.current_block()->skip = 1; - - return STATUS_BUILTIN_OK; -} - /** The random builtin. For generating random numbers. */ @@ -3713,195 +3381,6 @@ static int builtin_for(parser_t &parser, wchar_t **argv) return res; } -/** - The begin builtin. Creates a new block. -*/ -static int builtin_begin(parser_t &parser, wchar_t **argv) -{ - parser.push_block(new scope_block_t(BEGIN)); - parser.current_block()->tok_pos = parser.get_pos(); - return proc_get_last_status(); -} - - -/** - Builtin for ending a block of code, such as a for-loop or an if statement. - - The end command is whare a lot of the block-level magic happens. -*/ -static int builtin_end(parser_t &parser, wchar_t **argv) -{ - if (! parser.block_at_index(1)) - { - append_format(stderr_buffer, - _(L"%ls: Not inside of block\n"), - argv[0]); - - builtin_print_help(parser, argv[0], stderr_buffer); - return STATUS_BUILTIN_ERROR; - } - else - { - /** - By default, 'end' kills the current block scope. But if we - are rewinding a loop, this should be set to false, so that - variables in the current loop scope won't die between laps. - */ - bool kill_block = true; - - block_t * const current_block = parser.current_block(); - switch (current_block->type()) - { - case WHILE: - { - /* - If this is a while loop, we rewind the loop unless - it's the last lap, in which case we continue. - */ - if (!(current_block->skip && (current_block->loop_status != LOOP_CONTINUE))) - { - current_block->loop_status = LOOP_NORMAL; - current_block->skip = 0; - kill_block = false; - parser.set_pos(current_block->tok_pos); - while_block_t *blk = static_cast(current_block); - blk->status = WHILE_TEST_AGAIN; - } - - break; - } - - case IF: - /* Always return success (#1061) */ - proc_set_last_status(0); - break; - - case SUBST: - case BEGIN: - case SWITCH: - case FAKE: - /* - Nothing special happens at the end of these commands. The scope just ends. - */ - - break; - - case FOR: - { - /* - set loop variable to next element, and rewind to the beginning of the block. - */ - for_block_t *fb = static_cast(current_block); - wcstring_list_t &for_vars = fb->sequence; - if (current_block->loop_status == LOOP_BREAK) - { - for_vars.clear(); - } - - if (! for_vars.empty()) - { - const wcstring val = for_vars.back(); - for_vars.pop_back(); - const wcstring &for_variable = fb->variable; - env_set(for_variable, val.c_str(), ENV_LOCAL); - current_block->loop_status = LOOP_NORMAL; - current_block->skip = 0; - - kill_block = false; - parser.set_pos(current_block->tok_pos); - } - break; - } - - case FUNCTION_DEF: - { - function_def_block_t *fdb = static_cast(current_block); - function_data_t &d = fdb->function_data; - - if (d.name.empty()) - { - /* Disallow empty function names */ - append_format(stderr_buffer, _(L"%ls: No function name given\n"), argv[0]); - - /* Return an error via a crummy way. Don't just return here, since we need to pop the block. */ - proc_set_last_status(STATUS_BUILTIN_ERROR); - } - else - { - /** - Copy the text from the beginning of the function - until the end command and use as the new definition - for the specified function - */ - - wchar_t *def = wcsndup(parser.get_buffer()+current_block->tok_pos, - parser.get_job_pos()-current_block->tok_pos); - d.definition = def; - - function_add(d, parser); - free(def); - } - } - break; - - default: - assert(false); //should never get here - break; - - } - if (kill_block) - { - parser.pop_block(); - } - - /* - If everything goes ok, return status of last command to execute. - */ - return proc_get_last_status(); - } -} - -/** - Builtin for executing commands if an if statement is false -*/ -static int builtin_else(parser_t &parser, wchar_t **argv) -{ - bool block_ok = false; - if_block_t *if_block = NULL; - if (parser.current_block() != NULL && parser.current_block()->type() == IF) - { - if_block = static_cast(parser.current_block()); - /* Ensure that we're past IF but not up to an ELSE */ - if (if_block->if_expr_evaluated && ! if_block->else_evaluated) - { - block_ok = true; - } - } - - if (! block_ok) - { - append_format(stderr_buffer, - _(L"%ls: Not inside of 'if' block\n"), - argv[0]); - builtin_print_help(parser, argv[0], stderr_buffer); - return STATUS_BUILTIN_ERROR; - } - else - { - /* Run the else block if the IF expression was false and so were all the ELSEIF expressions (if any) */ - bool run_else = ! if_block->any_branch_taken; - if_block->skip = ! run_else; - if_block->else_evaluated = true; - env_pop(); - env_push(false); - } - - /* - If everything goes ok, return status of last command to execute. - */ - return proc_get_last_status(); -} - /** This function handles both the 'continue' and the 'break' builtins that are used for loop control. @@ -4037,90 +3516,6 @@ static int builtin_return(parser_t &parser, wchar_t **argv) return status; } -/** - Builtin for executing one of several blocks of commands depending - on the value of an argument. -*/ -static int builtin_switch(parser_t &parser, wchar_t **argv) -{ - int res=STATUS_BUILTIN_OK; - int argc = builtin_count_args(argv); - - /* Hackish - if we have no arguments other than the command, we are a "naked invocation" and we just print help */ - if (argc == 1) - { - builtin_print_help(parser, argv[0], stdout_buffer); - return STATUS_BUILTIN_ERROR; - } - - if (argc != 2) - { - append_format(stderr_buffer, - _(L"%ls: Expected exactly one argument, got %d\n"), - argv[0], - argc-1); - - builtin_print_help(parser, argv[0], stderr_buffer); - res=1; - parser.push_block(new fake_block_t()); - } - else - { - parser.push_block(new switch_block_t(argv[1])); - parser.current_block()->skip=1; - res = proc_get_last_status(); - } - - return res; -} - -/** - Builtin used together with the switch builtin for conditional - execution -*/ -static int builtin_case(parser_t &parser, wchar_t **argv) -{ - int argc = builtin_count_args(argv); - int i; - wchar_t *unescaped=0; - - if (parser.current_block()->type() != SWITCH) - { - append_format(stderr_buffer, - _(L"%ls: 'case' command while not in switch block\n"), - argv[0]); - builtin_print_help(parser, argv[0], stderr_buffer); - return STATUS_BUILTIN_ERROR; - } - - parser.current_block()->skip = 1; - switch_block_t *sb = static_cast(parser.current_block()); - if (sb->switch_taken) - { - return proc_get_last_status(); - } - - const wcstring &switch_value = sb->switch_value; - for (i=1; iskip = 0; - sb->switch_taken = true; - break; - } - } - - return proc_get_last_status(); -} - - /** History of commands executed by user */ @@ -4317,14 +3712,14 @@ static const builtin_data_t builtin_datas[]= { L"[", &builtin_test, N_(L"Test a condition") }, { L"__fish_parse", &builtin_parse, N_(L"Try out the new parser") }, { L"and", &builtin_generic, N_(L"Execute command if previous command suceeded") }, - { L"begin", &builtin_begin, N_(L"Create a block of code") }, + { L"begin", &builtin_generic, N_(L"Create a block of code") }, { L"bg", &builtin_bg, N_(L"Send job to background") }, { L"bind", &builtin_bind, N_(L"Handle fish key bindings") }, { L"block", &builtin_block, N_(L"Temporarily block delivery of events") }, { L"break", &builtin_break_continue, N_(L"Stop the innermost loop") }, { L"breakpoint", &builtin_breakpoint, N_(L"Temporarily halt execution of a script and launch an interactive debug prompt") }, { L"builtin", &builtin_builtin, N_(L"Run a builtin command instead of a function") }, - { L"case", &builtin_case, N_(L"Conditionally execute a block of commands") }, + { L"case", &builtin_generic, N_(L"Conditionally execute a block of commands") }, { L"cd", &builtin_cd, N_(L"Change working directory") }, { L"command", &builtin_generic, N_(L"Run a program instead of a function or builtin") }, { L"commandline", &builtin_commandline, N_(L"Set or get the commandline") }, @@ -4333,14 +3728,14 @@ static const builtin_data_t builtin_datas[]= { L"continue", &builtin_break_continue, N_(L"Skip the rest of the current lap of the innermost loop") }, { L"count", &builtin_count, N_(L"Count the number of arguments") }, { L"echo", &builtin_echo, N_(L"Print arguments") }, - { L"else", &builtin_else, N_(L"Evaluate block if condition is false") }, + { L"else", &builtin_generic, N_(L"Evaluate block if condition is false") }, { L"emit", &builtin_emit, N_(L"Emit an event") }, - { L"end", &builtin_end, N_(L"End a block of commands") }, + { L"end", &builtin_generic, N_(L"End a block of commands") }, { L"exec", &builtin_generic, N_(L"Run command in current process") }, { L"exit", &builtin_exit, N_(L"Exit the shell") }, { L"fg", &builtin_fg, N_(L"Send job to foreground") }, { L"for", &builtin_for, N_(L"Perform a set of commands multiple times") }, - { L"function", &builtin_function, N_(L"Define a new function") }, + { L"function", &builtin_generic, N_(L"Define a new function") }, { L"functions", &builtin_functions, N_(L"List or remove functions") }, { L"history", &builtin_history, N_(L"History of commands executed by user") }, { L"if", &builtin_generic, N_(L"Evaluate block if condition is true") }, @@ -4356,7 +3751,7 @@ static const builtin_data_t builtin_datas[]= { L"set_color", &builtin_set_color, N_(L"Set the terminal color") }, { L"source", &builtin_source, N_(L"Evaluate contents of file") }, { L"status", &builtin_status, N_(L"Return status information about fish") }, - { L"switch", &builtin_switch, N_(L"Conditionally execute a block of commands") }, + { L"switch", &builtin_generic, N_(L"Conditionally execute a block of commands") }, { L"test", &builtin_test, N_(L"Test a condition") }, { L"ulimit", &builtin_ulimit, N_(L"Set or get the shells resource usage limits") }, { L"while", &builtin_generic, N_(L"Perform a command multiple times") } diff --git a/parser.cpp b/parser.cpp index de1a635a6..f23ddda92 100644 --- a/parser.cpp +++ b/parser.cpp @@ -349,93 +349,6 @@ block_t *parser_t::current_block() return block_stack.empty() ? NULL : block_stack.back(); } -/** - Search the text for the end of the current block -*/ -static const wchar_t *parser_find_end(const wchar_t * buff) -{ - int had_cmd=0; - int count = 0; - int error=0; - int mark=0; - - CHECK(buff, 0); - - tokenizer_t tok(buff, 0); - for (; tok_has_next(&tok) && !error; tok_next(&tok)) - { - int last_type = tok_last_type(&tok); - switch (last_type) - { - case TOK_STRING: - { - if (!had_cmd) - { - if (wcscmp(tok_last(&tok), L"end")==0) - { - count--; - } - else if (parser_keywords_is_block(tok_last(&tok))) - { - count++; - } - - if (count < 0) - { - error = 1; - } - had_cmd = 1; - } - break; - } - - case TOK_END: - { - had_cmd = 0; - break; - } - - case TOK_PIPE: - case TOK_BACKGROUND: - { - if (had_cmd) - { - had_cmd = 0; - } - else - { - error = 1; - } - break; - - } - - case TOK_ERROR: - error = 1; - break; - - default: - break; - - } - if (!count) - { - tok_next(&tok); - mark = tok_get_pos(&tok); - break; - } - - } - if (!count && !error) - { - - return buff+mark; - } - return 0; - -} - - void parser_t::forbid_function(const wcstring &function) { forbidden_function.push_back(function); @@ -826,29 +739,11 @@ const wchar_t *parser_t::is_function() const int parser_t::get_lineno() const { - if (parser_use_ast()) + int lineno = -1; + if (! execution_contexts.empty()) { - int lineno = -1; - if (! execution_contexts.empty()) - { - lineno = execution_contexts.back()->get_current_line_number(); - } - return lineno; + lineno = execution_contexts.back()->get_current_line_number(); } - - int lineno; - - if (! current_tokenizer || ! tok_string(current_tokenizer)) - return -1; - - lineno = current_tokenizer->line_number_of_character_at_offset(current_tokenizer_pos); - - const wchar_t *function_name; - if ((function_name = is_function())) - { - lineno += function_get_definition_offset(function_name); - } - return lineno; }