diff options
Diffstat (limited to 'php3_realpath.c')
-rw-r--r-- | php3_realpath.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/php3_realpath.c b/php3_realpath.c new file mode 100644 index 0000000000..44ef3f9a23 --- /dev/null +++ b/php3_realpath.c @@ -0,0 +1,290 @@ +/* + +----------------------------------------------------------------------+ + | PHP HTML Embedded Scripting Language Version 3.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997,1998 PHP Development Team (See Credits file) | + +----------------------------------------------------------------------+ + | This program is free software; you can redistribute it and/or modify | + | it under the terms of one of the following licenses: | + | | + | A) the GNU General Public License as published by the Free Software | + | Foundation; either version 2 of the License, or (at your option) | + | any later version. | + | | + | B) the PHP License as published by the PHP Development Team and | + | included in the distribution in the file: LICENSE | + | | + | This program 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 both licenses referred to here. | + | If you did not, or have any questions about PHP licensing, please | + | contact core@php.net. | + +----------------------------------------------------------------------+ + | Author: Sander Steffann (sander@steffann.nl) | + +----------------------------------------------------------------------+ + */ + +#ifdef THREAD_SAFE +#include "tls.h" +#endif + +#include "php.h" + +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <sys/stat.h> + +#ifndef MAXSYMLINKS +#define MAXSYMLINKS 32 +#endif + +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) +#endif + +char *_php3_realpath(char *path, char resolved_path[]); + +char *_php3_realpath(char *path, char resolved_path []) { + char path_construction[MAXPATHLEN]; /* We build the result in here */ + char *writepos; /* Position to write next char */ + + char path_copy[MAXPATHLEN]; /* A work-copy of the path */ + char *workpos; /* working position in *path */ + +#if !(WIN32|WINNT) + char buf[MAXPATHLEN]; /* Buffer for readlink */ + int linklength; /* The result from readlink */ +#endif + int linkcount = 0; /* Count symlinks to avoid loops */ + + struct stat filestat; /* result from stat */ + +#if WIN32|WINNT + char *temppos; /* position while counting '.' */ + int dotcount; /* number of '.' */ + int t; /* counter */ +#endif + + /* Set the work-position to the beginning of the given path */ + strcpy(path_copy, path); + workpos = path_copy; + +#if WIN32|WINNT + /* Find out where we start - Windows version */ + if ((*workpos == '\\') || (*workpos == '/')) { + /* We start at the root of the current drive */ + /* Get the current directory */ + if (getcwd(path_construction, MAXPATHLEN-1) == NULL) { + /* Unable to get cwd */ + resolved_path[0] = 0; + return NULL; + } + /* We only need the first three chars (for example "C:\") */ + path_construction[3] = 0; + workpos++; + } else if (workpos[1] == ':') { + /* A drive-letter is specified, copy it */ + strncpy(path_construction, path, 2); + strcat(path_construction, "\\"); + workpos++; + workpos++; + } else { + /* Use the current directory */ + if (getcwd(path_construction, MAXPATHLEN-1) == NULL) { + /* Unable to get cwd */ + resolved_path[0] = 0; + return NULL; + } + strcat(path_construction, "\\"); + } +#else + /* Find out where we start - Unix version */ + if (*workpos == '/') { + /* We start at the root */ + strcpy(path_construction, "/"); + workpos++; + } else { + /* Use the current directory */ + if (getcwd(path_construction, MAXPATHLEN-1) == NULL) { + /* Unable to get cwd */ + resolved_path[0] = 0; + return NULL; + } + strcat(path_construction, "/"); + } +#endif + + /* Set the next-char-position */ + writepos = &path_construction[strlen(path_construction)]; + + /* Go to the end, then stop */ + while(*workpos != 0) { + /* Strip (back)slashes */ +#if WIN32|WINNT + while(*workpos == '\\') workpos++; +#else + while(*workpos == '/') workpos++; +#endif + +#if WIN32|WINNT + /* reset dotcount */ + dotcount = 0; + + /* Look for .. */ + if ((workpos[0] == '.') && (workpos[1] != 0)) { + /* Windows accepts \...\ as \..\..\, \....\ as \..\..\..\, etc */ + /* At least Win98 does */ + + temppos = workpos; + while(*temppos++ == '.') { + dotcount++; + if ((*temppos != '\\') && (*temppos != 0) && (*temppos != '.')) { + /* This is not a /../ component, but a filename that starts with '.' */ + dotcount = 0; + } + } + + /* Go back dotcount-1 times */ + for (t=0 ; t<(dotcount-1) ; t++) { + workpos++; /* move to next '.' */ + + /* Can we still go back? */ + if ((writepos-3) <= path_construction) return NULL; + + /* Go back */ + writepos--; /* move to '\' */ + while(*--writepos != '\\') ; /* skip until previous '\\' */ + } + } + + /* No special case */ + if (dotcount == 0) { + /* Append */ + while((*workpos != '\\') && (*workpos != 0)) { + *writepos++ = *workpos++; + } + } + + /* Just one '.', go to next element */ + if (dotcount == 1) { + while((*workpos != '\\') && (*workpos != 0)) { + *workpos++; + } + + /* Avoid double \ in the result */ + writepos--; + } + + /* If it was a directory, append a slash */ + if (*workpos == '\\') { + *writepos++ = *workpos++; + } + *writepos = 0; +#else /* WIN32|WINNT */ + /* Look for .. */ + if ((workpos[0] == '.') && (workpos[1] != 0)) { + if ((workpos[1] == '.') && ((workpos[2] == '/') || (workpos[2] == 0))) { + /* One directory back */ + /* Set pointers to right position */ + workpos++; /* move to second '.' */ + workpos++; /* move to '/' */ + + /* Only apply .. if not in root */ + if ((writepos-1) > path_construction) { + writepos--; /* move to '/' */ + while(*--writepos != '/') ; /* skip until previous '/' */ + } + } else { + if (workpos[1] == '/') { + /* Found a /./ skip it */ + workpos++; /* move to '/' */ + + /* Avoid double / in the result */ + writepos--; + } else { + /* No special case, the name just started with a . */ + /* Append */ + while((*workpos != '/') && (*workpos != 0)) { + *writepos++ = *workpos++; + } + } + } + } else { + /* No special case */ + /* Append */ + while((*workpos != '/') && (*workpos != 0)) { + *writepos++ = *workpos++; + } + } + +#if HAVE_SYMLINK + /* We are going to use path_construction, so close it */ + *writepos = 0; + + /* Check the current location to see if it is a symlink */ + if((linklength = readlink(path_construction, buf, MAXPATHLEN)) != -1) { + /* Check linkcount */ + if (linkcount > MAXSYMLINKS) return NULL; + + /* Count this symlink */ + linkcount++; + + /* Set end of buf */ + buf[linklength] = 0; + + /* Check for overflow */ + if ((strlen(workpos) + strlen(buf) + 1) >= MAXPATHLEN) return NULL; + + /* Remove the symlink-component wrom path_construction */ + writepos--; /* move to '/' */ + while(*--writepos != '/') ; /* skip until previous '/' */ + *++writepos = 0; /* end of string after '/' */ + + /* If the symlink starts with a '/', empty path_construction */ + if (*buf == '/') { + *path_construction = 0; + writepos = path_construction; + } + + /* Insert symlink into path_copy */ + strcat(buf, workpos); + strcpy(path_copy, buf); + workpos = path_copy; + } +#endif /* HAVE_SYMLINK */ + + /* If it was a directory, append a slash */ + if (*workpos == '/') { + *writepos++ = *workpos++; + } + *writepos = 0; +#endif /* WIN32|WINNT */ + } + + /* Check if the resolved path is a directory */ + if (stat(path_construction, &filestat) != 0) return NULL; + if (S_ISDIR(filestat.st_mode)) { + /* It's a directory, append a / if needed */ + if (*(writepos-1) != '/') { + /* Check for overflow */ + if ((strlen(workpos) + 2) >= MAXPATHLEN) return NULL; + + *writepos++ = '/'; + *writepos = 0; + } + } + + strcpy(resolved_path, path_construction); + return resolved_path; +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ |