summaryrefslogtreecommitdiffstats
path: root/lib/libc/gen/getcwd.c
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>1997-03-11 11:37:59 +0000
committerpeter <peter@FreeBSD.org>1997-03-11 11:37:59 +0000
commitbb2b65bb1cc28ad6ffb0a8ab803ddd9943de91da (patch)
treead41fd2b3398760ffefb779e1db7a0f3e078e00a /lib/libc/gen/getcwd.c
parent724b89cfd3fcf9531a8abee51d88f8722ab252a9 (diff)
downloadFreeBSD-src-bb2b65bb1cc28ad6ffb0a8ab803ddd9943de91da.zip
FreeBSD-src-bb2b65bb1cc28ad6ffb0a8ab803ddd9943de91da.tar.gz
merge from Lite2 - realpath() now shares a lot of code with getcwd()
and is now in the same file.
Diffstat (limited to 'lib/libc/gen/getcwd.c')
-rw-r--r--lib/libc/gen/getcwd.c162
1 files changed, 158 insertions, 4 deletions
diff --git a/lib/libc/gen/getcwd.c b/lib/libc/gen/getcwd.c
index dfa482a..c067d11 100644
--- a/lib/libc/gen/getcwd.c
+++ b/lib/libc/gen/getcwd.c
@@ -1,7 +1,10 @@
/*
- * Copyright (c) 1989, 1991, 1993
+ * Copyright (c) 1989, 1991, 1993, 1995
* 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
* modification, are permitted provided that the following conditions
* are met:
@@ -32,19 +35,22 @@
*/
#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)getcwd.c 8.1 (Berkeley) 6/4/93";
+static char sccsid[] = "@(#)getcwd.c 8.5 (Berkeley) 2/7/95";
#endif /* LIBC_SCCS and not lint */
#include <sys/param.h>
#include <sys/stat.h>
-#include <errno.h>
#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+static char *getcwd_physical __P((char *, size_t));
+
#define ISDOT(dp) \
(dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
(dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
@@ -54,6 +60,154 @@ getcwd(pt, size)
char *pt;
size_t size;
{
+ char *pwd;
+ size_t pwdlen;
+ dev_t dev;
+ ino_t ino;
+ struct stat s;
+
+ /* Check $PWD -- if it's right, it's fast. */
+ if ((pwd = getenv("PWD")) != NULL && pwd[0] == '/' && !stat(pwd, &s)) {
+ dev = s.st_dev;
+ ino = s.st_ino;
+ if (!stat(".", &s) && dev == s.st_dev && ino == s.st_ino) {
+ pwdlen = strlen(pwd);
+ if (size != 0) {
+ if (pwdlen + 1 > size) {
+ errno = ERANGE;
+ return (NULL);
+ }
+ } else if ((pt = malloc(pwdlen + 1)) == NULL)
+ return (NULL);
+ memmove(pt, pwd, pwdlen);
+ pt[pwdlen] = '\0';
+ return (pt);
+ }
+ }
+
+ return (getcwd_physical(pt, size));
+}
+
+/*
+ * 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;
+ 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 (*p != '\0' && lstat(p, &sb) == 0) {
+ if (S_ISLNK(sb.st_mode)) {
+ 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)strcpy(wbuf, p);
+
+ /*
+ * Call the inernal internal version of getcwd which
+ * does a physical search rather than using the $PWD short-cut
+ */
+ if (getcwd_physical(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, "/");
+ (void)strcat(resolved, wbuf);
+ }
+
+ /* 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);
+}
+
+static char *
+getcwd_physical(pt, size)
+ char *pt;
+ size_t size;
+{
register struct dirent *dp;
register DIR *dir;
register dev_t dev;
@@ -94,7 +248,7 @@ getcwd(pt, size)
/*
* Allocate bytes (1024 - malloc space) for the string of "../"'s.
* 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)
goto err;
OpenPOWER on IntegriCloud