1 |
|
|
/* $OpenBSD: ex_cd.c,v 1.15 2016/05/27 09:18:12 martijn 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 |
|
|
#include <sys/queue.h> |
15 |
|
|
|
16 |
|
|
#include <bitstring.h> |
17 |
|
|
#include <errno.h> |
18 |
|
|
#include <limits.h> |
19 |
|
|
#include <pwd.h> |
20 |
|
|
#include <stdio.h> |
21 |
|
|
#include <stdlib.h> |
22 |
|
|
#include <string.h> |
23 |
|
|
#include <unistd.h> |
24 |
|
|
|
25 |
|
|
#include "../common/common.h" |
26 |
|
|
|
27 |
|
|
/* |
28 |
|
|
* ex_cd -- :cd[!] [directory] |
29 |
|
|
* Change directories. |
30 |
|
|
* |
31 |
|
|
* PUBLIC: int ex_cd(SCR *, EXCMD *); |
32 |
|
|
*/ |
33 |
|
|
int |
34 |
|
|
ex_cd(SCR *sp, EXCMD *cmdp) |
35 |
|
|
{ |
36 |
|
|
struct passwd *pw; |
37 |
|
|
ARGS *ap; |
38 |
|
|
CHAR_T savech; |
39 |
|
|
char *dir, *p, *t; |
40 |
|
|
char buf[PATH_MAX * 2]; |
41 |
|
|
|
42 |
|
|
/* |
43 |
|
|
* !!! |
44 |
|
|
* Historic practice is that the cd isn't attempted if the file has |
45 |
|
|
* been modified, unless its name begins with a leading '/' or the |
46 |
|
|
* force flag is set. |
47 |
|
|
*/ |
48 |
|
|
if (F_ISSET(sp->ep, F_MODIFIED) && |
49 |
|
|
!FL_ISSET(cmdp->iflags, E_C_FORCE) && sp->frp->name[0] != '/') { |
50 |
|
|
msgq(sp, M_ERR, |
51 |
|
|
"File modified since last complete write; write or use ! to override"); |
52 |
|
|
return (1); |
53 |
|
|
} |
54 |
|
|
|
55 |
|
|
switch (cmdp->argc) { |
56 |
|
|
case 0: |
57 |
|
|
/* If no argument, change to the user's home directory. */ |
58 |
|
|
if ((dir = getenv("HOME")) == NULL || *dir == '\0') { |
59 |
|
|
if ((pw = getpwuid(getuid())) == NULL || |
60 |
|
|
pw->pw_dir == NULL || pw->pw_dir[0] == '\0') { |
61 |
|
|
msgq(sp, M_ERR, |
62 |
|
|
"Unable to find home directory location"); |
63 |
|
|
return (1); |
64 |
|
|
} |
65 |
|
|
dir = pw->pw_dir; |
66 |
|
|
} |
67 |
|
|
break; |
68 |
|
|
case 1: |
69 |
|
|
dir = cmdp->argv[0]->bp; |
70 |
|
|
break; |
71 |
|
|
default: |
72 |
|
|
abort(); |
73 |
|
|
} |
74 |
|
|
|
75 |
|
|
/* |
76 |
|
|
* Try the current directory first. If this succeeds, don't display |
77 |
|
|
* a message, vi didn't historically, and it should be obvious to the |
78 |
|
|
* user where they are. |
79 |
|
|
*/ |
80 |
|
|
if (!chdir(dir)) |
81 |
|
|
return (0); |
82 |
|
|
|
83 |
|
|
/* |
84 |
|
|
* If moving to the user's home directory, or, the path begins with |
85 |
|
|
* "/", "./" or "../", it's the only place we try. |
86 |
|
|
*/ |
87 |
|
|
if (cmdp->argc == 0 || |
88 |
|
|
(ap = cmdp->argv[0])->bp[0] == '/' || |
89 |
|
|
(ap->len == 1 && ap->bp[0] == '.') || |
90 |
|
|
(ap->len >= 2 && ap->bp[0] == '.' && ap->bp[1] == '.' && |
91 |
|
|
(ap->bp[2] == '/' || ap->bp[2] == '\0'))) |
92 |
|
|
goto err; |
93 |
|
|
|
94 |
|
|
/* Try the O_CDPATH option values. */ |
95 |
|
|
for (p = t = O_STR(sp, O_CDPATH);; ++p) |
96 |
|
|
if (*p == '\0' || *p == ':') { |
97 |
|
|
/* |
98 |
|
|
* Empty strings specify ".". The only way to get an |
99 |
|
|
* empty string is a leading colon, colons in a row, |
100 |
|
|
* or a trailing colon. Or, to put it the other way, |
101 |
|
|
* if the length is 1 or less, then we're dealing with |
102 |
|
|
* ":XXX", "XXX::XXXX" , "XXX:", or "". Since we've |
103 |
|
|
* already tried dot, we ignore them all. |
104 |
|
|
*/ |
105 |
|
|
if (t < p - 1) { |
106 |
|
|
savech = *p; |
107 |
|
|
*p = '\0'; |
108 |
|
|
(void)snprintf(buf, |
109 |
|
|
sizeof(buf), "%s/%s", t, dir); |
110 |
|
|
*p = savech; |
111 |
|
|
if (!chdir(buf)) { |
112 |
|
|
if (getcwd(buf, sizeof(buf)) != NULL) |
113 |
|
|
msgq_str(sp, M_INFO, buf, "New current directory: %s"); |
114 |
|
|
return (0); |
115 |
|
|
} |
116 |
|
|
} |
117 |
|
|
t = p + 1; |
118 |
|
|
if (*p == '\0') |
119 |
|
|
break; |
120 |
|
|
} |
121 |
|
|
|
122 |
|
|
err: msgq_str(sp, M_SYSERR, dir, "%s"); |
123 |
|
|
return (1); |
124 |
|
|
} |