Coverage Report

Created: 2025-10-12 06:50

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-2025
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
0
{
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
0
  int res = mkdir(DirPathName, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
128
0
  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
0
#endif
138
0
  return GF_OK;
139
0
}
140
141
142
GF_EXPORT
143
Bool gf_dir_exists(const char* DirPathName)
144
0
{
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
0
  struct stat sb;
161
0
  if (stat(DirPathName, &sb) == 0 && S_ISDIR(sb.st_mode)) {
162
0
    return GF_TRUE;
163
0
  }
164
0
  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
0
}
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
0
{
244
0
  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
0
  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
0
  return ( remove(fileName) == 0) ? GF_OK : GF_IO_ERR;
270
0
#endif
271
0
}
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
0
{
546
0
  if (is_temp_file
547
#ifdef GPAC_MEMORY_TRACKING
548
    || gf_mem_track_enabled
549
#endif
550
0
  ) {
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
0
  gpac_file_handles++;
568
0
}
569
570
static Bool gf_unregister_file_handle(FILE *ptr)
571
0
{
572
0
  u32 i, count;
573
0
  Bool res = GF_FALSE;
574
0
  if (gpac_file_handles)
575
0
    gpac_file_handles--;
576
577
0
  if (!gpac_open_files)
578
0
    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
0
}
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
0
{
706
#ifdef WIN32
707
  wchar_t item_path[GF_MAX_PATH];
708
#else
709
0
  char item_path[GF_MAX_PATH];
710
0
#endif
711
0
  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
0
  char path[GF_MAX_PATH], *file;
726
0
#endif
727
728
#ifdef WIN32
729
  WIN32_FIND_DATAW FindData;
730
  HANDLE SearchH;
731
#else
732
0
  DIR *the_dir;
733
0
  struct dirent* the_file;
734
0
  struct stat st;
735
0
#endif
736
737
0
  if (!dir || !strlen(dir) || !enum_dir_fct) return GF_BAD_PARAM;
738
739
0
  if (filter && (!strcmp(filter, "*") || !filter[0])) filter=NULL;
740
741
0
  memset(&file_info, 0, sizeof(GF_FileEnumInfo) );
742
743
0
  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
0
  size_t dir_len = strlen(dir);
823
0
  if (dir_len < GF_ARRAY_LENGTH(path)) {
824
0
    strcpy(path, dir);
825
0
  }
826
0
  else {
827
0
    memcpy(path, dir, GF_ARRAY_LENGTH(path));
828
0
    path[ GF_ARRAY_LENGTH(path) - 1] = 0;
829
0
  }
830
0
  size_t path_len = strlen(path);
831
0
  if (path_len && path[path_len-1] != '/') strcat(path, "/");
832
0
#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
0
  the_dir = opendir(path);
850
0
  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
0
  the_file = readdir(the_dir);
855
0
  while (the_file) {
856
857
0
#endif
858
859
0
    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
0
    if (!strcmp(the_file->d_name, "..")) goto next;
870
0
    if (the_file->d_name[0] == '.') goto next;
871
0
#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
0
    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
0
      char ext[30];
906
0
      char *sep = strrchr(the_file->d_name, '.');
907
0
      char *found_ext;
908
0
      u32 ext_len;
909
0
      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
0
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
0
    the_file = readdir(the_dir);
1021
0
#endif
1022
0
  }
1023
0
#ifndef WIN32
1024
0
  closedir(the_dir);
1025
0
#endif
1026
0
  return GF_OK;
1027
0
}
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
2.39M
{
1063
2.39M
  GF_FileIO *fio = (GF_FileIO *)fp;
1064
2.39M
  if ((fp==stdin) || (fp==stderr) || (fp==stdout))
1065
2.39M
    return GF_FALSE;
1066
1067
0
  if (fio && !fio->_reserved_null && (fio->__this==fio))
1068
0
    return GF_TRUE;
1069
0
  return GF_FALSE;
1070
0
}
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
} GF_FileIOBlob;
1108
1109
static GF_FileIO *gfio_blob_open(GF_FileIO *fileio_ref, const char *url, const char *mode, GF_Err *out_error)
1110
0
{
1111
0
  GF_FileIOBlob *blob = gf_fileio_get_udta(fileio_ref);
1112
0
  if (!strcmp(mode, "close") || !strcmp(mode, "unref")) {
1113
0
    if (blob->nb_ref) blob->nb_ref--;
1114
0
    blob->pos = 0;
1115
0
    if (blob->nb_ref)
1116
0
      return NULL;
1117
1118
0
    gf_free(blob);
1119
0
    gf_fileio_del(fileio_ref);
1120
0
    return NULL;
1121
0
  }
1122
0
  if (!strcmp(mode, "ref")) {
1123
0
    blob->nb_ref++;
1124
0
    *out_error = GF_OK;
1125
0
    return NULL;
1126
0
  }
1127
0
  if (!strcmp(mode, "url")) {
1128
0
    *out_error = GF_BAD_PARAM;
1129
0
    return NULL;
1130
0
  }
1131
0
  if (!strcmp(mode, "probe")) {
1132
0
    u32 crc = gf_crc_32(url, (u32) strlen(url) );
1133
0
    *out_error = (crc==blob->url_crc) ? GF_OK : GF_URL_ERROR;
1134
0
    return NULL;
1135
0
  }
1136
0
  if (mode[0]!='r') {
1137
0
    *out_error = GF_BAD_PARAM;
1138
0
    return NULL;
1139
0
  }
1140
0
  blob->nb_ref++;
1141
0
  if (blob->nb_ref>2) {
1142
0
    *out_error = GF_BAD_PARAM;
1143
0
    return NULL;
1144
0
  }
1145
0
  *out_error = GF_OK;
1146
0
  return fileio_ref;
1147
0
}
1148
1149
static GF_Err gfio_blob_seek(GF_FileIO *fileio, u64 offset, s32 whence)
1150
0
{
1151
0
  GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
1152
0
  if (whence==SEEK_END) blob->pos = blob->size;
1153
0
  else if (whence==SEEK_SET) blob->pos = (u32) offset;
1154
0
  else {
1155
0
    if (blob->pos + offset > blob->size) return GF_BAD_PARAM;
1156
0
    blob->pos += (u32) offset;
1157
0
  }
1158
0
  return GF_OK;
1159
0
}
1160
static u32 gfio_blob_read(GF_FileIO *fileio, u8 *buffer, u32 bytes)
1161
0
{
1162
0
  GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
1163
0
  if (bytes + blob->pos > blob->size)
1164
0
    bytes = blob->size - blob->pos;
1165
0
  if (bytes) {
1166
0
    memcpy(buffer, blob->data+blob->pos, bytes);
1167
0
    blob->pos += bytes;
1168
0
  }
1169
0
  return bytes;
1170
0
}
1171
static s64 gfio_blob_tell(GF_FileIO *fileio)
1172
0
{
1173
0
  GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
1174
0
  return (s64) blob->pos;
1175
0
}
1176
static Bool gfio_blob_eof(GF_FileIO *fileio)
1177
0
{
1178
0
  GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
1179
0
  if (blob->pos==blob->size) return GF_TRUE;
1180
0
  return GF_FALSE;
1181
0
}
1182
1183
static char *gfio_blob_gets(GF_FileIO *fileio, char *ptr, u32 size)
1184
0
{
1185
0
  GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
1186
0
  char *buf = blob->data + blob->pos;
1187
0
  u32 len = blob->size - blob->pos;
1188
0
  if (!len) return NULL;
1189
1190
0
  char *next = memchr(buf, '\n', len);
1191
0
  if (next) {
1192
0
    len = (u32) (next - buf);
1193
0
    if (len + blob->pos<blob->size) len++;
1194
0
  }
1195
0
  if (len > size) len = size;
1196
0
  memcpy(ptr, blob->data+blob->pos, len);
1197
0
  blob->pos += len;
1198
0
  return ptr;
1199
0
}
1200
1201
GF_List *allocated_gfios = NULL;
1202
1203
GF_EXPORT
1204
GF_FileIO *gf_fileio_new(char *url, void *udta,
1205
  gfio_open_proc open,
1206
  gfio_seek_proc seek,
1207
  gfio_read_proc read,
1208
  gfio_write_proc write,
1209
  gfio_tell_proc tell,
1210
  gfio_eof_proc eof,
1211
  gfio_printf_proc printf)
1212
0
{
1213
0
  char szURL[100];
1214
0
  GF_FileIO *tmp;
1215
1216
0
  if (!open || !seek || !tell || !eof) return NULL;
1217
1218
0
  if (!write && !read) return NULL;
1219
0
  GF_SAFEALLOC(tmp, GF_FileIO);
1220
0
  if (!tmp) return NULL;
1221
1222
0
  tmp->open = open;
1223
0
  tmp->seek = seek;
1224
0
  tmp->write = write;
1225
0
  tmp->read = read;
1226
0
  tmp->tell = tell;
1227
0
  tmp->eof = eof;
1228
0
  tmp->printf = printf;
1229
1230
0
  tmp->udta = udta;
1231
0
  if (url)
1232
0
    tmp->res_url = gf_strdup(url);
1233
1234
  //add clock as cookie to avoid creating twice th same gfio url in case the above malloc returns
1235
  //the same adress as a previously allocated gfio
1236
0
  tmp->creation_time = gf_sys_clock();
1237
0
  sprintf(szURL, "gfio://%p&%u", tmp, tmp->creation_time);
1238
0
  tmp->url = gf_strdup(szURL);
1239
0
  tmp->__this = tmp;
1240
  //track all allocated gfios so that we don't attempt accessing a freed one !
1241
0
  gf_mx_p(logs_mx);
1242
0
  if (!allocated_gfios) allocated_gfios = gf_list_new();
1243
0
  gf_list_add(allocated_gfios, tmp);
1244
0
  gf_mx_v(logs_mx);
1245
1246
0
  return tmp;
1247
0
}
1248
1249
GF_EXPORT
1250
const char * gf_fileio_url(GF_FileIO *gfio)
1251
0
{
1252
0
  return gfio ? gfio->url : NULL;
1253
0
}
1254
1255
GF_EXPORT
1256
const char * gf_fileio_resource_url(GF_FileIO *gfio)
1257
0
{
1258
0
  return gfio ? gfio->res_url : NULL;
1259
0
}
1260
1261
GF_EXPORT
1262
void gf_fileio_del(GF_FileIO *gfio)
1263
0
{
1264
0
  if (!gfio) return;
1265
0
  gf_mx_p(logs_mx);
1266
0
  if (gf_list_del_item(allocated_gfios, gfio)<0) {
1267
0
    gf_mx_v(logs_mx);
1268
0
    return;
1269
0
  }
1270
0
  if (!gf_list_count(allocated_gfios)) {
1271
0
    gf_list_del(allocated_gfios);
1272
0
    allocated_gfios = NULL;
1273
0
  }
1274
0
  gf_mx_v(logs_mx);
1275
1276
0
  if (gfio->url) gf_free(gfio->url);
1277
0
  if (gfio->res_url) gf_free(gfio->res_url);
1278
0
  if (gfio->printf_buf) gf_free(gfio->printf_buf);
1279
0
  gf_free(gfio);
1280
1281
1282
0
}
1283
1284
GF_EXPORT
1285
void *gf_fileio_get_udta(GF_FileIO *gfio)
1286
0
{
1287
0
  return gfio ? gfio->udta : NULL;
1288
0
}
1289
1290
GF_EXPORT
1291
GF_FileIO *gf_fileio_open_url(GF_FileIO *gfio_ref, const char *url, const char *mode, GF_Err *out_err)
1292
0
{
1293
0
  if (!gfio_ref) {
1294
0
    *out_err = GF_BAD_PARAM;
1295
0
    return NULL;
1296
0
  }
1297
0
  if (!gfio_ref->open) {
1298
0
    *out_err = url ? GF_NOT_SUPPORTED : GF_OK;
1299
0
    return NULL;
1300
0
  }
1301
0
  return gfio_ref->open(gfio_ref, url, mode, out_err);
1302
0
}
1303
1304
static u32 gf_fileio_read(GF_FileIO *gfio, u8 *buffer, u32 nb_bytes)
1305
0
{
1306
0
  if (!gfio) return GF_BAD_PARAM;
1307
0
  if (gfio->read)
1308
0
    return gfio->read(gfio, buffer, nb_bytes);
1309
0
  return 0;
1310
0
}
1311
1312
static u32 gf_fileio_write(GF_FileIO *gfio, u8 *buffer, u32 nb_bytes)
1313
0
{
1314
0
  if (!gfio) return GF_BAD_PARAM;
1315
0
  if (!gfio->write) return 0;
1316
0
  return gfio->write(gfio, buffer, (u32) nb_bytes);
1317
0
}
1318
1319
int gf_fileio_printf(GF_FileIO *gfio, const char *format, va_list args)
1320
0
{
1321
0
  va_list args_copy;
1322
0
  if (!gfio) return -1;
1323
0
  if (gfio->printf) return gfio->printf(gfio, format, args);
1324
1325
0
  if (!gfio->write) return -1;
1326
1327
0
  va_copy(args_copy, args);
1328
0
  u32 len=vsnprintf(NULL, 0, format, args_copy);
1329
0
  va_end(args_copy);
1330
1331
0
  if (len>=gfio->printf_alloc) {
1332
0
    gfio->printf_alloc = len+1;
1333
0
    gfio->printf_buf = gf_realloc(gfio->printf_buf, gfio->printf_alloc);
1334
0
  }
1335
0
  vsnprintf(gfio->printf_buf, len, format, args);
1336
0
  gfio->printf_buf[len] = 0;
1337
0
  return gfio->write(gfio, gfio->printf_buf, len+1);
1338
0
}
1339
1340
GF_EXPORT
1341
Bool gf_fileio_write_mode(GF_FileIO *gfio)
1342
0
{
1343
0
  return (gfio && gfio->write) ? GF_TRUE : GF_FALSE;
1344
0
}
1345
1346
GF_EXPORT
1347
Bool gf_fileio_read_mode(GF_FileIO *gfio)
1348
0
{
1349
0
  return (gfio && gfio->read) ? GF_TRUE : GF_FALSE;
1350
0
}
1351
1352
GF_EXPORT
1353
GF_FileIO *gf_fileio_from_url(const char *url)
1354
0
{
1355
0
  char szURL[100];
1356
0
  u32 cookie=0;
1357
0
  GF_FileIO *ptr=NULL;
1358
0
  if (!url) return NULL;
1359
0
  if (strncmp(url, "gfio://", 7)) return NULL;
1360
1361
0
  sscanf(url, "gfio://%p&%u", &ptr, &cookie);
1362
0
  sprintf(szURL, "gfio://%p&%u", ptr, cookie);
1363
0
  if (strcmp(url, szURL))
1364
0
    return NULL;
1365
1366
0
  gf_mx_p(logs_mx);
1367
0
  if (gf_list_find(allocated_gfios, ptr)<0) {
1368
0
    gf_mx_v(logs_mx);
1369
0
    return NULL;
1370
0
  }
1371
0
  gf_mx_v(logs_mx);
1372
1373
0
  if (ptr && ptr->url && !strcmp(ptr->url, url) && (ptr->creation_time==cookie) ) {
1374
0
    return ptr;
1375
0
  }
1376
0
  return NULL;
1377
0
}
1378
1379
GF_EXPORT
1380
const char * gf_fileio_translate_url(const char *url)
1381
0
{
1382
0
  GF_FileIO *gfio = gf_fileio_from_url(url);
1383
0
  return gfio ? gfio->res_url : NULL;
1384
0
}
1385
1386
Bool gf_fileio_eof(GF_FileIO *fileio)
1387
0
{
1388
0
  if (!fileio || !fileio->eof) return GF_TRUE;
1389
0
  return fileio->eof(fileio);
1390
0
}
1391
1392
int gf_fileio_flush(GF_FileIO *fileio)
1393
0
{
1394
0
  if (!fileio || !fileio->write) return 0;
1395
0
  fileio->write(fileio, NULL, 0);
1396
0
  return 0;
1397
0
}
1398
1399
GF_EXPORT
1400
const char *gf_fileio_factory(GF_FileIO *gfio, const char *new_res_url)
1401
0
{
1402
0
  GF_Err e;
1403
0
  if (!gfio || !gfio->open) return NULL;
1404
0
  GF_FileIO *new_res = gfio->open(gfio, new_res_url, "url", &e);
1405
0
  if (e) return NULL;
1406
0
  return gf_fileio_url(new_res);
1407
0
}
1408
1409
GF_EXPORT
1410
void gf_fileio_set_stats(GF_FileIO *gfio, u64 bytes_done, u64 file_size, GF_FileIOCacheState cache_state, u32 bytes_per_sec)
1411
0
{
1412
0
  if (!gfio) return;
1413
0
  gfio->bytes_done = bytes_done;
1414
0
  gfio->file_size_plus_one = file_size ? (1 + file_size) : 0;
1415
0
  gfio->cache_state = cache_state;
1416
0
  gfio->bytes_per_sec = bytes_per_sec;
1417
0
}
1418
1419
GF_EXPORT
1420
void gf_fileio_set_write_state(GF_FileIO *gfio, GF_FileIOWriteState write_state)
1421
0
{
1422
0
  if (!gfio) return;
1423
0
  gfio->write_state = write_state;
1424
0
}
1425
1426
GF_EXPORT
1427
Bool gf_fileio_get_stats(GF_FileIO *gfio, u64 *bytes_done, u64 *file_size, GF_FileIOCacheState *cache_state, u32 *bytes_per_sec)
1428
0
{
1429
0
  if (!gf_fileio_check((FILE *)gfio))
1430
0
    return GF_FALSE;
1431
1432
0
  if (bytes_done) *bytes_done = gfio->bytes_done;
1433
0
  if (file_size) *file_size = gfio->file_size_plus_one ? gfio->file_size_plus_one-1 : 0;
1434
0
  if (cache_state) *cache_state = gfio->cache_state;
1435
0
  if (bytes_per_sec) *bytes_per_sec = gfio->bytes_per_sec;
1436
0
  return GF_TRUE;
1437
0
}
1438
1439
GF_EXPORT
1440
GF_Err gf_fileio_tag_main_thread(GF_FileIO *fileio)
1441
0
{
1442
0
  if (!fileio) return GF_BAD_PARAM;
1443
0
  fileio->main_th = GF_TRUE;
1444
0
  return GF_OK;
1445
0
}
1446
1447
GF_EXPORT
1448
Bool gf_fileio_is_main_thread(const char *url)
1449
0
{
1450
0
  GF_FileIO *gfio = gf_fileio_from_url(url);
1451
0
  if (!gfio) return GF_FALSE;
1452
0
  return gfio->main_th;
1453
0
}
1454
1455
1456
1457
GF_EXPORT
1458
u64 gf_ftell(FILE *fp)
1459
0
{
1460
0
  if (!fp) return -1;
1461
1462
0
  if (gf_fileio_check(fp)) {
1463
0
    GF_FileIO *gfio = (GF_FileIO *)fp;
1464
0
    if (!gfio->tell) return -1;
1465
0
    return gfio->tell(gfio);
1466
0
  }
1467
1468
#if defined(_WIN32_WCE)
1469
  return (u64) ftell(fp);
1470
#elif defined(GPAC_CONFIG_WIN32) && (defined(__CYGWIN__) || defined(__MINGW32__))
1471
#if (_FILE_OFFSET_BITS >= 64)
1472
  return (u64) ftello64(fp);
1473
#else
1474
  return (u64) ftell(fp);
1475
#endif
1476
#elif defined(WIN32)
1477
  return (u64) _ftelli64(fp);
1478
#elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID)
1479
0
  return (u64) ftello64(fp);
1480
#elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
1481
  return (u64) ftello(fp);
1482
#else
1483
  return (u64) ftell(fp);
1484
#endif
1485
0
}
1486
1487
GF_EXPORT
1488
s32 gf_fseek(FILE *fp, s64 offset, s32 whence)
1489
0
{
1490
0
  if (!fp) return -1;
1491
0
  if (gf_fileio_check(fp)) {
1492
0
    GF_FileIO *gfio = (GF_FileIO *)fp;
1493
0
    if (gfio->seek) {
1494
0
      GF_Err e = gfio->seek(gfio, offset, whence);
1495
0
      if (e) return -1;
1496
0
      return 0;
1497
0
    }
1498
0
    return -1;
1499
0
  }
1500
1501
#if defined(_WIN32_WCE)
1502
  return (u64) fseek(fp, (s32) offset, whence);
1503
#elif defined(GPAC_CONFIG_WIN32) && defined(__CYGWIN__) /* mingw or cygwin */
1504
#if (_FILE_OFFSET_BITS >= 64)
1505
  return (u64) fseeko64(fp, offset, whence);
1506
#else
1507
  return (u64) fseek(fp, (s32) offset, whence);
1508
#endif
1509
#elif defined(WIN32)
1510
  return (u64) _fseeki64(fp, offset, whence);
1511
#elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID)
1512
0
  return fseeko64(fp, (off64_t) offset, whence);
1513
#elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
1514
  return fseeko(fp, (off_t) offset, whence);
1515
#else
1516
  return fseek(fp, (s32) offset, whence);
1517
#endif
1518
0
}
1519
1520
1521
static GF_FileIO *gf_fileio_from_blob(const char *file_name)
1522
0
{
1523
0
  u8 *blob_data;
1524
0
  u32 blob_size, flags;
1525
0
  GF_FileIOBlob *gfio_blob;
1526
0
  GF_Err e = gf_blob_get(file_name, &blob_data, &blob_size, &flags);
1527
0
  if (e || !blob_data) return NULL;
1528
0
  gf_blob_release(file_name);
1529
1530
0
  if (flags) {
1531
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Attempt at creating a GFIO object on blob corrupted or in transfer, not supported !"));
1532
0
    return NULL;
1533
0
  }
1534
1535
0
  GF_SAFEALLOC(gfio_blob, GF_FileIOBlob);
1536
0
  if (!gfio_blob) return NULL;
1537
0
  gfio_blob->data = blob_data;
1538
0
  gfio_blob->size = blob_size;
1539
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);
1540
0
  if (!res) {
1541
0
    gf_free(gfio_blob);
1542
0
    return NULL;
1543
0
  }
1544
0
  res->gets = gfio_blob_gets;
1545
0
  if (file_name)
1546
0
    gfio_blob->url_crc = gf_crc_32(file_name, (u32) strlen(file_name) );
1547
0
  return res;
1548
0
}
1549
1550
GF_EXPORT
1551
GF_FileIO *gf_fileio_from_mem(const char *URL, const u8 *data, u32 size)
1552
0
{
1553
0
  GF_FileIOBlob *gfio_blob;
1554
0
  GF_SAFEALLOC(gfio_blob, GF_FileIOBlob);
1555
0
  if (!gfio_blob) return NULL;
1556
0
  gfio_blob->data = (u8 *) data;
1557
0
  gfio_blob->size = size;
1558
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);
1559
0
  if (!res)  {
1560
0
    gf_free(gfio_blob);
1561
0
    return NULL;
1562
0
  }
