Coverage Report

Created: 2026-03-10 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/utils/os_file.c
Line
Count
Source
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre - Copyright (c) Telecom ParisTech 2000-2026
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
57
GF_Err gf_rmdir(const char *DirPathName)
58
0
{
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
0
  int res = rmdir(DirPathName);
82
0
  if (res==-1) {
83
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete directory \"%s\": last error %d\n", DirPathName, errno  ));
84
0
    return GF_IO_ERR;
85
0
  }
86
0
#endif
87
0
  return GF_OK;
88
0
}
89
90
GF_EXPORT
91
GF_Err gf_mkdir(const char* DirPathName)
92
2
{
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
105
  //don't try creation for UNC root:  \\foo\bar\name will trigger creation for "", "\\foo" and "\\foo\bar", ignore these
106
  if (!strcmp(DirPathName, "")) return GF_OK;
107
  if (!strncmp(DirPathName, "\\\\", 2)) {
108
    char *sep = strchr(DirPathName + 2, '\\');
109
    if (!sep) return GF_OK;
110
    sep = strchr(sep + 1, '\\');
111
    if (!sep) return GF_OK;
112
  }
113
114
  wchar_t* wcsDirPathName = gf_utf8_to_wcs(DirPathName);
115
  if (!wcsDirPathName)
116
    return GF_IO_ERR;
117
  res = _wmkdir(wcsDirPathName);
118
  gf_free(wcsDirPathName);
119
  if (res==-1) {
120
    int err = GetLastError();
121
    if (err != 183) {
122
      // don't throw error if dir already exists
123
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create directory \"%s\": last error %d\n", DirPathName, err));
124
    }
125
  }
126
#else
127
2
  int res = mkdir(DirPathName, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
128
2
  if (res==-1) {
129
0
    if(errno == 17) {
130
0
      GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("Cannot create directory \"%s\", it already exists: last error %d \n", DirPathName, errno ));
131
0
      return GF_OK;
132
0
    } else {
133
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create directory \"%s\": last error %d\n", DirPathName, errno ));
134
0
      return GF_IO_ERR;
135
0
    }
136
0
  }
137
2
#endif
138
2
  return GF_OK;
139
2
}
140
141
142
GF_EXPORT
143
Bool gf_dir_exists(const char* DirPathName)
144
120k
{
145
#if defined (_WIN32_WCE)
146
  TCHAR swzName[MAX_PATH];
147
  DWORD att;
148
  CE_CharToWide(DirPathName, swzName);
149
  att = GetFileAttributes(swzName);
150
  return (att != INVALID_FILE_ATTRIBUTES && (att & FILE_ATTRIBUTE_DIRECTORY)) ? GF_TRUE : GF_FALSE;
151
#elif defined (WIN32)
152
  DWORD att;
153
  wchar_t* wcsDirPathName = gf_utf8_to_wcs(DirPathName);
154
  if (!wcsDirPathName)
155
    return GF_FALSE;
156
  att = GetFileAttributesW(wcsDirPathName);
157
  gf_free(wcsDirPathName);
158
  return (att != INVALID_FILE_ATTRIBUTES && (att & FILE_ATTRIBUTE_DIRECTORY)) ? GF_TRUE : GF_FALSE;
159
#else
160
120k
  struct stat sb;
161
120k
  if (stat(DirPathName, &sb) == 0 && S_ISDIR(sb.st_mode)) {
162
21.7k
    return GF_TRUE;
163
21.7k
  }
164
98.5k
  return GF_FALSE;
165
//  DIR* dir = opendir(DirPathName);
166
//  if (!dir) return GF_FALSE;
167
//  closedir(dir);
168
//  return GF_TRUE;
169
0
#endif
170
0
  return GF_FALSE;
171
120k
}
172
static Bool delete_dir(void *cbck, char *item_name, char *item_path, GF_FileEnumInfo *file_info)
173
0
{
174
0
  Bool directory_clean_mode = *(Bool*)cbck;
175
176
0
  if(directory_clean_mode) {
177
0
    gf_dir_cleanup(item_path);
178
0
    gf_rmdir(item_path);
179
0
  } else {
180
0
    gf_file_delete(item_path);
181
0
  }
182
0
  return GF_FALSE;
183
0
}
184
185
GF_EXPORT
186
GF_Err gf_dir_cleanup(const char* DirPathName)
187
0
{
188
0
  Bool directory_clean_mode;
189
190
0
  directory_clean_mode = GF_TRUE;
191
0
  gf_enum_directory(DirPathName, GF_TRUE, delete_dir, &directory_clean_mode, NULL);
192
0
  directory_clean_mode = GF_FALSE;
193
0
  gf_enum_directory(DirPathName, GF_FALSE, delete_dir, &directory_clean_mode, NULL);
194
195
0
  return GF_OK;
196
0
}
197
198
#include <gpac/list.h>
199
GF_List *gfio_delete_handlers = NULL;
200
201
GF_EXPORT
202
GF_Err gf_fileio_register_delete_proc(gfio_delete_proc del_proc)
203
0
{
204
0
  if (!del_proc) return GF_OK;
205
206
0
  if (!gfio_delete_handlers) gfio_delete_handlers = gf_list_new();
207
0
  if (!gfio_delete_handlers) return GF_OUT_OF_MEM;
208
0
  if (gf_list_find(gfio_delete_handlers, del_proc)>=0) return GF_BAD_PARAM;
209
0
  return gf_list_add(gfio_delete_handlers, del_proc);
210
0
}
211
GF_EXPORT
212
void gf_fileio_unregister_delete_proc(gfio_delete_proc del_proc)
213
0
{
214
0
  if (!del_proc || !gfio_delete_handlers) return;
215
0
  gf_list_del_item(gfio_delete_handlers, del_proc);
216
0
  if (!gf_list_count(gfio_delete_handlers)) {
217
0
    gf_list_del(gfio_delete_handlers);
218
0
    gfio_delete_handlers = NULL;
219
0
  }
220
0
}
221
222
GF_Err gf_fileio_file_delete(const char *fileName, const char *parent_gfio)
223
0
{
224
0
  if (!gfio_delete_handlers || !fileName) return GF_OK;
225
0
  if (!parent_gfio) {
226
0
    if (strncmp(fileName, "gfio://", 7)) return GF_EOS;
227
0
  } else {
228
0
    if (strncmp(parent_gfio, "gfio://", 7)) return GF_EOS;
229
0
  }
230
231
0
  u32 i, count=gf_list_count(gfio_delete_handlers);
232
0
  for (i=0; i<count; i++) {
233
0
    gfio_delete_proc del_proc = gf_list_get(gfio_delete_handlers, i);
234
0
    GF_Err ret = del_proc(fileName, parent_gfio);
235
0
    if (ret==GF_EOS) continue;
236
0
    return ret;
237
0
  }
238
0
  return GF_OK;
239
0
}
240
241
GF_EXPORT
242
GF_Err gf_file_delete(const char *fileName)
243
4.85k
{
244
4.85k
  if (!fileName || !fileName[0]) {
245
0
    GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("gf_file_delete with no param - ignoring\n"));
246
0
    return GF_OK;
247
0
  }
248
4.85k
  if (!strncmp(fileName, "gfio://", 7)) {
249
0
    return gf_fileio_file_delete(fileName, NULL);
250
0
  }
251
252
#if defined(_WIN32_WCE)
253
  TCHAR swzName[MAX_PATH];
254
  CE_CharToWide((char*)fileName, swzName);
255
  return (DeleteFile(swzName)==0) ? GF_IO_ERR : GF_OK;
256
#elif defined(WIN32)
257
  /* success if != 0 */
258
  {
259
    BOOL op_result;
260
    wchar_t* wcsFileName = gf_utf8_to_wcs(fileName);
261
    if (!wcsFileName)
262
      return GF_IO_ERR;
263
    op_result = DeleteFileW(wcsFileName);
264
    gf_free(wcsFileName);
265
    return (op_result==0) ? GF_IO_ERR : GF_OK;
266
  }
267
#else
268
  /* success is == 0 */
269
4.85k
  return ( remove(fileName) == 0) ? GF_OK : GF_IO_ERR;
270
4.85k
#endif
271
4.85k
}
272
273
#ifndef WIN32
274
/**
275
 * Remove existing single-quote from a single-quoted string.
276
 * The caller is responsible for deallocating the returns string with gf_free()
277
 */
