From 1d5cace9db9aef76f26b2d7ba54bbb76443b00b2 Mon Sep 17 00:00:00 2001
From: Daniel Baumann <daniel.baumann@progress-linux.org>
Date: Sun, 5 May 2024 20:33:23 +0200
Subject: Adding upstream version 5.0.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
---
 builtins/cd.def | 684 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 684 insertions(+)
 create mode 100644 builtins/cd.def

(limited to 'builtins/cd.def')

diff --git a/builtins/cd.def b/builtins/cd.def
new file mode 100644
index 0000000..fe75b57
--- /dev/null
+++ b/builtins/cd.def
@@ -0,0 +1,684 @@
+This file is cd.def, from which is created cd.c.  It implements the
+builtins "cd" and "pwd" in Bash.
+
+Copyright (C) 1987-2016 Free Software Foundation, Inc.
+
+This file is part of GNU Bash, the Bourne Again SHell.
+
+Bash 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.
+
+Bash 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 Bash.  If not, see <http://www.gnu.org/licenses/>.
+
+$PRODUCES cd.c
+#include <config.h>
+
+#if defined (HAVE_UNISTD_H)
+#  ifdef _MINIX
+#    include <sys/types.h>
+#  endif
+#  include <unistd.h>
+#endif
+
+#include "../bashtypes.h"
+#include "posixdir.h"
+#include "posixstat.h"
+#if defined (HAVE_SYS_PARAM_H)
+#include <sys/param.h>
+#endif
+#include <fcntl.h>
+
+#include <stdio.h>
+
+#include "../bashansi.h"
+#include "../bashintl.h"
+
+#include <errno.h>
+#include <tilde/tilde.h>
+
+#include "../shell.h"
+#include "../flags.h"
+#include "maxpath.h"
+#include "common.h"
+#include "bashgetopt.h"
+
+#if !defined (errno)
+extern int errno;
+#endif /* !errno */
+
+extern const char * const bash_getcwd_errstr;
+
+static int bindpwd __P((int));
+static int setpwd __P((char *));
+static char *resetpwd __P((char *));
+static int change_to_directory __P((char *, int, int));
+
+static int cdxattr __P((char *, char **));
+static void resetxattr __P((void));
+
+/* Change this to 1 to get cd spelling correction by default. */
+int cdspelling = 0;
+
+int cdable_vars;
+
+static int eflag;	/* file scope so bindpwd() can see it */
+static int xattrflag;	/* O_XATTR support for openat */
+static int xattrfd = -1;
+
+$BUILTIN cd
+$FUNCTION cd_builtin
+$SHORT_DOC cd [-L|[-P [-e]] [-@]] [dir]
+Change the shell working directory.
+
+Change the current directory to DIR.  The default DIR is the value of the
+HOME shell variable.
+
+The variable CDPATH defines the search path for the directory containing
+DIR.  Alternative directory names in CDPATH are separated by a colon (:).
+A null directory name is the same as the current directory.  If DIR begins
+with a slash (/), then CDPATH is not used.
+
+If the directory is not found, and the shell option `cdable_vars' is set,
+the word is assumed to be  a variable name.  If that variable has a value,
+its value is used for DIR.
+
+Options:
+  -L	force symbolic links to be followed: resolve symbolic
+		links in DIR after processing instances of `..'
+  -P	use the physical directory structure without following
+		symbolic links: resolve symbolic links in DIR before
+		processing instances of `..'
+  -e	if the -P option is supplied, and the current working
+		directory cannot be determined successfully, exit with
+		a non-zero status
+#if defined (O_XATTR)
+  -@	on systems that support it, present a file with extended
+		attributes as a directory containing the file attributes
+#endif
+
+The default is to follow symbolic links, as if `-L' were specified.
+`..' is processed by removing the immediately previous pathname component
+back to a slash or the beginning of DIR.
+
+Exit Status:
+Returns 0 if the directory is changed, and if $PWD is set successfully when
+-P is used; non-zero otherwise.
+$END
+
+/* Just set $PWD, don't change OLDPWD.  Used by `pwd -P' in posix mode. */
+static int
+setpwd (dirname)
+     char *dirname;
+{
+  int old_anm;
+  SHELL_VAR *tvar;
+
+  old_anm = array_needs_making;
+  tvar = bind_variable ("PWD", dirname ? dirname : "", 0);
+  if (tvar && readonly_p (tvar))
+    return EXECUTION_FAILURE;
+  if (tvar && old_anm == 0 && array_needs_making && exported_p (tvar))
+    {
+      update_export_env_inplace ("PWD=", 4, dirname ? dirname : "");
+      array_needs_making = 0;
+    }
+  return EXECUTION_SUCCESS;
+}
+
+static int
+bindpwd (no_symlinks)
+     int no_symlinks;
+{
+  char *dirname, *pwdvar;
+  int old_anm, r, canon_failed;
+  SHELL_VAR *tvar;
+
+  r = sh_chkwrite (EXECUTION_SUCCESS);
+
+#define tcwd the_current_working_directory
+  dirname = tcwd ? (no_symlinks ? sh_physpath (tcwd, 0) : tcwd)
+  		 : get_working_directory ("cd");
+#undef tcwd
+
+  /* If canonicalization fails, reset dirname to the_current_working_directory */
+  if (dirname == 0)
+    {
+      canon_failed = 1;
+      dirname = the_current_working_directory;
+    }
+
+  old_anm = array_needs_making;
+  pwdvar = get_string_value ("PWD");
+
+  tvar = bind_variable ("OLDPWD", pwdvar, 0);
+  if (tvar && readonly_p (tvar))
+    r = EXECUTION_FAILURE;
+
+  if (old_anm == 0 && array_needs_making && exported_p (tvar))
+    {
+      update_export_env_inplace ("OLDPWD=", 7, pwdvar);
+      array_needs_making = 0;
+    }
+
+  if (setpwd (dirname) == EXECUTION_FAILURE)
+    r = EXECUTION_FAILURE;
+  if (canon_failed && eflag)
+    r = EXECUTION_FAILURE;
+
+  if (dirname && dirname != the_current_working_directory)
+    free (dirname);
+
+  return (r);
+}
+
+/* Call get_working_directory to reset the value of
+   the_current_working_directory () */
+static char *
+resetpwd (caller)
+     char *caller;
+{
+  char *tdir;
+      
+  FREE (the_current_working_directory);
+  the_current_working_directory = (char *)NULL;
+  tdir = get_working_directory (caller);
+  return (tdir);
+}
+
+static int
+cdxattr (dir, ndirp)
+     char *dir;		/* don't assume we can always free DIR */
+     char **ndirp;	/* return new constructed directory name */
+{
+#if defined (O_XATTR)
+  int apfd, fd, r, e;
+  char buf[11+40+40];	/* construct new `fake' path for pwd */
+
+  apfd = openat (AT_FDCWD, dir, O_RDONLY|O_NONBLOCK);
+  if (apfd < 0)
+    return -1;
+  fd = openat (apfd, ".", O_XATTR);
+  e = errno;
+  close (apfd);		/* ignore close error for now */
+  errno = e;
+  if (fd < 0)
+    return -1;
+  r = fchdir (fd);	/* assume fchdir exists everywhere with O_XATTR */
+  if (r < 0)
+    {
+      close (fd);
+      return -1;
+    }
+  /* NFSv4 and ZFS extended attribute directories do not have names which are
+     visible in the standard Unix directory tree structure.  To ensure we have
+     a valid name for $PWD, we synthesize one under /proc, but to keep that
+     path valid, we need to keep the file descriptor open as long as we are in
+     this directory.  This imposes a certain structure on /proc. */
+  if (ndirp)
+    {
+      sprintf (buf, "/proc/%d/fd/%d", getpid(), fd);
+      *ndirp = savestring (buf);
+    }
+
+  if (xattrfd >= 0)
+    close (xattrfd);
+  xattrfd = fd;  
+
+  return r;
+#else
+  return -1;
+#endif
+}
+
+/* Clean up the O_XATTR baggage.  Currently only closes xattrfd */
+static void
+resetxattr ()
+{
+#if defined (O_XATTR)
+  if (xattrfd >= 0)
+    {
+      close (xattrfd);
+      xattrfd = -1;
+    }
+#else
+  xattrfd = -1;		/* not strictly necessary */
+#endif
+}
+
+#define LCD_DOVARS	0x001
+#define LCD_DOSPELL	0x002
+#define LCD_PRINTPATH	0x004
+#define LCD_FREEDIRNAME	0x008
+
+/* This builtin is ultimately the way that all user-visible commands should
+   change the current working directory.  It is called by cd_to_string (),
+   so the programming interface is simple, and it handles errors and
+   restrictions properly. */
+int
+cd_builtin (list)
+     WORD_LIST *list;
+{
+  char *dirname, *cdpath, *path, *temp;
+  int path_index, no_symlinks, opt, lflag, e;
+
+#if defined (RESTRICTED_SHELL)
+  if (restricted)
+    {
+      sh_restricted ((char *)NULL);
+      return (EXECUTION_FAILURE);
+    }
+#endif /* RESTRICTED_SHELL */
+
+  eflag = 0;
+  no_symlinks = no_symbolic_links;
+  xattrflag = 0;
+  reset_internal_getopt ();
+#if defined (O_XATTR)
+  while ((opt = internal_getopt (list, "eLP@")) != -1)
+#else
+  while ((opt = internal_getopt (list, "eLP")) != -1)
+#endif
+    {
+      switch (opt)
+	{
+	case 'P':
+	  no_symlinks = 1;
+	  break;
+	case 'L':
+	  no_symlinks = 0;
+	  break;
+	case 'e':
+	  eflag = 1;
+	  break;
+#if defined (O_XATTR)
+	case '@':
+	  xattrflag = 1;
+	  break;
+#endif
+	CASE_HELPOPT;
+	default:
+	  builtin_usage ();
+	  return (EX_USAGE);
+	}
+    }
+  list = loptend;
+
+  lflag = (cdable_vars ? LCD_DOVARS : 0) |
+	  ((interactive && cdspelling) ? LCD_DOSPELL : 0);
+  if (eflag && no_symlinks == 0)
+    eflag = 0;
+
+  if (list == 0)
+    {
+      /* `cd' without arguments is equivalent to `cd $HOME' */
+      dirname = get_string_value ("HOME");
+
+      if (dirname == 0)
+	{
+	  builtin_error (_("HOME not set"));
+	  return (EXECUTION_FAILURE);
+	}
+      lflag = 0;
+    }
+#if defined (CD_COMPLAINS)
+  else if (list->next)
+    {
+      builtin_error (_("too many arguments"));
+      return (EXECUTION_FAILURE);
+    }
+#endif
+#if 0
+  else if (list->word->word[0] == '\0')
+    {
+      builtin_error (_("null directory"));
+      return (EXECUTION_FAILURE);
+    }
+#endif
+  else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
+    {
+      /* This is `cd -', equivalent to `cd $OLDPWD' */
+      dirname = get_string_value ("OLDPWD");
+
+      if (dirname == 0)
+	{
+	  builtin_error (_("OLDPWD not set"));
+	  return (EXECUTION_FAILURE);
+	}
+#if 0
+      lflag = interactive ? LCD_PRINTPATH : 0;
+#else
+      lflag = LCD_PRINTPATH;		/* According to SUSv3 */
+#endif
+    }
+  else if (absolute_pathname (list->word->word))
+    dirname = list->word->word;
+  else if (privileged_mode == 0 && (cdpath = get_string_value ("CDPATH")))
+    {
+      dirname = list->word->word;
+
+      /* Find directory in $CDPATH. */
+      path_index = 0;
+      while (path = extract_colon_unit (cdpath, &path_index))
+	{
+	  /* OPT is 1 if the path element is non-empty */
+	  opt = path[0] != '\0';
+	  temp = sh_makepath (path, dirname, MP_DOTILDE);
+	  free (path);
+
+	  if (change_to_directory (temp, no_symlinks, xattrflag))
+	    {
+	      /* POSIX.2 says that if a nonempty directory from CDPATH
+		 is used to find the directory to change to, the new
+		 directory name is echoed to stdout, whether or not
+		 the shell is interactive. */
+	      if (opt && (path = no_symlinks ? temp : the_current_working_directory))
+		printf ("%s\n", path);
+
+	      free (temp);
+#if 0
+	      /* Posix.2 says that after using CDPATH, the resultant
+		 value of $PWD will not contain `.' or `..'. */
+	      return (bindpwd (posixly_correct || no_symlinks));
+#else
+	      return (bindpwd (no_symlinks));
+#endif
+	    }
+	  else
+	    free (temp);
+	}
+
+#if 0
+      /* changed for bash-4.2 Posix cd description steps 5-6 */
+      /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
+	 try the current directory, so we just punt now with an error
+	 message if POSIXLY_CORRECT is non-zero.  The check for cdpath[0]
+	 is so we don't mistakenly treat a CDPATH value of "" as not
+	 specifying the current directory. */
+      if (posixly_correct && cdpath[0])
+	{
+	  builtin_error ("%s: %s", dirname, strerror (ENOENT));
+	  return (EXECUTION_FAILURE);
+	}
+#endif
+    }
+  else
+    dirname = list->word->word;
+
+  /* When we get here, DIRNAME is the directory to change to.  If we
+     chdir successfully, just return. */
+  if (change_to_directory (dirname, no_symlinks, xattrflag))
+    {
+      if (lflag & LCD_PRINTPATH)
+	printf ("%s\n", dirname);
+      return (bindpwd (no_symlinks));
+    }
+
+  /* If the user requests it, then perhaps this is the name of
+     a shell variable, whose value contains the directory to
+     change to. */
+  if (lflag & LCD_DOVARS)
+    {
+      temp = get_string_value (dirname);
+      if (temp && change_to_directory (temp, no_symlinks, xattrflag))
+	{
+	  printf ("%s\n", temp);
+	  return (bindpwd (no_symlinks));
+	}
+    }
+
+  /* If the user requests it, try to find a directory name similar in
+     spelling to the one requested, in case the user made a simple
+     typo.  This is similar to the UNIX 8th and 9th Edition shells. */
+  if (lflag & LCD_DOSPELL)
+    {
+      temp = dirspell (dirname);
+      if (temp && change_to_directory (temp, no_symlinks, xattrflag))
+	{
+	  printf ("%s\n", temp);
+	  free (temp);
+	  return (bindpwd (no_symlinks));
+	}
+      else
+	FREE (temp);
+    }
+
+  e = errno;
+  temp = printable_filename (dirname, 0);
+  builtin_error ("%s: %s", temp, strerror (e));
+  if (temp != dirname)
+    free (temp);
+  return (EXECUTION_FAILURE);
+}
+
+$BUILTIN pwd
+$FUNCTION pwd_builtin
+$SHORT_DOC pwd [-LP]
+Print the name of the current working directory.
+
+Options:
+  -L	print the value of $PWD if it names the current working
+		directory
+  -P	print the physical directory, without any symbolic links
+
+By default, `pwd' behaves as if `-L' were specified.
+
+Exit Status:
+Returns 0 unless an invalid option is given or the current directory
+cannot be read.
+$END
+
+/* Non-zero means that pwd always prints the physical directory, without
+   symbolic links. */
+static int verbatim_pwd;
+
+/* Print the name of the current working directory. */
+int
+pwd_builtin (list)
+     WORD_LIST *list;
+{
+  char *directory;
+  int opt, pflag;
+
+  verbatim_pwd = no_symbolic_links;
+  pflag = 0;
+  reset_internal_getopt ();
+  while ((opt = internal_getopt (list, "LP")) != -1)
+    {
+      switch (opt)
+	{
+	case 'P':
+	  verbatim_pwd = pflag = 1;
+	  break;
+	case 'L':
+	  verbatim_pwd = 0;
+	  break;
+	CASE_HELPOPT;
+	default:
+	  builtin_usage ();
+	  return (EX_USAGE);
+	}
+    }
+  list = loptend;
+
+#define tcwd the_current_working_directory
+
+  directory = tcwd ? (verbatim_pwd ? sh_physpath (tcwd, 0) : tcwd)
+		   : get_working_directory ("pwd");
+
+  /* Try again using getcwd() if canonicalization fails (for instance, if
+     the file system has changed state underneath bash). */
+  if ((tcwd && directory == 0) ||
+      (posixly_correct && same_file (".", tcwd, (struct stat *)0, (struct stat *)0) == 0))
+    {
+      if (directory && directory != tcwd)
+        free (directory);
+      directory = resetpwd ("pwd");
+    }
+
+#undef tcwd
+
+  if (directory)
+    {
+      opt = EXECUTION_SUCCESS;
+      printf ("%s\n", directory);
+      /* This is dumb but posix-mandated. */
+      if (posixly_correct && pflag)
+	opt = setpwd (directory);
+      if (directory != the_current_working_directory)
+	free (directory);
+      return (sh_chkwrite (opt));
+    }
+  else
+    return (EXECUTION_FAILURE);
+}
+
+/* Do the work of changing to the directory NEWDIR.  Handle symbolic
+   link following, etc.  This function *must* return with
+   the_current_working_directory either set to NULL (in which case
+   getcwd() will eventually be called), or set to a string corresponding
+   to the working directory.  Return 1 on success, 0 on failure. */
+
+static int
+change_to_directory (newdir, nolinks, xattr)
+     char *newdir;
+     int nolinks, xattr;
+{
+  char *t, *tdir, *ndir;
+  int err, canon_failed, r, ndlen;
+
+  tdir = (char *)NULL;
+
+  if (the_current_working_directory == 0)
+    {
+      t = get_working_directory ("chdir");
+      FREE (t);
+    }
+
+  t = make_absolute (newdir, the_current_working_directory);
+
+  /* TDIR is either the canonicalized absolute pathname of NEWDIR
+     (nolinks == 0) or the absolute physical pathname of NEWDIR
+     (nolinks != 0). */
+  tdir = nolinks ? sh_physpath (t, 0)
+		 : sh_canonpath (t, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
+
+  ndlen = strlen (newdir);
+
+  /* Use the canonicalized version of NEWDIR, or, if canonicalization
+     failed, use the non-canonical form. */
+  canon_failed = 0;
+  if (tdir && *tdir)
+    free (t);
+  else
+    {
+      FREE (tdir);
+      tdir = t;
+      canon_failed = 1;
+    }
+
+  /* In POSIX mode, if we're resolving symlinks logically and sh_canonpath
+     returns NULL (because it checks the path, it will return NULL if the
+     resolved path doesn't exist), fail immediately. */
+#if defined (ENAMETOOLONG)
+  if (posixly_correct && nolinks == 0 && canon_failed && (errno != ENAMETOOLONG || ndlen > PATH_MAX))
+#else
+  if (posixly_correct && nolinks == 0 && canon_failed && ndlen > PATH_MAX)
+#endif
+    {
+#if defined ENAMETOOLONG
+      if (errno != ENOENT && errno != ENAMETOOLONG)
+#else
+      if (errno != ENOENT)
+#endif
+	errno = ENOTDIR;
+      free (tdir);
+      return (0);
+    }
+
+#if defined (O_XATTR)
+  if (xattrflag)
+    {
+      r = cdxattr (nolinks ? newdir : tdir, &ndir);
+      if (r >= 0)
+	{
+	  canon_failed = 0;
+	  free (tdir);
+	  tdir = ndir;
+	}
+      else
+	{
+	  err = errno;
+	  free (tdir);
+	  errno = err;
+	  return (0);		/* no xattr */
+	}
+    }
+  else
+#endif
+    {
+      r = chdir (nolinks ? newdir : tdir);
+      if (r >= 0)
+	resetxattr ();
+    }
+
+  /* If the chdir succeeds, update the_current_working_directory. */
+  if (r == 0)
+    {
+      /* If canonicalization failed, but the chdir succeeded, reset the
+	 shell's idea of the_current_working_directory. */
+      if (canon_failed)
+	{
+	  t = resetpwd ("cd");
+	  if (t == 0)
+	    set_working_directory (tdir);
+	  else
+	    free (t);
+	}
+      else
+	set_working_directory (tdir);
+
+      free (tdir);
+      return (1);
+    }
+
+  /* We failed to change to the appropriate directory name.  If we tried
+     what the user passed (nolinks != 0), punt now. */
+  if (nolinks)
+    {
+      free (tdir);
+      return (0);
+    }
+
+  err = errno;
+
+  /* We're not in physical mode (nolinks == 0), but we failed to change to
+     the canonicalized directory name (TDIR).  Try what the user passed
+     verbatim. If we succeed, reinitialize the_current_working_directory.
+     POSIX requires that we just fail here, so we do in posix mode. */
+  if (posixly_correct == 0 && chdir (newdir) == 0)
+    {
+      t = resetpwd ("cd");
+      if (t == 0)
+	set_working_directory (tdir);
+      else
+	free (t);
+
+      r = 1;
+    }
+  else
+    {
+      errno = err;
+      r = 0;
+    }
+
+  free (tdir);
+  return r;
+}
-- 
cgit v1.2.3

