>From ebe4e313994059ef0c3b98c1c86b0137538e9604 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Sun, 27 Aug 2017 00:14:06 +0100 Subject: [PATCH] chdir: new command to run command with changed CWD * AUTHORS: Add my name. * NEWS: Add new binary. * README: Likewise. * build-aux/gen-lists-of-programs.sh: Likewise. * scripts/git-hooks/commit-msg: Likewise. * doc/coreutils.texi (chdir invocation): Document the new program. * man/chdir.x: New template. * man/local.mk: Reference new template. * po/POTFILES.in: Add src/chdir.c. * src/.gitignore: Ignore chdir. * src/chdir.c: New file. * src/local.mk (src_chdir_LDADD): New variable. * tests/misc/chdir.sh: New file. * tests/local.mk (all_tests): Add tests/misc/chdir.sh. * tests/misc/help-version.sh: Handle expected chdir exit code and setup requirements. * tests/misc/invalid-opt.pl: Handle expected chdir exit code. --- AUTHORS | 1 + NEWS | 4 ++ README | 6 +- build-aux/gen-lists-of-programs.sh | 1 + doc/coreutils.texi | 63 ++++++++++++++++-- man/chdir.x | 6 ++ man/local.mk | 1 + po/POTFILES.in | 1 + scripts/git-hooks/commit-msg | 6 +- src/.gitignore | 1 + src/chdir.c | 132 +++++++++++++++++++++++++++++++++++++ src/local.mk | 1 + tests/local.mk | 1 + tests/misc/chdir.sh | 35 ++++++++++ tests/misc/help-version.sh | 2 + tests/misc/invalid-opt.pl | 1 + 16 files changed, 252 insertions(+), 10 deletions(-) create mode 100644 man/chdir.x create mode 100644 src/chdir.c create mode 100644 tests/misc/chdir.sh diff --git a/AUTHORS b/AUTHORS index 93d547b..fbf92b8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -8,6 +8,7 @@ base64: Simon Josefsson basename: David MacKenzie cat: Torbjorn Granlund, Richard M. Stallman chcon: Russell Coker, Jim Meyering +chdir: Colin Watson chgrp: David MacKenzie, Jim Meyering chmod: David MacKenzie, Jim Meyering chown: David MacKenzie, Jim Meyering diff --git a/NEWS b/NEWS index d37195e..c7b49e6 100644 --- a/NEWS +++ b/NEWS @@ -75,6 +75,10 @@ GNU coreutils NEWS -*- outline -*- expr now returns number of characters matched (instead of incorrect number of bytes matched) with 'match'/':' operators on multibyte strings. +** New programs + + chdir: Run command or shell with changed working directory. + ** New features expand and unexpand now support specifying an offset for tab stops diff --git a/README b/README index 5cb90d3..5cfd228 100644 --- a/README +++ b/README @@ -7,9 +7,9 @@ arbitrary limits. The programs that can be built with this package are: - [ arch b2sum base32 base64 basename cat chcon chgrp chmod chown chroot cksum - comm coreutils cp csplit cut date dd df dir dircolors dirname du echo env - expand expr factor false fmt fold groups head hostid hostname id install + [ arch b2sum base32 base64 basename cat chcon chdir chgrp chmod chown chroot + cksum comm coreutils cp csplit cut date dd df dir dircolors dirname du echo + env expand expr factor false fmt fold groups head hostid hostname id install join kill link ln logname ls md5sum mkdir mkfifo mknod mktemp mv nice nl nohup nproc numfmt od paste pathchk pinky pr printenv printf ptx pwd readlink realpath rm rmdir runcon seq sha1sum sha224sum sha256sum sha384sum diff --git a/build-aux/gen-lists-of-programs.sh b/build-aux/gen-lists-of-programs.sh index cdbcd0a..59ac692 100755 --- a/build-aux/gen-lists-of-programs.sh +++ b/build-aux/gen-lists-of-programs.sh @@ -48,6 +48,7 @@ normal_progs=' basename cat chcon + chdir chgrp chmod chown diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 8f1cb4c..8c1d5eb 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -43,6 +43,7 @@ * basename: (coreutils)basename invocation. Strip directory and suffix. * cat: (coreutils)cat invocation. Concatenate and write files. * chcon: (coreutils)chcon invocation. Change SELinux CTX of files. +* chdir: (coreutils)chdir invocation. Specify the working directory. * chgrp: (coreutils)chgrp invocation. Change file groups. * chmod: (coreutils)chmod invocation. Change access permissions. * chown: (coreutils)chown invocation. Change file owners and groups. @@ -204,7 +205,7 @@ Free Documentation License''. * User information:: id logname whoami groups users who * System context:: date arch nproc uname hostname hostid uptime * SELinux context:: chcon runcon -* Modified command invocation:: chroot env nice nohup stdbuf timeout +* Modified command invocation:: chdir chroot env nice nohup stdbuf timeout * Process control:: kill * Delaying:: sleep * Numeric operations:: factor numfmt seq @@ -446,6 +447,7 @@ SELinux context Modified command invocation +* chdir invocation:: Run a command in a different working directory * chroot invocation:: Run a command with a different root directory * env invocation:: Run a command in a modified environment * nice invocation:: Run a command with modified niceness @@ -813,9 +815,10 @@ However, some of the programs documented here do produce other exit status values and a few associate different meanings with the values @samp{0} and @samp{1}. Here are some of the exceptions: -@command{chroot}, @command{env}, @command{expr}, @command{nice}, -@command{nohup}, @command{numfmt}, @command{printenv}, @command{sort}, -@command{stdbuf}, @command{test}, @command{timeout}, @command{tty}. +@command{chdir}, @command{chroot}, @command{env}, @command{expr}, +@command{nice}, @command{nohup}, @command{numfmt}, @command{printenv}, +@command{sort}, @command{stdbuf}, @command{test}, @command{timeout}, +@command{tty}. @node Backup options @@ -16648,6 +16651,7 @@ different than the current one: a modified environment, as a different user, etc. @menu +* chdir invocation:: Modify the working directory. * chroot invocation:: Modify the root directory. * env invocation:: Modify environment variables. * nice invocation:: Modify niceness. @@ -16657,6 +16661,57 @@ user, etc. @end menu +@node chdir invocation +@section @command{chdir}: Run a command in a different working directory + +@pindex chdir +@cindex running a program in a specified working directory +@cindex working directory, running a program in a specified + +@command{chdir} runs a command with a specified working directory. Synopses: + +@example +chdir @var{option} @var{newdir} [@var{command} [@var{args}]@dots{}] +chdir @var{option} +@end example + +This command differs from the shell built-in @command{cd} in that it starts +@var{command} as a subprocess rather than altering the shell's own working +directory; this allows it to be chained with other commands that run commands +in a different context. For example: + +@example +# Run 'true' with /chroot as its root directory and /srv as its working +# directory. +chroot /chroot chdir /srv true +# Run 'true' with /build as its working directory and FOO=bar in its +# environment. +chdir /build env FOO=bar true +@end example + +If @var{command} is not specified, the default is the value of the @env{SHELL} +environment variable or @command{/bin/sh} if not set, invoked with the +@option{-i} option. +@var{command} must not be a special built-in utility (@pxref{Special built-in +utilities}). + +@c zsh has chdir as a synonym for cd +@mayConflictWithShellBuiltIn{chdir} + +The only options are @option{--help} and @option{--version}. @xref{Common +options}. Options must precede operands. + +@cindex exit status of @command{chdir} +Exit status: + +@display +125 if @command{chdir} itself fails +126 if @var{command} is found but cannot be invoked +127 if @var{command} cannot be found +the exit status of @var{command} otherwise +@end display + + @node chroot invocation @section @command{chroot}: Run a command with a different root directory diff --git a/man/chdir.x b/man/chdir.x new file mode 100644 index 0000000..ee591f0 --- /dev/null +++ b/man/chdir.x @@ -0,0 +1,6 @@ +[NAME] +chdir \- run command or interactive shell with changed working directory +[DESCRIPTION] +.\" Add any additional description here +[SEE ALSO] +chdir(2) diff --git a/man/local.mk b/man/local.mk index 4b98bfd..e7ab51a 100644 --- a/man/local.mk +++ b/man/local.mk @@ -66,6 +66,7 @@ man/base64.1: src/base64$(EXEEXT) man/basename.1: src/basename$(EXEEXT) man/cat.1: src/cat$(EXEEXT) man/chcon.1: src/chcon$(EXEEXT) +man/chdir.1: src/chdir$(EXEEXT) man/chgrp.1: src/chgrp$(EXEEXT) man/chmod.1: src/chmod$(EXEEXT) man/chown.1: src/chown$(EXEEXT) diff --git a/po/POTFILES.in b/po/POTFILES.in index e4fcdbf..9dd9d93 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -37,6 +37,7 @@ src/base64.c src/basename.c src/cat.c src/chcon.c +src/chdir.c src/chgrp.c src/chmod.c src/chown-core.c diff --git a/scripts/git-hooks/commit-msg b/scripts/git-hooks/commit-msg index 6aedf80..a1d4d44 100755 --- a/scripts/git-hooks/commit-msg +++ b/scripts/git-hooks/commit-msg @@ -14,9 +14,9 @@ $editor = "vi" if $? != 0 or $editor =~ /^\s*\z/; # Keywords allowed before the colon on the first line of a commit message: # program names and a few general category names. my @valid = qw( - arch b2sum base32 base64 basename cat chcon chgrp chmod chown chroot cksum - comm cp csplit cut date dd df dir dircolors dirname du echo env expand - expr factor false fmt fold groups head hostid hostname id install + arch b2sum base32 base64 basename cat chcon chdir chgrp chmod chown chroot + cksum comm cp csplit cut date dd df dir dircolors dirname du echo env + expand expr factor false fmt fold groups head hostid hostname id install join kill link ln logname ls md5sum mkdir mkfifo mknod mktemp mv nice nl nohup nproc numfmt od paste pathchk pinky pr printenv printf ptx pwd readlink realpath rm rmdir runcon seq sha1sum sha224sum sha256sum diff --git a/src/.gitignore b/src/.gitignore index 70ab2ca..e3c4072 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -8,6 +8,7 @@ base64 basename cat chcon +chdir chgrp chmod chown diff --git a/src/chdir.c b/src/chdir.c new file mode 100644 index 0000000..07210cd --- /dev/null +++ b/src/chdir.c @@ -0,0 +1,132 @@ +/* chdir -- run command or shell with changed working directory + Copyright (C) 2017 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Colin Watson. */ + +#include +#include +#include +#include +#include +#include + +#include "system.h" +#include "die.h" +#include "error.h" +#include "quote.h" + +/* The official name of this program (e.g., no 'g' prefix). */ +#define PROGRAM_NAME "chdir" + +#define AUTHORS proper_name ("Colin Watson") + +static struct option const long_opts[] = +{ + {GETOPT_HELP_OPTION_DECL}, + {GETOPT_VERSION_OPTION_DECL}, + {NULL, 0, NULL, 0} +}; + +void +usage (int status) +{ + if (status != EXIT_SUCCESS) + emit_try_help (); + else + { + printf (_("\ +Usage: %s [OPTION] NEWDIR [COMMAND [ARG]...]\n\ + or: %s OPTION\n\ +"), program_name, program_name); + + fputs (_("\ +Run COMMAND with working directory set to NEWDIR.\n\ +\n\ +"), stdout); + + fputs (HELP_OPTION_DESCRIPTION, stdout); + fputs (VERSION_OPTION_DESCRIPTION, stdout); + fputs (_("\ +\n\ +If no command is given, run '\"$SHELL\" -i' (default: '/bin/sh -i').\n\ +"), stdout); + emit_ancillary_info (PROGRAM_NAME); + } + exit (status); +} + +int +main (int argc, char **argv) +{ + int c; + + initialize_main (&argc, &argv); + set_program_name (argv[0]); + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + initialize_exit_failure (EXIT_CANCELED); + atexit (close_stdout); + + while ((c = getopt_long (argc, argv, "+", long_opts, NULL)) != -1) + { + switch (c) + { + case_GETOPT_HELP_CHAR; + + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); + + default: + usage (EXIT_CANCELED); + } + } + + if (argc <= optind) + { + error (0, 0, _("missing operand")); + usage (EXIT_CANCELED); + } + + char const *newdir = argv[optind]; + + if (chdir (newdir) != 0) + die (EXIT_CANCELED, errno, _("cannot change directory to %s"), + quoteaf (newdir)); + + if (argc == optind + 1) + { + /* No command. Run an interactive shell. */ + char *shell = getenv ("SHELL"); + if (shell == NULL) + shell = bad_cast ("/bin/sh"); + argv[0] = shell; + argv[1] = bad_cast ("-i"); + argv[2] = NULL; + } + else + { + /* The following arguments give the command. */ + argv += optind + 1; + } + + /* Execute the given command. */ + execvp (argv[0], argv); + + int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE; + error (0, errno, _("failed to run command %s"), quote (argv[0])); + return exit_status; +} diff --git a/src/local.mk b/src/local.mk index 1cb6859..ae27d01 100644 --- a/src/local.mk +++ b/src/local.mk @@ -100,6 +100,7 @@ src_base32_LDADD = $(LDADD) src_basename_LDADD = $(LDADD) src_cat_LDADD = $(LDADD) src_chcon_LDADD = $(LDADD) +src_chdir_LDADD = $(LDADD) src_chgrp_LDADD = $(LDADD) src_chmod_LDADD = $(LDADD) src_chown_LDADD = $(LDADD) diff --git a/tests/local.mk b/tests/local.mk index fd4713d..abbbaaa 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -275,6 +275,7 @@ all_tests = \ tests/misc/base64.pl \ tests/misc/basename.pl \ tests/misc/close-stdout.sh \ + tests/misc/chdir.sh \ tests/misc/chroot-fail.sh \ tests/misc/comm.pl \ tests/misc/csplit.sh \ diff --git a/tests/misc/chdir.sh b/tests/misc/chdir.sh new file mode 100644 index 0000000..0b6bd01 --- /dev/null +++ b/tests/misc/chdir.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# Verify behavior of chdir. + +# Copyright (C) 2017 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ chdir pwd + +# Verify exact status of internal failure. +returns_ 125 chdir || fail=1 # missing argument +returns_ 125 chdir --- / true || fail=1 # unknown option + +returns_ 2 chdir / sh -c 'exit 2' || fail=1 # exit status propagation +returns_ 126 chdir / . || fail=1 # invalid command +returns_ 127 chdir / no_such || fail=1 # no such command + +# Ensure we change directory. +curdir=$(chdir /bin env pwd) || fail=1 +test "$curdir" = /bin || fail=1 + +Exit $fail diff --git a/tests/misc/help-version.sh b/tests/misc/help-version.sh index b864e8c..77d74a7 100755 --- a/tests/misc/help-version.sh +++ b/tests/misc/help-version.sh @@ -22,6 +22,7 @@ # Terminate any background processes cleanup_() { kill $pid 2>/dev/null && wait $pid; } +expected_failure_status_chdir=125 expected_failure_status_chroot=125 expected_failure_status_env=125 expected_failure_status_nice=125 @@ -188,6 +189,7 @@ expr_setup () { args=foo; } # Punt, in case GNU 'id' hasn't been installed yet. groups_setup () { args=--version; } +chdir_setup () { args="/ true"; } pathchk_setup () { args=$tmp_in; } yes_setup () { args=--version; } logname_setup () { args=--version; } diff --git a/tests/misc/invalid-opt.pl b/tests/misc/invalid-opt.pl index cca7811..c7437ed 100755 --- a/tests/misc/invalid-opt.pl +++ b/tests/misc/invalid-opt.pl @@ -28,6 +28,7 @@ my %exit_status = dir => 2, vdir => 2, test => 2, + chdir => 125, chroot => 125, echo => 0, env => 125, -- 2.7.4