1563
0
  res->gets = gfio_blob_gets;
1564
0
  if (URL)
1565
0
    gfio_blob->url_crc = gf_crc_32(URL, (u32) strlen(URL) );
1566
0
  gf_fopen(gf_fileio_url(res), "r");
1567
0
  return res;
1568
0
}
1569
1570
1571
#ifdef GPAC_CONFIG_EMSCRIPTEN
1572
static u32 mainloop_th_id = 0;
1573
void gf_set_mainloop_thread(u32 thread_id)
1574
{
1575
  mainloop_th_id = thread_id;
1576
}
1577
#endif
1578
1579
GF_EXPORT
1580
FILE *gf_fopen_ex(const char *file_name, const char *parent_name, const char *mode, Bool no_warn)
1581
0
{
1582
0
  FILE *res = NULL;
1583
0
  u32 gfio_type = 0;
1584
0
  Bool is_mkdir = GF_FALSE;
1585
1586
0
  if (!file_name || !mode) return NULL;
1587
0
  if (!strcmp(mode, "mkdir")) {
1588
0
    is_mkdir = GF_TRUE;
1589
0
    mode = "w";
1590
0
  }
1591
1592
0
  if (!strncmp(file_name, "gmem://", 7)) {
1593
0
    GF_FileIO *new_gfio;
1594
0
    if (strstr(mode, "w"))
1595
0
      return NULL;
1596
0
    new_gfio = gf_fileio_from_blob(file_name);
1597
0
    if (new_gfio)
1598
0
      gf_register_file_handle((char*)file_name, (FILE *) new_gfio, GF_FALSE);
1599
0
    return (FILE *) new_gfio;
1600
1601
0
  }
1602
1603
0
  if (!strncmp(file_name, "gfio://", 7))
1604
0
    gfio_type = 1;
1605
0
  else if (parent_name && !strncmp(parent_name, "gfio://", 7))
1606
0
    gfio_type = 2;
1607
1608
0
  if (gfio_type) {
1609
0
    GF_FileIO *gfio_ref;
1610
0
    GF_FileIO *new_gfio;
1611
0
    GF_Err e;
1612
0
    GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Open GFIO %s in mode %s\n", file_name, mode));
1613
1614
0
    if (gfio_type==1)
1615
0
      gfio_ref = gf_fileio_from_url(file_name);
1616
0
    else
1617
0
      gfio_ref = gf_fileio_from_url(parent_name);
1618
1619
0
    if (!gfio_ref) return NULL;
1620
0
    if (strchr(mode, 'r') && !gf_fileio_read_mode(gfio_ref)) {
1621
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));
1622
0
      return NULL;
1623
0
    }
