cd.c revision 36150
1/*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Kenneth Almquist. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#ifndef lint 38#if 0 39static char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; 40#endif 41static const char rcsid[] = 42 "$Id$"; 43#endif /* not lint */ 44 45#include <sys/types.h> 46#include <sys/stat.h> 47#include <stdlib.h> 48#include <string.h> 49#include <unistd.h> 50#include <errno.h> 51 52/* 53 * The cd and pwd commands. 54 */ 55 56#include "shell.h" 57#include "var.h" 58#include "nodes.h" /* for jobs.h */ 59#include "jobs.h" 60#include "options.h" 61#include "output.h" 62#include "memalloc.h" 63#include "error.h" 64#include "exec.h" 65#include "redir.h" 66#include "mystring.h" 67#include "show.h" 68#include "cd.h" 69 70STATIC int docd __P((char *, int)); 71STATIC char *getcomponent __P((void)); 72STATIC void updatepwd __P((char *)); 73 74char *curdir = NULL; /* current working directory */ 75char *prevdir; /* previous working directory */ 76STATIC char *cdcomppath; 77 78int 79cdcmd(argc, argv) 80 int argc __unused; 81 char **argv __unused; 82{ 83 char *dest; 84 char *path; 85 char *p; 86 struct stat statb; 87 int print = 0; 88 89 nextopt(nullstr); 90 if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) 91 error("HOME not set"); 92 if (*dest == '\0') 93 dest = "."; 94 if (dest[0] == '-' && dest[1] == '\0') { 95 dest = prevdir ? prevdir : curdir; 96 if (dest) 97 print = 1; 98 else 99 dest = "."; 100 } 101 if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) 102 path = nullstr; 103 while ((p = padvance(&path, dest)) != NULL) { 104 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { 105 if (!print) { 106 /* 107 * XXX - rethink 108 */ 109 if (p[0] == '.' && p[1] == '/') 110 p += 2; 111 print = strcmp(p, dest); 112 } 113 if (docd(p, print) >= 0) 114 return 0; 115 116 } 117 } 118 error("can't cd to %s", dest); 119 /*NOTREACHED*/ 120 return 0; 121} 122 123 124/* 125 * Actually do the chdir. In an interactive shell, print the 126 * directory name if "print" is nonzero. 127 */ 128STATIC int 129docd(dest, print) 130 char *dest; 131 int print; 132{ 133 134 TRACE(("docd(\"%s\", %d) called\n", dest, print)); 135 INTOFF; 136 updatepwd(dest); 137 if (chdir(stackblock()) < 0) { 138 INTON; 139 return -1; 140 } 141 hashcd(); /* update command hash table */ 142 if (prevdir) 143 ckfree(prevdir); 144 prevdir = curdir; 145 curdir = savestr(stackblock()); 146 INTON; 147 if (print && iflag) 148 out1fmt("%s\n", stackblock()); 149 return 0; 150} 151 152 153/* 154 * Get the next component of the path name pointed to by cdcomppath. 155 * This routine overwrites the string pointed to by cdcomppath. 156 */ 157STATIC char * 158getcomponent() 159{ 160 char *p; 161 char *start; 162 163 if ((p = cdcomppath) == NULL) 164 return NULL; 165 start = cdcomppath; 166 while (*p != '/' && *p != '\0') 167 p++; 168 if (*p == '\0') { 169 cdcomppath = NULL; 170 } else { 171 *p++ = '\0'; 172 cdcomppath = p; 173 } 174 return start; 175} 176 177 178/* 179 * Determine the new working directory, but don't actually enforce 180 * any changes. 181 */ 182STATIC void 183updatepwd(dir) 184 char *dir; 185{ 186 char *new; 187 char *p; 188 189 cdcomppath = stalloc(strlen(dir) + 1); 190 scopy(dir, cdcomppath); 191 STARTSTACKSTR(new); 192 if (*dir != '/') { 193 if (curdir == NULL) 194 return; 195 p = curdir; 196 while (*p) 197 STPUTC(*p++, new); 198 if (p[-1] == '/') 199 STUNPUTC(new); 200 } 201 while ((p = getcomponent()) != NULL) { 202 if (equal(p, "..")) { 203 while (new > stackblock() && (STUNPUTC(new), *new) != '/'); 204 } else if (*p != '\0' && ! equal(p, ".")) { 205 STPUTC('/', new); 206 while (*p) 207 STPUTC(*p++, new); 208 } 209 } 210 if (new == stackblock()) 211 STPUTC('/', new); 212 STACKSTRNUL(new); 213} 214 215 216int 217pwdcmd(argc, argv) 218 int argc __unused; 219 char **argv __unused; 220{ 221 if (!getpwd()) 222 error("getcwd() failed: %s", strerror(errno)); 223 out1str(curdir); 224 out1c('\n'); 225 return 0; 226} 227 228 229/* 230 * Find out what the current directory is. If we already know the current 231 * directory, this routine returns immediately. 232 */ 233char * 234getpwd() 235{ 236 if (curdir) 237 return (curdir); 238 return ((curdir = getcwd(NULL, 0))); 239} 240