1 /*
2 *			GPAC - Multimedia Framework C SDK
3 *
4 *			Authors: Jean Le Feuvre - Copyright (c) Telecom ParisTech 2000-2012
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 <direct.h>
38 #include <sys/stat.h>
39 #include <share.h>
40 
41 #else
42 
43 #include <sys/stat.h>
44 #include <unistd.h>
45 #include <dirent.h>
46 #include <time.h>
47 #include <sys/time.h>
48 
49 #ifndef __BEOS__
50 #include <errno.h>
51 #endif
52 
53 #endif
54 
55 
gf_rmdir(char * DirPathName)56 GF_Err gf_rmdir(char *DirPathName)
57 {
58 #if defined (_WIN32_WCE)
59 	TCHAR swzName[MAX_PATH];
60 	BOOL res;
61 	CE_CharToWide(DirPathName, swzName);
62 	res = RemoveDirectory(swzName);
63 	if (!res) {
64 		int err = GetLastError();
65 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete directory %s: last error %d\n", DirPathName, err));
66 	}
67 #elif defined (WIN32)
68 	int res = rmdir(DirPathName);
69 	if (res == -1) {
70 		int err = GetLastError();
71 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete directory %s: last error %d\n", DirPathName, err));
72 		return GF_IO_ERR;
73 	}
74 #else
75 	int res = rmdir(DirPathName);
76 	if (res == -1) {
77 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete directory %s: last error %d\n", DirPathName, errno));
78 		return GF_IO_ERR;
79 	}
80 #endif
81 	return GF_OK;
82 }
83 
84 GF_EXPORT
gf_mkdir(char * DirPathName)85 GF_Err gf_mkdir(char* DirPathName)
86 {
87 #if defined (_WIN32_WCE)
88 	TCHAR swzName[MAX_PATH];
89 	BOOL res;
90 	CE_CharToWide(DirPathName, swzName);
91 	res = CreateDirectory(swzName, NULL);
92 	if (!res) {
93 		int err = GetLastError();
94 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create directory %s: last error %d\n", DirPathName, err));
95 	}
96 #elif defined (WIN32)
97 	int res = mkdir(DirPathName);
98 	if (res == -1) {
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 #else
103 	int res = mkdir(DirPathName, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
104 	if (res == -1) {
105 		if (errno == 17) {
106 			GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create directory %s, it already exists: last error %d \n", DirPathName, errno));
107 			return GF_BAD_PARAM;
108 		}
109 		else {
110 			GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create directory %s: last error %d\n", DirPathName, errno));
111 			return GF_IO_ERR;
112 		}
113 	}
114 #endif
115 	return GF_OK;
116 }
117 
118 
119 GF_EXPORT
gf_dir_exists(char * DirPathName)120 Bool gf_dir_exists(char* DirPathName)
121 {
122 #if defined (_WIN32_WCE)
123 	TCHAR swzName[MAX_PATH];
124 	BOOL res;
125 	DWORD att;
126 	CE_CharToWide(DirPathName, swzName);
127 	att = GetFileAttributes(swzName);
128 	return (att != INVALID_FILE_ATTRIBUTES && (att & FILE_ATTRIBUTE_DIRECTORY)) ? GF_TRUE : GF_FALSE;
129 #elif defined (WIN32)
130 	DWORD att = GetFileAttributes(DirPathName);
131 	return (att != INVALID_FILE_ATTRIBUTES && (att & FILE_ATTRIBUTE_DIRECTORY)) ? GF_TRUE : GF_FALSE;
132 #else
133 	DIR* dir = opendir(DirPathName);
134 	if (!dir) return GF_FALSE;
135 	closedir(dir);
136 	return GF_TRUE;
137 #endif
138 	return GF_FALSE;
139 }
delete_dir(void * cbck,char * item_name,char * item_path,GF_FileEnumInfo * file_info)140 static Bool delete_dir(void *cbck, char *item_name, char *item_path, GF_FileEnumInfo *file_info)
141 {
142 	Bool directory_clean_mode = *(Bool*)cbck;
143 
144 	if (directory_clean_mode) {
145 		gf_cleanup_dir(item_path);
146 		gf_rmdir(item_path);
147 	}
148 	else {
149 		gf_delete_file(item_path);
150 	}
151 	return GF_FALSE;
152 }
153 
gf_cleanup_dir(char * DirPathName)154 GF_Err gf_cleanup_dir(char* DirPathName)
155 {
156 	Bool directory_clean_mode;
157 
158 	directory_clean_mode = GF_TRUE;
159 	gf_enum_directory(DirPathName, GF_TRUE, delete_dir, &directory_clean_mode, NULL);
160 	directory_clean_mode = GF_FALSE;
161 	gf_enum_directory(DirPathName, GF_FALSE, delete_dir, &directory_clean_mode, NULL);
162 
163 	return GF_OK;
164 }
165 
166 GF_EXPORT
gf_delete_file(const char * fileName)167 GF_Err gf_delete_file(const char *fileName)
168 {
169 	if (!fileName) {
170 		GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("gf_delete_file deletes nothing - ignoring\n"));
171 		return GF_OK;
172 	}
173 #if defined(_WIN32_WCE)
174 	TCHAR swzName[MAX_PATH];
175 	CE_CharToWide((char*)fileName, swzName);
176 	return (DeleteFile(swzName) == 0) ? GF_IO_ERR : GF_OK;
177 #elif defined(WIN32)
178 	/* success if != 0 */
179 	return (DeleteFile(fileName) == 0) ? GF_IO_ERR : GF_OK;
180 #else
181 	/* success is == 0 */
182 	return (remove(fileName) == 0) ? GF_OK : GF_IO_ERR;
183 #endif
184 }
185 
186 #ifndef WIN32
187 /**
188 * Remove existing single-quote from a single-quoted string.
189 * The caller is responsible for deallocating the returns string with gf_free()
190 */
gf_sanetize_single_quoted_string(const char * src)191 static char* gf_sanetize_single_quoted_string(const char *src) {
192 	int i, j;
193 	char *out = (char*)gf_malloc(4 * strlen(src) + 3);
194 	out[0] = '\'';
195 	for (i = 0, j = 1; (out[j] = src[i]); ++i, ++j) {
196 		if (src[i] == '\'') {
197 			out[++j] = '\\';
198 			out[++j] = '\'';
199 			out[++j] = '\'';
200 		}
201 	}
202 	out[j++] = '\'';
203 	out[j++] = 0;
204 	return out;
205 }
206 #endif
207 
208 #if defined(GPAC_IPHONE) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 80000)
209 #include <spawn.h>
210 extern char **environ;
211 #endif
212 
213 GF_EXPORT
gf_file_exists(const char * fileName)214 Bool gf_file_exists(const char *fileName)
215 {
216 	FILE *f = gf_fopen(fileName, "r");
217 	if (f) {
218 		gf_fclose(f);
219 		return GF_TRUE;
220 	}
221 	return GF_FALSE;
222 }
223 
224 GF_EXPORT
gf_move_file(const char * fileName,const char * newFileName)225 GF_Err gf_move_file(const char *fileName, const char *newFileName)
226 {
227 #if defined(_WIN32_WCE)
228 	TCHAR swzName[MAX_PATH];
229 	TCHAR swzNewName[MAX_PATH];
230 	CE_CharToWide((char*)fileName, swzName);
231 	CE_CharToWide((char*)newFileName, swzNewName);
232 	return (MoveFile(swzName, swzNewName) == 0) ? GF_IO_ERR : GF_OK;
233 #elif defined(WIN32)
234 	/* success if != 0 */
235 	return (MoveFile(fileName, newFileName) == 0) ? GF_IO_ERR : GF_OK;
236 #else
237 	GF_Err e = GF_IO_ERR;
238 	char cmd[1024], *arg1, *arg2;
239 	if (!fileName || !newFileName)
240 		return GF_IO_ERR;
241 	arg1 = gf_sanetize_single_quoted_string(fileName);
242 	arg2 = gf_sanetize_single_quoted_string(newFileName);
243 	if (snprintf(cmd, sizeof cmd, "mv %s %s", arg1, arg2) >= sizeof cmd) goto error;
244 
245 #if defined(GPAC_IPHONE) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 80000)
246 	{
247 		pid_t pid;
248 		char *argv[3];
249 		argv[0] = "mv";
250 		argv[1] = cmd;
251 		argv[2] = NULL;
252 		posix_spawn(&pid, argv[0], NULL, NULL, argv, environ);
253 		waitpid(pid, NULL, 0);
254 	}
255 #else
256 	e = (system(cmd) == 0) ? GF_OK : GF_IO_ERR;
257 #endif
258 
259 error:
260 	gf_free(arg1);
261 	gf_free(arg2);
262 	return e;
263 #endif
264 }
265 
266 GF_EXPORT
gf_file_modification_time(const char * filename)267 u64 gf_file_modification_time(const char *filename)
268 {
269 #if defined(_WIN32_WCE)
270 	WCHAR _file[GF_MAX_PATH];
271 	WIN32_FIND_DATA FindData;
272 	HANDLE fh;
273 	ULARGE_INTEGER uli;
274 	ULONGLONG time_ms;
275 	BOOL ret;
276 	CE_CharToWide((char *)filename, _file);
277 	fh = FindFirstFile(_file, &FindData);
278 	if (fh == INVALID_HANDLE_VALUE) return 0;
279 	uli.LowPart = FindData.ftLastWriteTime.dwLowDateTime;
280 	uli.HighPart = FindData.ftLastWriteTime.dwHighDateTime;
281 	ret = FindClose(fh);
282 	if (!ret) {
283 		DWORD err = GetLastError();
284 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_file_modification_time() returned the following error code: %d\n", err));
285 	}
286 	time_ms = uli.QuadPart / 10000;
287 	return time_ms;
288 #elif defined(WIN32) && !defined(__GNUC__)
289 	struct _stat64 sb;
290 	if (_stat64(filename, &sb) != 0) return 0;
291 	return sb.st_mtime;
292 #else
293 	struct stat sb;
294 	if (stat(filename, &sb) != 0) return 0;
295 	return sb.st_mtime;
296 #endif
297 	return 0;
298 }
299 
300 static u32 gpac_file_handles = 0;
301 GF_EXPORT
gf_file_handles_count()302 u32 gf_file_handles_count()
303 {
304 	return gpac_file_handles;
305 }
306 
307 GF_EXPORT
gf_temp_file_new(char ** const fileName)308 FILE *gf_temp_file_new(char ** const fileName)
309 {
310 	FILE *res = NULL;
311 #if defined(_WIN32_WCE)
312 	TCHAR pPath[MAX_PATH + 1];
313 	TCHAR pTemp[MAX_PATH + 1];
314 	if (!GetTempPath(MAX_PATH, pPath)) {
315 		pPath[0] = '.';
316 		pPath[1] = '.';
317 	}
318 	if (GetTempFileName(pPath, TEXT("git"), 0, pTemp))
319 		res = _wfopen(pTemp, TEXT("w+b"));
320 #elif defined(WIN32)
321 	char tmp[MAX_PATH];
322 	res = tmpfile();
323 	if (!res) {
324 		GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[Win32] system failure for tmpfile(): 0x%08x\n", GetLastError()));
325 
326 		/*tmpfile() may fail under vista ...*/
327 		if (GetEnvironmentVariable("TEMP", tmp, MAX_PATH)) {
328 			char tmp2[MAX_PATH], *t_file;
329 			gf_rand_init(GF_FALSE);
330 			sprintf(tmp2, "gpac_%08x_", gf_rand());
331 			t_file = tempnam(tmp, tmp2);
332 			res = gf_fopen(t_file, "w+b");
333 			if (res) {
334 				gpac_file_handles--;
335 				if (fileName) {
336 					*fileName = gf_strdup(t_file);
337 				}
338 				else {
339 					GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[Win32] temporary file %s won't be deleted - contact the GPAC team\n", t_file));
340 				}
341 			}
342 			free(t_file);
343 		}
344 	}
345 #else
346 	res = tmpfile();
347 #endif
348 
349 	if (res) {
350 		gpac_file_handles++;
351 	}
352 	return res;
353 }
354 
355 /*enumerate directories*/
356 GF_EXPORT
gf_enum_directory(const char * dir,Bool enum_directory,gf_enum_dir_item enum_dir_fct,void * cbck,const char * filter)357 GF_Err gf_enum_directory(const char *dir, Bool enum_directory, gf_enum_dir_item enum_dir_fct, void *cbck, const char *filter)
358 {
359 	char item_path[GF_MAX_PATH];
360 	GF_FileEnumInfo file_info;
361 
362 #if defined(_WIN32_WCE)
363 	char _path[GF_MAX_PATH];
364 	unsigned short path[GF_MAX_PATH];
365 	unsigned short w_filter[GF_MAX_PATH];
366 	char file[GF_MAX_PATH];
367 #else
368 	char path[GF_MAX_PATH], *file;
369 #endif
370 
371 #ifdef WIN32
372 	WIN32_FIND_DATA FindData;
373 	HANDLE SearchH;
374 #else
375 	DIR *the_dir;
376 	struct dirent* the_file;
377 	struct stat st;
378 #endif
379 
380 	if (!dir || !enum_dir_fct) return GF_BAD_PARAM;
381 
382 	if (filter && (!strcmp(filter, "*") || !filter[0])) filter = NULL;
383 
384 	memset(&file_info, 0, sizeof(GF_FileEnumInfo));
385 
386 	if (!strcmp(dir, "/")) {
387 #if defined(WIN32) && !defined(_WIN32_WCE)
388 		u32 len;
389 		char *drives, *volume;
390 		len = GetLogicalDriveStrings(0, NULL);
391 		drives = (char*)gf_malloc(sizeof(char)*(len + 1));
392 		drives[0] = 0;
393 		GetLogicalDriveStrings(len, drives);
394 		len = (u32)strlen(drives);
395 		volume = drives;
396 		file_info.directory = GF_TRUE;
397 		file_info.drive = GF_TRUE;
398 		while (len) {
399 			enum_dir_fct(cbck, volume, "", &file_info);
400 			volume += len + 1;
401 			len = (u32)strlen(volume);
402 		}
403 		gf_free(drives);
404 		return GF_OK;
405 #elif defined(__SYMBIAN32__)
406 		RFs iFs;
407 		TDriveList aList;
408 		iFs.Connect();
409 		iFs.DriveList(aList);
410 		for (TInt i = 0; i<KMaxDrives; i++) {
411 			if (aList[i]) {
412 				char szDrive[10];
413 				TChar aDrive;
414 				iFs.DriveToChar(i, aDrive);
415 				sprintf(szDrive, "%c:", (TUint)aDrive);
416 				enum_dir_fct(cbck, szDrive, "", &file_info);
417 			}
418 		}
419 		iFs.Close();
420 		FlushItemList();
421 		return GF_OK;
422 #endif
423 	}
424 
425 
426 #if defined (_WIN32_WCE)
427 	switch (dir[strlen(dir) - 1]) {
428 	case '/':
429 	case '\\':
430 		sprintf(_path, "%s*", dir);
431 		break;
432 	default:
433 		sprintf(_path, "%s%c*", dir, GF_PATH_SEPARATOR);
434 		break;
435 	}
436 	CE_CharToWide(_path, path);
437 	CE_CharToWide((char *)filter, w_filter);
438 #elif defined(WIN32)
439 	switch (dir[strlen(dir) - 1]) {
440 	case '/':
441 	case '\\':
442 		sprintf(path, "%s*", dir);
443 		break;
444 	default:
445 		sprintf(path, "%s%c*", dir, GF_PATH_SEPARATOR);
446 		break;
447 	}
448 #else
449 	strcpy(path, dir);
450 	if (path[strlen(path) - 1] != '/') strcat(path, "/");
451 #endif
452 
453 #ifdef WIN32
454 	SearchH = FindFirstFile(path, &FindData);
455 	if (SearchH == INVALID_HANDLE_VALUE) return GF_IO_ERR;
456 
457 #if defined (_WIN32_WCE)
458 	_path[strlen(_path) - 1] = 0;
459 #else
460 	path[strlen(path) - 1] = 0;
461 #endif
462 
463 	while (SearchH != INVALID_HANDLE_VALUE) {
464 
465 #else
466 
467 	the_dir = opendir(path);
468 	if (the_dir == NULL) {
469 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot open directory %s for enumeration: %d\n", path, errno));
470 		return GF_IO_ERR;
471 	}
472 	the_file = readdir(the_dir);
473 	while (the_file) {
474 
475 #endif
476 
477 		memset(&file_info, 0, sizeof(GF_FileEnumInfo));
478 
479 
480 #if defined (_WIN32_WCE)
481 		if (!wcscmp(FindData.cFileName, _T("."))) goto next;
482 		if (!wcscmp(FindData.cFileName, _T(".."))) goto next;
483 #elif defined(WIN32)
484 		if (!strcmp(FindData.cFileName, ".")) goto next;
485 		if (!strcmp(FindData.cFileName, "..")) goto next;
486 #else
487 		if (!strcmp(the_file->d_name, "..")) goto next;
488 		if (the_file->d_name[0] == '.') goto next;
489 #endif
490 
491 #ifdef WIN32
492 		file_info.directory = (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? GF_TRUE : GF_FALSE;
493 		if (!enum_directory && file_info.directory) goto next;
494 		if (enum_directory && !file_info.directory) goto next;
495 #endif
496 
497 		if (filter) {
498 #if defined (_WIN32_WCE)
499 			short ext[30];
500 			short *sep = wcsrchr(FindData.cFileName, (wchar_t) '.');
501 			if (!sep) goto next;
502 			wcscpy(ext, sep + 1);
503 			wcslwr(ext);
504 			if (!wcsstr(w_filter, ext)) goto next;
505 #elif defined(WIN32)
506 			char ext[30];
507 			char *sep = strrchr(FindData.cFileName, '.');
508 			if (!sep) goto next;
509 			strcpy(ext, sep + 1);
510 			strlwr(ext);
511 			if (!strstr(filter, ext)) goto next;
512 #else
513 			char ext[30];
514 			char *sep = strrchr(the_file->d_name, '.');
515 			if (!sep) goto next;
516 			strcpy(ext, sep + 1);
517 			strlwr(ext);
518 			if (!strstr(filter, sep + 1)) goto next;
519 #endif
520 		}
521 
522 #if defined(WIN32)
523 		file_info.hidden = (FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? GF_TRUE : GF_FALSE;
524 		file_info.system = (FindData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) ? GF_TRUE : GF_FALSE;
525 		file_info.size = MAXDWORD;
526 		file_info.size += 1;
527 		file_info.size *= FindData.nFileSizeHigh;
528 		file_info.size += FindData.nFileSizeLow;
529 		file_info.last_modified = (u64)((*(LONGLONG *)&FindData.ftLastWriteTime - TIMESPEC_TO_FILETIME_OFFSET) / 10000000);
530 #endif
531 
532 #if defined (_WIN32_WCE)
533 		CE_WideToChar(FindData.cFileName, file);
534 		strcpy(item_path, _path);
535 		strcat(item_path, file);
536 #elif defined(WIN32)
537 		strcpy(item_path, path);
538 		strcat(item_path, FindData.cFileName);
539 		file = FindData.cFileName;
540 #else
541 		strcpy(item_path, path);
542 		strcat(item_path, the_file->d_name);
543 		GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Checking file %s for enum\n", item_path));
544 
545 		if (stat(item_path, &st) != 0) goto next;
546 
547 		file_info.directory = ((st.st_mode & S_IFMT) == S_IFDIR) ? GF_TRUE : GF_FALSE;
548 		if (enum_directory && !file_info.directory) goto next;
549 		if (!enum_directory && file_info.directory) goto next;
550 
551 		file_info.size = st.st_size;
552 
553 		{
554 			struct tm _t = *gmtime(&st.st_mtime);
555 			file_info.last_modified = mktime(&_t);
556 		}
557 		file = the_file->d_name;
558 		if (file && file[0] == '.') file_info.hidden = 1;
559 
560 		if (file_info.directory) {
561 			char * parent_name = strrchr(item_path, '/');
562 			if (!parent_name) {
563 				file_info.drive = GF_TRUE;
564 			}
565 			else {
566 				struct stat st_parent;
567 				parent_name[0] = 0;
568 				if (stat(item_path, &st_parent) == 0) {
569 					if ((st.st_dev != st_parent.st_dev) || ((st.st_dev == st_parent.st_dev) && (st.st_ino == st_parent.st_ino))) {
570 						file_info.drive = GF_TRUE;
571 					}
572 				}
573 				parent_name[0] = '/';
574 			}
575 		}
576 #endif
577 		if (enum_dir_fct(cbck, file, item_path, &file_info)) {
578 #ifdef WIN32
579 			BOOL ret = FindClose(SearchH);
580 			if (!ret) {
581 				DWORD err = GetLastError();
582 				GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_enum_directory() returned(1) the following error code: %d\n", err));
583 			}
584 #endif
585 			break;
586 		}
587 
588 	next:
589 #ifdef WIN32
590 		if (!FindNextFile(SearchH, &FindData)) {
591 			BOOL ret = FindClose(SearchH);
592 			if (!ret) {
593 				DWORD err = GetLastError();
594 				GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_enum_directory() returned(2) the following error code: %d\n", err));
595 			}
596 			break;
597 		}
598 #else
599 		the_file = readdir(the_dir);
600 #endif
601 	}
602 #ifndef WIN32
603 	closedir(the_dir);
604 #endif
605 	return GF_OK;
606 	}
607 
608 GF_EXPORT
609 u64 gf_ftell(FILE *fp)
610 {
611 #if defined(_WIN32_WCE)
612 	return (u64)ftell(fp);
613 #elif defined(GPAC_CONFIG_WIN32) && !defined(__CYGWIN__)	/* mingw or cygwin */
614 #if (_FILE_OFFSET_BITS >= 64)
615 	return (u64)ftello64(fp);
616 #else
617 	return (u64)ftell(fp);
618 #endif
619 #elif defined(WIN32)
620 	return (u64)_ftelli64(fp);
621 #elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_ANDROID)
622 	return (u64)ftello64(fp);
623 #elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
624 	return (u64)ftello(fp);
625 #else
626 	return (u64)ftell(fp);
627 #endif
628 }
629 
630 GF_EXPORT
631 u64 gf_fseek(FILE *fp, s64 offset, s32 whence)
632 {
633 #if defined(_WIN32_WCE)
634 	return (u64)fseek(fp, (s32)offset, whence);
635 #elif defined(GPAC_CONFIG_WIN32) && !defined(__CYGWIN__)	/* mingw or cygwin */
636 #if (_FILE_OFFSET_BITS >= 64)
637 	return (u64)fseeko64(fp, offset, whence);
638 #else
639 	return (u64)fseek(fp, (s32)offset, whence);
640 #endif
641 #elif defined(WIN32)
642 	return (u64)_fseeki64(fp, offset, whence);
643 #elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_ANDROID)
644 	return fseeko64(fp, (off64_t)offset, whence);
645 #elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
646 	return fseeko(fp, (off_t)offset, whence);
647 #else
648 	return fseek(fp, (s32)offset, whence);
649 #endif
650 }
651 
652 GF_EXPORT
653 FILE *gf_fopen(const char *file_name, const char *mode)
654 {
655 	FILE *res = NULL;
656 
657 #if defined(WIN32)
658 	Bool is_create;
659 	is_create = (strchr(mode, 'w') == NULL) ? GF_FALSE : GF_TRUE;
660 	if (!is_create) {
661 		if (strchr(mode, 'a')) {
662 			res = fopen(file_name, "rb");
663 			if (res) {
664 				fclose(res);
665 				res = fopen(file_name, mode);
666 			}
667 		}
668 		else {
669 			res = fopen(file_name, mode);
670 		}
671 	}
672 	if (!res) {
673 		const char *str_src;
674 		wchar_t *wname;
675 		wchar_t *wmode;
676 		size_t len;
677 		size_t len_res;
678 		if (!is_create) {
679 			GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[Core] Could not open file %s mode %s in UTF-8 mode, trying UTF-16\n", file_name, mode));
680 		}
681 		len = (strlen(file_name) + 1) * sizeof(wchar_t);
682 		wname = (wchar_t *)gf_malloc(len);
683 		str_src = file_name;
684 		len_res = gf_utf8_mbstowcs(wname, len, &str_src);
685 		if (len_res == -1) {
686 			return NULL;
687 		}
688 		len = (strlen(mode) + 1) * sizeof(wchar_t);
689 		wmode = (wchar_t *)gf_malloc(len);
690 		str_src = mode;
691 		len_res = gf_utf8_mbstowcs(wmode, len, &str_src);
692 		if (len_res == -1) {
693 			return NULL;
694 		}
695 
696 		res = _wfsopen(wname, wmode, _SH_DENYNO);
697 		gf_free(wname);
698 		gf_free(wmode);
699 	}
700 #elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_ANDROID)
701 	res = fopen64(file_name, mode);
702 #elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
703 	res = fopen(file_name, mode);
704 #else
705 	res = fopen(file_name, mode);
706 #endif
707 
708 	if (res) {
709 		gpac_file_handles++;
710 		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));
711 	}
712 	else {
713 		if (strchr(mode, 'w') || strchr(mode, 'a')) {
714 #if defined(WIN32)
715 			u32 err = GetLastError();
716 			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));
717 #else
718 			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));
719 #endif
720 		}
721 	}
722 	return res;
723 }
724 
725 GF_EXPORT
726 s32 gf_fclose(FILE *file)
727 {
728 	if (file) {
729 		assert(gpac_file_handles);
730 		gpac_file_handles--;
731 	}
732 	return fclose(file);
733 }
734 
735 #if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! defined(_GNU_SOURCE) && !defined(WIN32)
736 #define HAVE_STRERROR_R 1
737 #endif
738 
739 GF_EXPORT
740 size_t gf_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) {
741 	return fread(ptr, size, nmemb, stream);
742 }
743 
744 GF_EXPORT
745 size_t gf_fwrite(const void *ptr, size_t size, size_t nmemb,
746 	FILE *stream)
747 {
748 	size_t result = fwrite(ptr, size, nmemb, stream);
749 	if (result != nmemb) {
750 #ifdef _WIN32_WCE
751 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Error writing data: %d blocks to write but %d blocks written\n", nmemb, result));
752 #else
753 #if defined WIN32 && !defined(GPAC_CONFIG_WIN32)
754 		errno_t errno_save;
755 		_get_errno(&errno_save);
756 #else
757 		int errno_save = errno;
758 #endif
759 		//if (errno_save!=0)
760 		{
761 #ifdef HAVE_STRERROR_R
762 #define ERRSTR_BUF_SIZE 256
763 			char errstr[ERRSTR_BUF_SIZE];
764 			if (strerror_r(errno_save, errstr, ERRSTR_BUF_SIZE) != 0)
765 			{
766 				strerror_r(0, errstr, ERRSTR_BUF_SIZE);
767 			}
768 #else
769 			char *errstr = (char*)strerror(errno_save);
770 #endif
771 			GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Error writing data (%s): %d blocks to write but %d blocks written\n", errstr, nmemb, result));
772 		}
773 #endif
774 	}
775 	return result;
776 }
777 
778