1624
0
    if (strpbrk(mode, "wa") && !gf_fileio_write_mode(gfio_ref)) {
1625
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));
1626
0
      return NULL;
1627
0
    }
1628
0
    new_gfio = gf_fileio_open_url(gfio_ref, file_name, mode, &e);
1629
0
    if (e) {
1630
0
      if (!no_warn) {
1631
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)));
1632
0
      }
1633
0
      return NULL;
1634
0
    }
1635
0
    if (new_gfio)
1636
0
      gf_register_file_handle((char*)file_name, (FILE *) new_gfio, GF_FALSE);
1637
0
    return (FILE *) new_gfio;
1638
0
  }
1639
1640
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Open file %s in mode %s\n", file_name, mode));
1641
0
  if (strchr(mode, 'w')) {
1642
0
    char *fname = gf_strdup(file_name);
1643
0
    char *sep = strchr(fname, '/');
1644
0
    if (!sep) sep = strchr(fname, '\\');
1645
0
    if (file_name[0] == '/') sep = strchr(fname+1, '/');
1646
0
    else if (file_name[2] == '\\') sep = strchr(fname+3, '\\');
1647
1648
0
    while (sep) {
1649
0
      char *n_sep;
1650
0
      char c = sep[0];
1651
0
      sep[0] = 0;
1652
0
      if (!gf_dir_exists(fname)) {
1653
0
        GF_Err e = gf_mkdir(fname);
1654
0
        if (e != GF_OK) {
1655
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Failed to create directory \"%s\": %s\n", file_name, gf_error_to_string(e) ));
1656
0
          sep[0] = c;
1657
0
          gf_free(fname);
1658
0
          return NULL;
1659
0
        }
1660
0
      }
