Mercurial > hg > xemacs-beta
diff src/realpath.c @ 428:3ecd8885ac67 r21-2-22
Import from CVS: tag r21-2-22
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:28:15 +0200 |
parents | |
children | 8de8e3f6228a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/realpath.c Mon Aug 13 11:28:15 2007 +0200 @@ -0,0 +1,239 @@ +/* + * realpath.c -- canonicalize pathname by removing symlinks + * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com> + * + +This file is part of XEmacs. + +XEmacs is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +XEmacs is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Not in FSF. */ + +#include <config.h> + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef _POSIX_VERSION +#include <limits.h> /* for PATH_MAX */ +#else +#include <sys/param.h> /* for MAXPATHLEN */ +#endif + +#ifdef WINDOWSNT +#include <direct.h> +#endif + +#include <sys/stat.h> /* for S_IFLNK */ + +#ifndef PATH_MAX +#ifdef _POSIX_VERSION +#define PATH_MAX _POSIX_PATH_MAX +#else +#ifdef MAXPATHLEN +#define PATH_MAX MAXPATHLEN +#else +#define PATH_MAX 1024 +#endif +#endif +#endif + +#define MAX_READLINKS 32 + +char * +xrealpath (const char *path, char resolved_path []) +{ + char copy_path[PATH_MAX]; + char *new_path = resolved_path; + char *max_path; +#ifdef S_IFLNK + int readlinks = 0; + char link_path[PATH_MAX]; + int n; +#endif + + /* Make a copy of the source path since we may need to modify it. */ + strcpy(copy_path, path); + path = copy_path; + max_path = copy_path + PATH_MAX - 2; +#ifdef WINDOWSNT + /* + ** In NT we have two different cases: (1) the path name begins + ** with a drive letter, e.g., "C:"; and (2) the path name begins + ** with just a slash, which roots to the current drive. In the + ** first case we are going to leave things alone, in the second + ** case we will prepend the drive letter to the given path. + ** Note: So far in testing, I'm only seeing case #1, even though + ** I've tried to get the other cases to happen. + ** August Hill, 31 Aug 1997. + ** + ** Check for a driver letter...C:/... + */ + if (*(path + 1) == ':') + { + strncpy(new_path, path, 3); + new_path += 3; + path += 3; + } + + /* + ** No drive letter, but a beginning slash? Prepend the drive + ** letter... + */ + else if (*path == '/') + { + getcwd(new_path, PATH_MAX - 1); + new_path += 3; + path++; + } + + /* + ** Just a path name, prepend the current directory + */ + else + { + getcwd(new_path, PATH_MAX - 1); + new_path += strlen(new_path); + if (new_path[-1] != '/') + *new_path++ = '/'; + } + +#else + /* If it's a relative pathname use getwd for starters. */ + if (*path != '/') + { +#ifdef HAVE_GETCWD + getcwd(new_path, PATH_MAX - 1); +#else + getwd(new_path); +#endif + new_path += strlen(new_path); + if (new_path[-1] != '/') + *new_path++ = '/'; + } + else + { + *new_path++ = '/'; + path++; + } +#endif + /* Expand each slash-separated pathname component. */ + while (*path != '\0') + { + /* Ignore stray "/". */ + if (*path == '/') + { + path++; + continue; + } + + if (*path == '.') + { + /* Ignore ".". */ + if (path[1] == '\0' || path[1] == '/') + { + path++; + continue; + } + + if (path[1] == '.') + { + if (path[2] == '\0' || path[2] == '/') + { + path += 2; + + /* Ignore ".." at root. */ + if (new_path == resolved_path + 1) + continue; + + /* Handle ".." by backing up. */ + while ((--new_path)[-1] != '/') + ; + continue; + } + } + } + + /* Safely copy the next pathname component. */ + while (*path != '\0' && *path != '/') + { + if (path > max_path) + { + errno = ENAMETOOLONG; + return NULL; + } + *new_path++ = *path++; + } + +#ifdef S_IFLNK + /* See if latest pathname component is a symlink. */ + *new_path = '\0'; + n = readlink(resolved_path, link_path, PATH_MAX - 1); + + if (n < 0) + { + /* EINVAL means the file exists but isn't a symlink. */ + if (errno != EINVAL) + return NULL; + } + else + { + /* Protect against infinite loops. */ + if (readlinks++ > MAX_READLINKS) + { + errno = ELOOP; + return NULL; + } + + /* Note: readlink doesn't add the null byte. */ + link_path[n] = '\0'; + + if (*link_path == '/') + /* Start over for an absolute symlink. */ + new_path = resolved_path; + else + /* Otherwise back up over this component. */ + while (*(--new_path) != '/') + ; + + /* Safe sex check. */ + if (strlen(path) + n >= PATH_MAX) + { + errno = ENAMETOOLONG; + return NULL; + } + + /* Insert symlink contents into path. */ + strcat(link_path, path); + strcpy(copy_path, link_path); + path = copy_path; + } +#endif /* S_IFLNK */ + *new_path++ = '/'; + } + + /* Delete trailing slash but don't whomp a lone slash. */ + if (new_path != resolved_path + 1 && new_path[-1] == '/') + new_path--; + + /* Make sure it's null terminated. */ + *new_path = '\0'; + return resolved_path; +}