~ubuntu-branches/ubuntu/gutsy/findutils/gutsy-proposed

« back to all changes in this revision

Viewing changes to gnulib/lib/getcwd.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Metzler
  • Date: 2005-07-04 11:37:37 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20050704113737-ll89ui8be35r0pir
Tags: 4.2.22-2
* Remove locatedb on purge. (Closes: #315343)
* revert regex-syntax back to emacs-re. (Closes: #315136) Future versions
  will allow to select this by commandline parameter.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#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
 
 
117
/* Get the name 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 *dir;
 
151
  register char *dirp;
 
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
  dir = getcwd (buf, size);
 
165
  if (dir || (errno != ERANGE && !is_ENAMETOOLONG (errno) && errno != ENOENT))
 
166
    return dir;
 
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
      dir = malloc (allocated);
 
183
      if (dir == NULL)
 
184
        return NULL;
 
185
    }
 
186
  else
 
187
    dir = buf;
 
188
 
 
189
  dirp = dir + allocated;
 
190
  *--dirp = '\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 dirroom = dirp - dir;
 
322
          size_t namlen = _D_EXACT_NAMLEN (d);
 
323
 
 
324
          if (dirroom <= 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 (dir, 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
                  dirp = memcpy (tmp + allocated - (oldsize - dirroom),
 
344
                                 tmp + dirroom,
 
345
                                 oldsize - dirroom);
 
346
                  dir = tmp;
 
347
                }
 
348
            }
 
349
          dirp -= namlen;
 
350
          memcpy (dirp, d->d_name, namlen);
 
351
          *--dirp = '/';
 
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 (dirp == &dir[allocated - 1])
 
365
    *--dirp = '/';
 
366
 
 
367
#ifndef AT_FDCWD
 
368
  if (dotlist != dots)
 
369
    free (dotlist);
 
370
#endif
 
371
 
 
372
  used = dir + allocated - dirp;
 
373
  memmove (dir, dirp, used);
 
374
 
 
375
  if (buf == NULL && size == 0)
 
376
    /* Ensure that the buffer is only as large as necessary.  */
 
377
    buf = realloc (dir, 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 = dir;
 
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 (dir);
 
402
    __set_errno (save);
 
403
  }
 
404
  return NULL;
 
405
}
 
406
 
 
407
#ifdef weak_alias
 
408
weak_alias (__getcwd, getcwd)
 
409
#endif