1661
0
      sep[0] = c;
1662
0
      n_sep = strchr(sep+1, '/');
1663
0
      if (!n_sep) n_sep = strchr(sep+1, '\\');
1664
0
      sep = n_sep;
1665
0
    }
1666
0
    gf_free(fname);
1667
0
    if (is_mkdir) return NULL;
1668
0
  }
1669
1670
#ifdef GPAC_CONFIG_EMSCRIPTEN
1671
  if (mainloop_th_id && strchr(mode, 'r') && (gf_th_id() != mainloop_th_id)) {
1672
    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"));
1673
  }
1674
#endif
1675
1676
#if defined(WIN32)
1677
  wchar_t *wname;
1678
  wchar_t *wmode;
1679
1680
  wname = gf_utf8_to_wcs(file_name);
1681
  wmode = gf_utf8_to_wcs(mode);
1682
  if (!wname || !wmode)
1683
  {
1684
    if (wname) gf_free(wname);
1685
    if (wmode) gf_free(wmode);
1686
    return NULL;
1687
  }
1688
  res = _wfsopen(wname, wmode, _SH_DENYNO);
1689
  gf_free(wname);
1690
  gf_free(wmode);
1691
#elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID)
1692
0
  res = fopen64(file_name, mode);
1693
#elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
1694
  res = fopen(file_name, mode);
