| 
									
										
										
										
											2009-01-12 13:36:28 +00:00
										 |  |  | /* pathcanon.c -- canonicalize and manipulate pathnames. */ | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* Copyright (C) 2000 Free Software Foundation, Inc.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    This file is part of GNU Bash, the Bourne Again SHell. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-12 13:36:28 +00:00
										 |  |  |    Bash 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 3 of the License, or | 
					
						
							|  |  |  |    (at your option) any later version. | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-12 13:36:28 +00:00
										 |  |  |    Bash 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. | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-01-12 13:36:28 +00:00
										 |  |  |    You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  |    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  | */ | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-11-13 17:56:06 +00:00
										 |  |  | #include <config.h>
 | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-11-13 17:56:06 +00:00
										 |  |  | #include <bashtypes.h>
 | 
					
						
							| 
									
										
										
										
											2014-02-26 09:36:43 -05:00
										 |  |  | #if defined (HAVE_SYS_PARAM_H)
 | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  | #  include <sys/param.h>
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2001-11-13 17:56:06 +00:00
										 |  |  | #include <posixstat.h>
 | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #if defined (HAVE_UNISTD_H)
 | 
					
						
							|  |  |  | #  include <unistd.h>
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-11-13 17:56:06 +00:00
										 |  |  | #include <filecntl.h>
 | 
					
						
							|  |  |  | #include <bashansi.h>
 | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  | #include <stdio.h>
 | 
					
						
							| 
									
										
										
										
											2001-11-13 17:56:06 +00:00
										 |  |  | #include <chartypes.h>
 | 
					
						
							| 
									
										
										
										
											2004-07-27 13:29:18 +00:00
										 |  |  | #include <errno.h>
 | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "shell.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-07-27 13:29:18 +00:00
										 |  |  | #if !defined (errno)
 | 
					
						
							|  |  |  | extern int errno; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-07-17 14:10:11 +00:00
										 |  |  | #if defined (__CYGWIN__)
 | 
					
						
							|  |  |  | #include <sys/cygwin.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _is_cygdrive (path) | 
					
						
							|  |  |  |      char *path; | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   static char user[MAXPATHLEN]; | 
					
						
							|  |  |  |   static char system[MAXPATHLEN]; | 
					
						
							|  |  |  |   static int first_time = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* If the path is the first part of a network path, treat it as
 | 
					
						
							|  |  |  |      existing. */ | 
					
						
							|  |  |  |   if (path[0] == '/' && path[1] == '/' && !strchr (path + 2, '/')) | 
					
						
							|  |  |  |     return 1;  | 
					
						
							|  |  |  |   /* Otherwise check for /cygdrive prefix. */ | 
					
						
							|  |  |  |   if (first_time) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       char user_flags[MAXPATHLEN]; | 
					
						
							|  |  |  |       char system_flags[MAXPATHLEN]; | 
					
						
							|  |  |  |       /* Get the cygdrive info */ | 
					
						
							|  |  |  |       cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system, user_flags, system_flags); | 
					
						
							|  |  |  |       first_time = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   return !strcasecmp (path, user) || !strcasecmp (path, system); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif /* __CYGWIN__ */	
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  | /* Return 1 if PATH corresponds to a directory.  A function for debugging. */ | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | _path_isdir (path) | 
					
						
							|  |  |  |      char *path; | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2005-12-07 14:08:12 +00:00
										 |  |  |   int l; | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  |   struct stat sb; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-07-27 13:29:18 +00:00
										 |  |  |   /* This should leave errno set to the correct value. */ | 
					
						
							| 
									
										
										
										
											2005-12-07 14:08:12 +00:00
										 |  |  |   errno = 0; | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  |   l = stat (path, &sb) == 0 && S_ISDIR (sb.st_mode); | 
					
						
							| 
									
										
										
										
											2002-07-17 14:10:11 +00:00
										 |  |  | #if defined (__CYGWIN__)
 | 
					
						
							|  |  |  |   if (l == 0) | 
					
						
							|  |  |  |     l = _is_cygdrive (path); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  |   return l; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Canonicalize PATH, and return a new path.  The new path differs from PATH
 | 
					
						
							|  |  |  |    in that: | 
					
						
							|  |  |  | 	Multple `/'s are collapsed to a single `/'. | 
					
						
							|  |  |  | 	Leading `./'s and trailing `/.'s are removed. | 
					
						
							|  |  |  | 	Trailing `/'s are removed. | 
					
						
							|  |  |  | 	Non-leading `../'s and trailing `..'s are handled by removing | 
					
						
							|  |  |  | 	portions of the path. */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Look for ROOTEDPATH, PATHSEP, DIRSEP, and ISDIRSEP in ../../general.h */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define DOUBLE_SLASH(p)	((p[0] == '/') && (p[1] == '/') && p[2] != '/')
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | char * | 
					
						
							|  |  |  | sh_canonpath (path, flags) | 
					
						
							|  |  |  |      char *path; | 
					
						
							|  |  |  |      int flags; | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   char stub_char; | 
					
						
							|  |  |  |   char *result, *p, *q, *base, *dotdot; | 
					
						
							|  |  |  |   int rooted, double_slash_path; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* The result cannot be larger than the input PATH. */ | 
					
						
							|  |  |  |   result = (flags & PATH_NOALLOC) ? path : savestring (path); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* POSIX.2 says to leave a leading `//' alone.  On cygwin, we skip over any
 | 
					
						
							|  |  |  |      leading `x:' (dos drive name). */ | 
					
						
							|  |  |  |   if (rooted = ROOTEDPATH(path)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       stub_char = DIRSEP; | 
					
						
							|  |  |  | #if defined (__CYGWIN__)
 | 
					
						
							| 
									
										
										
										
											2001-11-13 17:56:06 +00:00
										 |  |  |       base = (ISALPHA((unsigned char)result[0]) && result[1] == ':') ? result + 3 : result + 1; | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  | #else
 | 
					
						
							|  |  |  |       base = result + 1; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |       double_slash_path = DOUBLE_SLASH (path); | 
					
						
							|  |  |  |       base += double_slash_path; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       stub_char = '.'; | 
					
						
							|  |  |  | #if defined (__CYGWIN__)
 | 
					
						
							| 
									
										
										
										
											2001-11-13 17:56:06 +00:00
										 |  |  |       base = (ISALPHA((unsigned char)result[0]) && result[1] == ':') ? result + 2 : result; | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  | #else
 | 
					
						
							|  |  |  |       base = result; | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2001-11-13 17:56:06 +00:00
										 |  |  |       double_slash_path = 0; | 
					
						
							| 
									
										
										
										
											2001-04-06 19:14:31 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /*
 | 
					
						
							|  |  |  |    * invariants: | 
					
						
							|  |  |  |    *	  base points to the portion of the path we want to modify | 
					
						
							|  |  |  |    *      p points at beginning of path element we're considering. | 
					
						
							|  |  |  |    *      q points just past the last path element we wrote (no slash). | 
					
						
							|  |  |  |    *      dotdot points just past the point where .. cannot backtrack | 
					
						
							|  |  |  |    *	  any further (no slash). | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   p = q = dotdot = base; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while (*p) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       if (ISDIRSEP(p[0])) /* null element */ | 
					
						
							|  |  |  | 	p++; | 
					
						
							|  |  |  |       else if(p[0] == '.' && PATHSEP(p[1]))	/* . and ./ */ | 
					
						
							|  |  |  | 	p += 1; 	/* don't count the separator in case it is nul */ | 
					
						
							|  |  |  |       else if (p[0] == '.' && p[1] == '.' && PATHSEP(p[2])) /* .. and ../ */ | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 	  p += 2; /* skip `..' */ | 
					
						
							|  |  |  | 	  if (q > dotdot)	/* can backtrack */ | 
					
						
							|  |  |  | 	    { | 
					
						
							|  |  |  | 	      if (flags & PATH_CHECKDOTDOT) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 		  char c; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		  /* Make sure what we have so far corresponds to a valid
 | 
					
						
							|  |  |  | 		     path before we chop some of it off. */ | 
					
						
							|  |  |  | 		  c = *q; | 
					
						
							|  |  |  | 		  *q = '\0'; | 
					
						
							|  |  |  | 		  if (_path_isdir (result) == 0) | 
					
						
							|  |  |  | 		    { | 
					
						
							|  |  |  | 		      if ((flags & PATH_NOALLOC) == 0) | 
					
						
							|  |  |  | 			free (result); | 
					
						
							|  |  |  | 		      return ((char *)NULL); | 
					
						
							|  |  |  | 		    } | 
					
						
							|  |  |  | 		  *q = c; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	      while (--q > dotdot && ISDIRSEP(*q) == 0) | 
					
						
							|  |  |  | 		; | 
					
						
							|  |  |  | 	    } | 
					
						
							|  |  |  | 	  else if (rooted == 0) | 
					
						
							|  |  |  | 	    { | 
					
						
							|  |  |  | 	      /* /.. is / but ./../ is .. */ | 
					
						
							|  |  |  | 	      if (q != base) | 
					
						
							|  |  |  | 		*q++ = DIRSEP; | 
					
						
							|  |  |  | 	      *q++ = '.'; | 
					
						
							|  |  |  | 	      *q++ = '.'; | 
					
						
							|  |  |  | 	      dotdot = q; | 
					
						
							|  |  |  | 	    } | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  |       else	/* real path element */ | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 	  /* add separator if not at start of work portion of result */ | 
					
						
							|  |  |  | 	  if (q != base) | 
					
						
							|  |  |  | 	    *q++ = DIRSEP; | 
					
						
							|  |  |  | 	  while (*p && (ISDIRSEP(*p) == 0)) | 
					
						
							|  |  |  | 	    *q++ = *p++; | 
					
						
							|  |  |  | 	  /* Check here for a valid directory with _path_isdir. */ | 
					
						
							|  |  |  | 	  if (flags & PATH_CHECKEXISTS) | 
					
						
							|  |  |  | 	    { | 
					
						
							|  |  |  | 	      char c; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	      /* Make sure what we have so far corresponds to a valid
 | 
					
						
							|  |  |  | 		 path before we chop some of it off. */ | 
					
						
							|  |  |  | 	      c = *q; | 
					
						
							|  |  |  | 	      *q = '\0'; | 
					
						
							|  |  |  | 	      if (_path_isdir (result) == 0) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 		  if ((flags & PATH_NOALLOC) == 0) | 
					
						
							|  |  |  | 		    free (result); | 
					
						
							|  |  |  | 		  return ((char *)NULL); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	      *q = c; | 
					
						
							|  |  |  | 	    } | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Empty string is really ``.'' or `/', depending on what we started with. */ | 
					
						
							|  |  |  |   if (q == result) | 
					
						
							|  |  |  |     *q++ = stub_char; | 
					
						
							|  |  |  |   *q = '\0'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* If the result starts with `//', but the original path does not, we
 | 
					
						
							|  |  |  |      can turn the // into /.  Because of how we set `base', this should never
 | 
					
						
							|  |  |  |      be true, but it's a sanity check. */ | 
					
						
							|  |  |  |   if (DOUBLE_SLASH(result) && double_slash_path == 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       if (result[2] == '\0')	/* short-circuit for bare `//' */ | 
					
						
							|  |  |  | 	result[1] = '\0'; | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  | 	strcpy (result, result + 1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return (result); | 
					
						
							|  |  |  | } |