Some cleanups. Add branch prediction.
[glibc/history.git] / sysdeps / posix / getcwd.c
blobf793521340ef1e56cbce0695fc17315934e55bed
1 /* Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
4 The GNU C Library is free software; you can redistribute it and/or
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.
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
12 Lesser General Public License for more details.
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. */
19 /* Wants:
20 AC_STDC_HEADERS
21 AC_DIR_HEADER
22 AC_UNISTD_H
23 AC_MEMORY_H
24 AC_CONST
25 AC_ALLOCA
28 /* AIX requires this to be the first thing in the file. */
29 #if defined _AIX && !defined __GNUC__
30 #pragma alloca
31 #endif
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
37 #include <errno.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
41 #ifdef STDC_HEADERS
42 # include <stddef.h>
43 #endif
45 #if !defined __GNU_LIBRARY__ && !defined STDC_HEADERS
46 extern int errno;
47 #endif
48 #ifndef __set_errno
49 # define __set_errno(val) errno = (val)
50 #endif
52 #ifndef NULL
53 # define NULL 0
54 #endif
56 #if defined USGr3 && !defined DIRENT
57 # define DIRENT
58 #endif /* USGr3 */
59 #if defined Xenix && !defined SYSNDIR
60 # define SYSNDIR
61 #endif /* Xenix */
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
71 #else /* not POSIX or DIRENT */
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 */
84 #endif /* POSIX or DIRENT or __GNU_LIBRARY__ */
86 #if defined HAVE_UNISTD_H || defined __GNU_LIBRARY__
87 # include <unistd.h>
88 #endif
90 #if defined STDC_HEADERS || defined __GNU_LIBRARY__ || defined POSIX
91 # include <stdlib.h>
92 # include <string.h>
93 # define ANSI_STRING
94 #else /* No standard headers. */
96 # ifdef USG
98 # include <string.h>
99 # ifdef NEED_MEMORY_H
100 # include <memory.h>
101 # endif
102 # define ANSI_STRING
104 # else /* Not USG. */
106 # ifdef NeXT
108 # include <string.h>
110 # else /* Not NeXT. */
112 # include <strings.h>
114 # ifndef bcmp
115 extern int bcmp ();
116 # endif
117 # ifndef bzero
118 extern void bzero ();
119 # endif
120 # ifndef bcopy
121 extern void bcopy ();
122 # endif
124 # endif /* NeXT. */
126 # endif /* USG. */
128 extern char *malloc (), *realloc ();
129 extern void free ();
131 #endif /* Standard headers. */
133 #ifndef ANSI_STRING
134 # define memcpy(d, s, n) bcopy((s), (d), (n))
135 # define memmove memcpy
136 #endif /* Not ANSI_STRING. */
138 #ifndef MAX
139 # define MAX(a, b) ((a) < (b) ? (b) : (a))
140 #endif
142 #ifdef _LIBC
143 # ifndef mempcpy
144 # define mempcpy __mempcpy
145 # endif
146 # define HAVE_MEMPCPY 1
147 #endif
149 #if !defined __alloca && !defined __GNU_LIBRARY__
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
159 extern char *alloca ();
160 # endif /* Not _AIX. */
161 # endif /* sparc or HAVE_ALLOCA_H. */
162 # endif /* GCC. */
164 # define __alloca alloca
166 #endif
168 #if defined HAVE_LIMITS_H || defined STDC_HEADERS || defined __GNU_LIBRARY__
169 # include <limits.h>
170 #else
171 # include <sys/param.h>
172 #endif
174 #ifndef PATH_MAX
175 # ifdef MAXPATHLEN
176 # define PATH_MAX MAXPATHLEN
177 # else
178 # define PATH_MAX 1024
179 # endif
180 #endif
182 #if !defined STDC_HEADERS && !defined __GNU_LIBRARY__
183 # undef size_t
184 # define size_t unsigned int
185 #endif
187 #if !__STDC__ && !defined const
188 # define const
189 #endif
191 #ifndef __GNU_LIBRARY__
192 # define __lstat stat
193 #endif
195 #ifndef _LIBC
196 # define __getcwd getcwd
197 #endif
199 #ifndef GETCWD_RETURN_TYPE
200 # define GETCWD_RETURN_TYPE char *
201 #endif
203 /* Get the pathname of the current working directory, and put it in SIZE
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,
207 unless SIZE == 0, in which case it is as big as necessary. */
209 GETCWD_RETURN_TYPE
210 __getcwd (buf, size)
211 char *buf;
212 size_t size;
214 static const char dots[]
215 = "../../../../../../../../../../../../../../../../../../../../../../../\
216 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
217 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
218 const char *dotp = &dots[sizeof (dots)];
219 const char *dotlist = dots;
220 size_t dotsize = sizeof (dots) - 1;
221 dev_t rootdev, thisdev;
222 ino_t rootino, thisino;
223 char *path;
224 register char *pathp;
225 struct stat st;
226 int prev_errno = errno;
227 size_t allocated = size;
229 if (size == 0)
231 if (buf != NULL)
233 __set_errno (EINVAL);
234 return NULL;
237 allocated = PATH_MAX + 1;
240 if (buf != NULL)
241 path = buf;
242 else
244 path = malloc (allocated);
245 if (path == NULL)
246 return NULL;
249 pathp = path + allocated;
250 *--pathp = '\0';
252 if (__lstat (".", &st) < 0)
253 goto lose2;
254 thisdev = st.st_dev;
255 thisino = st.st_ino;
257 if (__lstat ("/", &st) < 0)
258 goto lose2;
259 rootdev = st.st_dev;
260 rootino = st.st_ino;
262 while (!(thisdev == rootdev && thisino == rootino))
264 register DIR *dirstream;
265 struct dirent *d;
266 dev_t dotdev;
267 ino_t dotino;
268 char mount_point;
270 /* Look at the parent directory. */
271 if (dotp == dotlist)
273 /* My, what a deep directory tree you have, Grandma. */
274 char *new;
275 if (dotlist == dots)
277 new = malloc (dotsize * 2 + 1);
278 if (new == NULL)
279 goto lose;
280 #ifdef HAVE_MEMPCPY
281 dotp = mempcpy (new, dots, dotsize);
282 #else
283 memcpy (new, dots, dotsize);
284 dotp = &new[dotsize];
285 #endif
287 else
289 new = realloc ((__ptr_t) dotlist, dotsize * 2 + 1);
290 if (new == NULL)
291 goto lose;
292 dotp = &new[dotsize];
294 #ifdef HAVE_MEMPCPY
295 *((char *) mempcpy ((char *) dotp, new, dotsize)) = '\0';
296 dotsize *= 2;
297 #else
298 memcpy ((char *) dotp, new, dotsize);
299 dotsize *= 2;
300 new[dotsize] = '\0';
301 #endif
302 dotlist = new;
305 dotp -= 3;
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;
314 /* Search for the last directory. */
315 dirstream = __opendir (dotp);
316 if (dirstream == NULL)
317 goto lose;
318 /* Clear errno to distinguish EOF from error if readdir returns
319 NULL. */
320 __set_errno (0);
321 while ((d = __readdir (dirstream)) != NULL)
323 if (d->d_name[0] == '.' &&
324 (d->d_name[1] == '\0' ||
325 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
326 continue;
327 if (mount_point || (ino_t) d->d_ino == thisino)
329 char name[dotlist + dotsize - dotp + 1 + _D_ALLOC_NAMLEN (d)];
330 #ifdef HAVE_MEMPCPY
331 char *tmp = mempcpy (name, dotp, dotlist + dotsize - dotp);
332 *tmp++ = '/';
333 strcpy (tmp, d->d_name);
334 #else
335 memcpy (name, dotp, dotlist + dotsize - dotp);
336 name[dotlist + dotsize - dotp] = '/';
337 strcpy (&name[dotlist + dotsize - dotp + 1], d->d_name);
338 #endif
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)
346 break;
349 if (d == NULL)
351 int save = errno;
352 (void) __closedir (dirstream);
353 if (save == 0)
354 /* EOF on dirstream, which means that the current directory
355 has been removed. */
356 save = ENOENT;
357 __set_errno (save);
358 goto lose;
360 else
362 size_t namlen = _D_EXACT_NAMLEN (d);
364 if ((size_t) (pathp - path) <= namlen)
366 if (size != 0)
368 (void) __closedir (dirstream);
369 __set_errno (ERANGE);
370 goto lose;
372 else
374 char *tmp;
375 size_t oldsize = allocated;
377 allocated = 2 * MAX (allocated, namlen);
378 tmp = realloc (path, allocated);
379 if (tmp == NULL)
381 (void) __closedir (dirstream);
382 __set_errno (ENOMEM);/* closedir might have changed it.*/
383 goto lose;
386 /* Move current contents up to the end of the buffer.
387 This is guaranteed to be non-overlapping. */
388 pathp = memcpy (tmp + allocated - (path + oldsize - pathp),
389 tmp + (pathp - path),
390 path + oldsize - pathp);
391 path = tmp;
394 pathp -= namlen;
395 (void) memcpy (pathp, d->d_name, namlen);
396 *--pathp = '/';
397 (void) __closedir (dirstream);
400 thisdev = dotdev;
401 thisino = dotino;
404 if (pathp == &path[allocated - 1])
405 *--pathp = '/';
407 if (dotlist != dots)
408 free ((__ptr_t) dotlist);
410 memmove (path, pathp, path + allocated - pathp);
412 /* Restore errno on successful return. */
413 __set_errno (prev_errno);
415 return path;
417 lose:
418 if (dotlist != dots)
419 free ((__ptr_t) dotlist);
420 lose2:
421 if (buf == NULL)
422 free (path);
423 return NULL;
426 #if defined _LIBC && !defined __getcwd
427 weak_alias (__getcwd, getcwd)
428 #endif