Formerly posix/getcwd.c.~8~
[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   size_t pathlen;
52   struct stat st;
53
54 if (size == 0)
55   {
56     if (buf != NULL)
57       {
58         errno = EINVAL;
59         return NULL;
60       }
61
62 #ifndef PATH_MAX
63 #define PATH_MAX 1024
64 #endif
65     size = PATH_MAX + 1;
66   }
67
68   if (buf != NULL)
69     path = buf;
70   else
71     {
72       path = malloc (size);
73       if (path == NULL)
74         return NULL;
75     }
76
77   pathp = path + size;
78   *--pathp = '\0';
79
80   if (stat (".", &st) < 0)
81     return NULL;
82   thisdev = st.st_dev;
83   thisino = st.st_ino;
84
85   if (stat ("/", &st) < 0)
86     return NULL;
87   rootdev = st.st_dev;
88   rootino = st.st_ino;
89
90   dotsize = sizeof (dots) - 1;
91   dotp = &dots[sizeof (dots)];
92   dotlist = dots;
93   while (!(thisdev == rootdev && thisino == rootino))
94     {
95       register DIR *dirstream;
96       register struct dirent *d;
97       dev_t dotdev;
98       ino_t dotino;
99       char mount_point;
100
101       /* Look at the parent directory.  */
102       if (dotp == dotlist)
103         {
104           /* My, what a deep directory tree you have, Grandma.  */
105           char *new;
106           if (dotlist == dots)
107             {
108               new = malloc (dotsize * 2 + 1);
109               if (new == NULL)
110                 return NULL;
111               memcpy (new, dots, dotsize);
112             }
113           else
114             {
115               new = realloc ((PTR) dotlist, dotsize * 2 + 1);
116               if (new == NULL)
117                 goto lose;
118             }
119           memcpy (&new[dotsize], new, dotsize);
120           dotp = &new[dotsize];
121           dotsize *= 2;
122           new[dotsize] = '\0';
123           dotlist = new;
124         }
125
126       dotp -= 3;
127
128       /* Figure out if this directory is a mount point.  */
129       if (stat (dotp, &st) < 0)
130         goto lose;
131       dotdev = st.st_dev;
132       dotino = st.st_ino;
133       mount_point = dotdev != thisdev;
134
135       /* Search for the last directory.  */
136       dirstream = opendir (dotp);
137       if (dirstream == NULL)
138         goto lose;
139       while ((d = readdir (dirstream)) != NULL)
140         {
141           if (d->d_name[0] == '.' &&
142               (d->d_namlen == 1 || (d->d_namlen == 2 && d->d_name[1] == '.')))
143             continue;
144           if (mount_point || d->d_fileno == thisino)
145             {
146               char *name = __alloca (dotlist + dotsize - dotp +
147                                      1 + d->d_namlen + 1);
148               memcpy (name, dotp, dotlist + dotsize - dotp);
149               name[dotlist + dotsize - dotp] = '/';
150               memcpy (&name[dotlist + dotsize - dotp + 1],
151                       d->d_name, d->d_namlen + 1);
152               if (stat (name, &st) < 0)
153                 {
154                   int save = errno;
155                   (void) closedir (dirstream);
156                   errno = save;
157                   goto lose;
158                 }
159               if (st.st_dev == thisdev && st.st_ino == thisino)
160                 break;
161             }
162         }
163       if (d == NULL)
164         {
165           int save = errno;
166           (void) closedir (dirstream);
167           errno = save;
168           goto lose;
169         }
170       else
171         {
172           if (pathp - path < d->d_namlen + 1)
173             {
174               if (buf != NULL)
175                 {
176                   errno = ERANGE;
177                   return NULL;
178                 }
179               else
180                 {
181                   size *= 2;
182                   buf = realloc (path, size);
183                   if (buf == NULL)
184                     {
185                       (void) closedir (dirstream);
186                       free (path);
187                       errno = ENOMEM; /* closedir might have changed it.  */
188                       return NULL;
189                     }
190                   pathp = &buf[pathp - path];
191                   path = buf;
192                 }
193             }
194           pathp -= d->d_namlen;
195           (void) closedir (dirstream);
196           (void) memcpy (pathp, d->d_name, d->d_namlen);
197           *--pathp = '/';
198           (void) closedir (dirstream);
199         }
200
201       thisdev = dotdev;
202       thisino = dotino;
203     }
204
205   if (pathp == &path[size - 1])
206     *--pathp = '/';
207
208   if (dotlist != dots)
209     free ((PTR) dotlist);
210
211   return memmove (path, pathp, path + size - pathp);
212
213  lose:
214   if (dotlist != dots)
215     free ((PTR) dotlist);
216   return NULL;
217 }