Back to index

php5  5.3.10
tsrm_virtual_cwd.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Authors: Andi Gutmans <andi@zend.com>                                |
00016    |          Sascha Schumann <sascha@schumann.cx>                        |
00017    |          Pierre Joye <pierre@php.net>                                |
00018    +----------------------------------------------------------------------+
00019 */
00020 
00021 /* $Id: tsrm_virtual_cwd.c 321634 2012-01-01 13:15:04Z felipe $ */
00022 
00023 #include <sys/types.h>
00024 #include <sys/stat.h>
00025 #include <string.h>
00026 #include <stdio.h>
00027 #include <limits.h>
00028 #include <errno.h>
00029 #include <stdlib.h>
00030 #include <fcntl.h>
00031 #include <time.h>
00032 
00033 #include "tsrm_virtual_cwd.h"
00034 #include "tsrm_strtok_r.h"
00035 
00036 #ifdef TSRM_WIN32
00037 #include <io.h>
00038 #include "tsrm_win32.h"
00039 # ifndef IO_REPARSE_TAG_SYMLINK
00040 #  define IO_REPARSE_TAG_SYMLINK 0xA000000C
00041 # endif
00042 
00043 # ifndef VOLUME_NAME_NT
00044 #  define VOLUME_NAME_NT 0x2
00045 # endif
00046 
00047 # ifndef VOLUME_NAME_DOS
00048 #  define VOLUME_NAME_DOS 0x0
00049 # endif
00050 #endif
00051 
00052 #ifndef S_IFLNK
00053 # define S_IFLNK 0120000
00054 #endif
00055 
00056 #ifdef NETWARE
00057 #include <fsio.h>
00058 #endif
00059 
00060 #ifndef HAVE_REALPATH
00061 #define realpath(x,y) strcpy(y,x)
00062 #endif
00063 
00064 #define VIRTUAL_CWD_DEBUG 0
00065 
00066 #include "TSRM.h"
00067 
00068 /* Only need mutex for popen() in Windows and NetWare because it doesn't chdir() on UNIX */
00069 #if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS)
00070 MUTEX_T cwd_mutex;
00071 #endif
00072 
00073 #ifdef ZTS
00074 ts_rsrc_id cwd_globals_id;
00075 #else
00076 virtual_cwd_globals cwd_globals;
00077 #endif
00078 
00079 cwd_state main_cwd_state; /* True global */
00080 
00081 #ifndef TSRM_WIN32
00082 #include <unistd.h>
00083 #else
00084 #include <direct.h>
00085 #endif
00086 
00087 #ifndef S_ISDIR
00088 #define S_ISDIR(mode) ((mode) & _S_IFDIR)
00089 #endif
00090 
00091 #ifndef S_ISREG
00092 #define S_ISREG(mode) ((mode) & _S_IFREG)
00093 #endif
00094 
00095 #ifdef TSRM_WIN32
00096 #include <tchar.h>
00097 #define tsrm_strtok_r(a,b,c) _tcstok((a),(b))
00098 #define TOKENIZER_STRING "/\\"
00099 
00100 static int php_check_dots(const char *element, int n)
00101 {
00102        while (n-- > 0) if (element[n] != '.') break;
00103 
00104        return (n != -1);
00105 }
00106 
00107 #define IS_DIRECTORY_UP(element, len) \
00108        (len >= 2 && !php_check_dots(element, len))
00109 
00110 #define IS_DIRECTORY_CURRENT(element, len) \
00111        (len == 1 && element[0] == '.')
00112 
00113 #elif defined(NETWARE)
00114 /* NetWare has strtok() (in LibC) and allows both slashes in paths, like Windows --
00115    but rest of the stuff is like Unix */
00116 /* strtok() call in LibC is abending when used in a different address space -- hence using
00117    PHP's version itself for now */
00118 /*#define tsrm_strtok_r(a,b,c) strtok((a),(b))*/
00119 #define TOKENIZER_STRING "/\\"
00120 
00121 #else
00122 #define TOKENIZER_STRING "/"
00123 #endif
00124 
00125 
00126 /* default macros */
00127 
00128 #ifndef IS_DIRECTORY_UP
00129 #define IS_DIRECTORY_UP(element, len) \
00130        (len == 2 && element[0] == '.' && element[1] == '.')
00131 #endif
00132 
00133 #ifndef IS_DIRECTORY_CURRENT
00134 #define IS_DIRECTORY_CURRENT(element, len) \
00135        (len == 1 && element[0] == '.')
00136 #endif
00137 
00138 /* define this to check semantics */
00139 #define IS_DIR_OK(s) (1)
00140 
00141 #ifndef IS_DIR_OK
00142 #define IS_DIR_OK(state) (php_is_dir_ok(state) == 0)
00143 #endif
00144 
00145 
00146 #define CWD_STATE_COPY(d, s)                            \
00147        (d)->cwd_length = (s)->cwd_length;        \
00148        (d)->cwd = (char *) malloc((s)->cwd_length+1);   \
00149        memcpy((d)->cwd, (s)->cwd, (s)->cwd_length+1);
00150 
00151 #define CWD_STATE_FREE(s)                 \
00152        free((s)->cwd);
00153 
00154 #ifdef TSRM_WIN32
00155 
00156 #ifdef CTL_CODE
00157 #undef CTL_CODE
00158 #endif
00159 #define CTL_CODE(DeviceType,Function,Method,Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
00160 #define FILE_DEVICE_FILE_SYSTEM 0x00000009
00161 #define METHOD_BUFFERED            0
00162 #define FILE_ANY_ACCESS     0
00163 #define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
00164 #define MAXIMUM_REPARSE_DATA_BUFFER_SIZE  ( 16 * 1024 )
00165 
00166 typedef struct {
00167        unsigned long  ReparseTag;
00168        unsigned short ReparseDataLength;
00169        unsigned short Reserved;
00170        union {
00171               struct {
00172                      unsigned short SubstituteNameOffset;
00173                      unsigned short SubstituteNameLength;
00174                      unsigned short PrintNameOffset;
00175                      unsigned short PrintNameLength;
00176                      unsigned long  Flags;
00177                      wchar_t        ReparseTarget[1];
00178               } SymbolicLinkReparseBuffer;
00179               struct {
00180                      unsigned short SubstituteNameOffset;
00181                      unsigned short SubstituteNameLength;
00182                      unsigned short PrintNameOffset;
00183                      unsigned short PrintNameLength;
00184                      wchar_t        ReparseTarget[1];
00185               } MountPointReparseBuffer;
00186               struct {
00187                      unsigned char  ReparseTarget[1];
00188               } GenericReparseBuffer;
00189        };
00190 } REPARSE_DATA_BUFFER;
00191 
00192 #define SECS_BETWEEN_EPOCHS (__int64)11644473600
00193 #define SECS_TO_100NS (__int64)10000000
00194 static inline time_t FileTimeToUnixTime(const FILETIME FileTime)
00195 {
00196        __int64 UnixTime;
00197        long *nsec = NULL;
00198        SYSTEMTIME SystemTime;
00199        FileTimeToSystemTime(&FileTime, &SystemTime);
00200 
00201        UnixTime = ((__int64)FileTime.dwHighDateTime << 32) +
00202        FileTime.dwLowDateTime;
00203 
00204        UnixTime -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS);
00205 
00206        if (nsec) {
00207               *nsec = (UnixTime % SECS_TO_100NS) * (__int64)100;
00208        }
00209 
00210        UnixTime /= SECS_TO_100NS; /* now convert to seconds */
00211 
00212        if ((time_t)UnixTime != UnixTime) {
00213               UnixTime = 0;
00214        }
00215        return (time_t)UnixTime;
00216 }
00217 
00218 CWD_API int php_sys_readlink(const char *link, char *target, size_t target_len){ /* {{{ */
00219        HINSTANCE kernel32;
00220        HANDLE hFile;
00221        DWORD dwRet;
00222 
00223        typedef BOOL (WINAPI *gfpnh_func)(HANDLE, LPTSTR, DWORD, DWORD);
00224        gfpnh_func pGetFinalPathNameByHandle;
00225 
00226        kernel32 = LoadLibrary("kernel32.dll");
00227 
00228        if (kernel32) {
00229               pGetFinalPathNameByHandle = (gfpnh_func)GetProcAddress(kernel32, "GetFinalPathNameByHandleA");
00230               if (pGetFinalPathNameByHandle == NULL) {
00231                      return -1;
00232               }
00233        } else {
00234               return -1;
00235        }
00236 
00237        hFile = CreateFile(link,            // file to open
00238                              GENERIC_READ,          // open for reading
00239                              FILE_SHARE_READ,       // share for reading
00240                              NULL,                  // default security
00241                              OPEN_EXISTING,         // existing file only
00242                              FILE_FLAG_BACKUP_SEMANTICS, // normal file
00243                              NULL);                 // no attr. template
00244 
00245        if( hFile == INVALID_HANDLE_VALUE) {
00246                      return -1;
00247        }
00248 
00249        dwRet = pGetFinalPathNameByHandle(hFile, target, MAXPATHLEN, VOLUME_NAME_DOS);
00250        if(dwRet >= MAXPATHLEN) {
00251               return -1;
00252        }
00253 
00254        CloseHandle(hFile);
00255 
00256        if(dwRet > 4) {
00257               /* Skip first 4 characters if they are "\??\" */
00258               if(target[0] == '\\' && target[1] == '\\' && target[2] == '?' && target[3] ==  '\\') {
00259                      char tmp[MAXPATHLEN];
00260                      unsigned int offset = 4;
00261                      dwRet -= 4;
00262 
00263                      /* \??\UNC\ */
00264                      if (dwRet > 7 && target[4] == 'U' && target[5] == 'N' && target[6] == 'C') {
00265                             offset += 2;
00266                             dwRet -= 2;
00267                             target[offset] = '\\';
00268                      }
00269 
00270                      memcpy(tmp, target + offset, dwRet);
00271                      memcpy(target, tmp, dwRet);
00272               }
00273        }
00274 
00275        target[dwRet] = '\0';
00276        return dwRet;
00277 }
00278 /* }}} */
00279 
00280 CWD_API int php_sys_stat(const char *path, struct stat *buf) /* {{{ */
00281 {
00282        return php_sys_stat_ex(path, buf, 0);
00283 }
00284 /* }}} */
00285 
00286 CWD_API int php_sys_lstat(const char *path, struct stat *buf) /* {{{ */
00287 {
00288        return php_sys_stat_ex(path, buf, 1);
00289 }
00290 /* }}} */
00291 
00292 CWD_API int php_sys_stat_ex(const char *path, struct stat *buf, int lstat) /* {{{ */
00293 {
00294        WIN32_FILE_ATTRIBUTE_DATA data;
00295        __int64 t;
00296        const size_t path_len = strlen(path);
00297 
00298        if (!GetFileAttributesEx(path, GetFileExInfoStandard, &data)) {
00299               return stat(path, buf);
00300        }
00301 
00302        if (path_len >= 1 && path[1] == ':') {
00303               if (path[0] >= 'A' && path[0] <= 'Z') {
00304                      buf->st_dev = buf->st_rdev = path[0] - 'A';
00305               } else {
00306                      buf->st_dev = buf->st_rdev = path[0] - 'a';
00307               }
00308        } else if (IS_UNC_PATH(path, path_len)) {
00309               buf->st_dev = buf->st_rdev = 0;
00310        } else {
00311               char  cur_path[MAXPATHLEN+1];
00312               DWORD len = sizeof(cur_path);
00313               char *tmp = cur_path;
00314 
00315               while(1) {
00316                      DWORD r = GetCurrentDirectory(len, tmp);
00317                      if (r < len) {
00318                             if (tmp[1] == ':') {
00319                                    if (path[0] >= 'A' && path[0] <= 'Z') {
00320                                           buf->st_dev = buf->st_rdev = path[0] - 'A';
00321                                    } else {
00322                                           buf->st_dev = buf->st_rdev = path[0] - 'a';
00323                                    }
00324                             } else {
00325                                    buf->st_dev = buf->st_rdev = -1;
00326                             }
00327                             break;
00328                      } else if (!r) {
00329                             buf->st_dev = buf->st_rdev = -1;
00330                             break;
00331                      } else {
00332                             len = r+1;
00333                             tmp = (char*)malloc(len);
00334                      }
00335               }
00336               if (tmp != cur_path) {
00337                      free(tmp);
00338               }
00339        }
00340 
00341        buf->st_uid = buf->st_gid = buf->st_ino = 0;
00342 
00343        if (lstat && data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
00344               /* File is a reparse point. Get the target */
00345               HANDLE hLink = NULL;
00346               REPARSE_DATA_BUFFER * pbuffer;
00347               unsigned int retlength = 0;
00348               TSRM_ALLOCA_FLAG(use_heap_large);
00349 
00350               hLink = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);
00351               if(hLink == INVALID_HANDLE_VALUE) {
00352                      return -1;
00353               }
00354 
00355               pbuffer = (REPARSE_DATA_BUFFER *)tsrm_do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large);
00356               if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer,  MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) {
00357                      tsrm_free_alloca(pbuffer, use_heap_large);
00358                      CloseHandle(hLink);
00359                      return -1;
00360               }
00361 
00362               CloseHandle(hLink);
00363 
00364               if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
00365                      buf->st_mode = S_IFLNK;
00366                      buf->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)) : (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)|S_IWRITE|(S_IWRITE>>3)|(S_IWRITE>>6));
00367               }
00368 
00369 #if 0 /* Not used yet */
00370               else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
00371                      buf->st_mode |=;
00372               }
00373 #endif
00374               tsrm_free_alloca(pbuffer, use_heap_large);
00375        } else {
00376               buf->st_mode = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR|S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)) : S_IFREG;
00377               buf->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)) : (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)|S_IWRITE|(S_IWRITE>>3)|(S_IWRITE>>6));
00378        }
00379 
00380        if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
00381               int len = strlen(path);
00382 
00383               if (path[len-4] == '.') {
00384                      if (_memicmp(path+len-3, "exe", 3) == 0 ||
00385                             _memicmp(path+len-3, "com", 3) == 0 ||
00386                             _memicmp(path+len-3, "bat", 3) == 0 ||
00387                             _memicmp(path+len-3, "cmd", 3) == 0) {
00388                             buf->st_mode  |= (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6));
00389                      }
00390               }
00391        }
00392 
00393        buf->st_nlink = 1;
00394        t = data.nFileSizeHigh;
00395        t = t << 32;
00396        t |= data.nFileSizeLow;
00397        buf->st_size = t;
00398        buf->st_atime = FileTimeToUnixTime(data.ftLastAccessTime);
00399        buf->st_ctime = FileTimeToUnixTime(data.ftCreationTime);
00400        buf->st_mtime = FileTimeToUnixTime(data.ftLastWriteTime);
00401        return 0;
00402 }
00403 /* }}} */
00404 #endif
00405 
00406 static int php_is_dir_ok(const cwd_state *state)  /* {{{ */
00407 {
00408        struct stat buf;
00409 
00410        if (php_sys_stat(state->cwd, &buf) == 0 && S_ISDIR(buf.st_mode))
00411               return (0);
00412 
00413        return (1);
00414 }
00415 /* }}} */
00416 
00417 static int php_is_file_ok(const cwd_state *state)  /* {{{ */
00418 {
00419        struct stat buf;
00420 
00421        if (php_sys_stat(state->cwd, &buf) == 0 && S_ISREG(buf.st_mode))
00422               return (0);
00423 
00424        return (1);
00425 }
00426 /* }}} */
00427 
00428 static void cwd_globals_ctor(virtual_cwd_globals *cwd_g TSRMLS_DC) /* {{{ */
00429 {
00430        CWD_STATE_COPY(&cwd_g->cwd, &main_cwd_state);
00431        cwd_g->realpath_cache_size = 0;
00432        cwd_g->realpath_cache_size_limit = REALPATH_CACHE_SIZE;
00433        cwd_g->realpath_cache_ttl = REALPATH_CACHE_TTL;
00434        memset(cwd_g->realpath_cache, 0, sizeof(cwd_g->realpath_cache));
00435 }
00436 /* }}} */
00437 
00438 static void cwd_globals_dtor(virtual_cwd_globals *cwd_g TSRMLS_DC) /* {{{ */
00439 {
00440        CWD_STATE_FREE(&cwd_g->cwd);
00441        realpath_cache_clean(TSRMLS_C);
00442 }
00443 /* }}} */
00444 
00445 CWD_API void virtual_cwd_startup(void) /* {{{ */
00446 {
00447        char cwd[MAXPATHLEN];
00448        char *result;
00449 
00450 #ifdef NETWARE
00451        result = getcwdpath(cwd, NULL, 1);
00452        if(result)
00453        {
00454               char *c=cwd;
00455               while(c = strchr(c, '\\'))
00456               {
00457                      *c='/';
00458                      ++c;
00459               }
00460        }
00461 #else
00462        result = getcwd(cwd, sizeof(cwd));
00463 #endif
00464        if (!result) {
00465               cwd[0] = '\0';
00466        }
00467 
00468        main_cwd_state.cwd_length = strlen(cwd);
00469 #ifdef TSRM_WIN32
00470        if (main_cwd_state.cwd_length >= 2 && cwd[1] == ':') {
00471               cwd[0] = toupper(cwd[0]);
00472        }
00473 #endif
00474        main_cwd_state.cwd = strdup(cwd);
00475 
00476 #ifdef ZTS
00477        ts_allocate_id(&cwd_globals_id, sizeof(virtual_cwd_globals), (ts_allocate_ctor) cwd_globals_ctor, (ts_allocate_dtor) cwd_globals_dtor);
00478 #else
00479        cwd_globals_ctor(&cwd_globals TSRMLS_CC);
00480 #endif
00481 
00482 #if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS)
00483        cwd_mutex = tsrm_mutex_alloc();
00484 #endif
00485 }
00486 /* }}} */
00487 
00488 CWD_API void virtual_cwd_shutdown(void) /* {{{ */
00489 {
00490 #ifndef ZTS
00491        cwd_globals_dtor(&cwd_globals TSRMLS_CC);
00492 #endif
00493 #if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS)
00494        tsrm_mutex_free(cwd_mutex);
00495 #endif
00496 
00497        free(main_cwd_state.cwd); /* Don't use CWD_STATE_FREE because the non global states will probably use emalloc()/efree() */
00498 }
00499 /* }}} */
00500 
00501 CWD_API char *virtual_getcwd_ex(size_t *length TSRMLS_DC) /* {{{ */
00502 {
00503        cwd_state *state;
00504 
00505        state = &CWDG(cwd);
00506 
00507        if (state->cwd_length == 0) {
00508               char *retval;
00509 
00510               *length = 1;
00511               retval = (char *) malloc(2);
00512               if (retval == NULL) {
00513                      return NULL;
00514               }
00515               retval[0] = DEFAULT_SLASH;
00516               retval[1] = '\0';
00517               return retval;
00518        }
00519 
00520 #ifdef TSRM_WIN32
00521        /* If we have something like C: */
00522        if (state->cwd_length == 2 && state->cwd[state->cwd_length-1] == ':') {
00523               char *retval;
00524 
00525               *length = state->cwd_length+1;
00526               retval = (char *) malloc(*length+1);
00527               if (retval == NULL) {
00528                      return NULL;
00529               }
00530               memcpy(retval, state->cwd, *length);
00531               retval[0] = toupper(retval[0]);
00532               retval[*length-1] = DEFAULT_SLASH;
00533               retval[*length] = '\0';
00534               return retval;
00535        }
00536 #endif
00537        *length = state->cwd_length;
00538        return strdup(state->cwd);
00539 }
00540 /* }}} */
00541 
00542 /* Same semantics as UNIX getcwd() */
00543 CWD_API char *virtual_getcwd(char *buf, size_t size TSRMLS_DC) /* {{{ */
00544 {
00545        size_t length;
00546        char *cwd;
00547 
00548        cwd = virtual_getcwd_ex(&length TSRMLS_CC);
00549 
00550        if (buf == NULL) {
00551               return cwd;
00552        }
00553        if (length > size-1) {
00554               free(cwd);
00555               errno = ERANGE; /* Is this OK? */
00556               return NULL;
00557        }
00558        memcpy(buf, cwd, length+1);
00559        free(cwd);
00560        return buf;
00561 }
00562 /* }}} */
00563 
00564 #ifdef PHP_WIN32
00565 static inline unsigned long realpath_cache_key(const char *path, int path_len TSRMLS_DC) /* {{{ */
00566 {
00567        register unsigned long h;
00568        char *bucket_key_start = tsrm_win32_get_path_sid_key(path TSRMLS_CC);
00569        char *bucket_key = (char *)bucket_key_start;
00570        const char *e = bucket_key + strlen(bucket_key);
00571 
00572        if (!bucket_key) {
00573               return 0;
00574        }
00575 
00576        for (h = 2166136261U; bucket_key < e;) {
00577               h *= 16777619;
00578               h ^= *bucket_key++;
00579        }
00580        HeapFree(GetProcessHeap(), 0, (LPVOID)bucket_key_start);
00581        return h;
00582 }
00583 /* }}} */
00584 #else
00585 static inline unsigned long realpath_cache_key(const char *path, int path_len) /* {{{ */
00586 {
00587        register unsigned long h;
00588        const char *e = path + path_len;
00589 
00590        for (h = 2166136261U; path < e;) {
00591               h *= 16777619;
00592               h ^= *path++;
00593        }
00594 
00595        return h;
00596 }
00597 /* }}} */
00598 #endif /* defined(PHP_WIN32) */
00599 
00600 CWD_API void realpath_cache_clean(TSRMLS_D) /* {{{ */
00601 {
00602        int i;
00603 
00604        for (i = 0; i < sizeof(CWDG(realpath_cache))/sizeof(CWDG(realpath_cache)[0]); i++) {
00605               realpath_cache_bucket *p = CWDG(realpath_cache)[i];
00606               while (p != NULL) {
00607                      realpath_cache_bucket *r = p;
00608                      p = p->next;
00609                      free(r);
00610               }
00611               CWDG(realpath_cache)[i] = NULL;
00612        }
00613        CWDG(realpath_cache_size) = 0;
00614 }
00615 /* }}} */
00616 
00617 CWD_API void realpath_cache_del(const char *path, int path_len TSRMLS_DC) /* {{{ */
00618 {
00619 #ifdef PHP_WIN32
00620        unsigned long key = realpath_cache_key(path, path_len TSRMLS_CC);
00621 #else
00622        unsigned long key = realpath_cache_key(path, path_len);
00623 #endif
00624        unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
00625        realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n];
00626 
00627        while (*bucket != NULL) {
00628               if (key == (*bucket)->key && path_len == (*bucket)->path_len &&
00629                                    memcmp(path, (*bucket)->path, path_len) == 0) {
00630                      realpath_cache_bucket *r = *bucket;
00631                      *bucket = (*bucket)->next;
00632                  
00633                      /* if the pointers match then only subtract the length of the path */
00634                      if(r->path == r->realpath) {
00635                             CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1;
00636                      } else {
00637                             CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1;
00638                      }
00639                  
00640                      free(r);
00641                      return;
00642               } else {
00643                      bucket = &(*bucket)->next;
00644               }
00645        }
00646 }
00647 /* }}} */
00648 
00649 static inline void realpath_cache_add(const char *path, int path_len, const char *realpath, int realpath_len, int is_dir, time_t t TSRMLS_DC) /* {{{ */
00650 {
00651        long size = sizeof(realpath_cache_bucket) + path_len + 1;
00652        int same = 1;
00653 
00654        if (realpath_len != path_len ||
00655               memcmp(path, realpath, path_len) != 0) {
00656               size += realpath_len + 1;
00657               same = 0;
00658        }
00659 
00660        if (CWDG(realpath_cache_size) + size <= CWDG(realpath_cache_size_limit)) {
00661               realpath_cache_bucket *bucket = malloc(size);
00662               unsigned long n;
00663 
00664               if (bucket == NULL) {
00665                      return;
00666               }
00667 
00668 #ifdef PHP_WIN32
00669               bucket->key = realpath_cache_key(path, path_len TSRMLS_CC);
00670 #else
00671               bucket->key = realpath_cache_key(path, path_len);
00672 #endif
00673               bucket->path = (char*)bucket + sizeof(realpath_cache_bucket);
00674               memcpy(bucket->path, path, path_len+1);
00675               bucket->path_len = path_len;
00676               if (same) {
00677                      bucket->realpath = bucket->path;
00678               } else {
00679                      bucket->realpath = bucket->path + (path_len + 1);
00680                      memcpy(bucket->realpath, realpath, realpath_len+1);
00681               }
00682               bucket->realpath_len = realpath_len;
00683               bucket->is_dir = is_dir;
00684 #ifdef PHP_WIN32
00685               bucket->is_rvalid   = 0;
00686               bucket->is_readable = 0;
00687               bucket->is_wvalid   = 0;
00688               bucket->is_writable = 0;
00689 #endif
00690               bucket->expires = t + CWDG(realpath_cache_ttl);
00691               n = bucket->key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
00692               bucket->next = CWDG(realpath_cache)[n];
00693               CWDG(realpath_cache)[n] = bucket;
00694               CWDG(realpath_cache_size) += size;
00695        }
00696 }
00697 /* }}} */
00698 
00699 static inline realpath_cache_bucket* realpath_cache_find(const char *path, int path_len, time_t t TSRMLS_DC) /* {{{ */
00700 {
00701 #ifdef PHP_WIN32
00702        unsigned long key = realpath_cache_key(path, path_len TSRMLS_CC);
00703 #else
00704        unsigned long key = realpath_cache_key(path, path_len);
00705 #endif
00706 
00707        unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
00708        realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n];
00709 
00710        while (*bucket != NULL) {
00711               if (CWDG(realpath_cache_ttl) && (*bucket)->expires < t) {
00712                      realpath_cache_bucket *r = *bucket;
00713                      *bucket = (*bucket)->next;
00714 
00715                      /* if the pointers match then only subtract the length of the path */           
00716                      if(r->path == r->realpath) {
00717                             CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1;
00718                      } else {
00719                             CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1;
00720                      }
00721                  
00722                      free(r);
00723               } else if (key == (*bucket)->key && path_len == (*bucket)->path_len &&
00724                                    memcmp(path, (*bucket)->path, path_len) == 0) {
00725                      return *bucket;
00726               } else {
00727                      bucket = &(*bucket)->next;
00728               }
00729        }
00730        return NULL;
00731 }
00732 /* }}} */
00733 
00734 CWD_API realpath_cache_bucket* realpath_cache_lookup(const char *path, int path_len, time_t t TSRMLS_DC) /* {{{ */
00735 {
00736        return realpath_cache_find(path, path_len, t TSRMLS_CC);
00737 }
00738 /* }}} */
00739 
00740 CWD_API int realpath_cache_size(TSRMLS_D)
00741 {
00742        return CWDG(realpath_cache_size);
00743 }
00744 
00745 CWD_API int realpath_cache_max_buckets(TSRMLS_D)
00746 {
00747        return (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
00748 }
00749 
00750 CWD_API realpath_cache_bucket** realpath_cache_get_buckets(TSRMLS_D)
00751 {
00752        return CWDG(realpath_cache);
00753 }
00754 
00755 
00756 #undef LINK_MAX
00757 #define LINK_MAX 32
00758 
00759 static int tsrm_realpath_r(char *path, int start, int len, int *ll, time_t *t, int use_realpath, int is_dir, int *link_is_dir TSRMLS_DC) /* {{{ */
00760 {
00761        int i, j, save;
00762        int directory = 0;
00763 #ifdef TSRM_WIN32
00764        WIN32_FIND_DATA data;
00765        HANDLE hFind;
00766        TSRM_ALLOCA_FLAG(use_heap_large)
00767 #else
00768        struct stat st;
00769 #endif
00770        realpath_cache_bucket *bucket;
00771        char *tmp;
00772        TSRM_ALLOCA_FLAG(use_heap)
00773 
00774        while (1) {
00775               if (len <= start) {
00776                      return start;
00777               }
00778 
00779               i = len;
00780               while (i > start && !IS_SLASH(path[i-1])) {
00781                      i--;
00782               }
00783 
00784               if (i == len ||
00785                      (i == len - 1 && path[i] == '.')) {
00786                      /* remove double slashes and '.' */
00787                      len = i - 1;
00788                      is_dir = 1;
00789                      continue;
00790               } else if (i == len - 2 && path[i] == '.' && path[i+1] == '.') {
00791                      /* remove '..' and previous directory */
00792                      if (i - 1 <= start) {
00793                             return start ? start : len;
00794                      }
00795                      j = tsrm_realpath_r(path, start, i-1, ll, t, use_realpath, 1, NULL TSRMLS_CC);
00796                      if (j > start) {
00797                             j--;
00798                             while (j > start && !IS_SLASH(path[j])) {
00799                                    j--;
00800                             }
00801                             if (!start) {
00802                                    /* leading '..' must not be removed in case of relative path */
00803                                    if (j == 0 && path[0] == '.' && path[1] == '.' &&
00804                                                  IS_SLASH(path[2])) {
00805                                           path[3] = '.';
00806                                           path[4] = '.';
00807                                           path[5] = DEFAULT_SLASH;
00808                                           j = 5;
00809                                    } else if (j > 0 &&
00810                                                  path[j+1] == '.' && path[j+2] == '.' &&
00811                                                  IS_SLASH(path[j+3])) {
00812                                           j += 4;
00813                                           path[j++] = '.';
00814                                           path[j++] = '.';
00815                                           path[j] = DEFAULT_SLASH;
00816                                    }
00817                             }
00818                      } else if (!start && !j) {
00819                             /* leading '..' must not be removed in case of relative path */
00820                             path[0] = '.';
00821                             path[1] = '.';
00822                             path[2] = DEFAULT_SLASH;
00823                             j = 2;
00824                      }
00825                      return j;
00826               }
00827 
00828               path[len] = 0;
00829 
00830               save = (use_realpath != CWD_EXPAND);
00831 
00832               if (start && save && CWDG(realpath_cache_size_limit)) {
00833                      /* cache lookup for absolute path */
00834                      if (!*t) {
00835                             *t = time(0);
00836                      }
00837                      if ((bucket = realpath_cache_find(path, len, *t TSRMLS_CC)) != NULL) {
00838                             if (is_dir && !bucket->is_dir) {
00839                                    /* not a directory */
00840                                    return -1;
00841                             } else {
00842                                    if (link_is_dir) {
00843                                           *link_is_dir = bucket->is_dir;
00844                                    }
00845                                    memcpy(path, bucket->realpath, bucket->realpath_len + 1);
00846                                    return bucket->realpath_len;
00847                             }
00848                      }
00849               }
00850 
00851 #ifdef TSRM_WIN32
00852               if (save && (hFind = FindFirstFile(path, &data)) == INVALID_HANDLE_VALUE) {
00853                      if (use_realpath == CWD_REALPATH) {
00854                             /* file not found */
00855                             return -1;
00856                      }
00857                      /* continue resolution anyway but don't save result in the cache */
00858                      save = 0;
00859               }
00860 
00861               if (save) {
00862                      FindClose(hFind);
00863               }
00864 
00865               tmp = tsrm_do_alloca(len+1, use_heap);
00866               memcpy(tmp, path, len+1);
00867 
00868               if(save &&
00869                             !(IS_UNC_PATH(path, len) && len >= 3 && path[2] != '?') &&
00870                             (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
00871                      /* File is a reparse point. Get the target */
00872                      HANDLE hLink = NULL;
00873                      REPARSE_DATA_BUFFER * pbuffer;
00874                      unsigned int retlength = 0;
00875                      int bufindex = 0, isabsolute = 0;
00876                      wchar_t * reparsetarget;
00877                      BOOL isVolume = FALSE;
00878                      char printname[MAX_PATH];
00879                      char substitutename[MAX_PATH];
00880                      int printname_len, substitutename_len;
00881                      int substitutename_off = 0;
00882 
00883                      if(++(*ll) > LINK_MAX) {
00884                             return -1;
00885                      }
00886 
00887                      hLink = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);
00888                      if(hLink == INVALID_HANDLE_VALUE) {
00889                             return -1;
00890                      }
00891 
00892                      pbuffer = (REPARSE_DATA_BUFFER *)tsrm_do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large);
00893                      if (pbuffer == NULL) {
00894                             return -1;
00895                      }
00896                      if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer,  MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) {
00897                             tsrm_free_alloca(pbuffer, use_heap_large);
00898                             CloseHandle(hLink);
00899                             return -1;
00900                      }
00901 
00902                      CloseHandle(hLink);
00903 
00904                      if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
00905                             reparsetarget = pbuffer->SymbolicLinkReparseBuffer.ReparseTarget;
00906                             printname_len = pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR);
00907                             isabsolute = (pbuffer->SymbolicLinkReparseBuffer.Flags == 0) ? 1 : 0;
00908                             if (!WideCharToMultiByte(CP_THREAD_ACP, 0,
00909                                    reparsetarget + pbuffer->MountPointReparseBuffer.PrintNameOffset  / sizeof(WCHAR),
00910                                    printname_len + 1,
00911                                    printname, MAX_PATH, NULL, NULL
00912                             )) {
00913                                    tsrm_free_alloca(pbuffer, use_heap_large);
00914                                    return -1;
00915                             };
00916                             printname_len = pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR);
00917                             printname[printname_len] = 0;
00918 
00919                             substitutename_len = pbuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
00920                             if (!WideCharToMultiByte(CP_THREAD_ACP, 0,
00921                                    reparsetarget + pbuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR),
00922                                    substitutename_len + 1,
00923                                    substitutename, MAX_PATH, NULL, NULL
00924                             )) {
00925                                    tsrm_free_alloca(pbuffer, use_heap_large);
00926                                    return -1;
00927                             };
00928                             substitutename[substitutename_len] = 0;
00929                      }
00930                      else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
00931                             isabsolute = 1;
00932                             reparsetarget = pbuffer->MountPointReparseBuffer.ReparseTarget;
00933                             printname_len = pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR);
00934                             if (!WideCharToMultiByte(CP_THREAD_ACP, 0,
00935                                    reparsetarget + pbuffer->MountPointReparseBuffer.PrintNameOffset  / sizeof(WCHAR),
00936                                    printname_len + 1,
00937                                    printname, MAX_PATH, NULL, NULL
00938                             )) {
00939                                    tsrm_free_alloca(pbuffer, use_heap_large);
00940                                    return -1;
00941                             };
00942                             printname[pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR)] = 0;
00943 
00944                             substitutename_len = pbuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
00945                             if (!WideCharToMultiByte(CP_THREAD_ACP, 0,
00946                                    reparsetarget + pbuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR),
00947                                    substitutename_len + 1,
00948                                    substitutename, MAX_PATH, NULL, NULL
00949                             )) {
00950                                    tsrm_free_alloca(pbuffer, use_heap_large);
00951                                    return -1;
00952                             };
00953                             substitutename[substitutename_len] = 0;
00954                      } else {
00955                             tsrm_free_alloca(pbuffer, use_heap_large);
00956                             return -1;
00957                      }
00958 
00959                      if(isabsolute && substitutename_len > 4) {
00960                             /* Do not resolve volumes (for now). A mounted point can
00961                                target a volume without a drive, it is not certain that
00962                                all IO functions we use in php and its deps support
00963                                path with volume GUID instead of the DOS way, like:
00964                                d:\test\mnt\foo
00965                                \\?\Volume{62d1c3f8-83b9-11de-b108-806e6f6e6963}\foo
00966                             */
00967                             if (strncmp(substitutename, "\\??\\Volume{",11) == 0
00968                                    || strncmp(substitutename, "\\\\?\\Volume{",11) == 0
00969                                    || strncmp(substitutename, "\\??\\UNC\\", 8) == 0
00970                                    ) {
00971                                    isVolume = TRUE;
00972                                    substitutename_off = 0;
00973                             } else
00974                                    /* do not use the \??\ and \\?\ prefix*/
00975                                    if (strncmp(substitutename, "\\??\\", 4) == 0
00976                                           || strncmp(substitutename, "\\\\?\\", 4) == 0) {
00977                                    substitutename_off = 4;
00978                             }
00979                      }
00980 
00981                      if (!isVolume) {
00982                             char * tmp2 = substitutename + substitutename_off;
00983                             for(bufindex = 0; bufindex < (substitutename_len - substitutename_off); bufindex++) {
00984                                    *(path + bufindex) = *(tmp2 + bufindex);
00985                             }
00986 
00987                             *(path + bufindex) = 0;
00988                             j = bufindex;
00989                      } else {
00990                             j = len;
00991                      }
00992 
00993 
00994 #if VIRTUAL_CWD_DEBUG
00995                      fprintf(stderr, "reparse: print: %s ", printname);
00996                      fprintf(stderr, "sub: %s ", substitutename);
00997                      fprintf(stderr, "resolved: %s ", path);
00998 #endif
00999                      tsrm_free_alloca(pbuffer, use_heap_large);
01000 
01001                      if(isabsolute == 1) {
01002                             if (!((j == 3) && (path[1] == ':') && (path[2] == '\\'))) {
01003                                    /* use_realpath is 0 in the call below coz path is absolute*/
01004                                    j = tsrm_realpath_r(path, 0, j, ll, t, 0, is_dir, &directory TSRMLS_CC);
01005                                    if(j < 0) {
01006                                           tsrm_free_alloca(tmp, use_heap);
01007                                           return -1;
01008                                    }
01009                             }
01010                      }
01011                      else {
01012                             if(i + j >= MAXPATHLEN - 1) {
01013                                    tsrm_free_alloca(tmp, use_heap);
01014                                    return -1;
01015                             }
01016 
01017                             memmove(path+i, path, j+1);
01018                             memcpy(path, tmp, i-1);
01019                             path[i-1] = DEFAULT_SLASH;
01020                             j  = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC);
01021                             if(j < 0) {
01022                                    tsrm_free_alloca(tmp, use_heap);
01023                                    return -1;
01024                             }
01025                      }
01026                      directory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
01027 
01028                      if(link_is_dir) {
01029                             *link_is_dir = directory;
01030                      }
01031               }
01032               else {
01033                      if (save) {
01034                             directory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
01035                             if (is_dir && !directory) {
01036                                    /* not a directory */
01037                                    return -1;
01038                             }
01039                      }
01040 
01041 #elif defined(NETWARE)
01042               save = 0;
01043               tmp = tsrm_do_alloca(len+1, use_heap);
01044               memcpy(tmp, path, len+1);
01045 #else
01046               if (save && php_sys_lstat(path, &st) < 0) {
01047                      if (use_realpath == CWD_REALPATH) {
01048                             /* file not found */
01049                             return -1;
01050                      }
01051                      /* continue resolution anyway but don't save result in the cache */
01052                      save = 0;
01053               }
01054 
01055               tmp = tsrm_do_alloca(len+1, use_heap);
01056               memcpy(tmp, path, len+1);
01057 
01058               if (save && S_ISLNK(st.st_mode)) {
01059                      if (++(*ll) > LINK_MAX || (j = php_sys_readlink(tmp, path, MAXPATHLEN)) < 0) {
01060                             /* too many links or broken symlinks */
01061                             tsrm_free_alloca(tmp, use_heap);
01062                             return -1;
01063                      }
01064                      path[j] = 0;
01065                      if (IS_ABSOLUTE_PATH(path, j)) {
01066                             j = tsrm_realpath_r(path, 1, j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC);
01067                             if (j < 0) {
01068                                    tsrm_free_alloca(tmp, use_heap);
01069                                    return -1;
01070                             }
01071                      } else {
01072                             if (i + j >= MAXPATHLEN-1) {
01073                                    tsrm_free_alloca(tmp, use_heap);
01074                                    return -1; /* buffer overflow */
01075                             }
01076                             memmove(path+i, path, j+1);
01077                             memcpy(path, tmp, i-1);
01078                             path[i-1] = DEFAULT_SLASH;
01079                             j = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC);
01080                             if (j < 0) {
01081                                    tsrm_free_alloca(tmp, use_heap);
01082                                    return -1;
01083                             }
01084                      }
01085                      if (link_is_dir) {
01086                             *link_is_dir = directory;
01087                      }
01088               } else {
01089                      if (save) {
01090                             directory = S_ISDIR(st.st_mode);
01091                             if (link_is_dir) {
01092                                    *link_is_dir = directory;
01093                             }
01094                             if (is_dir && !directory) {
01095                                    /* not a directory */
01096                                    tsrm_free_alloca(tmp, use_heap);
01097                                    return -1;
01098                             }
01099                      }
01100 #endif
01101                      if (i - 1 <= start) {
01102                             j = start;
01103                      } else {
01104                             /* some leading directories may be unaccessable */
01105                             j = tsrm_realpath_r(path, start, i-1, ll, t, save ? CWD_FILEPATH : use_realpath, 1, NULL TSRMLS_CC);
01106                             if (j > start) {
01107                                    path[j++] = DEFAULT_SLASH;
01108                             }
01109                      }
01110 #ifdef TSRM_WIN32
01111                      if (j < 0 || j + len - i >= MAXPATHLEN-1) {
01112                             tsrm_free_alloca(tmp, use_heap);
01113                             return -1;
01114                      }
01115                      if (save) {
01116                             i = strlen(data.cFileName);
01117                             memcpy(path+j, data.cFileName, i+1);
01118                             j += i;
01119                      } else {
01120                             /* use the original file or directory name as it wasn't found */
01121                             memcpy(path+j, tmp+i, len-i+1);
01122                             j += (len-i);
01123                      }
01124               }
01125 #else
01126                      if (j < 0 || j + len - i >= MAXPATHLEN-1) {
01127                             tsrm_free_alloca(tmp, use_heap);
01128                             return -1;
01129                      }
01130                      memcpy(path+j, tmp+i, len-i+1);
01131                      j += (len-i);
01132               }
01133 #endif
01134 
01135               if (save && start && CWDG(realpath_cache_size_limit)) {
01136                      /* save absolute path in the cache */
01137                      realpath_cache_add(tmp, len, path, j, directory, *t TSRMLS_CC);
01138               }
01139 
01140               tsrm_free_alloca(tmp, use_heap);
01141               return j;
01142        }
01143 }
01144 /* }}} */
01145 
01146 /* Resolve path relatively to state and put the real path into state */
01147 /* returns 0 for ok, 1 for error */
01148 CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath) /* {{{ */
01149 {
01150        int path_length = strlen(path);
01151        char resolved_path[MAXPATHLEN];
01152        int start = 1;
01153        int ll = 0;
01154        time_t t;
01155        int ret;
01156        int add_slash;
01157        void *tmp;
01158        TSRMLS_FETCH();
01159 
01160        if (path_length == 0 || path_length >= MAXPATHLEN-1) {
01161 #ifdef TSRM_WIN32
01162 # if _MSC_VER < 1300
01163               errno = EINVAL;
01164 # else
01165               _set_errno(EINVAL);
01166 # endif
01167 #else
01168               errno = EINVAL;
01169 #endif
01170               return 1;
01171        }
01172 
01173 #if VIRTUAL_CWD_DEBUG
01174        fprintf(stderr,"cwd = %s path = %s\n", state->cwd, path);
01175 #endif
01176 
01177        /* cwd_length can be 0 when getcwd() fails.
01178         * This can happen under solaris when a dir does not have read permissions
01179         * but *does* have execute permissions */
01180        if (!IS_ABSOLUTE_PATH(path, path_length)) {
01181               if (state->cwd_length == 0) {
01182                      /* resolve relative path */
01183                      start = 0;
01184                      memcpy(resolved_path , path, path_length + 1);
01185               } else {
01186                      int state_cwd_length = state->cwd_length;
01187 
01188 #ifdef TSRM_WIN32
01189                      if (IS_SLASH(path[0])) {
01190                             if (state->cwd[1] == ':') {
01191                                    /* Copy only the drive name */
01192                                    state_cwd_length = 2;
01193                             } else if (IS_UNC_PATH(state->cwd, state->cwd_length)) {
01194                                    /* Copy only the share name */
01195                                    state_cwd_length = 2;
01196                                    while (IS_SLASH(state->cwd[state_cwd_length])) {
01197                                           state_cwd_length++;
01198                                    }
01199                                    while (state->cwd[state_cwd_length] &&
01200                                                  !IS_SLASH(state->cwd[state_cwd_length])) {
01201                                           state_cwd_length++;
01202                                    }
01203                                    while (IS_SLASH(state->cwd[state_cwd_length])) {
01204                                           state_cwd_length++;
01205                                    }
01206                                    while (state->cwd[state_cwd_length] &&
01207                                                  !IS_SLASH(state->cwd[state_cwd_length])) {
01208                                           state_cwd_length++;
01209                                    }
01210                             }
01211                      }
01212 #endif
01213                      if (path_length + state_cwd_length + 1 >= MAXPATHLEN-1) {
01214                             return 1;
01215                      }
01216                      memcpy(resolved_path, state->cwd, state_cwd_length);
01217                      resolved_path[state_cwd_length] = DEFAULT_SLASH;
01218                      memcpy(resolved_path + state_cwd_length + 1, path, path_length + 1);
01219                      path_length += state_cwd_length + 1;
01220               }
01221        } else {
01222 #ifdef TSRM_WIN32
01223               if (path_length > 2 && path[1] == ':' && !IS_SLASH(path[2])) {
01224                      resolved_path[0] = path[0];
01225                      resolved_path[1] = ':';
01226                      resolved_path[2] = DEFAULT_SLASH;
01227                      memcpy(resolved_path + 3, path + 2, path_length - 1);
01228                      path_length++;
01229               } else
01230 #endif
01231               memcpy(resolved_path, path, path_length + 1);
01232        }
01233 
01234 #ifdef TSRM_WIN32
01235        if (memchr(resolved_path, '*', path_length) ||
01236               memchr(resolved_path, '?', path_length)) {
01237               return 1;
01238        }
01239 #endif
01240 
01241 #ifdef TSRM_WIN32
01242        if (IS_UNC_PATH(resolved_path, path_length)) {
01243               /* skip UNC name */
01244               resolved_path[0] = DEFAULT_SLASH;
01245               resolved_path[1] = DEFAULT_SLASH;
01246               start = 2;
01247               while (!IS_SLASH(resolved_path[start])) {
01248                      if (resolved_path[start] == 0) {
01249                             goto verify;
01250                      }
01251                      resolved_path[start] = toupper(resolved_path[start]);
01252                      start++;
01253               }
01254               resolved_path[start++] = DEFAULT_SLASH;
01255               while (!IS_SLASH(resolved_path[start])) {
01256                      if (resolved_path[start] == 0) {
01257                             goto verify;
01258                      }
01259                      resolved_path[start] = toupper(resolved_path[start]);
01260                      start++;
01261               }
01262               resolved_path[start++] = DEFAULT_SLASH;
01263        } else if (IS_ABSOLUTE_PATH(resolved_path, path_length)) {
01264               /* skip DRIVE name */
01265               resolved_path[0] = toupper(resolved_path[0]);
01266               resolved_path[2] = DEFAULT_SLASH;
01267               start = 3;
01268        }
01269 #elif defined(NETWARE)
01270        if (IS_ABSOLUTE_PATH(resolved_path, path_length)) {
01271               /* skip VOLUME name */
01272               start = 0;
01273               while (start != ':') {
01274                      if (resolved_path[start] == 0) return -1;
01275                      start++;
01276               }
01277               start++;
01278               if (!IS_SLASH(resolved_path[start])) return -1;
01279               resolved_path[start++] = DEFAULT_SLASH;
01280        }
01281 #endif
01282 
01283        add_slash = (use_realpath != CWD_REALPATH) && path_length > 0 && IS_SLASH(resolved_path[path_length-1]);
01284        t = CWDG(realpath_cache_ttl) ? 0 : -1;
01285        path_length = tsrm_realpath_r(resolved_path, start, path_length, &ll, &t, use_realpath, 0, NULL TSRMLS_CC);
01286 
01287        if (path_length < 0) {
01288               errno = ENOENT;
01289               return 1;
01290        }
01291 
01292        if (!start && !path_length) {
01293               resolved_path[path_length++] = '.';
01294        }
01295        if (add_slash && path_length && !IS_SLASH(resolved_path[path_length-1])) {
01296               if (path_length >= MAXPATHLEN-1) {
01297                      return -1;
01298               }
01299               resolved_path[path_length++] = DEFAULT_SLASH;
01300        }
01301        resolved_path[path_length] = 0;
01302 
01303 #ifdef TSRM_WIN32
01304 verify:
01305 #endif
01306        if (verify_path) {
01307               cwd_state old_state;
01308 
01309               CWD_STATE_COPY(&old_state, state);
01310               state->cwd_length = path_length;
01311 
01312               tmp = realloc(state->cwd, state->cwd_length+1);
01313               if (tmp == NULL) {
01314 #if VIRTUAL_CWD_DEBUG
01315                      fprintf (stderr, "Out of memory\n");
01316 #endif
01317                      return 1;
01318               }
01319               state->cwd = (char *) tmp;
01320 
01321               memcpy(state->cwd, resolved_path, state->cwd_length+1);
01322               if (verify_path(state)) {
01323                      CWD_STATE_FREE(state);
01324                      *state = old_state;
01325                      ret = 1;
01326               } else {
01327                      CWD_STATE_FREE(&old_state);
01328                      ret = 0;
01329               }
01330        } else {
01331               state->cwd_length = path_length;
01332               tmp = realloc(state->cwd, state->cwd_length+1);
01333               if (tmp == NULL) {
01334 #if VIRTUAL_CWD_DEBUG
01335                      fprintf (stderr, "Out of memory\n");
01336 #endif
01337                      return 1;
01338               }
01339               state->cwd = (char *) tmp;
01340 
01341               memcpy(state->cwd, resolved_path, state->cwd_length+1);
01342               ret = 0;
01343        }
01344 
01345 #if VIRTUAL_CWD_DEBUG
01346        fprintf (stderr, "virtual_file_ex() = %s\n",state->cwd);
01347 #endif
01348        return (ret);
01349 }
01350 /* }}} */
01351 
01352 CWD_API int virtual_chdir(const char *path TSRMLS_DC) /* {{{ */
01353 {
01354        return virtual_file_ex(&CWDG(cwd), path, php_is_dir_ok, CWD_REALPATH)?-1:0;
01355 }
01356 /* }}} */
01357 
01358 CWD_API int virtual_chdir_file(const char *path, int (*p_chdir)(const char *path TSRMLS_DC) TSRMLS_DC) /* {{{ */
01359 {
01360        int length = strlen(path);
01361        char *temp;
01362        int retval;
01363        TSRM_ALLOCA_FLAG(use_heap)
01364 
01365        if (length == 0) {
01366               return 1; /* Can't cd to empty string */
01367        }
01368        while(--length >= 0 && !IS_SLASH(path[length])) {
01369        }
01370 
01371        if (length == -1) {
01372               /* No directory only file name */
01373               errno = ENOENT;
01374               return -1;
01375        }
01376 
01377        if (length == COPY_WHEN_ABSOLUTE(path) && IS_ABSOLUTE_PATH(path, length+1)) { /* Also use trailing slash if this is absolute */
01378               length++;
01379        }
01380        temp = (char *) tsrm_do_alloca(length+1, use_heap);
01381        memcpy(temp, path, length);
01382        temp[length] = 0;
01383 #if VIRTUAL_CWD_DEBUG
01384        fprintf (stderr, "Changing directory to %s\n", temp);
01385 #endif
01386        retval = p_chdir(temp TSRMLS_CC);
01387        tsrm_free_alloca(temp, use_heap);
01388        return retval;
01389 }
01390 /* }}} */
01391 
01392 CWD_API char *virtual_realpath(const char *path, char *real_path TSRMLS_DC) /* {{{ */
01393 {
01394        cwd_state new_state;
01395        char *retval;
01396        char cwd[MAXPATHLEN];
01397 
01398        /* realpath("") returns CWD */
01399        if (!*path) {
01400               new_state.cwd = (char*)malloc(1);
01401               if (new_state.cwd == NULL) {
01402                      retval = NULL;
01403                      goto end;
01404               }
01405               new_state.cwd[0] = '\0';
01406               new_state.cwd_length = 0;
01407               if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
01408                      path = cwd;
01409               }
01410        } else if (!IS_ABSOLUTE_PATH(path, strlen(path))) {
01411               CWD_STATE_COPY(&new_state, &CWDG(cwd));
01412        } else {
01413               new_state.cwd = (char*)malloc(1);
01414               if (new_state.cwd == NULL) {
01415                      retval = NULL;
01416                      goto end;
01417               }
01418               new_state.cwd[0] = '\0';
01419               new_state.cwd_length = 0;
01420        }
01421 
01422        if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)==0) {
01423               int len = new_state.cwd_length>MAXPATHLEN-1?MAXPATHLEN-1:new_state.cwd_length;
01424 
01425               memcpy(real_path, new_state.cwd, len);
01426               real_path[len] = '\0';
01427               retval = real_path;
01428        } else {
01429               retval = NULL;
01430        }
01431 
01432        CWD_STATE_FREE(&new_state);
01433 end:
01434        return retval;
01435 }
01436 /* }}} */
01437 
01438 CWD_API int virtual_filepath_ex(const char *path, char **filepath, verify_path_func verify_path TSRMLS_DC) /* {{{ */
01439 {
01440        cwd_state new_state;
01441        int retval;
01442 
01443        CWD_STATE_COPY(&new_state, &CWDG(cwd));
01444        retval = virtual_file_ex(&new_state, path, verify_path, CWD_FILEPATH);
01445 
01446        *filepath = new_state.cwd;
01447 
01448        return retval;
01449 
01450 }
01451 /* }}} */
01452 
01453 CWD_API int virtual_filepath(const char *path, char **filepath TSRMLS_DC) /* {{{ */
01454 {
01455        return virtual_filepath_ex(path, filepath, php_is_file_ok TSRMLS_CC);
01456 }
01457 /* }}} */
01458 
01459 CWD_API FILE *virtual_fopen(const char *path, const char *mode TSRMLS_DC) /* {{{ */
01460 {
01461        cwd_state new_state;
01462        FILE *f;
01463 
01464        if (path[0] == '\0') { /* Fail to open empty path */
01465               return NULL;
01466        }
01467 
01468        CWD_STATE_COPY(&new_state, &CWDG(cwd));
01469        if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
01470               CWD_STATE_FREE(&new_state);
01471               return NULL;
01472        }
01473 
01474        f = fopen(new_state.cwd, mode);
01475 
01476        CWD_STATE_FREE(&new_state);
01477        return f;
01478 }
01479 /* }}} */
01480 
01481 CWD_API int virtual_access(const char *pathname, int mode TSRMLS_DC) /* {{{ */
01482 {
01483        cwd_state new_state;
01484        int ret;
01485 
01486        CWD_STATE_COPY(&new_state, &CWDG(cwd));
01487        if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH)) {
01488               CWD_STATE_FREE(&new_state);
01489               return -1;
01490        }
01491 
01492 #if defined(TSRM_WIN32)
01493        ret = tsrm_win32_access(new_state.cwd, mode);
01494 #else
01495        ret = access(new_state.cwd, mode);
01496 #endif
01497 
01498        CWD_STATE_FREE(&new_state);
01499 
01500        return ret;
01501 }
01502 /* }}} */
01503 
01504 #if HAVE_UTIME
01505 #ifdef TSRM_WIN32
01506 static void UnixTimeToFileTime(time_t t, LPFILETIME pft) /* {{{ */
01507 {
01508        // Note that LONGLONG is a 64-bit value
01509        LONGLONG ll;
01510 
01511        ll = Int32x32To64(t, 10000000) + 116444736000000000;
01512        pft->dwLowDateTime = (DWORD)ll;
01513        pft->dwHighDateTime = ll >> 32;
01514 }
01515 /* }}} */
01516 
01517 TSRM_API int win32_utime(const char *filename, struct utimbuf *buf) /* {{{ */
01518 {
01519        FILETIME mtime, atime;
01520        HANDLE hFile;
01521 
01522        hFile = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL,
01523                              OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);
01524 
01525        /* OPEN_ALWAYS mode sets the last error to ERROR_ALREADY_EXISTS but
01526           the CreateFile operation succeeds */
01527        if (GetLastError() == ERROR_ALREADY_EXISTS) {
01528               SetLastError(0);
01529        }
01530 
01531        if ( hFile == INVALID_HANDLE_VALUE ) {
01532               return -1;
01533        }
01534 
01535        if (!buf) {
01536               SYSTEMTIME st;
01537               GetSystemTime(&st);
01538               SystemTimeToFileTime(&st, &mtime);
01539               atime = mtime;
01540        } else {
01541               UnixTimeToFileTime(buf->modtime, &mtime);
01542               UnixTimeToFileTime(buf->actime, &atime);
01543        }
01544        if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
01545               CloseHandle(hFile);
01546               return -1;
01547        }
01548        CloseHandle(hFile);
01549        return 1;
01550 }
01551 /* }}} */
01552 #endif
01553 
01554 CWD_API int virtual_utime(const char *filename, struct utimbuf *buf TSRMLS_DC) /* {{{ */
01555 {
01556        cwd_state new_state;
01557        int ret;
01558 
01559        CWD_STATE_COPY(&new_state, &CWDG(cwd));
01560        if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
01561               CWD_STATE_FREE(&new_state);
01562               return -1;
01563        }
01564 
01565 #ifdef TSRM_WIN32
01566        ret = win32_utime(new_state.cwd, buf);
01567 #else
01568        ret = utime(new_state.cwd, buf);
01569 #endif
01570 
01571        CWD_STATE_FREE(&new_state);
01572        return ret;
01573 }
01574 /* }}} */
01575 #endif
01576 
01577 CWD_API int virtual_chmod(const char *filename, mode_t mode TSRMLS_DC) /* {{{ */
01578 {
01579        cwd_state new_state;
01580        int ret;
01581 
01582        CWD_STATE_COPY(&new_state, &CWDG(cwd));
01583        if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
01584               CWD_STATE_FREE(&new_state);
01585               return -1;
01586        }
01587 
01588        ret = chmod(new_state.cwd, mode);
01589 
01590        CWD_STATE_FREE(&new_state);
01591        return ret;
01592 }
01593 /* }}} */
01594 
01595 #if !defined(TSRM_WIN32) && !defined(NETWARE)
01596 CWD_API int virtual_chown(const char *filename, uid_t owner, gid_t group, int link TSRMLS_DC) /* {{{ */
01597 {
01598        cwd_state new_state;
01599        int ret;
01600 
01601        CWD_STATE_COPY(&new_state, &CWDG(cwd));
01602        if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
01603               CWD_STATE_FREE(&new_state);
01604               return -1;
01605        }
01606 
01607        if (link) {
01608 #if HAVE_LCHOWN
01609               ret = lchown(new_state.cwd, owner, group);
01610 #else
01611               ret = -1;
01612 #endif
01613        } else {
01614               ret = chown(new_state.cwd, owner, group);
01615        }
01616 
01617        CWD_STATE_FREE(&new_state);
01618        return ret;
01619 }
01620 /* }}} */
01621 #endif
01622 
01623 CWD_API int virtual_open(const char *path TSRMLS_DC, int flags, ...) /* {{{ */
01624 {
01625        cwd_state new_state;
01626        int f;
01627 
01628        CWD_STATE_COPY(&new_state, &CWDG(cwd));
01629        if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
01630               CWD_STATE_FREE(&new_state);
01631               return -1;
01632        }
01633 
01634        if (flags & O_CREAT) {
01635               mode_t mode;
01636               va_list arg;
01637 
01638               va_start(arg, flags);
01639               mode = (mode_t) va_arg(arg, int);
01640               va_end(arg);
01641 
01642               f = open(new_state.cwd, flags, mode);
01643        } else {
01644               f = open(new_state.cwd, flags);
01645        }
01646        CWD_STATE_FREE(&new_state);
01647        return f;
01648 }
01649 /* }}} */
01650 
01651 CWD_API int virtual_creat(const char *path, mode_t mode TSRMLS_DC) /* {{{ */
01652 {
01653        cwd_state new_state;
01654        int f;
01655 
01656        CWD_STATE_COPY(&new_state, &CWDG(cwd));
01657        if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
01658               CWD_STATE_FREE(&new_state);
01659               return -1;
01660        }
01661 
01662        f = creat(new_state.cwd,  mode);
01663 
01664        CWD_STATE_FREE(&new_state);
01665        return f;
01666 }
01667 /* }}} */
01668 
01669 CWD_API int virtual_rename(char *oldname, char *newname TSRMLS_DC) /* {{{ */
01670 {
01671        cwd_state old_state;
01672        cwd_state new_state;
01673        int retval;
01674 
01675        CWD_STATE_COPY(&old_state, &CWDG(cwd));
01676        if (virtual_file_ex(&old_state, oldname, NULL, CWD_EXPAND)) {
01677               CWD_STATE_FREE(&old_state);
01678               return -1;
01679        }
01680        oldname = old_state.cwd;
01681 
01682        CWD_STATE_COPY(&new_state, &CWDG(cwd));
01683        if (virtual_file_ex(&new_state, newname, NULL, CWD_EXPAND)) {
01684               CWD_STATE_FREE(&old_state);
01685               CWD_STATE_FREE(&new_state);
01686               return -1;
01687        }
01688        newname = new_state.cwd;
01689 
01690        /* rename on windows will fail if newname already exists.
01691           MoveFileEx has to be used */
01692 #ifdef TSRM_WIN32
01693        /* MoveFileEx returns 0 on failure, other way 'round for this function */
01694        retval = (MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED) == 0) ? -1 : 0;
01695 #else
01696        retval = rename(oldname, newname);
01697 #endif
01698 
01699        CWD_STATE_FREE(&old_state);
01700        CWD_STATE_FREE(&new_state);
01701 
01702        return retval;
01703 }
01704 /* }}} */
01705 
01706 CWD_API int virtual_stat(const char *path, struct stat *buf TSRMLS_DC) /* {{{ */
01707 {
01708        cwd_state new_state;
01709        int retval;
01710 
01711        CWD_STATE_COPY(&new_state, &CWDG(cwd));
01712        if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
01713               CWD_STATE_FREE(&new_state);
01714               return -1;
01715        }
01716 
01717        retval = php_sys_stat(new_state.cwd, buf);
01718 
01719        CWD_STATE_FREE(&new_state);
01720        return retval;
01721 }
01722 /* }}} */
01723 
01724 CWD_API int virtual_lstat(const char *path, struct stat *buf TSRMLS_DC) /* {{{ */
01725 {
01726        cwd_state new_state;
01727        int retval;
01728 
01729        CWD_STATE_COPY(&new_state, &CWDG(cwd));
01730        if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND)) {
01731               CWD_STATE_FREE(&new_state);
01732               return -1;
01733        }
01734 
01735        retval = php_sys_lstat(new_state.cwd, buf);
01736 
01737        CWD_STATE_FREE(&new_state);
01738        return retval;
01739 }
01740 /* }}} */
01741 
01742 CWD_API int virtual_unlink(const char *path TSRMLS_DC) /* {{{ */
01743 {
01744        cwd_state new_state;
01745        int retval;
01746 
01747        CWD_STATE_COPY(&new_state, &CWDG(cwd));
01748        if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND)) {
01749               CWD_STATE_FREE(&new_state);
01750               return -1;
01751        }
01752 
01753        retval = unlink(new_state.cwd);
01754 
01755        CWD_STATE_FREE(&new_state);
01756        return retval;
01757 }
01758 /* }}} */
01759 
01760 CWD_API int virtual_mkdir(const char *pathname, mode_t mode TSRMLS_DC) /* {{{ */
01761 {
01762        cwd_state new_state;
01763        int retval;
01764 
01765        CWD_STATE_COPY(&new_state, &CWDG(cwd));
01766        if (virtual_file_ex(&new_state, pathname, NULL, CWD_FILEPATH)) {
01767               CWD_STATE_FREE(&new_state);
01768               return -1;
01769        }
01770 
01771 #ifdef TSRM_WIN32
01772        retval = mkdir(new_state.cwd);
01773 #else
01774        retval = mkdir(new_state.cwd, mode);
01775 #endif
01776        CWD_STATE_FREE(&new_state);
01777        return retval;
01778 }
01779 /* }}} */
01780 
01781 CWD_API int virtual_rmdir(const char *pathname TSRMLS_DC) /* {{{ */
01782 {
01783        cwd_state new_state;
01784        int retval;
01785 
01786        CWD_STATE_COPY(&new_state, &CWDG(cwd));
01787        if (virtual_file_ex(&new_state, pathname, NULL, CWD_EXPAND)) {
01788               CWD_STATE_FREE(&new_state);
01789               return -1;
01790        }
01791 
01792        retval = rmdir(new_state.cwd);
01793 
01794        CWD_STATE_FREE(&new_state);
01795        return retval;
01796 }
01797 /* }}} */
01798 
01799 #ifdef TSRM_WIN32
01800 DIR *opendir(const char *name);
01801 #endif
01802 
01803 CWD_API DIR *virtual_opendir(const char *pathname TSRMLS_DC) /* {{{ */
01804 {
01805        cwd_state new_state;
01806        DIR *retval;
01807 
01808        CWD_STATE_COPY(&new_state, &CWDG(cwd));
01809        if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH)) {
01810               CWD_STATE_FREE(&new_state);
01811               return NULL;
01812        }
01813 
01814        retval = opendir(new_state.cwd);
01815 
01816        CWD_STATE_FREE(&new_state);
01817        return retval;
01818 }
01819 /* }}} */
01820 
01821 #ifdef TSRM_WIN32
01822 CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */
01823 {
01824        return popen_ex(command, type, CWDG(cwd).cwd, NULL);
01825 }
01826 /* }}} */
01827 #elif defined(NETWARE)
01828 /* On NetWare, the trick of prepending "cd cwd; " doesn't work so we need to perform
01829    a VCWD_CHDIR() and mutex it
01830  */
01831 CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */
01832 {
01833        char prev_cwd[MAXPATHLEN];
01834        char *getcwd_result;
01835        FILE *retval;
01836 
01837        getcwd_result = VCWD_GETCWD(prev_cwd, MAXPATHLEN);
01838        if (!getcwd_result) {
01839               return NULL;
01840        }
01841 
01842 #ifdef ZTS
01843        tsrm_mutex_lock(cwd_mutex);
01844 #endif
01845 
01846        VCWD_CHDIR(CWDG(cwd).cwd);
01847        retval = popen(command, type);
01848        VCWD_CHDIR(prev_cwd);
01849 
01850 #ifdef ZTS
01851        tsrm_mutex_unlock(cwd_mutex);
01852 #endif
01853 
01854        return retval;
01855 }
01856 /* }}} */
01857 #else /* Unix */
01858 CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */
01859 {
01860        int command_length;
01861        int dir_length, extra = 0;
01862        char *command_line;
01863        char *ptr, *dir;
01864        FILE *retval;
01865 
01866        command_length = strlen(command);
01867 
01868        dir_length = CWDG(cwd).cwd_length;
01869        dir = CWDG(cwd).cwd;
01870        while (dir_length > 0) {
01871               if (*dir == '\'') extra+=3;
01872               dir++;
01873               dir_length--;
01874        }
01875        dir_length = CWDG(cwd).cwd_length;
01876        dir = CWDG(cwd).cwd;
01877 
01878        ptr = command_line = (char *) malloc(command_length + sizeof("cd '' ; ") + dir_length + extra+1+1);
01879        if (!command_line) {
01880               return NULL;
01881        }
01882        memcpy(ptr, "cd ", sizeof("cd ")-1);
01883        ptr += sizeof("cd ")-1;
01884 
01885        if (CWDG(cwd).cwd_length == 0) {
01886               *ptr++ = DEFAULT_SLASH;
01887        } else {
01888               *ptr++ = '\'';
01889               while (dir_length > 0) {
01890                      switch (*dir) {
01891                      case '\'':
01892                             *ptr++ = '\'';
01893                             *ptr++ = '\\';
01894                             *ptr++ = '\'';
01895                             /* fall-through */
01896                      default:
01897                             *ptr++ = *dir;
01898                      }
01899                      dir++;
01900                      dir_length--;
01901               }
01902               *ptr++ = '\'';
01903        }
01904 
01905        *ptr++ = ' ';
01906        *ptr++ = ';';
01907        *ptr++ = ' ';
01908 
01909        memcpy(ptr, command, command_length+1);
01910        retval = popen(command_line, type);
01911 
01912        free(command_line);
01913        return retval;
01914 }
01915 /* }}} */
01916 #endif
01917 
01918 CWD_API char *tsrm_realpath(const char *path, char *real_path TSRMLS_DC) /* {{{ */
01919 {
01920        cwd_state new_state;
01921        char cwd[MAXPATHLEN];
01922 
01923        /* realpath("") returns CWD */
01924        if (!*path) {
01925               new_state.cwd = (char*)malloc(1);
01926               if (new_state.cwd == NULL) {
01927                      return NULL;
01928               }
01929               new_state.cwd[0] = '\0';
01930               new_state.cwd_length = 0;
01931               if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
01932                      path = cwd;
01933               }
01934        } else if (!IS_ABSOLUTE_PATH(path, strlen(path)) &&
01935                                    VCWD_GETCWD(cwd, MAXPATHLEN)) {
01936               new_state.cwd = strdup(cwd);
01937               new_state.cwd_length = strlen(cwd);
01938        } else {
01939               new_state.cwd = (char*)malloc(1);
01940               if (new_state.cwd == NULL) {
01941                      return NULL;
01942               }
01943               new_state.cwd[0] = '\0';
01944               new_state.cwd_length = 0;
01945        }
01946 
01947        if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
01948               free(new_state.cwd);
01949               return NULL;
01950        }
01951 
01952        if (real_path) {
01953               int copy_len = new_state.cwd_length>MAXPATHLEN-1 ? MAXPATHLEN-1 : new_state.cwd_length;
01954               memcpy(real_path, new_state.cwd, copy_len);
01955               real_path[copy_len] = '\0';
01956               free(new_state.cwd);
01957               return real_path;
01958        } else {
01959               return new_state.cwd;
01960        }
01961 }
01962 /* }}} */
01963 
01964 /*
01965  * Local variables:
01966  * tab-width: 4
01967  * c-basic-offset: 4
01968  * End:
01969  */