1695
#else
1696
  res = fopen(file_name, mode);
1697
#endif
1698
1699
0
  if (res) {
1700
0
    if (!parent_name || strcmp(parent_name, "__temp_file"))
1701
0
      gf_register_file_handle((char*)file_name, res, GF_FALSE);
1702
1703
0
    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));
1704
0
  } else if (!no_warn) {
1705
0
    if (strpbrk(mode, "wa")) {
1706
#if defined(WIN32)
1707
      u32 err = GetLastError();
1708
      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));
1709
#else
1710
0
      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));
1711
0
#endif
1712
0
    }
1713
0
  }
1714
0
  return res;
1715
0
}
1716
1717
1718
GF_EXPORT
1719
s32 gf_fd_open(const char *file_name, u32 oflags, u32 uflags)
1720
30.2k
{
1721
30.2k
  if (!file_name) return -1;
1722
1723
#if defined(WIN32)
1724
  wchar_t *wname = gf_utf8_to_wcs(file_name);
1725
  if (!wname) return -1;
1726
  int res = _wopen(wname, oflags, uflags);
1727
  gf_free(wname);
1728
  return res;
1729
#elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID)
1730
30.2k
  return open(file_name, oflags, uflags);
1731
#elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
1732
  return open(file_name, oflags, uflags);
1733
#else
1734
  return open(file_name, oflags, uflags);
1735
#endif
1736
0
  return -1;
1737
30.2k
}
1738
1739
GF_EXPORT
1740
FILE *gf_fopen(const char *file_name, const char *mode)
1741
0
{
1742
0
  return gf_fopen_ex(file_name, NULL, mode, GF_FALSE);
1743
0
}
1744
1745
GF_EXPORT
1746
s32 gf_fclose(FILE *file)
1747
0
{
1748
0
  if (!file)
1749
0
    return 0;
1750
1751
0
  if (gf_unregister_file_handle(file))
1752
0
    return 0;
1753
0
  if (gf_fileio_check(file)) {
1754
0
    GF_Err e;
1755
0
    gf_fileio_open_url((GF_FileIO *) file, NULL, "close", &e);
1756
0
    if (e) return -1;
1757
0
    return 0;
1758
0
  }
1759
0
  return fclose(file);
1760
0
}
1761
1762
#if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! defined(_GNU_SOURCE) && !defined(WIN32)
1763
#define HAVE_STRERROR_R 1
1764
#endif
1765
1766
GF_EXPORT
1767
size_t gf_fwrite(const void *ptr, size_t nb_bytes, FILE *stream)
1768
0
{
1769
0
  size_t result=0;
1770
1771
0
  if (gf_fileio_check(stream)) {
1772
0
    return(size_t) gf_fileio_write((GF_FileIO *)stream, (u8 *) ptr, (u32) nb_bytes);
1773
0
  }
1774
1775
0
  if (ptr)
1776
0
    result = fwrite(ptr, 1, nb_bytes, stream);
1777
0
  if (result != nb_bytes) {
1778
#ifdef _WIN32_WCE
1779
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Error writing data: %d blocks to write but %d blocks written\n", nb_bytes, result));
1780
#else
1781
#if defined WIN32 && !defined(GPAC_CONFIG_WIN32)
1782
    errno_t errno_save;
1783
    _get_errno(&errno_save);
1784
#else
1785
0
    int errno_save = errno;
1786
0
#endif
1787
0
#ifdef HAVE_STRERROR_R
1788
0
#define ERRSTR_BUF_SIZE 256
1789
0
    char errstr[ERRSTR_BUF_SIZE];
1790
0
    if(strerror_r(errno_save, errstr, ERRSTR_BUF_SIZE) != 0)
1791
0
    {
1792
0
      strerror_r(0, errstr, ERRSTR_BUF_SIZE);
1793
0
    }
1794
#else
1795
    char *errstr = (char*)strerror(errno_save);
1796
#endif
1797
1798
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));
1799
0
#endif
1800
0
  }
