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