Mon Feb 26 10:22:30 1996 Roland McGrath <roland@charlie-brown.gnu.ai.mit.edu>
[kopensolaris-gnu/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
49 #ifndef NULL
50 #define NULL    0
51 #endif
52
53 #if defined (USGr3) && !defined (DIRENT)
54 #define DIRENT
55 #endif /* USGr3 */
56 #if defined (Xenix) && !defined (SYSNDIR)
57 #define SYSNDIR
58 #endif /* Xenix */
59
60 #if defined (POSIX) || defined (DIRENT) || defined (__GNU_LIBRARY__)
61 #include <dirent.h>
62 #ifndef __GNU_LIBRARY__
63 #define D_NAMLEN(d) strlen((d)->d_name)
64 #else
65 #define HAVE_D_NAMLEN
66 #define D_NAMLEN(d) ((d)->d_namlen)
67 #endif
68 #else /* not POSIX or DIRENT */
69 #define dirent          direct
70 #define D_NAMLEN(d)     ((d)->d_namlen)
71 #define HAVE_D_NAMLEN
72 #if defined (USG) && !defined (sgi)
73 #if defined (SYSNDIR)
74 #include <sys/ndir.h>
75 #else /* Not SYSNDIR */
76 #include "ndir.h"
77 #endif /* SYSNDIR */
78 #else /* not USG */
79 #include <sys/dir.h>
80 #endif /* USG */
81 #endif /* POSIX or DIRENT or __GNU_LIBRARY__ */
82
83 #if     defined (HAVE_UNISTD_H) || defined (__GNU_LIBRARY__)
84 #include <unistd.h>
85 #endif
86
87 #if     (defined (STDC_HEADERS) || defined (__GNU_LIBRARY__) \
88          || defined (POSIX))
89 #include <stdlib.h>
90 #include <string.h>
91 #define ANSI_STRING
92 #else   /* No standard headers.  */
93
94 #ifdef  USG
95
96 #include <string.h>
97 #ifdef  NEED_MEMORY_H
98 #include <memory.h>
99 #endif
100 #define ANSI_STRING
101
102 #else   /* Not USG.  */
103
104 #ifdef  NeXT
105
106 #include <string.h>
107
108 #else   /* Not NeXT.  */
109
110 #include <strings.h>
111
112 #ifndef bcmp
113 extern int bcmp ();
114 #endif
115 #ifndef bzero
116 extern void bzero ();
117 #endif
118 #ifndef bcopy
119 extern void bcopy ();
120 #endif
121
122 #endif  /* NeXT. */
123
124 #endif  /* USG.  */
125
126 extern char *malloc (), *realloc ();
127 extern void free ();
128
129 #endif /* Standard headers.  */
130
131 #ifndef ANSI_STRING
132 #define memcpy(d, s, n) bcopy((s), (d), (n))
133 #define memmove memcpy
134 #endif  /* Not ANSI_STRING.  */
135
136 #if     !defined(__alloca) && !defined(__GNU_LIBRARY__)
137
138 #ifdef  __GNUC__
139 #undef  alloca
140 #define alloca(n)       __builtin_alloca (n)
141 #else   /* Not GCC.  */
142 #if     defined (sparc) || defined (HAVE_ALLOCA_H)
143 #include <alloca.h>
144 #else   /* Not sparc or HAVE_ALLOCA_H.  */
145 #ifndef _AIX
146 extern char *alloca ();
147 #endif  /* Not _AIX.  */
148 #endif  /* sparc or HAVE_ALLOCA_H.  */
149 #endif  /* GCC.  */
150
151 #define __alloca        alloca
152
153 #endif
154
155 #if (defined (HAVE_LIMITS_H) || defined (STDC_HEADERS) || \
156      defined (__GNU_LIBRARY__))
157 #include <limits.h>
158 #else
159 #include <sys/param.h>
160 #endif
161
162 #ifndef PATH_MAX
163 #ifdef  MAXPATHLEN
164 #define PATH_MAX MAXPATHLEN
165 #else
166 #define PATH_MAX 1024
167 #endif
168 #endif
169
170 #if !defined (STDC_HEADERS) && !defined (__GNU_LIBRARY__)
171 #undef  size_t
172 #define size_t  unsigned int
173 #endif
174
175 #if !__STDC__ && !defined (const)
176 #define const
177 #endif
178
179 #ifndef __GNU_LIBRARY__
180 #define __lstat stat
181 #endif
182 \f
183 #ifndef _LIBC
184 #define __getcwd getcwd
185 #endif
186
187 /* Get the pathname of the current working directory, and put it in SIZE
188    bytes of BUF.  Returns NULL if the directory couldn't be determined or
189    SIZE was too small.  If successful, returns BUF.  In GNU, if BUF is
190    NULL, an array is allocated with `malloc'; the array is SIZE bytes long,
191    unless SIZE <= 0, in which case it is as big as necessary.  */
192
193 char *
194 __getcwd (buf, size)
195      char *buf;
196      size_t size;
197 {
198   static const char dots[]
199     = "../../../../../../../../../../../../../../../../../../../../../../../\
200 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
201 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
202   const char *dotp, *dotlist;
203   size_t dotsize;
204   dev_t rootdev, thisdev;
205   ino_t rootino, thisino;
206   char *path;
207   register char *pathp;
208   struct stat st;
209
210   if (size == 0)
211     {
212       if (buf != NULL)
213         {
214           errno = EINVAL;
215           return NULL;
216         }
217
218       size = PATH_MAX + 1;
219     }
220
221   if (buf != NULL)
222     path = buf;
223   else
224     {
225       path = malloc (size);
226       if (path == NULL)
227         return NULL;
228     }
229
230   pathp = path + size;
231   *--pathp = '\0';
232
233   if (__lstat (".", &st) < 0)
234     return NULL;
235   thisdev = st.st_dev;
236   thisino = st.st_ino;
237
238   if (__lstat ("/", &st) < 0)
239     return NULL;
240   rootdev = st.st_dev;
241   rootino = st.st_ino;
242
243   dotsize = sizeof (dots) - 1;
244   dotp = &dots[sizeof (dots)];
245   dotlist = dots;
246   while (!(thisdev == rootdev && thisino == rootino))
247     {
248       register DIR *dirstream;
249       register struct dirent *d;
250       dev_t dotdev;
251       ino_t dotino;
252       char mount_point;
253
254       /* Look at the parent directory.  */
255       if (dotp == dotlist)
256         {
257           /* My, what a deep directory tree you have, Grandma.  */
258           char *new;
259           if (dotlist == dots)
260             {
261               new = malloc (dotsize * 2 + 1);
262               if (new == NULL)
263                 return NULL;
264               memcpy (new, dots, dotsize);
265             }
266           else
267             {
268               new = realloc ((__ptr_t) dotlist, dotsize * 2 + 1);
269               if (new == NULL)
270                 goto lose;
271             }
272           memcpy (&new[dotsize], new, dotsize);
273           dotp = &new[dotsize];
274           dotsize *= 2;
275           new[dotsize] = '\0';
276           dotlist = new;
277         }
278
279       dotp -= 3;
280
281       /* Figure out if this directory is a mount point.  */
282       if (__lstat (dotp, &st) < 0)
283         goto lose;
284       dotdev = st.st_dev;
285       dotino = st.st_ino;
286       mount_point = dotdev != thisdev;
287
288       /* Search for the last directory.  */
289       dirstream = opendir (dotp);
290       if (dirstream == NULL)
291         goto lose;
292       while ((d = readdir (dirstream)) != NULL)
293         {
294           if (d->d_name[0] == '.' &&
295               (d->d_name[1] == '\0' ||
296                (d->d_name[1] == '.' && d->d_name[2] == '\0')))
297             continue;
298           if (mount_point || d->d_ino == thisino)
299             {
300               char name[dotlist + dotsize - dotp + 1 + _D_ALLOC_NAMLEN (d)];
301               memcpy (name, dotp, dotlist + dotsize - dotp);
302               name[dotlist + dotsize - dotp] = '/';
303               strcpy (&name[dotlist + dotsize - dotp + 1], d->d_name);
304               if (__lstat (name, &st) < 0)
305                 {
306                   int save = errno;
307                   (void) closedir (dirstream);
308                   errno = save;
309                   goto lose;
310                 }
311               if (st.st_dev == thisdev && st.st_ino == thisino)
312                 break;
313             }
314         }
315       if (d == NULL)
316         {
317           int save = errno;
318           (void) closedir (dirstream);
319           errno = save;
320           goto lose;
321         }
322       else
323         {
324           size_t namlen = _D_EXACT_NAMLEN (d);
325
326           if (pathp - path < namlen)
327             {
328               if (buf != NULL)
329                 {
330                   errno = ERANGE;
331                   return NULL;
332                 }
333               else
334                 {
335                   size *= 2;
336                   buf = realloc (path, size);
337                   if (buf == NULL)
338                     {
339                       (void) closedir (dirstream);
340                       free (path);
341                       errno = ENOMEM; /* closedir might have changed it.  */
342                       return NULL;
343                     }
344                   pathp = &buf[pathp - path];
345                   path = buf;
346                 }
347             }
348           pathp -= namlen;
349           (void) memcpy (pathp, d->d_name, namlen);
350           *--pathp = '/';
351           (void) closedir (dirstream);
352         }
353
354       thisdev = dotdev;
355       thisino = dotino;
356     }
357
358   if (pathp == &path[size - 1])
359     *--pathp = '/';
360
361   if (dotlist != dots)
362     free ((__ptr_t) dotlist);
363
364   memmove (path, pathp, path + size - pathp);
365   return path;
366
367  lose:
368   if (dotlist != dots)
369     free ((__ptr_t) dotlist);
370   return NULL;
371 }
372
373 #ifdef _LIBC
374 weak_alias (__getcwd, getcwd)
375 #endif