improve of cmpl.
[bush.git] / lib / sh / getcwd.c
bloba19b6c7279ede901a703975ae3e2010de40d528e
1 /* getcwd.c -- get pathname of current directory */
3 /* Copyright (C) 1991 Free Software Foundation, Inc.
5 This file is part of GNU Bush, the Bourne Again SHell.
7 Bush 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.
12 Bush is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Bush. If not, see <http://www.gnu.org/licenses/>.
21 #include <config.h>
23 #if !defined (HAVE_GETCWD)
25 #if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
26 #pragma alloca
27 #endif /* _AIX && RISC6000 && !__GNUC__ */
29 #if defined (__QNX__)
30 # undef HAVE_LSTAT
31 #endif
33 #include <bushtypes.h>
34 #include <errno.h>
36 #if defined (HAVE_LIMITS_H)
37 # include <limits.h>
38 #endif
40 #if defined (HAVE_UNISTD_H)
41 # include <unistd.h>
42 #endif
44 #include <posixdir.h>
45 #include <posixstat.h>
46 #include <maxpath.h>
47 #include <memalloc.h>
49 #include <bushansi.h>
51 #if !defined (D_FILENO_AVAILABLE)
52 # include "lxrgmr/command.h"
53 # include "general.h"
54 # include "externs.h"
55 #endif
57 #include <xmalloc.h>
59 #if !defined (errno)
60 extern int errno;
61 #endif /* !errno */
63 #if !defined (HAVE_LSTAT)
64 # define lstat stat
65 #endif
67 #if !defined (NULL)
68 # define NULL 0
69 #endif
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 (D_FILENO_AVAILABLE)
75 static int
76 _path_checkino (dotp, name, thisino)
77 char *dotp;
78 char *name;
79 ino_t thisino;
81 char *fullpath;
82 int r, e;
83 struct stat st;
85 e = errno;
86 fullpath = sh_makepath (dotp, name, MP_RMDOT);
87 if (stat (fullpath, &st) < 0)
89 errno = e;
90 return 0;
92 free (fullpath);
93 errno = e;
94 return (st.st_ino == thisino);
96 #endif
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__ */
115 static const char dots[]
116 = "../../../../../../../../../../../../../../../../../../../../../../../\
117 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
118 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
119 const char *dotp, *dotlist;
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;
128 int saved_errno;
130 if (buf != NULL && size == 0)
132 errno = EINVAL;
133 return ((char *)NULL);
136 pathsize = sizeof (path);
137 pathp = &path[pathsize];
138 *--pathp = '\0';
139 pathbuf = path;
141 if (stat (".", &st) < 0)
142 return ((char *)NULL);
143 thisdev = st.st_dev;
144 thisino = st.st_ino;
146 if (stat ("/", &st) < 0)
147 return ((char *)NULL);
148 rootdev = st.st_dev;
149 rootino = st.st_ino;
151 saved_errno = 0;
153 dotsize = sizeof (dots) - 1;
154 dotp = &dots[sizeof (dots)];
155 dotlist = dots;
156 while (!(thisdev == rootdev && thisino == rootino))
158 register DIR *dirstream;
159 register struct dirent *d;
160 dev_t dotdev;
161 ino_t dotino;
162 char mount_point;
163 int namlen;
165 /* Look at the parent directory. */
166 if (dotp == dotlist)
168 /* My, what a deep directory tree you have, Grandma. */
169 char *new;
170 if (dotlist == dots)
172 new = (char *)malloc (dotsize * 2 + 1);
173 if (new == NULL)
174 goto lose;
175 memcpy (new, dots, dotsize);
177 else
179 new = (char *)realloc ((PTR_T) dotlist, dotsize * 2 + 1);
180 if (new == NULL)
181 goto lose;
183 memcpy (&new[dotsize], new, dotsize);
184 dotp = &new[dotsize];
185 dotsize *= 2;
186 new[dotsize] = '\0';
187 dotlist = new;
190 dotp -= 3;
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;
199 /* Search for the last directory. */
200 dirstream = opendir (dotp);
201 if (dirstream == NULL)
202 goto lose;
203 while ((d = readdir (dirstream)) != NULL)
205 if (d->d_name[0] == '.' &&
206 (d->d_name[1] == '\0' ||
207 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
208 continue;
209 #if defined (D_FILENO_AVAILABLE)
210 if (mount_point || d->d_fileno == thisino)
211 #else
212 if (mount_point || _path_checkino (dotp, d->d_name, thisino))
213 #endif
215 char *name;
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)
226 #if 0
227 int save = errno;
228 (void) closedir (dirstream);
229 errno = save;
230 goto lose;
231 #else
232 saved_errno = errno;
233 #endif
235 if (st.st_dev == thisdev && st.st_ino == thisino)
236 break;
239 if (d == NULL)
241 #if 0
242 int save = errno;
243 #else
244 int save = errno ? errno : saved_errno;
245 #endif
246 (void) closedir (dirstream);
247 errno = save;
248 goto lose;
250 else
252 size_t space;
254 while ((space = pathp - pathbuf) <= namlen)
256 char *new;
258 if (pathbuf == path)
260 new = (char *)malloc (pathsize * 2);
261 if (!new)
262 goto lose;
264 else
266 new = (char *)realloc ((PTR_T) pathbuf, (pathsize * 2));
267 if (!new)
268 goto lose;
269 pathp = new + space;
271 (void) memcpy (new + pathsize + space, pathp, pathsize - space);
272 pathp = new + pathsize + space;
273 pathbuf = new;
274 pathsize *= 2;
277 pathp -= namlen;
278 (void) memcpy (pathp, d->d_name, namlen);
279 *--pathp = '/';
280 (void) closedir (dirstream);
283 thisdev = dotdev;
284 thisino = dotino;
287 if (pathp == &path[sizeof(path) - 1])
288 *--pathp = '/';
290 if (dotlist != dots)
291 free ((PTR_T) dotlist);
294 size_t len = pathbuf + pathsize - pathp;
295 if (buf == NULL && size <= 0)
296 size = len;
298 if ((size_t) size < len)
300 errno = ERANGE;
301 goto lose2;
303 if (buf == NULL)
305 buf = (char *) malloc (size);
306 if (buf == NULL)
307 goto lose2;
310 (void) memcpy((PTR_T) buf, (PTR_T) pathp, len);
313 if (pathbuf != path)
314 free (pathbuf);
316 return (buf);
318 lose:
319 if ((dotlist != dots) && dotlist)
321 int e = errno;
322 free ((PTR_T) dotlist);
323 errno = e;
326 lose2:
327 if ((pathbuf != path) && pathbuf)
329 int e = errno;
330 free ((PTR_T) pathbuf);
331 errno = e;
333 return ((char *)NULL);
336 #if defined (TEST)
337 # include <stdio.h>
338 main (argc, argv)
339 int argc;
340 char **argv;
342 char b[PATH_MAX];
344 if (getcwd(b, sizeof(b)))
346 printf ("%s\n", b);
347 exit (0);
349 else
351 perror ("cwd: getcwd");
352 exit (1);
355 #endif /* TEST */
356 #endif /* !HAVE_GET