update from main archive 961203
[platform/upstream/glibc.git] / sysdeps / posix / getcwd.c
1 /* Copyright (C) 1991, 92, 93, 94, 95, 96 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__) \
91          || defined (POSIX))
92 #include <stdlib.h>
93 #include <string.h>
94 #define ANSI_STRING
95 #else   /* No standard headers.  */
96
97 #ifdef  USG
98
99 #include <string.h>
100 #ifdef  NEED_MEMORY_H
101 #include <memory.h>
102 #endif
103 #define ANSI_STRING
104
105 #else   /* Not USG.  */
106
107 #ifdef  NeXT
108
109 #include <string.h>
110
111 #else   /* Not NeXT.  */
112
113 #include <strings.h>
114
115 #ifndef bcmp
116 extern int bcmp ();
117 #endif
118 #ifndef bzero
119 extern void bzero ();
120 #endif
121 #ifndef bcopy
122 extern void bcopy ();
123 #endif
124
125 #endif  /* NeXT. */
126
127 #endif  /* USG.  */
128
129 extern char *malloc (), *realloc ();
130 extern void free ();
131
132 #endif /* Standard headers.  */
133
134 #ifndef ANSI_STRING
135 #define memcpy(d, s, n) bcopy((s), (d), (n))
136 #define memmove memcpy
137 #endif  /* Not ANSI_STRING.  */
138
139 #if     !defined(__alloca) && !defined(__GNU_LIBRARY__)
140
141 #ifdef  __GNUC__
142 #undef  alloca
143 #define alloca(n)       __builtin_alloca (n)
144 #else   /* Not GCC.  */
145 #if     defined (sparc) || defined (HAVE_ALLOCA_H)
146 #include <alloca.h>
147 #else   /* Not sparc or HAVE_ALLOCA_H.  */
148 #ifndef _AIX
149 extern char *alloca ();
150 #endif  /* Not _AIX.  */
151 #endif  /* sparc or HAVE_ALLOCA_H.  */
152 #endif  /* GCC.  */
153
154 #define __alloca        alloca
155
156 #endif
157
158 #if (defined (HAVE_LIMITS_H) || defined (STDC_HEADERS) || \
159      defined (__GNU_LIBRARY__))
160 #include <limits.h>
161 #else
162 #include <sys/param.h>
163 #endif
164
165 #ifndef PATH_MAX
166 #ifdef  MAXPATHLEN
167 #define PATH_MAX MAXPATHLEN
168 #else
169 #define PATH_MAX 1024
170 #endif
171 #endif
172
173 #if !defined (STDC_HEADERS) && !defined (__GNU_LIBRARY__)
174 #undef  size_t
175 #define size_t  unsigned int
176 #endif
177
178 #if !__STDC__ && !defined (const)
179 #define const
180 #endif
181
182 #ifndef __GNU_LIBRARY__
183 #define __lstat stat
184 #endif
185 \f
186 #ifndef _LIBC
187 #define __getcwd getcwd
188 #endif
189
190 #if defined HAVE_READDIR_R && !defined _LIBC
191 #define __readdir_r readdir_r
192 #endif
193
194 /* Get the pathname of the current working directory, and put it in SIZE
195    bytes of BUF.  Returns NULL if the directory couldn't be determined or
196    SIZE was too small.  If successful, returns BUF.  In GNU, if BUF is
197    NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
198    unless SIZE <= 0, in which case it is as big as necessary.  */
199
200 char *
201 __getcwd (buf, size)
202      char *buf;
203      size_t size;
204 {
205   static const char dots[]
206     = "../../../../../../../../../../../../../../../../../../../../../../../\
207 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
208 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
209   const char *dotp, *dotlist;
210   size_t dotsize;
211   dev_t rootdev, thisdev;
212   ino_t rootino, thisino;
213   char *path;
214   register char *pathp;
215   struct stat st;
216
217   if (size == 0)
218     {
219       if (buf != NULL)
220         {
221           __set_errno (EINVAL);
222           return NULL;
223         }
224
225       size = PATH_MAX + 1;
226     }
227
228   if (buf != NULL)
229     path = buf;
230   else
231     {
232       path = malloc (size);
233       if (path == NULL)
234         return NULL;
235     }
236
237   pathp = path + size;
238   *--pathp = '\0';
239
240   if (__lstat (".", &st) < 0)
241     return NULL;
242   thisdev = st.st_dev;
243   thisino = st.st_ino;
244
245   if (__lstat ("/", &st) < 0)
246     return NULL;
247   rootdev = st.st_dev;
248   rootino = st.st_ino;
249
250   dotsize = sizeof (dots) - 1;
251   dotp = &dots[sizeof (dots)];
252   dotlist = dots;
253   while (!(thisdev == rootdev && thisino == rootino))
254     {
255       register DIR *dirstream;
256       register struct dirent *d;
257 #if defined HAVE_READDIR_R || defined _LIBC
258       struct dirent dirbuf;
259 #endif
260       dev_t dotdev;
261       ino_t dotino;
262       char mount_point;
263
264       /* Look at the parent directory.  */
265       if (dotp == dotlist)
266         {
267           /* My, what a deep directory tree you have, Grandma.  */
268           char *new;
269           if (dotlist == dots)
270             {
271               new = malloc (dotsize * 2 + 1);
272               if (new == NULL)
273                 return NULL;
274               memcpy (new, dots, dotsize);
275             }
276           else
277             {
278               new = realloc ((__ptr_t) dotlist, dotsize * 2 + 1);
279               if (new == NULL)
280                 goto lose;
281             }
282           memcpy (&new[dotsize], new, dotsize);
283           dotp = &new[dotsize];
284           dotsize *= 2;
285           new[dotsize] = '\0';
286           dotlist = new;
287         }
288
289       dotp -= 3;
290
291       /* Figure out if this directory is a mount point.  */
292       if (__lstat (dotp, &st) < 0)
293         goto lose;
294       dotdev = st.st_dev;
295       dotino = st.st_ino;
296       mount_point = dotdev != thisdev;
297
298       /* Search for the last directory.  */
299       dirstream = __opendir (dotp);
300       if (dirstream == NULL)
301         goto lose;
302       while (
303 #if defined HAVE_READDIR_R || defined _LIBC
304              __readdir_r (dirstream, &dirbuf, &d) >= 0
305 #else
306              (d = __readdir (dirstream)) != NULL
307 #endif
308              )
309         {
310           if (d->d_name[0] == '.' &&
311               (d->d_name[1] == '\0' ||
312                (d->d_name[1] == '.' && d->d_name[2] == '\0')))
313             continue;
314           if (mount_point || (ino_t) d->d_ino == thisino)
315             {
316               char name[dotlist + dotsize - dotp + 1 + _D_ALLOC_NAMLEN (d)];
317               memcpy (name, dotp, dotlist + dotsize - dotp);
318               name[dotlist + dotsize - dotp] = '/';
319               strcpy (&name[dotlist + dotsize - dotp + 1], d->d_name);
320               if (__lstat (name, &st) < 0)
321                 {
322                   int save = errno;
323                   (void) __closedir (dirstream);
324                   __set_errno (save);
325                   goto lose;
326                 }
327               if (st.st_dev == thisdev && st.st_ino == thisino)
328                 break;
329             }
330         }
331       if (d == NULL)
332         {
333           int save = errno;
334           (void) __closedir (dirstream);
335           __set_errno (save);
336           goto lose;
337         }
338       else
339         {
340           size_t namlen = _D_EXACT_NAMLEN (d);
341
342           if ((size_t) (pathp - path) < namlen)
343             {
344               if (buf != NULL)
345                 {
346                   __set_errno (ERANGE);
347                   return NULL;
348                 }
349               else
350                 {
351                   size *= 2;
352                   buf = realloc (path, size);
353                   if (buf == NULL)
354                     {
355                       (void) __closedir (dirstream);
356                       free (path);
357                       __set_errno (ENOMEM);/* closedir might have changed it.*/
358                       return NULL;
359                     }
360                   pathp = &buf[pathp - path + size / 2];
361                   path = buf;
362                   /* Move current contents up to the end of the buffer.
363                      This is guaranteed to be non-overlapping.  */
364                   memcpy (pathp, pathp - size / 2, path + size - pathp);
365                 }
366             }
367           pathp -= namlen;
368           (void) memcpy (pathp, d->d_name, namlen);
369           *--pathp = '/';
370           (void) __closedir (dirstream);
371         }
372
373       thisdev = dotdev;
374       thisino = dotino;
375     }
376
377   if (pathp == &path[size - 1])
378     *--pathp = '/';
379
380   if (dotlist != dots)
381     free ((__ptr_t) dotlist);
382
383   memmove (path, pathp, path + size - pathp);
384   return path;
385
386  lose:
387   if (dotlist != dots)
388     free ((__ptr_t) dotlist);
389   return NULL;
390 }
391
392 #ifdef _LIBC
393 weak_alias (__getcwd, getcwd)
394 #endif