diff --git a/Makefile.in b/Makefile.in index fb8fd0420..000e5468a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -100,6 +100,7 @@ HAVE_DOXYGEN=@HAVE_DOXYGEN@ # FISH_OBJS := obj/autoload.o obj/builtin.o obj/builtin_bind.o obj/builtin_block.o \ obj/builtin_commandline.o obj/builtin_emit.o obj/builtin_functions.o \ + obj/builtin_fg.o \ obj/builtin_history.o obj/builtin_status.o obj/builtin_read.o obj/builtin_pwd.o \ obj/builtin_source.o obj/builtin_random.o obj/builtin_echo.o \ obj/builtin_cd.o obj/builtin_disown.o obj/builtin_function.o \ diff --git a/src/builtin.cpp b/src/builtin.cpp index 9c75a93aa..08d8babc1 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -18,7 +18,6 @@ #include "config.h" // IWYU pragma: keep #include -#include #include #include #include @@ -40,6 +39,7 @@ #include "builtin_disown.h" #include "builtin_echo.h" #include "builtin_emit.h" +#include "builtin_fg.h" #include "builtin_functions.h" #include "builtin_history.h" #include "builtin_jobs.h" @@ -56,7 +56,6 @@ #include "builtin_ulimit.h" #include "common.h" #include "complete.h" -#include "env.h" #include "exec.h" #include "fallback.h" // IWYU pragma: keep #include "intern.h" @@ -66,7 +65,6 @@ #include "parser.h" #include "proc.h" #include "reader.h" -#include "tokenizer.h" #include "wgetopt.h" #include "wutil.h" // IWYU pragma: keep @@ -313,89 +311,6 @@ static int builtin_count(parser_t &parser, io_streams_t &streams, wchar_t **argv return argc - 1 == 0 ? STATUS_CMD_ERROR : STATUS_CMD_OK; } -/// Builtin for putting a job in the foreground. -static int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { - job_t *j = NULL; - - if (argv[1] == 0) { - // Select last constructed job (I.e. first job in the job que) that is possible to put in - // the foreground. - job_iterator_t jobs; - while ((j = jobs.next())) { - if (j->get_flag(JOB_CONSTRUCTED) && (!job_is_completed(j)) && - ((job_is_stopped(j) || (!j->get_flag(JOB_FOREGROUND))) && - j->get_flag(JOB_CONTROL))) { - break; - } - } - if (!j) { - streams.err.append_format(_(L"%ls: There are no suitable jobs\n"), argv[0]); - } - } else if (argv[2] != 0) { - // Specifying more than one job to put to the foreground is a syntax error, we still - // try to locate the job argv[1], since we want to know if this is an ambigous job - // specification or if this is an malformed job id. - int pid; - int found_job = 0; - - pid = fish_wcstoi(argv[1]); - if (!(errno || pid < 0)) { - j = job_get_from_pid(pid); - if (j) found_job = 1; - } - - if (found_job) { - streams.err.append_format(_(L"%ls: Ambiguous job\n"), argv[0]); - } else { - streams.err.append_format(_(L"%ls: '%ls' is not a job\n"), argv[0], argv[1]); - } - - builtin_print_help(parser, streams, argv[0], streams.err); - - j = 0; - - } else { - int pid = abs(fish_wcstoi(argv[1])); - if (errno) { - streams.err.append_format(BUILTIN_ERR_NOT_NUMBER, argv[0], argv[1]); - builtin_print_help(parser, streams, argv[0], streams.err); - } else { - j = job_get_from_pid(pid); - if (!j || !j->get_flag(JOB_CONSTRUCTED) || job_is_completed(j)) { - streams.err.append_format(_(L"%ls: No suitable job: %d\n"), argv[0], pid); - j = 0; - } else if (!j->get_flag(JOB_CONTROL)) { - streams.err.append_format(_(L"%ls: Can't put job %d, '%ls' to foreground because " - L"it is not under job control\n"), - argv[0], pid, j->command_wcstr()); - j = 0; - } - } - } - - if (!j) { - return STATUS_INVALID_ARGS; - } - - if (streams.err_is_redirected) { - streams.err.append_format(FG_MSG, j->job_id, j->command_wcstr()); - } else { - // If we aren't redirecting, send output to real stderr, since stuff in sb_err won't get - // printed until the command finishes. - fwprintf(stderr, FG_MSG, j->job_id, j->command_wcstr()); - } - - const wcstring ft = tok_first(j->command()); - if (!ft.empty()) env_set(L"_", ft.c_str(), ENV_EXPORT); - reader_write_title(j->command()); - - job_promote(j); - j->set_flag(JOB_FOREGROUND, true); - - job_continue(j, job_is_stopped(j)); - return STATUS_CMD_OK; -} - /// Helper function for builtin_bg(). static int send_to_bg(parser_t &parser, io_streams_t &streams, job_t *j) { assert(j != NULL); diff --git a/src/builtin_fg.cpp b/src/builtin_fg.cpp new file mode 100644 index 000000000..3e9df4474 --- /dev/null +++ b/src/builtin_fg.cpp @@ -0,0 +1,147 @@ +// Implementation of the fg builtin. +#include "config.h" // IWYU pragma: keep + +#include +#include +#include +#include + +#include "builtin.h" +#include "builtin_fg.h" +#include "common.h" +#include "env.h" +#include "fallback.h" // IWYU pragma: keep +#include "io.h" +#include "proc.h" +#include "reader.h" +#include "tokenizer.h" // for tok_first +#include "wgetopt.h" +#include "wutil.h" // IWYU pragma: keep + +struct cmd_opts { + bool print_help = false; +}; +static const wchar_t *short_options = L"h"; +static const struct woption long_options[] = {{L"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0}}; + +static int parse_cmd_opts(struct cmd_opts *opts, int *optind, int argc, wchar_t **argv, + parser_t &parser, io_streams_t &streams) { + wchar_t *cmd = argv[0]; + int opt; + wgetopter_t w; + while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) { + switch (opt) { //!OCLINT(too few branches) + case 'h': { + opts->print_help = true; + return STATUS_CMD_OK; + } + case '?': { + builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]); + return STATUS_INVALID_ARGS; + } + default: { + DIE("unexpected retval from wgetopt_long"); + break; + } + } + } + + *optind = w.woptind; + return STATUS_CMD_OK; +} + +/// Builtin for putting a job in the foreground. +int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { + const wchar_t *cmd = argv[0]; + int argc = builtin_count_args(argv); + struct cmd_opts opts; + + int optind; + int retval = parse_cmd_opts(&opts, &optind, argc, argv, parser, streams); + if (retval != STATUS_CMD_OK) return retval; + + if (opts.print_help) { + builtin_print_help(parser, streams, cmd, streams.out); + return STATUS_CMD_OK; + } + + job_t *j = NULL; + + if (optind == argc) { + // Select last constructed job (I.e. first job in the job que) that is possible to put in + // the foreground. + job_iterator_t jobs; + while ((j = jobs.next())) { + if (j->get_flag(JOB_CONSTRUCTED) && (!job_is_completed(j)) && + ((job_is_stopped(j) || (!j->get_flag(JOB_FOREGROUND))) && + j->get_flag(JOB_CONTROL))) { + break; + } + } + if (!j) { + streams.err.append_format(_(L"%ls: There are no suitable jobs\n"), cmd); + } + } else if (optind + 1 < argc) { + // Specifying more than one job to put to the foreground is a syntax error, we still + // try to locate the job argv[1], since we want to know if this is an ambigous job + // specification or if this is an malformed job id. + int pid; + int found_job = 0; + + pid = fish_wcstoi(argv[optind]); + if (!(errno || pid < 0)) { + j = job_get_from_pid(pid); + if (j) found_job = 1; + } + + if (found_job) { + streams.err.append_format(_(L"%ls: Ambiguous job\n"), cmd); + } else { + streams.err.append_format(_(L"%ls: '%ls' is not a job\n"), cmd, argv[optind]); + } + + builtin_print_help(parser, streams, cmd, streams.err); + + j = 0; + } else { + int pid = abs(fish_wcstoi(argv[optind])); + if (errno) { + streams.err.append_format(BUILTIN_ERR_NOT_NUMBER, cmd, argv[optind]); + builtin_print_help(parser, streams, cmd, streams.err); + } else { + j = job_get_from_pid(pid); + if (!j || !j->get_flag(JOB_CONSTRUCTED) || job_is_completed(j)) { + streams.err.append_format(_(L"%ls: No suitable job: %d\n"), cmd, pid); + j = 0; + } else if (!j->get_flag(JOB_CONTROL)) { + streams.err.append_format(_(L"%ls: Can't put job %d, '%ls' to foreground because " + L"it is not under job control\n"), + cmd, pid, j->command_wcstr()); + j = 0; + } + } + } + + if (!j) { + return STATUS_INVALID_ARGS; + } + + if (streams.err_is_redirected) { + streams.err.append_format(FG_MSG, j->job_id, j->command_wcstr()); + } else { + // If we aren't redirecting, send output to real stderr, since stuff in sb_err won't get + // printed until the command finishes. + fwprintf(stderr, FG_MSG, j->job_id, j->command_wcstr()); + } + + const wcstring ft = tok_first(j->command()); + if (!ft.empty()) env_set(L"_", ft.c_str(), ENV_EXPORT); + reader_write_title(j->command()); + + job_promote(j); + j->set_flag(JOB_FOREGROUND, true); + + job_continue(j, job_is_stopped(j)); + return STATUS_CMD_OK; +} diff --git a/src/builtin_fg.h b/src/builtin_fg.h new file mode 100644 index 000000000..251ce157b --- /dev/null +++ b/src/builtin_fg.h @@ -0,0 +1,9 @@ +// Prototypes for executing builtin_fg function. +#ifndef FISH_BUILTIN_FG_H +#define FISH_BUILTIN_FG_H + +class parser_t; +struct io_streams_t; + +int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv); +#endif