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