1801
0
  return result;
1802
0
}
1803
1804
GF_EXPORT
1805
size_t gf_fread(void *ptr, size_t nbytes, FILE *stream)
1806
0
{
1807
0
  size_t result;
1808
0
  if (gf_fileio_check(stream)) {
1809
0
    return (size_t) gf_fileio_read((GF_FileIO *)stream, ptr, (u32) nbytes);
1810
0
  }
1811
0
  result = fread(ptr, 1, nbytes, stream);
1812
0
  return result;
1813
0
}
1814
1815
GF_EXPORT
1816
char *gf_fgets(char *ptr, size_t size, FILE *stream)
1817
0
{
1818
0
  if (gf_fileio_check(stream)) {
1819
0
    GF_FileIO *fio = (GF_FileIO *)stream;
1820
0
    if (fio->gets)
1821
0
      return fio->gets(fio, ptr, (u32) size);
1822
1823
0
    u32 i, read, nb_read=0;
1824
0
    for (i=0; i<size; i++) {
1825
0
      u8 buf[1];
1826
0
      read = (u32) gf_fileio_read(fio, buf, 1);
1827
0
      if (!read) break;
1828
1829
0
      ptr[nb_read] = buf[0];
1830
0
      nb_read++;
1831
0
      if (buf[0]=='\n') break;
1832
0
    }
1833
0
    if (!nb_read) return NULL;
1834
0
    return ptr;
1835
0
  }
1836
0
  return fgets(ptr, (u32) size, stream);
1837
0
}
1838
1839
GF_EXPORT
1840
int gf_fgetc(FILE *stream)
1841
0
{
1842
0
  if (gf_fileio_check(stream)) {
1843
0
    GF_FileIO *fio = (GF_FileIO *)stream;
1844
0
    u8 buf[1];
1845
0
    u32 read = gf_fileio_read(fio, buf, 1);
1846
0
    if (!read) return -1;
1847
0
    return buf[0];
1848
0
  }
1849
0
  return fgetc(stream);
1850
0
}
1851
1852
GF_EXPORT
1853
int gf_fputc(int val, FILE *stream)
1854
0
{
1855
0
  if (gf_fileio_check(stream)) {
1856
0
    GF_FileIO *fio = (GF_FileIO *)stream;
1857
0
    u32 write;
1858
0
    u8 buf[1];
1859
0
    buf[0] = val;
1860
0
    write = gf_fileio_write(fio, buf, 1);
1861
0
    if (!write) return -1;
1862
0
    return buf[0];
1863
0
  }
1864
0
  return fputc(val, stream);
1865
0
}
1866
1867
GF_EXPORT
1868
int gf_fputs(const char *buf, FILE *stream)
1869
0
{
1870
0
  if (gf_fileio_check(stream)) {
1871
0
    GF_FileIO *fio = (GF_FileIO *)stream;
1872
0
    u32 write, len = (u32) strlen(buf);
1873
0
    write = gf_fileio_write(fio, (u8 *) buf, len);
1874
0
    if (write != len) return -1;
1875
0
    return write;
1876
0
  }
1877
0
  return fputs(buf, stream);
1878
0
}
1879
1880
GF_EXPORT
1881
int gf_fprintf(FILE *stream, const char *format, ...)
1882
0
{
1883
0
  int res;
1884
0
  va_list args;
1885
0
  va_start(args, format);
1886
0
  if (gf_fileio_check(stream)) {
1887
0
    res = gf_fileio_printf((GF_FileIO *)stream, format, args);
1888
0
  } else {
1889
0
    res = vfprintf(stream, format, args);
1890
0
  }
1891
0
  va_end(args);
1892
0
  return res;
1893
0
}
1894
1895
GF_EXPORT
1896
int gf_vfprintf(FILE *stream, const char *format, va_list args)
1897
0
{
1898
0
  int res;
1899
0
  if (gf_fileio_check(stream)) {
1900
0
    res = gf_fileio_printf((GF_FileIO *)stream, format, args);
1901
0
  } else {
1902
0
    res = vfprintf(stream, format, args);
1903
0
  }
1904
0
  return res;
1905
0
}
1906
1907
GF_EXPORT
1908
int gf_fflush(FILE *stream)
1909
2.39M
{
1910
2.39M
  if (gf_fileio_check(stream)) {
1911
0
    return gf_fileio_flush((GF_FileIO *)stream);
1912
0
  }
1913
2.39M
  return fflush(stream);
1914
2.39M
}
1915
1916
GF_EXPORT
1917
int gf_feof(FILE *stream)
1918
0
{
1919
0
  if (!stream) return 1;
1920
1921
0
  if (gf_fileio_check(stream)) {
1922
0
    return gf_fileio_eof((GF_FileIO *)stream) ? 1 : 0;
1923
0
  }
1924
0
  return feof(stream);
1925
0
}
1926
1927
1928
GF_EXPORT
1929
int gf_ferror(FILE *stream)
1930
0
{
1931
0
  if (gf_fileio_check(stream)) {
1932
0
    return 0;
1933
0
  }
1934
0
  return ferror(stream);
1935
0
}
1936
1937
GF_EXPORT
1938
u64 gf_fsize(FILE *fp)
1939
0
{
1940
0
  u64 size;
1941
1942
0
  if (gf_fileio_check(fp)) {
1943
0
    GF_FileIO *gfio = (GF_FileIO *)fp;
1944
0
    if (gfio->file_size_plus_one) {
1945
0
      gf_fseek(fp, 0, SEEK_SET);
1946
0
      return gfio->file_size_plus_one - 1;
1947
0
    }
1948
    //fall through
1949
0
  }
1950
0
  gf_fseek(fp, 0, SEEK_END);
1951
0
  size = gf_ftell(fp);
1952
0
  gf_fseek(fp, 0, SEEK_SET);
1953
0
  return size;
1954
0
}
1955
1956
1957
GF_EXPORT
1958
u64 gf_fd_fsize(int fd)
1959
30.2k
{
1960
30.2k
  u64 size=0;
1961
30.2k
#ifdef GPAC_HAS_FD
1962
1963
30.2k
  if (fd >= 0) {
1964
1965
#if defined(WIN32)
1966
    struct _stat64  sb;
1967
    _fstat64(fd, &sb);
1968
#else
1969
30.2k
    struct stat sb;
1970
30.2k
    fstat(fd, &sb);
1971
30.2k
#endif
1972
30.2k
    size = (u64) sb.st_size;
1973
30.2k
  }
1974
1975
30.2k
#endif
1976
30.2k
  return size;
1977
30.2k
}
1978
1979
1980
1981
/**
1982
  * Returns a pointer to the start of a filepath basename
1983
 **/
