/* * 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 itunder the terms of the GNU General Public License as published by theFree Software Foundation; either version 2, or (at your option) anylater version.XEmacs is distributed in the hope that it will be useful, but WITHOUTANY WARRANTY; without even the implied warranty of MERCHANTABILITY orFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licensefor more details.You should have received a copy of the GNU General Public Licensealong with XEmacs; see the file COPYING. If not, write tothe Free Software Foundation, Inc., 59 Temple Place - Suite 330,Boston, MA 02111-1307, USA. *//* Synched up with: Not in FSF. */#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <sys/types.h>#if defined(HAVE_UNISTD_H) || defined(STDC_HEADERS)#include <unistd.h>#endif#include <stdio.h>#include <string.h>#ifdef _POSIX_VERSION#include <limits.h> /* for PATH_MAX */#else#include <sys/param.h> /* for MAXPATHLEN */#endif#include <errno.h>#ifndef STDC_HEADERSexterninterrno;#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#ifdef __STDC__char*xrealpath(constchar*path,charresolved_path[])#elsechar*xrealpath(path,resolved_path)constchar*path;charresolved_path[];#endif{charcopy_path[PATH_MAX];char*new_path=resolved_path;char*max_path;intreadlinks=0;#ifdef S_IFLNKcharlink_path[PATH_MAX];intn;#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... */elseif(*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_GETCWDgetcwd(new_path,PATH_MAX-1);#elsegetwd(new_path);#endifnew_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;returnNULL;}*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)returnNULL;}else{/* Protect against infinite loops. */if(readlinks++>MAX_READLINKS){errno=ELOOP;returnNULL;}/* 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;returnNULL;}/* 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';returnresolved_path;}