ef4cb84191246e8ad95547fd49f19b3abc74fe27
[kopensolaris-gnu/glibc.git] / sysdeps / posix / getcwd.c
1 /* Copyright (C) 1991, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
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    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public
15    License along with the GNU C Library; see the file COPYING.LIB.  If not,
16    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17    Boston, MA 02111-1307, USA.  */
18
19 /* Wants:
20    AC_STDC_HEADERS
21    AC_DIR_HEADER
22    AC_UNISTD_H
23    AC_MEMORY_H
24    AC_CONST
25    AC_ALLOCA
26  */
27
28 /* AIX requires this to be the first thing in the file.  */
29 #if defined _AIX && !defined __GNUC__
30  #pragma alloca
31 #endif
32
33 #ifdef  HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 #include <errno.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40
41 #ifdef  STDC_HEADERS
42 # include <stddef.h>
43 #endif
44
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
51
52 #ifndef NULL
53 # define NULL   0
54 #endif
55
56 #if defined USGr3 && !defined DIRENT
57 # define DIRENT
58 #endif /* USGr3 */
59 #if defined Xenix && !defined SYSNDIR
60 # define SYSNDIR
61 #endif /* Xenix */
62
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__ */
85
86 #if defined HAVE_UNISTD_H || defined __GNU_LIBRARY__
87 # include <unistd.h>
88 #endif
89
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.  */
95
96 # ifdef USG
97
98 #  include <string.h>
99 #  ifdef NEED_MEMORY_H
100 #   include <memory.h>
101 #  endif
102 #  define       ANSI_STRING
103
104 # else  /* Not USG.  */
105
106 #  ifdef NeXT
107
108 #   include <string.h>
109
110 #  else /* Not NeXT.  */
111
112 #   include <strings.h>
113
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
123
124 #  endif /* NeXT. */
125
126 # endif /* USG.  */
127
128 extern char *malloc (), *realloc ();
129 extern void free ();
130
131 #endif /* Standard headers.  */
132
133 #ifndef ANSI_STRING
134 # define memcpy(d, s, n)        bcopy((s), (d), (n))
135 # define memmove memcpy
136 #endif  /* Not ANSI_STRING.  */
137
138 #ifdef _LIBC
139 # define mempcpy __mempcpy
140 # define HAVE_MEMPCPY   1
141 #endif
142
143 #if !defined __alloca && !defined __GNU_LIBRARY__
144
145 # ifdef __GNUC__
146 #  undef alloca
147 #  define alloca(n)     __builtin_alloca (n)
148 # else  /* Not GCC.  */
149 #  if   defined sparc || defined HAVE_ALLOCA_H
150 #   include <alloca.h>
151 #  else /* Not sparc or HAVE_ALLOCA_H.  */
152 #   ifndef _AIX
153 extern char *alloca ();
154 #   endif /* Not _AIX.  */
155 #  endif /* sparc or HAVE_ALLOCA_H.  */
156 # endif /* GCC.  */
157
158 # define __alloca       alloca
159
160 #endif
161
162 #if defined HAVE_LIMITS_H || defined STDC_HEADERS || defined __GNU_LIBRARY__
163 # include <limits.h>
164 #else
165 # include <sys/param.h>
166 #endif
167
168 #ifndef PATH_MAX
169 # ifdef MAXPATHLEN
170 #  define PATH_MAX MAXPATHLEN
171 # else
172 #  define PATH_MAX 1024
173 # endif
174 #endif
175
176 #if !defined STDC_HEADERS && !defined __GNU_LIBRARY__
177 # undef size_t
178 # define size_t unsigned int
179 #endif
180
181 #if !__STDC__ && !defined const
182 # define const
183 #endif
184
185 #ifndef __GNU_LIBRARY__
186 # define __lstat        stat
187 #endif
188 \f
189 #ifndef _LIBC
190 # define __getcwd getcwd
191 #endif
192
193 #ifndef GETCWD_RETURN_TYPE
194 # define GETCWD_RETURN_TYPE char *
195 #endif
196
197 /* Get the pathname of the current working directory, and put it in SIZE
198    bytes of BUF.  Returns NULL if the directory couldn't be determined or
199    SIZE was too small.  If successful, returns BUF.  In GNU, if BUF is
200    NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
201    unless SIZE <= 0, in which case it is as big as necessary.  */
202
203 GETCWD_RETURN_TYPE
204 __getcwd (buf, size)
205      char *buf;
206      size_t size;
207 {
208   static const char dots[]
209     = "../../../../../../../../../../../../../../../../../../../../../../../\
210 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
211 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
212   const char *dotp, *dotlist;
213   size_t dotsize;
214   dev_t rootdev, thisdev;
215   ino_t rootino, thisino;
216   char *path;
217   register char *pathp;
218   struct stat st;
219   int prev_errno = errno;
220
221   if (size == 0)
222     {
223       if (buf != NULL)
224         {
225           __set_errno (EINVAL);
226           return NULL;
227         }
228
229       size = PATH_MAX + 1;
230     }
231
232   if (buf != NULL)
233     path = buf;
234   else
235     {
236       path = malloc (size);
237       if (path == NULL)
238         return NULL;
239     }
240
241   pathp = path + size;
242   *--pathp = '\0';
243
244   if (__lstat (".", &st) < 0)
245     return NULL;
246   thisdev = st.st_dev;
247   thisino = st.st_ino;
248
249   if (__lstat ("/", &st) < 0)
250     return NULL;
251   rootdev = st.st_dev;
252   rootino = st.st_ino;
253
254   dotsize = sizeof (dots) - 1;
255   dotp = &dots[sizeof (dots)];
256   dotlist = dots;
257   while (!(thisdev == rootdev && thisino == rootino))
258     {
259       register DIR *dirstream;
260       struct dirent *d;
261       dev_t dotdev;
262       ino_t dotino;
263       char mount_point;
264
265       /* Look at the parent directory.  */
266       if (dotp == dotlist)
267         {
268           /* My, what a deep directory tree you have, Grandma.  */
269           char *new;
270           if (dotlist == dots)
271             {
272               new = malloc (dotsize * 2 + 1);
273               if (new == NULL)
274                 return NULL;
275 #ifdef HAVE_MEMPCPY
276               dotp = mempcpy (new, dots, dotsize);
277 #else
278               memcpy (new, dots, dotsize);
279               dotp = &new[dotsize];
280 #endif
281             }
282           else
283             {
284               new = realloc ((__ptr_t) dotlist, dotsize * 2 + 1);
285               if (new == NULL)
286                 goto lose;
287               dotp = &new[dotsize];
288             }
289 #ifdef HAVE_MEMPCPY
290           *((char *) mempcpy ((char *) dotp, new, dotsize)) = '\0';
291           dotsize *= 2;
292 #else
293           memcpy ((char *) dotp, new, dotsize);
294           dotsize *= 2;
295           new[dotsize] = '\0';
296 #endif
297           dotlist = new;
298         }
299
300       dotp -= 3;
301
302       /* Figure out if this directory is a mount point.  */
303       if (__lstat (dotp, &st) < 0)
304         goto lose;
305       dotdev = st.st_dev;
306       dotino = st.st_ino;
307       mount_point = dotdev != thisdev;
308
309       /* Search for the last directory.  */
310       dirstream = __opendir (dotp);
311       if (dirstream == NULL)
312         goto lose;
313       /* Clear errno to distinguish EOF from error if readdir returns
314          NULL.  */
315       __set_errno (0);
316       while ((d = __readdir (dirstream)) != NULL)
317         {
318           if (d->d_name[0] == '.' &&
319               (d->d_name[1] == '\0' ||
320                (d->d_name[1] == '.' && d->d_name[2] == '\0')))
321             continue;
322           if (mount_point || (ino_t) d->d_ino == thisino)
323             {
324               char name[dotlist + dotsize - dotp + 1 + _D_ALLOC_NAMLEN (d)];
325 #ifdef HAVE_MEMPCPY
326               char *tmp = mempcpy (name, dotp, dotlist + dotsize - dotp);
327               *tmp++ = '/';
328               strcpy (tmp, d->d_name);
329 #else
330               memcpy (name, dotp, dotlist + dotsize - dotp);
331               name[dotlist + dotsize - dotp] = '/';
332               strcpy (&name[dotlist + dotsize - dotp + 1], d->d_name);
333 #endif
334               if (__lstat (name, &st) < 0)
335                 {
336                   int save = errno;
337                   (void) __closedir (dirstream);
338                   __set_errno (save);
339                   goto lose;
340                 }
341               if (st.st_dev == thisdev && st.st_ino == thisino)
342                 break;
343             }
344         }
345       if (d == NULL)
346         {
347           int save = errno;
348           (void) __closedir (dirstream);
349           if (save == 0)
350             /* EOF on dirstream, which means that the current directory
351                has been removed.  */
352             save = ENOENT;
353           __set_errno (save);
354           goto lose;
355         }
356       else
357         {
358           size_t namlen = _D_EXACT_NAMLEN (d);
359
360           if ((size_t) (pathp - path) <= namlen)
361             {
362               if (buf != NULL)
363                 {
364                   (void) __closedir (dirstream);
365                   __set_errno (ERANGE);
366                   goto lose;
367                 }
368               else
369                 {
370                   size *= 2;
371                   buf = realloc (path, size);
372                   if (buf == NULL)
373                     {
374                       (void) __closedir (dirstream);
375                       free (path);
376                       __set_errno (ENOMEM);/* closedir might have changed it.*/
377                       goto lose;
378                     }
379                   pathp = &buf[pathp - path + size / 2];
380                   path = buf;
381                   /* Move current contents up to the end of the buffer.
382                      This is guaranteed to be non-overlapping.  */
383                   memcpy (pathp, pathp - size / 2, path + size - pathp);
384                 }
385             }
386           pathp -= namlen;
387           (void) memcpy (pathp, d->d_name, namlen);
388           *--pathp = '/';
389           (void) __closedir (dirstream);
390         }
391
392       thisdev = dotdev;
393       thisino = dotino;
394     }
395
396   if (pathp == &path[size - 1])
397     *--pathp = '/';
398
399   if (dotlist != dots)
400     free ((__ptr_t) dotlist);
401
402   memmove (path, pathp, path + size - pathp);
403
404   /* Restore errno on successful return.  */
405   __set_errno (prev_errno);
406
407   return path;
408
409  lose:
410   if (dotlist != dots)
411     free ((__ptr_t) dotlist);
412   return NULL;
413 }
414
415 #if defined _LIBC && !defined __getcwd
416 weak_alias (__getcwd, getcwd)
417 #endif