/*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * Copyright (c) 1997-2005 * Herbert Xu . 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. 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. */ #include #include #include #include #include #include #ifdef __CYGWIN__ #include #endif /* * 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 "exec.h" #include "redir.h" #include "main.h" #include "mystring.h" #include "show.h" #include "cd.h" #define CD_PHYSICAL 1 #define CD_PRINT 2 STATIC int docd(const char *, int); STATIC const char *updatepwd(const char *); STATIC char *getpwd(void); STATIC int cdopt(void); STATIC char *curdir = nullstr; /* current working directory */ STATIC char *physdir = nullstr; /* physical working directory */ STATIC int cdopt() { int flags = 0; int i, j; j = 'L'; while ((i = nextopt("LP"))) { if (i != j) { flags ^= CD_PHYSICAL; j = i; } } return flags; } int cdcmd(int argc, char **argv) { const char *dest; const char *path; const char *p; char c; struct stat64 statb; int flags; int len; flags = cdopt(); dest = *argptr; if (!dest) dest = bltinlookup(homestr); else if (dest[0] == '-' && dest[1] == '\0') { dest = bltinlookup("OLDPWD"); flags |= CD_PRINT; } if (!dest) dest = nullstr; if (*dest == '/') goto step6; if (*dest == '.') { c = dest[1]; dotdot: switch (c) { case '\0': case '/': goto step6; case '.': c = dest[2]; if (c != '.') goto dotdot; } } if (!*dest) dest = "."; path = bltinlookup("CDPATH"); while (p = path, (len = padvance_magic(&path, dest, 0)) >= 0) { c = *p; p = stalloc(len); if (stat64(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { if (c && c != ':') flags |= CD_PRINT; docd: if (!docd(p, flags)) goto out; goto err; } } step6: p = dest; goto docd; err: sh_error("can't cd to %s", dest); /* NOTREACHED */ out: if (flags & CD_PRINT) out1fmt(snlfmt, curdir); return 0; } /* * Actually do the chdir. We also call hashcd to let the routines in exec.c * know that the current directory has changed. */ STATIC int docd(const char *dest, int flags) { const char *dir = 0; int err; TRACE(("docd(\"%s\", %d) called\n", dest, flags)); INTOFF; if (!(flags & CD_PHYSICAL)) { dir = updatepwd(dest); if (dir) dest = dir; } err = chdir(dest); if (err) goto out; setpwd(dir, 1); hashcd(); out: INTON; return err; } /* * Update curdir (the name of the current directory) in response to a * cd command. */ STATIC const char * updatepwd(const char *dir) { char *new; char *p; char *cdcomppath; const char *lim; #ifdef __CYGWIN__ /* On cygwin, thanks to drive letters, some absolute paths do not begin with slash; but cygwin includes a function that forces normalization to the posix form */ char pathbuf[PATH_MAX]; if (cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_RELATIVE, dir, pathbuf, sizeof(pathbuf)) < 0) sh_error("can't normalize %s", dir); dir = pathbuf; #endif cdcomppath = sstrdup(dir); STARTSTACKSTR(new); if (*dir != '/') { if (curdir == nullstr) return 0; new = stputs(curdir, new); } new = makestrspace(strlen(dir) + 2, new); lim = stackblock() + 1; if (*dir != '/') { if (new[-1] != '/') USTPUTC('/', new); if (new > lim && *lim == '/') lim++; } else { USTPUTC('/', new); cdcomppath++; if (dir[1] == '/' && dir[2] != '/') { USTPUTC('/', new); cdcomppath++; lim++; } } p = strtok(cdcomppath, "/"); while (p) { switch(*p) { case '.': if (p[1] == '.' && p[2] == '\0') { while (new > lim) { STUNPUTC(new); if (new[-1] == '/') break; } break; } else if (p[1] == '\0') break; /* fall through */ default: new = stputs(p, new); USTPUTC('/', new); } p = strtok(0, "/"); } if (new > lim) STUNPUTC(new); *new = 0; return stackblock(); } /* * Find out what the current directory is. If we already know the current * directory, this routine returns immediately. */ inline STATIC char * getpwd() { #ifdef __GLIBC__ char *dir = getcwd(0, 0); if (dir) return dir; #else char buf[PATH_MAX]; if (getcwd(buf, sizeof(buf))) return savestr(buf); #endif sh_warnx("getcwd() failed: %s", strerror(errno)); return nullstr; } int pwdcmd(int argc, char **argv) { int flags; const char *dir = curdir; flags = cdopt(); if (flags) { if (physdir == nullstr) setpwd(dir, 0); dir = physdir; } out1fmt(snlfmt, dir); return 0; } void setpwd(const char *val, int setold) { char *oldcur, *dir; oldcur = dir = curdir; if (setold) { setvar("OLDPWD", oldcur, VEXPORT); } INTOFF; if (physdir != nullstr) { if (physdir != oldcur) free(physdir); physdir = nullstr; } if (oldcur == val || !val) { char *s = getpwd(); physdir = s; if (!val) dir = s; } else dir = savestr(val); if (oldcur != dir && oldcur != nullstr) { free(oldcur); } curdir = dir; INTON; setvar("PWD", dir, VEXPORT); }