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