1 /*
2  *			GPAC - Multimedia Framework C SDK
3  *
4  *			Authors: Jean Le Feuvre - Copyright (c) Telecom ParisTech 2000-2020
5  *			         Romain Bouqueau - Copyright (c) Romain Bouqueau 2015
6  *					All rights reserved
7  *
8  *  This file is part of GPAC / common tools sub-project
9  *
10  *  GPAC is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU Lesser General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  GPAC is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; see the file COPYING.  If not, write to
22  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  */
25 
26 #include <gpac/tools.h>
27 #include <gpac/utf.h>
28 
29 #if defined(_WIN32_WCE)
30 
31 #include <winbase.h>
32 #include <tlhelp32.h>
33 
34 #elif defined(WIN32)
35 
36 #include <windows.h>
37 #include <process.h>
38 #include <direct.h>
39 #include <sys/stat.h>
40 #include <share.h>
41 
42 #else
43 
44 #include <sys/stat.h>
45 #include <unistd.h>
46 #include <dirent.h>
47 #include <sys/time.h>
48 
49 #ifndef __BEOS__
50 #include <errno.h>
51 #endif
52 
53 #endif
54 
55 
56 GF_EXPORT
gf_rmdir(const char * DirPathName)57 GF_Err gf_rmdir(const char *DirPathName)
58 {
59 #if defined (_WIN32_WCE)
60 	TCHAR swzName[MAX_PATH];
61 	BOOL res;
62 	CE_CharToWide(DirPathName, swzName);
63 	res = RemoveDirectory(swzName);
64 	if (! res) {
65 		int err = GetLastError();
66 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete directory \"%s\": last error %d\n", DirPathName, err ));
67 	}
68 #elif defined (WIN32)
69 	int res;
70 	wchar_t* wcsDirPathName = gf_utf8_to_wcs(DirPathName);
71 	if (!wcsDirPathName)
72 		return GF_IO_ERR;
73 	res = _wrmdir(wcsDirPathName);
74 	gf_free(wcsDirPathName);
75 	if (res == -1) {
76 		int err = GetLastError();
77 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete directory \"%s\": last error %d\n", DirPathName, err ));
78 		return GF_IO_ERR;
79 	}
80 #else
81 	int res = rmdir(DirPathName);
82 	if (res==-1) {
83 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete directory \"%s\": last error %d\n", DirPathName, errno  ));
84 		return GF_IO_ERR;
85 	}
86 #endif
87 	return GF_OK;
88 }
89 
90 GF_EXPORT
gf_mkdir(const char * DirPathName)91 GF_Err gf_mkdir(const char* DirPathName)
92 {
93 #if defined (_WIN32_WCE)
94 	TCHAR swzName[MAX_PATH];
95 	BOOL res;
96 	CE_CharToWide(DirPathName, swzName);
97 	res = CreateDirectory(swzName, NULL);
98 	if (! res) {
99 		int err = GetLastError();
100 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create directory \"%s\": last error %d\n", DirPathName, err ));
101 	}
102 #elif defined (WIN32)
103 	int res;
104 	wchar_t* wcsDirPathName = gf_utf8_to_wcs(DirPathName);
105 	if (!wcsDirPathName)
106 		return GF_IO_ERR;
107 	res = _wmkdir(wcsDirPathName);
108 	gf_free(wcsDirPathName);
109 	if (res==-1) {
110 		int err = GetLastError();
111 		if (err != 183) {
112 			// don't throw error if dir already exists
113 			GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create directory \"%s\": last error %d\n", DirPathName, err));
114 		}
115 	}
116 #else
117 	int res = mkdir(DirPathName, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
118 	if (res==-1) {
119 		if(errno == 17) {
120 			GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("Cannot create directory \"%s\", it already exists: last error %d \n", DirPathName, errno ));
121 			return GF_OK;
122 		} else {
123 			GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create directory \"%s\": last error %d\n", DirPathName, errno ));
124 			return GF_IO_ERR;
125 		}
126 	}
127 #endif
128 	return GF_OK;
129 }
130 
131 
132 GF_EXPORT
gf_dir_exists(const char * DirPathName)133 Bool gf_dir_exists(const char* DirPathName)
134 {
135 #if defined (_WIN32_WCE)
136 	TCHAR swzName[MAX_PATH];
137 	DWORD att;
138 	CE_CharToWide(DirPathName, swzName);
139 	att = GetFileAttributes(swzName);
140 	return (att != INVALID_FILE_ATTRIBUTES && (att & FILE_ATTRIBUTE_DIRECTORY)) ? GF_TRUE : GF_FALSE;
141 #elif defined (WIN32)
142 	DWORD att;
143 	wchar_t* wcsDirPathName = gf_utf8_to_wcs(DirPathName);
144 	if (!wcsDirPathName)
145 		return GF_FALSE;
146 	att = GetFileAttributesW(wcsDirPathName);
147 	gf_free(wcsDirPathName);
148 	return (att != INVALID_FILE_ATTRIBUTES && (att & FILE_ATTRIBUTE_DIRECTORY)) ? GF_TRUE : GF_FALSE;
149 #else
150 	DIR* dir = opendir(DirPathName);
151 	if (!dir) return GF_FALSE;
152 	closedir(dir);
153 	return GF_TRUE;
154 #endif
155 	return GF_FALSE;
156 }
delete_dir(void * cbck,char * item_name,char * item_path,GF_FileEnumInfo * file_info)157 static Bool delete_dir(void *cbck, char *item_name, char *item_path, GF_FileEnumInfo *file_info)
158 {
159 	Bool directory_clean_mode = *(Bool*)cbck;
160 
161 	if(directory_clean_mode) {
162 		gf_cleanup_dir(item_path);
163 		gf_rmdir(item_path);
164 	} else {
165 		gf_file_delete(item_path);
166 	}
167 	return GF_FALSE;
168 }
169 
170 GF_EXPORT
gf_cleanup_dir(const char * DirPathName)171 GF_Err gf_cleanup_dir(const char* DirPathName)
172 {
173 	Bool directory_clean_mode;
174 
175 	directory_clean_mode = GF_TRUE;
176 	gf_enum_directory(DirPathName, GF_TRUE, delete_dir, &directory_clean_mode, NULL);
177 	directory_clean_mode = GF_FALSE;
178 	gf_enum_directory(DirPathName, GF_FALSE, delete_dir, &directory_clean_mode, NULL);
179 
180 	return GF_OK;
181 }
182 
183 GF_EXPORT
gf_file_delete(const char * fileName)184 GF_Err gf_file_delete(const char *fileName)
185 {
186 	if (!fileName || !fileName[0]) {
187 		GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("gf_file_delete with no param - ignoring\n"));
188 		return GF_OK;
189 	}
190 #if defined(_WIN32_WCE)
191 	TCHAR swzName[MAX_PATH];
192 	CE_CharToWide((char*)fileName, swzName);
193 	return (DeleteFile(swzName)==0) ? GF_IO_ERR : GF_OK;
194 #elif defined(WIN32)
195 	/* success if != 0 */
196 	{
197 		BOOL op_result;
198 		wchar_t* wcsFileName = gf_utf8_to_wcs(fileName);
199 		if (!wcsFileName)
200 			return GF_IO_ERR;
201 		op_result = DeleteFileW(wcsFileName);
202 		gf_free(wcsFileName);
203 		return (op_result==0) ? GF_IO_ERR : GF_OK;
204 	}
205 #else
206 	/* success is == 0 */
207 	return ( remove(fileName) == 0) ? GF_OK : GF_IO_ERR;
208 #endif
209 }
210 
211 #ifndef WIN32
212 /**
213  * Remove existing single-quote from a single-quoted string.
214  * The caller is responsible for deallocating the returns string with gf_free()
215  */
gf_sanetize_single_quoted_string(const char * src)216 static char* gf_sanetize_single_quoted_string(const char *src) {
217 	int i, j;
218 	char *out = (char*)gf_malloc(4*strlen(src)+3);
219 	out[0] = '\'';
220 	for (i=0, j=1; (out[j]=src[i]); ++i, ++j) {
221 		if (src[i]=='\'') {
222 			out[++j]='\\';
223 			out[++j]='\'';
224 			out[++j]='\'';
225 		}
226 	}
227 	out[j++] = '\'';
228 	out[j++] = 0;
229 	return out;
230 }
231 #endif
232 
233 #if defined(GPAC_CONFIG_IOS) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 80000)
234 #include <spawn.h>
235 extern char **environ;
236 #endif
237 
238 #if defined(WIN32)
239 #include <io.h>
240 #endif
241 
242 GF_EXPORT
gf_file_exists_ex(const char * fileName,const char * par_name)243 Bool gf_file_exists_ex(const char *fileName, const char *par_name)
244 {
245 	u32 gfio_type = 0;
246 	if (!fileName) return GF_FALSE;
247 
248 	if (!strncmp(fileName, "gfio://", 7))
249 		gfio_type = 1;
250 	else if (par_name && !strncmp(par_name, "gfio://", 7))
251 		gfio_type = 2;
252 
253 	if (gfio_type) {
254 		GF_FileIO *gfio_ref;
255 		GF_FileIO *new_gfio;
256 		GF_Err e;
257 		Bool res = GF_TRUE;
258 		if (gfio_type==1)
259 			gfio_ref = gf_fileio_from_url(fileName);
260 		else
261 			gfio_ref = gf_fileio_from_url(par_name);
262 
263 		if (!gfio_ref) return GF_FALSE;
264 		new_gfio = gf_fileio_open_url(gfio_ref, fileName, "probe", &e);
265 		if (e==GF_URL_ERROR)
266 			res = GF_FALSE;
267 		if (new_gfio)
268 			gf_fileio_open_url(new_gfio, NULL, "r", &e);
269 		return res;
270 	}
271 
272 #if defined(WIN32)
273 	Bool res;
274 	wchar_t* wname = gf_utf8_to_wcs(fileName);
275 	if (!wname) return GF_FALSE;
276  	res = (_waccess(wname, 4) == -1) ? GF_FALSE : GF_TRUE;
277 	gf_free(wname);
278 	return res;
279 #elif defined(GPAC_CONFIG_LINUX)
280  	return (access(fileName, 4) == -1) ? GF_FALSE : GF_TRUE;
281 #elif defined(__DARWIN__) || defined(__APPLE__)
282  	return (access(fileName, 4) == -1) ? GF_FALSE : GF_TRUE;
283 #elif defined(GPAC_CONFIG_IOS) || defined(GPAC_CONFIG_ANDROID)
284  	return (access(fileName, 4) == -1) ? GF_FALSE : GF_TRUE;
285 #else
286 	FILE *f = gf_fopen(fileName, "r");
287 	if (f) {
288 		gf_fclose(f);
289 		return GF_TRUE;
290 	}
291 	return GF_FALSE;
292 #endif
293 }
294 
295 GF_EXPORT
gf_file_exists(const char * fileName)296 Bool gf_file_exists(const char *fileName)
297 {
298 	return gf_file_exists_ex(fileName, NULL);
299 }
300 
301 GF_EXPORT
gf_file_move(const char * fileName,const char * newFileName)302 GF_Err gf_file_move(const char *fileName, const char *newFileName)
303 {
304 #if defined(_WIN32_WCE)
305 	GF_Err e = GF_OK;
306 	TCHAR swzName[MAX_PATH];
307 	TCHAR swzNewName[MAX_PATH];
308 	CE_CharToWide((char*)fileName, swzName);
309 	CE_CharToWide((char*)newFileName, swzNewName);
310 	if (MoveFile(swzName, swzNewName) == 0 )
311 		e = GF_IO_ERR;
312 #elif defined(WIN32)
313 	GF_Err e = GF_OK;
314 	/* success if != 0 */
315 	BOOL op_result;
316 	wchar_t* wcsFileName = gf_utf8_to_wcs(fileName);
317 	wchar_t* wcsNewFileName = gf_utf8_to_wcs(newFileName);
318 	if (!wcsFileName || !wcsNewFileName) {
319 		if (wcsFileName) gf_free(wcsFileName);
320 		if (wcsNewFileName) gf_free(wcsNewFileName);
321 		e = GF_IO_ERR;
322 	} else {
323 		op_result = MoveFileW(wcsFileName, wcsNewFileName);
324 		gf_free(wcsFileName);
325 		gf_free(wcsNewFileName);
326 		if ( op_result == 0 )
327 			e = GF_IO_ERR;
328 	}
329 #else
330 	GF_Err e = GF_IO_ERR;
331 	char cmd[1024], *arg1, *arg2;
332 	if (!fileName || !newFileName) {
333 		e = GF_IO_ERR;
334 	} else {
335 		arg1 = gf_sanetize_single_quoted_string(fileName);
336 		arg2 = gf_sanetize_single_quoted_string(newFileName);
337 		if (snprintf(cmd, sizeof cmd, "mv %s %s", arg1, arg2) >= sizeof cmd) goto error;
338 
339 #if defined(GPAC_CONFIG_IOS) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 80000)
340 		{
341 			pid_t pid;
342 			char *argv[3];
343 			argv[0] = "mv";
344 			argv[1] = cmd;
345 			argv[2] = NULL;
346 			posix_spawn(&pid, argv[0], NULL, NULL, argv, environ);
347 			waitpid(pid, NULL, 0);
348 		}
349 #else
350 		e = (system(cmd) == 0) ? GF_OK : GF_IO_ERR;
351 #endif
352 
353 error:
354 		gf_free(arg1);
355 		gf_free(arg2);
356 	}
357 #endif
358 
359 	if (e) {
360 		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) ));
361 	}
362 	return e;
363 }
364 
365 GF_EXPORT
gf_file_modification_time(const char * filename)366 u64 gf_file_modification_time(const char *filename)
367 {
368 #if defined(_WIN32_WCE)
369 	WCHAR _file[GF_MAX_PATH];
370 	WIN32_FIND_DATA FindData;
371 	HANDLE fh;
372 	ULARGE_INTEGER uli;
373 	ULONGLONG time_ms;
374 	BOOL ret;
375 	CE_CharToWide((char *) filename, _file);
376 	fh = FindFirstFile(_file, &FindData);
377 	if (fh == INVALID_HANDLE_VALUE) return 0;
378 	uli.LowPart = FindData.ftLastWriteTime.dwLowDateTime;
379 	uli.HighPart = FindData.ftLastWriteTime.dwHighDateTime;
380 	ret = FindClose(fh);
381 	if (!ret) {
382 		DWORD err = GetLastError();
383 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_file_modification_time() returned the following error code: %d\n", err));
384 	}
385 	time_ms = uli.QuadPart/10000;
386 	return time_ms;
387 #elif defined(WIN32) && !defined(__GNUC__)
388 	struct _stat64 sb;
389 	int op_result;
390 	wchar_t* wcsFilename = gf_utf8_to_wcs(filename);
391 	if (!wcsFilename)
392 		return 0;
393 	op_result = _wstat64(wcsFilename, &sb);
394 	gf_free(wcsFilename);
395 	if (op_result != 0) return 0;
396 	return sb.st_mtime;
397 #else
398 	struct stat sb;
399 	if (stat(filename, &sb) != 0) return 0;
400 	return sb.st_mtime;
401 #endif
402 	return 0;
403 }
404 
405 #ifdef GPAC_MEMORY_TRACKING
406 #include <gpac/list.h>
407 extern int gf_mem_track_enabled;
408 static  GF_List * gpac_open_files = NULL;
409 typedef struct
410 {
411 	FILE *ptr;
412 	char *url;
413 } GF_FileHandle;
414 #endif
415 static u32 gpac_file_handles = 0;
416 
417 GF_EXPORT
gf_file_handles_count()418 u32 gf_file_handles_count()
419 {
420 #ifdef GPAC_MEMORY_TRACKING
421 	if (gpac_open_files) {
422 		return gf_list_count(gpac_open_files);
423 	}
424 #endif
425 	return gpac_file_handles;
426 }
427 
428 #ifdef GPAC_MEMORY_TRACKING
enum_open_handles(u32 * idx)429 const char *enum_open_handles(u32 *idx)
430 {
431 	GF_FileHandle *h;
432 	u32 count = gf_list_count(gpac_open_files);
433 	if (*idx >= count) return NULL;
434 	h = gf_list_get(gpac_open_files, *idx);
435 	(*idx)++;
436 	return h->url;
437 }
438 #endif
439 
gf_register_file_handle(const char * filename,FILE * ptr)440 static void gf_register_file_handle(const char *filename, FILE *ptr)
441 {
442 #ifdef GPAC_MEMORY_TRACKING
443 	if (gf_mem_track_enabled) {
444 		GF_FileHandle *h;
445 		if (!gpac_open_files) gpac_open_files = gf_list_new();
446 		GF_SAFEALLOC(h, GF_FileHandle);
447 		if (h) {
448 			h->ptr = ptr;
449 			h->url = gf_strdup(filename);
450 			gf_list_add(gpac_open_files, h);
451 		}
452 	}
453 #endif
454 	gpac_file_handles++;
455 }
456 
457 #include <gpac/thread.h>
458 extern GF_Mutex *logs_mx;
459 
gf_unregister_file_handle(FILE * ptr)460 static void gf_unregister_file_handle(FILE *ptr)
461 {
462 	assert(gpac_file_handles);
463 	gpac_file_handles--;
464 #ifdef GPAC_MEMORY_TRACKING
465 	if (gf_mem_track_enabled) {
466 		u32 i, count;
467 		gf_mx_p(logs_mx);
468 		count = gf_list_count(gpac_open_files);
469 		for (i=0; i<count; i++) {
470 			GF_FileHandle *h = gf_list_get(gpac_open_files, i);
471 			if (h->ptr==ptr) {
472 				gf_free(h->url);
473 				gf_free(h);
474 				gf_list_rem(gpac_open_files, i);
475 				if (!gf_list_count(gpac_open_files)) {
476 					gf_list_del(gpac_open_files);
477 					gpac_open_files = NULL;
478 				}
479 				gf_mx_v(logs_mx);
480 				return;
481 			}
482 		}
483 		gf_mx_v(logs_mx);
484 	}
485 #endif
486 }
487 
488 GF_EXPORT
gf_file_temp(char ** const fileName)489 FILE *gf_file_temp(char ** const fileName)
490 {
491 	FILE *res;
492 #if defined(_WIN32_WCE)
493 	TCHAR pPath[MAX_PATH+1];
494 	TCHAR pTemp[MAX_PATH+1];
495 	res = NULL;
496 	if (!GetTempPath(MAX_PATH, pPath)) {
497 		pPath[0] = '.';
498 		pPath[1] = '.';
499 	}
500 	if (GetTempFileName(pPath, TEXT("git"), 0, pTemp))
501 		res = _wfopen(pTemp, TEXT("w+b"));
502 #elif defined(WIN32)
503 	res = tmpfile();
504 
505 	if (!res) {
506 		wchar_t tmp[MAX_PATH];
507 
508 		GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[Win32] system failure for tmpfile(): 0x%08x\n", GetLastError()));
509 
510 		/*tmpfile() may fail under vista ...*/
511 		if (GetEnvironmentVariableW(L"TEMP", tmp, MAX_PATH)) {
512 			wchar_t tmp2[MAX_PATH], *t_file;
513 			char* mbs_t_file;
514 			gf_rand_init(GF_FALSE);
515 			swprintf(tmp2, MAX_PATH, L"gpac_%d_%08x_", _getpid(), gf_rand());
516 			t_file = _wtempnam(tmp, tmp2);
517 			mbs_t_file = gf_wcs_to_utf8(t_file);
518 			if (!mbs_t_file)
519 				return 0;
520 			res = gf_fopen(mbs_t_file, "w+b");
521 			if (res) {
522 				gf_unregister_file_handle(res);
523 				if (fileName) {
524 					*fileName = gf_strdup(mbs_t_file);
525 				} else {
526 					GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[Win32] temporary file \"%s\" won't be deleted - contact the GPAC team\n", mbs_t_file));
527 				}
528 			}
529 			gf_free(mbs_t_file);
530 			free(t_file);
531 		}
532 	}
533 #else
534 	res = tmpfile();
535 #endif
536 
537 	if (res) {
538 		gf_register_file_handle("temp file", res);
539 	}
540 	return res;
541 }
542 
543 /*enumerate directories*/
544 GF_EXPORT
gf_enum_directory(const char * dir,Bool enum_directory,gf_enum_dir_item enum_dir_fct,void * cbck,const char * filter)545 GF_Err gf_enum_directory(const char *dir, Bool enum_directory, gf_enum_dir_item enum_dir_fct, void *cbck, const char *filter)
546 {
547 #ifdef WIN32
548 	wchar_t item_path[GF_MAX_PATH];
549 #else
550 	char item_path[GF_MAX_PATH];
551 #endif
552 	GF_FileEnumInfo file_info;
553 
554 #if defined(_WIN32_WCE)
555 	char _path[GF_MAX_PATH];
556 	unsigned short path[GF_MAX_PATH];
557 	unsigned short w_filter[GF_MAX_PATH];
558 	char file[GF_MAX_PATH];
559 #elif defined(WIN32)
560 	wchar_t path[GF_MAX_PATH], *file;
561 	wchar_t w_filter[GF_MAX_PATH];
562 	wchar_t w_dir[GF_MAX_PATH];
563 	char *mbs_file, *mbs_item_path;
564 #else
565 	char path[GF_MAX_PATH], *file;
566 #endif
567 
568 #ifdef WIN32
569 	WIN32_FIND_DATAW FindData;
570 	HANDLE SearchH;
571 #else
572 	DIR *the_dir;
573 	struct dirent* the_file;
574 	struct stat st;
575 #endif
576 
577 	if (!dir || !enum_dir_fct) return GF_BAD_PARAM;
578 
579 	if (filter && (!strcmp(filter, "*") || !filter[0])) filter=NULL;
580 
581 	memset(&file_info, 0, sizeof(GF_FileEnumInfo) );
582 
583 	if (!strcmp(dir, "/")) {
584 #if defined(WIN32) && !defined(_WIN32_WCE)
585 		u32 len;
586 		char *drives, *volume;
587 		len = GetLogicalDriveStrings(0, NULL);
588 		drives = (char*)gf_malloc(sizeof(char)*(len+1));
589 		drives[0]=0;
590 		GetLogicalDriveStrings(len, drives);
591 		len = (u32) strlen(drives);
592 		volume = drives;
593 		file_info.directory = GF_TRUE;
594 		file_info.drive = GF_TRUE;
595 		while (len) {
596 			enum_dir_fct(cbck, volume, "", &file_info);
597 			volume += len+1;
598 			len = (u32) strlen(volume);
599 		}
600 		gf_free(drives);
601 		return GF_OK;
602 #elif defined(__SYMBIAN32__)
603 		RFs iFs;
604 		TDriveList aList;
605 		iFs.Connect();
606 		iFs.DriveList(aList);
607 		for (TInt i=0; i<KMaxDrives; i++) {
608 			if (aList[i]) {
609 				char szDrive[10];
610 				TChar aDrive;
611 				iFs.DriveToChar(i, aDrive);
612 				sprintf(szDrive, "%c:", (TUint)aDrive);
613 				enum_dir_fct(cbck, szDrive, "", &file_info);
614 			}
615 		}
616 		iFs.Close();
617 		FlushItemList();
618 		return GF_OK;
619 #endif
620 	}
621 
622 
623 #if defined (_WIN32_WCE)
624 	switch (dir[strlen(dir) - 1]) {
625 	case '/':
626 	case '\\':
627 		sprintf(_path, "%s*", dir);
628 		break;
629 	default:
630 		sprintf(_path, "%s%c*", dir, GF_PATH_SEPARATOR);
631 		break;
632 	}
633 	CE_CharToWide(_path, path);
634 	CE_CharToWide((char *)filter, w_filter);
635 #elif defined(WIN32)
636 	{
637 		const char* tmpdir = dir;
638 		gf_utf8_mbstowcs(w_dir, sizeof(w_dir), &tmpdir);
639 	}
640 	switch (w_dir[wcslen(w_dir) - 1]) {
641 	case '/':
642 	case '\\':
643 		swprintf(path, MAX_PATH, L"%s*", w_dir);
644 		break;
645 	default:
646 		swprintf(path, MAX_PATH, L"%s%c*", w_dir, GF_PATH_SEPARATOR);
647 		break;
648 	}
649 	{
650 		const char* tmpfilter = filter;
651 		gf_utf8_mbstowcs(w_filter, sizeof(w_filter), &tmpfilter);
652 	}
653 #else
654 	strcpy(path, dir);
655 	if (path[strlen(path)-1] != '/') strcat(path, "/");
656 #endif
657 
658 #ifdef WIN32
659 	SearchH= FindFirstFileW(path, &FindData);
660 	if (SearchH == INVALID_HANDLE_VALUE) return GF_IO_ERR;
661 
662 #if defined (_WIN32_WCE)
663 	_path[strlen(_path)-1] = 0;
664 #else
665 	path[wcslen(path)-1] = 0;
666 #endif
667 
668 	while (SearchH != INVALID_HANDLE_VALUE) {
669 
670 #else
671 
672 	the_dir = opendir(path);
673 	if (the_dir == NULL) {
674 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot open directory \"%s\" for enumeration: %d\n", path, errno));
675 		return GF_IO_ERR;
676 	}
677 	the_file = readdir(the_dir);
678 	while (the_file) {
679 
680 #endif
681 
682 		memset(&file_info, 0, sizeof(GF_FileEnumInfo) );
683 
684 
685 #if defined (_WIN32_WCE)
686 		if (!wcscmp(FindData.cFileName, _T(".") )) goto next;
687 		if (!wcscmp(FindData.cFileName, _T("..") )) goto next;
688 #elif defined(WIN32)
689 		if (!wcscmp(FindData.cFileName, L".")) goto next;
690 		if (!wcscmp(FindData.cFileName, L"..")) goto next;
691 #else
692 		if (!strcmp(the_file->d_name, "..")) goto next;
693 		if (the_file->d_name[0] == '.') goto next;
694 #endif
695 
696 #ifdef WIN32
697 		file_info.directory = (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? GF_TRUE : GF_FALSE;
698 		if (!enum_directory && file_info.directory) goto next;
699 		if (enum_directory && !file_info.directory) goto next;
700 #endif
701 
702 		if (filter) {
703 #if defined (_WIN32_WCE)
704 			short ext[30];
705 			short *sep = wcsrchr(FindData.cFileName, (wchar_t) '.');
706 			short *found_ext;
707 			u32 ext_len;
708 			if (!sep) goto next;
709 			wcscpy(ext, sep+1);
710 			wcslwr(ext);
711 			ext_len = (u32) wcslen(ext);
712 			found_ext = wcsstr(w_filter, ext);
713 			if (!found_ext) goto next;
714 			if (found_ext[ext_len] && (found_ext[ext_len] != (wchar_t) ';')) goto next;
715 #elif defined(WIN32)
716 			wchar_t ext[30];
717 			wchar_t *sep = wcsrchr(FindData.cFileName, L'.');
718 			wchar_t *found_ext;
719 			u32 ext_len;
720 			if (!sep) goto next;
721 			wcscpy(ext, sep+1);
722 			wcslwr(ext);
723 			ext_len = (u32) wcslen(ext);
724 			found_ext = wcsstr(w_filter, ext);
725 			if (!found_ext) goto next;
726 			if (found_ext[ext_len] && (found_ext[ext_len] != L';')) goto next;
727 #else
728 			char ext[30];
729 			char *sep = strrchr(the_file->d_name, '.');
730 			char *found_ext;
731 			u32 ext_len;
732 			if (!sep) goto next;
733 			strcpy(ext, sep+1);
734 			strlwr(ext);
735 			ext_len = (u32) strlen(ext);
736 			found_ext = strstr(filter, ext);
737 			if (!found_ext) goto next;
738 			if (found_ext[ext_len] && (found_ext[ext_len]!=';')) goto next;
739 #endif
740 		}
741 
742 #if defined(WIN32)
743 		file_info.hidden = (FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? GF_TRUE : GF_FALSE;
744 		file_info.system = (FindData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) ? GF_TRUE : GF_FALSE;
745 		file_info.size = MAXDWORD;
746 		file_info.size += 1;
747 		file_info.size *= FindData.nFileSizeHigh;
748 		file_info.size += FindData.nFileSizeLow;
749 		file_info.last_modified = (u64) ((*(LONGLONG *) &FindData.ftLastWriteTime - TIMESPEC_TO_FILETIME_OFFSET) / 10000000);
750 #endif
751 
752 #if defined (_WIN32_WCE)
753 		CE_WideToChar(FindData.cFileName, file);
754 		strcpy(item_path, _path);
755 		strcat(item_path, file);
756 #elif defined(WIN32)
757 		wcscpy(item_path, path);
758 		wcscat(item_path, FindData.cFileName);
759 		file = FindData.cFileName;
760 #else
761 		strcpy(item_path, path);
762 		strcat(item_path, the_file->d_name);
763 		GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Checking file \"%s\" for enum\n", item_path));
764 
765 		if (stat( item_path, &st ) != 0) goto next;
766 
767 		file_info.directory = ((st.st_mode & S_IFMT) == S_IFDIR) ? GF_TRUE : GF_FALSE;
768 		if (enum_directory && !file_info.directory) goto next;
769 		if (!enum_directory && file_info.directory) goto next;
770 
771 		file_info.size = st.st_size;
772 
773 		{
774 			struct tm _t = * gf_gmtime(& st.st_mtime);
775 			file_info.last_modified = mktime(&_t);
776 		}
777 		file = the_file->d_name;
778 		if (file && file[0]=='.') file_info.hidden = 1;
779 
780 		if (file_info.directory) {
781 			char * parent_name = strrchr(item_path, '/');
782 			if (!parent_name) {
783 				file_info.drive = GF_TRUE;
784 			} else {
785 				struct stat st_parent;
786 				parent_name[0] = 0;
787 				if (stat(item_path, &st_parent) == 0)  {
788 					if ((st.st_dev != st_parent.st_dev) || (st.st_ino == st_parent.st_ino) ) {
789 						file_info.drive = GF_TRUE;
790 					}
791 				}
792 				parent_name[0] = '/';
793 			}
794 		}
795 #endif
796 
797 #ifdef WIN32
798 		mbs_file = gf_wcs_to_utf8(file);
799 		mbs_item_path = gf_wcs_to_utf8(item_path);
800 		if (!mbs_file || !mbs_item_path)
801 		{
802 			if (mbs_file) gf_free(mbs_file);
803 			if (mbs_item_path) gf_free(mbs_item_path);
804 			return GF_IO_ERR;
805 		}
806 		if (enum_dir_fct(cbck, mbs_file, mbs_item_path, &file_info)) {
807 			BOOL ret = FindClose(SearchH);
808 			if (!ret) {
809 				DWORD err = GetLastError();
810 				GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_enum_directory() returned(1) the following error code: %d\n", err));
811 			}
812 #else
813 		if (enum_dir_fct(cbck, file, item_path, &file_info)) {
814 #endif
815 			break;
816 		}
817 
818 #ifdef WIN32
819 		gf_free(mbs_file);
820 		gf_free(mbs_item_path);
821 #endif
822 
823 next:
824 #ifdef WIN32
825 		if (!FindNextFileW(SearchH, &FindData)) {
826 			BOOL ret = FindClose(SearchH);
827 			if (!ret) {
828 				DWORD err = GetLastError();
829 				GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_enum_directory() returned(2) the following error code: %d\n", err));
830 			}
831 			break;
832 		}
833 #else
834 		the_file = readdir(the_dir);
835 #endif
836 	}
837 #ifndef WIN32
838 	closedir(the_dir);
839 #endif
840 	return GF_OK;
841 }
842 
843 
844 
845 struct __gf_file_io
846 {
847 	u32 _reserved_null;
848 	void *__this;
849 
850 	GF_FileIO * (*open)(GF_FileIO *fileio_ref, const char *url, const char *mode, GF_Err *out_error);
851 	GF_Err (*seek)(GF_FileIO *fileio, u64 offset, s32 whence);
852 	u32 (*read)(GF_FileIO *fileio, u8 *buffer, u32 bytes);
853 	u32 (*write)(GF_FileIO *fileio, u8 *buffer, u32 bytes);
854 	s64 (*tell)(GF_FileIO *fileio);
855 	Bool (*eof)(GF_FileIO *fileio);
856 	int (*printf)(GF_FileIO *gfio, const char *format, va_list args);
857 
858 	char *url;
859 	char *res_url;
860 	void *udta;
861 
862 	u64 bytes_done, file_size_plus_one;
863 	Bool cache_complete;
864 	u32 bytes_per_sec;
865 
866 	u32 printf_alloc;
867 	u8* printf_buf;
868 };
869 
870 GF_EXPORT
871 Bool gf_fileio_check(FILE *fp)
872 {
873 	GF_FileIO *fio = (GF_FileIO *)fp;
874 	if ((fp==stdin) || (fp==stderr) || (fp==stdout))
875 		return GF_FALSE;
876 
877 	if (fio && !fio->_reserved_null && (fio->__this==fio))
878 		return GF_TRUE;
879 	return GF_FALSE;
880 }
881 
882 typedef struct
883 {
884 	u8 *data;
885 	u32 size;
886 	u32 pos;
887 } GF_FileIOBlob;
888 
889 static GF_FileIO *gfio_blob_open(GF_FileIO *fileio_ref, const char *url, const char *mode, GF_Err *out_error)
890 {
891 	GF_FileIOBlob *blob = gf_fileio_get_udta(fileio_ref);
892 	if (!url) {
893 		gf_free(blob);
894 		gf_fileio_del(fileio_ref);
895 	}
896 	return NULL;
897 }
898 
899 static GF_Err gfio_blob_seek(GF_FileIO *fileio, u64 offset, s32 whence)
900 {
901 	GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
902 	if (whence==SEEK_END) blob->pos = blob->size;
903 	else if (whence==SEEK_SET) blob->pos = 0;
904 	else {
905 		if (blob->pos + offset > blob->size) return GF_BAD_PARAM;
906 		blob->pos += (u32) offset;
907 	}
908 	return GF_OK;
909 }
910 static u32 gfio_blob_read(GF_FileIO *fileio, u8 *buffer, u32 bytes)
911 {
912 	GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
913 	if (bytes + blob->pos > blob->size)
914 		bytes = blob->size - blob->pos;
915 	if (bytes) {
916 		memcpy(buffer, blob->data+blob->pos, bytes);
917 		blob->pos += bytes;
918 	}
919 	return bytes;
920 }
921 static s64 gfio_blob_tell(GF_FileIO *fileio)
922 {
923 	GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
924 	return (s64) blob->pos;
925 }
926 static Bool gfio_blob_eof(GF_FileIO *fileio)
927 {
928 	GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
929 	if (blob->pos==blob->size) return GF_TRUE;
930 	return GF_FALSE;
931 }
932 
933 GF_EXPORT
934 GF_FileIO *gf_fileio_new(char *url, void *udta,
935 	gfio_open_proc open,
936 	gfio_seek_proc seek,
937 	gfio_read_proc read,
938 	gfio_write_proc write,
939 	gfio_tell_proc tell,
940 	gfio_eof_proc eof,
941 	gfio_printf_proc printf)
942 {
943 	char szURL[100];
944 	GF_FileIO *tmp;
945 
946 	if (!open || !seek || !tell || !eof) return NULL;
947 
948 	if (!write && !read) return NULL;
949 	GF_SAFEALLOC(tmp, GF_FileIO);
950 	if (!tmp) return NULL;
951 
952 	tmp->open = open;
953 	tmp->seek = seek;
954 	tmp->write = write;
955 	tmp->read = read;
956 	tmp->tell = tell;
957 	tmp->eof = eof;
958 	tmp->printf = printf;
959 
960 	tmp->udta = udta;
961 	if (url)
962 		tmp->res_url = gf_strdup(url);
963 
964 	sprintf(szURL, "gfio://%p", tmp);
965 	tmp->url = gf_strdup(szURL);
966 	tmp->__this = tmp;
967 	return tmp;
968 }
969 
970 GF_EXPORT
971 const char * gf_fileio_url(GF_FileIO *gfio)
972 {
973 	return gfio ? gfio->url : NULL;
974 }
975 
976 GF_EXPORT
977 const char * gf_fileio_resource_url(GF_FileIO *gfio)
978 {
979 	return gfio ? gfio->res_url : NULL;
980 }
981 
982 GF_EXPORT
983 void gf_fileio_del(GF_FileIO *gfio)
984 {
985 	if (!gfio) return;
986 	if (gfio->url) gf_free(gfio->url);
987 	if (gfio->res_url) gf_free(gfio->res_url);
988 	if (gfio->printf_buf) gf_free(gfio->printf_buf);
989 	gf_free(gfio);
990 }
991 
992 GF_EXPORT
993 void *gf_fileio_get_udta(GF_FileIO *gfio)
994 {
995 	return gfio ? gfio->udta : NULL;
996 }
997 
998 GF_EXPORT
999 GF_FileIO *gf_fileio_open_url(GF_FileIO *gfio_ref, const char *url, const char *mode, GF_Err *out_err)
1000 {
1001 	if (!gfio_ref) {
1002 		*out_err = GF_BAD_PARAM;
1003 		return NULL;
1004 	}
1005 	if (!gfio_ref->open) {
1006 		*out_err = url ? GF_NOT_SUPPORTED : GF_OK;
1007 		return NULL;
1008 	}
1009 	return gfio_ref->open(gfio_ref, url, mode, out_err);
1010 }
1011 
1012 static u32 gf_fileio_read(GF_FileIO *gfio, u8 *buffer, u32 nb_bytes)
1013 {
1014 	if (!gfio) return GF_BAD_PARAM;
1015 	if (gfio->read)
1016 		return gfio->read(gfio, buffer, nb_bytes);
1017 	return 0;
1018 }
1019 
1020 static u32 gf_fileio_write(GF_FileIO *gfio, u8 *buffer, u32 nb_bytes)
1021 {
1022 	if (!gfio) return GF_BAD_PARAM;
1023 	if (!gfio->write) return 0;
1024 	return gfio->write(gfio, buffer, (u32) nb_bytes);
1025 }
1026 
1027 int gf_fileio_printf(GF_FileIO *gfio, const char *format, va_list args)
1028 {
1029 	va_list args_copy;
1030 	if (!gfio) return -1;
1031 	if (gfio->printf) return gfio->printf(gfio, format, args);
1032 
1033 	if (!gfio->write) return -1;
1034 
1035 	va_copy(args_copy, args);
1036 	u32 len=vsnprintf(NULL, 0, format, args_copy);
1037 	va_end(args_copy);
1038 
1039 	if (len>=gfio->printf_alloc) {
1040 		gfio->printf_alloc = len+1;
1041 		gfio->printf_buf = gf_realloc(gfio->printf_buf, gfio->printf_alloc);
1042 	}
1043 	vsnprintf(gfio->printf_buf, len, format, args);
1044 	gfio->printf_buf[len] = 0;
1045 	return gfio->write(gfio, gfio->printf_buf, len+1);
1046 }
1047 
1048 GF_EXPORT
1049 Bool gf_fileio_write_mode(GF_FileIO *gfio)
1050 {
1051 	return (gfio && gfio->write) ? GF_TRUE : GF_FALSE;
1052 }
1053 
1054 GF_EXPORT
1055 Bool gf_fileio_read_mode(GF_FileIO *gfio)
1056 {
1057 	return (gfio && gfio->read) ? GF_TRUE : GF_FALSE;
1058 }
1059 
1060 GF_EXPORT
1061 GF_FileIO *gf_fileio_from_url(const char *url)
1062 {
1063 	char szURL[100];
1064 	GF_FileIO *ptr=NULL;
1065 	if (!url) return NULL;
1066 
1067 	sscanf(url, "gfio://%p", &ptr);
1068 	sprintf(szURL, "gfio://%p", ptr);
1069 	if (strcmp(url, szURL))
1070 		return NULL;
1071 
1072 	if (ptr && ptr->url && !strcmp(ptr->url, url) ) {
1073 		return ptr;
1074 	}
1075 	return NULL;
1076 }
1077 
1078 GF_EXPORT
1079 const char * gf_fileio_translate_url(const char *url)
1080 {
1081 	GF_FileIO *gfio = gf_fileio_from_url(url);
1082 	return gfio ? gfio->res_url : NULL;
1083 }
1084 
1085 Bool gf_fileio_eof(GF_FileIO *fileio)
1086 {
1087 	if (!fileio || !fileio->tell) return GF_TRUE;
1088 	return fileio->eof(fileio);
1089 }
1090 
1091 int gf_fileio_flush(GF_FileIO *fileio)
1092 {
1093 	if (!fileio || !fileio->write) return 0;
1094 	fileio->write(fileio, NULL, 0);
1095 	return 0;
1096 }
1097 
1098 GF_EXPORT
1099 const char *gf_fileio_factory(GF_FileIO *gfio, const char *new_res_url)
1100 {
1101 	GF_Err e;
1102 	if (!gfio || !gfio->open) return NULL;
1103 	GF_FileIO *new_res = gfio->open(gfio, new_res_url, "url", &e);
1104 	if (e) return NULL;
1105 	return gf_fileio_url(new_res);
1106 }
1107 
1108 GF_EXPORT
1109 void gf_fileio_set_stats(GF_FileIO *gfio, u64 bytes_done, u64 file_size, Bool cache_complete, u32 bytes_per_sec)
1110 {
1111 	if (!gfio) return;
1112 	gfio->bytes_done = bytes_done;
1113 	gfio->file_size_plus_one = file_size ? (1 + file_size) : 0;
1114 	gfio->cache_complete = cache_complete;
1115 	gfio->bytes_per_sec = bytes_per_sec;
1116 }
1117 
1118 
1119 GF_EXPORT
1120 Bool gf_fileio_get_stats(GF_FileIO *gfio, u64 *bytes_done, u64 *file_size, Bool *cache_complete, u32 *bytes_per_sec)
1121 {
1122 	if (!gf_fileio_check((FILE *)gfio))
1123 		return GF_FALSE;
1124 
1125 	if (bytes_done) *bytes_done = gfio->bytes_done;
1126 	if (file_size) *file_size = gfio->file_size_plus_one ? gfio->file_size_plus_one-1 : 0;
1127 	if (cache_complete) *cache_complete = gfio->cache_complete;
1128 	if (bytes_per_sec) *bytes_per_sec = gfio->bytes_per_sec;
1129 	return GF_TRUE;
1130 }
1131 
1132 GF_EXPORT
1133 u64 gf_ftell(FILE *fp)
1134 {
1135 	if (!fp) return -1;
1136 
1137 	if (gf_fileio_check(fp)) {
1138 		GF_FileIO *gfio = (GF_FileIO *)fp;
1139 		if (!gfio->tell) return -1;
1140 		return gfio->tell(gfio);
1141 	}
1142 
1143 #if defined(_WIN32_WCE)
1144 	return (u64) ftell(fp);
1145 #elif defined(GPAC_CONFIG_WIN32) && (defined(__CYGWIN__) || defined(__MINGW32__))
1146 #if (_FILE_OFFSET_BITS >= 64)
1147 	return (u64) ftello64(fp);
1148 #else
1149 	return (u64) gf_ftell(fp);
1150 #endif
1151 #elif defined(WIN32)
1152 	return (u64) _ftelli64(fp);
1153 #elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID)
1154 	return (u64) ftello64(fp);
1155 #elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
1156 	return (u64) ftello(fp);
1157 #else
1158 	return (u64) gf_ftell(fp);
1159 #endif
1160 }
1161 
1162 GF_EXPORT
1163 s32 gf_fseek(FILE *fp, s64 offset, s32 whence)
1164 {
1165 	if (!fp) return -1;
1166 	if (gf_fileio_check(fp)) {
1167 		GF_FileIO *gfio = (GF_FileIO *)fp;
1168 		if (gfio->seek) {
1169 			GF_Err e = gfio->seek(gfio, offset, whence);
1170 			if (e) return -1;
1171 			return 0;
1172 		}
1173 		return -1;
1174 	}
1175 
1176 #if defined(_WIN32_WCE)
1177 	return (u64) fseek(fp, (s32) offset, whence);
1178 #elif defined(GPAC_CONFIG_WIN32) && defined(__CYGWIN__)	/* mingw or cygwin */
1179 #if (_FILE_OFFSET_BITS >= 64)
1180 	return (u64) fseeko64(fp, offset, whence);
1181 #else
1182 	return (u64) fseek(fp, (s32) offset, whence);
1183 #endif
1184 #elif defined(WIN32)
1185 	return (u64) _fseeki64(fp, offset, whence);
1186 #elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID)
1187 	return fseeko64(fp, (off64_t) offset, whence);
1188 #elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
1189 	return fseeko(fp, (off_t) offset, whence);
1190 #else
1191 	return fseek(fp, (s32) offset, whence);
1192 #endif
1193 }
1194 
1195 
1196 static GF_FileIO *gf_fileio_from_blob(const char *file_name)
1197 {
1198 	u8 *blob_data;
1199 	u32 blob_size;
1200 	GF_FileIOBlob *gfio_blob;
1201 	GF_Err e = gf_blob_get_data(file_name, &blob_data, &blob_size);
1202 	if (e || !blob_data) return NULL;
1203 
1204 	GF_SAFEALLOC(gfio_blob, GF_FileIOBlob);
1205 	if (!gfio_blob) return NULL;
1206 	gfio_blob->data = blob_data;
1207 	gfio_blob->size = blob_size;
1208 	return 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);
1209 }
1210 GF_EXPORT
1211 FILE *gf_fopen_ex(const char *file_name, const char *parent_name, const char *mode)
1212 {
1213 	FILE *res = NULL;
1214 	u32 gfio_type = 0;
1215 
1216 	if (!file_name) return NULL;
1217 
1218 	if (!strncmp(file_name, "gmem://", 7)) {
1219 		GF_FileIO *new_gfio;
1220 		if (strstr(mode, "w"))
1221 			return NULL;
1222 		new_gfio = gf_fileio_from_blob(file_name);
1223 		if (new_gfio)
1224 			gf_register_file_handle(file_name, (FILE *) new_gfio);
1225 		return (FILE *) new_gfio;
1226 
1227 	}
1228 
1229 	if (!strncmp(file_name, "gfio://", 7))
1230 		gfio_type = 1;
1231 	else if (parent_name && !strncmp(parent_name, "gfio://", 7))
1232 		gfio_type = 2;
1233 
1234 	if (gfio_type) {
1235 		GF_FileIO *gfio_ref;
1236 		GF_FileIO *new_gfio;
1237 		GF_Err e;
1238 
1239 		if (gfio_type==1)
1240 			gfio_ref = gf_fileio_from_url(file_name);
1241 		else
1242 			gfio_ref = gf_fileio_from_url(parent_name);
1243 
1244 		if (!gfio_ref) return NULL;
1245 		if (strchr(mode, 'r') && !gf_fileio_read_mode(gfio_ref)) {
1246 			GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("FileIO %s is not read-enabled and open mode %s was requested\n", file_name, mode));
1247 			return NULL;
1248 		}
1249 		if ((strchr(mode, 'w') || strchr(mode, 'a'))  && !gf_fileio_write_mode(gfio_ref)) {
1250 			GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("FileIO %s is not write-enabled and open mode %s was requested\n", file_name, mode));
1251 			return NULL;
1252 		}
1253 		new_gfio = gf_fileio_open_url(gfio_ref, file_name, mode, &e);
1254 		if (e) {
1255 			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)));
1256 			return NULL;
1257 		}
1258 		gf_register_file_handle(file_name, (FILE *) new_gfio);
1259 		return (FILE *) new_gfio;
1260 	}
1261 
1262 	if (strchr(mode, 'w')) {
1263 		char *fname = gf_strdup(file_name);
1264 		char *sep = strchr(fname, '/');
1265 		if (!sep) sep = strchr(fname, '\\');
1266 		if (file_name[0] == '/') sep = strchr(fname+1, '/');
1267 		else if (file_name[2] == '\\') sep = strchr(fname+3, '\\');
1268 
1269 		while (sep) {
1270 			char *n_sep;
1271 			char c = sep[0];
1272 			sep[0] = 0;
1273 			if (!gf_dir_exists(fname)) {
1274 				GF_Err e = gf_mkdir(fname);
1275 				if (e != GF_OK) {
1276 					GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Failed to create directory \"%s\": %s\n", file_name, gf_error_to_string(e) ));
1277 					sep[0] = c;
1278 					gf_free(fname);
1279 					return NULL;
1280 				}
1281 			}
1282 			sep[0] = c;
1283 			n_sep = strchr(sep+1, '/');
1284 			if (!n_sep) n_sep = strchr(sep+1, '\\');
1285 			sep = n_sep;
1286 		}
1287 		gf_free(fname);
1288 	}
1289 
1290 #if defined(WIN32)
1291 	wchar_t *wname;
1292 	wchar_t *wmode;
1293 
1294 	wname = gf_utf8_to_wcs(file_name);
1295 	wmode = gf_utf8_to_wcs(mode);
1296 	if (!wname || !wmode)
1297 	{
1298 		if (wname) gf_free(wname);
1299 		if (wmode) gf_free(wmode);
1300 		return NULL;
1301 	}
1302 	res = _wfsopen(wname, wmode, _SH_DENYNO);
1303 	gf_free(wname);
1304 	gf_free(wmode);
1305 #elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID)
1306 	res = fopen64(file_name, mode);
1307 #elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
1308 	res = fopen(file_name, mode);
1309 #else
1310 	res = fopen(file_name, mode);
1311 #endif
1312 
1313 	if (res) {
1314 		gf_register_file_handle(file_name, res);
1315 		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));
1316 	} else {
1317 		if (strchr(mode, 'w') || strchr(mode, 'a')) {
1318 #if defined(WIN32)
1319 			u32 err = GetLastError();
1320 			GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] system failure for file opening of \"%s\" in mode \"%s\": 0x%08x\n", file_name, mode, err));
1321 #else
1322 			GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] system failure for file opening of \"%s\" in mode \"%s\": %d\n", file_name, mode, errno));
1323 #endif
1324 		}
1325 	}
1326 	return res;
1327 }
1328 
1329 GF_EXPORT
1330 FILE *gf_fopen(const char *file_name, const char *mode)
1331 {
1332 	return gf_fopen_ex(file_name, NULL, mode);
1333 }
1334 
1335 GF_EXPORT
1336 s32 gf_fclose(FILE *file)
1337 {
1338 	if (!file)
1339 		return GF_OK;
1340 
1341 	gf_unregister_file_handle(file);
1342 	if (gf_fileio_check(file)) {
1343 		GF_Err e;
1344 		gf_fileio_open_url((GF_FileIO *) file, NULL, "deref", &e);
1345 		if (e) return -1;
1346 		return 0;
1347 	}
1348 	return fclose(file);
1349 }
1350 
1351 #if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! defined(_GNU_SOURCE) && !defined(WIN32)
1352 #define HAVE_STRERROR_R 1
1353 #endif
1354 
1355 GF_EXPORT
1356 size_t gf_fwrite(const void *ptr, size_t nb_bytes, FILE *stream)
1357 {
1358 	size_t result=0;
1359 
1360 	if (gf_fileio_check(stream)) {
1361 		return(size_t) gf_fileio_write((GF_FileIO *)stream, (u8 *) ptr, (u32) nb_bytes);
1362 	}
1363 
1364 	if (ptr)
1365 		result = fwrite(ptr, 1, nb_bytes, stream);
1366 	if (result != nb_bytes) {
1367 #ifdef _WIN32_WCE
1368 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Error writing data: %d blocks to write but %d blocks written\n", nb_bytes, result));
1369 #else
1370 #if defined WIN32 && !defined(GPAC_CONFIG_WIN32)
1371 		errno_t errno_save;
1372 		_get_errno(&errno_save);
1373 #else
1374 		int errno_save = errno;
1375 #endif
1376 		//if (errno_save!=0)
1377 		{
1378 #ifdef HAVE_STRERROR_R
1379 #define ERRSTR_BUF_SIZE 256
1380 			char errstr[ERRSTR_BUF_SIZE];
1381 			if(strerror_r(errno_save, errstr, ERRSTR_BUF_SIZE) != 0)
1382 			{
1383 				strerror_r(0, errstr, ERRSTR_BUF_SIZE);
1384 			}
1385 #else
1386 			char *errstr = (char*)strerror(errno_save);
1387 #endif
1388 
1389 #ifndef GPAC_DISABLE_LOG
1390 			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));
1391 #else
1392 			fprintf(stderr, "Error writing data (%s): %d blocks to write but %d blocks written\n", errstr, (u32) nb_bytes, (u32) result);
1393 #endif
1394 
1395 		}
1396 #endif
1397 	}
1398 	return result;
1399 }
1400 
1401 GF_EXPORT
1402 size_t gf_fread(void *ptr, size_t nbytes, FILE *stream)
1403 {
1404 	size_t result;
1405 	if (gf_fileio_check(stream)) {
1406 		return (size_t) gf_fileio_read((GF_FileIO *)stream, ptr, (u32) nbytes);
1407 	}
1408 	result = fread(ptr, 1, nbytes, stream);
1409 	return result;
1410 }
1411 
1412 GF_EXPORT
1413 char *gf_fgets(char *ptr, size_t size, FILE *stream)
1414 {
1415 	if (gf_fileio_check(stream)) {
1416 		GF_FileIO *fio = (GF_FileIO *)stream;
1417 		u32 i, read, nb_read=0;
1418 		for (i=0; i<size; i++) {
1419 			u8 buf[1];
1420 			read = (u32) gf_fileio_read(fio, buf, 1);
1421 			if (!read) break;
1422 
1423 			ptr[nb_read] = buf[0];
1424 			nb_read++;
1425 			if (buf[0]=='\n') break;
1426 		}
1427 		if (!nb_read) return NULL;
1428 		return ptr;
1429 	}
1430 	return fgets(ptr, (u32) size, stream);
1431 }
1432 
1433 GF_EXPORT
1434 int gf_fgetc(FILE *stream)
1435 {
1436 	if (gf_fileio_check(stream)) {
1437 		GF_FileIO *fio = (GF_FileIO *)stream;
1438 		u8 buf[1];
1439 		u32 read = gf_fileio_read(fio, buf, 1);
1440 		if (!read) return -1;
1441 		return buf[0];
1442 	}
1443 	return fgetc(stream);
1444 }
1445 
1446 GF_EXPORT
1447 int gf_fputc(int val, FILE *stream)
1448 {
1449 	if (gf_fileio_check(stream)) {
1450 		GF_FileIO *fio = (GF_FileIO *)stream;
1451 		u32 write;
1452 		u8 buf[1];
1453 		buf[0] = val;
1454 		write = gf_fileio_write(fio, buf, 1);
1455 		if (!write) return -1;
1456 		return buf[0];
1457 	}
1458 	return fputc(val, stream);
1459 }
1460 
1461 GF_EXPORT
1462 int	gf_fputs(const char *buf, FILE *stream)
1463 {
1464 	if (gf_fileio_check(stream)) {
1465 		GF_FileIO *fio = (GF_FileIO *)stream;
1466 		u32 write, len = (u32) strlen(buf);
1467 		write = gf_fileio_write(fio, (u8 *) buf, len);
1468 		if (write != len) return -1;
1469 		return write;
1470 	}
1471 	return fputs(buf, stream);
1472 }
1473 
1474 GF_EXPORT
1475 int gf_fprintf(FILE *stream, const char *format, ...)
1476 {
1477 	int	res;
1478 	va_list args;
1479 	va_start(args, format);
1480 	if (gf_fileio_check(stream)) {
1481 		res = gf_fileio_printf((GF_FileIO *)stream, format, args);
1482 	} else {
1483 		res = vfprintf(stream, format, args);
1484 	}
1485 	va_end(args);
1486 	return res;
1487 }
1488 
1489 GF_EXPORT
1490 int gf_fflush(FILE *stream)
1491 {
1492 	if (gf_fileio_check(stream)) {
1493 		return gf_fileio_flush((GF_FileIO *)stream);
1494 	}
1495 	return fflush(stream);
1496 }
1497 
1498 GF_EXPORT
1499 int gf_feof(FILE *stream)
1500 {
1501 	if (gf_fileio_check(stream)) {
1502 		return gf_fileio_eof((GF_FileIO *)stream) ? 1 : 0;
1503 	}
1504 	return feof(stream);
1505 }
1506 
1507 
1508 GF_EXPORT
1509 int gf_ferror(FILE *stream)
1510 {
1511 	if (gf_fileio_check(stream)) {
1512 		return 0;
1513 	}
1514 	return ferror(stream);
1515 }
1516 
1517 GF_EXPORT
1518 u64 gf_fsize(FILE *fp)
1519 {
1520 	u64 size;
1521 
1522 	if (gf_fileio_check(fp)) {
1523 		GF_FileIO *gfio = (GF_FileIO *)fp;
1524 		if (gfio->file_size_plus_one) {
1525 			gf_fseek(fp, 0, SEEK_SET);
1526 			return gfio->file_size_plus_one - 1;
1527 		}
1528 		//fall through
1529 	}
1530 	gf_fseek(fp, 0, SEEK_END);
1531 	size = gf_ftell(fp);
1532 	gf_fseek(fp, 0, SEEK_SET);
1533 	return size;
1534 }
1535 
1536 /**
1537   * Returns a pointer to the start of a filepath basename
1538  **/
1539 GF_EXPORT
1540 char* gf_file_basename(const char* filename)
1541 {
1542 	char* lastPathPart = NULL;
1543 	if (filename) {
1544 		lastPathPart = strrchr(filename , GF_PATH_SEPARATOR);
1545 #if GF_PATH_SEPARATOR != '/'
1546 		// windows paths can mix slashes and backslashes
1547 		// so we search for the last slash that occurs after the last backslash
1548 		// if it occurs before it's not relevant
1549 		// if there's no backslashes we search in the whole file path
1550 
1551 		char* trailingSlash = strrchr(lastPathPart?lastPathPart:filename, '/');
1552 		if (trailingSlash)
1553 			lastPathPart = trailingSlash;
1554 #endif
1555 		if (!lastPathPart)
1556 			lastPathPart = (char *)filename;
1557 		else
1558 			lastPathPart++;
1559 	}
1560 	return lastPathPart;
1561 }
1562 
1563 /**
1564   * Returns a pointer to the start of a filepath extension or null
1565  **/
1566 GF_EXPORT
1567 char* gf_file_ext_start(const char* filename)
1568 {
1569 	char* basename;
1570 
1571 	if (filename && !strncmp(filename, "gfio://", 7)) {
1572 		GF_FileIO *gfio = gf_fileio_from_url(filename);
1573 		filename = gf_fileio_resource_url(gfio);
1574 	}
1575 	basename = gf_file_basename(filename);
1576 
1577 	if (basename) {
1578 		char *ext = strrchr(basename, '.');
1579 		if (ext && !strcmp(ext, ".gz")) {
1580 			ext[0] = 0;
1581 			char *ext2 = strrchr(basename, '.');
1582 			ext[0] = '.';
1583 			if (ext2) return ext2;
1584 		}
1585 		return ext;
1586 	}
1587 	return NULL;
1588 }
1589 
1590 GF_EXPORT
1591 char* gf_url_colon_suffix(const char *path)
1592 {
1593 	char *sep = strchr(path, ':');
1594 	if (!sep) return NULL;
1595 
1596 	//handle Z:\ and Z:/
1597 	if ((path[1]==':') && ( (path[2]=='/') || (path[2]=='\\') ) )
1598 		return gf_url_colon_suffix(path+2);
1599 
1600 	if (!strncmp(path, "gfio://", 7) || !strncmp(path, "gmem://", 7)) {
1601 		return strchr(path+7, ':');
1602 	}
1603 
1604 	//handle PROTO://ADD:PORT/
1605 	if ((sep[1]=='/') && (sep[2]=='/')) {
1606 		char *next_colon, *next_slash;
1607 		sep++;
1608 		//skip all // (eg PROTO://////////////mytest/)
1609 		while (sep[0]=='/')
1610 			sep++;
1611 		if (!sep[0]) return NULL;
1612 
1613 		/*we may now have C:\ or C:/  (eg file://///C:\crazy\ or  file://///C:/crazy/)
1614 			if sep[1]==':', then sep[2] is valid (0 or something else), no need to check for len
1615 		*/
1616 		if ((sep[1]==':') && ( (sep[2]=='/') || (sep[2]=='\\') ) ) {
1617 			return gf_url_colon_suffix(sep+2);
1618 		}
1619 		//find closest : or /, if : is before / consider this is a port or an IPv6 address and check next : after /
1620 		next_colon = strchr(sep, ':');
1621 		next_slash = strchr(sep, '/');
1622 		if (next_colon && next_slash && ((next_slash - sep) > (next_colon - sep)) ) {
1623 			next_colon = strchr(next_slash, ':');
1624 		}
1625 		return next_colon;
1626 	}
1627 	return sep;
1628 }
1629