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