121345Sdist /* 246483Sbostic * Copyright (c) 1989, 1991 The Regents of the University of California. 337209Sbostic * All rights reserved. 437209Sbostic * 542625Sbostic * %sccs.include.redist.c% 621345Sdist */ 79986Ssam 826565Sdonn #if defined(LIBC_SCCS) && !defined(lint) 9*46597Sdonn static char sccsid[] = "@(#)getcwd.c 5.10 (Berkeley) 02/23/91"; 1037209Sbostic #endif /* LIBC_SCCS and not lint */ 1121345Sdist 1210088Ssam #include <sys/param.h> 1310088Ssam #include <sys/stat.h> 1446483Sbostic #include <errno.h> 1537209Sbostic #include <dirent.h> 1646483Sbostic #include <stdio.h> 1746483Sbostic #include <stdlib.h> 1842396Sbostic #include <string.h> 19*46597Sdonn #include <unistd.h> 209986Ssam 2142396Sbostic #define ISDOT(dp) \ 2242396Sbostic (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ 2342396Sbostic dp->d_name[1] == '.' && dp->d_name[2] == '\0')) 2442396Sbostic 259986Ssam char * 2646483Sbostic getcwd(pt, size) 2746483Sbostic char *pt; 2846483Sbostic size_t size; 299986Ssam { 3042396Sbostic register struct dirent *dp; 3137209Sbostic register DIR *dir; 3246483Sbostic register dev_t dev; 3342396Sbostic register ino_t ino; 3437209Sbostic register int first; 3546483Sbostic register char *bpt, *bup; 3642396Sbostic struct stat s; 3746483Sbostic dev_t root_dev; 3837209Sbostic ino_t root_ino; 3946483Sbostic size_t ptsize, upsize; 4046483Sbostic int save_errno; 4146483Sbostic char *ept, *eup, *up; 429986Ssam 4346483Sbostic /* 4446483Sbostic * If no buffer specified by the user, allocate one as necessary. 4546483Sbostic * If a buffer is specified, the size has to be non-zero. The path 4646483Sbostic * is built from the end of the buffer backwards. 4746483Sbostic */ 4846483Sbostic if (pt) { 4946483Sbostic ptsize = 0; 5046483Sbostic if (!size) { 5146483Sbostic errno = EINVAL; 5246483Sbostic return((char *)NULL); 5346483Sbostic } 5446483Sbostic ept = pt + size; 5546483Sbostic } else { 5646483Sbostic if (!(pt = (char *)malloc(ptsize = 1024 - 4))) 5746483Sbostic return((char *)NULL); 5846483Sbostic ept = pt + ptsize; 5946483Sbostic } 6046483Sbostic bpt = ept - 1; 6146483Sbostic *ept = '\0'; 6246483Sbostic 6346483Sbostic /* 6446483Sbostic * Allocate bytes (1024 - malloc space) for the string of "../"'s. 6546483Sbostic * Should always be enough (it's 340 levels). If it's not, allocate 6646483Sbostic * as necessary. Special * case the first stat, it's ".", not "..". 6746483Sbostic */ 6846483Sbostic if (!(up = (char *)malloc(upsize = 1024 - 4))) 6937209Sbostic goto err; 7046483Sbostic eup = up + MAXPATHLEN; 7146483Sbostic bup = up; 7246483Sbostic up[0] = '.'; 7346483Sbostic up[1] = '\0'; 7446483Sbostic 7546483Sbostic /* Save root values, so know when to stop. */ 7646483Sbostic if (stat("/", &s)) 7746483Sbostic goto err; 7837209Sbostic root_dev = s.st_dev; 7937209Sbostic root_ino = s.st_ino; 8042396Sbostic 8146483Sbostic errno = 0; /* XXX readdir has no error return. */ 8242396Sbostic 8346483Sbostic for (first = 1;; first = 0) { 8446483Sbostic /* Stat the current level. */ 8546483Sbostic if (lstat(up, &s)) 8642396Sbostic goto err; 8742396Sbostic 8846483Sbostic /* Save current node values. */ 8942396Sbostic ino = s.st_ino; 9042396Sbostic dev = s.st_dev; 9142396Sbostic 9246483Sbostic /* Check for reaching root. */ 9342396Sbostic if (root_dev == dev && root_ino == ino) { 9446483Sbostic *--bpt = '/'; 9546483Sbostic /* 9646483Sbostic * It's unclear that it's a requirement to copy the 9746483Sbostic * path to the beginning of the buffer, but it's always 9846483Sbostic * been that way and stuff would probably break. 9946483Sbostic */ 10046483Sbostic (void)bcopy(bpt, pt, ept - bpt); 10146483Sbostic free(up); 10246483Sbostic return(pt); 10310155Ssam } 10442396Sbostic 10546483Sbostic /* 10646483Sbostic * Build pointer to the parent directory, allocating memory 10746483Sbostic * as necessary. Max length is 3 for "../", the largest 10846483Sbostic * possible component name, plus a trailing NULL. 10946483Sbostic */ 11046483Sbostic if (bup + 3 + MAXNAMLEN + 1 >= eup) { 11146483Sbostic if (!(up = (char *)realloc(up, upsize *= 2))) 11246483Sbostic goto err; 11346483Sbostic eup = up + upsize; 11446483Sbostic } 11546483Sbostic *bup++ = '.'; 11646483Sbostic *bup++ = '.'; 11746483Sbostic *bup = '\0'; 11842396Sbostic 11946483Sbostic /* Open and stat parent directory. */ 12046483Sbostic if (!(dir = opendir(up)) || fstat(dirfd(dir), &s)) 12140556Skarels goto err; 12242396Sbostic 12346483Sbostic /* Add trailing slash for next directory. */ 12446483Sbostic *bup++ = '/'; 12542396Sbostic 12642396Sbostic /* 12746483Sbostic * If it's a mount point, have to stat each element because 12842396Sbostic * the inode number in the directory is for the entry in the 12942396Sbostic * parent directory, not the inode number of the mounted file. 13042396Sbostic */ 13146483Sbostic save_errno = 0; 13242396Sbostic if (s.st_dev == dev) { 13346483Sbostic for (;;) { 13446483Sbostic if (!(dp = readdir(dir))) 13546483Sbostic goto notfound; 13642396Sbostic if (dp->d_fileno == ino) 13746483Sbostic break; 13846483Sbostic } 13946483Sbostic } else 14046483Sbostic for (;;) { 14146483Sbostic if (!(dp = readdir(dir))) 14246483Sbostic goto notfound; 14342396Sbostic if (ISDOT(dp)) 14442396Sbostic continue; 14546483Sbostic bcopy(dp->d_name, bup, dp->d_namlen + 1); 14646483Sbostic 14746483Sbostic /* Save the first error for later. */ 14842396Sbostic if (lstat(up, &s)) { 14946483Sbostic if (!save_errno) 15046483Sbostic save_errno = errno; 15142396Sbostic errno = 0; 15242396Sbostic continue; 15342396Sbostic } 15446483Sbostic if (s.st_dev == dev && s.st_ino == ino) 15542396Sbostic break; 15642396Sbostic } 15742396Sbostic 15846483Sbostic /* 15946483Sbostic * Check for length of the current name, preceding slash, 16046483Sbostic * leading slash. 16146483Sbostic */ 16246483Sbostic if (bpt - pt <= dp->d_namlen + (first ? 1 : 2)) { 16346483Sbostic size_t len, off; 16442396Sbostic 16546483Sbostic if (!ptsize) { 16646483Sbostic errno = ERANGE; 16740556Skarels goto err; 16840556Skarels } 16946483Sbostic off = bpt - pt; 17046483Sbostic len = ept - bpt; 17146483Sbostic if (!(pt = (char *)realloc(pt, ptsize *= 2))) 17246483Sbostic goto err; 17346483Sbostic bpt = pt + off; 17446483Sbostic ept = pt + ptsize; 17546483Sbostic (void)bcopy(bpt, ept - len, len); 17646483Sbostic bpt = ept - len; 17740556Skarels } 17846483Sbostic if (!first) 17946483Sbostic *--bpt = '/'; 18046483Sbostic bpt -= dp->d_namlen; 18146483Sbostic bcopy(dp->d_name, bpt, dp->d_namlen); 18246483Sbostic (void)closedir(dir); 18346483Sbostic 18446483Sbostic /* Truncate any file name. */ 18546483Sbostic *bup = '\0'; 1869986Ssam } 18746483Sbostic 18846483Sbostic notfound: 18946483Sbostic /* 19046483Sbostic * If readdir set errno, use it, not any saved error; otherwise, 19146483Sbostic * didn't find the current directory in its parent directory, set 19246483Sbostic * errno to ENOENT. 19346483Sbostic */ 19446483Sbostic if (!errno) 19546483Sbostic errno = save_errno ? save_errno : ENOENT; 19646483Sbostic /* FALLTHROUGH */ 19740556Skarels err: 19846483Sbostic if (ptsize) 19946483Sbostic free(pt); 20046483Sbostic free(up); 20146483Sbostic return((char *)NULL); 2029986Ssam } 203