278
0
static char* gf_sanetize_single_quoted_string(const char *src) {
279
0
  int i, j;
280
0
  char *out = (char*)gf_malloc(4*strlen(src)+3);
281
0
  out[0] = '\'';
282
0
  for (i=0, j=1; (out[j]=src[i]); ++i, ++j) {
283
0
    if (src[i]=='\'') {
284
0
      out[++j]='\\';
285
0
      out[++j]='\'';
286
0
      out[++j]='\'';
287
0
    }
288
0
  }
289
0
  out[j++] = '\'';
290
0
  out[j++] = 0;
291
0
  return out;
292
0
}
293
#endif
294
295
#if defined(GPAC_CONFIG_IOS) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 80000)
296
#include <spawn.h>
297
extern char **environ;
298
#endif
299
300
#if defined(WIN32)
301
#include <io.h>
302
#endif
303
304
GF_EXPORT
305
Bool gf_file_exists_ex(const char *fileName, const char *par_name)
306
0
{
307
0
  u32 gfio_type = 0;
308
0
  if (!fileName) return GF_FALSE;
309
310
0
  if (!strncmp(fileName, "gfio://", 7))
311
0
    gfio_type = 1;
312
0
  else if (par_name && !strncmp(par_name, "gfio://", 7))
313
0
    gfio_type = 2;
314
315
0
  if (gfio_type) {
316
0
    GF_FileIO *gfio_ref;
317
0
    GF_Err e;
318
0
    Bool res = GF_TRUE;
319
0
    if (gfio_type==1)
320
0
      gfio_ref = gf_fileio_from_url(fileName);
321
0
    else
322
0
      gfio_ref = gf_fileio_from_url(par_name);
323
324
0
    if (!gfio_ref) return GF_FALSE;
325
0
    gf_fileio_open_url(gfio_ref, (gfio_type==1) ? gf_fileio_resource_url(gfio_ref) : fileName, "probe", &e);
326
0
    if (e==GF_URL_ERROR)
327
0
      res = GF_FALSE;
328
0
    return res;
329
0
  }
330
331
#if defined(WIN32)
332
  Bool res;
333
  wchar_t* wname = gf_utf8_to_wcs(fileName);
334
  if (!wname) return GF_FALSE;
335
  res = (_waccess(wname, 4) == -1) ? GF_FALSE : GF_TRUE;
336
  if (res == GF_TRUE) {
337
    DWORD att;
338
    att = GetFileAttributesW(wname);
339
    if (att != INVALID_FILE_ATTRIBUTES && (att & FILE_ATTRIBUTE_DIRECTORY))
340
      res = GF_FALSE;
341
  }
342
  gf_free(wname);
343
  return res;
344
#elif defined(GPAC_CONFIG_LINUX) || defined(GPAC_CONFIG_EMSCRIPTEN)
345
0
  Bool res = (access(fileName, 4) == -1) ? GF_FALSE : GF_TRUE;
346
0
  if (res && gf_dir_exists(fileName))
347
0
    res = GF_FALSE;
348
0
  return res;
349
#elif defined(__DARWIN__) || defined(__APPLE__)
350
  Bool res = (access(fileName, 4) == -1) ? GF_FALSE : GF_TRUE;
351
  if (res && gf_dir_exists(fileName))
352
    res = GF_FALSE;
353
  return res;
354
#elif defined(GPAC_CONFIG_IOS) || defined(GPAC_CONFIG_ANDROID)
355
  Bool res = (access(fileName, 4) == -1) ? GF_FALSE : GF_TRUE;
356
  if (res && gf_dir_exists(fileName))
357
    res = GF_FALSE;
358
  return res;
359
#else
360
  FILE *f = gf_fopen(fileName, "r");
361
  if (f) {
362
    gf_fclose(f);
363
    return GF_TRUE;
364
  }
365
  return GF_FALSE;
366
#endif
367
0
}
368
369
GF_EXPORT
370
Bool gf_file_exists(const char *fileName)
371
0
{
372
0
  return gf_file_exists_ex(fileName, NULL);
373
0
}
374
375
GF_EXPORT
376
GF_Err gf_file_move(const char *fileName, const char *newFileName)
377
0
{
378
#if defined(_WIN32_WCE)
379
  GF_Err e = GF_OK;
380
  TCHAR swzName[MAX_PATH];
381
  TCHAR swzNewName[MAX_PATH];
382
  CE_CharToWide((char*)fileName, swzName);
383
  CE_CharToWide((char*)newFileName, swzNewName);
384
  if (MoveFile(swzName, swzNewName) == 0 )
385
    e = GF_IO_ERR;
386
#elif defined(WIN32)
387
  GF_Err e = GF_OK;
388
  /* success if != 0 */
389
  BOOL op_result;
390
  wchar_t* wcsFileName = gf_utf8_to_wcs(fileName);
391
  wchar_t* wcsNewFileName = gf_utf8_to_wcs(newFileName);
392
  if (!wcsFileName || !wcsNewFileName) {
393
    if (wcsFileName) gf_free(wcsFileName);
394
    if (wcsNewFileName) gf_free(wcsNewFileName);
395
    e = GF_IO_ERR;
396
  } else {
397
    op_result = MoveFileW(wcsFileName, wcsNewFileName);
398
    gf_free(wcsFileName);
399
    gf_free(wcsNewFileName);
400
    if ( op_result == 0 )
401
      e = GF_IO_ERR;
402
  }
403
#else
404
0
  GF_Err e = GF_IO_ERR;
405
0
  char cmd[1024], *arg1, *arg2;
406
0
  if (!fileName || !newFileName) {
407
0
    e = GF_IO_ERR;
408
0
  } else {
409
    //try direct rename - will fail if moving across file systems but faster for most use cases
410
0
    int res = rename(fileName, newFileName);
411
0
    if (!res) return GF_OK;
412
    //use mv command
413
0
    arg1 = gf_sanetize_single_quoted_string(fileName);
414
0
    arg2 = gf_sanetize_single_quoted_string(newFileName);
415
0
    if (snprintf(cmd, sizeof cmd, "mv %s %s", arg1, arg2) >= sizeof cmd) goto error;
416
417
#if defined(GPAC_CONFIG_IOS) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 80000)
418
    {
419
      pid_t pid;
420
      char *argv[3];
421
      argv[0] = "mv";
422
      argv[1] = cmd;
423
      argv[2] = NULL;
424
      posix_spawn(&pid, argv[0], NULL, NULL, argv, environ);
425
      waitpid(pid, NULL, 0);
426
    }
427
#else
428
0
    e = (system(cmd) == 0) ? GF_OK : GF_IO_ERR;
429
0
#endif
430
431
0
error:
432
0
    gf_free(arg1);
433
0
    gf_free(arg2);
434
0
  }
435
0
#endif
436
437
0
  if (e) {
438
0
    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) ));
439
0
  }
440
0
  return e;
441
0
}
442
443
GF_EXPORT
444
u64 gf_file_modification_time(const char *filename)
445
0
{
446
#if defined(_WIN32_WCE)
447
  WCHAR _file[GF_MAX_PATH];
448
  WIN32_FIND_DATA FindData;
449
  HANDLE fh;
450
//  ULARGE_INTEGER uli;
451
  ULONGLONG time_us;
452
  BOOL ret;
453
  CE_CharToWide((char *) filename, _file);
454
  fh = FindFirstFile(_file, &FindData);
455
  if (fh == INVALID_HANDLE_VALUE) return 0;
456
//  uli.LowPart = FindData.ftLastWriteTime.dwLowDateTime;
457
//  uli.HighPart = FindData.ftLastWriteTime.dwHighDateTime;
458
  time_us = (u64) ((*(LONGLONG *) &FindData.ftLastWriteTime - TIMESPEC_TO_FILETIME_OFFSET) / 10);
459
  ret = FindClose(fh);
460
  if (!ret) {
461
    DWORD err = GetLastError();
462
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_file_modification_time() error: %d\n", err));
463
  }
464
//  time_ms = uli.QuadPart/10000;
465
  return time_us;
466
#elif defined(WIN32) && !defined(__GNUC__)
467
  struct _stat64 st;
468
  int op_result;
469
  wchar_t* wcsFilename = gf_utf8_to_wcs(filename);
470
  if (!wcsFilename)
471
    return 0;
472
  op_result = _wstat64(wcsFilename, &st);
473
  gf_free(wcsFilename);
474
  if (op_result != 0) return 0;
475
  u64 time_us = st.st_mtime * 1000000;
476
#if defined(GPAC_HAS_MTIM_NSEC)
477
  time_us += st.st_mtim.tv_nsec / 1000;
478
#endif
479
  return time_us;
480
#else
481
0
  struct stat st;
482
0
  if (stat(filename, &st) != 0) return 0;
483
0
  u64 time_us = st.st_mtime * 1000000;
484
485
#if defined(__DARWIN__) || defined(__APPLE__) || defined(GPAC_CONFIG_IOS)
486
  time_us += st.st_mtimespec.tv_nsec / 1000;
487
#elif defined(GPAC_HAS_MTIM_NSEC)
488
  time_us += st.st_mtim.tv_nsec / 1000;
489
#elif defined(GPAC_CONFIG_ANDROID)
490
  time_us += st.st_mtime_nsec / 1000;
491
#endif
492
493
0
  return time_us;
494
0
#endif
495
0
  return 0;
496
0
}
497
498
#include <gpac/list.h>
499
extern int gf_mem_track_enabled;
500
static  GF_List * gpac_open_files = NULL;
501
502
typedef struct
503
{
504
  FILE *ptr;
505
  char *url;
506
  Bool is_temp;
507
} GF_FileHandle;
508
509
static u32 gpac_file_handles = 0;
510
511
512
513
GF_EXPORT
514
u32 gf_file_handles_count()
515
0
{
516
#ifdef GPAC_MEMORY_TRACKING
517
  if (gpac_open_files) {
518
    return gf_list_count(gpac_open_files);
519
  }
520
#endif
521
0
  return gpac_file_handles;
522
0
}
523
524
#include <gpac/thread.h>
525
extern GF_Mutex *logs_mx;
526
527
#ifdef GPAC_MEMORY_TRACKING
528
const char *enum_open_handles(u32 *idx)
529
{
530
  GF_FileHandle *h;
531
  gf_mx_p(logs_mx);
532
  u32 count = gf_list_count(gpac_open_files);
533
  if (*idx >= count) {
534
    gf_mx_v(logs_mx);
535
    return NULL;
536
  }
537
  h = gf_list_get(gpac_open_files, *idx);
538
  (*idx)++;
539
  gf_mx_v(logs_mx);
540
  return h->url;
541
}
542
#endif
543
544
static void gf_register_file_handle(char *filename, FILE *ptr, Bool is_temp_file)
545
8.06k
{
546
8.06k
  if (is_temp_file
547
#ifdef GPAC_MEMORY_TRACKING
548
    || gf_mem_track_enabled
549
#endif
550
8.06k
  ) {
551
0
    GF_FileHandle *h;
552
0
    gf_mx_p(logs_mx);
553
0
    if (!gpac_open_files) gpac_open_files = gf_list_new();
554
0
    GF_SAFEALLOC(h, GF_FileHandle);
555
0
    if (h) {
556
0
      h->ptr = ptr;
557
0
      if (is_temp_file) {
558
0
        h->is_temp = GF_TRUE;
559
0
        h->url = filename;
560
0
      } else {
561
0
        h->url = gf_strdup(filename);
562
0
      }
563
0
      gf_list_add(gpac_open_files, h);
564
0
    }
565
0
    gf_mx_v(logs_mx);
566
0
  }
567
8.06k
  gpac_file_handles++;
568
8.06k
}
569
570
static Bool gf_unregister_file_handle(FILE *ptr)
571
8.06k
{
572
8.06k
  u32 i, count;
573
8.06k
  Bool res = GF_FALSE;
574
8.06k
  if (gpac_file_handles)
575
8.06k
    gpac_file_handles--;
576
577
8.06k
  if (!gpac_open_files)
578
8.06k
    return GF_FALSE;
579
580
0
  gf_mx_p(logs_mx);
581
0
  count = gf_list_count(gpac_open_files);
582
0
  for (i=0; i<count; i++) {
583
0
    GF_FileHandle *h = gf_list_get(gpac_open_files, i);
584
0
    if (h->ptr != ptr) continue;
585
586
0
    if (h->is_temp) {
587
0
      GF_Err e;
588
0
      fclose(h->ptr);
589
0
      e = gf_file_delete(h->url);
590
0
      if (e) {
591
0
        GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[Core] Failed to delete temp file %s: %s\n", h->url, gf_error_to_string(e)));
592
0
      }
593
0
      res = GF_TRUE;
594
0
    }
595
0
    gf_free(h->url);
596
0
    gf_free(h);
597
0
    gf_list_rem(gpac_open_files, i);
598
0
    if (!gf_list_count(gpac_open_files)) {
599
0
      gf_list_del(gpac_open_files);
600
0
      gpac_open_files = NULL;
601
0
    }
602
0
    break;
603
0
  }
604
0
  gf_mx_v(logs_mx);
605
0
  return res;
606
8.06k
}
607
608
static FILE *gf_file_temp_os(char ** const fileName)
609
0
{
610
0
  FILE *res;
611
#if defined(_WIN32_WCE)
612
  TCHAR pPath[MAX_PATH+1];
613
  TCHAR pTemp[MAX_PATH+1];
614
  res = NULL;
615
  if (!GetTempPath(MAX_PATH, pPath)) {
616
    pPath[0] = '.';
617
    pPath[1] = '.';
618
  }
619
  if (GetTempFileName(pPath, TEXT("git"), 0, pTemp))
620
    res = _wfopen(pTemp, TEXT("w+b"));
621
#elif defined(WIN32)
622
  res = tmpfile();
623
624
  if (!res) {
625
    wchar_t tmp[MAX_PATH];
626
627
    GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[Win32] system failure for tmpfile(): 0x%08x\n", GetLastError()));
628
629
    /*tmpfile() may fail under vista ...*/
630
    if (GetEnvironmentVariableW(L"TEMP", tmp, MAX_PATH)) {
631
      wchar_t tmp2[MAX_PATH], *t_file;
632
      char* mbs_t_file;
633
      gf_rand_init(GF_FALSE);
634
      swprintf(tmp2, MAX_PATH, L"gpac_%d_%08x_", _getpid(), gf_rand());
635
      t_file = _wtempnam(tmp, tmp2);
636
      mbs_t_file = gf_wcs_to_utf8(t_file);
637
      if (!mbs_t_file)
638
        return 0;
639
      res = gf_fopen(mbs_t_file, "w+b");
640
      if (res) {
641
        gf_unregister_file_handle(res);
642
        if (fileName) {
643
          *fileName = gf_strdup(mbs_t_file);
644
        } else {
645
          GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[Win32] temporary file \"%s\" won't be deleted - contact the GPAC team\n", mbs_t_file));
646
        }
647
      }
648
      gf_free(mbs_t_file);
649
      free(t_file);
650
    }
