5f3f628fb58d38ee33a5d759b11d813c2ad930aa
[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
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, 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 /* Get the pathname of the current working directory, and put it in SIZE
191    bytes of BUF.  Returns NULL if the directory couldn't be determined or
192    SIZE was too small.  If successful, returns BUF.  In GNU, if BUF is
193    NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
194    unless SIZE <= 0, in which case it is as big as necessary.  */
195
196 char *
197 __getcwd (buf, size)
198      char *buf;
199      size_t size;
200 {
201   static const char dots[]
202     = "../../../../../../../../../../../../../../../../../../../../../../../\
203 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
204 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
205   const char *dotp, *dotlist;
206   size_t dotsize;
207   dev_t rootdev, thisdev;
208   ino_t rootino, thisino;
209   char *path;
210   register char *pathp;
211   struct stat st;
212
213   if (size == 0)
214     {
215       if (buf != NULL)
216         {
217           __set_errno (EINVAL);
218           return NULL;
219         }
220
221       size = PATH_MAX + 1;
222     }
223
224   if (buf != NULL)
225     path = buf;
226   else
227     {
228       path = malloc (size);
229       if (path == NULL)
230         return NULL;
231     }
232
233   pathp = path + size;
234   *--pathp = '\0';
235
236   if (__lstat (".", &st) < 0)
237     return NULL;
238   thisdev = st.st_dev;
239   thisino = st.st_ino;
240
241   if (__lstat ("/", &st) < 0)
242     return NULL;
243   rootdev = st.st_dev;
244   rootino = st.st_ino;
245
246   dotsize = sizeof (dots) - 1;
247   dotp = &dots[sizeof (dots)];
248   dotlist = dots;
249   while (!(thisdev == rootdev && thisino == rootino))
250     {
251       register DIR *dirstream;
252       register struct dirent *d;
253       dev_t dotdev;
254       ino_t dotino;
255       char mount_point;
256
257       /* Look at the parent directory.  */
258       if (dotp == dotlist)
259         {
260           /* My, what a deep directory tree you have, Grandma.  */
261           char *new;
262           if (dotlist == dots)
263             {
264               new = malloc (dotsize * 2 + 1);
265               if (new == NULL)
266                 return NULL;
267               memcpy (new, dots, dotsize);
268             }
269           else
270             {
271               new = realloc ((__ptr_t) dotlist, dotsize * 2 + 1);
272               if (new == NULL)
273                 goto lose;
274             }
275           memcpy (&new[dotsize], new, dotsize);
276           dotp = &new[dotsize];
277           dotsize *= 2;
278           new[dotsize] = '\0';
279           dotlist = new;
280         }
281
282       dotp -= 3;
283
284       /* Figure out if this directory is a mount point.  */
285       if (__lstat (dotp, &st) < 0)
286         goto lose;
287       dotdev = st.st_dev;
288       dotino = st.st_ino;
289       mount_point = dotdev != thisdev;
290
291       /* Search for the last directory.  */
292       dirstream = __opendir (dotp);
293       if (dirstream == NULL)
294         goto lose;
295       while ((d = __readdir (dirstream)) != NULL)
296         {
297           if (d->d_name[0] == '.' &&
298               (d->d_name[1] == '\0' ||
299                (d->d_name[1] == '.' && d->d_name[2] == '\0')))
300             continue;
301           if (mount_point || (ino_t) d->d_ino == thisino)
302             {
303               char name[dotlist + dotsize - dotp + 1 + _D_ALLOC_NAMLEN (d)];
304               memcpy (name, dotp, dotlist + dotsize - dotp);
305               name[dotlist + dotsize - dotp] = '/';
306               strcpy (&name[dotlist + dotsize - dotp + 1], d->d_name);
307               if (__lstat (name, &st) < 0)
308                 {
309                   int save = errno;
310                   (void) __closedir (dirstream);
311                   __set_errno (save);
312                   goto lose;
313                 }
314               if (st.st_dev == thisdev && st.st_ino == thisino)
315                 break;
316             }
317         }
318       if (d == NULL)
319         {
320           int save = errno;
321           (void) __closedir (dirstream);
322           __set_errno (save);
323           goto lose;
324         }
325       else
326         {
327           size_t namlen = _D_EXACT_NAMLEN (d);
328
329           if ((size_t) (pathp - path) < namlen)
330             {
331               if (buf != NULL)
332                 {
333                   __set_errno (ERANGE);
334                   return NULL;
335                 }
336               else
337                 {
338                   size *= 2;
339                   buf = realloc (path, size);
340                   if (buf == NULL)
341                     {
342                       (void) __closedir (dirstream);
343                       free (path);
344                       __set_errno (ENOMEM);/* closedir might have changed it.*/
345                       return NULL;
346                     }
347                   pathp = &buf[pathp - path + size / 2];
348                   path = buf;
349                   /* Move current contents up to the end of the buffer.
350                      This is guaranteed to be non-overlapping.  */
351                   memcpy (pathp, pathp - size / 2, path + size - pathp);
352                 }
353             }
354           pathp -= namlen;
355           (void) memcpy (pathp, d->d_name, namlen);
356           *--pathp = '/';
357           (void) __closedir (dirstream);
358         }
359
360       thisdev = dotdev;
361       thisino = dotino;
362     }
363
364   if (pathp == &path[size - 1])
365     *--pathp = '/';
366
367   if (dotlist != dots)
368     free ((__ptr_t) dotlist);
369
370   memmove (path, pathp, path + size - pathp);
371   return path;
372
373  lose:
374   if (dotlist != dots)
375     free ((__ptr_t) dotlist);
376   return NULL;
377 }
378
379 #ifdef _LIBC
380 weak_alias (__getcwd, getcwd)
381 #endif