/* * Copyright (c) 2008 Tim Post * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include "util.h" #include "errors.h" #include "entry.h" #include "cmds.h" #include "cd.h" static const char *cmdname = "cd"; /* * Previous directory variables. * * Declaring them static to avoid many "== NULL" checks. * PATH_MAX is not that big to cause any problems with memory overhead. */ static char previous_directory[PATH_MAX] = ""; static char previous_directory_tmp[PATH_MAX]; static bool previous_directory_valid = true; static bool previous_directory_set = false; static errno_t chdir_and_remember(const char *new_dir) { errno_t rc = vfs_cwd_get(previous_directory_tmp, PATH_MAX); previous_directory_valid = (rc == EOK); previous_directory_set = true; rc = vfs_cwd_set(new_dir); if (rc != EOK) return rc; str_cpy(previous_directory, PATH_MAX, previous_directory_tmp); return EOK; } void help_cmd_cd(unsigned int level) { if (level == HELP_SHORT) { printf("`%s' changes the current working directory.\n", cmdname); } else { printf( " %s \n" " Change directory to , e.g `%s /sbin'\n", cmdname, cmdname); } return; } /* This is a very rudamentary 'cd' command. It is not 'link smart' (yet) */ int cmd_cd(char **argv, cliuser_t *usr) { int argc; errno_t rc = EOK; argc = cli_count_args(argv); /* Handle cd -- -. Override to switch to a directory named '-' */ bool hyphen_override = false; char *target_directory = argv[1]; if (argc == 3) { if (!str_cmp(argv[1], "--")) { hyphen_override = true; argc--; target_directory = argv[2]; } } /* * We don't yet play nice with whitespace, a getopt implementation should * protect "quoted\ destination" as a single argument. Its not our job to * look for && || or redirection as the tokenizer should have done that * (currently, it does not) */ if (argc > 2) { cli_error(CL_EFAIL, "Too many arguments to `%s'", cmdname); return CMD_FAILURE; } if (argc < 2) { printf("%s - no directory specified. Try `help %s extended'\n", cmdname, cmdname); return CMD_FAILURE; } /* We have the correct # of arguments */ // TODO: handle tidle (~) expansion? */ /* Handle 'cd -' first. */ if (!str_cmp(target_directory, "-") && !hyphen_override) { if (!previous_directory_valid) { cli_error(CL_EFAIL, "Cannot switch to previous directory"); return CMD_FAILURE; } if (!previous_directory_set) { cli_error(CL_EFAIL, "No previous directory to switch to"); return CMD_FAILURE; } char *prev_dup = str_dup(previous_directory); if (prev_dup == NULL) { cli_error(CL_ENOMEM, "Cannot switch to previous directory"); return CMD_FAILURE; } rc = chdir_and_remember(prev_dup); free(prev_dup); } else { rc = chdir_and_remember(target_directory); } if (rc == 0) { cli_set_prompt(usr); return CMD_SUCCESS; } else { switch (rc) { case ENOMEM: cli_error(CL_EFAIL, "Destination path too long"); break; case ENOENT: cli_error(CL_ENOENT, "Invalid directory `%s'", target_directory); break; default: cli_error(CL_EFAIL, "Unable to change to `%s'", target_directory); break; } } return CMD_FAILURE; }