/* +----------------------------------------------------------------------+ | 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. | +----------------------------------------------------------------------+ | Authors: Rasmus Lerdorf | | Jim Winstead | +----------------------------------------------------------------------+ */ /* $Id$ */ #ifdef THREAD_SAFE #include "tls.h" #endif #include "php.h" #include "php_globals.h" #include #include #include #include #include #include #if MSVC5 #include #include #define O_RDONLY _O_RDONLY #include "win32/param.h" #else #include #endif #include "safe_mode.h" #include "php3_realpath.h" #include "functions/head.h" #include "ext/standard/php3_standard.h" #include "zend_compile.h" #if HAVE_PWD_H #if MSVC5 #include "win32/pwd.h" #else #include #endif #endif #include #if HAVE_SYS_SOCKET_H #include #endif #ifndef S_ISREG #define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) #endif #if MSVC5 #include #else #include #include #include #endif #if MSVC5 #undef AF_UNIX #endif #if defined(AF_UNIX) #include #endif static FILE *php3_fopen_url_wrapper(const char *path, char *mode, int options, int *issock, int *socketd); int _php3_getftpresult(int socketd); /* When open_basedir is not NULL, check if the given filename is located in open_basedir. Returns -1 if error or not in the open_basedir, else 0 When open_basedir is NULL, always return 0 */ PHPAPI int _php3_check_open_basedir(char *path) { char resolved_name[MAXPATHLEN]; char local_open_basedir[MAXPATHLEN]; int local_open_basedir_pos; /* Only check when open_basedir is available */ if (PG(open_basedir) && *PG(open_basedir)) { /* Special case basedir==".": Use script-directory */ if ((strcmp(PG(open_basedir), ".") == 0) && GLOBAL(request_info).filename && *GLOBAL(request_info).filename ) { strcpy(local_open_basedir, GLOBAL(request_info).filename); local_open_basedir_pos = strlen(local_open_basedir) - 1; /* Strip filename */ while (( #if WIN32|WINNT (local_open_basedir[local_open_basedir_pos] != '\\') || #endif (local_open_basedir[local_open_basedir_pos] != '/') ) && (local_open_basedir_pos >= 0) ) { local_open_basedir[local_open_basedir_pos--] = 0; } /* Strip double (back)slashes */ if (local_open_basedir_pos > 0) { while (( #if WIN32|WINNT (local_open_basedir[local_open_basedir_pos-1] == '\\') || #endif (local_open_basedir[local_open_basedir_pos-1] == '/') ) && (local_open_basedir_pos > 0) ) { local_open_basedir[local_open_basedir_pos--] = 0; } } } else { /* Else use the unmodified path */ strcpy(local_open_basedir, PG(open_basedir)); } /* Resolve the real path into resolved_name */ if (_php3_realpath(path, resolved_name) != NULL) { /* Check the path */ #if WIN32|WINNT if (strncasecmp(local_open_basedir, resolved_name, strlen(local_open_basedir)) == 0) { #else if (strncmp(local_open_basedir, resolved_name, strlen(local_open_basedir)) == 0) { #endif /* File is in the right directory */ return 0; } else { php3_error(E_WARNING, "open_basedir restriction in effect. File is in wrong directory."); return -1; } } else { /* Unable to resolve the real path, return -1 */ php3_error(E_WARNING, "open_basedir restriction in effect. Unable to verify location of file."); return -1; } } else { /* open_basedir is not available, return 0 */ return 0; } } PHPAPI FILE *php3_fopen_wrapper(char *path, char *mode, int options, int *issock, int *socketd) { int cm=2; /* checkuid mode: 2 = if file does not exist, check directory */ #if PHP3_URL_FOPEN if (!(options & IGNORE_URL)) { return php3_fopen_url_wrapper(path, mode, options, issock, socketd); } #endif if (options & USE_PATH && PG(include_path) != NULL) { return php3_fopen_with_path(path, mode, PG(include_path), NULL); } else { if(!strcmp(mode,"r") || !strcmp(mode,"r+")) cm=0; if (options & ENFORCE_SAFE_MODE && PG(safe_mode) && (!_php3_checkuid(path, cm))) { return NULL; } if (_php3_check_open_basedir(path)) return NULL; return fopen(path, mode); } } #if CGI_BINARY || FHTTPD || USE_SAPI FILE *php3_fopen_for_parser(void) { FILE *fp; struct stat st; char *temp, *path_info, *fn; int l; TLS_VARS; fn = GLOBAL(request_info).filename; path_info = GLOBAL(request_info).path_info; #if HAVE_PWD_H if (PG(user_dir) && *PG(user_dir) && path_info && '/' == path_info[0] && '~' == path_info[1]) { char user[32]; struct passwd *pw; char *s = strchr(path_info + 2, '/'); fn = NULL; /* discard the original filename, it must not be used */ if (s) { /* if there is no path name after the file, do not bother to try open the directory */ l = s - (path_info + 2); if (l > sizeof(user) - 1) l = sizeof(user) - 1; memcpy(user, path_info + 2, l); user[l] = '\0'; pw = getpwnam(user); if (pw && pw->pw_dir) { fn = emalloc(strlen(PG(user_dir)) + strlen(path_info) + strlen(pw->pw_dir) + 4); if (fn) { strcpy(fn, pw->pw_dir); /* safe */ strcat(fn, "/"); /* safe */ strcat(fn, PG(user_dir)); /* safe */ strcat(fn, "/"); /* safe */ strcat(fn, s + 1); /* safe (shorter than path_info) */ STR_FREE(GLOBAL(request_info).filename); GLOBAL(request_info).filename = fn; } } } } else #endif #if WIN32 if (PG(doc_root) && path_info && ('/' == *PG(doc_root) || '\\' == *PG(doc_root) || strstr(PG(doc_root),":\\") || strstr(PG(doc_root),":/"))) { #else if (PG(doc_root) && '/' == *PG(doc_root) && path_info) { #endif l = strlen(PG(doc_root)); fn = emalloc(l + strlen(path_info) + 2); if (fn) { memcpy(fn, PG(doc_root), l); if ('/' != fn[l - 1] || '\\' != fn[l - 1]) /* l is never 0 */ fn[l++] = '/'; if ('/' == path_info[0]) l--; strcpy(fn + l, path_info); STR_FREE(GLOBAL(request_info).filename); GLOBAL(request_info).filename = fn; } } /* if doc_root && path_info */ if (!fn) { /* we have to free request_info.filename here because php3_destroy_request_info assumes that it will get freed when the include_names hash is emptied, but we're not adding it in this case */ STR_FREE(GLOBAL(request_info).filename); GLOBAL(request_info).filename = NULL; return NULL; } fp = fopen(fn, "r"); /* refuse to open anything that is not a regular file */ if (fp && (0 > fstat(fileno(fp), &st) || !S_ISREG(st.st_mode))) { fclose(fp); fp = NULL; } if (!fp) { php3_error(E_CORE_ERROR, "Unable to open %s", fn); STR_FREE(GLOBAL(request_info).filename); /* for same reason as above */ return NULL; } temp = estrdup(fn); _php3_dirname(temp, strlen(temp)); if (*temp) { chdir(temp); } efree(temp); return fp; } #endif /* CGI_BINARY || USE_SAPI */ /* * Tries to open a file with a PATH-style list of directories. * If the filename starts with "." or "/", the path is ignored. */ PHPAPI FILE *php3_fopen_with_path(char *filename, char *mode, char *path, char **opened_path) { char *pathbuf, *ptr, *end; char trypath[MAXPATHLEN + 1]; struct stat sb; FILE *fp; int cm=2; TLS_VARS; if(!strcmp(mode,"r") || !strcmp(mode,"r+")) cm=0; if (opened_path) { *opened_path = NULL; } /* Relative path open */ if (*filename == '.') { if (PG(safe_mode) && (!_php3_checkuid(filename, cm))) { return NULL; } if (_php3_check_open_basedir(filename)) return NULL; fp = fopen(filename, mode); if (fp && opened_path) { *opened_path = expand_filepath(filename); } return fp; } /* Absolute path open - prepend document_root in safe mode */ #if WIN32|WINNT if ((*filename == '\\') || (*filename == '/') || (filename[1] == ':')) { #else if (*filename == '/') { #endif if (PG(safe_mode)) { if(PG(doc_root)) { snprintf(trypath, MAXPATHLEN, "%s%s", PG(doc_root), filename); } else { strncpy(trypath,filename,MAXPATHLEN); } if (!_php3_checkuid(trypath, cm)) { return NULL; } if (_php3_check_open_basedir(trypath)) return NULL; fp = fopen(trypath, mode); if (fp && opened_path) { *opened_path = expand_filepath(trypath); } return fp; } else { if (_php3_check_open_basedir(filename)) return NULL; return fopen(filename, mode); } } if (!path || (path && !*path)) { if (PG(safe_mode) && (!_php3_checkuid(filename, cm))) { return NULL; } if (_php3_check_open_basedir(filename)) return NULL; fp = fopen(filename, mode); if (fp && opened_path) { *opened_path = strdup(filename); } return fp; } pathbuf = estrdup(path); ptr = pathbuf; while (ptr && *ptr) { #if WIN32|WINNT end = strchr(ptr, ';'); #else end = strchr(ptr, ':'); #endif if (end != NULL) { *end = '\0'; end++; } snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename); if (PG(safe_mode)) { if (stat(trypath, &sb) == 0 && (!_php3_checkuid(trypath, cm))) { efree(pathbuf); return NULL; } } if ((fp = fopen(trypath, mode)) != NULL) { if (_php3_check_open_basedir(trypath)) { fclose(fp); efree(pathbuf); return NULL; } if (opened_path) { *opened_path = expand_filepath(trypath); } efree(pathbuf); return fp; } ptr = end; } efree(pathbuf); return NULL; } /* * If the specified path starts with "http://" (insensitive to case), * a socket is opened to the specified web server and a file pointer is * position at the start of the body of the response (past any headers). * This makes a HTTP/1.0 request, and knows how to pass on the username * and password for basic authentication. * * If the specified path starts with "ftp://" (insensitive to case), * a pair of sockets are used to request the specified file and a file * pointer to the requested file is returned. Passive mode ftp is used, * so if the server doesn't suppor this, it will fail! * * Otherwise, fopen is called as usual and the file pointer is returned. */ static FILE *php3_fopen_url_wrapper(const char *path, char *mode, int options, int *issock, int *socketd) { url *resource; int result; char *scratch; unsigned char *tmp; char tmp_line[256]; char location[256]; int chptr = 0; char *tpath, *ttpath; int body = 0; int reqok = 0; int lineone = 1; int i; char buf[2]; char oldch1 = 0; char oldch2 = 0; char oldch3 = 0; char oldch4 = 0; char oldch5 = 0; FILE *fp = NULL; struct sockaddr_in server; unsigned short portno; char winfeof; if (!strncasecmp(path, "http://", 7)) { resource = url_parse((char *) path); if (resource == NULL) { php3_error(E_WARNING, "Invalid URL specified, %s", path); *issock = BAD_URL; return NULL; } /* use port 80 if one wasn't specified */ if (resource->port == 0) resource->port = 80; *socketd = socket(AF_INET, SOCK_STREAM, 0); if (*socketd == SOCK_ERR) { SOCK_FCLOSE(*socketd); *socketd = 0; free_url(resource); return NULL; } lookup_hostname(resource->host, &server.sin_addr); if (server.sin_addr.s_addr == -1) { SOCK_FCLOSE(*socketd); *socketd = 0; free_url(resource); return NULL; } server.sin_port = htons(resource->port); if (connect(*socketd, (struct sockaddr *) &server, sizeof(server)) == SOCK_CONN_ERR) { SOCK_FCLOSE(*socketd); *socketd = 0; free_url(resource); return NULL; } #if 0 if ((fp = fdopen(*socketd, "r+")) == NULL) { free_url(resource); return NULL; } #ifdef HAVE_SETVBUF if ((setvbuf(fp, NULL, _IONBF, 0)) != 0) { free_url(resource); return NULL; } #endif #endif /*win32 */ /* tell remote http which file to get */ SOCK_WRITE("GET ", *socketd); if (resource->path != NULL) { SOCK_WRITE(resource->path, *socketd); } else { SOCK_WRITE("/", *socketd); } /* append the query string, if any */ if (resource->query != NULL) { SOCK_WRITE("?", *socketd); SOCK_WRITE(resource->query, *socketd); } SOCK_WRITE(" HTTP/1.0\n", *socketd); /* send authorization header if we have user/pass */ if (resource->user != NULL && resource->pass != NULL) { scratch = (char *) emalloc(strlen(resource->user) + strlen(resource->pass) + 2); if (!scratch) { free_url(resource); return NULL; } strcpy(scratch, resource->user); strcat(scratch, ":"); strcat(scratch, resource->pass); tmp = _php3_base64_encode((unsigned char *)scratch, strlen(scratch), NULL); SOCK_WRITE("Authorization: Basic ", *socketd); /* output "user:pass" as base64-encoded string */ SOCK_WRITE((char *)tmp, *socketd); SOCK_WRITE("\n", *socketd); efree(scratch); efree(tmp); } /* if the user has configured who they are, send a From: line */ if (cfg_get_string("from", &scratch) == SUCCESS) { SOCK_WRITE("From: ", *socketd); SOCK_WRITE(scratch, *socketd); SOCK_WRITE("\n", *socketd); } /* send a Host: header so name-based virtual hosts work */ SOCK_WRITE("Host: ", *socketd); SOCK_WRITE(resource->host, *socketd); if(resource->port!=80) { sprintf(tmp_line,"%i",resource->port); SOCK_WRITE(":", *socketd); SOCK_WRITE(tmp_line, *socketd); } SOCK_WRITE("\n", *socketd); /* identify ourselves */ SOCK_WRITE("User-Agent: PHP/", *socketd); SOCK_WRITE(PHP_VERSION, *socketd); SOCK_WRITE("\n", *socketd); /* end the headers */ SOCK_WRITE("\n", *socketd); /* Read past http header */ body = 0; location[0] = '\0'; while (!body && recv(*socketd, (char *) &winfeof, 1, MSG_PEEK)) { if (SOCK_FGETC(buf, *socketd) == SOCK_RECV_ERR) { SOCK_FCLOSE(*socketd); *socketd = 0; free_url(resource); return NULL; } oldch5 = oldch4; oldch4 = oldch3; oldch3 = oldch2; oldch2 = oldch1; oldch1 = *buf; tmp_line[chptr++] = *buf; if (*buf == 10 || *buf == 13) { tmp_line[chptr] = '\0'; chptr = 0; if (!strncasecmp(tmp_line, "Location: ", 10)) { tpath = tmp_line + 10; strcpy(location, tpath); } } if (lineone && (*buf == 10 || *buf == 13)) { lineone = 0; } if (lineone && oldch5 == ' ' && oldch4 == '2' && oldch3 == '0' && oldch2 == '0' && oldch1 == ' ') { reqok = 1; } if (oldch4 == 13 && oldch3 == 10 && oldch2 == 13 && oldch1 == 10) { body = 1; } if (oldch2 == 10 && oldch1 == 10) { body = 1; } if (oldch2 == 13 && oldch1 == 13) { body = 1; } } if (!reqok) { SOCK_FCLOSE(*socketd); *socketd = 0; free_url(resource); if (location[0] != '\0') { return php3_fopen_url_wrapper(location, mode, options, issock, socketd); } else { return NULL; } } free_url(resource); *issock = 1; return (fp); } else if (!strncasecmp(path, "ftp://", 6)) { resource = url_parse((char *) path); if (resource == NULL) { php3_error(E_WARNING, "Invalid URL specified, %s", path); *issock = BAD_URL; return NULL; } else if (resource->path == NULL) { php3_error(E_WARNING, "No file-path specified"); free_url(resource); *issock = BAD_URL; return NULL; } /* use port 21 if one wasn't specified */ if (resource->port == 0) resource->port = 21; *socketd = socket(AF_INET, SOCK_STREAM, 0); if (*socketd == SOCK_ERR) { SOCK_FCLOSE(*socketd); *socketd = 0; free_url(resource); return NULL; } lookup_hostname(resource->host, &server.sin_addr); if (server.sin_addr.s_addr == -1) { SOCK_FCLOSE(*socketd); *socketd = 0; free_url(resource); return NULL; } server.sin_port = htons(resource->port); if (connect(*socketd, (struct sockaddr *) &server, sizeof(server)) == SOCK_CONN_ERR) { SOCK_FCLOSE(*socketd); *socketd = 0; free_url(resource); return NULL; } #if 0 if ((fpc = fdopen(*socketd, "r+")) == NULL) { free_url(resource); return NULL; } #ifdef HAVE_SETVBUF if ((setvbuf(fpc, NULL, _IONBF, 0)) != 0) { free_url(resource); fclose(fpc); return NULL; } #endif #endif /* Start talking to ftp server */ result = _php3_getftpresult(*socketd); if (result > 299 || result < 200) { free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; return NULL; } /* send the user name */ SOCK_WRITE("USER ", *socketd); if (resource->user != NULL) { _php3_rawurldecode(resource->user, strlen(resource->user)); SOCK_WRITE(resource->user, *socketd); } else { SOCK_WRITE("anonymous", *socketd); } SOCK_WRITE("\n", *socketd); /* get the response */ result = _php3_getftpresult(*socketd); /* if a password is required, send it */ if (result >= 300 && result <= 399) { SOCK_WRITE("PASS ", *socketd); if (resource->pass != NULL) { _php3_rawurldecode(resource->pass, strlen(resource->pass)); SOCK_WRITE(resource->pass, *socketd); } else { /* if the user has configured who they are, send that as the password */ if (cfg_get_string("from", &scratch) == SUCCESS) { SOCK_WRITE(scratch, *socketd); } else { SOCK_WRITE("anonymous", *socketd); } } SOCK_WRITE("\n", *socketd); /* read the response */ result = _php3_getftpresult(*socketd); if (result > 299 || result < 200) { free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; return NULL; } } else if (result > 299 || result < 200) { free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; return NULL; } /* find out the size of the file (verifying it exists) */ SOCK_WRITE("SIZE ", *socketd); SOCK_WRITE(resource->path, *socketd); SOCK_WRITE("\n", *socketd); /* read the response */ result = _php3_getftpresult(*socketd); if (mode[0] == 'r') { /* when reading file, it must exist */ if (result > 299 || result < 200) { php3_error(E_WARNING, "File not found"); free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; errno = ENOENT; return NULL; } } else { /* when writing file, it must NOT exist */ if (result <= 299 && result >= 200) { php3_error(E_WARNING, "File already exists"); free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; errno = EEXIST; return NULL; } } /* set the connection to be binary */ SOCK_WRITE("TYPE I\n", *socketd); result = _php3_getftpresult(*socketd); if (result > 299 || result < 200) { free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; return NULL; } /* set up the passive connection */ SOCK_WRITE("PASV\n", *socketd); while (SOCK_FGETS(tmp_line, 256, *socketd) && !(isdigit((int) tmp_line[0]) && isdigit((int) tmp_line[1]) && isdigit((int) tmp_line[2]) && tmp_line[3] == ' ')); /* make sure we got a 227 response */ if (strncmp(tmp_line, "227", 3)) { free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; return NULL; } /* parse pasv command (129,80,95,25,13,221) */ tpath = tmp_line; /* skip over the "227 Some message " part */ for (tpath += 4; *tpath && !isdigit((int) *tpath); tpath++); if (!*tpath) { free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; return NULL; } /* skip over the host ip, we just assume it's the same */ for (i = 0; i < 4; i++) { for (; isdigit((int) *tpath); tpath++); if (*tpath == ',') { tpath++; } else { SOCK_FCLOSE(*socketd); *socketd = 0; return NULL; } } /* pull out the MSB of the port */ portno = (unsigned short) strtol(tpath, &ttpath, 10) * 256; if (ttpath == NULL) { /* didn't get correct response from PASV */ free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; return NULL; } tpath = ttpath; if (*tpath == ',') { tpath++; } else { free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; return NULL; } /* pull out the LSB of the port */ portno += (unsigned short) strtol(tpath, &ttpath, 10); if (ttpath == NULL) { /* didn't get correct response from PASV */ free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; return NULL; } if (mode[0] == 'r') { /* retrieve file */ SOCK_WRITE("RETR ", *socketd); } else { /* store file */ SOCK_WRITE("STOR ", *socketd); } if (resource->path != NULL) { SOCK_WRITE(resource->path, *socketd); } else { SOCK_WRITE("/", *socketd); } /* close control connection */ SOCK_WRITE("\nQUIT\n", *socketd); SOCK_FCLOSE(*socketd); /* open the data channel */ *socketd = socket(AF_INET, SOCK_STREAM, 0); if (*socketd == SOCK_ERR) { SOCK_FCLOSE(*socketd); *socketd = 0; free_url(resource); return NULL; } lookup_hostname(resource->host, &server.sin_addr); if (server.sin_addr.s_addr == -1) { free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; return NULL; } server.sin_port = htons(portno); if (connect(*socketd, (struct sockaddr *) &server, sizeof(server)) == SOCK_CONN_ERR) { free_url(resource); SOCK_FCLOSE(*socketd); *socketd = 0; return NULL; } #if 0 if (mode[0] == 'r') { if ((fp = fdopen(*socketd, "r+")) == NULL) { free_url(resource); return NULL; } } else { if ((fp = fdopen(*socketd, "w+")) == NULL) { free_url(resource); return NULL; } } #ifdef HAVE_SETVBUF if ((setvbuf(fp, NULL, _IONBF, 0)) != 0) { free_url(resource); fclose(fp); return NULL; } #endif #endif free_url(resource); *issock = 1; return (fp); } else { if (options & USE_PATH) { fp = php3_fopen_with_path((char *) path, mode, PG(include_path), NULL); } else { int cm=2; if(!strcmp(mode,"r") || !strcmp(mode,"r+")) cm=0; if (options & ENFORCE_SAFE_MODE && PG(safe_mode) && (!_php3_checkuid(path, cm))) { fp = NULL; } else { if (_php3_check_open_basedir((char *) path)) { fp = NULL; } else { fp = fopen(path, mode); } } } *issock = 0; return (fp); } /* NOTREACHED */ SOCK_FCLOSE(*socketd); *socketd = 0; return NULL; } int _php3_getftpresult(int socketd) { char tmp_line[256]; while (SOCK_FGETS(tmp_line, 256, socketd) && !(isdigit((int) tmp_line[0]) && isdigit((int) tmp_line[1]) && isdigit((int) tmp_line[2]) && tmp_line[3] == ' ')); return strtol(tmp_line, NULL, 10); } PHPAPI int php3_isurl(char *path) { return (!strncasecmp(path, "http://", 7) || !strncasecmp(path, "ftp://", 6)); } PHPAPI char *php3_strip_url_passwd(char *url) { register char *p = url, *url_start; while (*p) { if (*p==':' && *(p+1)=='/' && *(p+2)=='/') { /* found protocol */ url_start = p = p+3; while (*p) { if (*p=='@') { int i; for (i=0; i<3 && url_start cwd && *cwd_end == '/') { /* remove trailing slashes */ *cwd_end-- = 0; } retval = (char *) malloc(strlen(cwd) + strlen(filepath) - 1 + 1); strcpy(retval, cwd); strcat(retval, filepath + 1); free(cwd); } } if (!retval) { retval = strdup(filepath); } return retval; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */