]>
Commit | Line | Data |
---|---|---|
3185942a | 1 | /* getcwd.c -- get pathname of current directory */ |
726f6388 JA |
2 | |
3 | /* Copyright (C) 1991 Free Software Foundation, Inc. | |
726f6388 | 4 | |
3185942a | 5 | This file is part of GNU Bash, the Bourne Again SHell. |
726f6388 | 6 | |
3185942a JA |
7 | Bash is free software: you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation, either version 3 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | Bash is distributed in the hope that it will be useful, | |
726f6388 | 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
3185942a JA |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | GNU General Public License for more details. | |
726f6388 | 16 | |
3185942a JA |
17 | You should have received a copy of the GNU General Public License |
18 | along with Bash. If not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
726f6388 | 20 | |
cce855bc | 21 | #include <config.h> |
ccc6cda3 JA |
22 | |
23 | #if !defined (HAVE_GETCWD) | |
24 | ||
b80f6443 JA |
25 | #if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX) |
26 | #pragma alloca | |
27 | #endif /* _AIX && RISC6000 && !__GNUC__ */ | |
28 | ||
3185942a JA |
29 | #if defined (__QNX__) |
30 | # undef HAVE_LSTAT | |
31 | #endif | |
32 | ||
cce855bc | 33 | #include <bashtypes.h> |
726f6388 JA |
34 | #include <errno.h> |
35 | ||
36 | #if defined (HAVE_LIMITS_H) | |
37 | # include <limits.h> | |
38 | #endif | |
39 | ||
726f6388 JA |
40 | #if defined (HAVE_UNISTD_H) |
41 | # include <unistd.h> | |
42 | #endif | |
43 | ||
cce855bc JA |
44 | #include <posixdir.h> |
45 | #include <posixstat.h> | |
46 | #include <maxpath.h> | |
47 | #include <memalloc.h> | |
726f6388 | 48 | |
cce855bc | 49 | #include <bashansi.h> |
726f6388 | 50 | |
0001803f CR |
51 | #if defined (BROKEN_DIRENT_D_INO) |
52 | # include "command.h" | |
53 | # include "general.h" | |
54 | # include "externs.h" | |
55 | #endif | |
56 | ||
f73dda09 JA |
57 | #include <xmalloc.h> |
58 | ||
726f6388 JA |
59 | #if !defined (errno) |
60 | extern int errno; | |
61 | #endif /* !errno */ | |
62 | ||
ccc6cda3 | 63 | #if !defined (HAVE_LSTAT) |
726f6388 JA |
64 | # define lstat stat |
65 | #endif | |
66 | ||
bb70624e JA |
67 | #if !defined (NULL) |
68 | # define NULL 0 | |
69 | #endif | |
70 | ||
3185942a JA |
71 | /* If the d_fileno member of a struct dirent doesn't return anything useful, |
72 | we need to check inode number equivalence the hard way. Return 1 if | |
73 | the inode corresponding to PATH/DIR is identical to THISINO. */ | |
74 | #if defined (BROKEN_DIRENT_D_INO) | |
75 | static int | |
76 | _path_checkino (dotp, name, thisino) | |
77 | char *dotp; | |
78 | char *name; | |
79 | ino_t thisino; | |
80 | { | |
81 | char *fullpath; | |
82 | int r, e; | |
83 | struct stat st; | |
84 | ||
85 | e = errno; | |
86 | fullpath = sh_makepath (dotp, name, MP_RMDOT); | |
87 | if (stat (fullpath, &st) < 0) | |
88 | { | |
89 | errno = e; | |
90 | return 0; | |
91 | } | |
92 | free (fullpath); | |
93 | errno = e; | |
94 | return (st.st_ino == thisino); | |
95 | } | |
96 | #endif | |
97 | ||
726f6388 JA |
98 | /* Get the pathname of the current working directory, |
99 | and put it in SIZE bytes of BUF. Returns NULL if the | |
100 | directory couldn't be determined or SIZE was too small. | |
101 | If successful, returns BUF. In GNU, if BUF is NULL, | |
102 | an array is allocated with `malloc'; the array is SIZE | |
103 | bytes long, unless SIZE <= 0, in which case it is as | |
104 | big as necessary. */ | |
105 | #if defined (__STDC__) | |
106 | char * | |
107 | getcwd (char *buf, size_t size) | |
108 | #else /* !__STDC__ */ | |
109 | char * | |
110 | getcwd (buf, size) | |
111 | char *buf; | |
112 | size_t size; | |
113 | #endif /* !__STDC__ */ | |
114 | { | |
f73dda09 | 115 | static const char dots[] |
726f6388 JA |
116 | = "../../../../../../../../../../../../../../../../../../../../../../../\ |
117 | ../../../../../../../../../../../../../../../../../../../../../../../../../../\ | |
118 | ../../../../../../../../../../../../../../../../../../../../../../../../../.."; | |
f73dda09 | 119 | const char *dotp, *dotlist; |
726f6388 JA |
120 | size_t dotsize; |
121 | dev_t rootdev, thisdev; | |
122 | ino_t rootino, thisino; | |
123 | char path[PATH_MAX + 1]; | |
124 | register char *pathp; | |
125 | char *pathbuf; | |
126 | size_t pathsize; | |
127 | struct stat st; | |
cce855bc | 128 | int saved_errno; |
726f6388 JA |
129 | |
130 | if (buf != NULL && size == 0) | |
131 | { | |
132 | errno = EINVAL; | |
133 | return ((char *)NULL); | |
134 | } | |
135 | ||
136 | pathsize = sizeof (path); | |
137 | pathp = &path[pathsize]; | |
138 | *--pathp = '\0'; | |
139 | pathbuf = path; | |
140 | ||
141 | if (stat (".", &st) < 0) | |
142 | return ((char *)NULL); | |
143 | thisdev = st.st_dev; | |
144 | thisino = st.st_ino; | |
145 | ||
146 | if (stat ("/", &st) < 0) | |
147 | return ((char *)NULL); | |
148 | rootdev = st.st_dev; | |
149 | rootino = st.st_ino; | |
150 | ||
cce855bc JA |
151 | saved_errno = 0; |
152 | ||
726f6388 JA |
153 | dotsize = sizeof (dots) - 1; |
154 | dotp = &dots[sizeof (dots)]; | |
155 | dotlist = dots; | |
156 | while (!(thisdev == rootdev && thisino == rootino)) | |
157 | { | |
158 | register DIR *dirstream; | |
159 | register struct dirent *d; | |
160 | dev_t dotdev; | |
161 | ino_t dotino; | |
162 | char mount_point; | |
163 | int namlen; | |
164 | ||
165 | /* Look at the parent directory. */ | |
166 | if (dotp == dotlist) | |
167 | { | |
168 | /* My, what a deep directory tree you have, Grandma. */ | |
169 | char *new; | |
170 | if (dotlist == dots) | |
171 | { | |
bb70624e | 172 | new = (char *)malloc (dotsize * 2 + 1); |
726f6388 JA |
173 | if (new == NULL) |
174 | goto lose; | |
175 | memcpy (new, dots, dotsize); | |
176 | } | |
177 | else | |
178 | { | |
f73dda09 | 179 | new = (char *)realloc ((PTR_T) dotlist, dotsize * 2 + 1); |
726f6388 JA |
180 | if (new == NULL) |
181 | goto lose; | |
182 | } | |
183 | memcpy (&new[dotsize], new, dotsize); | |
184 | dotp = &new[dotsize]; | |
185 | dotsize *= 2; | |
186 | new[dotsize] = '\0'; | |
187 | dotlist = new; | |
188 | } | |
189 | ||
190 | dotp -= 3; | |
191 | ||
192 | /* Figure out if this directory is a mount point. */ | |
193 | if (stat (dotp, &st) < 0) | |
194 | goto lose; | |
195 | dotdev = st.st_dev; | |
196 | dotino = st.st_ino; | |
197 | mount_point = dotdev != thisdev; | |
198 | ||
199 | /* Search for the last directory. */ | |
200 | dirstream = opendir (dotp); | |
201 | if (dirstream == NULL) | |
202 | goto lose; | |
203 | while ((d = readdir (dirstream)) != NULL) | |
204 | { | |
205 | if (d->d_name[0] == '.' && | |
206 | (d->d_name[1] == '\0' || | |
207 | (d->d_name[1] == '.' && d->d_name[2] == '\0'))) | |
208 | continue; | |
3185942a | 209 | #if !defined (BROKEN_DIRENT_D_INO) |
726f6388 | 210 | if (mount_point || d->d_fileno == thisino) |
3185942a JA |
211 | #else |
212 | if (mount_point || _path_checkino (dotp, d->d_name, thisino)) | |
213 | #endif | |
726f6388 JA |
214 | { |
215 | char *name; | |
216 | ||
217 | namlen = D_NAMLEN(d); | |
218 | name = (char *) | |
219 | alloca (dotlist + dotsize - dotp + 1 + namlen + 1); | |
220 | memcpy (name, dotp, dotlist + dotsize - dotp); | |
221 | name[dotlist + dotsize - dotp] = '/'; | |
222 | memcpy (&name[dotlist + dotsize - dotp + 1], | |
223 | d->d_name, namlen + 1); | |
224 | if (lstat (name, &st) < 0) | |
225 | { | |
cce855bc | 226 | #if 0 |
726f6388 JA |
227 | int save = errno; |
228 | (void) closedir (dirstream); | |
229 | errno = save; | |
230 | goto lose; | |
cce855bc JA |
231 | #else |
232 | saved_errno = errno; | |
233 | #endif | |
726f6388 JA |
234 | } |
235 | if (st.st_dev == thisdev && st.st_ino == thisino) | |
236 | break; | |
237 | } | |
238 | } | |
239 | if (d == NULL) | |
240 | { | |
cce855bc | 241 | #if 0 |
726f6388 | 242 | int save = errno; |
cce855bc JA |
243 | #else |
244 | int save = errno ? errno : saved_errno; | |
245 | #endif | |
726f6388 JA |
246 | (void) closedir (dirstream); |
247 | errno = save; | |
248 | goto lose; | |
249 | } | |
250 | else | |
251 | { | |
252 | size_t space; | |
253 | ||
254 | while ((space = pathp - pathbuf) <= namlen) | |
255 | { | |
256 | char *new; | |
257 | ||
258 | if (pathbuf == path) | |
259 | { | |
bb70624e | 260 | new = (char *)malloc (pathsize * 2); |
726f6388 JA |
261 | if (!new) |
262 | goto lose; | |
263 | } | |
264 | else | |
265 | { | |
f73dda09 | 266 | new = (char *)realloc ((PTR_T) pathbuf, (pathsize * 2)); |
726f6388 JA |
267 | if (!new) |
268 | goto lose; | |
269 | pathp = new + space; | |
270 | } | |
271 | (void) memcpy (new + pathsize + space, pathp, pathsize - space); | |
272 | pathp = new + pathsize + space; | |
273 | pathbuf = new; | |
274 | pathsize *= 2; | |
275 | } | |
276 | ||
277 | pathp -= namlen; | |
278 | (void) memcpy (pathp, d->d_name, namlen); | |
279 | *--pathp = '/'; | |
280 | (void) closedir (dirstream); | |
281 | } | |
282 | ||
283 | thisdev = dotdev; | |
284 | thisino = dotino; | |
285 | } | |
286 | ||
287 | if (pathp == &path[sizeof(path) - 1]) | |
288 | *--pathp = '/'; | |
289 | ||
290 | if (dotlist != dots) | |
f73dda09 | 291 | free ((PTR_T) dotlist); |
726f6388 JA |
292 | |
293 | { | |
294 | size_t len = pathbuf + pathsize - pathp; | |
f1be666c JA |
295 | if (buf == NULL && size <= 0) |
296 | size = len; | |
297 | ||
298 | if ((size_t) size < len) | |
299 | { | |
300 | errno = ERANGE; | |
301 | goto lose2; | |
302 | } | |
726f6388 JA |
303 | if (buf == NULL) |
304 | { | |
f1be666c | 305 | buf = (char *) malloc (size); |
726f6388 JA |
306 | if (buf == NULL) |
307 | goto lose2; | |
308 | } | |
f1be666c | 309 | |
f73dda09 | 310 | (void) memcpy((PTR_T) buf, (PTR_T) pathp, len); |
726f6388 JA |
311 | } |
312 | ||
313 | if (pathbuf != path) | |
314 | free (pathbuf); | |
315 | ||
316 | return (buf); | |
317 | ||
318 | lose: | |
319 | if ((dotlist != dots) && dotlist) | |
320 | { | |
321 | int e = errno; | |
f73dda09 | 322 | free ((PTR_T) dotlist); |
726f6388 JA |
323 | errno = e; |
324 | } | |
325 | ||
326 | lose2: | |
327 | if ((pathbuf != path) && pathbuf) | |
328 | { | |
329 | int e = errno; | |
f73dda09 | 330 | free ((PTR_T) pathbuf); |
726f6388 JA |
331 | errno = e; |
332 | } | |
333 | return ((char *)NULL); | |
334 | } | |
335 | ||
336 | #if defined (TEST) | |
337 | # include <stdio.h> | |
338 | main (argc, argv) | |
339 | int argc; | |
340 | char **argv; | |
341 | { | |
342 | char b[PATH_MAX]; | |
343 | ||
344 | if (getcwd(b, sizeof(b))) | |
345 | { | |
346 | printf ("%s\n", b); | |
347 | exit (0); | |
348 | } | |
349 | else | |
350 | { | |
351 | perror ("cwd: getcwd"); | |
352 | exit (1); | |
353 | } | |
354 | } | |
355 | #endif /* TEST */ | |
ccc6cda3 | 356 | #endif /* !HAVE_GETCWD */ |