651
  }
652
#else
653
0
  res = tmpfile();
654
0
#endif
655
656
0
  if (res) {
657
0
    gf_register_file_handle("temp file", res, GF_FALSE);
658
0
  }
659
0
  return res;
660
0
}
661
662
GF_EXPORT
663
FILE *gf_file_temp(char ** const fileName)
664
0
{
665
0
  const char *tmp_dir = gf_opts_get_key("core", "tmp");
666
667
0
  if (tmp_dir && tmp_dir[0]) {
668
0
    FILE *res;
669
0
    char *opath=NULL, szFName[100];
670
0
    u32 len = (u32) strlen(tmp_dir);
671
672
0
    gf_dynstrcat(&opath, tmp_dir, NULL);
673
0
    if (!strchr("/\\", tmp_dir[len-1]))
674
0
      gf_dynstrcat(&opath, "/", NULL);
675
0
    if (!opath) return NULL;
676
677
0
    sprintf(szFName, "_libgpac_%d_%p_" LLU "_%d", gf_sys_get_process_id(), opath, gf_sys_clock_high_res(), gf_rand() );
678
0
    gf_dynstrcat(&opath, szFName, NULL);
679
0
    if (fileName) {
680
0
      sprintf(szFName, "%p", fileName);
681
0
      gf_dynstrcat(&opath, szFName, "_");
682
0
    }
683
684
0
    if (gf_file_exists(opath)) {
685
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Something went wrong creating temp file path %s, file already exists !\n", opath));
686
0
      gf_free(opath);
687
0
      return NULL;
688
0
    }
689
0
    GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Opening new temp file %s\n", opath));
690
0
    res = gf_fopen_ex(opath, "__temp_file", "w+b", GF_FALSE);
691
0
    if (!res) {
692
0
      gf_free(opath);
693
0
      return NULL;
694
0
    }
695
0
    gf_register_file_handle(opath, res, GF_TRUE);
696
0
    return res;
697
0
  }
698
0
  return gf_file_temp_os(fileName);
699
0
}
700
701
702
/*enumerate directories*/
703
GF_EXPORT
704
GF_Err gf_enum_directory(const char *dir, Bool enum_directory, gf_enum_dir_item enum_dir_fct, void *cbck, const char *filter)
705
4.85k
{
706
#ifdef WIN32
707
  wchar_t item_path[GF_MAX_PATH];
708
#else
709
4.85k
  char item_path[GF_MAX_PATH];
710
4.85k
#endif
711
4.85k
  GF_FileEnumInfo file_info;
712
713
#if defined(_WIN32_WCE)
714
  char _path[GF_MAX_PATH];
715
  unsigned short path[GF_MAX_PATH];
716
  unsigned short w_filter[GF_MAX_PATH];
717
  char file[GF_MAX_PATH];
718
#elif defined(WIN32)
719
  wchar_t path[GF_MAX_PATH], *file;
720
  wchar_t w_filter[GF_MAX_PATH];
721
  char *mbs_file, *mbs_item_path;
722
  char _path[GF_MAX_PATH];
723
  const char* tmpdir;
724
#else
725
4.85k
  char path[GF_MAX_PATH], *file;
726
4.85k
#endif
727
728
#ifdef WIN32
729
  WIN32_FIND_DATAW FindData;
730
  HANDLE SearchH;
731
#else
732
4.85k
  DIR *the_dir;
733
4.85k
  struct dirent* the_file;
734
4.85k
  struct stat st;
735
4.85k
#endif
736
737
4.85k
  if (!dir || !strlen(dir) || !enum_dir_fct) return GF_BAD_PARAM;
738
739
4.85k
  if (filter && (!strcmp(filter, "*") || !filter[0])) filter=NULL;
740
741
4.85k
  memset(&file_info, 0, sizeof(GF_FileEnumInfo) );
742
743
4.85k
  if (!strcmp(dir, "/")) {
744
#if defined(WIN32) && !defined(_WIN32_WCE)
745
    u32 len;
746
    char *drives, *volume;
747
    len = GetLogicalDriveStrings(0, NULL);
748
    drives = (char*)gf_malloc(sizeof(char)*(len+1));
749
    drives[0]=0;
750
    GetLogicalDriveStrings(len, drives);
751
    len = (u32) strlen(drives);
752
    volume = drives;
753
    file_info.directory = GF_TRUE;
754
    file_info.drive = GF_TRUE;
755
    while (len) {
756
      enum_dir_fct(cbck, volume, "", &file_info);
757
      volume += len+1;
758
      len = (u32) strlen(volume);
759
    }
760
    gf_free(drives);
761
    return GF_OK;
762
#elif defined(__SYMBIAN32__)
763
    RFs iFs;
764
    TDriveList aList;
765
    iFs.Connect();
766
    iFs.DriveList(aList);
767
    for (TInt i=0; i<KMaxDrives; i++) {
768
      if (aList[i]) {
769
        char szDrive[10];
770
        TChar aDrive;
771
        iFs.DriveToChar(i, aDrive);
772
        sprintf(szDrive, "%c:", (TUint)aDrive);
773
        enum_dir_fct(cbck, szDrive, "", &file_info);
774
      }
775
    }
776
    iFs.Close();
777
    FlushItemList();
778
    return GF_OK;
779
#elif defined(GPAC_CONFIG_ANDROID) || defined(GPAC_CONFIG_IOS)
780
    dir = (char *) gf_opts_get_key("core", "docs-dir");
781
#endif
782
0
  }
783
784
785
#if defined (_WIN32_WCE)
786
  switch (dir[strlen(dir) - 1]) {
787
  case '/':
788
  case '\\':
789
    sprintf(_path, "%s*", dir);
790
    break;
791
  default:
792
    sprintf(_path, "%s%c*", dir, GF_PATH_SEPARATOR);
793
    break;
794
  }
795
  CE_CharToWide(_path, path);
796
  CE_CharToWide((char *)filter, w_filter);
797
#elif defined(WIN32)
798
799
  strcpy(_path, dir);
800
  switch (dir[strlen(dir)] - 1) {
801
  case '/':
802
  case '\\':
803
    snprintf(_path, MAX_PATH, "%s*", dir);
804
    break;
805
  default:
806
    snprintf(_path, MAX_PATH, "%s%c*", dir, GF_PATH_SEPARATOR);
807
    break;
808
  }
809
810
  tmpdir = _path;
811
  if (gf_utf8_mbstowcs(path, GF_MAX_PATH, &tmpdir) == GF_UTF8_FAIL) {
812
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot convert %s to UTF16: broken string\n", dir));
813
    return GF_BAD_PARAM;
814
  }
815
  tmpdir  = filter;
816
  if (gf_utf8_mbstowcs(w_filter, sizeof(w_filter), &tmpdir) == GF_UTF8_FAIL) {
817
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot convert %s to UTF16: broken string\n", filter));
818
    return GF_BAD_PARAM;
819
  }
820
821
#else
822
4.85k
  size_t dir_len = strlen(dir);
823
4.85k
  if (dir_len < GF_ARRAY_LENGTH(path)) {
824
4.85k
    strcpy(path, dir);
825
4.85k
  }
826
0
  else {
827
0
    memcpy(path, dir, GF_ARRAY_LENGTH(path));
828
0
    path[ GF_ARRAY_LENGTH(path) - 1] = 0;
829
0
  }
830
4.85k
  size_t path_len = strlen(path);
831
4.85k
  if (path_len && path[path_len-1] != '/') strcat(path, "/");
832
4.85k
#endif
833
834
#ifdef WIN32
835
  SearchH= FindFirstFileW(path, &FindData);
836
  if (SearchH == INVALID_HANDLE_VALUE) return GF_IO_ERR;
837
838
#if defined (_WIN32_WCE)
839
  _path[strlen(_path) ? strlen(_path)-1:0] = 0;
840
#else
841
  size_t path_len = wcslen(path);
842
  path[ path_len ? path_len-1 : 0] = 0;
843
#endif
844
845
  while (SearchH != INVALID_HANDLE_VALUE) {
846
847
#else
848
849
4.85k
  the_dir = opendir(path);
850
4.85k
  if (the_dir == NULL) {
851
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot open directory \"%s\" for enumeration: %d\n", path, errno));
852
0
    return GF_IO_ERR;
853
0
  }
854
4.85k
  the_file = readdir(the_dir);
