text: /*-
   * Copyright (c) 1991 The Regents of the University of California.
   * All rights reserved.
   *
   * This code is derived from software contributed to Berkeley by
   * Kenneth Almquist.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. 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.
   * 3. All advertising materials mentioning features or use of this software
   *    must display the following acknowledgement:
   *	This product includes software developed by the University of
   *	California, Berkeley and its contributors.
   * 4. Neither the name of the University nor the names of its contributors
   *    may be used to endorse or promote products derived from this software
   *    without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
   */
  
  #ifndef lint
  static char sccsid[] = "@(#)cd.c	5.2 (Berkeley) 3/13/91";
  #endif /* not lint */
  
  /*
   * The cd and pwd commands.
   */
  
  #include "shell.h"
  #include "var.h"
  #include "nodes.h"	/* for jobs.h */
  #include "jobs.h"
  #include "options.h"
  #include "output.h"
  #include "memalloc.h"
  #include "error.h"
  #include "mystring.h"
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <errno.h>
  
  
  #ifdef __STDC__
  STATIC int docd(char *, int, int);
  STATIC void updatepwd(char *);
  STATIC void getpwd(void);
  STATIC char *getcomponent(void);
  #else
  STATIC int docd();
  STATIC void updatepwd();
  STATIC void getpwd();
  STATIC char *getcomponent();
  #endif
  
  
  char *curdir;			/* current working directory */
  STATIC char *cdcomppath;
  
  #if UDIR || TILDE
  extern int didudir;		/* set if /u/logname or ~logname expanded */
  #endif
  
  
  int
  cdcmd(argc, argv)  char **argv; {
  	char *dest;
  	char *path;
  	char *p;
  	struct stat statb;
  	char *padvance();
  	int tohome= 0;
  
  	nextopt(nullstr);
  	if ((dest = *argptr) == NULL) {
  		if ((dest = bltinlookup("HOME", 1)) == NULL)
  			error("HOME not set");
  		tohome = 1;
  	}
  	if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
  		path = nullstr;
  	while ((p = padvance(&path, dest)) != NULL) {
  		if (stat(p, &statb) >= 0
  		 && (statb.st_mode & S_IFMT) == S_IFDIR
  		 && docd(p, strcmp(p, dest), tohome) >= 0)
  			return 0;
  	}
  	error("can't cd to %s", dest);
  }
  
  
  /*
   * Actually do the chdir.  If the name refers to symbolic links, we
   * compute the actual directory name before doing the cd.  In an
   * interactive shell, print the directory name if "print" is nonzero
   * or if the name refers to a symbolic link.  We also print the name
   * if "/u/logname" was expanded in it, since this is similar to a
   * symbolic link.  (The check for this breaks if the user gives the
   * cd command some additional, unused arguments.)
   */
  
  #if SYMLINKS == 0
  STATIC int
  docd(dest, print, tohome)
  	char *dest;
  	{
  #if UDIR || TILDE
  	if (didudir)
  		print = 1;
  #endif
  	INTOFF;
  	if (chdir(dest) < 0) {
  		INTON;
  		return -1;
  	}
  	updatepwd(dest);
  	INTON;
  	if (print && iflag)
  		out1fmt("%s\n", stackblock());
  	return 0;
  }
  
  #else
  
  
  
  STATIC int
  docd(dest, print, tohome)
  	char *dest;
  	{
  	register char *p;
  	register char *q;
  	char *symlink;
  	char *component;
  	struct stat statb;
  	int first;
  	int i;
  
  	TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, tohome));
  #if UDIR || TILDE
  	if (didudir)
  		print = 1;
  #endif
  
  top:
  	cdcomppath = dest;
  	STARTSTACKSTR(p);
  	if (*dest == '/') {
  		STPUTC('/', p);
  		cdcomppath++;
  	}
  	first = 1;
  	while ((q = getcomponent()) != NULL) {
  		if (q[0] == '\0' || q[0] == '.' && q[1] == '\0')
  			continue;
  		if (! first)
  			STPUTC('/', p);
  		first = 0;
  		component = q;
  		while (*q)
  			STPUTC(*q++, p);
  		if (equal(component, ".."))
  			continue;
  		STACKSTRNUL(p);
  		if (lstat(stackblock(), &statb) < 0)
  			error("lstat %s failed", stackblock());
  		if ((statb.st_mode & S_IFMT) != S_IFLNK)
  			continue;
  
  		/* Hit a symbolic link.  We have to start all over again. */
  		print = 1;
  		STPUTC('\0', p);
  		symlink = grabstackstr(p);
  		i = (int)statb.st_size + 2;		/* 2 for '/' and '\0' */
  		if (cdcomppath != NULL)
  			i += strlen(cdcomppath);
  		p = stalloc(i);
  		if (readlink(symlink, p, (int)statb.st_size) < 0) {
  			error("readlink %s failed", stackblock());
  		}
  		if (cdcomppath != NULL) {
  			p[(int)statb.st_size] = '/';
  			scopy(cdcomppath, p + (int)statb.st_size + 1);
  		} else {
  			p[(int)statb.st_size] = '\0';
  		}
  		if (p[0] != '/') {	/* relative path name */
  			char *r;
  			q = r = symlink;
  			while (*q) {
  				if (*q++ == '/')
  					r = q;
  			}
  			*r = '\0';
  			dest = stalloc(strlen(symlink) + strlen(p) + 1);
  			scopy(symlink, dest);
  			strcat(dest, p);
  		} else {
  			dest = p;
  		}
  		goto top;
  	}
  	STPUTC('\0', p);
  	p = grabstackstr(p);
  	INTOFF;
  	/* The empty string is not a legal argument to chdir on a POSIX 1003.1 
  	 * system. */
  	if (p[0] != '\0' && chdir(p) < 0) {
  		INTON;
  		return -1;
  	}
  	updatepwd(p);
  	INTON;
  	if (print && !tohome && iflag)
  		out1fmt("%s\n", p);
  	return 0;
  }
  #endif /* SYMLINKS */
  
  
  
  /*
   * Get the next component of the path name pointed to by cdcomppath.
   * This routine overwrites the string pointed to by cdcomppath.
   */
  
  STATIC char *
  getcomponent() {
  	register char *p;
  	char *start;
  
  	if ((p = cdcomppath) == NULL)
  		return NULL;
  	start = cdcomppath;
  	while (*p != '/' && *p != '\0')
  		p++;
  	if (*p == '\0') {
  		cdcomppath = NULL;
  	} else {
  		*p++ = '\0';
  		cdcomppath = p;
  	}
  	return start;
  }
  
  
  
  /*
   * Update curdir (the name of the current directory) in response to a
   * cd command.  We also call hashcd to let the routines in exec.c know
   * that the current directory has changed.
   */
  
  void hashcd();
  
  STATIC void
  updatepwd(dir)
  	char *dir;
  	{
  	char *new;
  	char *p;
  
  	hashcd();				/* update command hash table */
  	cdcomppath = stalloc(strlen(dir) + 1);
  	scopy(dir, cdcomppath);
  	STARTSTACKSTR(new);
  	if (*dir != '/') {
  		if (curdir == NULL)
  			return;
  		p = curdir;
  		while (*p)
  			STPUTC(*p++, new);
  		if (p[-1] == '/')
  			STUNPUTC(new);
  	}
  	while ((p = getcomponent()) != NULL) {
  		if (equal(p, "..")) {
  			while (new > stackblock() && (STUNPUTC(new), *new) != '/');
  		} else if (*p != '\0' && ! equal(p, ".")) {
  			STPUTC('/', new);
  			while (*p)
  				STPUTC(*p++, new);
  		}
  	}
  	if (new == stackblock())
  		STPUTC('/', new);
  	STACKSTRNUL(new);
  	if (curdir)
  		ckfree(curdir);
  	curdir = savestr(stackblock());
  }
  
  
  
  int
  pwdcmd(argc, argv)  char **argv; {
  	getpwd();
  	out1str(curdir);
  	out1c('\n');
  	return 0;
  }
  
  
  
  /*
   * Run /bin/pwd to find out what the current directory is.  We suppress
   * interrupts throughout most of this, but the user can still break out
   * of it by killing the pwd program.  If we already know the current
   * directory, this routine returns immediately.
   */
  
  #define MAXPWD 256
  
  STATIC void
  getpwd() {
  	char buf[MAXPWD];
  	char *p;
  	int i;
  	int status;
  	struct job *jp;
  	int pip[2];
  
  	if (curdir)
  		return;
  	INTOFF;
  	if (pipe(pip) < 0)
  		error("Pipe call failed");
  	jp = makejob((union node *)NULL, 1);
  	if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
  		close(pip[0]);
  		if (pip[1] != 1) {
  			close(1);
  			copyfd(pip[1], 1);
  			close(pip[1]);
  		}
  		execl("/bin/pwd", "pwd", (char *)0);
  		error("Cannot exec /bin/pwd");
  	}
  	close(pip[1]);
  	pip[1] = -1;
  	p = buf;
  	while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
  	     || i == -1 && errno == EINTR) {
  		if (i > 0)
  			p += i;
  	}
  	close(pip[0]);
  	pip[0] = -1;
  	status = waitforjob(jp);
  	if (status != 0)
  		error((char *)0);
  	if (i < 0 || p == buf || p[-1] != '\n')
  		error("pwd command failed");
  	p[-1] = '\0';
  	curdir = savestr(buf);
  	INTON;
  }
  