]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/posix/getcwd.c
Update to LGPL v2.1.
[thirdparty/glibc.git] / sysdeps / posix / getcwd.c
CommitLineData
475e390e 1/* Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc.
47707456 2 This file is part of the GNU C Library.
28f540f4 3
47707456 4 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
28f540f4 8
47707456
UD
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41bdb6e2 12 Lesser General Public License for more details.
28f540f4 13
41bdb6e2
AJ
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, write to the Free
16 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17 02111-1307 USA. */
28f540f4
RM
18
19/* Wants:
20 AC_STDC_HEADERS
21 AC_DIR_HEADER
22 AC_UNISTD_H
23 AC_MEMORY_H
24 AC_CONST
25 AC_ALLOCA
26 */
27
28/* AIX requires this to be the first thing in the file. */
86187531 29#if defined _AIX && !defined __GNUC__
28f540f4
RM
30 #pragma alloca
31#endif
32
33#ifdef HAVE_CONFIG_H
86187531 34# include "config.h"
28f540f4
RM
35#endif
36
37#include <errno.h>
38#include <sys/types.h>
39#include <sys/stat.h>
40
41#ifdef STDC_HEADERS
86187531 42# include <stddef.h>
28f540f4
RM
43#endif
44
86187531 45#if !defined __GNU_LIBRARY__ && !defined STDC_HEADERS
28f540f4
RM
46extern int errno;
47#endif
c4029823 48#ifndef __set_errno
86187531 49# define __set_errno(val) errno = (val)
c4029823 50#endif
28f540f4
RM
51
52#ifndef NULL
86187531 53# define NULL 0
28f540f4
RM
54#endif
55
86187531
UD
56#if defined USGr3 && !defined DIRENT
57# define DIRENT
28f540f4 58#endif /* USGr3 */
86187531
UD
59#if defined Xenix && !defined SYSNDIR
60# define SYSNDIR
28f540f4
RM
61#endif /* Xenix */
62
86187531
UD
63#if defined POSIX || defined DIRENT || defined __GNU_LIBRARY__
64# include <dirent.h>
65# ifndef __GNU_LIBRARY__
66# define D_NAMLEN(d) strlen((d)->d_name)
67# else
68# define HAVE_D_NAMLEN
69# define D_NAMLEN(d) ((d)->d_namlen)
70# endif
28f540f4 71#else /* not POSIX or DIRENT */
86187531
UD
72# define dirent direct
73# define D_NAMLEN(d) ((d)->d_namlen)
74# define HAVE_D_NAMLEN
75# if defined USG && !defined sgi
76# if defined SYSNDIR
77# include <sys/ndir.h>
78# else /* Not SYSNDIR */
79# include "ndir.h"
80# endif /* SYSNDIR */
81# else /* not USG */
82# include <sys/dir.h>
83# endif /* USG */
28f540f4
RM
84#endif /* POSIX or DIRENT or __GNU_LIBRARY__ */
85
86187531
UD
86#if defined HAVE_UNISTD_H || defined __GNU_LIBRARY__
87# include <unistd.h>
28f540f4
RM
88#endif
89
86187531
UD
90#if defined STDC_HEADERS || defined __GNU_LIBRARY__ || defined POSIX
91# include <stdlib.h>
92# include <string.h>
93# define ANSI_STRING
28f540f4
RM
94#else /* No standard headers. */
95
86187531 96# ifdef USG
28f540f4 97
86187531
UD
98# include <string.h>
99# ifdef NEED_MEMORY_H
100# include <memory.h>
101# endif
102# define ANSI_STRING
28f540f4 103
86187531 104# else /* Not USG. */
28f540f4 105
86187531 106# ifdef NeXT
28f540f4 107
86187531 108# include <string.h>
28f540f4 109
86187531 110# else /* Not NeXT. */
28f540f4 111
86187531 112# include <strings.h>
28f540f4 113
86187531 114# ifndef bcmp
28f540f4 115extern int bcmp ();
86187531
UD
116# endif
117# ifndef bzero
28f540f4 118extern void bzero ();
86187531
UD
119# endif
120# ifndef bcopy
28f540f4 121extern void bcopy ();
86187531 122# endif
28f540f4 123
86187531 124# endif /* NeXT. */
28f540f4 125
86187531 126# endif /* USG. */
28f540f4
RM
127
128extern char *malloc (), *realloc ();
129extern void free ();
130
131#endif /* Standard headers. */
132
133#ifndef ANSI_STRING
86187531
UD
134# define memcpy(d, s, n) bcopy((s), (d), (n))
135# define memmove memcpy
28f540f4
RM
136#endif /* Not ANSI_STRING. */
137
c9cddf51
UD
138#ifndef MAX
139# define MAX(a, b) ((a) < (b) ? (b) : (a))
140#endif
141
cc3fa755 142#ifdef _LIBC
50a1b837
UD
143# ifndef mempcpy
144# define mempcpy __mempcpy
145# endif
cc3fa755
UD
146# define HAVE_MEMPCPY 1
147#endif
148
86187531 149#if !defined __alloca && !defined __GNU_LIBRARY__
28f540f4 150
86187531
UD
151# ifdef __GNUC__
152# undef alloca
153# define alloca(n) __builtin_alloca (n)
154# else /* Not GCC. */
155# if defined sparc || defined HAVE_ALLOCA_H
156# include <alloca.h>
157# else /* Not sparc or HAVE_ALLOCA_H. */
158# ifndef _AIX
28f540f4 159extern char *alloca ();
86187531
UD
160# endif /* Not _AIX. */
161# endif /* sparc or HAVE_ALLOCA_H. */
162# endif /* GCC. */
28f540f4 163
86187531 164# define __alloca alloca
28f540f4
RM
165
166#endif
167
86187531
UD
168#if defined HAVE_LIMITS_H || defined STDC_HEADERS || defined __GNU_LIBRARY__
169# include <limits.h>
28f540f4 170#else
86187531 171# include <sys/param.h>
28f540f4
RM
172#endif
173
174#ifndef PATH_MAX
86187531
UD
175# ifdef MAXPATHLEN
176# define PATH_MAX MAXPATHLEN
177# else
178# define PATH_MAX 1024
179# endif
28f540f4
RM
180#endif
181
86187531
UD
182#if !defined STDC_HEADERS && !defined __GNU_LIBRARY__
183# undef size_t
184# define size_t unsigned int
28f540f4
RM
185#endif
186
86187531
UD
187#if !__STDC__ && !defined const
188# define const
28f540f4
RM
189#endif
190
191#ifndef __GNU_LIBRARY__
86187531 192# define __lstat stat
28f540f4
RM
193#endif
194\f
7f811679 195#ifndef _LIBC
86187531 196# define __getcwd getcwd
7f811679
RM
197#endif
198
04be94a8
UD
199#ifndef GETCWD_RETURN_TYPE
200# define GETCWD_RETURN_TYPE char *
fe0ec73e
UD
201#endif
202
7f811679 203/* Get the pathname of the current working directory, and put it in SIZE
28f540f4
RM
204 bytes of BUF. Returns NULL if the directory couldn't be determined or
205 SIZE was too small. If successful, returns BUF. In GNU, if BUF is
206 NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
a2b3aa73 207 unless SIZE == 0, in which case it is as big as necessary. */
28f540f4 208
04be94a8 209GETCWD_RETURN_TYPE
7f811679 210__getcwd (buf, size)
28f540f4
RM
211 char *buf;
212 size_t size;
213{
214 static const char dots[]
215 = "../../../../../../../../../../../../../../../../../../../../../../../\
216../../../../../../../../../../../../../../../../../../../../../../../../../../\
217../../../../../../../../../../../../../../../../../../../../../../../../../..";
475e390e
UD
218 const char *dotp = &dots[sizeof (dots)];
219 const char *dotlist = dots;
220 size_t dotsize = sizeof (dots) - 1;
28f540f4
RM
221 dev_t rootdev, thisdev;
222 ino_t rootino, thisino;
223 char *path;
224 register char *pathp;
225 struct stat st;
f4017d20 226 int prev_errno = errno;
1823e76b 227 size_t allocated = size;
28f540f4 228
a2b3aa73 229 if (size == 0)
28f540f4
RM
230 {
231 if (buf != NULL)
232 {
c4029823 233 __set_errno (EINVAL);
28f540f4
RM
234 return NULL;
235 }
236
1823e76b 237 allocated = PATH_MAX + 1;
28f540f4
RM
238 }
239
240 if (buf != NULL)
241 path = buf;
242 else
243 {
1823e76b 244 path = malloc (allocated);
28f540f4
RM
245 if (path == NULL)
246 return NULL;
247 }
248
1823e76b 249 pathp = path + allocated;
28f540f4
RM
250 *--pathp = '\0';
251
7f811679 252 if (__lstat (".", &st) < 0)
475e390e 253 goto lose2;
28f540f4
RM
254 thisdev = st.st_dev;
255 thisino = st.st_ino;
256
257 if (__lstat ("/", &st) < 0)
475e390e 258 goto lose2;
28f540f4
RM
259 rootdev = st.st_dev;
260 rootino = st.st_ino;
261
28f540f4
RM
262 while (!(thisdev == rootdev && thisino == rootino))
263 {
264 register DIR *dirstream;
a18f587d 265 struct dirent *d;
28f540f4
RM
266 dev_t dotdev;
267 ino_t dotino;
268 char mount_point;
269
270 /* Look at the parent directory. */
271 if (dotp == dotlist)
272 {
273 /* My, what a deep directory tree you have, Grandma. */
274 char *new;
275 if (dotlist == dots)
276 {
277 new = malloc (dotsize * 2 + 1);
278 if (new == NULL)
475e390e 279 goto lose;
86187531
UD
280#ifdef HAVE_MEMPCPY
281 dotp = mempcpy (new, dots, dotsize);
282#else
28f540f4 283 memcpy (new, dots, dotsize);
86187531
UD
284 dotp = &new[dotsize];
285#endif
28f540f4
RM
286 }
287 else
288 {
289 new = realloc ((__ptr_t) dotlist, dotsize * 2 + 1);
290 if (new == NULL)
291 goto lose;
86187531 292 dotp = &new[dotsize];
28f540f4 293 }
86187531 294#ifdef HAVE_MEMPCPY
cc3fa755 295 *((char *) mempcpy ((char *) dotp, new, dotsize)) = '\0';
86187531
UD
296 dotsize *= 2;
297#else
cc3fa755 298 memcpy ((char *) dotp, new, dotsize);
28f540f4
RM
299 dotsize *= 2;
300 new[dotsize] = '\0';
86187531 301#endif
28f540f4
RM
302 dotlist = new;
303 }
304
305 dotp -= 3;
306
307 /* Figure out if this directory is a mount point. */
308 if (__lstat (dotp, &st) < 0)
309 goto lose;
310 dotdev = st.st_dev;
311 dotino = st.st_ino;
312 mount_point = dotdev != thisdev;
313
314 /* Search for the last directory. */
23396375 315 dirstream = __opendir (dotp);
28f540f4
RM
316 if (dirstream == NULL)
317 goto lose;
f4017d20
UD
318 /* Clear errno to distinguish EOF from error if readdir returns
319 NULL. */
320 __set_errno (0);
10dc2a90 321 while ((d = __readdir (dirstream)) != NULL)
28f540f4
RM
322 {
323 if (d->d_name[0] == '.' &&
92777700
RM
324 (d->d_name[1] == '\0' ||
325 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
28f540f4 326 continue;
d68171ed 327 if (mount_point || (ino_t) d->d_ino == thisino)
28f540f4 328 {
92777700 329 char name[dotlist + dotsize - dotp + 1 + _D_ALLOC_NAMLEN (d)];
86187531
UD
330#ifdef HAVE_MEMPCPY
331 char *tmp = mempcpy (name, dotp, dotlist + dotsize - dotp);
332 *tmp++ = '/';
333 strcpy (tmp, d->d_name);
334#else
28f540f4
RM
335 memcpy (name, dotp, dotlist + dotsize - dotp);
336 name[dotlist + dotsize - dotp] = '/';
92777700 337 strcpy (&name[dotlist + dotsize - dotp + 1], d->d_name);
86187531 338#endif
74f7e7c0
UD
339 /* We don't fail here if we cannot stat() a directory entry.
340 This can happen when (network) filesystems fail. If this
341 entry is in fact the one we are looking for we will find
342 out soon as we reach the end of the directory without
343 having found anything. */
344 if (__lstat (name, &st) >= 0
345 && st.st_dev == thisdev && st.st_ino == thisino)
28f540f4
RM
346 break;
347 }
348 }
349 if (d == NULL)
350 {
351 int save = errno;
23396375 352 (void) __closedir (dirstream);
f4017d20
UD
353 if (save == 0)
354 /* EOF on dirstream, which means that the current directory
355 has been removed. */
356 save = ENOENT;
c4029823 357 __set_errno (save);
28f540f4
RM
358 goto lose;
359 }
360 else
361 {
92777700
RM
362 size_t namlen = _D_EXACT_NAMLEN (d);
363
9a0a462c 364 if ((size_t) (pathp - path) <= namlen)
28f540f4 365 {
a2b3aa73 366 if (size != 0)
28f540f4 367 {
7cc27f44 368 (void) __closedir (dirstream);
c4029823 369 __set_errno (ERANGE);
7cc27f44 370 goto lose;
28f540f4
RM
371 }
372 else
373 {
8325d82c 374 char *tmp;
1823e76b 375 size_t oldsize = allocated;
8325d82c 376
1823e76b
UD
377 allocated = 2 * MAX (allocated, namlen);
378 tmp = realloc (path, allocated);
8325d82c 379 if (tmp == NULL)
28f540f4 380 {
23396375 381 (void) __closedir (dirstream);
c4029823 382 __set_errno (ENOMEM);/* closedir might have changed it.*/
7cc27f44 383 goto lose;
28f540f4 384 }
1823e76b 385
8a523922
UD
386 /* Move current contents up to the end of the buffer.
387 This is guaranteed to be non-overlapping. */
1823e76b
UD
388 pathp = memcpy (tmp + allocated - (path + oldsize - pathp),
389 tmp + (pathp - path),
390 path + oldsize - pathp);
391 path = tmp;
28f540f4
RM
392 }
393 }
92777700
RM
394 pathp -= namlen;
395 (void) memcpy (pathp, d->d_name, namlen);
28f540f4 396 *--pathp = '/';
23396375 397 (void) __closedir (dirstream);
28f540f4
RM
398 }
399
400 thisdev = dotdev;
401 thisino = dotino;
402 }
403
1823e76b 404 if (pathp == &path[allocated - 1])
28f540f4
RM
405 *--pathp = '/';
406
407 if (dotlist != dots)
408 free ((__ptr_t) dotlist);
409
1823e76b 410 memmove (path, pathp, path + allocated - pathp);
f4017d20
UD
411
412 /* Restore errno on successful return. */
413 __set_errno (prev_errno);
414
28f540f4
RM
415 return path;
416
417 lose:
418 if (dotlist != dots)
419 free ((__ptr_t) dotlist);
475e390e
UD
420 lose2:
421 if (buf == NULL)
422 free (path);
28f540f4
RM
423 return NULL;
424}
3ec41e03 425
fe0ec73e 426#if defined _LIBC && !defined __getcwd
3ec41e03
RM
427weak_alias (__getcwd, getcwd)
428#endif