Initial revision
[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   dev_t rootdev, thisdev;
42   ino_t rootino, thisino;
43   char path[PATH_MAX + 1];
44   register char *pathp;
45   struct stat st;
46
47   if (buf != NULL && size == 0)
48     {
49       errno = EINVAL;
50       return NULL;
51     }
52
53   pathp = &path[sizeof(path)];
54   *--pathp = '\0';
55
56   if (stat(".", &st) < 0)
57     return NULL;
58   thisdev = st.st_dev;
59   thisino = st.st_ino;
60
61   if (stat("/", &st) < 0)
62     return NULL;
63   rootdev = st.st_dev;
64   rootino = st.st_ino;
65
66   while (!(thisdev == rootdev && thisino == rootino))
67     {
68       register DIR *dirstream;
69       register struct dirent *d;
70       dev_t dotdev;
71       ino_t dotino;
72       char mount_point;
73
74       /* Move up a directory.  */
75       if (chdir("..") < 0)
76         {
77           if (pathp != &path[sizeof(path) - 1])
78             {
79               /* Try to get back to the original directory.
80                  This is the only place where this is possible.  */
81               int save = errno;
82               (void) chdir (pathp);
83               errno = save;
84             }
85           return NULL;
86         }
87
88       /* Figure out if this directory is a mount point.  */
89       if (stat(".", &st) < 0)
90         return NULL;
91       dotdev = st.st_dev;
92       dotino = st.st_ino;
93       mount_point = dotdev != thisdev;
94
95       /* Search for the last directory.  */
96       dirstream = opendir(".");
97       if (dirstream == NULL)
98         return NULL;
99       while ((d = readdir(dirstream)) != NULL)
100         {
101           if (d->d_name[0] == '.' &&
102               (d->d_namlen == 1 || (d->d_namlen == 2 && d->d_name[1] == '.')))
103             continue;
104           if (mount_point || d->d_fileno == thisino)
105             {
106               if (stat(d->d_name, &st) < 0)
107                 {
108                   int save = errno;
109                   (void) closedir(dirstream);
110                   errno = save;
111                   return NULL;
112                 }
113               if (st.st_dev == thisdev && st.st_ino == thisino)
114                 break;
115             }
116         }
117       if (d == NULL)
118         {
119           int save = errno;
120           (void) closedir(dirstream);
121           errno = save;
122           return NULL;
123         }
124       else
125         {
126           pathp -= d->d_namlen;
127           (void) memcpy(pathp, d->d_name, d->d_namlen);
128           *--pathp = '/';
129           (void) closedir(dirstream);
130         }
131
132       thisdev = dotdev;
133       thisino = dotino;
134     }
135
136   if (pathp == &path[sizeof(path) - 1])
137     *--pathp = '/';
138
139   if (chdir(pathp) < 0)
140     return NULL;
141
142   {
143     size_t len = &path[sizeof(path)] - pathp;
144     if (buf == NULL)
145       {
146         if (len < (size_t) size)
147           len = size;
148         buf = (char *) malloc(len);
149         if (buf == NULL)
150           return NULL;
151       }
152     else if ((size_t) size < len)
153       {
154         errno = ERANGE;
155         return NULL;
156       }
157     (void) memcpy((PTR) buf, (PTR) pathp, len);
158   }
159
160   return buf;
161 }