855
60.5k
  while (the_file) {
856
857
55.6k
#endif
858
859
55.6k
    memset(&file_info, 0, sizeof(GF_FileEnumInfo) );
860
861
862
#if defined (_WIN32_WCE)
863
    if (!wcscmp(FindData.cFileName, _T(".") )) goto next;
864
    if (!wcscmp(FindData.cFileName, _T("..") )) goto next;
865
#elif defined(WIN32)
866
    if (!wcscmp(FindData.cFileName, L".")) goto next;
867
    if (!wcscmp(FindData.cFileName, L"..")) goto next;
868
#else
869
55.6k
    if (!strcmp(the_file->d_name, "..")) goto next;
870
50.8k
    if (the_file->d_name[0] == '.') goto next;
871
43.5k
#endif
872
873
#ifdef WIN32
874
    file_info.directory = (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? GF_TRUE : GF_FALSE;
875
    if (!enum_directory && file_info.directory) goto next;
876
    if (enum_directory && !file_info.directory) goto next;
877
#endif
878
879
43.5k
    if (filter) {
880
#if defined (_WIN32_WCE)
881
      short ext[30];
882
      short *sep = wcsrchr(FindData.cFileName, (wchar_t) '.');
883
      short *found_ext;
884
      u32 ext_len;
885
      if (!sep) goto next;
886
      wcscpy(ext, sep+1);
887
      wcslwr(ext);
888
      ext_len = (u32) wcslen(ext);
889
      found_ext = wcsstr(w_filter, ext);
890
      if (!found_ext) goto next;
891
      if (found_ext[ext_len] && (found_ext[ext_len] != (wchar_t) ';')) goto next;
892
#elif defined(WIN32)
893
      wchar_t ext[30];
894
      wchar_t *sep = wcsrchr(FindData.cFileName, L'.');
895
      wchar_t *found_ext;
896
      u32 ext_len;
897
      if (!sep) goto next;
898
      wcscpy(ext, sep+1);
899
      wcslwr(ext);
900
      ext_len = (u32) wcslen(ext);
901
      found_ext = wcsstr(w_filter, ext);
902
      if (!found_ext) goto next;
903
      if (found_ext[ext_len] && (found_ext[ext_len] != L';')) goto next;
904
#else
905
43.5k
      char ext[30];
906
43.5k
      char *sep = strrchr(the_file->d_name, '.');
907
43.5k
      char *found_ext;
908
43.5k
      u32 ext_len;
909
43.5k
      if (!sep) goto next;
910
0
      strcpy(ext, sep+1);
911
0
      strlwr(ext);
912
0
      ext_len = (u32) strlen(ext);
913
0
      found_ext = strstr(filter, ext);
914
0
      if (!found_ext) goto next;
915
0
      if (found_ext[ext_len] && (found_ext[ext_len]!=';')) goto next;
916
0
#endif
917
0
    }
918
919
#if defined(WIN32)
920
    file_info.hidden = (FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? GF_TRUE : GF_FALSE;
921
    file_info.system = (FindData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) ? GF_TRUE : GF_FALSE;
922
    file_info.size = MAXDWORD;
923
    file_info.size += 1;
924
    file_info.size *= FindData.nFileSizeHigh;
925
    file_info.size += FindData.nFileSizeLow;
926
    file_info.last_modified = (u64) ((*(LONGLONG *) &FindData.ftLastWriteTime - TIMESPEC_TO_FILETIME_OFFSET) / 10);
927
#endif
928
929
#if defined (_WIN32_WCE)
930
    CE_WideToChar(FindData.cFileName, file);
931
    strcpy(item_path, _path);
932
    strcat(item_path, file);
933
#elif defined(WIN32)
934
    wcscpy(item_path, path);
935
    wcscat(item_path, FindData.cFileName);
936
    file = FindData.cFileName;
937
#else
938
0
    strcpy(item_path, path);
939
0
    strcat(item_path, the_file->d_name);
940
0
    GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Checking file \"%s\" for enum\n", item_path));
941
942
0
    if (stat( item_path, &st ) != 0) goto next;
943
944
0
    file_info.directory = ((st.st_mode & S_IFMT) == S_IFDIR) ? GF_TRUE : GF_FALSE;
945
0
    if (enum_directory && !file_info.directory) goto next;
946
0
    if (!enum_directory && file_info.directory) goto next;
947
948
0
    file_info.size = st.st_size;
949
950
0
    struct tm _t = * gf_gmtime(& st.st_mtime);
951
0
    file_info.last_modified = mktime(&_t);
952
0
    file_info.last_modified *= 1000000;
953
#if defined(__DARWIN__) || defined(__APPLE__) || defined(GPAC_CONFIG_IOS)
954
    file_info.last_modified += st.st_mtimespec.tv_nsec / 1000;
955
#elif defined(GPAC_HAS_MTIM_NSEC)
956
    file_info.last_modified += st.st_mtim.tv_nsec / 1000;
957
#elif defined(GPAC_CONFIG_ANDROID)
958
    file_info.last_modified += st.st_mtime_nsec / 1000;
959
#endif
960
961
0
    file = the_file->d_name;
962
0
    if (file && file[0]=='.') file_info.hidden = GF_TRUE;
963
964
0
    if (file_info.directory) {
965
0
      char * parent_name = strrchr(item_path, '/');
966
0
      if (!parent_name) {
967
0
        file_info.drive = GF_TRUE;
968
0
      } else {
969
0
        struct stat st_parent;
970
0
        parent_name[0] = 0;
971
0
        if (stat(item_path, &st_parent) == 0)  {
972
0
          if ((st.st_dev != st_parent.st_dev) || (st.st_ino == st_parent.st_ino) ) {
973
0
            file_info.drive = GF_TRUE;
974
0
          }
975
0
        }
976
0
        parent_name[0] = '/';
977
0
      }
978
0
    }
979
0
#endif
980
981
0
    Bool done = GF_FALSE;
982
#ifdef WIN32
983
    mbs_file = gf_wcs_to_utf8(file);
984
    mbs_item_path = gf_wcs_to_utf8(item_path);
985
    if (!mbs_file || !mbs_item_path)
986
    {
987
      if (mbs_file) gf_free(mbs_file);
988
      if (mbs_item_path) gf_free(mbs_item_path);
989
      return GF_IO_ERR;
990
    }
991
    if (enum_dir_fct(cbck, mbs_file, mbs_item_path, &file_info)) {
992
      BOOL ret = FindClose(SearchH);
993
      if (!ret) {
994
        DWORD err = GetLastError();
995
        GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_enum_directory() returned(1) the following error code: %d\n", err));
996
      }
997
#else
998
0
    if (enum_dir_fct(cbck, file, item_path, &file_info)) {
999
0
#endif
1000
0
      done = GF_TRUE;
1001
0
    }
1002
1003
#ifdef WIN32
1004
    gf_free(mbs_file);
1005
    gf_free(mbs_item_path);
1006
#endif
1007
0
    if (done) break;
1008
1009
55.6k
next:
1010
#ifdef WIN32
1011
    if (!FindNextFileW(SearchH, &FindData)) {
1012
      BOOL ret = FindClose(SearchH);
1013
      if (!ret) {
1014
        DWORD err = GetLastError();
1015
        GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_enum_directory() returned(2) the following error code: %d\n", err));
1016
      }
1017
      break;
1018
    }
1019
#else
1020
55.6k
    the_file = readdir(the_dir);
1021
55.6k
#endif
1022
55.6k
  }
1023
4.85k
#ifndef WIN32
1024
4.85k
  closedir(the_dir);
1025
4.85k
#endif
1026
4.85k
  return GF_OK;
1027
4.85k
}
1028
1029
1030
1031
struct __gf_file_io
1032
{
1033
  u32 _reserved_null;
1034
  void *__this;
1035
1036
  GF_FileIO * (*open)(GF_FileIO *fileio_ref, const char *url, const char *mode, GF_Err *out_error);
1037
  GF_Err (*seek)(GF_FileIO *fileio, u64 offset, s32 whence);
1038
  u32 (*read)(GF_FileIO *fileio, u8 *buffer, u32 bytes);
1039
  u32 (*write)(GF_FileIO *fileio, u8 *buffer, u32 bytes);
1040
  s64 (*tell)(GF_FileIO *fileio);
1041
  Bool (*eof)(GF_FileIO *fileio);
1042
  int (*printf)(GF_FileIO *gfio, const char *format, va_list args);
1043
  char *(*gets)(GF_FileIO *gfio, char *ptr, u32 size);
1044
1045
  char *url;
1046
  char *res_url;
1047
  void *udta;
1048
1049
  u32 creation_time;
1050
  u64 bytes_done, file_size_plus_one;
1051
  Bool main_th;
1052
  GF_FileIOCacheState cache_state;
1053
  u32 bytes_per_sec;
1054
  u32 write_state;
1055
1056
  u32 printf_alloc;
1057
  u8* printf_buf;
1058
};
1059
1060
GF_EXPORT
1061
Bool gf_fileio_check(FILE *fp)
1062
54.2k
{
1063
54.2k
  GF_FileIO *fio = (GF_FileIO *)fp;
1064
54.2k
  if ((fp==stdin) || (fp==stderr) || (fp==stdout))
1065
37.7k
    return GF_FALSE;
1066
1067
16.5k
  if (fio && !fio->_reserved_null && (fio->__this==fio))
1068
0
    return GF_TRUE;
1069
16.5k
  return GF_FALSE;
1070
16.5k
}
1071
1072
GF_EXPORT
1073
GF_FileIOWriteState gf_fileio_write_ready(FILE *fp)
1074
0
{
1075
0
  GF_FileIO *fio = (GF_FileIO *)fp;
1076
0
  if ((fp==stdin) || (fp==stderr) || (fp==stdout))
1077
0
    return GF_FIO_WRITE_READY;
1078
1079
  //GFIO, check state
1080
0
  if (fio && !fio->_reserved_null && (fio->__this==fio)) {
1081
0
    return fio->write_state;
1082
0
  }
1083
  //sync stdio
1084
0
  return GF_FIO_WRITE_READY;
1085
0
}
1086
1087
u32 gf_fileio_get_write_rate(FILE *fp)
1088
0
{
1089
0
  GF_FileIO *fio = (GF_FileIO *)fp;
1090
0
  if ((fp==stdin) || (fp==stderr) || (fp==stdout))
1091
0
    return 0;
1092
1093
0
  if (fio && !fio->_reserved_null && (fio->__this==fio)) {
1094
0
    return fio->bytes_per_sec;
1095
0
  }
1096
  //sync stdio
1097
0
  return 0;
1098
0
}
1099
1100
typedef struct
1101
{
1102
  u8 *data;
1103
  u32 size;
1104
  u32 pos;
1105
  u32 url_crc;
1106
  u32 nb_ref;
1107
  char *URL;
1108
  Bool sub_open;
1109
} GF_FileIOBlob;
1110
1111
static GF_FileIO *gfio_blob_open(GF_FileIO *fileio_ref, const char *url, const char *mode, GF_Err *out_error)
1112
0
{
1113
0
  GF_FileIOBlob *blob = gf_fileio_get_udta(fileio_ref);
1114
0
  if (!strcmp(mode, "close") || !strcmp(mode, "unref")) {
1115
0
    if (blob->nb_ref) blob->nb_ref--;
1116
0
    blob->pos = 0;
1117
0
    if (blob->nb_ref)
1118
0
      return NULL;
1119
1120
0
    if (blob->sub_open)
1121
0
      gf_unregister_file_handle((FILE *)fileio_ref);
1122
0
    if (blob->URL) gf_free(blob->URL);
1123
0
    gf_free(blob);
1124
0
    gf_fileio_del(fileio_ref);
1125
0
    return NULL;
1126
0
  }
1127
0
  if (!strcmp(mode, "ref")) {
1128
0
    blob->nb_ref++;
1129
0
    *out_error = GF_OK;
1130
0
    return NULL;
1131
0
  }
1132
0
  if (!strcmp(mode, "url")) {
1133
0
    *out_error = GF_BAD_PARAM;
1134
0
    return NULL;
1135
0
  }
1136
0
  if (!strcmp(mode, "probe")) {
1137
0
    u32 crc = gf_crc_32(url, (u32) strlen(url) );
1138
0
    *out_error = (crc==blob->url_crc) ? GF_OK : GF_URL_ERROR;
1139
0
    return NULL;
1140
0
  }
1141
0
  if (mode[0]!='r') {
1142
0
    *out_error = GF_BAD_PARAM;
1143
0
    return NULL;
1144
0
  }
1145
  //no user, we can use this object directly
1146
0
  if (!blob->nb_ref) {
1147
0
    blob->nb_ref = 1;
1148
0
    *out_error = GF_OK;
1149
0
    return fileio_ref;
1150
0
  }
1151
  //if already in use, we must allocate a new gfio otherwise read position would get shared across FILE streams !
1152
0
  fileio_ref = gf_fileio_from_mem(blob->URL, blob->data, blob->size);
1153
0
  blob = gf_fileio_get_udta(fileio_ref);
1154
  //remember we forced a new gfio, it will have to be unregistered upon close
1155
0
  blob->sub_open = GF_TRUE;
1156
0
  *out_error = fileio_ref ? GF_OK : GF_IO_ERR;
1157
0
  return fileio_ref;
1158
0
}
1159
1160
static GF_Err gfio_blob_seek(GF_FileIO *fileio, u64 offset, s32 whence)
1161
0
{
1162
0
  GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
1163
0
  if (whence==SEEK_END)
1164
0
    blob->pos = blob->size;
1165
0
  else if (whence==SEEK_SET) {
1166
0
    if ((s64)offset<0)
1167
0
      return GF_BAD_PARAM;
1168
0
    if ((u32)offset >= blob->size)
1169
0
      return GF_BAD_PARAM;
1170
0
    blob->pos = (u32) offset;
1171
0
  } else {
1172
0
    if (!offset)
1173
0
      return GF_OK;
1174
0
    if ((s64)offset<0)
1175
0
      return GF_BAD_PARAM;
1176
0
    if (blob->pos + offset > blob->size)
1177
0
      return GF_BAD_PARAM;
1178
0
    blob->pos += (u32) offset;
1179
0
  }
1180
0
  return GF_OK;
1181
0
}
1182
static u32 gfio_blob_read(GF_FileIO *fileio, u8 *buffer, u32 bytes)
1183
0
{
1184
0
  GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
1185
0
  if (bytes + blob->pos > blob->size)
1186
0
    bytes = blob->size - blob->pos;
1187
0
  if (bytes) {
1188
0
    memcpy(buffer, blob->data+blob->pos, bytes);
1189
0
    blob->pos += bytes;
1190
0
    assert(blob->pos<=blob->size);
1191
0
  }
1192
0
  return bytes;
1193
0
}
1194
static s64 gfio_blob_tell(GF_FileIO *fileio)
1195
0
{
1196
0
  GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
1197
0
  return (s64) blob->pos;
1198
0
}
1199
static Bool gfio_blob_eof(GF_FileIO *fileio)
1200
0
{
1201
0
  GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
1202
0
  if (blob->pos==blob->size) return GF_TRUE;
1203
0
  return GF_FALSE;
1204
0
}
1205
1206
static char *gfio_blob_gets(GF_FileIO *fileio, char *ptr, u32 size)
1207
0
{
1208
0
  GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
1209
0
  char *buf = blob->data + blob->pos;
1210
0
  u32 len = blob->size - blob->pos;
1211
0
  if (!len) return NULL;
1212
1213
0
  char *next = memchr(buf, '\n', len);
1214
0
  if (next) {
1215
0
    len = (u32) (next - buf);
1216
0
    if (len + blob->pos<blob->size) len++;
1217
0
  }
1218
0
  if (len + 1 > size) len = size - 1;
1219
0
  memcpy(ptr, blob->data+blob->pos, len);
1220
0
  blob->pos += len;
1221
0
  ptr[len] = 0;
1222
0
  return ptr;
1223
0
}
1224
1225
GF_List *allocated_gfios = NULL;
1226
1227
GF_EXPORT
1228
GF_FileIO *gf_fileio_new(char *url, void *udta,
1229
  gfio_open_proc open,
1230
  gfio_seek_proc seek,
1231
  gfio_read_proc read,
1232
  gfio_write_proc write,
1233
  gfio_tell_proc tell,
1234
  gfio_eof_proc eof,
1235
  gfio_printf_proc printf)
