1/* $NetBSD: ex_cd.c,v 1.1.1.2 2008/05/18 14:31:12 aymeric Exp $ */ 2 3/*- 4 * Copyright (c) 1992, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * Copyright (c) 1992, 1993, 1994, 1995, 1996 7 * Keith Bostic. All rights reserved. 8 * 9 * See the LICENSE file for redistribution information. 10 */ 11 12#include "config.h" 13 14#ifndef lint 15static const char sccsid[] = "Id: ex_cd.c,v 10.12 2001/06/25 15:19:14 skimo Exp (Berkeley) Date: 2001/06/25 15:19:14"; 16#endif /* not lint */ 17 18#include <sys/param.h> 19#include <sys/queue.h> 20 21#include <bitstring.h> 22#include <errno.h> 23#include <limits.h> 24#include <pwd.h> 25#include <stdio.h> 26#include <stdlib.h> 27#include <string.h> 28#include <unistd.h> 29 30#include "../common/common.h" 31 32/* 33 * ex_cd -- :cd[!] [directory] 34 * Change directories. 35 * 36 * PUBLIC: int ex_cd __P((SCR *, EXCMD *)); 37 */ 38int 39ex_cd(SCR *sp, EXCMD *cmdp) 40{ 41 struct passwd *pw; 42 ARGS *ap; 43 const char *p, *t; /* XXX: END OF THE STACK, DON'T TRUST GETCWD. */ 44 const char *dir; 45 char buf[MAXPATHLEN * 2]; 46 size_t dlen; 47 48 /* 49 * !!! 50 * Historic practice is that the cd isn't attempted if the file has 51 * been modified, unless its name begins with a leading '/' or the 52 * force flag is set. 53 */ 54 if (F_ISSET(sp->ep, F_MODIFIED) && 55 !FL_ISSET(cmdp->iflags, E_C_FORCE) && sp->frp->name[0] != '/') { 56 msgq(sp, M_ERR, 57 "120|File modified since last complete write; write or use ! to override"); 58 return (1); 59 } 60 61 switch (cmdp->argc) { 62 case 0: 63 /* If no argument, change to the user's home directory. */ 64 if ((dir = getenv("HOME")) == NULL) { 65 if ((pw = getpwuid(getuid())) == NULL || 66 pw->pw_dir == NULL || pw->pw_dir[0] == '\0') { 67 msgq(sp, M_ERR, 68 "121|Unable to find home directory location"); 69 return (1); 70 } 71 dir = pw->pw_dir; 72 } 73 break; 74 case 1: 75 INT2CHAR(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len + 1, 76 dir, dlen); 77 break; 78 default: 79 abort(); 80 } 81 82 /* 83 * Try the current directory first. If this succeeds, don't display 84 * a message, vi didn't historically, and it should be obvious to the 85 * user where they are. 86 */ 87 if (!chdir(dir)) 88 return (0); 89 90 /* 91 * If moving to the user's home directory, or, the path begins with 92 * "/", "./" or "../", it's the only place we try. 93 */ 94 if (cmdp->argc == 0 || 95 (ap = cmdp->argv[0])->bp[0] == '/' || 96 (ap->len == 1 && ap->bp[0] == '.') || 97 (ap->len >= 2 && ap->bp[0] == '.' && ap->bp[1] == '.' && 98 (ap->bp[2] == '/' || ap->bp[2] == '\0'))) 99 goto err; 100 101 /* Try the O_CDPATH option values. */ 102 for (p = t = O_STR(sp, O_CDPATH);; ++p) 103 if (*p == '\0' || *p == ':') { 104 /* 105 * Empty strings specify ".". The only way to get an 106 * empty string is a leading colon, colons in a row, 107 * or a trailing colon. Or, to put it the other way, 108 * if the length is 1 or less, then we're dealing with 109 * ":XXX", "XXX::XXXX" , "XXX:", or "". Since we've 110 * already tried dot, we ignore tham all. 111 */ 112 if (t < p - 1) { 113 (void)snprintf(buf, 114 sizeof(buf), "%.*s/%s", (int)(p - t), 115 t, dir); 116 if (!chdir(buf)) { 117 if (getcwd(buf, sizeof(buf)) != NULL) 118 msgq_str(sp, M_INFO, buf, "122|New current directory: %s"); 119 return (0); 120 } 121 } 122 t = p + 1; 123 if (*p == '\0') 124 break; 125 } 126 127err: msgq_str(sp, M_SYSERR, dir, "%s"); 128 return (1); 129} 130