Merge from coreutils for getcwd and HP-UX 11.
[gnulib.git] / lib / getcwd.c
1 /* Copyright (C) 1991,92,93,94,95,96,97,98,99,2004 Free Software Foundation,
2    Inc.
3    This file is part of the GNU C Library.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License along
16    with this program; if not, write to the Free Software Foundation,
17    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18
19 #ifdef  HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #if !_LIBC
24 # include "getcwd.h"
25 #endif
26
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <stdbool.h>
31 #include <stddef.h>
32
33 #if HAVE_FCNTL_H
34 # include <fcntl.h> /* For AT_FDCWD on Solaris 9.  */
35 #endif
36
37 #ifndef __set_errno
38 # define __set_errno(val) (errno = (val))
39 #endif
40
41 #if HAVE_DIRENT_H || _LIBC
42 # include <dirent.h>
43 # ifndef _D_EXACT_NAMLEN
44 #  define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
45 # endif
46 #else
47 # define dirent direct
48 # if HAVE_SYS_NDIR_H
49 #  include <sys/ndir.h>
50 # endif
51 # if HAVE_SYS_DIR_H
52 #  include <sys/dir.h>
53 # endif
54 # if HAVE_NDIR_H
55 #  include <ndir.h>
56 # endif
57 #endif
58 #ifndef _D_EXACT_NAMLEN
59 # define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
60 #endif
61 #ifndef _D_ALLOC_NAMLEN
62 # define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
63 #endif
64
65 #if HAVE_UNISTD_H || _LIBC
66 # include <unistd.h>
67 #endif
68
69 #include <stdlib.h>
70 #include <string.h>
71
72 #if _LIBC
73 # ifndef mempcpy
74 #  define mempcpy __mempcpy
75 # endif
76 #else
77 # include "mempcpy.h"
78 #endif
79
80 #include <limits.h>
81
82 #ifndef MAX
83 # define MAX(a, b) ((a) < (b) ? (b) : (a))
84 #endif
85 #ifndef MIN
86 # define MIN(a, b) ((a) < (b) ? (a) : (b))
87 #endif
88
89 #ifndef PATH_MAX
90 # ifdef MAXPATHLEN
91 #  define PATH_MAX MAXPATHLEN
92 # else
93 #  define PATH_MAX 1024
94 # endif
95 #endif
96
97 #if D_INO_IN_DIRENT
98 # define MATCHING_INO(dp, ino) ((dp)->d_ino == (ino))
99 #else
100 # define MATCHING_INO(dp, ino) true
101 #endif
102
103 #if !_LIBC
104 # define __getcwd getcwd
105 # define __lstat lstat
106 # define __closedir closedir
107 # define __opendir opendir
108 # define __readdir readdir
109 #endif
110 \f
111 /* Get the pathname of the current working directory, and put it in SIZE
112    bytes of BUF.  Returns NULL if the directory couldn't be determined or
113    SIZE was too small.  If successful, returns BUF.  In GNU, if BUF is
114    NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
115    unless SIZE == 0, in which case it is as big as necessary.  */
116
117 char *
118 __getcwd (char *buf, size_t size)
119 {
120   /* Lengths of big file name components and entire file names, and a
121      deep level of file name nesting.  These numbers are not upper
122      bounds; they are merely large values suitable for initial
123      allocations, designed to be large enough for most real-world
124      uses.  */
125   enum
126     {
127       BIG_FILE_NAME_COMPONENT_LENGTH = 255,
128       BIG_FILE_NAME_LENGTH = MIN (4095, PATH_MAX - 1),
129       DEEP_NESTING = 100
130     };
131
132 #ifdef AT_FDCWD
133   int fd = AT_FDCWD;
134   bool fd_needs_closing = false;
135 #else
136   char dots[DEEP_NESTING * sizeof ".." + BIG_FILE_NAME_COMPONENT_LENGTH + 1];
137   char *dotlist = dots;
138   size_t dotsize = sizeof dots;
139   size_t dotlen = 0;
140 #endif
141   DIR *dirstream = NULL;
142   dev_t rootdev, thisdev;
143   ino_t rootino, thisino;
144   char *path;
145   register char *pathp;
146   struct stat st;
147   int prev_errno = errno;
148   size_t allocated = size;
149
150   if (size == 0)
151     {
152       if (buf != NULL)
153         {
154           __set_errno (EINVAL);
155           return NULL;
156         }
157
158       allocated = BIG_FILE_NAME_LENGTH + 1;
159     }
160
161   if (buf != NULL)
162     path = buf;
163   else
164     {
165       path = malloc (allocated);
166       if (path == NULL)
167         return NULL;
168     }
169
170   pathp = path + allocated;
171   *--pathp = '\0';
172
173   if (__lstat (".", &st) < 0)
174     goto lose;
175   thisdev = st.st_dev;
176   thisino = st.st_ino;
177
178   if (__lstat ("/", &st) < 0)
179     goto lose;
180   rootdev = st.st_dev;
181   rootino = st.st_ino;
182
183   while (!(thisdev == rootdev && thisino == rootino))
184     {
185       struct dirent *d;
186       dev_t dotdev;
187       ino_t dotino;
188       bool mount_point;
189       int parent_status;
190
191       /* Look at the parent directory.  */
192 #ifdef AT_FDCWD
193       fd = openat (fd, "..", O_RDONLY);
194       if (fd < 0)
195         goto lose;
196       fd_needs_closing = true;
197       parent_status = fstat (fd, &st);
198 #else
199       dotlist[dotlen++] = '.';
200       dotlist[dotlen++] = '.';
201       dotlist[dotlen] = '\0';
202       parent_status = __lstat (dotlist, &st);
203 #endif
204       if (parent_status != 0)
205         goto lose;
206
207       if (dirstream && __closedir (dirstream) != 0)
208         {
209           dirstream = NULL;
210           goto lose;
211         }
212
213       /* Figure out if this directory is a mount point.  */
214       dotdev = st.st_dev;
215       dotino = st.st_ino;
216       mount_point = dotdev != thisdev;
217
218       /* Search for the last directory.  */
219 #ifdef AT_FDCWD
220       dirstream = fdopendir (fd);
221       if (dirstream == NULL)
222         goto lose;
223       fd_needs_closing = false;
224 #else
225       dirstream = __opendir (dotlist);
226       if (dirstream == NULL)
227         goto lose;
228       dotlist[dotlen++] = '/';
229 #endif
230       /* Clear errno to distinguish EOF from error if readdir returns
231          NULL.  */
232       __set_errno (0);
233       while ((d = __readdir (dirstream)) != NULL)
234         {
235           if (d->d_name[0] == '.' &&
236               (d->d_name[1] == '\0' ||
237                (d->d_name[1] == '.' && d->d_name[2] == '\0')))
238             continue;
239           if (MATCHING_INO (d, thisino) || mount_point)
240             {
241               int entry_status;
242 #ifdef AT_FDCWD
243               entry_status = fstatat (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW);
244 #else
245               /* Compute size needed for this file name, or for the file
246                  name ".." in the same directory, whichever is larger.
247                  Room for ".." might be needed the next time through
248                  the outer loop.  */
249               size_t name_alloc = _D_ALLOC_NAMLEN (d);
250               size_t filesize = dotlen + MAX (sizeof "..", name_alloc);
251
252               if (filesize < dotlen)
253                 goto memory_exhausted;
254
255               if (dotsize < filesize)
256                 {
257                   /* My, what a deep directory tree you have, Grandma.  */
258                   size_t newsize = MAX (filesize, dotsize * 2);
259                   size_t i;
260                   if (newsize < dotsize)
261                     goto memory_exhausted;
262                   if (dotlist != dots)
263                     free (dotlist);
264                   dotlist = malloc (newsize);
265                   if (dotlist == NULL)
266                     goto lose;
267                   dotsize = newsize;
268
269                   i = 0;
270                   do
271                     {
272                       dotlist[i++] = '.';
273                       dotlist[i++] = '.';
274                       dotlist[i++] = '/';
275                     }
276                   while (i < dotlen);
277                 }
278
279               strcpy (dotlist + dotlen, d->d_name);
280               entry_status = __lstat (dotlist, &st);
281 #endif
282               /* We don't fail here if we cannot stat() a directory entry.
283                  This can happen when (network) file systems fail.  If this
284                  entry is in fact the one we are looking for we will find
285                  out soon as we reach the end of the directory without
286                  having found anything.  */
287               if (entry_status == 0 && S_ISDIR (st.st_mode)
288                   && st.st_dev == thisdev && st.st_ino == thisino)
289                 break;
290             }
291         }
292       if (d == NULL)
293         {
294           if (errno == 0)
295             /* EOF on dirstream, which means that the current directory
296                has been removed.  */
297             __set_errno (ENOENT);
298           goto lose;
299         }
300       else
301         {
302           size_t pathroom = pathp - path;
303           size_t namlen = _D_EXACT_NAMLEN (d);
304
305           if (pathroom <= namlen)
306             {
307               if (size != 0)
308                 {
309                   __set_errno (ERANGE);
310                   goto lose;
311                 }
312               else
313                 {
314                   char *tmp;
315                   size_t oldsize = allocated;
316
317                   allocated += MAX (allocated, namlen);
318                   if (allocated < oldsize
319                       || ! (tmp = realloc (path, allocated)))
320                     goto memory_exhausted;
321
322                   /* Move current contents up to the end of the buffer.
323                      This is guaranteed to be non-overlapping.  */
324                   pathp = memcpy (tmp + allocated - (oldsize - pathroom),
325                                   tmp + pathroom,
326                                   oldsize - pathroom);
327                   path = tmp;
328                 }
329             }
330           pathp -= namlen;
331           memcpy (pathp, d->d_name, namlen);
332           *--pathp = '/';
333         }
334
335       thisdev = dotdev;
336       thisino = dotino;
337     }
338
339   if (dirstream && __closedir (dirstream) != 0)
340     {
341       dirstream = NULL;
342       goto lose;
343     }
344
345   if (pathp == &path[allocated - 1])
346     *--pathp = '/';
347
348 #ifndef AT_FDCWD
349   if (dotlist != dots)
350     free (dotlist);
351 #endif
352
353   memmove (path, pathp, path + allocated - pathp);
354
355   /* Restore errno on successful return.  */
356   __set_errno (prev_errno);
357
358   return path;
359
360  memory_exhausted:
361   __set_errno (ENOMEM);
362  lose:
363   {
364     int save = errno;
365     if (dirstream)
366       __closedir (dirstream);
367 #ifdef AT_FDCWD
368     if (fd_needs_closing)
369       close (fd);
370 #else
371     if (dotlist != dots)
372       free (dotlist);
373 #endif
374     if (buf == NULL)
375       free (path);
376     __set_errno (save);
377   }
378   return NULL;
379 }
380
381 #ifdef weak_alias
382 weak_alias (__getcwd, getcwd)
383 #endif