1236
0
{
1237
0
  char szURL[100];
1238
0
  GF_FileIO *tmp;
1239
1240
0
  if (!open || !seek || !tell || !eof) return NULL;
1241
1242
0
  if (!write && !read) return NULL;
1243
0
  GF_SAFEALLOC(tmp, GF_FileIO);
1244
0
  if (!tmp) return NULL;
1245
1246
0
  tmp->open = open;
1247
0
  tmp->seek = seek;
1248
0
  tmp->write = write;
1249
0
  tmp->read = read;
1250
0
  tmp->tell = tell;
1251
0
  tmp->eof = eof;
1252
0
  tmp->printf = printf;
1253
1254
0
  tmp->udta = udta;
1255
0
  if (url)
1256
0
    tmp->res_url = gf_strdup(url);
1257
1258
  //add clock as cookie to avoid creating twice th same gfio url in case the above malloc returns
1259
  //the same adress as a previously allocated gfio
1260
0
  tmp->creation_time = gf_sys_clock();
1261
0
  sprintf(szURL, "gfio://%p&%u", tmp, tmp->creation_time);
1262
0
  tmp->url = gf_strdup(szURL);
1263
0
  tmp->__this = tmp;
1264
  //track all allocated gfios so that we don't attempt accessing a freed one !
1265
0
  gf_mx_p(logs_mx);
1266
0
  if (!allocated_gfios) allocated_gfios = gf_list_new();
1267
0
  gf_list_add(allocated_gfios, tmp);
1268
0
  gf_mx_v(logs_mx);
1269
1270
0
  return tmp;
1271
0
}
1272
1273
GF_EXPORT
1274
const char * gf_fileio_url(GF_FileIO *gfio)
1275
0
{
1276
0
  return gfio ? gfio->url : NULL;
1277
0
}
1278
1279
GF_EXPORT
1280
const char * gf_fileio_resource_url(GF_FileIO *gfio)
1281
0
{
1282
0
  return gfio ? gfio->res_url : NULL;
1283
0
}
1284
1285
GF_EXPORT
1286
void gf_fileio_del(GF_FileIO *gfio)
1287
0
{
1288
0
  if (!gfio) return;
1289
0
  gf_mx_p(logs_mx);
1290
0
  if (gf_list_del_item(allocated_gfios, gfio)<0) {
1291
0
    gf_mx_v(logs_mx);
1292
0
    return;
1293
0
  }
1294
0
  if (!gf_list_count(allocated_gfios)) {
1295
0
    gf_list_del(allocated_gfios);
1296
0
    allocated_gfios = NULL;
1297
0
  }
1298
0
  gf_mx_v(logs_mx);
1299
1300
0
  if (gfio->url) gf_free(gfio->url);
1301
0
  if (gfio->res_url) gf_free(gfio->res_url);
1302
0
  if (gfio->printf_buf) gf_free(gfio->printf_buf);
1303
0
  gf_free(gfio);
1304
1305
1306
0
}
1307
1308
GF_EXPORT
1309
void *gf_fileio_get_udta(GF_FileIO *gfio)
1310
0
{
1311
0
  return gfio ? gfio->udta : NULL;
1312
0
}
1313
1314
GF_EXPORT
1315
GF_FileIO *gf_fileio_open_url(GF_FileIO *gfio_ref, const char *url, const char *mode, GF_Err *out_err)
1316
0
{
1317
0
  if (!gfio_ref) {
1318
0
    *out_err = GF_BAD_PARAM;
1319
0
    return NULL;
1320
0
  }
1321
0
  if (!gfio_ref->open) {
1322
0
    *out_err = url ? GF_NOT_SUPPORTED : GF_OK;
1323
0
    return NULL;
1324
0
  }
1325
0
  return gfio_ref->open(gfio_ref, url, mode, out_err);
1326
0
}
1327
1328
static u32 gf_fileio_read(GF_FileIO *gfio, u8 *buffer, u32 nb_bytes)
1329
0
{
1330
0
  if (!gfio) return GF_BAD_PARAM;
1331
0
  if (gfio->read)
1332
0
    return gfio->read(gfio, buffer, nb_bytes);
1333
0
  return 0;
1334
0
}
1335
1336
static u32 gf_fileio_write(GF_FileIO *gfio, u8 *buffer, u32 nb_bytes)
1337
0
{
1338
0
  if (!gfio) return GF_BAD_PARAM;
1339
0
  if (!gfio->write) return 0;
1340
0
  return gfio->write(gfio, buffer, (u32) nb_bytes);
1341
0
}
1342
1343
int gf_fileio_printf(GF_FileIO *gfio, const char *format, va_list args)
1344
0
{
1345
0
  va_list args_copy;
1346
0
  if (!gfio) return -1;
1347
0
  if (gfio->printf) return gfio->printf(gfio, format, args);
1348
1349
0
  if (!gfio->write) return -1;
1350
1351
0
  va_copy(args_copy, args);
1352
0
  u32 len=vsnprintf(NULL, 0, format, args_copy);
1353
0
  va_end(args_copy);
1354
1355
0
  if (len>=gfio->printf_alloc) {
1356
0
    gfio->printf_alloc = len+1;
1357
0
    gfio->printf_buf = gf_realloc(gfio->printf_buf, gfio->printf_alloc);
1358
0
  }
1359
0
  vsnprintf(gfio->printf_buf, len, format, args);
1360
0
  gfio->printf_buf[len] = 0;
1361
0
  return gfio->write(gfio, gfio->printf_buf, len+1);
1362
0
}
1363
1364
GF_EXPORT
1365
Bool gf_fileio_write_mode(GF_FileIO *gfio)
1366
0
{
1367
0
  return (gfio && gfio->write) ? GF_TRUE : GF_FALSE;
1368
0
}
1369
1370
GF_EXPORT
1371
Bool gf_fileio_read_mode(GF_FileIO *gfio)
1372
0
{
1373
0
  return (gfio && gfio->read) ? GF_TRUE : GF_FALSE;
1374
0
}
1375
1376
GF_EXPORT
1377
GF_FileIO *gf_fileio_from_url(const char *url)
1378
0
{
1379
0
  char szURL[100];
1380
0
  u32 cookie=0;
1381
0
  GF_FileIO *ptr=NULL;
1382
0
  if (!url) return NULL;
1383
0
  if (strncmp(url, "gfio://", 7)) return NULL;
1384
1385
0
  sscanf(url, "gfio://%p&%u", &ptr, &cookie);
1386
0
  sprintf(szURL, "gfio://%p&%u", ptr, cookie);
1387
0
  if (strcmp(url, szURL))
1388
0
    return NULL;
1389
1390
0
  gf_mx_p(logs_mx);
1391
0
  if (gf_list_find(allocated_gfios, ptr)<0) {
1392
0
    gf_mx_v(logs_mx);
1393
0
    return NULL;
1394
0
  }
1395
0
  gf_mx_v(logs_mx);
1396
1397
0
  if (ptr && ptr->url && !strcmp(ptr->url, url) && (ptr->creation_time==cookie) ) {
1398
0
    return ptr;
1399
0
  }
1400
0
  return NULL;
1401
0
}
1402
1403
GF_EXPORT
1404
const char * gf_fileio_translate_url(const char *url)
1405
0
{
1406
0
  GF_FileIO *gfio = gf_fileio_from_url(url);
1407
0
  return gfio ? gfio->res_url : NULL;
1408
0
}
1409
1410
Bool gf_fileio_eof(GF_FileIO *fileio)
1411
0
{
1412
0
  if (!fileio || !fileio->eof) return GF_TRUE;
1413
0
  return fileio->eof(fileio);
1414
0
}
1415
1416
int gf_fileio_flush(GF_FileIO *fileio)
1417
0
{
1418
0
  if (!fileio || !fileio->write) return 0;
1419
0
  fileio->write(fileio, NULL, 0);
1420
0
  return 0;
1421
0
}
1422
1423
GF_EXPORT
1424
const char *gf_fileio_factory(GF_FileIO *gfio, const char *new_res_url)
1425
0
{
1426
0
  GF_Err e;
1427
0
  if (!gfio || !gfio->open) return NULL;
1428
0
  GF_FileIO *new_res = gfio->open(gfio, new_res_url, "url", &e);
1429
0
  if (e) return NULL;
1430
0
  return gf_fileio_url(new_res);
1431
0
}
1432
1433
GF_EXPORT
1434
void gf_fileio_set_stats(GF_FileIO *gfio, u64 bytes_done, u64 file_size, GF_FileIOCacheState cache_state, u32 bytes_per_sec)
1435
0
{
1436
0
  if (!gfio) return;
1437
0
  gfio->bytes_done = bytes_done;
1438
0
  gfio->file_size_plus_one = file_size ? (1 + file_size) : 0;
1439
0
  gfio->cache_state = cache_state;
1440
0
  gfio->bytes_per_sec = bytes_per_sec;
1441
0
}
1442
1443
GF_EXPORT
1444
void gf_fileio_set_write_state(GF_FileIO *gfio, GF_FileIOWriteState write_state)
1445
0
{
1446
0
  if (!gfio) return;
1447
0
  gfio->write_state = write_state;
1448
0
}
1449
1450
GF_EXPORT
1451
Bool gf_fileio_get_stats(GF_FileIO *gfio, u64 *bytes_done, u64 *file_size, GF_FileIOCacheState *cache_state, u32 *bytes_per_sec)
1452
0
{
1453
0
  if (!gf_fileio_check((FILE *)gfio))
1454
0
    return GF_FALSE;
1455
1456
0
  if (bytes_done) *bytes_done = gfio->bytes_done;
1457
0
  if (file_size) *file_size = gfio->file_size_plus_one ? gfio->file_size_plus_one-1 : 0;
1458
0
  if (cache_state) *cache_state = gfio->cache_state;
1459
0
  if (bytes_per_sec) *bytes_per_sec = gfio->bytes_per_sec;
1460
0
  return GF_TRUE;
1461
0
}
1462
1463
GF_EXPORT
1464
GF_Err gf_fileio_tag_main_thread(GF_FileIO *fileio)
1465
0
{
1466
0
  if (!fileio) return GF_BAD_PARAM;
1467
0
  fileio->main_th = GF_TRUE;
1468
0
  return GF_OK;
1469
0
}
1470
1471
GF_EXPORT
1472
Bool gf_fileio_is_main_thread(const char *url)
1473
0
{
1474
0
  GF_FileIO *gfio = gf_fileio_from_url(url);
1475
0
  if (!gfio) return GF_FALSE;
1476
0
  return gfio->main_th;
1477
0
}
1478
1479
1480
1481
GF_EXPORT
1482
u64 gf_ftell(FILE *fp)
1483
2
{
1484
2
  if (!fp) return -1;
1485
1486
2
  if (gf_fileio_check(fp)) {
1487
0
    GF_FileIO *gfio = (GF_FileIO *)fp;
1488
0
    if (!gfio->tell) return -1;
1489
0
    return gfio->tell(gfio);
1490
0
  }
1491
1492
#if defined(_WIN32_WCE)
1493
  return (u64) ftell(fp);
1494
#elif defined(GPAC_CONFIG_WIN32) && (defined(__CYGWIN__) || defined(__MINGW32__))
1495
#if (_FILE_OFFSET_BITS >= 64)
1496
  return (u64) ftello64(fp);
1497
#else
1498
  return (u64) ftell(fp);
1499
#endif
1500
#elif defined(WIN32)
1501
  return (u64) _ftelli64(fp);
1502
#elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID)
1503
2
  return (u64) ftello64(fp);
1504
#elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
1505
  return (u64) ftello(fp);
1506
#else
1507
  return (u64) ftell(fp);
1508
#endif
1509
2
}
1510
1511
GF_EXPORT
1512
s32 gf_fseek(FILE *fp, s64 offset, s32 whence)
1513
4
{
1514
4
  if (!fp) return -1;
1515
4
  if (gf_fileio_check(fp)) {
1516
0
    GF_FileIO *gfio = (GF_FileIO *)fp;
1517
0
    if (gfio->seek) {
1518
0
      GF_Err e = gfio->seek(gfio, offset, whence);
1519
0
      if (e) return -1;
1520
0
      return 0;
1521
0
    }
1522
0
    return -1;
1523
0
  }
1524
1525
#if defined(_WIN32_WCE)
1526
  return (u64) fseek(fp, (s32) offset, whence);
1527
#elif defined(GPAC_CONFIG_WIN32) && defined(__CYGWIN__) /* mingw or cygwin */
1528
#if (_FILE_OFFSET_BITS >= 64)
1529
  return (u64) fseeko64(fp, offset, whence);
1530
#else
1531
  return (u64) fseek(fp, (s32) offset, whence);
1532
#endif
1533
#elif defined(WIN32)
1534
  return (u64) _fseeki64(fp, offset, whence);
1535
#elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID)
1536
4
  return fseeko64(fp, (off64_t) offset, whence);
1537
#elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
1538
  return fseeko(fp, (off_t) offset, whence);
1539
#else
1540
  return fseek(fp, (s32) offset, whence);
1541
#endif
1542
4
}
1543
1544
1545
static GF_FileIO *gf_fileio_from_blob(const char *file_name)
1546
0
{
1547
0
  u8 *blob_data;
1548
0
  u32 blob_size, flags;
1549
0
  GF_FileIOBlob *gfio_blob;
1550
0
  GF_Err e = gf_blob_get(file_name, &blob_data, &blob_size, &flags);
1551
0
  if (e || !blob_data) return NULL;
1552
0
  gf_blob_release(file_name);
1553
1554
0
  if (flags) {
1555
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Attempt at creating a GFIO object on blob corrupted or in transfer, not supported !"));
1556
0
    return NULL;
1557
0
  }
1558
1559
0
  GF_SAFEALLOC(gfio_blob, GF_FileIOBlob);
1560
0
  if (!gfio_blob) return NULL;
1561
0
  gfio_blob->data = blob_data;
1562
0
  gfio_blob->size = blob_size;
1563
0
  GF_FileIO *res = gf_fileio_new((char *) file_name, gfio_blob, gfio_blob_open, gfio_blob_seek, gfio_blob_read, NULL, gfio_blob_tell, gfio_blob_eof, NULL);
1564
0
  if (!res) {
1565
0
    gf_free(gfio_blob);
1566
0
    return NULL;
1567
0
  }
1568
0
  res->gets = gfio_blob_gets;
1569
0
  if (file_name)
1570
0
    gfio_blob->url_crc = gf_crc_32(file_name, (u32) strlen(file_name) );
1571
0
  return res;
1572
0
}
1573
1574
GF_EXPORT
1575
GF_FileIO *gf_fileio_from_mem(const char *URL, const u8 *data, u32 size)
1576
0
{
1577
0
  GF_FileIOBlob *gfio_blob;
1578
0
  GF_SAFEALLOC(gfio_blob, GF_FileIOBlob);
1579
0
  if (!gfio_blob) return NULL;
1580
0
  gfio_blob->data = (u8 *) data;
1581
0
  gfio_blob->size = size;
1582
0
  gfio_blob->URL = gf_strdup(URL);
1583
0
  GF_FileIO *res = gf_fileio_new((char *) URL, gfio_blob, gfio_blob_open, gfio_blob_seek, gfio_blob_read, NULL, gfio_blob_tell, gfio_blob_eof, NULL);
1584
0
  if (!res)  {
1585
0
    gf_free(gfio_blob);
1586
0
    return NULL;
1587
0
  }
1588
0
  res->gets = gfio_blob_gets;
1589
0
  if (URL)
1590
0
    gfio_blob->url_crc = gf_crc_32(URL, (u32) strlen(URL) );
1591
0
  gf_fopen(gf_fileio_url(res), "r");
1592
0
  return res;
1593
0
}
1594
1595
1596
#ifdef GPAC_CONFIG_EMSCRIPTEN
1597
static u32 mainloop_th_id = 0;
1598
void gf_set_mainloop_thread(u32 thread_id)
1599
{
1600
  mainloop_th_id = thread_id;
1601
}
1602
#endif
1603
1604
GF_EXPORT
1605
FILE *gf_fopen_ex(const char *file_name, const char *parent_name, const char *mode, Bool no_warn)
1606
8.06k
{
1607
8.06k
  FILE *res = NULL;
1608
8.06k
  u32 gfio_type = 0;
1609
8.06k
  Bool is_mkdir = GF_FALSE;
1610
1611
8.06k
  if (!file_name || !mode) return NULL;
1612
8.06k
  if (!strcmp(mode, "mkdir")) {
1613
0
    is_mkdir = GF_TRUE;
1614
0
    mode = "w";
1615
0
  }
1616
1617
8.06k
  if (!strncmp(file_name, "gmem://", 7)) {
1618
0
    GF_FileIO *new_gfio;
1619
0
    if (strstr(mode, "w"))
1620
0
      return NULL;
1621
0
    new_gfio = gf_fileio_from_blob(file_name);
1622
0
    if (new_gfio)
1623
0
      gf_register_file_handle((char*)file_name, (FILE *) new_gfio, GF_FALSE);
1624
0
    return (FILE *) new_gfio;
1625
1626
0
  }
1627
1628
8.06k
  if (!strncmp(file_name, "gfio://", 7))
1629
0
    gfio_type = 1;
1630
8.06k
  else if (parent_name && !strncmp(parent_name, "gfio://", 7))
1631
0
    gfio_type = 2;
1632
1633
8.06k
  if (gfio_type) {
1634
0
    GF_FileIO *gfio_ref;
1635
0
    GF_FileIO *new_gfio;
1636
0
    GF_Err e;
1637
0
    GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Open GFIO %s in mode %s\n", file_name, mode));
1638
1639
0
    if (gfio_type==1)
1640
0
      gfio_ref = gf_fileio_from_url(file_name);
1641
0
    else
1642
0
      gfio_ref = gf_fileio_from_url(parent_name);
1643
1644
0
    if (!gfio_ref) return NULL;
1645
0
    if (strchr(mode, 'r') && !gf_fileio_read_mode(gfio_ref)) {
1646
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("FileIO %s is not read-enabled and open mode %s was requested\n", file_name, mode));
1647
0
      return NULL;
1648
0
    }
1649
0
    if (strpbrk(mode, "wa") && !gf_fileio_write_mode(gfio_ref)) {
1650
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("FileIO %s is not write-enabled and open mode %s was requested\n", file_name, mode));
1651
0
      return NULL;
1652
0
    }