1984
GF_EXPORT
1985
char* gf_file_basename(const char* filename)
1986
0
{
1987
0
  char* lastPathPart = NULL;
1988
0
  if (filename) {
1989
0
    lastPathPart = strrchr(filename , GF_PATH_SEPARATOR);
1990
#if GF_PATH_SEPARATOR != '/'
1991
    // windows paths can mix slashes and backslashes
1992
    // so we search for the last slash that occurs after the last backslash
1993
    // if it occurs before it's not relevant
1994
    // if there's no backslashes we search in the whole file path
1995
1996
    char* trailingSlash = strrchr(lastPathPart?lastPathPart:filename, '/');
1997
    if (trailingSlash)
1998
      lastPathPart = trailingSlash;
1999
#endif
2000
0
    if (!lastPathPart)
2001
0
      lastPathPart = (char *)filename;
2002
0
    else
2003
0
      lastPathPart++;
2004
0
  }
2005
0
  return lastPathPart;
2006
0
}
2007
2008
/**
2009
  * Returns a pointer to the start of a filepath extension or null
2010
 **/
2011
GF_EXPORT
2012
char* gf_file_ext_start(const char* filename)
2013
0
{
2014
0
  char* basename;
2015
2016
0
  if (filename && !strncmp(filename, "gfio://", 7)) {
2017
0
    GF_FileIO *gfio = gf_fileio_from_url(filename);
2018
0
    filename = gf_fileio_resource_url(gfio);
2019
0
  }
2020
0
  basename = gf_file_basename(filename);
2021
2022
0
  if (basename) {
2023
0
    char *ext = strrchr(basename, '.');
2024
0
    if (!ext) return NULL;
2025
0
    if (!strcmp(ext, ".gz")) {
2026
0
      ext[0] = 0;
2027
0
      char *ext2 = strrchr(basename, '.');
2028
0
      ext[0] = '.';
2029
0
      if (ext2) return ext2;
2030
0
    }
2031
    //consider that if we have a space after a dot and before any common separator, we have no file extension
2032
0
    u32 i;
2033
0
    for (i=1; ext[i] ; i++) {
2034
0
      if ((ext[i]==':') || (ext[i]=='@') || (ext[i]=='#') || (ext[i]=='?')) break;
2035
0
      if (ext[i]==' ') return NULL;
2036
0
    }
2037
0
    return ext;
2038
0
  }
2039
0
  return NULL;
2040
0
}
2041
2042
2043
GF_EXPORT
2044
char* gf_url_colon_suffix(const char *path, char assign_sep)
2045
0
{
2046
0
  char *sep = strchr(path, ':');
2047
0
  if (!sep) return NULL;
2048
2049
  //handle Z:\ and Z:/
2050
0
  if ((path[1]==':') && ( (path[2]=='/') || (path[2]=='\\') ) )
2051
0
    return gf_url_colon_suffix(path+2, assign_sep);
2052
2053
0
  if (!strncmp(path, "gfio://", 7) || !strncmp(path, "gmem://", 7)) {
2054
0
    return strchr(path+7, ':');
2055
0
  }
2056
2057
  //handle "\\foo\Z:\bar"
2058
0
  if ((path[0] == '\\') && (path[1] == '\\')) {
2059
0
    char *next = strchr(path+2, '\\');
2060
0
    if (next) next = strchr(next + 1, '\\');
2061
0
    if (next)
2062
0
      return gf_url_colon_suffix(next + 1, assign_sep);
2063
0
  }
2064
2065
2066
  //handle PROTO://ADD:PORT/
2067
0
  if ((sep[1]=='/') && (sep[2]=='/')) {
2068
0
    char *next_colon, *next_slash, *userpass;
2069
0
    sep++;
2070
    //skip all // (eg PROTO://////////////mytest/)
2071
0
    while (sep[0]=='/')
2072
0
      sep++;
2073
0
    if (!sep[0]) return NULL;
2074
2075
    /*we may now have C:\ or C:/  (eg file://///C:\crazy\ or  file://///C:/crazy/)
2076
      if sep[1]==':', then sep[2] is valid (0 or something else), no need to check for len
2077
    */
2078
0
    if ((sep[1]==':') && ( (sep[2]=='/') || (sep[2]=='\\') ) ) {
2079
0
      return gf_url_colon_suffix(sep+2, assign_sep);
2080
0
    }
2081
    //find closest : or /, if : is before / consider this is a port or an IPv6 address and check next : after /
2082
0
    next_colon = strchr(sep, ':');
2083
0
    next_slash = strchr(sep, '/');
2084
0
    userpass = strchr(sep, '@');
2085
    //if ':' is before '@' with '@' before next '/', consider this is `user:pass@SERVER`
2086
0
    if (userpass && next_colon && next_slash && (userpass<next_slash) && (userpass>next_colon))
2087
0
      next_colon = strchr(userpass, ':');
2088
2089
0
    if (next_colon && next_slash && ((next_slash - sep) > (next_colon - sep)) ) {
2090
0
      const char *last_colon;
2091
0
      u32 i, port, nb_colons=0, nb_dots=0, nb_non_alnums=0;
2092
0
      next_slash[0] = 0;
2093
0
      last_colon = strrchr(next_colon, ':');
2094
0
      port = atoi(last_colon+1);
2095
0
      for (i=0; i<strlen(next_colon+1); i++) {
2096
0
        if (next_colon[i+1] == ':') nb_colons++;
2097
0
        else if (next_colon[i+1] == '.') nb_dots++;
2098
        //closing bracket of IPv6
2099
0
        else if (next_colon[i+1] == ']') {}
2100
0
        else if (!isalnum(next_colon[i+1])) {
2101
0
          nb_non_alnums++;
2102
0
          break;
2103
0
        }
2104
0
      }
2105
0
      next_slash[0] = '/';
2106
      //if no non-alphanum, we must have either a port (ipv4) or extra colons but no dots (ipv6)
2107
0
      if (!nb_non_alnums && (port || (nb_colons && !nb_dots)))
2108
0
        next_colon = strchr(next_slash, ':');
2109
0
    }
2110
0
    return next_colon;
2111
0
  }
2112
2113
0
  if (sep && assign_sep) {
2114
0
    char *file_ext = strchr(path, '.');
2115
0
    char *assign = strchr(path, assign_sep);
2116
0
    if (assign && assign>file_ext) assign = NULL;
2117
0
    if (assign) file_ext = NULL;
2118
0
    if (file_ext && (file_ext>sep)) {
2119
0
      sep = strchr(file_ext, ':');
2120
0
    }
2121
0
    if (assign && (strlen(assign) > 4)) {
2122
0
      if ((assign[2] == ':') && ((assign[3] == '\\') || (assign[3] == '/'))) {
2123
0
        return gf_url_colon_suffix(assign + 1, 0);
2124
0
      }
2125
0
      if ((assign[1] == '\\') && (assign[2] == '\\')) {
2126
0
        char *next = strchr(assign + 3, '\\');
2127
0
        if (next) next = strchr(next+1, '\\');
2128
0
        if (next && (next>sep))
2129
0
          return gf_url_colon_suffix(next, 0);
2130
0
      }
2131
0
    }
2132
0
  }
2133
0
  return sep;
2134
0
}