version 1.7, 1997/07/21 14:07:04 |
version 1.21, 1999/08/10 13:03:11 |
|
|
/* $NetBSD$ */ |
/* $NetBSD$ */ |
|
|
/* |
/* |
* Copyright (c) 1989, 1991, 1993 |
* Copyright (c) 1989, 1991, 1993, 1995 |
* The Regents of the University of California. All rights reserved. |
* The Regents of the University of California. All rights reserved. |
* |
* |
|
* This code is derived from software contributed to Berkeley by |
|
* Jan-Simon Pendry. |
|
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* modification, are permitted provided that the following conditions |
* are met: |
* are met: |
|
|
#include <sys/cdefs.h> |
#include <sys/cdefs.h> |
#if defined(LIBC_SCCS) && !defined(lint) |
#if defined(LIBC_SCCS) && !defined(lint) |
#if 0 |
#if 0 |
static char sccsid[] = "@(#)getcwd.c 8.1 (Berkeley) 6/4/93"; |
static char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 2/7/95"; |
#else |
#else |
__RCSID("$NetBSD$"); |
__RCSID("$NetBSD$"); |
#endif |
#endif |
Line 45 __RCSID("$NetBSD$"); |
|
Line 48 __RCSID("$NetBSD$"); |
|
#include "namespace.h" |
#include "namespace.h" |
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/stat.h> |
#include <sys/stat.h> |
#include <errno.h> |
|
#include <dirent.h> |
#include <dirent.h> |
|
#include <errno.h> |
|
#include <fcntl.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
#include <unistd.h> |
#include <unistd.h> |
|
|
|
#include "extern.h" |
|
|
#ifdef __weak_alias |
#ifdef __weak_alias |
__weak_alias(getcwd,_getcwd); |
__weak_alias(getcwd,_getcwd); |
|
__weak_alias(realpath,_realpath); |
#endif |
#endif |
|
|
#define ISDOT(dp) \ |
#define ISDOT(dp) \ |
(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ |
(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \ |
(dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) |
(dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) |
|
|
|
|
|
#if defined(__SVR4) || defined(__svr4__) |
|
#define d_fileno d_ino |
|
#endif |
|
|
|
/* |
|
* char *realpath(const char *path, char resolved_path[MAXPATHLEN]); |
|
* |
|
* Find the real name of path, by removing all ".", ".." and symlink |
|
* components. Returns (resolved) on success, or (NULL) on failure, |
|
* in which case the path which caused trouble is left in (resolved). |
|
*/ |
|
char * |
|
realpath(path, resolved) |
|
const char *path; |
|
char *resolved; |
|
{ |
|
struct stat sb; |
|
int fd, n, rootd, serrno, nlnk = 0; |
|
char *p, *q, wbuf[MAXPATHLEN]; |
|
|
|
/* Save the starting point. */ |
|
if ((fd = open(".", O_RDONLY)) < 0) { |
|
(void)strcpy(resolved, "."); |
|
return (NULL); |
|
} |
|
|
|
/* |
|
* Find the dirname and basename from the path to be resolved. |
|
* Change directory to the dirname component. |
|
* lstat the basename part. |
|
* if it is a symlink, read in the value and loop. |
|
* if it is a directory, then change to that directory. |
|
* get the current directory name and append the basename. |
|
*/ |
|
(void)strncpy(resolved, path, MAXPATHLEN - 1); |
|
resolved[MAXPATHLEN - 1] = '\0'; |
|
loop: |
|
q = strrchr(resolved, '/'); |
|
if (q != NULL) { |
|
p = q + 1; |
|
if (q == resolved) |
|
q = "/"; |
|
else { |
|
do { |
|
--q; |
|
} while (q > resolved && *q == '/'); |
|
q[1] = '\0'; |
|
q = resolved; |
|
} |
|
if (chdir(q) < 0) |
|
goto err1; |
|
} else |
|
p = resolved; |
|
|
|
/* Deal with the last component. */ |
|
if (lstat(p, &sb) == 0) { |
|
if (S_ISLNK(sb.st_mode)) { |
|
if (nlnk++ >= MAXSYMLINKS) { |
|
errno = ELOOP; |
|
goto err1; |
|
} |
|
n = readlink(p, resolved, MAXPATHLEN); |
|
if (n < 0) |
|
goto err1; |
|
resolved[n] = '\0'; |
|
goto loop; |
|
} |
|
if (S_ISDIR(sb.st_mode)) { |
|
if (chdir(p) < 0) |
|
goto err1; |
|
p = ""; |
|
} |
|
} |
|
|
|
/* |
|
* Save the last component name and get the full pathname of |
|
* the current directory. |
|
*/ |
|
(void)strncpy(wbuf, p, (sizeof(wbuf) - 1)); |
|
|
|
/* |
|
* Call the inernal internal version of getcwd which |
|
* does a physical search rather than using the $PWD short-cut |
|
*/ |
|
if (getcwd(resolved, MAXPATHLEN) == 0) |
|
goto err1; |
|
|
|
/* |
|
* Join the two strings together, ensuring that the right thing |
|
* happens if the last component is empty, or the dirname is root. |
|
*/ |
|
if (resolved[0] == '/' && resolved[1] == '\0') |
|
rootd = 1; |
|
else |
|
rootd = 0; |
|
|
|
if (*wbuf) { |
|
if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) { |
|
errno = ENAMETOOLONG; |
|
goto err1; |
|
} |
|
if (rootd == 0) |
|
(void)strcat(resolved, "/"); /* XXX: strcat is safe */ |
|
(void)strcat(resolved, wbuf); /* XXX: strcat is safe */ |
|
} |
|
|
|
/* Go back to where we came from. */ |
|
if (fchdir(fd) < 0) { |
|
serrno = errno; |
|
goto err2; |
|
} |
|
|
|
/* It's okay if the close fails, what's an fd more or less? */ |
|
(void)close(fd); |
|
return (resolved); |
|
|
|
err1: serrno = errno; |
|
(void)fchdir(fd); |
|
err2: (void)close(fd); |
|
errno = serrno; |
|
return (NULL); |
|
} |
|
|
|
#ifdef OLD_GETCWD |
|
|
char * |
char * |
getcwd(pt, size) |
getcwd(pt, size) |
char *pt; |
char *pt; |
size_t size; |
size_t size; |
{ |
{ |
register struct dirent *dp; |
struct dirent *dp; |
register DIR *dir; |
DIR *dir; |
register dev_t dev; |
dev_t dev; |
register ino_t ino; |
ino_t ino; |
register int first; |
int first; |
register char *bpt, *bup; |
char *bpt, *bup; |
struct stat s; |
struct stat s; |
dev_t root_dev; |
dev_t root_dev; |
ino_t root_ino; |
ino_t root_ino; |
size_t ptsize, upsize; |
size_t ptsize, upsize; |
int save_errno; |
int save_errno; |
char *ept, *eup, *up; |
char *ept, *eup, *up; |
|
size_t dlen; |
|
|
/* |
/* |
* If no buffer specified by the user, allocate one as necessary. |
* If no buffer specified by the user, allocate one as necessary. |
Line 101 getcwd(pt, size) |
|
Line 236 getcwd(pt, size) |
|
/* |
/* |
* Allocate bytes (1024 - malloc space) for the string of "../"'s. |
* Allocate bytes (1024 - malloc space) for the string of "../"'s. |
* Should always be enough (it's 340 levels). If it's not, allocate |
* Should always be enough (it's 340 levels). If it's not, allocate |
* as necessary. Special * case the first stat, it's ".", not "..". |
* as necessary. Special case the first stat, it's ".", not "..". |
*/ |
*/ |
if ((up = malloc(upsize = 1024 - 4)) == NULL) |
if ((up = malloc(upsize = 1024 - 4)) == NULL) |
goto err; |
goto err; |
Line 135 getcwd(pt, size) |
|
Line 270 getcwd(pt, size) |
|
* path to the beginning of the buffer, but it's always |
* path to the beginning of the buffer, but it's always |
* been that way and stuff would probably break. |
* been that way and stuff would probably break. |
*/ |
*/ |
bcopy(bpt, pt, ept - bpt); |
memmove(pt, bpt, (size_t)(ept - bpt)); |
free(up); |
free(up); |
return (pt); |
return (pt); |
} |
} |
Line 172 getcwd(pt, size) |
|
Line 307 getcwd(pt, size) |
|
for (;;) { |
for (;;) { |
if (!(dp = readdir(dir))) |
if (!(dp = readdir(dir))) |
goto notfound; |
goto notfound; |
if (dp->d_fileno == ino) |
if (dp->d_fileno == ino) { |
|
#if defined(__SVR4) || defined(__svr4__) || defined(__linux__) |
|
dlen = strlen(dp->d_name); |
|
#else |
|
dlen = dp->d_namlen; |
|
#endif |
break; |
break; |
|
} |
} |
} |
} else |
} else |
for (;;) { |
for (;;) { |
Line 181 getcwd(pt, size) |
|
Line 322 getcwd(pt, size) |
|
goto notfound; |
goto notfound; |
if (ISDOT(dp)) |
if (ISDOT(dp)) |
continue; |
continue; |
bcopy(dp->d_name, bup, dp->d_namlen + 1); |
#if defined(__SVR4) || defined(__svr4__) || defined(__linux__) |
|
dlen = strlen(dp->d_name); |
|
#else |
|
dlen = dp->d_namlen; |
|
#endif |
|
memmove(bup, dp->d_name, dlen + 1); |
|
|
/* Save the first error for later. */ |
/* Save the first error for later. */ |
if (lstat(up, &s)) { |
if (lstat(up, &s)) { |
Line 198 getcwd(pt, size) |
|
Line 344 getcwd(pt, size) |
|
* Check for length of the current name, preceding slash, |
* Check for length of the current name, preceding slash, |
* leading slash. |
* leading slash. |
*/ |
*/ |
if (bpt - pt <= dp->d_namlen + (first ? 1 : 2)) { |
if (bpt - pt <= dlen + (first ? 1 : 2)) { |
size_t len, off; |
size_t len, off; |
|
|
if (!ptsize) { |
if (!ptsize) { |
Line 211 getcwd(pt, size) |
|
Line 357 getcwd(pt, size) |
|
goto err; |
goto err; |
bpt = pt + off; |
bpt = pt + off; |
ept = pt + ptsize; |
ept = pt + ptsize; |
bcopy(bpt, ept - len, len); |
memmove(ept - len, bpt, len); |
bpt = ept - len; |
bpt = ept - len; |
} |
} |
if (!first) |
if (!first) |
*--bpt = '/'; |
*--bpt = '/'; |
bpt -= dp->d_namlen; |
bpt -= dlen; |
bcopy(dp->d_name, bpt, dp->d_namlen); |
memmove(bpt, dp->d_name, dlen); |
(void)closedir(dir); |
(void)closedir(dir); |
|
|
/* Truncate any file name. */ |
/* Truncate any file name. */ |
|
|
free(up); |
free(up); |
return (NULL); |
return (NULL); |
} |
} |
|
|
|
#else /* New getcwd */ |
|
|
|
char * |
|
getcwd(pt, size) |
|
char *pt; |
|
size_t size; |
|
{ |
|
size_t ptsize, bufsize; |
|
int len; |
|
|
|
/* |
|
* If no buffer specified by the user, allocate one as necessary. |
|
* If a buffer is specified, the size has to be non-zero. The path |
|
* is built from the end of the buffer backwards. |
|
*/ |
|
if (pt) { |
|
ptsize = 0; |
|
if (!size) { |
|
errno = EINVAL; |
|
return (NULL); |
|
} |
|
bufsize = size; |
|
} else { |
|
if ((pt = malloc(ptsize = 1024 - 4)) == NULL) |
|
return (NULL); |
|
bufsize = ptsize; |
|
} |
|
for (;;) { |
|
len = __getcwd(pt, bufsize); |
|
if ((len < 0) && (size == 0) && (errno == ERANGE)) { |
|
if (ptsize > (MAXPATHLEN*4)) |
|
return NULL; |
|
if ((pt = realloc(pt, ptsize *= 2)) == NULL) |
|
return NULL; |
|
bufsize = ptsize; |
|
continue; |
|
} |
|
break; |
|
} |
|
if (len < 0) |
|
return NULL; |
|
else |
|
return pt; |
|
} |
|
|
|
#endif |