1653
0
    new_gfio = gf_fileio_open_url(gfio_ref, file_name, mode, &e);
1654
0
    if (e) {
1655
0
      if (!no_warn) {
1656
0
        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)));
1657
0
      }
1658
0
      return NULL;
1659
0
    }
1660
0
    if (new_gfio)
1661
0
      gf_register_file_handle((char*)file_name, (FILE *) new_gfio, GF_FALSE);
1662
0
    return (FILE *) new_gfio;
1663
0
  }
1664
1665
8.06k
  GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Open file %s in mode %s\n", file_name, mode));
1666
8.06k
  if (strchr(mode, 'w')) {
1667
2.42k
    char *fname = gf_strdup(file_name);
1668
2.42k
    char *sep = strchr(fname, '/');
1669
2.42k
    if (!sep) sep = strchr(fname, '\\');
1670
2.42k
    if (file_name[0] == '/') sep = strchr(fname+1, '/');
1671
0
    else if (file_name[2] == '\\') sep = strchr(fname+3, '\\');
1672
1673
7.28k
    while (sep) {
1674
4.85k
      char *n_sep;
1675
4.85k
      char c = sep[0];
1676
4.85k
      sep[0] = 0;
1677
4.85k
      if (!gf_dir_exists(fname)) {
1678
1
        GF_Err e = gf_mkdir(fname);
1679
1
        if (e != GF_OK) {
1680
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Failed to create directory \"%s\": %s\n", file_name, gf_error_to_string(e) ));
1681
0
          sep[0] = c;
1682
0
          gf_free(fname);
1683
0
          return NULL;
1684
0
        }
1685
1
      }
