diff --git a/Makefile.in b/Makefile.in index 2ae564ac9..401dd9150 100644 --- a/Makefile.in +++ b/Makefile.in @@ -102,7 +102,7 @@ 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_history.o obj/builtin_status.o obj/builtin_read.o \ obj/builtin_source.o obj/builtin_random.o obj/builtin_echo.o \ - obj/builtin_disown.o obj/builtin_function.o \ + obj/builtin_cd.o obj/builtin_disown.o obj/builtin_function.o \ obj/builtin_complete.o obj/builtin_jobs.o obj/builtin_printf.o \ obj/builtin_set.o obj/builtin_set_color.o obj/builtin_string.o \ obj/builtin_test.o obj/builtin_ulimit.o obj/color.o obj/common.o \ diff --git a/src/builtin.cpp b/src/builtin.cpp index 40827a424..ed64961f6 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -32,6 +31,7 @@ #include "builtin.h" #include "builtin_bind.h" #include "builtin_block.h" +#include "builtin_cd.h" #include "builtin_commandline.h" #include "builtin_complete.h" #include "builtin_disown.h" @@ -427,76 +427,6 @@ static int builtin_exit(parser_t &parser, io_streams_t &streams, wchar_t **argv) return (int)ec; } -/// The cd builtin. Changes the current directory to the one specified or to $HOME if none is -/// specified. The directory can be relative to any directory in the CDPATH variable. -/// The cd builtin. Changes the current directory to the one specified or to $HOME if none is -/// specified. The directory can be relative to any directory in the CDPATH variable. -static int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { - env_var_t dir_in; - wcstring dir; - - if (argv[1] == NULL) { - dir_in = env_get_string(L"HOME"); - if (dir_in.missing_or_empty()) { - streams.err.append_format(_(L"%ls: Could not find home directory\n"), argv[0]); - return STATUS_CMD_ERROR; - } - } else { - dir_in = env_var_t(argv[1]); - } - - bool got_cd_path = false; - if (!dir_in.missing()) { - got_cd_path = path_get_cdpath(dir_in, &dir); - } - - if (!got_cd_path) { - if (errno == ENOTDIR) { - streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), argv[0], - dir_in.c_str()); - } else if (errno == ENOENT) { - streams.err.append_format(_(L"%ls: The directory '%ls' does not exist\n"), argv[0], - dir_in.c_str()); - } else if (errno == EROTTEN) { - streams.err.append_format(_(L"%ls: '%ls' is a rotten symlink\n"), argv[0], - dir_in.c_str()); - } else { - streams.err.append_format(_(L"%ls: Unknown error trying to locate directory '%ls'\n"), - argv[0], dir_in.c_str()); - } - - if (!shell_is_interactive()) streams.err.append(parser.current_line()); - - return STATUS_CMD_ERROR; - } - - if (wchdir(dir) != 0) { - struct stat buffer; - int status; - - status = wstat(dir, &buffer); - if (!status && S_ISDIR(buffer.st_mode)) { - streams.err.append_format(_(L"%ls: Permission denied: '%ls'\n"), argv[0], dir.c_str()); - - } else { - streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), argv[0], dir.c_str()); - } - - if (!shell_is_interactive()) { - streams.err.append(parser.current_line()); - } - - return STATUS_CMD_ERROR; - } - - if (!env_set_pwd()) { - streams.err.append_format(_(L"%ls: Could not set PWD variable\n"), argv[0]); - return STATUS_CMD_ERROR; - } - - return STATUS_CMD_OK; -} - /// Implementation of the builtin count command, used to count the number of arguments sent to it. static int builtin_count(parser_t &parser, io_streams_t &streams, wchar_t **argv) { UNUSED(parser); diff --git a/src/builtin_cd.cpp b/src/builtin_cd.cpp new file mode 100644 index 000000000..380d06b83 --- /dev/null +++ b/src/builtin_cd.cpp @@ -0,0 +1,131 @@ +// Implementation of the cd builtin. +#include "config.h" // IWYU pragma: keep + +#include +#include +#include + +#include "builtin.h" +#include "builtin_cd.h" +#include "common.h" +#include "env.h" +#include "fallback.h" // IWYU pragma: keep +#include "io.h" +#include "parser.h" +#include "path.h" +#include "proc.h" +#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, argv[0], argv[w.woptind - 1]); + return STATUS_INVALID_ARGS; + } + default: { + DIE("unexpected retval from wgetopt_long"); + break; + } + } + } + + *optind = w.woptind; + return STATUS_CMD_OK; +} + +/// The cd builtin. Changes the current directory to the one specified or to $HOME if none is +/// specified. The directory can be relative to any directory in the CDPATH variable. +/// The cd builtin. Changes the current directory to the one specified or to $HOME if none is +/// specified. The directory can be relative to any directory in the CDPATH variable. +int builtin_cd(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_INVALID_ARGS; + } + + env_var_t dir_in; + wcstring dir; + + if (argv[optind]) { + dir_in = env_var_t(argv[optind]); + } else { + dir_in = env_get_string(L"HOME"); + if (dir_in.missing_or_empty()) { + streams.err.append_format(_(L"%ls: Could not find home directory\n"), cmd); + return STATUS_CMD_ERROR; + } + } + + bool got_cd_path = false; + if (!dir_in.missing()) { + got_cd_path = path_get_cdpath(dir_in, &dir); + } + + if (!got_cd_path) { + if (errno == ENOTDIR) { + streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), cmd, dir_in.c_str()); + } else if (errno == ENOENT) { + streams.err.append_format(_(L"%ls: The directory '%ls' does not exist\n"), cmd, + dir_in.c_str()); + } else if (errno == EROTTEN) { + streams.err.append_format(_(L"%ls: '%ls' is a rotten symlink\n"), cmd, dir_in.c_str()); + } else { + streams.err.append_format(_(L"%ls: Unknown error trying to locate directory '%ls'\n"), + cmd, dir_in.c_str()); + } + + if (!shell_is_interactive()) streams.err.append(parser.current_line()); + + return STATUS_CMD_ERROR; + } + + if (wchdir(dir) != 0) { + struct stat buffer; + int status; + + status = wstat(dir, &buffer); + if (!status && S_ISDIR(buffer.st_mode)) { + streams.err.append_format(_(L"%ls: Permission denied: '%ls'\n"), cmd, dir.c_str()); + + } else { + streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), cmd, dir.c_str()); + } + + if (!shell_is_interactive()) { + streams.err.append(parser.current_line()); + } + + return STATUS_CMD_ERROR; + } + + if (!env_set_pwd()) { + streams.err.append_format(_(L"%ls: Could not set PWD variable\n"), cmd); + return STATUS_CMD_ERROR; + } + + return STATUS_CMD_OK; +} diff --git a/src/builtin_cd.h b/src/builtin_cd.h new file mode 100644 index 000000000..85aa8cb28 --- /dev/null +++ b/src/builtin_cd.h @@ -0,0 +1,9 @@ +// Prototypes for executing builtin_cd function. +#ifndef FISH_BUILTIN_CD_H +#define FISH_BUILTIN_CD_H + +class parser_t; +struct io_streams_t; + +int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv); +#endif