Formerly posix/getcwd.c.~9~
[kopensolaris-gnu/glibc.git] / sysdeps / posix / getcwd.c
1 /* Copyright (C) 1991, 1992 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 #include <ansidecl.h>
20 #include <errno.h>
21 #include <limits.h>
22 #include <stddef.h>
23 #include <dirent.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30
31 /* Get the pathname of the current working directory,
32    and put it in SIZE bytes of BUF.  Returns NULL if the
33    directory couldn't be determined or SIZE was too small.
34    If successful, returns BUF.  In GNU, if BUF is NULL,
35    an array is allocated with `malloc'; the array is SIZE
36    bytes long, unless SIZE <= 0, in which case it is as
37    big as necessary.  */
38 char *
39 DEFUN(getcwd, (buf, size), char *buf AND size_t size)
40 {
41   static CONST char dots[]
42     = "../../../../../../../../../../../../../../../../../../../../../../../\
43 ../../../../../../../../../../../../../../../../../../../../../../../../../../\
44 ../../../../../../../../../../../../../../../../../../../../../../../../../..";
45   CONST char *dotp, *dotlist;
46   size_t dotsize;
47   dev_t rootdev, thisdev;
48   ino_t rootino, thisino;
49   char *path;
50   register char *pathp;
51   struct stat st;
52
53 if (size == 0)
54   {
55     if (buf != NULL)
56       {
57         errno = EINVAL;
58         return NULL;
59       }
60
61 #ifndef PATH_MAX
62 #define PATH_MAX 1024
63 #endif
64     size = PATH_MAX + 1;
65   }
66
67   if (buf != NULL)
68     path = buf;
69   else
70     {
71       path = malloc (size);
72       if (path == NULL)
73         return NULL;
74     }
75
76   pathp = path + size;
77   *--pathp = '\0';
78
79   if (stat (".", &st) < 0)
80     return NULL;
81   thisdev = st.st_dev;
82   thisino = st.st_ino;
83
84   if (stat ("/", &st) < 0)
85     return NULL;
86   rootdev = st.st_dev;
87   rootino = st.st_ino;
88
89   dotsize = sizeof (dots) - 1;
90   dotp = &dots[sizeof (dots)];
91   dotlist = dots;
92   while (!(thisdev == rootdev && thisino == rootino))
93     {
94       register DIR *dirstream;
95       register struct dirent *d;
96       dev_t dotdev;
97       ino_t dotino;
98       char mount_point;
99
100       /* Look at the parent directory.  */
101       if (dotp == dotlist)
102         {
103           /* My, what a deep directory tree you have, Grandma.  */
104           char *new;
105           if (dotlist == dots)
106             {
107               new = malloc (dotsize * 2 + 1);
108               if (new == NULL)
109                 return NULL;
110               memcpy (new, dots, dotsize);
111             }
112           else
113             {
114               new = realloc ((PTR) dotlist, dotsize * 2 + 1);
115               if (new == NULL)
116                 goto lose;
117             }
118           memcpy (&new[dotsize], new, dotsize);
119           dotp = &new[dotsize];
120           dotsize *= 2;
121           new[dotsize] = '\0';
122           dotlist = new;
123         }
124
125       dotp -= 3;
126
127       /* Figure out if this directory is a mount point.  */
128       if (stat (dotp, &st) < 0)
129         goto lose;
130       dotdev = st.st_dev;
131       dotino = st.st_ino;
132       mount_point = dotdev != thisdev;
133
134       /* Search for the last directory.  */
135       dirstream = opendir (dotp);
136       if (dirstream == NULL)
137         goto lose;
138       while ((d = readdir (dirstream)) != NULL)
139         {
140           if (d->d_name[0] == '.' &&
141               (d->d_namlen == 1 || (d->d_namlen == 2 && d->d_name[1] == '.')))
142             continue;
143           if (mount_point || d->d_fileno == thisino)
144             {
145               char *name = __alloca (dotlist + dotsize - dotp +
146                                      1 + d->d_namlen + 1);
147               memcpy (name, dotp, dotlist + dotsize - dotp);
148               name[dotlist + dotsize - dotp] = '/';
149               memcpy (&name[dotlist + dotsize - dotp + 1],
150                       d->d_name, d->d_namlen + 1);
151               if (stat (name, &st) < 0)
152                 {
153                   int save = errno;
154                   (void) closedir (dirstream);
155                   errno = save;
156                   goto lose;
157                 }
158               if (st.st_dev == thisdev && st.st_ino == thisino)
159                 break;
160             }
161         }
162       if (d == NULL)
163         {
164           int save = errno;
165           (void) closedir (dirstream);
166           errno = save;
167           goto lose;
168         }
169       else
170         {
171           if (pathp - path < d->d_namlen + 1)
172             {
173               if (buf != NULL)
174                 {
175                   errno = ERANGE;
176                   return NULL;
177                 }
178               else
179                 {
180                   size *= 2;
181                   buf = realloc (path, size);
182                   if (buf == NULL)
183                     {
184                       (void) closedir (dirstream);
185                       free (path);
186                       errno = ENOMEM; /* closedir might have changed it.  */
187                       return NULL;
188                     }
189                   pathp = &buf[pathp - path];
190                   path = buf;
191                 }
192             }
193           pathp -= d->d_namlen;
194           (void) closedir (dirstream);
195           (void) memcpy (pathp, d->d_name, d->d_namlen);
196           *--pathp = '/';
197           (void) closedir (dirstream);
198         }
199
200       thisdev = dotdev;
201       thisino = dotino;
202     }
203
204   if (pathp == &path[size - 1])
205     *--pathp = '/';
206
207   if (dotlist != dots)
208     free ((PTR) dotlist);
209
210   return memmove (path, pathp, path + size - pathp);
211
212  lose:
213   if (dotlist != dots)
214     free ((PTR) dotlist);
215   return NULL;
216 }