1686
4.85k
      sep[0] = c;
1687
4.85k
      n_sep = strchr(sep+1, '/');
1688
4.85k
      if (!n_sep) n_sep = strchr(sep+1, '\\');
1689
4.85k
      sep = n_sep;
1690
4.85k
    }
1691
2.42k
    gf_free(fname);
1692
2.42k
    if (is_mkdir) return NULL;
1693
2.42k
  }
1694
1695
#ifdef GPAC_CONFIG_EMSCRIPTEN
1696
  if (mainloop_th_id && strchr(mode, 'r') && (gf_th_id() != mainloop_th_id)) {
1697
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Opening file in read mode outside of main loop will likely deadlock - please report to GPAC devs\n"));
1698
  }
1699
#endif
1700
1701
#if defined(WIN32)
1702
  wchar_t *wname;
1703
  wchar_t *wmode;
1704
1705
  wname = gf_utf8_to_wcs(file_name);
1706
  wmode = gf_utf8_to_wcs(mode);
1707
  if (!wname || !wmode)
1708
  {
1709
    if (wname) gf_free(wname);
1710
    if (wmode) gf_free(wmode);
1711
    return NULL;
1712
  }
1713
  res = _wfsopen(wname, wmode, _SH_DENYNO);
1714
  gf_free(wname);
1715
  gf_free(wmode);
1716
#elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID)
1717
8.06k
  res = fopen64(file_name, mode);
1718
#elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
1719
  res = fopen(file_name, mode);
1720
#else
1721
  res = fopen(file_name, mode);
1722
#endif
1723
1724
8.06k
  if (res) {
1725
8.06k
    if (!parent_name || strcmp(parent_name, "__temp_file"))
1726
8.06k
      gf_register_file_handle((char*)file_name, res, GF_FALSE);
1727
1728
8.06k
    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));
1729
8.06k
  } else if (!no_warn) {
1730
0
    if (strpbrk(mode, "wa")) {
1731
#if defined(WIN32)
1732
      u32 err = GetLastError();
1733
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] system failure for file opening of \"%s\" in mode \"%s\": 0x%08x (%u opened)\n", file_name, mode, err, gpac_file_handles));
1734
#else
1735
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] system failure for file opening of \"%s\" in mode \"%s\": %d (%u opened)\n", file_name, mode, errno, gpac_file_handles));
1736
0
#endif
1737
0
    }
1738
0
  }
1739
8.06k
  return res;
1740
8.06k
}
1741
1742
1743
GF_EXPORT
1744
s32 gf_fd_open(const char *file_name, u32 oflags, u32 uflags)
1745
2.81k
{
1746
2.81k
  if (!file_name) return -1;
1747
1748
#if defined(WIN32)
1749
  wchar_t *wname = gf_utf8_to_wcs(file_name);
1750
  if (!wname) return -1;
1751
  int res = _wopen(wname, oflags, uflags);
1752
  gf_free(wname);
1753
  return res;
1754
#elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID)
1755
2.81k
  return open(file_name, oflags, uflags);
1756
#elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
1757
  return open(file_name, oflags, uflags);
1758
#else
1759
  return open(file_name, oflags, uflags);
1760
#endif
1761
0
  return -1;
1762
2.81k
}
1763
1764
GF_EXPORT
1765
FILE *gf_fopen(const char *file_name, const char *mode)
1766
8.06k
{
1767
8.06k
  return gf_fopen_ex(file_name, NULL, mode, GF_FALSE);
1768
8.06k
}
1769
1770
GF_EXPORT
1771
s32 gf_fclose(FILE *file)
1772
8.06k
{
1773
8.06k
  if (!file)
1774
0
    return 0;
1775
1776
8.06k
  if (gf_unregister_file_handle(file))
1777
0
    return 0;
1778
8.06k
  if (gf_fileio_check(file)) {
1779
0
    GF_Err e;
1780
0
    gf_fileio_open_url((GF_FileIO *) file, NULL, "close", &e);
1781
0
    if (e) return -1;
1782
0
    return 0;
1783
0
  }
1784
8.06k
  return fclose(file);
1785
8.06k
}
1786
1787
#if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! defined(_GNU_SOURCE) && !defined(WIN32)
1788
#define HAVE_STRERROR_R 1
1789
#endif
1790
1791
GF_EXPORT
1792
size_t gf_fwrite(const void *ptr, size_t nb_bytes, FILE *stream)
1793
0
{
1794
0
  size_t result=0;
1795
1796
0
  if (gf_fileio_check(stream)) {
1797
0
    return(size_t) gf_fileio_write((GF_FileIO *)stream, (u8 *) ptr, (u32) nb_bytes);
1798
0
  }
1799
1800
0
  if (ptr)
1801
0
    result = fwrite(ptr, 1, nb_bytes, stream);
1802
0
  if (result != nb_bytes) {
1803
#ifdef _WIN32_WCE
1804
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Error writing data: %d blocks to write but %d blocks written\n", nb_bytes, result));
1805
#else
1806
#if defined WIN32 && !defined(GPAC_CONFIG_WIN32)
1807
    errno_t errno_save;
1808
    _get_errno(&errno_save);
1809
#else
1810
0
    int errno_save = errno;
1811
0
#endif
1812
0
#ifdef HAVE_STRERROR_R
1813
0
#define ERRSTR_BUF_SIZE 256
1814
0
    char errstr[ERRSTR_BUF_SIZE];
1815
0
    if(strerror_r(errno_save, errstr, ERRSTR_BUF_SIZE) != 0)
1816
0
    {
1817
0
      strerror_r(0, errstr, ERRSTR_BUF_SIZE);
1818
0
    }
1819
#else
1820
    char *errstr = (char*)strerror(errno_save);
1821
#endif
1822
1823
0
    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));
1824
0
#endif
1825
0
  }
1826
0
  return result;
