| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre - Copyright (c) Telecom ParisTech 2000-2026 | |
| * Romain Bouqueau - Copyright (c) Romain Bouqueau 2015 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / common tools sub-project | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC 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 Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include <gpac/tools.h> | |
| #include <gpac/utf.h> | |
| #if defined(_WIN32_WCE) | |
| #include <winbase.h> | |
| #include <tlhelp32.h> | |
| #elif defined(WIN32) | |
| #include <windows.h> | |
| #include <process.h> | |
| #include <direct.h> | |
| #include <sys/stat.h> | |
| #include <share.h> | |
| #else | |
| #include <sys/stat.h> | |
| #include <unistd.h> | |
| #include <dirent.h> | |
| #include <sys/time.h> | |
| #ifndef __BEOS__ | |
| #include <errno.h> | |
| #endif | |
| #endif | |
| GF_EXPORT | |
| GF_Err gf_rmdir(const char *DirPathName) | |
| { | |
| #if defined (_WIN32_WCE) | |
| TCHAR swzName[MAX_PATH]; | |
| BOOL res; | |
| CE_CharToWide(DirPathName, swzName); | |
| res = RemoveDirectory(swzName); | |
| if (! res) { | |
| int err = GetLastError(); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete directory \"%s\": last error %d\n", DirPathName, err )); | |
| } | |
| #elif defined (WIN32) | |
| int res; | |
| wchar_t* wcsDirPathName = gf_utf8_to_wcs(DirPathName); | |
| if (!wcsDirPathName) | |
| return GF_IO_ERR; | |
| res = _wrmdir(wcsDirPathName); | |
| gf_free(wcsDirPathName); | |
| if (res == -1) { | |
| int err = GetLastError(); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete directory \"%s\": last error %d\n", DirPathName, err )); | |
| return GF_IO_ERR; | |
| } | |
| #else | |
| int res = rmdir(DirPathName); | |
| if (res==-1) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete directory \"%s\": last error %d\n", DirPathName, errno )); | |
| return GF_IO_ERR; | |
| } | |
| #endif | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_mkdir(const char* DirPathName) | |
| { | |
| #if defined (_WIN32_WCE) | |
| TCHAR swzName[MAX_PATH]; | |
| BOOL res; | |
| CE_CharToWide(DirPathName, swzName); | |
| res = CreateDirectory(swzName, NULL); | |
| if (! res) { | |
| int err = GetLastError(); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create directory \"%s\": last error %d\n", DirPathName, err )); | |
| } | |
| #elif defined (WIN32) | |
| int res; | |
| //don't try creation for UNC root: \\foo\bar\name will trigger creation for "", "\\foo" and "\\foo\bar", ignore these | |
| if (!strcmp(DirPathName, "")) return GF_OK; | |
| if (!strncmp(DirPathName, "\\\\", 2)) { | |
| char *sep = strchr(DirPathName + 2, '\\'); | |
| if (!sep) return GF_OK; | |
| sep = strchr(sep + 1, '\\'); | |
| if (!sep) return GF_OK; | |
| } | |
| wchar_t* wcsDirPathName = gf_utf8_to_wcs(DirPathName); | |
| if (!wcsDirPathName) | |
| return GF_IO_ERR; | |
| res = _wmkdir(wcsDirPathName); | |
| gf_free(wcsDirPathName); | |
| if (res==-1) { | |
| int err = GetLastError(); | |
| if (err != 183) { | |
| // don't throw error if dir already exists | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create directory \"%s\": last error %d\n", DirPathName, err)); | |
| } | |
| } | |
| #else | |
| int res = mkdir(DirPathName, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); | |
| if (res==-1) { | |
| if(errno == 17) { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("Cannot create directory \"%s\", it already exists: last error %d \n", DirPathName, errno )); | |
| return GF_OK; | |
| } else { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create directory \"%s\": last error %d\n", DirPathName, errno )); | |
| return GF_IO_ERR; | |
| } | |
| } | |
| #endif | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| Bool gf_dir_exists(const char* DirPathName) | |
| { | |
| #if defined (_WIN32_WCE) | |
| TCHAR swzName[MAX_PATH]; | |
| DWORD att; | |
| CE_CharToWide(DirPathName, swzName); | |
| att = GetFileAttributes(swzName); | |
| return (att != INVALID_FILE_ATTRIBUTES && (att & FILE_ATTRIBUTE_DIRECTORY)) ? GF_TRUE : GF_FALSE; | |
| #elif defined (WIN32) | |
| DWORD att; | |
| wchar_t* wcsDirPathName = gf_utf8_to_wcs(DirPathName); | |
| if (!wcsDirPathName) | |
| return GF_FALSE; | |
| att = GetFileAttributesW(wcsDirPathName); | |
| gf_free(wcsDirPathName); | |
| return (att != INVALID_FILE_ATTRIBUTES && (att & FILE_ATTRIBUTE_DIRECTORY)) ? GF_TRUE : GF_FALSE; | |
| #else | |
| struct stat sb; | |
| if (stat(DirPathName, &sb) == 0 && S_ISDIR(sb.st_mode)) { | |
| return GF_TRUE; | |
| } | |
| return GF_FALSE; | |
| // DIR* dir = opendir(DirPathName); | |
| // if (!dir) return GF_FALSE; | |
| // closedir(dir); | |
| // return GF_TRUE; | |
| #endif | |
| return GF_FALSE; | |
| } | |
| static Bool delete_dir(void *cbck, char *item_name, char *item_path, GF_FileEnumInfo *file_info) | |
| { | |
| Bool directory_clean_mode = *(Bool*)cbck; | |
| if(directory_clean_mode) { | |
| gf_dir_cleanup(item_path); | |
| gf_rmdir(item_path); | |
| } else { | |
| gf_file_delete(item_path); | |
| } | |
| return GF_FALSE; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_dir_cleanup(const char* DirPathName) | |
| { | |
| Bool directory_clean_mode; | |
| directory_clean_mode = GF_TRUE; | |
| gf_enum_directory(DirPathName, GF_TRUE, delete_dir, &directory_clean_mode, NULL); | |
| directory_clean_mode = GF_FALSE; | |
| gf_enum_directory(DirPathName, GF_FALSE, delete_dir, &directory_clean_mode, NULL); | |
| return GF_OK; | |
| } | |
| #include <gpac/list.h> | |
| GF_List *gfio_delete_handlers = NULL; | |
| GF_EXPORT | |
| GF_Err gf_fileio_register_delete_proc(gfio_delete_proc del_proc) | |
| { | |
| if (!del_proc) return GF_OK; | |
| if (!gfio_delete_handlers) gfio_delete_handlers = gf_list_new(); | |
| if (!gfio_delete_handlers) return GF_OUT_OF_MEM; | |
| if (gf_list_find(gfio_delete_handlers, del_proc)>=0) return GF_BAD_PARAM; | |
| return gf_list_add(gfio_delete_handlers, del_proc); | |
| } | |
| GF_EXPORT | |
| void gf_fileio_unregister_delete_proc(gfio_delete_proc del_proc) | |
| { | |
| if (!del_proc || !gfio_delete_handlers) return; | |
| gf_list_del_item(gfio_delete_handlers, del_proc); | |
| if (!gf_list_count(gfio_delete_handlers)) { | |
| gf_list_del(gfio_delete_handlers); | |
| gfio_delete_handlers = NULL; | |
| } | |
| } | |
| GF_Err gf_fileio_file_delete(const char *fileName, const char *parent_gfio) | |
| { | |
| if (!gfio_delete_handlers || !fileName) return GF_OK; | |
| if (!parent_gfio) { | |
| if (strncmp(fileName, "gfio://", 7)) return GF_EOS; | |
| } else { | |
| if (strncmp(parent_gfio, "gfio://", 7)) return GF_EOS; | |
| } | |
| u32 i, count=gf_list_count(gfio_delete_handlers); | |
| for (i=0; i<count; i++) { | |
| gfio_delete_proc del_proc = gf_list_get(gfio_delete_handlers, i); | |
| GF_Err ret = del_proc(fileName, parent_gfio); | |
| if (ret==GF_EOS) continue; | |
| return ret; | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_file_delete(const char *fileName) | |
| { | |
| if (!fileName || !fileName[0]) { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("gf_file_delete with no param - ignoring\n")); | |
| return GF_OK; | |
| } | |
| if (!strncmp(fileName, "gfio://", 7)) { | |
| return gf_fileio_file_delete(fileName, NULL); | |
| } | |
| #if defined(_WIN32_WCE) | |
| TCHAR swzName[MAX_PATH]; | |
| CE_CharToWide((char*)fileName, swzName); | |
| return (DeleteFile(swzName)==0) ? GF_IO_ERR : GF_OK; | |
| #elif defined(WIN32) | |
| /* success if != 0 */ | |
| { | |
| BOOL op_result; | |
| wchar_t* wcsFileName = gf_utf8_to_wcs(fileName); | |
| if (!wcsFileName) | |
| return GF_IO_ERR; | |
| op_result = DeleteFileW(wcsFileName); | |
| gf_free(wcsFileName); | |
| return (op_result==0) ? GF_IO_ERR : GF_OK; | |
| } | |
| #else | |
| /* success is == 0 */ | |
| return ( remove(fileName) == 0) ? GF_OK : GF_IO_ERR; | |
| #endif | |
| } | |
| #ifndef WIN32 | |
| /** | |
| * Remove existing single-quote from a single-quoted string. | |
| * The caller is responsible for deallocating the returns string with gf_free() | |
| */ | |
| static char* gf_sanetize_single_quoted_string(const char *src) { | |
| int i, j; | |
| char *out = (char*)gf_malloc(4*strlen(src)+3); | |
| out[0] = '\''; | |
| for (i=0, j=1; (out[j]=src[i]); ++i, ++j) { | |
| if (src[i]=='\'') { | |
| out[++j]='\\'; | |
| out[++j]='\''; | |
| out[++j]='\''; | |
| } | |
| } | |
| out[j++] = '\''; | |
| out[j++] = 0; | |
| return out; | |
| } | |
| #endif | |
| #if defined(GPAC_CONFIG_IOS) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 80000) | |
| #include <spawn.h> | |
| extern char **environ; | |
| #endif | |
| #if defined(WIN32) | |
| #include <io.h> | |
| #endif | |
| GF_EXPORT | |
| Bool gf_file_exists_ex(const char *fileName, const char *par_name) | |
| { | |
| u32 gfio_type = 0; | |
| if (!fileName) return GF_FALSE; | |
| if (!strncmp(fileName, "gfio://", 7)) | |
| gfio_type = 1; | |
| else if (par_name && !strncmp(par_name, "gfio://", 7)) | |
| gfio_type = 2; | |
| if (gfio_type) { | |
| GF_FileIO *gfio_ref; | |
| GF_Err e; | |
| Bool res = GF_TRUE; | |
| if (gfio_type==1) | |
| gfio_ref = gf_fileio_from_url(fileName); | |
| else | |
| gfio_ref = gf_fileio_from_url(par_name); | |
| if (!gfio_ref) return GF_FALSE; | |
| gf_fileio_open_url(gfio_ref, (gfio_type==1) ? gf_fileio_resource_url(gfio_ref) : fileName, "probe", &e); | |
| if (e==GF_URL_ERROR) | |
| res = GF_FALSE; | |
| return res; | |
| } | |
| #if defined(WIN32) | |
| Bool res; | |
| wchar_t* wname = gf_utf8_to_wcs(fileName); | |
| if (!wname) return GF_FALSE; | |
| res = (_waccess(wname, 4) == -1) ? GF_FALSE : GF_TRUE; | |
| if (res == GF_TRUE) { | |
| DWORD att; | |
| att = GetFileAttributesW(wname); | |
| if (att != INVALID_FILE_ATTRIBUTES && (att & FILE_ATTRIBUTE_DIRECTORY)) | |
| res = GF_FALSE; | |
| } | |
| gf_free(wname); | |
| return res; | |
| #elif defined(GPAC_CONFIG_LINUX) || defined(GPAC_CONFIG_EMSCRIPTEN) | |
| Bool res = (access(fileName, 4) == -1) ? GF_FALSE : GF_TRUE; | |
| if (res && gf_dir_exists(fileName)) | |
| res = GF_FALSE; | |
| return res; | |
| #elif defined(__DARWIN__) || defined(__APPLE__) | |
| Bool res = (access(fileName, 4) == -1) ? GF_FALSE : GF_TRUE; | |
| if (res && gf_dir_exists(fileName)) | |
| res = GF_FALSE; | |
| return res; | |
| #elif defined(GPAC_CONFIG_IOS) || defined(GPAC_CONFIG_ANDROID) | |
| Bool res = (access(fileName, 4) == -1) ? GF_FALSE : GF_TRUE; | |
| if (res && gf_dir_exists(fileName)) | |
| res = GF_FALSE; | |
| return res; | |
| #else | |
| FILE *f = gf_fopen(fileName, "r"); | |
| if (f) { | |
| gf_fclose(f); | |
| return GF_TRUE; | |
| } | |
| return GF_FALSE; | |
| #endif | |
| } | |
| GF_EXPORT | |
| Bool gf_file_exists(const char *fileName) | |
| { | |
| return gf_file_exists_ex(fileName, NULL); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_file_move(const char *fileName, const char *newFileName) | |
| { | |
| #if defined(_WIN32_WCE) | |
| GF_Err e = GF_OK; | |
| TCHAR swzName[MAX_PATH]; | |
| TCHAR swzNewName[MAX_PATH]; | |
| CE_CharToWide((char*)fileName, swzName); | |
| CE_CharToWide((char*)newFileName, swzNewName); | |
| if (MoveFile(swzName, swzNewName) == 0 ) | |
| e = GF_IO_ERR; | |
| #elif defined(WIN32) | |
| GF_Err e = GF_OK; | |
| /* success if != 0 */ | |
| BOOL op_result; | |
| wchar_t* wcsFileName = gf_utf8_to_wcs(fileName); | |
| wchar_t* wcsNewFileName = gf_utf8_to_wcs(newFileName); | |
| if (!wcsFileName || !wcsNewFileName) { | |
| if (wcsFileName) gf_free(wcsFileName); | |
| if (wcsNewFileName) gf_free(wcsNewFileName); | |
| e = GF_IO_ERR; | |
| } else { | |
| op_result = MoveFileW(wcsFileName, wcsNewFileName); | |
| gf_free(wcsFileName); | |
| gf_free(wcsNewFileName); | |
| if ( op_result == 0 ) | |
| e = GF_IO_ERR; | |
| } | |
| #else | |
| GF_Err e = GF_IO_ERR; | |
| char cmd[1024], *arg1, *arg2; | |
| if (!fileName || !newFileName) { | |
| e = GF_IO_ERR; | |
| } else { | |
| //try direct rename - will fail if moving across file systems but faster for most use cases | |
| int res = rename(fileName, newFileName); | |
| if (!res) return GF_OK; | |
| //use mv command | |
| arg1 = gf_sanetize_single_quoted_string(fileName); | |
| arg2 = gf_sanetize_single_quoted_string(newFileName); | |
| if (snprintf(cmd, sizeof cmd, "mv %s %s", arg1, arg2) >= sizeof cmd) goto error; | |
| #if defined(GPAC_CONFIG_IOS) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 80000) | |
| { | |
| pid_t pid; | |
| char *argv[3]; | |
| argv[0] = "mv"; | |
| argv[1] = cmd; | |
| argv[2] = NULL; | |
| posix_spawn(&pid, argv[0], NULL, NULL, argv, environ); | |
| waitpid(pid, NULL, 0); | |
| } | |
| #else | |
| e = (system(cmd) == 0) ? GF_OK : GF_IO_ERR; | |
| #endif | |
| error: | |
| gf_free(arg1); | |
| gf_free(arg2); | |
| } | |
| #endif | |
| if (e) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Failed to move file %s to %s: %s\n", fileName, newFileName, gf_error_to_string(e) )); | |
| } | |
| return e; | |
| } | |
| GF_EXPORT | |
| u64 gf_file_modification_time(const char *filename) | |
| { | |
| #if defined(_WIN32_WCE) | |
| WCHAR _file[GF_MAX_PATH]; | |
| WIN32_FIND_DATA FindData; | |
| HANDLE fh; | |
| // ULARGE_INTEGER uli; | |
| ULONGLONG time_us; | |
| BOOL ret; | |
| CE_CharToWide((char *) filename, _file); | |
| fh = FindFirstFile(_file, &FindData); | |
| if (fh == INVALID_HANDLE_VALUE) return 0; | |
| // uli.LowPart = FindData.ftLastWriteTime.dwLowDateTime; | |
| // uli.HighPart = FindData.ftLastWriteTime.dwHighDateTime; | |
| time_us = (u64) ((*(LONGLONG *) &FindData.ftLastWriteTime - TIMESPEC_TO_FILETIME_OFFSET) / 10); | |
| ret = FindClose(fh); | |
| if (!ret) { | |
| DWORD err = GetLastError(); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_file_modification_time() error: %d\n", err)); | |
| } | |
| // time_ms = uli.QuadPart/10000; | |
| return time_us; | |
| #elif defined(WIN32) && !defined(__GNUC__) | |
| struct _stat64 st; | |
| int op_result; | |
| wchar_t* wcsFilename = gf_utf8_to_wcs(filename); | |
| if (!wcsFilename) | |
| return 0; | |
| op_result = _wstat64(wcsFilename, &st); | |
| gf_free(wcsFilename); | |
| if (op_result != 0) return 0; | |
| u64 time_us = st.st_mtime * 1000000; | |
| #if defined(GPAC_HAS_MTIM_NSEC) | |
| time_us += st.st_mtim.tv_nsec / 1000; | |
| #endif | |
| return time_us; | |
| #else | |
| struct stat st; | |
| if (stat(filename, &st) != 0) return 0; | |
| u64 time_us = st.st_mtime * 1000000; | |
| #if defined(__DARWIN__) || defined(__APPLE__) || defined(GPAC_CONFIG_IOS) | |
| time_us += st.st_mtimespec.tv_nsec / 1000; | |
| #elif defined(GPAC_HAS_MTIM_NSEC) | |
| time_us += st.st_mtim.tv_nsec / 1000; | |
| #elif defined(GPAC_CONFIG_ANDROID) | |
| time_us += st.st_mtime_nsec / 1000; | |
| #endif | |
| return time_us; | |
| #endif | |
| return 0; | |
| } | |
| #include <gpac/list.h> | |
| extern int gf_mem_track_enabled; | |
| static GF_List * gpac_open_files = NULL; | |
| typedef struct | |
| { | |
| FILE *ptr; | |
| char *url; | |
| Bool is_temp; | |
| } GF_FileHandle; | |
| static u32 gpac_file_handles = 0; | |
| GF_EXPORT | |
| u32 gf_file_handles_count() | |
| { | |
| #ifdef GPAC_MEMORY_TRACKING | |
| if (gpac_open_files) { | |
| return gf_list_count(gpac_open_files); | |
| } | |
| #endif | |
| return gpac_file_handles; | |
| } | |
| #include <gpac/thread.h> | |
| extern GF_Mutex *logs_mx; | |
| #ifdef GPAC_MEMORY_TRACKING | |
| const char *enum_open_handles(u32 *idx) | |
| { | |
| GF_FileHandle *h; | |
| gf_mx_p(logs_mx); | |
| u32 count = gf_list_count(gpac_open_files); | |
| if (*idx >= count) { | |
| gf_mx_v(logs_mx); | |
| return NULL; | |
| } | |
| h = gf_list_get(gpac_open_files, *idx); | |
| (*idx)++; | |
| gf_mx_v(logs_mx); | |
| return h->url; | |
| } | |
| #endif | |
| static void gf_register_file_handle(char *filename, FILE *ptr, Bool is_temp_file) | |
| { | |
| if (is_temp_file | |
| #ifdef GPAC_MEMORY_TRACKING | |
| || gf_mem_track_enabled | |
| #endif | |
| ) { | |
| GF_FileHandle *h; | |
| gf_mx_p(logs_mx); | |
| if (!gpac_open_files) gpac_open_files = gf_list_new(); | |
| GF_SAFEALLOC(h, GF_FileHandle); | |
| if (h) { | |
| h->ptr = ptr; | |
| if (is_temp_file) { | |
| h->is_temp = GF_TRUE; | |
| h->url = filename; | |
| } else { | |
| h->url = gf_strdup(filename); | |
| } | |
| gf_list_add(gpac_open_files, h); | |
| } | |
| gf_mx_v(logs_mx); | |
| } | |
| gpac_file_handles++; | |
| } | |
| static Bool gf_unregister_file_handle(FILE *ptr) | |
| { | |
| u32 i, count; | |
| Bool res = GF_FALSE; | |
| if (gpac_file_handles) | |
| gpac_file_handles--; | |
| if (!gpac_open_files) | |
| return GF_FALSE; | |
| gf_mx_p(logs_mx); | |
| count = gf_list_count(gpac_open_files); | |
| for (i=0; i<count; i++) { | |
| GF_FileHandle *h = gf_list_get(gpac_open_files, i); | |
| if (h->ptr != ptr) continue; | |
| if (h->is_temp) { | |
| GF_Err e; | |
| fclose(h->ptr); | |
| e = gf_file_delete(h->url); | |
| if (e) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[Core] Failed to delete temp file %s: %s\n", h->url, gf_error_to_string(e))); | |
| } | |
| res = GF_TRUE; | |
| } | |
| gf_free(h->url); | |
| gf_free(h); | |
| gf_list_rem(gpac_open_files, i); | |
| if (!gf_list_count(gpac_open_files)) { | |
| gf_list_del(gpac_open_files); | |
| gpac_open_files = NULL; | |
| } | |
| break; | |
| } | |
| gf_mx_v(logs_mx); | |
| return res; | |
| } | |
| static FILE *gf_file_temp_os(char ** const fileName) | |
| { | |
| FILE *res; | |
| #if defined(_WIN32_WCE) | |
| TCHAR pPath[MAX_PATH+1]; | |
| TCHAR pTemp[MAX_PATH+1]; | |
| res = NULL; | |
| if (!GetTempPath(MAX_PATH, pPath)) { | |
| pPath[0] = '.'; | |
| pPath[1] = '.'; | |
| } | |
| if (GetTempFileName(pPath, TEXT("git"), 0, pTemp)) | |
| res = _wfopen(pTemp, TEXT("w+b")); | |
| #elif defined(WIN32) | |
| res = tmpfile(); | |
| if (!res) { | |
| wchar_t tmp[MAX_PATH]; | |
| GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[Win32] system failure for tmpfile(): 0x%08x\n", GetLastError())); | |
| /*tmpfile() may fail under vista ...*/ | |
| if (GetEnvironmentVariableW(L"TEMP", tmp, MAX_PATH)) { | |
| wchar_t tmp2[MAX_PATH], *t_file; | |
| char* mbs_t_file; | |
| gf_rand_init(GF_FALSE); | |
| swprintf(tmp2, MAX_PATH, L"gpac_%d_%08x_", _getpid(), gf_rand()); | |
| t_file = _wtempnam(tmp, tmp2); | |
| mbs_t_file = gf_wcs_to_utf8(t_file); | |
| if (!mbs_t_file) | |
| return 0; | |
| res = gf_fopen(mbs_t_file, "w+b"); | |
| if (res) { | |
| gf_unregister_file_handle(res); | |
| if (fileName) { | |
| *fileName = gf_strdup(mbs_t_file); | |
| } else { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[Win32] temporary file \"%s\" won't be deleted - contact the GPAC team\n", mbs_t_file)); | |
| } | |
| } | |
| gf_free(mbs_t_file); | |
| free(t_file); | |
| } | |
| } | |
| #else | |
| res = tmpfile(); | |
| #endif | |
| if (res) { | |
| gf_register_file_handle("temp file", res, GF_FALSE); | |
| } | |
| return res; | |
| } | |
| GF_EXPORT | |
| FILE *gf_file_temp(char ** const fileName) | |
| { | |
| const char *tmp_dir = gf_opts_get_key("core", "tmp"); | |
| if (tmp_dir && tmp_dir[0]) { | |
| FILE *res; | |
| char *opath=NULL, szFName[100]; | |
| u32 len = (u32) strlen(tmp_dir); | |
| gf_dynstrcat(&opath, tmp_dir, NULL); | |
| if (!strchr("/\\", tmp_dir[len-1])) | |
| gf_dynstrcat(&opath, "/", NULL); | |
| if (!opath) return NULL; | |
| sprintf(szFName, "_libgpac_%d_%p_" LLU "_%d", gf_sys_get_process_id(), opath, gf_sys_clock_high_res(), gf_rand() ); | |
| gf_dynstrcat(&opath, szFName, NULL); | |
| if (fileName) { | |
| sprintf(szFName, "%p", fileName); | |
| gf_dynstrcat(&opath, szFName, "_"); | |
| } | |
| if (gf_file_exists(opath)) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Something went wrong creating temp file path %s, file already exists !\n", opath)); | |
| gf_free(opath); | |
| return NULL; | |
| } | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Opening new temp file %s\n", opath)); | |
| res = gf_fopen_ex(opath, "__temp_file", "w+b", GF_FALSE); | |
| if (!res) { | |
| gf_free(opath); | |
| return NULL; | |
| } | |
| gf_register_file_handle(opath, res, GF_TRUE); | |
| return res; | |
| } | |
| return gf_file_temp_os(fileName); | |
| } | |
| /*enumerate directories*/ | |
| GF_EXPORT | |
| GF_Err gf_enum_directory(const char *dir, Bool enum_directory, gf_enum_dir_item enum_dir_fct, void *cbck, const char *filter) | |
| { | |
| #ifdef WIN32 | |
| wchar_t item_path[GF_MAX_PATH]; | |
| #else | |
| char item_path[GF_MAX_PATH]; | |
| #endif | |
| GF_FileEnumInfo file_info; | |
| #if defined(_WIN32_WCE) | |
| char _path[GF_MAX_PATH]; | |
| unsigned short path[GF_MAX_PATH]; | |
| unsigned short w_filter[GF_MAX_PATH]; | |
| char file[GF_MAX_PATH]; | |
| #elif defined(WIN32) | |
| wchar_t path[GF_MAX_PATH], *file; | |
| wchar_t w_filter[GF_MAX_PATH]; | |
| char *mbs_file, *mbs_item_path; | |
| char _path[GF_MAX_PATH]; | |
| const char* tmpdir; | |
| #else | |
| char path[GF_MAX_PATH], *file; | |
| #endif | |
| #ifdef WIN32 | |
| WIN32_FIND_DATAW FindData; | |
| HANDLE SearchH; | |
| #else | |
| DIR *the_dir; | |
| struct dirent* the_file; | |
| struct stat st; | |
| #endif | |
| if (!dir || !strlen(dir) || !enum_dir_fct) return GF_BAD_PARAM; | |
| if (filter && (!strcmp(filter, "*") || !filter[0])) filter=NULL; | |
| memset(&file_info, 0, sizeof(GF_FileEnumInfo) ); | |
| if (!strcmp(dir, "/")) { | |
| #if defined(WIN32) && !defined(_WIN32_WCE) | |
| u32 len; | |
| char *drives, *volume; | |
| len = GetLogicalDriveStrings(0, NULL); | |
| drives = (char*)gf_malloc(sizeof(char)*(len+1)); | |
| drives[0]=0; | |
| GetLogicalDriveStrings(len, drives); | |
| len = (u32) strlen(drives); | |
| volume = drives; | |
| file_info.directory = GF_TRUE; | |
| file_info.drive = GF_TRUE; | |
| while (len) { | |
| enum_dir_fct(cbck, volume, "", &file_info); | |
| volume += len+1; | |
| len = (u32) strlen(volume); | |
| } | |
| gf_free(drives); | |
| return GF_OK; | |
| #elif defined(__SYMBIAN32__) | |
| RFs iFs; | |
| TDriveList aList; | |
| iFs.Connect(); | |
| iFs.DriveList(aList); | |
| for (TInt i=0; i<KMaxDrives; i++) { | |
| if (aList[i]) { | |
| char szDrive[10]; | |
| TChar aDrive; | |
| iFs.DriveToChar(i, aDrive); | |
| sprintf(szDrive, "%c:", (TUint)aDrive); | |
| enum_dir_fct(cbck, szDrive, "", &file_info); | |
| } | |
| } | |
| iFs.Close(); | |
| FlushItemList(); | |
| return GF_OK; | |
| #elif defined(GPAC_CONFIG_ANDROID) || defined(GPAC_CONFIG_IOS) | |
| dir = (char *) gf_opts_get_key("core", "docs-dir"); | |
| #endif | |
| } | |
| #if defined (_WIN32_WCE) | |
| switch (dir[strlen(dir) - 1]) { | |
| case '/': | |
| case '\\': | |
| sprintf(_path, "%s*", dir); | |
| break; | |
| default: | |
| sprintf(_path, "%s%c*", dir, GF_PATH_SEPARATOR); | |
| break; | |
| } | |
| CE_CharToWide(_path, path); | |
| CE_CharToWide((char *)filter, w_filter); | |
| #elif defined(WIN32) | |
| gf_strcpy(_path, dir); | |
| switch (dir[strlen(dir)] - 1) { | |
| case '/': | |
| case '\\': | |
| snprintf(_path, MAX_PATH, "%s*", dir); | |
| break; | |
| default: | |
| snprintf(_path, MAX_PATH, "%s%c*", dir, GF_PATH_SEPARATOR); | |
| break; | |
| } | |
| tmpdir = _path; | |
| if (gf_utf8_mbstowcs(path, GF_MAX_PATH, &tmpdir) == GF_UTF8_FAIL) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot convert %s to UTF16: broken string\n", dir)); | |
| return GF_BAD_PARAM; | |
| } | |
| tmpdir = filter; | |
| if (gf_utf8_mbstowcs(w_filter, sizeof(w_filter), &tmpdir) == GF_UTF8_FAIL) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot convert %s to UTF16: broken string\n", filter)); | |
| return GF_BAD_PARAM; | |
| } | |
| #else | |
| size_t dir_len = strlen(dir); | |
| if (dir_len < GF_ARRAY_LENGTH(path)) { | |
| gf_strcpy(path, dir); | |
| } | |
| else { | |
| memcpy(path, dir, GF_ARRAY_LENGTH(path)); | |
| path[ GF_ARRAY_LENGTH(path) - 1] = 0; | |
| } | |
| size_t path_len = strlen(path); | |
| if (path_len && path[path_len-1] != '/') { | |
| if (path_len + 1 >= GF_ARRAY_LENGTH(path)) | |
| return GF_BAD_PARAM; | |
| gf_strcat(path, "/"); | |
| } | |
| #endif | |
| #ifdef WIN32 | |
| SearchH= FindFirstFileW(path, &FindData); | |
| if (SearchH == INVALID_HANDLE_VALUE) return GF_IO_ERR; | |
| #if defined (_WIN32_WCE) | |
| _path[strlen(_path) ? strlen(_path)-1:0] = 0; | |
| #else | |
| size_t path_len = wcslen(path); | |
| path[ path_len ? path_len-1 : 0] = 0; | |
| #endif | |
| while (SearchH != INVALID_HANDLE_VALUE) { | |
| #else | |
| the_dir = opendir(path); | |
| if (the_dir == NULL) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot open directory \"%s\" for enumeration: %d\n", path, errno)); | |
| return GF_IO_ERR; | |
| } | |
| the_file = readdir(the_dir); | |
| while (the_file) { | |
| #endif | |
| memset(&file_info, 0, sizeof(GF_FileEnumInfo) ); | |
| #if defined (_WIN32_WCE) | |
| if (!wcscmp(FindData.cFileName, _T(".") )) goto next; | |
| if (!wcscmp(FindData.cFileName, _T("..") )) goto next; | |
| #elif defined(WIN32) | |
| if (!wcscmp(FindData.cFileName, L".")) goto next; | |
| if (!wcscmp(FindData.cFileName, L"..")) goto next; | |
| #else | |
| if (!strcmp(the_file->d_name, "..")) goto next; | |
| if (the_file->d_name[0] == '.') goto next; | |
| #endif | |
| #ifdef WIN32 | |
| file_info.directory = (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? GF_TRUE : GF_FALSE; | |
| if (!enum_directory && file_info.directory) goto next; | |
| if (enum_directory && !file_info.directory) goto next; | |
| #endif | |
| if (filter) { | |
| #if defined (_WIN32_WCE) | |
| short ext[30]; | |
| short *sep = wcsrchr(FindData.cFileName, (wchar_t) '.'); | |
| short *found_ext; | |
| u32 ext_len; | |
| if (!sep) goto next; | |
| wcscpy(ext, sep+1); | |
| wcslwr(ext); | |
| ext_len = (u32) wcslen(ext); | |
| found_ext = wcsstr(w_filter, ext); | |
| if (!found_ext) goto next; | |
| if (found_ext[ext_len] && (found_ext[ext_len] != (wchar_t) ';')) goto next; | |
| #elif defined(WIN32) | |
| wchar_t ext[30]; | |
| wchar_t *sep = wcsrchr(FindData.cFileName, L'.'); | |
| wchar_t *found_ext; | |
| u32 ext_len; | |
| if (!sep) goto next; | |
| wcscpy(ext, sep+1); | |
| wcslwr(ext); | |
| ext_len = (u32) wcslen(ext); | |
| found_ext = wcsstr(w_filter, ext); | |
| if (!found_ext) goto next; | |
| if (found_ext[ext_len] && (found_ext[ext_len] != L';')) goto next; | |
| #else | |
| char ext[30]; | |
| char *sep = strrchr(the_file->d_name, '.'); | |
| char *found_ext; | |
| u32 ext_len; | |
| if (!sep) goto next; | |
| gf_strcpy(ext, sep+1); | |
| strlwr(ext); | |
| ext_len = (u32) strlen(ext); | |
| found_ext = strstr(filter, ext); | |
| if (!found_ext) goto next; | |
| if (found_ext[ext_len] && (found_ext[ext_len]!=';')) goto next; | |
| #endif | |
| } | |
| #if defined(WIN32) | |
| file_info.hidden = (FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? GF_TRUE : GF_FALSE; | |
| file_info.system = (FindData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) ? GF_TRUE : GF_FALSE; | |
| file_info.size = MAXDWORD; | |
| file_info.size += 1; | |
| file_info.size *= FindData.nFileSizeHigh; | |
| file_info.size += FindData.nFileSizeLow; | |
| file_info.last_modified = (u64) ((*(LONGLONG *) &FindData.ftLastWriteTime - TIMESPEC_TO_FILETIME_OFFSET) / 10); | |
| #endif | |
| #if defined (_WIN32_WCE) | |
| CE_WideToChar(FindData.cFileName, file); | |
| gf_strcpy(item_path, _path); | |
| gf_strcat(item_path, file); | |
| #elif defined(WIN32) | |
| wcscpy(item_path, path); | |
| wcscat(item_path, FindData.cFileName); | |
| file = FindData.cFileName; | |
| #else | |
| gf_strcpy(item_path, path); | |
| gf_strcat(item_path, the_file->d_name); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Checking file \"%s\" for enum\n", item_path)); | |
| if (stat( item_path, &st ) != 0) goto next; | |
| file_info.directory = ((st.st_mode & S_IFMT) == S_IFDIR) ? GF_TRUE : GF_FALSE; | |
| if (enum_directory && !file_info.directory) goto next; | |
| if (!enum_directory && file_info.directory) goto next; | |
| file_info.size = st.st_size; | |
| struct tm _t = * gf_gmtime(& st.st_mtime); | |
| file_info.last_modified = mktime(&_t); | |
| file_info.last_modified *= 1000000; | |
| #if defined(__DARWIN__) || defined(__APPLE__) || defined(GPAC_CONFIG_IOS) | |
| file_info.last_modified += st.st_mtimespec.tv_nsec / 1000; | |
| #elif defined(GPAC_HAS_MTIM_NSEC) | |
| file_info.last_modified += st.st_mtim.tv_nsec / 1000; | |
| #elif defined(GPAC_CONFIG_ANDROID) | |
| file_info.last_modified += st.st_mtime_nsec / 1000; | |
| #endif | |
| file = the_file->d_name; | |
| if (file && file[0]=='.') file_info.hidden = GF_TRUE; | |
| if (file_info.directory) { | |
| char * parent_name = strrchr(item_path, '/'); | |
| if (!parent_name) { | |
| file_info.drive = GF_TRUE; | |
| } else { | |
| struct stat st_parent; | |
| parent_name[0] = 0; | |
| if (stat(item_path, &st_parent) == 0) { | |
| if ((st.st_dev != st_parent.st_dev) || (st.st_ino == st_parent.st_ino) ) { | |
| file_info.drive = GF_TRUE; | |
| } | |
| } | |
| parent_name[0] = '/'; | |
| } | |
| } | |
| #endif | |
| Bool done = GF_FALSE; | |
| #ifdef WIN32 | |
| mbs_file = gf_wcs_to_utf8(file); | |
| mbs_item_path = gf_wcs_to_utf8(item_path); | |
| if (!mbs_file || !mbs_item_path) | |
| { | |
| if (mbs_file) gf_free(mbs_file); | |
| if (mbs_item_path) gf_free(mbs_item_path); | |
| return GF_IO_ERR; | |
| } | |
| if (enum_dir_fct(cbck, mbs_file, mbs_item_path, &file_info)) { | |
| BOOL ret = FindClose(SearchH); | |
| if (!ret) { | |
| DWORD err = GetLastError(); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_enum_directory() returned(1) the following error code: %d\n", err)); | |
| } | |
| #else | |
| if (enum_dir_fct(cbck, file, item_path, &file_info)) { | |
| #endif | |
| done = GF_TRUE; | |
| } | |
| #ifdef WIN32 | |
| gf_free(mbs_file); | |
| gf_free(mbs_item_path); | |
| #endif | |
| if (done) break; | |
| next: | |
| #ifdef WIN32 | |
| if (!FindNextFileW(SearchH, &FindData)) { | |
| BOOL ret = FindClose(SearchH); | |
| if (!ret) { | |
| DWORD err = GetLastError(); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_enum_directory() returned(2) the following error code: %d\n", err)); | |
| } | |
| break; | |
| } | |
| #else | |
| the_file = readdir(the_dir); | |
| #endif | |
| } | |
| #ifndef WIN32 | |
| closedir(the_dir); | |
| #endif | |
| return GF_OK; | |
| } | |
| struct __gf_file_io | |
| { | |
| u32 _reserved_null; | |
| void *__this; | |
| GF_FileIO * (*open)(GF_FileIO *fileio_ref, const char *url, const char *mode, GF_Err *out_error); | |
| GF_Err (*seek)(GF_FileIO *fileio, u64 offset, s32 whence); | |
| u32 (*read)(GF_FileIO *fileio, u8 *buffer, u32 bytes); | |
| u32 (*write)(GF_FileIO *fileio, u8 *buffer, u32 bytes); | |
| s64 (*tell)(GF_FileIO *fileio); | |
| Bool (*eof)(GF_FileIO *fileio); | |
| int (*printf)(GF_FileIO *gfio, const char *format, va_list args); | |
| char *(*gets)(GF_FileIO *gfio, char *ptr, u32 size); | |
| char *url; | |
| char *res_url; | |
| void *udta; | |
| u32 creation_time; | |
| u64 bytes_done, file_size_plus_one; | |
| Bool main_th; | |
| GF_FileIOCacheState cache_state; | |
| u32 bytes_per_sec; | |
| u32 write_state; | |
| u32 printf_alloc; | |
| u8* printf_buf; | |
| }; | |
| GF_EXPORT | |
| Bool gf_fileio_check(FILE *fp) | |
| { | |
| GF_FileIO *fio = (GF_FileIO *)fp; | |
| if ((fp==stdin) || (fp==stderr) || (fp==stdout)) | |
| return GF_FALSE; | |
| if (fio && !fio->_reserved_null && (fio->__this==fio)) | |
| return GF_TRUE; | |
| return GF_FALSE; | |
| } | |
| GF_EXPORT | |
| GF_FileIOWriteState gf_fileio_write_ready(FILE *fp) | |
| { | |
| GF_FileIO *fio = (GF_FileIO *)fp; | |
| if ((fp==stdin) || (fp==stderr) || (fp==stdout)) | |
| return GF_FIO_WRITE_READY; | |
| //GFIO, check state | |
| if (fio && !fio->_reserved_null && (fio->__this==fio)) { | |
| return fio->write_state; | |
| } | |
| //sync stdio | |
| return GF_FIO_WRITE_READY; | |
| } | |
| u32 gf_fileio_get_write_rate(FILE *fp) | |
| { | |
| GF_FileIO *fio = (GF_FileIO *)fp; | |
| if ((fp==stdin) || (fp==stderr) || (fp==stdout)) | |
| return 0; | |
| if (fio && !fio->_reserved_null && (fio->__this==fio)) { | |
| return fio->bytes_per_sec; | |
| } | |
| //sync stdio | |
| return 0; | |
| } | |
| typedef struct | |
| { | |
| u8 *data; | |
| u32 size; | |
| u32 pos; | |
| u32 url_crc; | |
| u32 nb_ref; | |
| char *URL; | |
| Bool sub_open; | |
| } GF_FileIOBlob; | |
| static GF_FileIO *gfio_blob_open(GF_FileIO *fileio_ref, const char *url, const char *mode, GF_Err *out_error) | |
| { | |
| GF_FileIOBlob *blob = gf_fileio_get_udta(fileio_ref); | |
| if (!strcmp(mode, "close") || !strcmp(mode, "unref")) { | |
| if (blob->nb_ref) blob->nb_ref--; | |
| blob->pos = 0; | |
| if (blob->nb_ref) | |
| return NULL; | |
| if (blob->sub_open) | |
| gf_unregister_file_handle((FILE *)fileio_ref); | |
| if (blob->URL) gf_free(blob->URL); | |
| gf_free(blob); | |
| gf_fileio_del(fileio_ref); | |
| return NULL; | |
| } | |
| if (!strcmp(mode, "ref")) { | |
| blob->nb_ref++; | |
| *out_error = GF_OK; | |
| return NULL; | |
| } | |
| if (!strcmp(mode, "url")) { | |
| *out_error = GF_BAD_PARAM; | |
| return NULL; | |
| } | |
| if (!strcmp(mode, "probe")) { | |
| u32 crc = gf_crc_32(url, (u32) strlen(url) ); | |
| *out_error = (crc==blob->url_crc) ? GF_OK : GF_URL_ERROR; | |
| return NULL; | |
| } | |
| if (mode[0]!='r') { | |
| *out_error = GF_BAD_PARAM; | |
| return NULL; | |
| } | |
| //no user, we can use this object directly | |
| if (!blob->nb_ref) { | |
| blob->nb_ref = 1; | |
| *out_error = GF_OK; | |
| return fileio_ref; | |
| } | |
| //if already in use, we must allocate a new gfio otherwise read position would get shared across FILE streams ! | |
| fileio_ref = gf_fileio_from_mem(blob->URL, blob->data, blob->size); | |
| blob = gf_fileio_get_udta(fileio_ref); | |
| //remember we forced a new gfio, it will have to be unregistered upon close | |
| blob->sub_open = GF_TRUE; | |
| *out_error = fileio_ref ? GF_OK : GF_IO_ERR; | |
| return fileio_ref; | |
| } | |
| static GF_Err gfio_blob_seek(GF_FileIO *fileio, u64 offset, s32 whence) | |
| { | |
| GF_FileIOBlob *blob = gf_fileio_get_udta(fileio); | |
| if (whence==SEEK_END) | |
| blob->pos = blob->size; | |
| else if (whence==SEEK_SET) { | |
| if ((s64)offset<0) | |
| return GF_BAD_PARAM; | |
| if ((u32)offset >= blob->size) | |
| return GF_BAD_PARAM; | |
| blob->pos = (u32) offset; | |
| } else { | |
| if (!offset) | |
| return GF_OK; | |
| if ((s64)offset<0) | |
| return GF_BAD_PARAM; | |
| if (blob->pos + offset > blob->size) | |
| return GF_BAD_PARAM; | |
| blob->pos += (u32) offset; | |
| } | |
| return GF_OK; | |
| } | |
| static u32 gfio_blob_read(GF_FileIO *fileio, u8 *buffer, u32 bytes) | |
| { | |
| GF_FileIOBlob *blob = gf_fileio_get_udta(fileio); | |
| if (bytes + blob->pos > blob->size) | |
| bytes = blob->size - blob->pos; | |
| if (bytes) { | |
| memcpy(buffer, blob->data+blob->pos, bytes); | |
| blob->pos += bytes; | |
| assert(blob->pos<=blob->size); | |
| } | |
| return bytes; | |
| } | |
| static s64 gfio_blob_tell(GF_FileIO *fileio) | |
| { | |
| GF_FileIOBlob *blob = gf_fileio_get_udta(fileio); | |
| return (s64) blob->pos; | |
| } | |
| static Bool gfio_blob_eof(GF_FileIO *fileio) | |
| { | |
| GF_FileIOBlob *blob = gf_fileio_get_udta(fileio); | |
| if (blob->pos==blob->size) return GF_TRUE; | |
| return GF_FALSE; | |
| } | |
| static char *gfio_blob_gets(GF_FileIO *fileio, char *ptr, u32 size) | |
| { | |
| GF_FileIOBlob *blob = gf_fileio_get_udta(fileio); | |
| char *buf = blob->data + blob->pos; | |
| u32 len = blob->size - blob->pos; | |
| if (!len) return NULL; | |
| char *next = memchr(buf, '\n', len); | |
| if (next) { | |
| len = (u32) (next - buf); | |
| if (len + blob->pos<blob->size) len++; | |
| } | |
| if (len + 1 > size) len = size - 1; | |
| memcpy(ptr, blob->data+blob->pos, len); | |
| blob->pos += len; | |
| ptr[len] = 0; | |
| return ptr; | |
| } | |
| GF_List *allocated_gfios = NULL; | |
| GF_EXPORT | |
| GF_FileIO *gf_fileio_new(char *url, void *udta, | |
| gfio_open_proc open, | |
| gfio_seek_proc seek, | |
| gfio_read_proc read, | |
| gfio_write_proc write, | |
| gfio_tell_proc tell, | |
| gfio_eof_proc eof, | |
| gfio_printf_proc printf) | |
| { | |
| char szURL[100]; | |
| GF_FileIO *tmp; | |
| if (!open || !seek || !tell || !eof) return NULL; | |
| if (!write && !read) return NULL; | |
| GF_SAFEALLOC(tmp, GF_FileIO); | |
| if (!tmp) return NULL; | |
| tmp->open = open; | |
| tmp->seek = seek; | |
| tmp->write = write; | |
| tmp->read = read; | |
| tmp->tell = tell; | |
| tmp->eof = eof; | |
| tmp->printf = printf; | |
| tmp->udta = udta; | |
| if (url) | |
| tmp->res_url = gf_strdup(url); | |
| //add clock as cookie to avoid creating twice th same gfio url in case the above malloc returns | |
| //the same adress as a previously allocated gfio | |
| tmp->creation_time = gf_sys_clock(); | |
| sprintf(szURL, "gfio://%p&%u", tmp, tmp->creation_time); | |
| tmp->url = gf_strdup(szURL); | |
| tmp->__this = tmp; | |
| //track all allocated gfios so that we don't attempt accessing a freed one ! | |
| gf_mx_p(logs_mx); | |
| if (!allocated_gfios) allocated_gfios = gf_list_new(); | |
| gf_list_add(allocated_gfios, tmp); | |
| gf_mx_v(logs_mx); | |
| return tmp; | |
| } | |
| GF_EXPORT | |
| const char * gf_fileio_url(GF_FileIO *gfio) | |
| { | |
| return gfio ? gfio->url : NULL; | |
| } | |
| GF_EXPORT | |
| const char * gf_fileio_resource_url(GF_FileIO *gfio) | |
| { | |
| return gfio ? gfio->res_url : NULL; | |
| } | |
| GF_EXPORT | |
| void gf_fileio_del(GF_FileIO *gfio) | |
| { | |
| if (!gfio) return; | |
| gf_mx_p(logs_mx); | |
| if (gf_list_del_item(allocated_gfios, gfio)<0) { | |
| gf_mx_v(logs_mx); | |
| return; | |
| } | |
| if (!gf_list_count(allocated_gfios)) { | |
| gf_list_del(allocated_gfios); | |
| allocated_gfios = NULL; | |
| } | |
| gf_mx_v(logs_mx); | |
| if (gfio->url) gf_free(gfio->url); | |
| if (gfio->res_url) gf_free(gfio->res_url); | |
| if (gfio->printf_buf) gf_free(gfio->printf_buf); | |
| gf_free(gfio); | |
| } | |
| GF_EXPORT | |
| void *gf_fileio_get_udta(GF_FileIO *gfio) | |
| { | |
| return gfio ? gfio->udta : NULL; | |
| } | |
| GF_EXPORT | |
| GF_FileIO *gf_fileio_open_url(GF_FileIO *gfio_ref, const char *url, const char *mode, GF_Err *out_err) | |
| { | |
| if (!gfio_ref) { | |
| *out_err = GF_BAD_PARAM; | |
| return NULL; | |
| } | |
| if (!gfio_ref->open) { | |
| *out_err = url ? GF_NOT_SUPPORTED : GF_OK; | |
| return NULL; | |
| } | |
| return gfio_ref->open(gfio_ref, url, mode, out_err); | |
| } | |
| static u32 gf_fileio_read(GF_FileIO *gfio, u8 *buffer, u32 nb_bytes) | |
| { | |
| if (!gfio) return GF_BAD_PARAM; | |
| if (gfio->read) | |
| return gfio->read(gfio, buffer, nb_bytes); | |
| return 0; | |
| } | |
| static u32 gf_fileio_write(GF_FileIO *gfio, u8 *buffer, u32 nb_bytes) | |
| { | |
| if (!gfio) return GF_BAD_PARAM; | |
| if (!gfio->write) return 0; | |
| return gfio->write(gfio, buffer, (u32) nb_bytes); | |
| } | |
| int gf_fileio_printf(GF_FileIO *gfio, const char *format, va_list args) | |
| { | |
| va_list args_copy; | |
| if (!gfio) return -1; | |
| if (gfio->printf) return gfio->printf(gfio, format, args); | |
| if (!gfio->write) return -1; | |
| va_copy(args_copy, args); | |
| u32 len=vsnprintf(NULL, 0, format, args_copy); | |
| va_end(args_copy); | |
| if (len>=gfio->printf_alloc) { | |
| gfio->printf_alloc = len+1; | |
| gfio->printf_buf = gf_realloc(gfio->printf_buf, gfio->printf_alloc); | |
| } | |
| vsnprintf(gfio->printf_buf, len, format, args); | |
| gfio->printf_buf[len] = 0; | |
| return gfio->write(gfio, gfio->printf_buf, len+1); | |
| } | |
| GF_EXPORT | |
| Bool gf_fileio_write_mode(GF_FileIO *gfio) | |
| { | |
| return (gfio && gfio->write) ? GF_TRUE : GF_FALSE; | |
| } | |
| GF_EXPORT | |
| Bool gf_fileio_read_mode(GF_FileIO *gfio) | |
| { | |
| return (gfio && gfio->read) ? GF_TRUE : GF_FALSE; | |
| } | |
| GF_EXPORT | |
| GF_FileIO *gf_fileio_from_url(const char *url) | |
| { | |
| char szURL[100]; | |
| u32 cookie=0; | |
| GF_FileIO *ptr=NULL; | |
| if (!url) return NULL; | |
| if (strncmp(url, "gfio://", 7)) return NULL; | |
| sscanf(url, "gfio://%p&%u", &ptr, &cookie); | |
| sprintf(szURL, "gfio://%p&%u", ptr, cookie); | |
| if (strcmp(url, szURL)) | |
| return NULL; | |
| gf_mx_p(logs_mx); | |
| if (gf_list_find(allocated_gfios, ptr)<0) { | |
| gf_mx_v(logs_mx); | |
| return NULL; | |
| } | |
| gf_mx_v(logs_mx); | |
| if (ptr && ptr->url && !strcmp(ptr->url, url) && (ptr->creation_time==cookie) ) { | |
| return ptr; | |
| } | |
| return NULL; | |
| } | |
| GF_EXPORT | |
| const char * gf_fileio_translate_url(const char *url) | |
| { | |
| GF_FileIO *gfio = gf_fileio_from_url(url); | |
| return gfio ? gfio->res_url : NULL; | |
| } | |
| Bool gf_fileio_eof(GF_FileIO *fileio) | |
| { | |
| if (!fileio || !fileio->eof) return GF_TRUE; | |
| return fileio->eof(fileio); | |
| } | |
| int gf_fileio_flush(GF_FileIO *fileio) | |
| { | |
| if (!fileio || !fileio->write) return 0; | |
| fileio->write(fileio, NULL, 0); | |
| return 0; | |
| } | |
| GF_EXPORT | |
| const char *gf_fileio_factory(GF_FileIO *gfio, const char *new_res_url) | |
| { | |
| GF_Err e; | |
| if (!gfio || !gfio->open) return NULL; | |
| GF_FileIO *new_res = gfio->open(gfio, new_res_url, "url", &e); | |
| if (e) return NULL; | |
| return gf_fileio_url(new_res); | |
| } | |
| GF_EXPORT | |
| void gf_fileio_set_stats(GF_FileIO *gfio, u64 bytes_done, u64 file_size, GF_FileIOCacheState cache_state, u32 bytes_per_sec) | |
| { | |
| if (!gfio) return; | |
| gfio->bytes_done = bytes_done; | |
| gfio->file_size_plus_one = file_size ? (1 + file_size) : 0; | |
| gfio->cache_state = cache_state; | |
| gfio->bytes_per_sec = bytes_per_sec; | |
| } | |
| GF_EXPORT | |
| void gf_fileio_set_write_state(GF_FileIO *gfio, GF_FileIOWriteState write_state) | |
| { | |
| if (!gfio) return; | |
| gfio->write_state = write_state; | |
| } | |
| GF_EXPORT | |
| Bool gf_fileio_get_stats(GF_FileIO *gfio, u64 *bytes_done, u64 *file_size, GF_FileIOCacheState *cache_state, u32 *bytes_per_sec) | |
| { | |
| if (!gf_fileio_check((FILE *)gfio)) | |
| return GF_FALSE; | |
| if (bytes_done) *bytes_done = gfio->bytes_done; | |
| if (file_size) *file_size = gfio->file_size_plus_one ? gfio->file_size_plus_one-1 : 0; | |
| if (cache_state) *cache_state = gfio->cache_state; | |
| if (bytes_per_sec) *bytes_per_sec = gfio->bytes_per_sec; | |
| return GF_TRUE; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_fileio_tag_main_thread(GF_FileIO *fileio) | |
| { | |
| if (!fileio) return GF_BAD_PARAM; | |
| fileio->main_th = GF_TRUE; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| Bool gf_fileio_is_main_thread(const char *url) | |
| { | |
| GF_FileIO *gfio = gf_fileio_from_url(url); | |
| if (!gfio) return GF_FALSE; | |
| return gfio->main_th; | |
| } | |
| GF_EXPORT | |
| u64 gf_ftell(FILE *fp) | |
| { | |
| if (!fp) return -1; | |
| if (gf_fileio_check(fp)) { | |
| GF_FileIO *gfio = (GF_FileIO *)fp; | |
| if (!gfio->tell) return -1; | |
| return gfio->tell(gfio); | |
| } | |
| #if defined(_WIN32_WCE) | |
| return (u64) ftell(fp); | |
| #elif defined(GPAC_CONFIG_WIN32) && (defined(__CYGWIN__) || defined(__MINGW32__)) | |
| #if (_FILE_OFFSET_BITS >= 64) | |
| return (u64) ftello64(fp); | |
| #else | |
| return (u64) ftell(fp); | |
| #endif | |
| #elif defined(WIN32) | |
| return (u64) _ftelli64(fp); | |
| #elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID) | |
| return (u64) ftello64(fp); | |
| #elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN)) | |
| return (u64) ftello(fp); | |
| #else | |
| return (u64) ftell(fp); | |
| #endif | |
| } | |
| GF_EXPORT | |
| s32 gf_fseek(FILE *fp, s64 offset, s32 whence) | |
| { | |
| if (!fp) return -1; | |
| if (gf_fileio_check(fp)) { | |
| GF_FileIO *gfio = (GF_FileIO *)fp; | |
| if (gfio->seek) { | |
| GF_Err e = gfio->seek(gfio, offset, whence); | |
| if (e) return -1; | |
| return 0; | |
| } | |
| return -1; | |
| } | |
| #if defined(_WIN32_WCE) | |
| return (u64) fseek(fp, (s32) offset, whence); | |
| #elif defined(GPAC_CONFIG_WIN32) && defined(__CYGWIN__) /* mingw or cygwin */ | |
| #if (_FILE_OFFSET_BITS >= 64) | |
| return (u64) fseeko64(fp, offset, whence); | |
| #else | |
| return (u64) fseek(fp, (s32) offset, whence); | |
| #endif | |
| #elif defined(WIN32) | |
| return (u64) _fseeki64(fp, offset, whence); | |
| #elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID) | |
| return fseeko64(fp, (off64_t) offset, whence); | |
| #elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN)) | |
| return fseeko(fp, (off_t) offset, whence); | |
| #else | |
| return fseek(fp, (s32) offset, whence); | |
| #endif | |
| } | |
| static GF_FileIO *gf_fileio_from_blob(const char *file_name) | |
| { | |
| u8 *blob_data; | |
| u32 blob_size, flags; | |
| GF_FileIOBlob *gfio_blob; | |
| GF_Err e = gf_blob_get(file_name, &blob_data, &blob_size, &flags); | |
| if (e || !blob_data) return NULL; | |
| gf_blob_release(file_name); | |
| if (flags) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Attempt at creating a GFIO object on blob corrupted or in transfer, not supported !")); | |
| return NULL; | |
| } | |
| GF_SAFEALLOC(gfio_blob, GF_FileIOBlob); | |
| if (!gfio_blob) return NULL; | |
| gfio_blob->data = blob_data; | |
| gfio_blob->size = blob_size; | |
| GF_FileIO *res = gf_fileio_new((char *) file_name, gfio_blob, gfio_blob_open, gfio_blob_seek, gfio_blob_read, NULL, gfio_blob_tell, gfio_blob_eof, NULL); | |
| if (!res) { | |
| gf_free(gfio_blob); | |
| return NULL; | |
| } | |
| res->gets = gfio_blob_gets; | |
| if (file_name) | |
| gfio_blob->url_crc = gf_crc_32(file_name, (u32) strlen(file_name) ); | |
| return res; | |
| } | |
| GF_EXPORT | |
| GF_FileIO *gf_fileio_from_mem(const char *URL, const u8 *data, u32 size) | |
| { | |
| GF_FileIOBlob *gfio_blob; | |
| GF_SAFEALLOC(gfio_blob, GF_FileIOBlob); | |
| if (!gfio_blob) return NULL; | |
| gfio_blob->data = (u8 *) data; | |
| gfio_blob->size = size; | |
| gfio_blob->URL = gf_strdup(URL); | |
| GF_FileIO *res = gf_fileio_new((char *) URL, gfio_blob, gfio_blob_open, gfio_blob_seek, gfio_blob_read, NULL, gfio_blob_tell, gfio_blob_eof, NULL); | |
| if (!res) { | |
| gf_free(gfio_blob); | |
| return NULL; | |
| } | |
| res->gets = gfio_blob_gets; | |
| if (URL) | |
| gfio_blob->url_crc = gf_crc_32(URL, (u32) strlen(URL) ); | |
| gf_fopen(gf_fileio_url(res), "r"); | |
| return res; | |
| } | |
| #ifdef GPAC_CONFIG_EMSCRIPTEN | |
| static u32 mainloop_th_id = 0; | |
| void gf_set_mainloop_thread(u32 thread_id) | |
| { | |
| mainloop_th_id = thread_id; | |
| } | |
| #endif | |
| GF_EXPORT | |
| FILE *gf_fopen_ex(const char *file_name, const char *parent_name, const char *mode, Bool no_warn) | |
| { | |
| FILE *res = NULL; | |
| u32 gfio_type = 0; | |
| Bool is_mkdir = GF_FALSE; | |
| if (!file_name || !mode) return NULL; | |
| if (!strcmp(mode, "mkdir")) { | |
| is_mkdir = GF_TRUE; | |
| mode = "w"; | |
| } | |
| if (!strncmp(file_name, "gmem://", 7)) { | |
| GF_FileIO *new_gfio; | |
| if (strstr(mode, "w")) | |
| return NULL; | |
| new_gfio = gf_fileio_from_blob(file_name); | |
| if (new_gfio) | |
| gf_register_file_handle((char*)file_name, (FILE *) new_gfio, GF_FALSE); | |
| return (FILE *) new_gfio; | |
| } | |
| if (!strncmp(file_name, "gfio://", 7)) | |
| gfio_type = 1; | |
| else if (parent_name && !strncmp(parent_name, "gfio://", 7)) | |
| gfio_type = 2; | |
| if (gfio_type) { | |
| GF_FileIO *gfio_ref; | |
| GF_FileIO *new_gfio; | |
| GF_Err e; | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Open GFIO %s in mode %s\n", file_name, mode)); | |
| if (gfio_type==1) | |
| gfio_ref = gf_fileio_from_url(file_name); | |
| else | |
| gfio_ref = gf_fileio_from_url(parent_name); | |
| if (!gfio_ref) return NULL; | |
| if (strchr(mode, 'r') && !gf_fileio_read_mode(gfio_ref)) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("FileIO %s is not read-enabled and open mode %s was requested\n", file_name, mode)); | |
| return NULL; | |
| } | |
| if (strpbrk(mode, "wa") && !gf_fileio_write_mode(gfio_ref)) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("FileIO %s is not write-enabled and open mode %s was requested\n", file_name, mode)); | |
| return NULL; | |
| } | |
| new_gfio = gf_fileio_open_url(gfio_ref, file_name, mode, &e); | |
| if (e) { | |
| if (!no_warn) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("FileIO %s open in mode %s failed: %s\n", file_name, mode, gf_error_to_string(e))); | |
| } | |
| return NULL; | |
| } | |
| if (new_gfio) | |
| gf_register_file_handle((char*)file_name, (FILE *) new_gfio, GF_FALSE); | |
| return (FILE *) new_gfio; | |
| } | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Open file %s in mode %s\n", file_name, mode)); | |
| if (strchr(mode, 'w')) { | |
| char *fname = gf_strdup(file_name); | |
| char *sep = strchr(fname, '/'); | |
| if (!sep) sep = strchr(fname, '\\'); | |
| if (file_name[0] == '/') sep = strchr(fname+1, '/'); | |
| else if (file_name[2] == '\\') sep = strchr(fname+3, '\\'); | |
| while (sep) { | |
| char *n_sep; | |
| char c = sep[0]; | |
| sep[0] = 0; | |
| if (!gf_dir_exists(fname)) { | |
| GF_Err e = gf_mkdir(fname); | |
| if (e != GF_OK) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Failed to create directory \"%s\": %s\n", file_name, gf_error_to_string(e) )); | |
| sep[0] = c; | |
| gf_free(fname); | |
| return NULL; | |
| } | |
| } | |
| sep[0] = c; | |
| n_sep = strchr(sep+1, '/'); | |
| if (!n_sep) n_sep = strchr(sep+1, '\\'); | |
| sep = n_sep; | |
| } | |
| gf_free(fname); | |
| if (is_mkdir) return NULL; | |
| } | |
| #ifdef GPAC_CONFIG_EMSCRIPTEN | |
| if (mainloop_th_id && strchr(mode, 'r') && (gf_th_id() != mainloop_th_id)) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Opening file in read mode outside of main loop will likely deadlock - please report to GPAC devs\n")); | |
| } | |
| #endif | |
| #if defined(WIN32) | |
| wchar_t *wname; | |
| wchar_t *wmode; | |
| wname = gf_utf8_to_wcs(file_name); | |
| wmode = gf_utf8_to_wcs(mode); | |
| if (!wname || !wmode) | |
| { | |
| if (wname) gf_free(wname); | |
| if (wmode) gf_free(wmode); | |
| return NULL; | |
| } | |
| res = _wfsopen(wname, wmode, _SH_DENYNO); | |
| gf_free(wname); | |
| gf_free(wmode); | |
| #elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID) | |
| res = fopen64(file_name, mode); | |
| #elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN)) | |
| res = fopen(file_name, mode); | |
| #else | |
| res = fopen(file_name, mode); | |
| #endif | |
| if (res) { | |
| if (!parent_name || strcmp(parent_name, "__temp_file")) | |
| gf_register_file_handle((char*)file_name, res, GF_FALSE); | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] file \"%s\" opened in mode \"%s\" - %d file handles\n", file_name, mode, gpac_file_handles)); | |
| } else if (!no_warn) { | |
| if (strpbrk(mode, "wa")) { | |
| #if defined(WIN32) | |
| u32 err = GetLastError(); | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] system failure for file opening of \"%s\" in mode \"%s\": 0x%08x (%u opened)\n", file_name, mode, err, gpac_file_handles)); | |
| #else | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] system failure for file opening of \"%s\" in mode \"%s\": %d (%u opened)\n", file_name, mode, errno, gpac_file_handles)); | |
| #endif | |
| } | |
| } | |
| return res; | |
| } | |
| GF_EXPORT | |
| s32 gf_fd_open(const char *file_name, u32 oflags, u32 uflags) | |
| { | |
| if (!file_name) return -1; | |
| #if defined(WIN32) | |
| wchar_t *wname = gf_utf8_to_wcs(file_name); | |
| if (!wname) return -1; | |
| int res = _wopen(wname, oflags, uflags); | |
| gf_free(wname); | |
| return res; | |
| #elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID) | |
| return open(file_name, oflags, uflags); | |
| #elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN)) | |
| return open(file_name, oflags, uflags); | |
| #else | |
| return open(file_name, oflags, uflags); | |
| #endif | |
| return -1; | |
| } | |
| GF_EXPORT | |
| FILE *gf_fopen(const char *file_name, const char *mode) | |
| { | |
| return gf_fopen_ex(file_name, NULL, mode, GF_FALSE); | |
| } | |
| GF_EXPORT | |
| s32 gf_fclose(FILE *file) | |
| { | |
| if (!file) | |
| return 0; | |
| if (gf_unregister_file_handle(file)) | |
| return 0; | |
| if (gf_fileio_check(file)) { | |
| GF_Err e; | |
| gf_fileio_open_url((GF_FileIO *) file, NULL, "close", &e); | |
| if (e) return -1; | |
| return 0; | |
| } | |
| return fclose(file); | |
| } | |
| #if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! defined(_GNU_SOURCE) && !defined(WIN32) | |
| #define HAVE_STRERROR_R 1 | |
| #endif | |
| GF_EXPORT | |
| size_t gf_fwrite(const void *ptr, size_t nb_bytes, FILE *stream) | |
| { | |
| size_t result=0; | |
| if (gf_fileio_check(stream)) { | |
| return(size_t) gf_fileio_write((GF_FileIO *)stream, (u8 *) ptr, (u32) nb_bytes); | |
| } | |
| if (ptr) | |
| result = fwrite(ptr, 1, nb_bytes, stream); | |
| if (result != nb_bytes) { | |
| #ifdef _WIN32_WCE | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Error writing data: %d blocks to write but %d blocks written\n", nb_bytes, result)); | |
| #else | |
| #if defined WIN32 && !defined(GPAC_CONFIG_WIN32) | |
| errno_t errno_save; | |
| _get_errno(&errno_save); | |
| #else | |
| int errno_save = errno; | |
| #endif | |
| #ifdef HAVE_STRERROR_R | |
| #define ERRSTR_BUF_SIZE 256 | |
| char errstr[ERRSTR_BUF_SIZE]; | |
| if(strerror_r(errno_save, errstr, ERRSTR_BUF_SIZE) != 0) | |
| { | |
| strerror_r(0, errstr, ERRSTR_BUF_SIZE); | |
| } | |
| #else | |
| char *errstr = (char*)strerror(errno_save); | |
| #endif | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Error writing data (%s): %d blocks to write but %d blocks written\n", errstr, nb_bytes, result)); | |
| #endif | |
| } | |
| return result; | |
| } | |
| GF_EXPORT | |
| size_t gf_fread(void *ptr, size_t nbytes, FILE *stream) | |
| { | |
| size_t result; | |
| if (gf_fileio_check(stream)) { | |
| return (size_t) gf_fileio_read((GF_FileIO *)stream, ptr, (u32) nbytes); | |
| } | |
| if (!stream) return 0; | |
| result = fread(ptr, 1, nbytes, stream); | |
| return result; | |
| } | |
| GF_EXPORT | |
| char *gf_fgets(char *ptr, size_t size, FILE *stream) | |
| { | |
| if (gf_fileio_check(stream)) { | |
| GF_FileIO *fio = (GF_FileIO *)stream; | |
| if (fio->gets) | |
| return fio->gets(fio, ptr, (u32) size); | |
| u32 i, read, nb_read=0; | |
| for (i=0; i<size-1; i++) { | |
| u8 buf[1]; | |
| read = (u32) gf_fileio_read(fio, buf, 1); | |
| if (!read) break; | |
| ptr[nb_read] = buf[0]; | |
| nb_read++; | |
| if (buf[0]=='\n') break; | |
| } | |
| ptr[nb_read]=0; | |
| if (!nb_read) return NULL; | |
| return ptr; | |
| } | |
| return fgets(ptr, (u32) size, stream); | |
| } | |
| GF_EXPORT | |
| int gf_fgetc(FILE *stream) | |
| { | |
| if (gf_fileio_check(stream)) { | |
| GF_FileIO *fio = (GF_FileIO *)stream; | |
| u8 buf[1]; | |
| u32 read = gf_fileio_read(fio, buf, 1); | |
| if (!read) return -1; | |
| return buf[0]; | |
| } | |
| return fgetc(stream); | |
| } | |
| GF_EXPORT | |
| int gf_fputc(int val, FILE *stream) | |
| { | |
| if (gf_fileio_check(stream)) { | |
| GF_FileIO *fio = (GF_FileIO *)stream; | |
| u32 write; | |
| u8 buf[1]; | |
| buf[0] = val; | |
| write = gf_fileio_write(fio, buf, 1); | |
| if (!write) return -1; | |
| return buf[0]; | |
| } | |
| return fputc(val, stream); | |
| } | |
| GF_EXPORT | |
| int gf_fputs(const char *buf, FILE *stream) | |
| { | |
| if (gf_fileio_check(stream)) { | |
| GF_FileIO *fio = (GF_FileIO *)stream; | |
| u32 write, len = (u32) strlen(buf); | |
| write = gf_fileio_write(fio, (u8 *) buf, len); | |
| if (write != len) return -1; | |
| return write; | |
| } | |
| return fputs(buf, stream); | |
| } | |
| GF_EXPORT | |
| int gf_fprintf(FILE *stream, const char *format, ...) | |
| { | |
| int res; | |
| va_list args; | |
| va_start(args, format); | |
| if (gf_fileio_check(stream)) { | |
| res = gf_fileio_printf((GF_FileIO *)stream, format, args); | |
| } else { | |
| res = vfprintf(stream, format, args); | |
| } | |
| va_end(args); | |
| return res; | |
| } | |
| GF_EXPORT | |
| int gf_vfprintf(FILE *stream, const char *format, va_list args) | |
| { | |
| int res; | |
| if (gf_fileio_check(stream)) { | |
| res = gf_fileio_printf((GF_FileIO *)stream, format, args); | |
| } else { | |
| res = vfprintf(stream, format, args); | |
| } | |
| return res; | |
| } | |
| GF_EXPORT | |
| int gf_fflush(FILE *stream) | |
| { | |
| if (gf_fileio_check(stream)) { | |
| return gf_fileio_flush((GF_FileIO *)stream); | |
| } | |
| return fflush(stream); | |
| } | |
| GF_EXPORT | |
| int gf_feof(FILE *stream) | |
| { | |
| if (!stream) return 1; | |
| if (gf_fileio_check(stream)) { | |
| return gf_fileio_eof((GF_FileIO *)stream) ? 1 : 0; | |
| } | |
| return feof(stream); | |
| } | |
| GF_EXPORT | |
| int gf_ferror(FILE *stream) | |
| { | |
| if (gf_fileio_check(stream)) { | |
| return 0; | |
| } | |
| return ferror(stream); | |
| } | |
| GF_EXPORT | |
| u64 gf_fsize(FILE *fp) | |
| { | |
| u64 size; | |
| if (gf_fileio_check(fp)) { | |
| GF_FileIO *gfio = (GF_FileIO *)fp; | |
| if (gfio->file_size_plus_one) { | |
| gf_fseek(fp, 0, SEEK_SET); | |
| return gfio->file_size_plus_one - 1; | |
| } | |
| //fall through | |
| } | |
| gf_fseek(fp, 0, SEEK_END); | |
| size = gf_ftell(fp); | |
| gf_fseek(fp, 0, SEEK_SET); | |
| return size; | |
| } | |
| GF_EXPORT | |
| u64 gf_fd_fsize(int fd) | |
| { | |
| u64 size=0; | |
| #ifdef GPAC_HAS_FD | |
| if (fd >= 0) { | |
| #if defined(WIN32) | |
| struct _stat64 sb; | |
| _fstat64(fd, &sb); | |
| #else | |
| struct stat sb; | |
| fstat(fd, &sb); | |
| #endif | |
| size = (u64) sb.st_size; | |
| } | |
| #endif | |
| return size; | |
| } | |
| /** | |
| * Returns a pointer to the start of a filepath basename | |
| **/ | |
| GF_EXPORT | |
| char* gf_file_basename(const char* filename) | |
| { | |
| char* lastPathPart = NULL; | |
| if (filename) { | |
| lastPathPart = strrchr(filename , GF_PATH_SEPARATOR); | |
| #if GF_PATH_SEPARATOR != '/' | |
| // windows paths can mix slashes and backslashes | |
| // so we search for the last slash that occurs after the last backslash | |
| // if it occurs before it's not relevant | |
| // if there's no backslashes we search in the whole file path | |
| char* trailingSlash = strrchr(lastPathPart?lastPathPart:filename, '/'); | |
| if (trailingSlash) | |
| lastPathPart = trailingSlash; | |
| #endif | |
| if (!lastPathPart) | |
| lastPathPart = (char *)filename; | |
| else | |
| lastPathPart++; | |
| } | |
| return lastPathPart; | |
| } | |
| /** | |
| * Returns a pointer to the start of a filepath extension or null | |
| **/ | |
| GF_EXPORT | |
| char* gf_file_ext_start(const char* filename) | |
| { | |
| char* basename; | |
| if (filename && !strncmp(filename, "gfio://", 7)) { | |
| GF_FileIO *gfio = gf_fileio_from_url(filename); | |
| filename = gf_fileio_resource_url(gfio); | |
| } | |
| basename = gf_file_basename(filename); | |
| if (basename) { | |
| char *ext = strrchr(basename, '.'); | |
| if (!ext) return NULL; | |
| if (!strcmp(ext, ".gz")) { | |
| ext[0] = 0; | |
| char *ext2 = strrchr(basename, '.'); | |
| ext[0] = '.'; | |
| if (ext2) return ext2; | |
| } | |
| //consider that if we have a space after a dot and before any common separator, we have no file extension | |
| u32 i; | |
| for (i=1; ext[i] ; i++) { | |
| if ((ext[i]==':') || (ext[i]=='@') || (ext[i]=='#') || (ext[i]=='?')) break; | |
| if (ext[i]==' ') return NULL; | |
| } | |
| return ext; | |
| } | |
| return NULL; | |
| } | |
| GF_EXPORT | |
| char* gf_url_colon_suffix(const char *path, char assign_sep) | |
| { | |
| char *sep = strchr(path, ':'); | |
| if (!sep) return NULL; | |
| //handle Z:\ and Z:/ | |
| if ((path[1]==':') && ( (path[2]=='/') || (path[2]=='\\') ) ) | |
| return gf_url_colon_suffix(path+2, assign_sep); | |
| if (!strncmp(path, "gfio://", 7) || !strncmp(path, "gmem://", 7)) { | |
| return strchr(path+7, ':'); | |
| } | |
| //handle "\\foo\Z:\bar" | |
| if ((path[0] == '\\') && (path[1] == '\\')) { | |
| char *next = strchr(path+2, '\\'); | |
| if (next) next = strchr(next + 1, '\\'); | |
| if (next) | |
| return gf_url_colon_suffix(next + 1, assign_sep); | |
| } | |
| //handle PROTO://ADD:PORT/ | |
| if ((sep[1]=='/') && (sep[2]=='/')) { | |
| char *next_colon, *next_slash, *userpass; | |
| sep++; | |
| //skip all // (eg PROTO://////////////mytest/) | |
| while (sep[0]=='/') | |
| sep++; | |
| if (!sep[0]) return NULL; | |
| /*we may now have C:\ or C:/ (eg file://///C:\crazy\ or file://///C:/crazy/) | |
| if sep[1]==':', then sep[2] is valid (0 or something else), no need to check for len | |
| */ | |
| if ((sep[1]==':') && ( (sep[2]=='/') || (sep[2]=='\\') ) ) { | |
| return gf_url_colon_suffix(sep+2, assign_sep); | |
| } | |
| //find closest : or /, if : is before / consider this is a port or an IPv6 address and check next : after / | |
| next_colon = strchr(sep, ':'); | |
| next_slash = strchr(sep, '/'); | |
| userpass = strchr(sep, '@'); | |
| //if ':' is before '@' with '@' before next '/', consider this is `user:pass@SERVER` | |
| if (userpass && next_colon && next_slash && (userpass<next_slash) && (userpass>next_colon)) | |
| next_colon = strchr(userpass, ':'); | |
| if (next_colon && next_slash && ((next_slash - sep) > (next_colon - sep)) ) { | |
| const char *last_colon; | |
| u32 i, port, nb_colons=0, nb_dots=0, nb_non_alnums=0; | |
| next_slash[0] = 0; | |
| last_colon = strrchr(next_colon, ':'); | |
| port = atoi(last_colon+1); | |
| for (i=0; i<strlen(next_colon+1); i++) { | |
| if (next_colon[i+1] == ':') nb_colons++; | |
| else if (next_colon[i+1] == '.') nb_dots++; | |
| //closing bracket of IPv6 | |
| else if (next_colon[i+1] == ']') {} | |
| else if (!isalnum(next_colon[i+1])) { | |
| nb_non_alnums++; | |
| break; | |
| } | |
| } | |
| next_slash[0] = '/'; | |
| //if no non-alphanum, we must have either a port (ipv4) or extra colons but no dots (ipv6) | |
| if (!nb_non_alnums && (port || (nb_colons && !nb_dots))) | |
| next_colon = strchr(next_slash, ':'); | |
| } | |
| return next_colon; | |
| } | |
| if (sep && assign_sep) { | |
| char *file_ext = strchr(path, '.'); | |
| char *assign = strchr(path, assign_sep); | |
| if (assign && assign>file_ext) assign = NULL; | |
| if (assign) file_ext = NULL; | |
| if (file_ext && (file_ext>sep)) { | |
| sep = strchr(file_ext, ':'); | |
| } | |
| if (assign && (strlen(assign) > 4)) { | |
| if ((assign[2] == ':') && ((assign[3] == '\\') || (assign[3] == '/'))) { | |
| return gf_url_colon_suffix(assign + 1, 0); | |
| } | |
| if ((assign[1] == '\\') && (assign[2] == '\\')) { | |
| char *next = strchr(assign + 3, '\\'); | |
| if (next) next = strchr(next+1, '\\'); | |
| if (next && (next>sep)) | |
| return gf_url_colon_suffix(next, 0); | |
| } | |
| } | |
| } | |
| return sep; | |
| } | |
| GF_EXPORT | |
| char* gf_realpath(const char* path, char* resolved_path) { | |
| #ifdef _WIN32 | |
| return _fullpath(resolved_path, path, GF_MAX_PATH); | |
| #else | |
| return realpath(path, resolved_path); | |
| #endif | |
| } |