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