1827
0
}
1828
1829
GF_EXPORT
1830
size_t gf_fread(void *ptr, size_t nbytes, FILE *stream)
1831
2
{
1832
2
  size_t result;
1833
2
  if (gf_fileio_check(stream)) {
1834
0
    return (size_t) gf_fileio_read((GF_FileIO *)stream, ptr, (u32) nbytes);
1835
0
  }
1836
2
  if (!stream) return 0;
1837
2
  result = fread(ptr, 1, nbytes, stream);
1838
2
  return result;
1839
2
}
1840
1841
GF_EXPORT
1842
char *gf_fgets(char *ptr, size_t size, FILE *stream)
1843
8.44k
{
1844
8.44k
  if (gf_fileio_check(stream)) {
1845
0
    GF_FileIO *fio = (GF_FileIO *)stream;
1846
0
    if (fio->gets)
1847
0
      return fio->gets(fio, ptr, (u32) size);
1848
1849
0
    u32 i, read, nb_read=0;
1850
0
    for (i=0; i<size-1; i++) {
1851
0
      u8 buf[1];
1852
0
      read = (u32) gf_fileio_read(fio, buf, 1);
1853
0
      if (!read) break;
1854
1855
0
      ptr[nb_read] = buf[0];
1856
0
      nb_read++;
1857
0
      if (buf[0]=='\n') break;
1858
0
    }
1859
0
    ptr[nb_read]=0;
1860
0
    if (!nb_read) return NULL;
1861
0
    return ptr;
1862
0
  }
1863
8.44k
  return fgets(ptr, (u32) size, stream);
1864
8.44k
}
1865
1866
GF_EXPORT
1867
int gf_fgetc(FILE *stream)
1868
0
{
1869
0
  if (gf_fileio_check(stream)) {
1870
0
    GF_FileIO *fio = (GF_FileIO *)stream;
1871
0
    u8 buf[1];
1872
0
    u32 read = gf_fileio_read(fio, buf, 1);
1873
0
    if (!read) return -1;
1874
0
    return buf[0];
1875
0
  }
1876
0
  return fgetc(stream);
1877
0
}
1878
1879
GF_EXPORT
1880
int gf_fputc(int val, FILE *stream)
1881
0
{
1882
0
  if (gf_fileio_check(stream)) {
1883
0
    GF_FileIO *fio = (GF_FileIO *)stream;
1884
0
    u32 write;
1885
0
    u8 buf[1];
1886
0
    buf[0] = val;
1887
0
    write = gf_fileio_write(fio, buf, 1);
1888
0
    if (!write) return -1;
1889
0
    return buf[0];
1890
0
  }
1891
0
  return fputc(val, stream);
1892
0
}
1893
1894
GF_EXPORT
1895
int gf_fputs(const char *buf, FILE *stream)
1896
0
{
1897
0
  if (gf_fileio_check(stream)) {
1898
0
    GF_FileIO *fio = (GF_FileIO *)stream;
1899
0
    u32 write, len = (u32) strlen(buf);
1900
0
    write = gf_fileio_write(fio, (u8 *) buf, len);
1901
0
    if (write != len) return -1;
1902
0
    return write;
1903
0
  }
1904
0
  return fputs(buf, stream);
1905
0
}
1906
1907
GF_EXPORT
1908
int gf_fprintf(FILE *stream, const char *format, ...)
1909
0
{
1910
0
  int res;
1911
0
  va_list args;
1912
0
  va_start(args, format);
1913
0
  if (gf_fileio_check(stream)) {
1914
0
    res = gf_fileio_printf((GF_FileIO *)stream, format, args);
1915
0
  } else {
1916
0
    res = vfprintf(stream, format, args);
1917
0
  }
1918
0
  va_end(args);
1919
0
  return res;
1920
0
}
1921
1922
GF_EXPORT
1923
int gf_vfprintf(FILE *stream, const char *format, va_list args)
1924
0
{
1925
0
  int res;
1926
0
  if (gf_fileio_check(stream)) {
1927
0
    res = gf_fileio_printf((GF_FileIO *)stream, format, args);
1928
0
  } else {
1929
0
    res = vfprintf(stream, format, args);
1930
0
  }
1931
0
  return res;
1932
0
}
1933
1934
GF_EXPORT
1935
int gf_fflush(FILE *stream)
1936
37.7k
{
1937
37.7k
  if (gf_fileio_check(stream)) {
1938
0
    return gf_fileio_flush((GF_FileIO *)stream);
1939
0
  }
1940
37.7k
  return fflush(stream);
1941
37.7k
}
1942
1943
GF_EXPORT
1944
int gf_feof(FILE *stream)
1945
0
{
1946
0
  if (!stream) return 1;
1947
1948
0
  if (gf_fileio_check(stream)) {
1949
0
    return gf_fileio_eof((GF_FileIO *)stream) ? 1 : 0;
1950
0
  }
1951
0
  return feof(stream);
1952
0
}
1953
1954
1955
GF_EXPORT
1956
int gf_ferror(FILE *stream)
1957
0
{
1958
0
  if (gf_fileio_check(stream)) {
1959
0
    return 0;
1960
0
  }
1961
0
  return ferror(stream);
1962
0
}
1963
1964
GF_EXPORT
1965
u64 gf_fsize(FILE *fp)
1966
2
{
1967
2
  u64 size;
1968
1969
2
  if (gf_fileio_check(fp)) {
1970
0
    GF_FileIO *gfio = (GF_FileIO *)fp;
1971
0
    if (gfio->file_size_plus_one) {
1972
0
      gf_fseek(fp, 0, SEEK_SET);
1973
0
      return gfio->file_size_plus_one - 1;
1974
0
    }
1975
    //fall through
1976
0
  }
1977
2
  gf_fseek(fp, 0, SEEK_END);
1978
2
  size = gf_ftell(fp);
1979
2
  gf_fseek(fp, 0, SEEK_SET);
1980
2
  return size;
1981
2
}
1982
1983
1984
GF_EXPORT
1985
u64 gf_fd_fsize(int fd)
1986
2.81k
{
1987
2.81k
  u64 size=0;
1988
2.81k
#ifdef GPAC_HAS_FD
1989
1990
2.81k
  if (fd >= 0) {
1991
1992
#if defined(WIN32)
1993
    struct _stat64  sb;
1994
    _fstat64(fd, &sb);
1995
#else
1996
2.81k
    struct stat sb;
1997
2.81k
    fstat(fd, &sb);
1998
2.81k
#endif
1999
2.81k
    size = (u64) sb.st_size;
2000
2.81k
  }
2001
2002
2.81k
#endif
2003
2.81k
  return size;
2004
2.81k
}
2005
2006
2007
2008
/**
2009
  * Returns a pointer to the start of a filepath basename
2010
 **/
2011
GF_EXPORT
2012
char* gf_file_basename(const char* filename)
2013
0
{
2014
0
  char* lastPathPart = NULL;
2015
0
  if (filename) {
2016
0
    lastPathPart = strrchr(filename , GF_PATH_SEPARATOR);
2017
#if GF_PATH_SEPARATOR != '/'
2018
    // windows paths can mix slashes and backslashes
2019
    // so we search for the last slash that occurs after the last backslash
2020
    // if it occurs before it's not relevant
2021
    // if there's no backslashes we search in the whole file path
2022
2023
    char* trailingSlash = strrchr(lastPathPart?lastPathPart:filename, '/');
2024
    if (trailingSlash)
2025
      lastPathPart = trailingSlash;
2026
#endif
2027
0
    if (!lastPathPart)
2028
0
      lastPathPart = (char *)filename;
2029
0
    else
2030
0
      lastPathPart++;
2031
0
  }
2032
0
  return lastPathPart;
2033
0
}
2034
2035
/**
2036
  * Returns a pointer to the start of a filepath extension or null
2037
 **/
2038
GF_EXPORT
2039
char* gf_file_ext_start(const char* filename)
2040
0
{
2041
0
  char* basename;
2042
2043
0
  if (filename && !strncmp(filename, "gfio://", 7)) {
2044
0
    GF_FileIO *gfio = gf_fileio_from_url(filename);
2045
0
    filename = gf_fileio_resource_url(gfio);
2046
0
  }
2047
0
  basename = gf_file_basename(filename);
2048
2049
0
  if (basename) {
2050
0
    char *ext = strrchr(basename, '.');
2051
0
    if (!ext) return NULL;
2052
0
    if (!strcmp(ext, ".gz")) {
2053
0
      ext[0] = 0;
2054
0
      char *ext2 = strrchr(basename, '.');
2055
0
      ext[0] = '.';
2056
0
      if (ext2) return ext2;
2057
0
    }
2058
    //consider that if we have a space after a dot and before any common separator, we have no file extension
2059
0
    u32 i;
2060
0
    for (i=1; ext[i] ; i++) {
2061
0
      if ((ext[i]==':') || (ext[i]=='@') || (ext[i]=='#') || (ext[i]=='?')) break;
2062
0
      if (ext[i]==' ') return NULL;
2063
0
    }
2064
0
    return ext;
2065
0
  }
2066
0
  return NULL;
2067
0
}
2068
2069
2070
GF_EXPORT
2071
char* gf_url_colon_suffix(const char *path, char assign_sep)
2072
7.67k
{
2073
7.67k
  char *sep = strchr(path, ':');
2074
7.67k
  if (!sep) return NULL;
2075
2076
  //handle Z:\ and Z:/
2077
4.85k
  if ((path[1]==':') && ( (path[2]=='/') || (path[2]=='\\') ) )
2078
0
    return gf_url_colon_suffix(path+2, assign_sep);
2079
2080
4.85k
  if (!strncmp(path, "gfio://", 7) || !strncmp(path, "gmem://", 7)) {
2081
0
    return strchr(path+7, ':');
2082
0
  }
2083
2084
  //handle "\\foo\Z:\bar"
2085
4.85k
  if ((path[0] == '\\') && (path[1] == '\\')) {
2086
0
    char *next = strchr(path+2, '\\');
2087
0
    if (next) next = strchr(next + 1, '\\');
2088
0
    if (next)
2089
0
      return gf_url_colon_suffix(next + 1, assign_sep);
2090
0
  }
2091
2092
2093
  //handle PROTO://ADD:PORT/
2094
4.85k
  if ((sep[1]=='/') && (sep[2]=='/')) {
2095
4.85k
    char *next_colon, *next_slash, *userpass;
2096
4.85k
    sep++;
2097
    //skip all // (eg PROTO://////////////mytest/)
2098
14.5k
    while (sep[0]=='/')
2099
9.70k
      sep++;
2100
4.85k
    if (!sep[0]) return NULL;
2101
2102
    /*we may now have C:\ or C:/  (eg file://///C:\crazy\ or  file://///C:/crazy/)
2103
      if sep[1]==':', then sep[2] is valid (0 or something else), no need to check for len
2104
    */
2105
4.85k
    if ((sep[1]==':') && ( (sep[2]=='/') || (sep[2]=='\\') ) ) {
2106
0
      return gf_url_colon_suffix(sep+2, assign_sep);
2107
0
    }
2108
    //find closest : or /, if : is before / consider this is a port or an IPv6 address and check next : after /
2109
4.85k
    next_colon = strchr(sep, ':');
2110
4.85k
    next_slash = strchr(sep, '/');
2111
4.85k
    userpass = strchr(sep, '@');
2112
    //if ':' is before '@' with '@' before next '/', consider this is `user:pass@SERVER`
2113
4.85k
    if (userpass && next_colon && next_slash && (userpass<next_slash) && (userpass>next_colon))
2114
0
      next_colon = strchr(userpass, ':');
2115
2116
4.85k
    if (next_colon && next_slash && ((next_slash - sep) > (next_colon - sep)) ) {
2117
4.85k
      const char *last_colon;
2118
4.85k
      u32 i, port, nb_colons=0, nb_dots=0, nb_non_alnums=0;
2119
4.85k
      next_slash[0] = 0;
2120
4.85k
      last_colon = strrchr(next_colon, ':');
2121
4.85k
      port = atoi(last_colon+1);
2122
24.2k
      for (i=0; i<strlen(next_colon+1); i++) {
2123
19.4k
        if (next_colon[i+1] == ':') nb_colons++;
2124
19.4k
        else if (next_colon[i+1] == '.') nb_dots++;
2125
        //closing bracket of IPv6
2126
19.4k
        else if (next_colon[i+1] == ']') {}
2127
19.4k
        else if (!isalnum(next_colon[i+1])) {
2128
0
          nb_non_alnums++;
2129
0
          break;
2130
0
        }
2131
19.4k
      }
2132
4.85k
      next_slash[0] = '/';
2133
      //if no non-alphanum, we must have either a port (ipv4) or extra colons but no dots (ipv6)
2134
4.85k
      if (!nb_non_alnums && (port || (nb_colons && !nb_dots)))
2135
4.85k
        next_colon = strchr(next_slash, ':');
2136
4.85k
    }
2137
4.85k
    return next_colon;
2138
4.85k
  }
2139
2140
0
  if (sep && assign_sep) {
2141
0
    char *file_ext = strchr(path, '.');
2142
0
    char *assign = strchr(path, assign_sep);
2143
0
    if (assign && assign>file_ext) assign = NULL;
2144
0
    if (assign) file_ext = NULL;
2145
0
    if (file_ext && (file_ext>sep)) {
2146
0
      sep = strchr(file_ext, ':');
2147
0
    }
2148
0
    if (assign && (strlen(assign) > 4)) {
2149
0
      if ((assign[2] == ':') && ((assign[3] == '\\') || (assign[3] == '/'))) {
2150
0
        return gf_url_colon_suffix(assign + 1, 0);
2151
0
      }
2152
0
      if ((assign[1] == '\\') && (assign[2] == '\\')) {
2153
0
        char *next = strchr(assign + 3, '\\');
2154
0
        if (next) next = strchr(next+1, '\\');
2155
0
        if (next && (next>sep))
2156
0
          return gf_url_colon_suffix(next, 0);
2157
0
      }
2158
0
    }
2159
0
  }
2160
0
  return sep;
2161
0
}