121345Sdist /* 2*68218Sbostic * Copyright (c) 1989, 1991, 1993, 1995 361111Sbostic * The Regents of the University of California. All rights reserved. 437209Sbostic * 542625Sbostic * %sccs.include.redist.c% 621345Sdist */ 79986Ssam 826565Sdonn #if defined(LIBC_SCCS) && !defined(lint) 9*68218Sbostic static char sccsid[] = "@(#)getcwd.c 8.2 (Berkeley) 01/31/95"; 1037209Sbostic #endif /* LIBC_SCCS and not lint */ 1121345Sdist 1210088Ssam #include <sys/param.h> 1310088Ssam #include <sys/stat.h> 1454722Sbostic 1546483Sbostic #include <errno.h> 1637209Sbostic #include <dirent.h> 1746483Sbostic #include <stdio.h> 1846483Sbostic #include <stdlib.h> 1942396Sbostic #include <string.h> 2046597Sdonn #include <unistd.h> 219986Ssam 2242396Sbostic #define ISDOT(dp) \ 2342396Sbostic (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 2442396Sbostic dp->d_name[1] == '.' && dp->d_name[2] == '\0')) 2542396Sbostic 269986Ssam char * 2746483Sbostic getcwd(pt, size) 2846483Sbostic char *pt; 2946483Sbostic size_t size; 309986Ssam { 3142396Sbostic register struct dirent *dp; 3237209Sbostic register DIR *dir; 3346483Sbostic register dev_t dev; 3442396Sbostic register ino_t ino; 3537209Sbostic register int first; 3646483Sbostic register char *bpt, *bup; 3742396Sbostic struct stat s; 3846483Sbostic dev_t root_dev; 3937209Sbostic ino_t root_ino; 40*68218Sbostic size_t ptsize, pwdlen, upsize; 4146483Sbostic int save_errno; 42*68218Sbostic char *ept, *eup, *pwd, *up; 439986Ssam 44*68218Sbostic /* Check $PWD -- if it's right, it's fast. */ 45*68218Sbostic if ((pwd = getenv("PWD")) != NULL && !stat(pwd, &s)) { 46*68218Sbostic dev = s.st_dev; 47*68218Sbostic ino = s.st_ino; 48*68218Sbostic if (!stat(".", &s) && dev == s.st_dev && ino == s.st_ino) { 49*68218Sbostic pwdlen = strlen(pwd); 50*68218Sbostic if (size != 0) { 51*68218Sbostic if (pwdlen + 1 > size) { 52*68218Sbostic errno = ERANGE; 53*68218Sbostic return (NULL); 54*68218Sbostic } 55*68218Sbostic } else if ((pt = malloc(pwdlen + 1)) == NULL) 56*68218Sbostic return (NULL); 57*68218Sbostic memmove(pt, pwd, pwdlen); 58*68218Sbostic pwd[pwdlen] = '\0'; 59*68218Sbostic return (pt); 60*68218Sbostic } 61*68218Sbostic } 62*68218Sbostic 6346483Sbostic /* 6446483Sbostic * If no buffer specified by the user, allocate one as necessary. 6546483Sbostic * If a buffer is specified, the size has to be non-zero. The path 6646483Sbostic * is built from the end of the buffer backwards. 6746483Sbostic */ 6846483Sbostic if (pt) { 6946483Sbostic ptsize = 0; 7046483Sbostic if (!size) { 7146483Sbostic errno = EINVAL; 7254722Sbostic return (NULL); 7346483Sbostic } 7446483Sbostic ept = pt + size; 7546483Sbostic } else { 7654722Sbostic if ((pt = malloc(ptsize = 1024 - 4)) == NULL) 7754722Sbostic return (NULL); 7846483Sbostic ept = pt + ptsize; 7946483Sbostic } 8046483Sbostic bpt = ept - 1; 8146645Sbostic *bpt = '\0'; 8246483Sbostic 8346483Sbostic /* 8446483Sbostic * Allocate bytes (1024 - malloc space) for the string of "../"'s. 8546483Sbostic * Should always be enough (it's 340 levels). If it's not, allocate 86*68218Sbostic * as necessary. Special case the first stat, it's ".", not "..". 8746483Sbostic */ 8854722Sbostic if ((up = malloc(upsize = 1024 - 4)) == NULL) 8937209Sbostic goto err; 9046483Sbostic eup = up + MAXPATHLEN; 9146483Sbostic bup = up; 9246483Sbostic up[0] = '.'; 9346483Sbostic up[1] = '\0'; 9446483Sbostic 9546483Sbostic /* Save root values, so know when to stop. */ 9646483Sbostic if (stat("/", &s)) 9746483Sbostic goto err; 9837209Sbostic root_dev = s.st_dev; 9937209Sbostic root_ino = s.st_ino; 10042396Sbostic 10146483Sbostic errno = 0; /* XXX readdir has no error return. */ 10242396Sbostic 10346483Sbostic for (first = 1;; first = 0) { 10446483Sbostic /* Stat the current level. */ 10546483Sbostic if (lstat(up, &s)) 10642396Sbostic goto err; 10742396Sbostic 10846483Sbostic /* Save current node values. */ 10942396Sbostic ino = s.st_ino; 11042396Sbostic dev = s.st_dev; 11142396Sbostic 11246483Sbostic /* Check for reaching root. */ 11342396Sbostic if (root_dev == dev && root_ino == ino) { 11446483Sbostic *--bpt = '/'; 11546483Sbostic /* 11646483Sbostic * It's unclear that it's a requirement to copy the 11746483Sbostic * path to the beginning of the buffer, but it's always 11846483Sbostic * been that way and stuff would probably break. 11946483Sbostic */ 12046483Sbostic (void)bcopy(bpt, pt, ept - bpt); 12146483Sbostic free(up); 12254722Sbostic return (pt); 12310155Ssam } 12442396Sbostic 12546483Sbostic /* 12646483Sbostic * Build pointer to the parent directory, allocating memory 12746483Sbostic * as necessary. Max length is 3 for "../", the largest 12846483Sbostic * possible component name, plus a trailing NULL. 12946483Sbostic */ 13046483Sbostic if (bup + 3 + MAXNAMLEN + 1 >= eup) { 13154722Sbostic if ((up = realloc(up, upsize *= 2)) == NULL) 13246483Sbostic goto err; 13354722Sbostic bup = up; 13446483Sbostic eup = up + upsize; 13546483Sbostic } 13646483Sbostic *bup++ = '.'; 13746483Sbostic *bup++ = '.'; 13846483Sbostic *bup = '\0'; 13942396Sbostic 14046483Sbostic /* Open and stat parent directory. */ 14146483Sbostic if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) 14240556Skarels goto err; 14342396Sbostic 14446483Sbostic /* Add trailing slash for next directory. */ 14546483Sbostic *bup++ = '/'; 14642396Sbostic 14742396Sbostic /* 14846483Sbostic * If it's a mount point, have to stat each element because 14942396Sbostic * the inode number in the directory is for the entry in the 15042396Sbostic * parent directory, not the inode number of the mounted file. 15142396Sbostic */ 15246483Sbostic save_errno = 0; 15342396Sbostic if (s.st_dev == dev) { 15446483Sbostic for (;;) { 15546483Sbostic if (!(dp = readdir(dir))) 15646483Sbostic goto notfound; 15742396Sbostic if (dp->d_fileno == ino) 15846483Sbostic break; 15946483Sbostic } 16046483Sbostic } else 16146483Sbostic for (;;) { 16246483Sbostic if (!(dp = readdir(dir))) 16346483Sbostic goto notfound; 16442396Sbostic if (ISDOT(dp)) 16542396Sbostic continue; 16646483Sbostic bcopy(dp->d_name, bup, dp->d_namlen + 1); 16746483Sbostic 16846483Sbostic /* Save the first error for later. */ 16942396Sbostic if (lstat(up, &s)) { 17046483Sbostic if (!save_errno) 17146483Sbostic save_errno = errno; 17242396Sbostic errno = 0; 17342396Sbostic continue; 17442396Sbostic } 17546483Sbostic if (s.st_dev == dev && s.st_ino == ino) 17642396Sbostic break; 17742396Sbostic } 17842396Sbostic 17946483Sbostic /* 18046483Sbostic * Check for length of the current name, preceding slash, 18146483Sbostic * leading slash. 18246483Sbostic */ 18346483Sbostic if (bpt - pt <= dp->d_namlen + (first ? 1 : 2)) { 18446483Sbostic size_t len, off; 18542396Sbostic 18646483Sbostic if (!ptsize) { 18746483Sbostic errno = ERANGE; 18840556Skarels goto err; 18940556Skarels } 19046483Sbostic off = bpt - pt; 19146483Sbostic len = ept - bpt; 19254722Sbostic if ((pt = realloc(pt, ptsize *= 2)) == NULL) 19346483Sbostic goto err; 19446483Sbostic bpt = pt + off; 19546483Sbostic ept = pt + ptsize; 19646483Sbostic (void)bcopy(bpt, ept - len, len); 19746483Sbostic bpt = ept - len; 19840556Skarels } 19946483Sbostic if (!first) 20046483Sbostic *--bpt = '/'; 20146483Sbostic bpt -= dp->d_namlen; 20246483Sbostic bcopy(dp->d_name, bpt, dp->d_namlen); 20346483Sbostic (void)closedir(dir); 20446483Sbostic 20546483Sbostic /* Truncate any file name. */ 20646483Sbostic *bup = '\0'; 2079986Ssam } 20846483Sbostic 20946483Sbostic notfound: 21046483Sbostic /* 21146483Sbostic * If readdir set errno, use it, not any saved error; otherwise, 21246483Sbostic * didn't find the current directory in its parent directory, set 21346483Sbostic * errno to ENOENT. 21446483Sbostic */ 21546483Sbostic if (!errno) 21646483Sbostic errno = save_errno ? save_errno : ENOENT; 21746483Sbostic /* FALLTHROUGH */ 21840556Skarels err: 21946483Sbostic if (ptsize) 22046483Sbostic free(pt); 22146483Sbostic free(up); 22254722Sbostic return (NULL); 2239986Ssam } 224