1/* getcwd.c -- get pathname of current directory */ 2 3/* Copyright (C) 1991 Free Software Foundation, Inc. 4 5 This file is part of GNU Bash, the Bourne Again SHell. 6 7 Bash is free software: you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation, either version 3 of the License, or 10 (at your option) any later version. 11 12 Bash is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Bash. If not, see <http://www.gnu.org/licenses/>. 19*/ 20 21#include <config.h> 22 23#if !defined (HAVE_GETCWD) 24 25#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX) 26 #pragma alloca 27#endif /* _AIX && RISC6000 && !__GNUC__ */ 28 29#if defined (__QNX__) 30# undef HAVE_LSTAT 31#endif 32 33#include <bashtypes.h> 34#include <errno.h> 35 36#if defined (HAVE_LIMITS_H) 37# include <limits.h> 38#endif 39 40#if defined (HAVE_UNISTD_H) 41# include <unistd.h> 42#endif 43 44#include <posixdir.h> 45#include <posixstat.h> 46#include <maxpath.h> 47#include <memalloc.h> 48 49#include <bashansi.h> 50 51#include <xmalloc.h> 52 53#if !defined (errno) 54extern int errno; 55#endif /* !errno */ 56 57#if !defined (HAVE_LSTAT) 58# define lstat stat 59#endif 60 61#if !defined (NULL) 62# define NULL 0 63#endif 64 65/* If the d_fileno member of a struct dirent doesn't return anything useful, 66 we need to check inode number equivalence the hard way. Return 1 if 67 the inode corresponding to PATH/DIR is identical to THISINO. */ 68#if defined (BROKEN_DIRENT_D_INO) 69static int 70_path_checkino (dotp, name, thisino) 71 char *dotp; 72 char *name; 73 ino_t thisino; 74{ 75 char *fullpath; 76 int r, e; 77 struct stat st; 78 79 e = errno; 80 fullpath = sh_makepath (dotp, name, MP_RMDOT); 81 if (stat (fullpath, &st) < 0) 82 { 83 errno = e; 84 return 0; 85 } 86 free (fullpath); 87 errno = e; 88 return (st.st_ino == thisino); 89} 90#endif 91 92/* Get the pathname of the current working directory, 93 and put it in SIZE bytes of BUF. Returns NULL if the 94 directory couldn't be determined or SIZE was too small. 95 If successful, returns BUF. In GNU, if BUF is NULL, 96 an array is allocated with `malloc'; the array is SIZE 97 bytes long, unless SIZE <= 0, in which case it is as 98 big as necessary. */ 99#if defined (__STDC__) 100char * 101getcwd (char *buf, size_t size) 102#else /* !__STDC__ */ 103char * 104getcwd (buf, size) 105 char *buf; 106 size_t size; 107#endif /* !__STDC__ */ 108{ 109 static const char dots[] 110 = "../../../../../../../../../../../../../../../../../../../../../../../\ 111../../../../../../../../../../../../../../../../../../../../../../../../../../\ 112../../../../../../../../../../../../../../../../../../../../../../../../../.."; 113 const char *dotp, *dotlist; 114 size_t dotsize; 115 dev_t rootdev, thisdev; 116 ino_t rootino, thisino; 117 char path[PATH_MAX + 1]; 118 register char *pathp; 119 char *pathbuf; 120 size_t pathsize; 121 struct stat st; 122 int saved_errno; 123 124 if (buf != NULL && size == 0) 125 { 126 errno = EINVAL; 127 return ((char *)NULL); 128 } 129 130 pathsize = sizeof (path); 131 pathp = &path[pathsize]; 132 *--pathp = '\0'; 133 pathbuf = path; 134 135 if (stat (".", &st) < 0) 136 return ((char *)NULL); 137 thisdev = st.st_dev; 138 thisino = st.st_ino; 139 140 if (stat ("/", &st) < 0) 141 return ((char *)NULL); 142 rootdev = st.st_dev; 143 rootino = st.st_ino; 144 145 saved_errno = 0; 146 147 dotsize = sizeof (dots) - 1; 148 dotp = &dots[sizeof (dots)]; 149 dotlist = dots; 150 while (!(thisdev == rootdev && thisino == rootino)) 151 { 152 register DIR *dirstream; 153 register struct dirent *d; 154 dev_t dotdev; 155 ino_t dotino; 156 char mount_point; 157 int namlen; 158 159 /* Look at the parent directory. */ 160 if (dotp == dotlist) 161 { 162 /* My, what a deep directory tree you have, Grandma. */ 163 char *new; 164 if (dotlist == dots) 165 { 166 new = (char *)malloc (dotsize * 2 + 1); 167 if (new == NULL) 168 goto lose; 169 memcpy (new, dots, dotsize); 170 } 171 else 172 { 173 new = (char *)realloc ((PTR_T) dotlist, dotsize * 2 + 1); 174 if (new == NULL) 175 goto lose; 176 } 177 memcpy (&new[dotsize], new, dotsize); 178 dotp = &new[dotsize]; 179 dotsize *= 2; 180 new[dotsize] = '\0'; 181 dotlist = new; 182 } 183 184 dotp -= 3; 185 186 /* Figure out if this directory is a mount point. */ 187 if (stat (dotp, &st) < 0) 188 goto lose; 189 dotdev = st.st_dev; 190 dotino = st.st_ino; 191 mount_point = dotdev != thisdev; 192 193 /* Search for the last directory. */ 194 dirstream = opendir (dotp); 195 if (dirstream == NULL) 196 goto lose; 197 while ((d = readdir (dirstream)) != NULL) 198 { 199 if (d->d_name[0] == '.' && 200 (d->d_name[1] == '\0' || 201 (d->d_name[1] == '.' && d->d_name[2] == '\0'))) 202 continue; 203#if !defined (BROKEN_DIRENT_D_INO) 204 if (mount_point || d->d_fileno == thisino) 205#else 206 if (mount_point || _path_checkino (dotp, d->d_name, thisino)) 207#endif 208 { 209 char *name; 210 211 namlen = D_NAMLEN(d); 212 name = (char *) 213 alloca (dotlist + dotsize - dotp + 1 + namlen + 1); 214 memcpy (name, dotp, dotlist + dotsize - dotp); 215 name[dotlist + dotsize - dotp] = '/'; 216 memcpy (&name[dotlist + dotsize - dotp + 1], 217 d->d_name, namlen + 1); 218 if (lstat (name, &st) < 0) 219 { 220#if 0 221 int save = errno; 222 (void) closedir (dirstream); 223 errno = save; 224 goto lose; 225#else 226 saved_errno = errno; 227#endif 228 } 229 if (st.st_dev == thisdev && st.st_ino == thisino) 230 break; 231 } 232 } 233 if (d == NULL) 234 { 235#if 0 236 int save = errno; 237#else 238 int save = errno ? errno : saved_errno; 239#endif 240 (void) closedir (dirstream); 241 errno = save; 242 goto lose; 243 } 244 else 245 { 246 size_t space; 247 248 while ((space = pathp - pathbuf) <= namlen) 249 { 250 char *new; 251 252 if (pathbuf == path) 253 { 254 new = (char *)malloc (pathsize * 2); 255 if (!new) 256 goto lose; 257 } 258 else 259 { 260 new = (char *)realloc ((PTR_T) pathbuf, (pathsize * 2)); 261 if (!new) 262 goto lose; 263 pathp = new + space; 264 } 265 (void) memcpy (new + pathsize + space, pathp, pathsize - space); 266 pathp = new + pathsize + space; 267 pathbuf = new; 268 pathsize *= 2; 269 } 270 271 pathp -= namlen; 272 (void) memcpy (pathp, d->d_name, namlen); 273 *--pathp = '/'; 274 (void) closedir (dirstream); 275 } 276 277 thisdev = dotdev; 278 thisino = dotino; 279 } 280 281 if (pathp == &path[sizeof(path) - 1]) 282 *--pathp = '/'; 283 284 if (dotlist != dots) 285 free ((PTR_T) dotlist); 286 287 { 288 size_t len = pathbuf + pathsize - pathp; 289 if (buf == NULL && size <= 0) 290 size = len; 291 292 if ((size_t) size < len) 293 { 294 errno = ERANGE; 295 goto lose2; 296 } 297 if (buf == NULL) 298 { 299 buf = (char *) malloc (size); 300 if (buf == NULL) 301 goto lose2; 302 } 303 304 (void) memcpy((PTR_T) buf, (PTR_T) pathp, len); 305 } 306 307 if (pathbuf != path) 308 free (pathbuf); 309 310 return (buf); 311 312 lose: 313 if ((dotlist != dots) && dotlist) 314 { 315 int e = errno; 316 free ((PTR_T) dotlist); 317 errno = e; 318 } 319 320 lose2: 321 if ((pathbuf != path) && pathbuf) 322 { 323 int e = errno; 324 free ((PTR_T) pathbuf); 325 errno = e; 326 } 327 return ((char *)NULL); 328} 329 330#if defined (TEST) 331# include <stdio.h> 332main (argc, argv) 333 int argc; 334 char **argv; 335{ 336 char b[PATH_MAX]; 337 338 if (getcwd(b, sizeof(b))) 339 { 340 printf ("%s\n", b); 341 exit (0); 342 } 343 else 344 { 345 perror ("cwd: getcwd"); 346 exit (1); 347 } 348} 349#endif /* TEST */ 350#endif /* !HAVE_GETCWD */ 351