Coverage Report

Created: 2026-10-01 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/utils/os_config_init.c
Line
Count
Source
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2000-2026
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/config_file.h>
27
28
29
#if defined(WIN32) || defined(_WIN32_WCE)
30
#include <windows.h> /*for GetModuleFileName*/
31
32
#ifndef _WIN32_WCE
33
#include <direct.h>  /*for _mkdir*/
34
#include <gpac/utf.h>
35
#include <shlobj.h>  /*for getting user-dir*/
36
37
#ifndef SHGFP_TYPE_CURRENT
38
#define SHGFP_TYPE_CURRENT 0 /*needed for MinGW*/
39
#endif
40
41
#endif
42
43
#define CFG_FILE_NAME "GPAC.cfg"
44
#define TEST_MODULE   "gm_"
45
46
#elif (defined(__DARWIN__) || defined(__APPLE__) )
47
#include <mach-o/dyld.h> /*for _NSGetExecutablePath */
48
49
#ifdef GPAC_CONFIG_IOS
50
#define TEST_MODULE     "gpac4ios"
51
#else
52
#define TEST_MODULE   "gm_"
53
#endif
54
#define CFG_FILE_NAME "GPAC.cfg"
55
56
#else
57
#ifdef GPAC_CONFIG_LINUX
58
#include <unistd.h>
59
#endif
60
61
3.05k
#define CFG_FILE_NAME "GPAC.cfg"
62
63
#if defined(GPAC_CONFIG_WIN32)
64
#define TEST_MODULE   "gm_"
65
#else
66
91.5k
#define TEST_MODULE   "gm_"
67
#endif
68
69
#endif
70
71
#if !defined(GPAC_STATIC_MODULES) && !defined(GPAC_MP4BOX_MINI)
72
73
static Bool mod_enum(void *cbck, char *item_name, char *item_path, GF_FileEnumInfo *file_info)
74
{
75
  if (!strncmp(item_name, "gm_", 3) || !strncmp(item_name, "gf_", 3)) {
76
    *(Bool*)cbck = GF_TRUE;
77
    return GF_TRUE;
78
  }
79
  return GF_FALSE;
80
}
81
#endif
82
83
static Bool check_file_exists(char *name, char *path, char outPath[GF_MAX_PATH])
84
119k
{
85
119k
  char szPath[GF_MAX_PATH];
86
119k
  FILE *f;
87
119k
  int concatres;
88
89
119k
  if (! gf_dir_exists(path)) return 0;
90
91
12.2k
  if (!strcmp(name, TEST_MODULE)) {
92
6.10k
    Bool res = GF_FALSE;
93
6.10k
#if defined(GPAC_STATIC_MODULES) || defined(GPAC_MP4BOX_MINI)
94
6.10k
    res = GF_TRUE;
95
#else
96
    gf_enum_directory(path, GF_FALSE, mod_enum, &res, NULL);
97
#endif
98
6.10k
    if (!res) return GF_FALSE;
99
6.10k
    if (outPath != path) gf_strlcpy(outPath, path, GF_MAX_PATH);
100
6.10k
    return 1;
101
6.10k
  }
102
103
6.11k
  concatres = snprintf(szPath, GF_MAX_PATH, "%s%c%s", path, GF_PATH_SEPARATOR, name);
104
6.11k
  if (concatres<0) {
105
0
    GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("Path too long (limit %d) when trying to concatenate %s and %s\n", GF_MAX_PATH, path, name));
106
0
  }
107
108
  //do not use gf_fopen here, we don't want to throw en error if failure
109
6.11k
  f = fopen(szPath, "rb");
110
6.11k
  if (!f) return GF_FALSE;
111
0
  fclose(f);
112
0
  if (outPath != path) gf_strlcpy(outPath, path, GF_MAX_PATH);
113
0
  return GF_TRUE;
114
6.11k
}
115
116
enum
117
{
118
  GF_PATH_APP,
119
  GF_PATH_CFG,
120
  //were we store gui/%, shaders/*, scripts/*
121
  GF_PATH_SHARE,
122
  GF_PATH_MODULES,
123
  GF_PATH_LIB
124
};
125
126
#if defined(WIN32) || defined(_WIN32_WCE)
127
static Bool get_default_install_path(char file_path[GF_MAX_PATH], u32 path_type)
128
{
129
  FILE *f;
130
  char szPath[GF_MAX_PATH];
131
  if (!file_path) return GF_FALSE;
132
133
#ifdef _WIN32_WCE
134
  TCHAR w_szPath[GF_MAX_PATH];
135
  GetModuleFileName(NULL, w_szPath, GF_MAX_PATH);
136
  CE_WideToChar((u16 *) w_szPath, file_path);
137
#else
138
  wchar_t wtmp_file_path[GF_MAX_PATH];
139
  char* tmp_file_path;
140
141
  GetModuleFileNameA(NULL, file_path, GF_MAX_PATH);
142
#endif
143
144
  /*remove exe name*/
145
  if (strstr(file_path, ".exe")) {
146
    char *sep = strrchr(file_path, '\\');
147
    if (sep) sep[0] = 0;
148
  }
149
150
  gf_strcpy(szPath, file_path);
151
  strlwr(szPath);
152
153
  /*if this is run from a browser, we do not get our app path - fortunately on Windows, we always use 'GPAC' in the
154
  installation path*/
155
  if (!strstr(file_path, "gpac") && !strstr(file_path, "GPAC") ) {
156
    HKEY hKey = NULL;
157
    DWORD dwSize = GF_MAX_PATH;
158
    file_path[0] = 0;
159
160
    /*locate the key in current user, then in local machine*/
161
#ifdef _WIN32_WCE
162
    DWORD dwType = REG_SZ;
163
    u16 w_path[1024];
164
    RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\GPAC"), 0, KEY_READ, &hKey);
165
#ifdef _DEBUG
166
    if (RegQueryValueEx(hKey, TEXT("DebugDir"), 0, &dwType, (LPBYTE) w_path, &dwSize) != ERROR_SUCCESS)
167
#endif
168
      RegQueryValueEx(hKey, TEXT("InstallDir"), 0, &dwType, (LPBYTE) w_path, &dwSize);
169
    CE_WideToChar(w_path, (char *)file_path);
170
    RegCloseKey(hKey);
171
#else
172
    if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\GPAC", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
173
      RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\GPAC", 0, KEY_READ, &hKey);
174
175
    dwSize = GF_MAX_PATH;
176
177
#ifdef _DEBUG
178
    if (RegQueryValueEx(hKey, "DebugDir", NULL, NULL,(unsigned char*) file_path, &dwSize) != ERROR_SUCCESS)
179
#endif
180
      RegQueryValueEx(hKey, "InstallDir", NULL, NULL,(unsigned char*) file_path, &dwSize);
181
182
    RegCloseKey(hKey);
183
#endif
184
  }
185
  //empty path, try DLL
186
  if (!file_path[0] && (path_type != GF_PATH_LIB)) {
187
    get_default_install_path(file_path, GF_PATH_LIB);
188
  }
189
190
  if (path_type==GF_PATH_APP) return GF_TRUE;
191
192
  if (path_type==GF_PATH_SHARE) {
193
    char *sep;
194
    gf_strlcat(file_path, "\\share", GF_MAX_PATH);
195
    if (check_file_exists("gui\\gui.bt", file_path, file_path)) return GF_TRUE;
196
    sep = strstr(file_path, "\\bin\\");
197
    if (sep) {
198
      sep[0] = 0;
199
      gf_strlcat(file_path, "\\share", GF_MAX_PATH);
200
      if (check_file_exists("gui\\gui.bt", file_path, file_path)) return GF_TRUE;
201
    }
202
    return GF_FALSE;
203
  }
204
  /*modules are stored in the GPAC directory (should be changed to GPAC/modules)*/
205
  if (path_type==GF_PATH_MODULES) return GF_TRUE;
206
207
  if (path_type == GF_PATH_LIB) {
208
    HMODULE hm=NULL;
209
    if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
210
      (LPCSTR)&get_default_install_path, &hm) == 0) {
211
      return 0;
212
    }
213
    if (GetModuleFileName(hm, file_path, GF_MAX_PATH) == 0) {
214
      return 0;
215
    }
216
    char *sep = strrchr(file_path, '\\');
217
    if (!sep) sep = strrchr(file_path, '/');
218
    if (sep) sep[0] = 0;
219
    return 1;
220
  }
221
222
  /*we are looking for the config file path - make sure it is writable*/
223
  gf_assert(path_type == GF_PATH_CFG);
224
225
  gf_strcpy(szPath, file_path);
226
  gf_strcat(szPath, "\\gpaccfgtest.txt");
227
  //do not use gf_fopen here, we don't want to through any error if failure
228
  f = fopen(szPath, "wb");
229
  if (f != NULL) {
230
    fclose(f);
231
    gf_file_delete(szPath);
232
    return GF_TRUE;
233
  }
234
#ifdef _WIN32_WCE
235
  return 0;
236
#else
237
  /*no write access, get user home directory*/
238
  SHGetSpecialFolderPathW(NULL, wtmp_file_path, CSIDL_APPDATA, 1);
239
  tmp_file_path = gf_wcs_to_utf8(wtmp_file_path);
240
  gf_strlcpy(file_path, tmp_file_path, GF_MAX_PATH);
241
  gf_free(tmp_file_path);
242
243
  if (file_path[strlen(file_path)-1] != '\\') gf_strlcat(file_path, "\\", GF_MAX_PATH);
244
  gf_strlcat(file_path, "GPAC", GF_MAX_PATH);
245
  /*create GPAC dir*/
246
  gf_mkdir(file_path);
247
  gf_strcpy(szPath, file_path);
248
  gf_strcat(szPath, "\\gpaccfgtest.txt");
249
  f = gf_fopen(szPath, "wb");
250
  /*COMPLETE FAILURE*/
251
  if (!f) return GF_FALSE;
252
253
  gf_fclose(f);
254
  gf_file_delete(szPath);
255
  return GF_TRUE;
256
#endif
257
}
258
259
#elif defined(GPAC_CONFIG_ANDROID)
260
static char android_app_data[512];
261
static char android_external_storage[512];
262
263
/*
264
  Called by GPAC JNI wrappers. If not set, default to app_data=/data/data/io.gpac.gpac and ext_storage=/sdcard
265
*/
266
GF_EXPORT
267
void gf_sys_set_android_paths(const char *app_data, const char *ext_storage)
268
{
269
  if (app_data && (strlen(app_data)<512)) {
270
    gf_strlcpy(android_app_data, app_data, 512);
271
  }
272
  if (ext_storage && (strlen(ext_storage)<512)) {
273
    gf_strlcpy(android_external_storage, ext_storage, 512);
274
  }
275
}
276
277
278
static Bool get_default_install_path(char file_path[GF_MAX_PATH], u32 path_type)
279
{
280
  if (!file_path) return 0;
281
282
  if (path_type==GF_PATH_APP) {
283
    gf_strlcpy(file_path, android_app_data[0] ? android_app_data : "/data/data/io.gpac.gpac", GF_MAX_PATH);
284
    return 1;
285
  } else if (path_type==GF_PATH_CFG) {
286
    const char *res = android_external_storage[0] ? android_external_storage : getenv("EXTERNAL_STORAGE");
287
    if (!res) res = "/sdcard";
288
    gf_strlcpy(file_path, res, GF_MAX_PATH);
289
    gf_strlcat(file_path, "/GPAC", GF_MAX_PATH);
290
    //GPAC folder exists in external storage, use profile from this location
291
    if (gf_dir_exists(file_path)) {
292
      return 1;
293
    }
294
    //otherwise use profile in app data store
295
    gf_strlcpy(file_path, android_app_data[0] ? android_app_data : "/data/data/io.gpac.gpac", GF_MAX_PATH);
296
    gf_strlcat(file_path, "/GPAC", GF_MAX_PATH);
297
    return 1;
298
  } else if (path_type==GF_PATH_SHARE) {
299
    if (!get_default_install_path(file_path, GF_PATH_APP))
300
      return 0;
301
    gf_strlcat(file_path, "/share", GF_MAX_PATH);
302
    return 1;
303
  } else if (path_type==GF_PATH_MODULES) {
304
    if (!get_default_install_path(file_path, GF_PATH_APP))
305
      return 0;
306
    gf_strlcat(file_path, "/lib", GF_MAX_PATH);
307
    return 1;
308
  }
309
  return 0;
310
}
311
312
313
#elif defined(__SYMBIAN__)
314
315
#if defined(__SERIES60_3X__)
316
#define SYMBIAN_GPAC_CFG_DIR  "\\private\\F01F9075"
317
#define SYMBIAN_GPAC_GUI_DIR  "\\private\\F01F9075\\gui"
318
#define SYMBIAN_GPAC_MODULES_DIR  "\\sys\\bin"
319
#else
320
#define SYMBIAN_GPAC_CFG_DIR  "\\system\\apps\\GPAC"
321
#define SYMBIAN_GPAC_GUI_DIR  "\\system\\apps\\GPAC\\gui"
322
#define SYMBIAN_GPAC_MODULES_DIR  GPAC_CFG_DIR
323
#endif
324
325
static Bool get_default_install_path(char file_path[GF_MAX_PATH], u32 path_type)
326
{
327
  if (path_type==GF_PATH_APP) gf_strlcpy(file_path, SYMBIAN_GPAC_MODULES_DIR, GF_MAX_PATH);
328
  else if (path_type==GF_PATH_CFG) gf_strlcpy(file_path, SYMBIAN_GPAC_CFG_DIR, GF_MAX_PATH);
329
  else if (path_type==GF_PATH_GUI) gf_strlcpy(file_path, SYMBIAN_GPAC_GUI_DIR, GF_MAX_PATH);
330
  else if (path_type==GF_PATH_MODULES) gf_strlcpy(file_path, SYMBIAN_GPAC_MODULES_DIR, GF_MAX_PATH);
331
  return 1;
332
}
333
334
/*Linux, OSX, iOS*/
335
#else
336
337
#include <errno.h>
338
339
#if defined(__FreeBSD__)
340
#include <sys/sysctl.h>
341
#endif
342
343
//dlinfo
344
#if defined(__DARWIN__) || defined(__APPLE__) || defined(__FreeBSD__)
345
#include <dlfcn.h>
346
347
typedef Dl_info _Dl_info;
348
#elif defined(GPAC_CONFIG_LINUX)
349
350
351
typedef struct
352
{
353
  const char *dli_fname;
354
  void *dli_fbase;
355
  const char *dli_sname;
356
  void *dli_saddr;
357
} _Dl_info;
358
int dladdr(void *, _Dl_info *);
359
360
#endif
361
362
static Bool get_default_install_path(char file_path[GF_MAX_PATH], u32 path_type)
363
42.7k
{
364
42.7k
  char app_path[GF_MAX_PATH];
365
42.7k
  char *sep;
366
#if (defined(__DARWIN__) || defined(__APPLE__))
367
  u32 size;
368
#endif
369
370
  /*on OSX, Linux & co, user home is where we store the cfg file*/
371
42.7k
  if (path_type==GF_PATH_CFG) {
372
373
#ifdef GPAC_CONFIG_EMSCRIPTEN
374
    if (gf_dir_exists("/idbfs")) {
375
      if (!gf_dir_exists("/idbfs/.gpac")) {
376
        gf_mkdir("/idbfs/.gpac");
377
      }
378
      gf_strlcpy(file_path, "/idbfs/.gpac", GF_MAX_PATH);
379
      return 1;
380
    }
381
#endif
382
383
6.11k
    char *user_home = getenv("HOME");
384
#ifdef GPAC_CONFIG_IOS
385
    char buf[PATH_MAX];
386
    char *res;
387
#endif
388
389
6.11k
    if (!user_home) {
390
0
      GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("Couldn't find HOME directory\n"));
391
0
      return 0;
392
0
    }
393
#ifdef GPAC_CONFIG_IOS
394
    res = realpath(user_home, buf);
395
    if (res) {
396
      gf_strlcpy(file_path, buf, GF_MAX_PATH);
397
      gf_strlcat(file_path, "/Documents", GF_MAX_PATH);
398
    } else
399
#endif
400
6.11k
      gf_strlcpy(file_path, user_home, GF_MAX_PATH);
401
402
6.11k
    if (file_path[strlen(file_path)-1] == '/') file_path[strlen(file_path)-1] = 0;
403
404
    //cleanup of old install in .gpacrc
405
6.11k
    if (check_file_exists(".gpacrc", file_path, file_path)) {
406
0
      gf_strcpy(app_path, file_path);
407
0
      gf_strcat(app_path, "/.gpacrc");
408
0
      gf_file_delete(app_path);
409
0
    }
410
411
6.11k
    gf_strlcat(file_path, "/.gpac", GF_MAX_PATH);
412
6.11k
    if (!gf_dir_exists(file_path)) {
413
1
      gf_mkdir(file_path);
414
1
    }
415
6.11k
    return 1;
416
6.11k
  }
417
418
36.6k
  if (path_type==GF_PATH_APP) {
419
#if (defined(__DARWIN__) || defined(__APPLE__) )
420
    size = GF_MAX_PATH-1;
421
    if (_NSGetExecutablePath(app_path, &size) ==0) {
422
      realpath(app_path, file_path);
423
      sep = strrchr(file_path, '/');
424
      if (sep) sep[0] = 0;
425
      return 1;
426
    }
427
428
#elif defined(__FreeBSD__)
429
    {
430
      int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
431
      size_t sz = GF_MAX_PATH;
432
      if (sysctl(mib, 4, file_path, &sz, NULL, 0) == 0) {
433
        sep = strrchr(file_path, '/');
434
        if (sep) sep[0] = 0;
435
        return 1;
436
      }
437
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("sysctl(KERN_PROC_PATHNAME) failed: %s\n", strerror(errno)));
438
      return 0;
439
    }
440
441
#elif defined(GPAC_CONFIG_LINUX)
442
    {
443
12.2k
      ssize_t ssize = readlink("/proc/self/exe", file_path, GF_MAX_PATH-1);
444
12.2k
      if (ssize > 0) {
445
12.2k
        file_path[ssize] = 0;
446
12.2k
        sep = strrchr(file_path, '/');
447
12.2k
        if (sep) sep[0] = 0;
448
12.2k
        return 1;
449
12.2k
      }
450
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot read /proc/self/exe: %s - is procfs mounted?\n", strerror(errno)));
451
0
      return 0;
452
12.2k
    }
453
454
#elif defined(GPAC_CONFIG_WIN32)
455
    GetModuleFileNameA(NULL, file_path, GF_MAX_PATH);
456
    if (strstr(file_path, ".exe")) {
457
      sep = strrchr(file_path, '\\');
458
      if (sep) sep[0] = 0;
459
      if ((file_path[1]==':') && (file_path[2]=='\\')) {
460
        gf_strlcpy(file_path, &file_path[2], GF_MAX_PATH);
461
      }
462
      sep = file_path;
463
      while ( sep[0] ) {
464
        if (sep[0]=='\\') sep[0]='/';
465
        sep++;
466
      }
467
      //get rid of /mingw32 or /mingw64
468
      sep = strstr(file_path, "/usr/");
469
      if (sep) {
470
        gf_strlcpy(file_path, sep, GF_MAX_PATH);
471
      }
472
      return 1;
473
    }
474
#elif defined(GPAC_CONFIG_EMSCRIPTEN)
475
    return 0;
476
#endif
477
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Unknown arch, cannot find executable path\n"));
478
0
    return 0;
479
12.2k
  }
480
481
24.4k
  if (path_type==GF_PATH_LIB) {
482
15.2k
#if defined(__DARWIN__) || defined(__APPLE__) || defined(GPAC_CONFIG_LINUX) || defined(__FreeBSD__)
483
15.2k
    _Dl_info dl_info;
484
15.2k
    dl_info.dli_fname = NULL;
485
15.2k
    if (dladdr((void *)get_default_install_path, &dl_info)
486
15.2k
      && dl_info.dli_fname
487
15.2k
    ) {
488
15.2k
      gf_strlcpy(file_path, dl_info.dli_fname, GF_MAX_PATH);
489
15.2k
      sep = strrchr(file_path, '/');
490
15.2k
      if (sep) sep[0] = 0;
491
15.2k
      return 1;
492
15.2k
    }
493
0
    return 0;
494
0
#endif
495
496
    //for emscripten we use a static load for now
497
0
#if !defined(GPAC_CONFIG_EMSCRIPTEN)
498
0
    GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("Unknown arch, cannot find library path\n"));
499
0
#endif
500
0
    return 0;
501
15.2k
  }
502
503
  /*check system install:
504
    - share is PREFIX/share/gpac/
505
    - mod_dir is PREFIX/LIBDIR/gpac/
506
  */
507
9.16k
  if ((path_type==GF_PATH_SHARE) || (path_type==GF_PATH_MODULES)) {
508
9.16k
    char lib_arch_scheme[100];
509
9.16k
    lib_arch_scheme[0] = 0;
510
#if defined(GPAC_CONFIG_EMSCRIPTEN)
511
    gf_strcpy(app_path, "/usr/");
512
#else
513
    /*locate libgpac path first in case we are not run using gpac apps (eg python bindings)
514
      if failure (static build) locate app
515
    */
516
9.16k
    if (!get_default_install_path(app_path, GF_PATH_LIB)) {
517
0
      if (!get_default_install_path(app_path, GF_PATH_APP)) {
518
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Couldn't find GPAC binaries install directory\n"));
519
0
        return 0;
520
0
      }
521
0
    }
522
9.16k
#endif
523
    //check if we are a lib path and have a .gpac/ here
524
9.16k
    if ((path_type==GF_PATH_MODULES) && strstr(app_path, "/lib")) {
525
6.10k
      gf_strcat(app_path, "/gpac");
526
6.10k
      if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
527
6.10k
      char *sep = strrchr(app_path, '/');
528
6.10k
      if (sep) sep[0]=0;
529
6.10k
    }
530
531
    //extract PREFIX
532
    //app_path is either PREFIX/lib or PREFIX/lib/arch-specific (shared lib) or PREFIX/bin (if app)
533
    //without trailing slash
534
9.16k
    char *root = strrchr(app_path, '/');
535
9.16k
    if (root) {
536
9.16k
      gf_strcpy(lib_arch_scheme, root+1);
537
9.16k
      root[0]=0;
538
9.16k
    }
539
    //check if we have a /lib/arch-specific scheme
540
9.16k
    char *lib = strrchr(app_path, '/');
541
9.16k
    if (lib && !strncmp(lib, "/lib/", 4)) {
542
0
      root = lib;
543
0
      root[0] = 0;
544
9.16k
    } else {
545
9.16k
      lib_arch_scheme[0]=0;
546
9.16k
    }
547
9.16k
    if ((path_type==GF_PATH_SHARE) && root) {
548
3.05k
      gf_strcat(app_path, "/share/gpac");
549
3.05k
      if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
550
551
      /*failsafe - look in possible install dirs ...*/
552
3.05k
      if (check_file_exists("gui/gui.bt", "/usr/share/gpac", file_path)) return 1;
553
3.05k
      if (check_file_exists("gui/gui.bt", "/usr/local/share/gpac", file_path)) return 1;
554
3.05k
      if (check_file_exists("gui/gui.bt", "/opt/share/gpac", file_path)) return 1;
555
3.05k
      if (check_file_exists("gui/gui.bt", "/opt/local/share/gpac", file_path)) return 1;
556
557
6.10k
    } else if ((path_type==GF_PATH_MODULES) && root) {
558
      //if arch-specific scheme try it first (to avoid loading 64bit versions for 32bit shared...)
559
6.10k
      if (lib_arch_scheme[0]) {
560
0
        gf_strcat(app_path, "/");
561
0
        gf_strcat(app_path, lib_arch_scheme);
562
0
        gf_strcat(app_path, "/gpac");
563
0
        if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
564
0
        root[0]=0;
565
0
      }
566
6.10k
      gf_strcat(app_path, "/lib64/gpac");
567
6.10k
      if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
568
6.10k
      root[0]=0;
569
6.10k
      gf_strcat(app_path, "/lib/gpac");
570
6.10k
      if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
571
6.10k
      root[0]=0;
572
6.10k
      gf_strcat(app_path, "/lib/x86_64-linux-gnu/gpac");
573
6.10k
      if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
574
6.10k
      root[0]=0;
575
6.10k
      gf_strcat(app_path, "/lib/i386-linux-gnu/gpac");
576
6.10k
      if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
577
578
      /*failsafe - look in possible install dirs ...*/
579
6.10k
      if (check_file_exists(TEST_MODULE, "/usr/lib64/gpac", file_path)) return 1;
580
6.10k
      if (check_file_exists(TEST_MODULE, "/usr/lib/gpac", file_path)) return 1;
581
6.10k
      if (check_file_exists(TEST_MODULE, "/usr/local/lib/gpac", file_path)) return 1;
582
6.10k
      if (check_file_exists(TEST_MODULE, "/opt/lib/gpac", file_path)) return 1;
583
6.10k
      if (check_file_exists(TEST_MODULE, "/opt/local/lib/gpac", file_path)) return 1;
584
6.10k
      if (check_file_exists(TEST_MODULE, "/usr/lib/x86_64-linux-gnu/gpac", file_path)) return 1;
585
6.10k
      if (check_file_exists(TEST_MODULE, "/usr/lib/i386-linux-gnu/gpac", file_path)) return 1;
586
6.10k
    }
587
9.16k
  }
588
589
9.16k
  if (path_type==GF_PATH_SHARE) {
590
3.05k
    Bool try_lib=GF_TRUE;
591
3.05k
    if (get_default_install_path(app_path, GF_PATH_CFG)) {
592
3.05k
      sep = strstr(app_path, ".gpac/");
593
3.05k
      if (sep) sep[5]=0;
594
      /*GUI not found, look in ~/.gpac/share/gui/ */
595
3.05k
      gf_strcat(app_path, "/share");
596
3.05k
      if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
597
3.05k
    }
598
599
    /*GUI not found, look in gpac distribution if any */
600
3.05k
    if (get_default_install_path(app_path, GF_PATH_APP)) {
601
3.05k
      GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[core] trying to locate share from application dir %s\n", app_path));
602
3.05k
      gf_strcat(app_path, "/");
603
6.11k
retry_lib:
604
6.11k
      sep = strstr(app_path, "/bin/");
605
6.11k
      if (sep) {
606
0
        sep[0] = 0;
607
0
        gf_strcat(app_path, "/share");
608
0
        if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
609
0
        gf_strcat(app_path, "/gpac");
610
0
        if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
611
0
      }
612
6.11k
      sep = strstr(app_path, "/build/");
613
6.11k
      if (sep) {
614
0
        sep[0] = 0;
615
0
        gf_strcat(app_path, "/share");
616
0
        if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
617
0
      }
618
6.11k
    }
619
6.11k
    if (get_default_install_path(app_path, GF_PATH_LIB)) {
620
6.11k
      GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[core] trying to locate share from dynamic libgpac dir %s\n", app_path));
621
6.11k
      sep = strstr(app_path, "/lib");
622
6.11k
      if (sep) {
623
6.11k
        sep[0] = 0;
624
6.11k
        gf_strcat(app_path, "/share");
625
6.11k
        if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
626
6.11k
        gf_strcat(app_path, "/gpac");
627
6.11k
        if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
628
6.11k
      }
629
6.11k
      if (try_lib) {
630
3.05k
        try_lib = GF_FALSE;
631
3.05k
        goto retry_lib;
632
3.05k
      }
633
6.11k
    }
634
    /*GUI not found, look in .app for OSX case*/
635
6.11k
  }
636
637
9.16k
  if (path_type==GF_PATH_MODULES) {
638
    /*look in gpac compilation tree (modules are output in the same folder as apps) and in distrib tree */
639
6.10k
    if (get_default_install_path(app_path, GF_PATH_APP)) {
640
6.10k
      if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
641
642
      /*on OSX check modules subdirectory */
643
0
      gf_strcat(app_path, "/modules");
644
0
      if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
645
646
0
      get_default_install_path(app_path, GF_PATH_APP);
647
0
      gf_strcat(app_path, "/");
648
0
      sep = strstr(app_path, "/bin/");
649
0
      if (sep) {
650
0
        sep[0] = 0;
651
0
        gf_strcat(app_path, "/lib/gpac");
652
0
        if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
653
0
      }
654
655
      /*modules not found*/
656
0
      GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("Couldn't find any modules in standard path (app path %s)\n", app_path));
657
0
    }
658
659
    /*look in lib install */
660
0
    if (get_default_install_path(app_path, GF_PATH_LIB)) {
661
0
      if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
662
0
      gf_strcat(app_path, "/gpac");
663
0
      if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
664
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Couldn't find any modules in lib path %s\n", app_path));
665
0
    }
666
667
668
    /*modules not found, look in ~/.gpac/modules/ */
669
0
    if (get_default_install_path(app_path, GF_PATH_CFG)) {
670
0
      gf_strcat(app_path, "/modules");
671
0
      if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
672
0
    }
673
    /*modules not found, failure*/
674
#ifndef GPAC_STATIC_MODULES
675
    GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("Couldn't find any modules in HOME path (app path %s)\n", app_path));
676
#endif
677
0
    return 0;
678
0
  }
679
680
  /*OSX way vs iPhone*/
681
3.05k
  get_default_install_path(app_path, GF_PATH_APP);
682
3.05k
  sep = strstr(app_path, ".app/");
683
3.05k
  if (sep) sep[4] = 0;
684
685
  /*we are looking for .app install path, or GUI */
686
3.05k
  if (path_type==GF_PATH_SHARE) {
687
3.05k
#ifndef GPAC_CONFIG_IOS
688
3.05k
    gf_strcat(app_path, "/Contents/MacOS/share");
689
3.05k
    if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
690
#else /*iOS: for now, everything is set flat within the package*/
691
    /*iOS app is distributed with embedded GUI*/
692
    get_default_install_path(app_path, GF_PATH_APP);
693
    if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
694
    gf_strcat(app_path, "/share");
695
    if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
696
#endif
697
3.05k
  }
698
0
  else { // (path_type==GF_PATH_MODULES)
699
0
    gf_strcat(app_path, "/Contents/MacOS/modules");
700
0
    if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
701
0
  }
702
  /*not found ...*/
703
3.05k
  return 0;
704
3.05k
}
705
706
#endif
707
708
//get real path where the .gpac dir has been created, and use this as the default path
709
//for cache (tmp/ dir of ios app) and last working fir
710
#ifdef GPAC_CONFIG_IOS
711
static void gf_ios_refresh_cache_directory( GF_Config *cfg, const char *file_path)
712
{
713
  char *cache_dir, *old_cache_dir;
714
  char buf[GF_MAX_PATH], *res, *sep;
715
  res = realpath(file_path, buf);
716
  if (!res) return;
717
718
  sep = strstr(res, ".gpac");
719
  gf_assert(sep);
720
  sep[0] = 0;
721
  gf_cfg_set_key(cfg, "core", "docs-dir", res);
722
  if (!gf_cfg_get_key(cfg, "core", "last-dir"))
723
    gf_cfg_set_key(cfg, "core", "last-dir", res);
724
725
  gf_strcat(res, "cache/");
726
  cache_dir = res;
727
  old_cache_dir = (char*) gf_opts_get_key("core", "cache");
728
729
  if (!gf_dir_exists(cache_dir)) {
730
    if (old_cache_dir && strcmp(old_cache_dir, cache_dir)) {
731
      GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("Cache dir changed: old %d -> new %s\n\n", old_cache_dir, cache_dir ));
732
    }
733
    gf_mkdir(cache_dir);
734
  }
735
  gf_cfg_set_key(cfg, "core", "cache", cache_dir);
736
}
737
738
#endif
739
740
const char * gf_get_default_cache_directory_ex(Bool do_create);
741
742
GF_EXPORT
743
void gf_get_default_font_dir(char szPath[GF_MAX_PATH])
744
3.05k
{
745
#if defined(_WIN32_WCE)
746
  gf_strlcpy(szPath, "\\Windows", GF_MAX_PATH);
747
748
#elif defined(WIN32)
749
  GetWindowsDirectory((char*)szPath, MAX_PATH);
750
  if (szPath[strlen((char*)szPath)-1] != '\\') gf_strlcat((char*)szPath, "\\", GF_MAX_PATH);
751
  gf_strlcat((char *)szPath, "Fonts", GF_MAX_PATH);
752
753
#elif defined(__APPLE__) && defined(GPAC_CONFIG_IOS)
754
  gf_strlcpy(szPath, "/System/Library/Fonts/Cache,/System/Library/Fonts/AppFonts,/System/Library/Fonts/Core,/System/Library/Fonts/Extra", GF_MAX_PATH);
755
#elif defined(__APPLE__)
756
  gf_strlcpy(szPath, "/System/Library/Fonts,/Library/Fonts", GF_MAX_PATH);
757
758
#elif defined(GPAC_CONFIG_ANDROID)
759
  gf_strlcpy(szPath, "/system/fonts/", GF_MAX_PATH);
760
#else
761
  //scan all /usr/share/fonts, not just /usr/share/fonts/truetype/ which does not exist in some distrros
762
3.05k
  gf_strlcpy(szPath, "/usr/share/fonts/", GF_MAX_PATH);
763
3.05k
#endif
764
3.05k
}
765
766
767
static GF_Config *create_default_config(char *file_path, const char *profile)
768
3.05k
{
769
3.05k
  Bool moddir_found;
770
3.05k
  GF_Config *cfg;
771
3.05k
  char szPath[GF_MAX_PATH];
772
773
3.05k
  if (file_path && ! get_default_install_path(file_path, GF_PATH_CFG)) {
774
0
    profile = "0";
775
0
  }
776
  /*Create temp config file*/
777
3.05k
  if (profile && (!strcmp(profile, "0") || !stricmp(profile, "n"))) {
778
3.05k
    cfg = gf_cfg_new(NULL, NULL);
779
3.05k
    if (!stricmp(profile, "n")) {
780
0
      gf_cfg_discard_changes(cfg);
781
0
      return cfg;
782
0
    }
783
3.05k
  } else {
784
0
    FILE *f;
785
786
    /*create config file from disk*/
787
0
    if (profile) {
788
0
      snprintf(szPath, sizeof(szPath), "%s%cprofiles%c%s%c%s", file_path, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, profile, GF_PATH_SEPARATOR, CFG_FILE_NAME);
789
0
    } else {
790
0
      snprintf(szPath, sizeof(szPath), "%s%c%s", file_path, GF_PATH_SEPARATOR, CFG_FILE_NAME);
791
0
    }
792
0
    GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("Trying to create config file: %s\n", szPath ));
793
794
0
    f = gf_fopen(szPath, "wt");
795
0
    if (!f) return NULL;
796
0
    gf_fclose(f);
797
798
0
    cfg = gf_cfg_new(NULL, szPath);
799
0
  }
800
801
3.05k
  if (!cfg) return NULL;
802
803
804
3.05k
#ifndef GPAC_CONFIG_IOS
805
3.05k
  moddir_found = get_default_install_path(szPath, GF_PATH_MODULES);
806
#else
807
  moddir_found = get_default_install_path(szPath, GF_PATH_APP);
808
#endif
809
810
#if defined(GPAC_CONFIG_IOS)
811
  gf_cfg_set_key(cfg, "core", "devclass", "ios");
812
#elif defined(GPAC_CONFIG_ANDROID)
813
  gf_cfg_set_key(cfg, "core", "devclass", "android");
814
#elif defined(GPAC_CONFIG_EMSCRIPTEN)
815
  gf_cfg_set_key(cfg, "core", "devclass", "wasm");
816
#else
817
3.05k
  gf_cfg_set_key(cfg, "core", "devclass", "desktop");
818
3.05k
#endif
819
820
821
3.05k
  if (!moddir_found) {
822
0
#if !defined(GPAC_CONFIG_EMSCRIPTEN)
823
0
    GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[Core] default modules directory not found\n"));
824
0
#endif
825
3.05k
  } else {
826
3.05k
    gf_cfg_set_key(cfg, "core", "module-dir", szPath);
827
3.05k
  }
828
829
830
#if defined(GPAC_CONFIG_IOS)
831
  gf_ios_refresh_cache_directory(cfg, file_path);
832
#elif defined(GPAC_CONFIG_ANDROID)
833
  if (get_default_install_path(szPath, GF_PATH_APP)) {
834
    gf_strcat(szPath, "/cache");
835
    gf_cfg_set_key(cfg, "core", "cache", szPath);
836
    gf_strcat(szPath, "/.nomedia");
837
    //create .nomedia in cache and in app dir
838
    if (!gf_file_exists(szPath)) {
839
      FILE *f = gf_fopen(szPath, "w");
840
      if (f) gf_fclose(f);
841
    }
842
    get_default_install_path(szPath, GF_PATH_APP);
843
    gf_strcat(szPath, "/.nomedia");
844
    if (!gf_file_exists(szPath)) {
845
      FILE *f = gf_fopen(szPath, "w");
846
      if (f) gf_fclose(f);
847
    }
848
    //add a tmp as well, tmpfile() does not work on android
849
    get_default_install_path(szPath, GF_PATH_APP);
850
    gf_strcat(szPath, "/gpac_tmp");
851
    gf_cfg_set_key(cfg, "core", "tmp", szPath);
852
    gf_strcat(szPath, "/.nomedia");
853
    if (!gf_file_exists(szPath)) {
854
      FILE *f = gf_fopen(szPath, "w");
855
      if (f) gf_fclose(f);
856
    }
857
  }
858
#else
859
  /*get default temporary directoy */
860
3.05k
  gf_cfg_set_key(cfg, "core", "cache", gf_get_default_cache_directory_ex(GF_FALSE));
861
3.05k
#endif
862
863
  /*Setup font engine to FreeType by default, and locate TrueType font directory on the system*/
864
3.05k
  gf_cfg_set_key(cfg, "core", "font-reader", "freetype");
865
3.05k
  gf_cfg_set_key(cfg, "core", "rescan-fonts", "yes");
866
867
868
3.05k
  gf_get_default_font_dir(szPath);
869
3.05k
  gf_cfg_set_key(cfg, "core", "font-dirs", szPath);
870
871
3.05k
  gf_cfg_set_key(cfg, "core", "cache-size", "100M");
872
3.05k
  gf_cfg_set_key(cfg, "core", "cache-check", "0");
873
874
#if defined(_WIN32_WCE)
875
  gf_cfg_set_key(cfg, "core", "video-output", "gapi");
876
#elif defined(WIN32)
877
  gf_cfg_set_key(cfg, "core", "video-output", "directx");
878
#elif defined(__DARWIN__) || defined(__APPLE__)
879
  gf_cfg_set_key(cfg, "core", "video-output", "sdl");
880
#elif defined(GPAC_CONFIG_ANDROID)
881
  gf_cfg_set_key(cfg, "core", "video-output", "android");
882
  gf_cfg_set_key(cfg, "core", "audio-output", "android");
883
#else
884
  //use SDL by default, will fallback to X11 if not found (our X11 wrapper is old and does not have all features of the SDL one)
885
3.05k
  gf_cfg_set_key(cfg, "core", "video-output", "sdl");
886
3.05k
  gf_cfg_set_key(cfg, "core", "audio-output", "sdla");
887
3.05k
#endif
888
889
890
3.05k
#if !defined(GPAC_CONFIG_IOS) && !defined(GPAC_CONFIG_ANDROID)
891
3.05k
  gf_cfg_set_key(cfg, "core", "switch-vres", "no");
892
3.05k
  gf_cfg_set_key(cfg, "core", "hwvmem", "auto");
893
3.05k
#endif
894
895
#ifdef GPAC_CONFIG_ANDROID
896
  const char *opt = android_external_storage[0] ? android_external_storage : getenv("EXTERNAL_STORAGE");
897
  if (!opt) opt = "/sdcard";
898
  gf_cfg_set_key(cfg, "core", "docs-dir", opt);
899
  gf_cfg_set_key(cfg, "core", "last-dir", opt);
900
#endif
901
902
  /*locate GUI*/
903
3.05k
  if ( get_default_install_path(szPath, GF_PATH_SHARE) ) {
904
0
    char gui_path[GF_MAX_PATH+100];
905
0
    sprintf(gui_path, "%s%cgui%cgui.bt", szPath, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
906
0
    if (gf_file_exists(gui_path)) {
907
0
      gf_cfg_set_key(cfg, "core", "startup-file", gui_path);
908
0
    }
909
910
0
    sprintf(gui_path, "%s%cres%cca-bundle.crt", szPath, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
911
0
    gf_cfg_set_key(cfg, "core", "ca-bundle-default", gui_path);
912
913
    /*shaders are at the same location*/
914
0
    sprintf(gui_path, "%s%cshaders%cvertex.glsl", szPath, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
915
0
    gf_cfg_set_key(cfg, "filter@compositor", "vertshader", gui_path);
916
0
    sprintf(gui_path, "%s%cshaders%cfragment.glsl", szPath, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
917
0
    gf_cfg_set_key(cfg, "filter@compositor", "fragshader", gui_path);
918
919
    //aliases and other defaults
920
0
    sprintf(gui_path, "%s%cdefault.cfg", szPath, GF_PATH_SEPARATOR);
921
0
    if (gf_file_exists(gui_path)) {
922
0
      GF_Config *aliases = gf_cfg_new(NULL, gui_path);
923
0
      if (aliases) {
924
0
        u32 i, count = gf_cfg_get_section_count(aliases);
925
0
        for (i=0; i<count; i++) {
926
0
          u32 j, count2;
927
0
          const char *sec_name = gf_cfg_get_section_name(aliases, i);
928
0
          if (!sec_name) continue;
929
0
          count2 = gf_cfg_get_key_count(aliases, sec_name);
930
0
          for (j=0; j<count2; j++) {
931
0
            const char *name = gf_cfg_get_key_name(aliases, sec_name, j);
932
0
            const char *val = gf_cfg_get_key(aliases, sec_name, name);
933
0
            gf_cfg_set_key(cfg, sec_name, name, val);
934
0
          }
935
0
        }
936
0
      }
937
0
      gf_cfg_del(aliases);
938
0
    }
939
0
  }
940
941
3.05k
  if (profile && !strcmp(profile, "0")) {
942
3.05k
    GF_Err gf_cfg_set_filename(GF_Config *iniFile, const char * fileName);
943
//    sprintf(szPath, "%s%c%s", gf_get_default_cache_directory(), GF_PATH_SEPARATOR, CFG_FILE_NAME);
944
3.05k
    gf_cfg_set_filename(cfg, CFG_FILE_NAME);
945
3.05k
    gf_cfg_discard_changes(cfg);
946
3.05k
    return cfg;
947
3.05k
  }
948
  /*store and reload*/
949
0
  gf_strcpy(szPath, gf_cfg_get_filename(cfg));
950
0
  gf_cfg_del(cfg);
951
0
  return gf_cfg_new(NULL, szPath);
952
3.05k
}
953
954
/*check if modules directory has changed in the config file
955
*/
956
static void check_modules_dir(GF_Config *cfg)
957
3.05k
{
958
3.05k
  char path[GF_MAX_PATH];
959
960
#ifdef GPAC_CONFIG_IOS
961
  const char *cfg_path;
962
  if ( get_default_install_path(path, GF_PATH_SHARE) ) {
963
    char *sep;
964
    char shader_path[GF_MAX_PATH];
965
    gf_strcat(path, "/gui/gui.bt");
966
    gf_cfg_set_key(cfg, "core", "startup-file", path);
967
    //get rid of "/gui/gui.bt"
968
    sep = strrchr(path, '/');
969
    sep[0] = 0;
970
    sep = strrchr(path, '/');
971
    sep[0] = 0;
972
973
    sprintf(shader_path, "%s%cshaders%cvertex.glsl", path, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
974
    gf_cfg_set_key(cfg, "filter@compositor", "vertshader", shader_path);
975
    sprintf(shader_path, "%s%cshaders%cfragment.glsl", path, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
976
    gf_cfg_set_key(cfg, "filter@compositor", "fragshader", shader_path);
977
  }
978
  cfg_path = gf_cfg_get_filename(cfg);
979
  gf_ios_refresh_cache_directory(cfg, cfg_path);
980
981
#else
982
3.05k
  const char *opt;
983
984
3.05k
  if ( get_default_install_path(path, GF_PATH_MODULES) ) {
985
3.05k
    opt = gf_cfg_get_key(cfg, "core", "module-dir");
986
    //for OSX, we can have an install in /usr/... and an install in /Applications/GPAC.app - always change
987
#if defined(__DARWIN__) || defined(__APPLE__)
988
    if (!opt || strcmp(opt, path))
989
      gf_cfg_set_key(cfg, "core", "module-dir", path);
990
#else
991
992
    //otherwise only check we didn't switch between a 64 bit version and a 32 bit version
993
3.05k
    if (!opt) {
994
0
      gf_cfg_set_key(cfg, "core", "module-dir", path);
995
3.05k
    } else  {
996
3.05k
      Bool erase_modules_dir = GF_FALSE;
997
3.05k
      const char *opt64 = gf_cfg_get_key(cfg, "core", "64bits");
998
3.05k
      if (!opt64) {
999
        //first run or old versions, erase
1000
3.05k
        erase_modules_dir = GF_TRUE;
1001
3.05k
      } else if (!strcmp(opt64, "yes") ) {
1002
#ifndef GPAC_64_BITS
1003
        erase_modules_dir = GF_TRUE;
1004
#endif
1005
0
      } else {
1006
0
#ifdef GPAC_64_BITS
1007
0
        erase_modules_dir = GF_TRUE;
1008
0
#endif
1009
0
      }
1010
1011
3.05k
#ifdef GPAC_64_BITS
1012
3.05k
      opt64 = "yes";
1013
#else
1014
      opt64 = "no";
1015
#endif
1016
3.05k
      gf_cfg_set_key(cfg, "core", "64bits", opt64);
1017
1018
3.05k
      if (erase_modules_dir) {
1019
3.05k
        gf_cfg_set_key(cfg, "core", "module-dir", path);
1020
3.05k
      }
1021
3.05k
    }
1022
3.05k
#endif
1023
3.05k
  }
1024
1025
  /*if startup file was disabled, do not attempt to correct it*/
1026
3.05k
  if (gf_cfg_get_key(cfg, "core", "startup-file")==NULL) return;
1027
1028
0
  if ( get_default_install_path(path, GF_PATH_SHARE) ) {
1029
0
    gf_cfg_set_key(cfg, "temp", "share_dir", path);
1030
0
    opt = gf_cfg_get_key(cfg, "core", "startup-file");
1031
0
    if (strstr(opt, "gui.bt") && strcmp(opt, path) && strstr(path, ".app") ) {
1032
#if defined(__DARWIN__) || defined(__APPLE__)
1033
      gf_strcat(path, "/gui/gui.bt");
1034
      gf_cfg_set_key(cfg, "core", "startup-file", path);
1035
#endif
1036
0
    }
1037
0
  }
1038
1039
0
#endif
1040
0
}
1041
1042
#ifdef GPAC_CONFIG_ANDROID
1043
1044
static Bool delete_tmp_files(void *cbck, char *item_name, char *item_path, GF_FileEnumInfo *file_info)
1045
{
1046
  if (gf_file_delete(item_path) != GF_OK) {
1047
    GF_LOG(GF_LOG_ERROR, GF_LOG_CACHE, ("[CORE] Failed to cleanup temp file %s\n", item_path));
1048
  }
1049
  return GF_FALSE;
1050
}
1051
#endif
1052
1053
static void check_default_cred_file(GF_Config *cfg, char szPath[GF_MAX_PATH])
1054
0
{
1055
0
#ifndef GPAC_CONFIG_EMSCRIPTEN
1056
0
  char key[16];
1057
0
  u64 v1, v2;
1058
0
  const char *opt = gf_cfg_get_key(cfg, "core", "cred");
1059
0
  if (opt) return;
1060
  //when running as service, the config file path may be unknown
1061
0
  if (!szPath[0] || !gf_dir_exists(szPath))
1062
0
    return;
1063
0
  gf_strlcat(szPath, "/creds.key", GF_MAX_PATH);
1064
0
  if (gf_file_exists(szPath)) return;
1065
1066
0
  GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[core] Creating default credential key in %s, use -cred=PATH/TO_FILE to overwrite\n", szPath));
1067
1068
0
  v1 = gf_rand(); v1<<=32; v1 |= gf_rand();
1069
0
  v2 = gf_rand(); v2<<=32; v2 |= gf_rand();
1070
0
  v1 |= gf_sys_clock_high_res();
1071
#ifndef GPAC_64_BITS
1072
  v2 |= (u64) (u32) cfg;
1073
  v2 ^= (u64) (u32) szPath;
1074
#else
1075
0
  v2 |= (u64) cfg;
1076
0
  v2 ^= (u64) szPath;
1077
0
#endif
1078
0
  * ( (u64*) &key[0] ) = v1;
1079
0
  * ( (u64*) &key[8] ) = v2;
1080
0
  FILE *crd = gf_fopen(szPath, "w");
1081
0
  if (!crd) {
1082
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Failed to create credential key in %s, credentials will not be stored\n", szPath));
1083
0
    return;
1084
0
  }
1085
0
  fwrite(key, 16, 1, crd);
1086
0
  fclose(crd);
1087
0
  gf_cfg_set_key(cfg, "core", "cred", szPath);
1088
0
#endif //GPAC_CONFIF_EMSCRIPTEN
1089
0
}
1090
1091
/*!
1092
\brief configuration file initialization
1093
 *
1094
 * Constructs a configuration file from fileName. If fileName is NULL, the default GPAC configuration file is loaded with the
1095
 * proper module directory, font directory and other default options. If fileName is non-NULL no configuration file is found,
1096
 * a "light" default configuration file is created.
1097
\param profile name or path to existing config file
1098
\return the configuration file object, NULL if the file could not be created
1099
 */
1100
 #include <gpac/network.h>
1101
1102
static GF_Config *gf_cfg_init(const char *profile)
1103
3.05k
{
1104
3.05k
  GF_Config *cfg=NULL;
1105
3.05k
  u32 prof_len=0;
1106
3.05k
  Bool force_new_cfg=GF_FALSE;
1107
3.05k
  Bool fast_profile=GF_FALSE;
1108
3.05k
  char szPath[GF_MAX_PATH];
1109
3.05k
  char *prof_opt = NULL;
1110
1111
3.05k
  if (profile) {
1112
3.05k
    prof_len = (u32) strlen(profile);
1113
3.05k
    prof_opt = gf_url_colon_suffix(profile, 0);
1114
3.05k
    if (prof_opt) {
1115
0
      prof_len -= (u32) strlen(prof_opt);
1116
0
      if (strstr(prof_opt, "reload")) force_new_cfg = GF_TRUE;
1117
0
      prof_opt[0] = 0;
1118
0
    }
1119
3.05k
    if (!stricmp(profile, "n")) {
1120
0
      fast_profile = GF_TRUE;
1121
0
      cfg = create_default_config(NULL, "n");
1122
0
      goto skip_cfg;
1123
0
    }
1124
3.05k
  }
1125
3.05k
  if (profile && !prof_len)
1126
0
    profile = NULL;
1127
1128
3.05k
  if (profile && strpbrk(profile, "/\\")) {
1129
0
    if (!gf_file_exists(profile)) {
1130
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Config file %s does not exist\n", profile));
1131
0
      goto exit;
1132
0
    }
1133
0
    cfg = gf_cfg_new(NULL, profile);
1134
0
    if (!cfg) {
1135
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Failed to load existing config file %s\n", profile));
1136
0
      goto exit;
1137
0
    }
1138
0
    if (force_new_cfg) {
1139
0
      gf_cfg_del(cfg);
1140
0
      cfg = create_default_config(NULL, profile);
1141
0
    }
1142
0
    check_modules_dir(cfg);
1143
0
    goto exit;
1144
0
  }
1145
1146
3.05k
  if (!get_default_install_path(szPath, GF_PATH_CFG)) {
1147
0
    if (!profile || strcmp(profile, "0")) {
1148
0
      GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] Cannot locate global config path in application or user home directory, using temporary config file\n"));
1149
0
    }
1150
0
    profile="0";
1151
0
    cfg = create_default_config(szPath, profile);
1152
0
    goto skip_cfg;
1153
0
  }
1154
1155
3.05k
  if (profile && !strcmp(profile, "0")) {
1156
3.05k
    cfg = create_default_config(NULL, "0");
1157
3.05k
    goto skip_cfg;
1158
3.05k
  }
1159
1160
0
  if (profile) {
1161
0
    gf_strcat(szPath, "/profiles/");
1162
0
    gf_strcat(szPath, profile);
1163
0
  }
1164
1165
0
  cfg = gf_cfg_new(szPath, CFG_FILE_NAME);
1166
  //config file not compatible with old arch, check it:
1167
0
  if (cfg) {
1168
0
    const char *key;
1169
0
    u32 nb_old_sec = gf_cfg_get_key_count(cfg, "Compositor");
1170
0
    nb_old_sec += gf_cfg_get_key_count(cfg, "MimeTypes");
1171
0
    nb_old_sec += gf_cfg_get_key_count(cfg, "Video");
1172
0
    nb_old_sec += gf_cfg_get_key_count(cfg, "Audio");
1173
0
    nb_old_sec += gf_cfg_get_key_count(cfg, "Systems");
1174
0
    nb_old_sec += gf_cfg_get_key_count(cfg, "General");
1175
0
    if (! gf_cfg_get_key_count(cfg, "core"))
1176
0
      nb_old_sec += 1;
1177
1178
    //check GUI is valid, if not recreate a config
1179
0
    key = gf_cfg_get_key(cfg, "core", "startup-file");
1180
0
    if (key && !gf_file_exists(key))
1181
0
      force_new_cfg = GF_TRUE;
1182
1183
    //check if reset flag is set in existing config
1184
0
    if (gf_cfg_get_key(cfg, "core", "reset"))
1185
0
      force_new_cfg = GF_TRUE;
1186
1187
0
    if (nb_old_sec || force_new_cfg) {
1188
0
      if (nb_old_sec && (!profile || strcmp(profile, "0"))) {
1189
0
        GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[core] Incompatible config file %s found in %s - creating new file\n", CFG_FILE_NAME, szPath ));
1190
0
      }
1191
0
      gf_cfg_del(cfg);
1192
0
      cfg = create_default_config(szPath, profile);
1193
0
    } else {
1194
      //check fonts are valid, if not reload fonts
1195
0
      Bool rescan_fonts = GF_FALSE;
1196
0
      key = gf_cfg_get_key_name(cfg, "FontCache", 0);
1197
0
      if (!key)
1198
0
        rescan_fonts = GF_TRUE;
1199
0
      else {
1200
0
        key = gf_cfg_get_key(cfg, "FontCache", key);
1201
0
        if (key && !gf_file_exists(key))
1202
0
          rescan_fonts = GF_TRUE;
1203
0
      }
1204
0
      if (rescan_fonts)
1205
0
        gf_opts_set_key("core", "rescan-fonts", "yes");
1206
1207
      // if ca-bundle is not set or explicitly disabled (empty string), set to default
1208
0
      const char* ca_bundle = gf_cfg_get_key(cfg, "core", "ca-bundle-default");
1209
0
      if (!ca_bundle) {
1210
0
        char szShare[GF_MAX_PATH];
1211
0
        if (get_default_install_path(szShare, GF_PATH_SHARE)) {
1212
0
          char gui_path[GF_MAX_PATH + 100];
1213
1214
0
          sprintf(gui_path, "%s%cres%cca-bundle.crt", szShare, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
1215
0
          gf_cfg_set_key(cfg, "core", "ca-bundle-default", gui_path);
1216
0
        }
1217
0
      }
1218
0
    }
1219
0
  }
1220
  //no config file found
1221
0
  else {
1222
0
    if (!profile || strcmp(profile, "0")) {
1223
0
      GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] Config file %s not found in %s - creating new file\n", CFG_FILE_NAME, szPath ));
1224
0
    }
1225
0
    cfg = create_default_config(szPath, profile);
1226
0
  }
1227
1228
3.05k
skip_cfg:
1229
3.05k
  if (!cfg) {
1230
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Cannot create config file %s in %s directory\n", CFG_FILE_NAME, szPath));
1231
0
    goto exit;
1232
0
  }
1233
1234
3.05k
#ifndef GPAC_CONFIG_IOS
1235
3.05k
  GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[core] Using global config file in %s directory\n", szPath));
1236
3.05k
#endif
1237
1238
3.05k
  if (fast_profile) goto exit;
1239
1240
3.05k
  check_modules_dir(cfg);
1241
3.05k
  if (!profile || strcmp(profile, "0"))
1242
0
    check_default_cred_file(cfg, szPath);
1243
1244
3.05k
  if (!gf_cfg_get_key(cfg, "core", "store-dir")) {
1245
3.05k
    if (profile && !strcmp(profile, "0")) {
1246
3.05k
      gf_strcpy(szPath, gf_get_default_cache_directory_ex(GF_FALSE) );
1247
3.05k
      gf_strcat(szPath, "/Storage");
1248
3.05k
    } else {
1249
0
      char *sep;
1250
0
      gf_strcpy(szPath, gf_cfg_get_filename(cfg));
1251
0
      sep = strrchr(szPath, '/');
1252
0
      if (!sep) sep = strrchr(szPath, '\\');
1253
0
      if (sep) sep[0] = 0;
1254
0
      gf_strcat(szPath, "/Storage");
1255
0
      if (!gf_dir_exists(szPath)) gf_mkdir(szPath);
1256
0
    }
1257
3.05k
    gf_cfg_set_key(cfg, "core", "store-dir", szPath);
1258
3.05k
  }
1259
1260
3.05k
exit:
1261
3.05k
  if (prof_opt) prof_opt[0] = ':';
1262
1263
  //clean tmp
1264
#ifdef GPAC_CONFIG_ANDROID
1265
  if (cfg) {
1266
    const char *tmp = gf_cfg_get_key(cfg, "core", "tmp");
1267
    if (tmp && !strstr(tmp, "/gpac_tmp")) tmp=NULL;
1268
    if (tmp) {
1269
      gf_enum_directory(tmp, GF_FALSE, delete_tmp_files, (void*)cfg, NULL);
1270
    }
1271
  }
1272
#endif
1273
1274
3.05k
  return cfg;
1275
3.05k
}
1276
1277
1278
GF_EXPORT
1279
Bool gf_opts_default_shared_directory(char path_buffer[GF_MAX_PATH])
1280
4
{
1281
4
  const char *opt = gf_opts_get_key("temp", "share_dir");
1282
4
  if (opt) {
1283
0
    gf_strlcpy(path_buffer, opt, GF_MAX_PATH);
1284
0
    return 1;
1285
0
  }
1286
4
  return get_default_install_path(path_buffer, GF_PATH_SHARE);
1287
4
}
1288
1289
void gf_modules_new(GF_Config *config);
1290
void gf_modules_del();
1291
1292
GF_Config *gpac_global_config = NULL;
1293
1294
void gf_init_global_config(const char *profile)
1295
3.05k
{
1296
3.05k
  if (!gpac_global_config) {
1297
3.05k
    gpac_global_config = gf_cfg_init(profile);
1298
3.05k
    if (!gpac_global_config) {
1299
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Fatal error: failed to initialize GPAC global configuration\n"));
1300
0
      exit(1);
1301
0
    }
1302
3.05k
    if (!profile || stricmp(profile, "n"))
1303
3.05k
      gf_modules_new(gpac_global_config);
1304
3.05k
  }
1305
3.05k
}
1306
1307
void gf_uninit_global_config(Bool discard_config)
1308
3.05k
{
1309
3.05k
  if (gpac_global_config) {
1310
3.05k
    if (discard_config) gf_cfg_discard_changes(gpac_global_config);
1311
3.05k
    gf_cfg_del(gpac_global_config);
1312
3.05k
    gpac_global_config = NULL;
1313
3.05k
    gf_modules_del();
1314
3.05k
  }
1315
3.05k
}
1316
1317
GF_Err gf_cfg_set_key_internal(GF_Config *iniFile, const char *secName, const char *keyName, const char *keyValue, Bool is_restrict);
1318
1319
#ifdef GPAC_ENABLE_RESTRICT
1320
void gf_cfg_load_restrict()
1321
{
1322
  char szPath[GF_MAX_PATH];
1323
  if (get_default_install_path(szPath, GF_PATH_SHARE)) {
1324
    gf_strcat(szPath, "/");
1325
    gf_strcat(szPath, "restrict.cfg");
1326
    if (gf_file_exists(szPath)) {
1327
      GF_Config *rcfg = gf_cfg_new(NULL, szPath);
1328
      if (rcfg) {
1329
        u32 i, count = gf_cfg_get_section_count(rcfg);
1330
        for (i=0; i<count; i++) {
1331
          u32 j, kcount;
1332
          const char *sname = gf_cfg_get_section_name(rcfg, i);
1333
          if (!sname) break;
1334
          kcount = gf_cfg_get_key_count(rcfg, sname);
1335
          for (j=0; j<kcount; j++) {
1336
            const char *kname = gf_cfg_get_key_name(rcfg, sname, j);
1337
            const char *kval = gf_cfg_get_key(rcfg, sname, kname);
1338
            gf_cfg_set_key_internal(gpac_global_config, sname, kname, kval, GF_TRUE);
1339
          }
1340
        }
1341
        gf_cfg_del(rcfg);
1342
      }
1343
    }
1344
  }
1345
}
1346
#endif
1347
1348
GF_EXPORT
1349
const char *gf_opts_get_key(const char *secName, const char *keyName)
1350
26.0M
{
1351
26.0M
  if (!gpac_global_config) return NULL;
1352
1353
390k
  if (!strcmp(secName, "core")) {
1354
158k
    const char *opt = gf_cfg_get_key(gpac_global_config, "temp", keyName);
1355
158k
    if (opt) return opt;
1356
158k
  }
1357
390k
  return gf_cfg_get_key(gpac_global_config, secName, keyName);
1358
390k
}
1359
GF_EXPORT
1360
GF_Err gf_opts_set_key(const char *secName, const char *keyName, const char *keyValue)
1361
6.58k
{
1362
6.58k
  if (!gpac_global_config) return GF_BAD_PARAM;
1363
5.68k
  return gf_cfg_set_key(gpac_global_config, secName, keyName, keyValue);
1364
6.58k
}
1365
GF_EXPORT
1366
void gf_opts_del_section(const char *secName)
1367
0
{
1368
0
  if (!gpac_global_config) return;
1369
0
  gf_cfg_del_section(gpac_global_config, secName);
1370
0
}
1371
GF_EXPORT
1372
u32 gf_opts_get_section_count()
1373
3.05k
{
1374
3.05k
  if (!gpac_global_config) return 0;
1375
3.05k
  return gf_cfg_get_section_count(gpac_global_config);
1376
3.05k
}
1377
GF_EXPORT
1378
const char *gf_opts_get_section_name(u32 secIndex)
1379
3.05k
{
1380
3.05k
  if (!gpac_global_config) return NULL;
1381
3.05k
  return gf_cfg_get_section_name(gpac_global_config, secIndex);
1382
3.05k
}
1383
GF_EXPORT
1384
u32 gf_opts_get_key_count(const char *secName)
1385
0
{
1386
0
  if (!gpac_global_config) return 0;
1387
0
  return gf_cfg_get_key_count(gpac_global_config, secName);
1388
0
}
1389
GF_EXPORT
1390
const char *gf_opts_get_key_name(const char *secName, u32 keyIndex)
1391
0
{
1392
0
  if (!gpac_global_config) return NULL;
1393
0
  return gf_cfg_get_key_name(gpac_global_config, secName, keyIndex);
1394
0
}
1395
1396
GF_EXPORT
1397
const char *gf_opts_get_key_restricted(const char *secName, const char *keyName)
1398
8.33k
{
1399
8.33k
  const char *res = NULL;
1400
8.33k
  const char *gf_cfg_get_key_internal(GF_Config *iniFile, const char *secName, const char *keyName, Bool restricted_only);
1401
8.33k
  if (gpac_global_config)
1402
5.27k
    res = gf_cfg_get_key_internal(gpac_global_config, secName, keyName, GF_TRUE);
1403
8.33k
  return res;
1404
8.33k
}
1405
1406
GF_EXPORT
1407
const char *gf_opts_get_filename()
1408
0
{
1409
0
  return gf_cfg_get_filename(gpac_global_config);
1410
0
}
1411
GF_EXPORT
1412
GF_Err gf_opts_discard_changes()
1413
0
{
1414
0
  return gf_cfg_discard_changes(gpac_global_config);
1415
0
}
1416
1417
GF_EXPORT
1418
GF_Err gf_opts_save()
1419
0
{
1420
0
  return gf_cfg_save(gpac_global_config);
1421
0
}
1422
1423
#include <gpac/main.h>
1424
1425
#ifdef GPAC_CONFIG_EMSCRIPTEN
1426
#define LOGFILE_HELP  "set output log file - use `console` for browser console"
1427
#else
1428
#define LOGFILE_HELP  "set output log file"
1429
#endif
1430
1431
GF_GPACArg GPAC_Args[] = {
1432
 GF_DEF_ARG("tmp", NULL, "specify directory for temporary file creation instead of OS-default temporary file management", NULL, NULL, GF_ARG_STRING, GF_ARG_SUBSYS_CORE),
1433
 GF_DEF_ARG("noprog", NULL, "disable progress messages", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_LOG),
1434
 GF_DEF_ARG("quiet", NULL, "disable all messages, including errors", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_LOG),
1435
 GF_DEF_ARG("log-file", "lf", LOGFILE_HELP, NULL, NULL, GF_ARG_STRING, GF_ARG_SUBSYS_LOG),
1436
 GF_DEF_ARG("log-clock", "lc", "log time in micro sec since start time of GPAC before each log line except for `app` tool", NULL, NULL, GF_ARG_BOOL, GF_ARG_SUBSYS_LOG),
1437
 GF_DEF_ARG("log-utc", "lu", "log UTC time in ms before each log line except for `app` tool", NULL, NULL, GF_ARG_BOOL, GF_ARG_SUBSYS_LOG),
1438
 GF_DEF_ARG("logs", NULL, "set log tools and levels.  \n"
1439
      "  \n"
1440
      "You can independently log different tools involved in a session.  \n"
1441
      "log_args is formatted as a colon (':') separated list of `toolX[:toolZ]@levelX`  \n"
1442
          "`levelX` can be one of:\n"
1443
          "- quiet: skip logs\n"
1444
          "- error: logs only error messages\n"
1445
          "- warning: logs error+warning messages\n"
1446
          "- info: logs error+warning+info messages\n"
1447
          "- debug: logs all messages\n"
1448
          "- strict: exit if error for this log tool and use default log level if tool\n"
1449
          "\n`toolX` can be one of:\n"
1450
          "- core: libgpac core\n"
1451
          "- mutex: log all mutex calls\n"
1452
          "- mem: GPAC memory tracker\n"
1453
          "- module: GPAC modules (av out, font engine, 2D rasterizer)\n"
1454
          "- filter: filter session debugging\n"
1455
          "- sched: filter session scheduler debugging\n"
1456
          "- codec: codec messages (used by encoder and decoder filters)\n"
1457
          "- coding: bitstream formats (audio, video, scene)\n"
1458
          "- container: container formats (ISO File, MPEG-2 TS, AVI, ...) and multiplexer/demultiplexer filters\n"
1459
          "- network: TCP/UDP sockets and TLS\n"
1460
          "- http: HTTP traffic\n"
1461
          "- cache: HTTP cache subsystem\n"
1462
          "- rtp: RTP traffic\n"
1463
          "- dash: HTTP streaming logs\n"
1464
          "- route: ROUTE (ATSC3) debugging\n"
1465
          "- media: messages from generic filters and reframer/rewriter filters\n"
1466
          "- parser: textual parsers (svg, xmt, bt, ...)\n"
1467
          "- mmio: I/O management (AV devices, file, pipes, OpenGL)\n"
1468
          "- audio: audio renderer/mixer/output\n"
1469
          "- script: script engine except console log\n"
1470
          "- console: script console log\n"
1471
          "- scene: scene graph and scene manager\n"
1472
          "- compose: composition engine (2D, 3D, etc)\n"
1473
          "- ctime: media and SMIL timing info from composition engine\n"
1474
          "- interact: interaction messages (UI events and triggered DOM events and VRML route)\n"
1475
          "- rti: run-time stats of compositor\n"
1476
          "- all: all tools logged - other tools can be specified afterwards\n"
1477
          "The special keyword `ncl` can be set to disable color logs.  \n"
1478
          "The special keyword `strict` can be set to exit at first error on any tool.  \n"
1479
          "`levelX` can accept the suffix `+strict` to force strict error only for the given log tool(s).   \n"
1480
          "\nEX -logs=all@info:dash@debug:ncl\n"
1481
      "This moves all log to info level, dash to debug level and disable color logs"
1482
      , NULL, NULL, GF_ARG_STRING, GF_ARG_SUBSYS_LOG),
1483
 GF_DEF_ARG("proglf", NULL, "use new line at each progress messages", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_LOG),
1484
 GF_DEF_ARG("log-dual", "ld", "output to both file and stderr", NULL, NULL, GF_ARG_BOOL, GF_ARG_SUBSYS_LOG),
1485
1486
 GF_DEF_ARG("strict-error", "se", "exit after the first error is reported", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_CORE),
1487
 GF_DEF_ARG("store-dir", NULL, "set storage directory", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_CORE),
1488
 GF_DEF_ARG("mod-dirs", NULL, "set additional module directories as a semi-colon `;` separated list", NULL, NULL, GF_ARG_STRINGS, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1489
 GF_DEF_ARG("js-dirs", NULL, "set javascript directories", NULL, NULL, GF_ARG_STRINGS, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1490
 GF_DEF_ARG("no-js-mods", NULL, "disable javascript module loading", NULL, NULL, GF_ARG_STRINGS, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1491
 GF_DEF_ARG("ifce", NULL, "set default multicast interface (default is ANY), either an IP address or a device name as listed by `gpac -h net`. Prefix '+' will force using IPv6 for dual interface", NULL, NULL, GF_ARG_STRING, GF_ARG_SUBSYS_CORE),
1492
 GF_DEF_ARG("lang", NULL, "set preferred language", NULL, NULL, GF_ARG_STRING, GF_ARG_SUBSYS_CORE),
1493
 GF_DEF_ARG("cfg", "opt", "get or set configuration file value. The string parameter can be formatted as:\n"
1494
          "- `section:key=val`: set the key to a new value\n"
1495
          "- `section:key=null`, `section:key`: remove the key\n"
1496
          "- `section=null`: remove the section\n"
1497
          "- no argument: print the entire configuration file\n"
1498
          "- `section`: print the given section\n"
1499
          "- `section:key`: print the given `key` in `section` (section can be set to `*`)"
1500
          "- `*:key`: print the given `key` in all sections"
1501
      , NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_CORE),
1502
 GF_DEF_ARG("no-save", NULL, "discard any changes made to the config file upon exit", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1503
 GF_DEF_ARG("version", NULL, "set to GPAC version, used to check config file refresh", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_HIDE|GF_ARG_SUBSYS_CORE),
1504
 GF_DEF_ARG("mod-reload", NULL, "unload / reload module shared libs when no longer used", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1505
 GF_DEF_ARG("for-test", NULL, "disable all creation/modification dates and GPAC versions in files", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1506
 GF_DEF_ARG("old-arch", NULL, "enable compatibility with pre-filters versions of GPAC", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1507
 GF_DEF_ARG("ntp-shift", NULL, "shift NTP clock by given amount in seconds", NULL, NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1508
 GF_DEF_ARG("devclass", NULL, "set device class\n"
1509
 "- ios: iOS-based mobile device\n"
1510
 "- android: Android-based mobile device\n"
1511
 "- desktop: desktop device", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_HIDE|GF_ARG_SUBSYS_CORE),
1512
1513
 GF_DEF_ARG("bs-cache-size", NULL, "cache size for bitstream read and write from file (0 disable cache, slower IOs)", "512", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1514
 GF_DEF_ARG("no-check", NULL, "disable compliance tests for inputs (ISOBMFF for now). This will likely result in random crashes", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1515
 GF_DEF_ARG("unhandled-rejection", NULL, "dump unhandled promise rejections", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1516
 GF_DEF_ARG("startup-file", NULL, "startup file of compositor in GUI mode", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1517
 GF_DEF_ARG("docs-dir", NULL, "default documents directory (for GUI on iOS and Android)", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1518
 GF_DEF_ARG("last-dir", NULL, "last working directory (for GUI)", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1519
#ifdef GPAC_HAS_POLL
1520
 GF_DEF_ARG("no-poll", NULL, "disable poll and use select for socket groups", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1521
#endif
1522
 GF_DEF_ARG("no-tls-rcfg", NULL, "disable automatic TCP to TLS reconfiguration", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1523
 GF_DEF_ARG("no-fd", NULL, "use buffered IO instead of file descriptor for read/write - this can speed up operations on small files", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1524
 GF_DEF_ARG("no-mx", NULL, "disable all mutexes, threads and semaphores (do not use if unsure about threading used)", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1525
 GF_DEF_ARG("xml-max-csize", NULL, "maximum XML content or attribute size", "100k", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1526
 GF_DEF_ARG("users", NULL, "authentication configuration file for users and groups", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1527
1528
1529
#ifndef GPAC_DISABLE_NETCAP
1530
 GF_DEF_ARG("netcap", NULL, "set packet capture and filtering rules formatted as [CFG][RULES]. Each `-netcap` argument will define a configuration\n"
1531
 "[CFG] is an optional comma-separated list of:\n"
1532
 "- id=ID: ID (string) for this configuration. If NULL, configuration will apply to all sockets not specifying a netcap ID\n"
1533
 "- src=F: read packets from `F`, as produced by GPAC or a pcap or pcapng file\n"
1534
 "- dst=F: output packets to `F` (GPAC or pcap/pcapng file), cannot be set if src is set\n"
1535
 "- loop[=N]: loop capture file N times, or forever if N is not set or negative\n"
1536
 "- nrt: disable real-time playback\n"
1537
 "[RULES] is an optional list of `[OPT,OPT2...]` with OPT in:\n"
1538
 "- m=K: set rule mode - `K` can be `r` for reception only (default), `w` for send only or `rw` for both\n"
1539
 "- s=K: set packet start range to `K`\n"
1540
 "- e=K: set packet end range to `K` - only used for `r` and `f` rules, 0 or not set means rule apply until end\n"
1541
 "- n=K: set number of packets to drop to `K` - not set, 0 or 1 means single packet\n"
1542
 "- r=K: random drop `n` packet every `K`\n"
1543
 "- f=K: drop first `n` packets every `K`\n"
1544
 "- d=K: reorder `n` packets after the next `K` packets, can be used with `f` or `r` rules\n"
1545
 "- p=K: filter packets on port `K` only, if not set the rule applies to all packets\n"
1546
 "- o=K: patch packet instead of dropping (always true for TCP), replacing byte at offset `K` (0 is first byte, <0 for random)\n"
1547
 "- v=K: set patch byte value to `K` (hexa) or negative value for random (default)\n"
1548
 "- S=K: same as `s` but adds number of capture file reload/loop\n"
1549
 "- E=K: same as `e` but adds number of capture file reload/loop\n"
1550
 "\nEX -netcap=dst=dump.gpc\n"
1551
 "This will record packets to dump.gpc\n"
1552
 "\nEX -netcap=src=dump.gpc,id=NC1 -i session1.sdp:NCID=NC1 -i session2.sdp\n"
1553
 "This will read packets from dump.gpc only for session1.sdp and let session2.sdp use regular sockets\n"
1554
 "\nEX -netcap=[p=1234,s=100,n=20][r=200,s=500,o=10,v=FE]\n"
1555
 "This will use regular network interface and drop packets 100 to 119 on port 1234 and patch one random packet every 200 starting from packet 500, setting byte 10 to FE", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1556
#endif
1557
1558
 GF_DEF_ARG("cache", NULL, "cache directory location", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1559
 GF_DEF_ARG("proxy", NULL, "set HTTP proxy server address and port (if no protocol scheme is set, use same as target)", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1560
 GF_DEF_ARG("maxrate", NULL, "set max HTTP download rate in bits per sec. 0 means unlimited", NULL, NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1561
 GF_DEF_ARG("no-cache", NULL, "disable HTTP caching", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1562
 GF_DEF_ARG("offline-cache", NULL, "enable offline HTTP caching (no re-validation of existing resource in cache)", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1563
 GF_DEF_ARG("clean-cache", NULL, "indicate if HTTP cache should be clean upon launch/exit", NULL, NULL, GF_ARG_BOOL, GF_ARG_SUBSYS_HTTP),
1564
 GF_DEF_ARG("cache-size", NULL, "specify maximum cache size on disk in bytes", "100M", NULL, GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1565
 GF_DEF_ARG("cache-check", NULL, "cache clean interval in seconds, 0 only clean cache at startup", "60", NULL, GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1566
 GF_DEF_ARG("tcp-timeout", NULL, "time in milliseconds to wait for HTTP/RTSP connect before error", "5000", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1567
 GF_DEF_ARG("req-timeout", NULL, "time in milliseconds to wait on HTTP/RTSP request before error (0 disables timeout)", "10000", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1568
 GF_DEF_ARG("no-timeout", NULL, "ignore HTTP 1.1 timeout in keep-alive", "false", NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1569
 GF_DEF_ARG("broken-cert", NULL, "enable accepting broken SSL certificates", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1570
 GF_DEF_ARG("ca-bundle", NULL, "path to a custom CA certificates bundle file", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT | GF_ARG_SUBSYS_HTTP),
1571
 GF_DEF_ARG("user-agent", "ua", "set user agent name for HTTP/RTSP", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1572
 GF_DEF_ARG("user-profileid", NULL, "set user profile ID (through **X-UserProfileID** entity header) in HTTP requests", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1573
 GF_DEF_ARG("user-profile", NULL, "set user profile filename. Content of file is appended as body to HTTP HEAD/GET requests, associated Mime is **text/xml**", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1574
 GF_DEF_ARG("query-string", NULL, "insert query string (without `?`) to URL on requests", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1575
 GF_DEF_ARG("dm-threads", NULL, "force using threads for async download requests rather than session scheduler", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1576
 GF_DEF_ARG("cte-rate-wnd", NULL, "set window analysis length in milliseconds for chunk-transfer encoding rate estimation", "20", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1577
 GF_DEF_ARG("cred", NULL, "path to 128 bits key for credential storage", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1578
1579
#if defined(GPAC_HAS_HTTP2) || defined(GPAC_HAS_CURL)
1580
 GF_DEF_ARG("no-h2", NULL, "disable HTTP2", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1581
#endif
1582
#ifdef GPAC_HAS_HTTP2
1583
 GF_DEF_ARG("no-h2c", NULL, "disable HTTP2 upgrade (i.e. over non-TLS)", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1584
 GF_DEF_ARG("h2-copy", NULL, "enable intermediate copy of data in nghttp2 (default is disabled but may report as broken frames in wireshark)", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1585
  GF_DEF_ARG("h2-max-frame", NULL, "set HTTP2 maximum frame size", "1m", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1586
#endif
1587
1588
#if defined(GPAC_HAS_NGTCP2)
1589
  GF_DEF_ARG("h3-ack-threshold", NULL, "QUIC ack threshhold (cf `ngtcp2_settings.ack_thresh`)", "2", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1590
  GF_DEF_ARG("h3-algo", NULL, "congestion control algorithm for QUIC\n"
1591
      "- cubic: CUBIC\n"
1592
      "- bbr: BBR v2\n"
1593
      "- reno: Reno",
1594
    "cubic", "cubic|bbr|reno", GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1595
  GF_DEF_ARG("h3-mwnd", NULL, "set QUIC/HTTP3 maximum window size (cf `ngtcp2_settings.max_window`)", "0", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1596
  GF_DEF_ARG("h3-st-mwnd", NULL, "set QUIC/HTTP3 maximum stream window size (cf `ngtcp2_settings.max_stream`)", "0", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1597
#endif
1598
1599
#if defined(GPAC_HAS_NGTCP2) || defined(GPAC_HAS_HTTP2)
1600
  GF_DEF_ARG("hx-max-st", NULL, "set maximum number of HTTP2 or QUIC/HTTP3 concurrent streams", "100", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1601
  GF_DEF_ARG("hx-iwnd", NULL, "set initial HTTP2 or QUIC/HTTP3 connection window size", "64m", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1602
  GF_DEF_ARG("hx-st-iwnd", NULL, "set initial HTTP2 or QUIC/HTTP3 stream window size", "32m", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1603
#endif
1604
1605
#ifdef GPAC_HAS_CURL
1606
 GF_DEF_ARG("curl", NULL, "use CURL instead of GPAC HTTP stack", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1607
#endif
1608
1609
#if defined(GPAC_HAS_NGTCP2) || defined(GPAC_HAS_CURL)
1610
 GF_DEF_ARG("h3", NULL, "set HTTP/3 mode\n"
1611
      "- no: disable HTTP/3\n"
1612
      "- first: force trying first with HTTP/3\n"
1613
      "- auto: connect using HTTP 1 or 2 and use HTTP/3 for next request(s) if announced\n"
1614
      "- only: only use HTTP/3", "auto", "no|first|auto|only", GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1615
#endif
1616
1617
#ifdef GPAC_HAS_NGTCP2
1618
 GF_DEF_ARG("h3-trace", NULL, "trace QUIC and HTTP3", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1619
#endif
1620
1621
1622
 GF_DEF_ARG("dbg-edges", NULL, "log edges status in filter graph before dijkstra resolution (for debug). Edges are logged as edge_source(status(disable_depth), weight, src_cap_idx -> dst_cap_idx)", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_FILTERS),
1623
 GF_DEF_ARG("full-link", NULL, "throw error if any PID in the filter graph cannot be linked", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_FILTERS),
1624
 GF_DEF_ARG("no-dynf", NULL, "disable dynamically loaded filters", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_FILTERS),
1625
1626
 GF_DEF_ARG("no-block", NULL, "disable blocking mode of filters\n"
1627
      "- no: enable blocking mode\n"
1628
      "- fanout: disable blocking on fan-out, unblocking the PID as soon as one of its destinations requires a packet\n"
1629
      "- all: disable blocking", "no", "no|fanout|all", GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_FILTERS),
1630
 GF_DEF_ARG("no-reg", NULL, "disable regulation (no sleep) in session", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_FILTERS),
1631
 GF_DEF_ARG("no-reassign", NULL, "disable source filter reassignment in PID graph resolution", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_FILTERS),
1632
 GF_DEF_ARG("sched", NULL, "set scheduler mode\n"
1633
    "- free: lock-free queues except for task list (default on most platforms)\n"
1634
    "- lock: mutexes for queues when several threads (default on arm64/aarch64)\n"
1635
    "- freex: lock-free queues including for task lists (experimental)\n"
1636
    "- flock: mutexes for queues even when no thread (debug mode)\n"
1637
    "- direct: no threads and direct dispatch of tasks whenever possible (debug mode)", GPAC_SCHED_DEFAULT, "free|lock|flock|freex|direct", GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_FILTERS),
1638
 GF_DEF_ARG("max-chain", NULL, "set maximum chain length when resolving filter links. Default value covers for __[ in -> ] dmx -> reframe -> decode -> encode -> reframe -> mx [ -> out]__. Filter chains loaded for adaptation (e.g. pixel format change) are loaded after the link resolution. Setting the value to 0 disables dynamic link resolution. You will have to specify the entire chain manually", "6", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_FILTERS),
1639
 GF_DEF_ARG("max-sleep", NULL, "set maximum sleep time slot in milliseconds when regulation is enabled", "50", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_FILTERS),
1640
 GF_DEF_ARG("step-link", NULL, "load filters one by one when solvink a link instead of loading all filters for the solved path", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_FILTERS),
1641
1642
 GF_DEF_ARG("threads", NULL, "set N extra thread for the session. -1 means use all available cores", NULL, NULL, GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_FILTERS),
1643
 GF_DEF_ARG("no-probe", NULL, "disable data probing on sources and relies on extension (faster load but more error-prone)", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_FILTERS),
1644
 GF_DEF_ARG("no-argchk", NULL, "disable tracking of argument usage (all arguments will be considered as used)", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_FILTERS),
1645
 GF_DEF_ARG("blacklist", NULL, "blacklist the filters listed in the given string (comma-separated list). If first character is '-', this is a whitelist, i.e. only filters listed in the given string will be allowed", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_FILTERS),
1646
 GF_DEF_ARG("no-graph-cache", NULL, "disable internal caching of filter graph connections. If disabled, the graph will be recomputed at each link resolution (lower memory usage but slower)", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_FILTERS),
1647
 GF_DEF_ARG("no-reservoir", NULL, "disable memory recycling for packets and properties. This uses much less memory but stresses the system memory allocator much more", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_FILTERS),
1648
 GF_DEF_ARG("buffer-gen", NULL, "default buffer size in microseconds for generic pids", "1000", NULL, GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_FILTERS),
1649
 GF_DEF_ARG("buffer-dec", NULL, "default buffer size in microseconds for decoder input pids", "1000000", NULL, GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_FILTERS),
1650
 GF_DEF_ARG("buffer-units", NULL, "default buffer size in frames when timing is not available", "1", NULL, GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_FILTERS),
1651
1652
 GF_DEF_ARG("check-props", NULL, "check known property types upon assignment and PID vs packet types upon fetch (in test mode, exit with error code 5 if mismatch)", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_FILTERS),
1653
1654
 GF_DEF_ARG("gl-bits-comp", NULL, "number of bits per color component in OpenGL", "8", NULL, GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_VIDEO),
1655
 GF_DEF_ARG("gl-bits-depth", NULL, "number of bits for depth buffer in OpenGL", "16", NULL, GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_VIDEO),
1656
 GF_DEF_ARG("gl-doublebuf", NULL, "enable OpenGL double buffering", "yes", NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_VIDEO),
1657
 GF_DEF_ARG("glfbo-txid", NULL, "set output texture ID when using `glfbo` output. The OpenGL context shall be initialized and gf_term_process shall be called with the OpenGL context active", NULL, NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_VIDEO),
1658
 GF_DEF_ARG("video-output", NULL, "indicate the name of the video output module to use (see `gpac -h modules`)."
1659
  " The reserved name `glfbo` is used in player mode to draw in the OpenGL texture identified by [-glfbo-txid](). "
1660
  " In this mode, the application is responsible for sending event to the compositor", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_VIDEO),
1661
1662
 GF_DEF_ARG("audio-output", NULL, "indicate the name of the audio output module to use", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_AUDIO),
1663
1664
 GF_DEF_ARG("font-reader", NULL, "indicate name of font reader module", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_TEXT),
1665
 GF_DEF_ARG("font-dirs", NULL, "indicate comma-separated list of directories to scan for fonts", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_TEXT),
1666
 GF_DEF_ARG("rescan-fonts", NULL, "indicate the font directory must be rescanned", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_TEXT),
1667
 GF_DEF_ARG("wait-fonts", NULL, "wait for SVG fonts to be loaded before displaying frames", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_TEXT),
1668
 GF_DEF_ARG("webvtt-hours", NULL, "force writing hour when serializing WebVTT", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_TEXT),
1669
GF_DEF_ARG("charset", NULL, "set charset when not recognized from input. Possible values are:\n"
1670
"- utf8: force UTF-8\n"
1671
"- utf16: force UTF-16 little endian\n"
1672
"- utf16be: force UTF-16 big endian\n"
1673
"- other: attempt to parse anyway", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_TEXT),
1674
 GF_DEF_ARG("srt-forced", NULL, "enable SRT with forced subtitles extensions", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_TEXT),
1675
1676
 GF_DEF_ARG("rmt", NULL, "enable remote monitoring webserver", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1677
 GF_DEF_ARG("rmt-port", NULL, "set rmt ws port", "6363", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1678
 GF_DEF_ARG("rmt-localhost", NULL, "make rmt ws only accepts localhost connection", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1679
 GF_DEF_ARG("rmt-sleep", NULL, "set rmt ws sleep (ms) between server updates", "10", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1680
 GF_DEF_ARG("rmt-cert", NULL, "certificate file in PEM format to use for TLS mode of rmt ws", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1681
 GF_DEF_ARG("rmt-pkey", NULL, "private key file in PEM format to use for TLS mode of rmt ws", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1682
 GF_DEF_ARG("rmt-path", NULL, "path to rmt ws JS backend", "$GSHARE/scripts/rmt/server.js", NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1683
 GF_DEF_ARG("rmt-log", NULL, "path to backend log recording, skip recording if NULL", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1684
 GF_DEF_ARG("userws-port", NULL, "set user ws port", "6364", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1685
 GF_DEF_ARG("userws-localhost", NULL, "make user ws only accepts localhost connection", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1686
 GF_DEF_ARG("userws-sleep", NULL, "set user ws sleep (ms) between server updates", "10", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1687
 GF_DEF_ARG("userws-cert", NULL, "certificate file in PEM format to use for TLS mode for user ws", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1688
 GF_DEF_ARG("userws-pkey", NULL, "private key file in PEM format to use for TLS mode for user ws", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1689
1690
1691
 GF_DEF_ARG("diso-nosize", NULL, "skip box size info when dumping ISOBMFF", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1692
1693
 GF_DEF_ARG("m2ts-vvc-old", NULL, "hack for old TS streams using 0x32 for VVC instead of 0x33", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HACKS),
1694
 GF_DEF_ARG("piff-force-subsamples", NULL, "hack for PIFF PSEC files generated by 0.9.0 and 1.0 MP4Box with wrong subsample_count inserted for audio", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HACKS),
1695
 GF_DEF_ARG("vvdec-annexb", NULL, "hack for old vvdec+libavcodec supporting only annexB format", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HACKS),
1696
 GF_DEF_ARG("heif-hevc-urn", NULL, "use HEVC URN for alpha and depth in HEIF instead of MPEG-B URN (HEIF first edition)", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HACKS),
1697
 GF_DEF_ARG("boxdir", NULL, "use box definitions in the given directory for XML dump", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HACKS),
1698
 GF_DEF_ARG("no-mabr-patch", NULL, "disable GPAC parsing of patched isom boxes from mabr (will behave like most browsers/players)", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HACKS),
1699
 GF_DEF_ARG("no-cdrf", NULL, "disable cdrf sample dep", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HACKS),
1700
 GF_DEF_ARG("no-evp", NULL, "disable all OpenSSL hardware acceleration", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HACKS),
1701
1702
1703
 {0}
1704
};
1705
1706
1707
const GF_Config *gf_sys_get_lang_file();
1708
1709
GF_EXPORT
1710
const GF_GPACArg *gf_sys_get_options()
1711
0
{
1712
0
  return GPAC_Args;
1713
0
}
1714
1715
static const char *gpac_opt_default(const char *argname)
1716
713k
{
1717
713k
  const GF_GPACArg *arg = NULL;
1718
713k
  u32 i=0;
1719
25.2M
  while (GPAC_Args[i].name) {
1720
25.2M
    arg = &GPAC_Args[i];
1721
25.2M
    i++;
1722
25.2M
    if (!strcmp(arg->name, argname)) break;
1723
24.5M
    arg = NULL;
1724
24.5M
  }
1725
713k
  if (!arg) return NULL;
1726
710k
  return arg->val;
1727
713k
}
1728
1729
GF_EXPORT
1730
Bool gf_opts_get_bool(const char *secName, const char *keyName)
1731
740k
{
1732
740k
  const char *opt = gf_opts_get_key(secName, keyName);
1733
1734
740k
  if (!opt && !strcmp(secName, "core")) {
1735
652k
    opt = gpac_opt_default(keyName);
1736
652k
  }
1737
1738
740k
  if (!opt) return GF_FALSE;
1739
0
  if (!strcmp(opt, "yes")) return GF_TRUE;
1740
0
  if (!strcmp(opt, "true")) return GF_TRUE;
1741
0
  if (!strcmp(opt, "1")) return GF_TRUE;
1742
0
  return GF_FALSE;
1743
0
}
1744
GF_EXPORT
1745
u32 gf_opts_get_int(const char *secName, const char *keyName)
1746
65.7k
{
1747
65.7k
  u32 times=1, val;
1748
65.7k
  char *opt = (char *) gf_opts_get_key(secName, keyName);
1749
1750
65.7k
  if (!opt && !strcmp(secName, "core")) {
1751
60.4k
    opt = (char *) gpac_opt_default(keyName);
1752
60.4k
  }
1753
65.7k
  if (!opt || !opt[0]) return 0;
1754
59.5k
  val = (u32) strlen(opt);
1755
59.5k
  char c = opt[val-1];
1756
59.5k
  switch (c) {
1757
1
  case 'k':
1758
1
  case 'K':
1759
1
    times=1000;
1760
1
    break;
1761
0
  case 'm':
1762
2.67k
  case 'M':
1763
2.67k
    times=1000000;
1764
2.67k
    break;
1765
59.5k
  }
1766
59.5k
  val = atoi(opt);
1767
59.5k
  return val*times;
1768
59.5k
}
1769
1770
GF_EXPORT
1771
Bool gf_sys_set_cfg_option(const char *opt_string)
1772
0
{
1773
0
  size_t sepIdx;
1774
0
  char *sep, *sep2, szSec[1024], szKey[1024], szVal[1024];
1775
0
  sep = strchr(opt_string, ':');
1776
0
  if (!sep) {
1777
0
    sep = strchr(opt_string, '=');
1778
0
    if (sep && !stricmp(sep, "=null")) {
1779
0
      sepIdx = sep - opt_string;
1780
0
      if (sepIdx>=1024) sepIdx = 1023;
1781
0
      memcpy(szSec, opt_string, sepIdx);
1782
0
      szSec[sepIdx] = 0;
1783
0
      gf_opts_del_section(szSec);
1784
0
      return  GF_TRUE;
1785
0
    }
1786
1787
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[CoreArgs] Badly formatted option %s - expected Section:Name=Value\n", opt_string ) );
1788
0
    return GF_FALSE;
1789
0
  }
1790
1791
0
  sepIdx = sep - opt_string;
1792
0
  if (sepIdx>=1024)
1793
0
    sepIdx = 1023;
1794
0
  memcpy(szSec, opt_string, sepIdx);
1795
0
  szSec[sepIdx] = 0;
1796
1797
0
  sep ++;
1798
0
  sep2 = strchr(sep, '=');
1799
0
  if (!sep2) {
1800
0
    gf_opts_set_key(szSec, sep, NULL);
1801
0
    return  GF_TRUE;
1802
0
  }
1803
1804
0
  sepIdx = sep2 - sep;
1805
0
  if (sepIdx>=1024)
1806
0
    sepIdx = 1023;
1807
0
  memcpy(szKey, sep, sepIdx);
1808
0
  szKey[sepIdx] = 0;
1809
1810
0
  sepIdx = strlen(sep2+1);
1811
0
  if (sepIdx>=1024)
1812
0
    sepIdx = 1023;
1813
0
  memcpy(szVal, sep2+1, sepIdx);
1814
0
  szVal[sepIdx] = 0;
1815
1816
0
  if (!stricmp(szKey, "*")) {
1817
0
    if (stricmp(szVal, "null")) {
1818
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[CoreArgs] Badly formatted option %s - expected Section:*=null\n", opt_string));
1819
0
      return GF_FALSE;
1820
0
    }
1821
0
    gf_opts_del_section(szSec);
1822
0
    return GF_TRUE;
1823
0
  }
1824
1825
0
  if (!stricmp(szVal, "null")) {
1826
0
    szVal[0]=0;
1827
0
  }
1828
0
  gf_opts_set_key(szSec, szKey, szVal[0] ? szVal : NULL);
1829
1830
0
  if (!strcmp(szSec, "core") || !strcmp(szSec, "temp")) {
1831
0
    if (!strcmp(szKey, "noprog") && (!strcmp(szVal, "yes")||!strcmp(szVal, "true")||!strcmp(szVal, "1")) ) {
1832
0
      void gpac_disable_progress();
1833
1834
0
      gpac_disable_progress();
1835
0
    }
1836
0
  }
1837
0
  return GF_TRUE;
1838
0
}
1839
1840
void gf_module_reload_dirs();
1841
1842
Bool gf_opts_load_option(const char *arg_name, const char *val, Bool *consumed_next, GF_Err *e)
1843
0
{
1844
0
  const GF_GPACArg *arg = NULL;
1845
0
  u32 i=0;
1846
0
  *e = GF_OK;
1847
0
  *consumed_next = GF_FALSE;
1848
0
  arg_name = arg_name+1;
1849
0
  while (GPAC_Args[i].name) {
1850
0
    arg = &GPAC_Args[i];
1851
0
    i++;
1852
0
    if (!strcmp(arg->name, arg_name)) break;
1853
0
    if (arg->altname && !strcmp(arg->altname, arg_name)) break;
1854
1855
0
    arg = NULL;
1856
0
  }
1857
0
  if (!arg) return GF_FALSE;
1858
1859
0
  if (!strcmp(arg->name, "cfg")) {
1860
0
    *consumed_next = GF_TRUE;
1861
0
    if (val && strchr(val, '=')) {
1862
0
      if (! gf_sys_set_cfg_option(val)) *e = GF_BAD_PARAM;
1863
0
    } else {
1864
0
      u32 sec_len = 0;
1865
0
      char *sep = val ? strchr(val, ':') : NULL;
1866
0
      u32 sec_count = gf_opts_get_section_count();
1867
0
      if (sep) {
1868
0
        sec_len = (u32) (sep - val - 1);
1869
0
        sep++;
1870
0
      } else if (val) {
1871
0
        sec_len = (u32) strlen(val);
1872
0
      }
1873
0
      for (i=0; i<sec_count; i++) {
1874
0
        u32 k, key_count;
1875
0
        Bool sec_hdr_done = GF_FALSE;
1876
0
        const char *sname = gf_opts_get_section_name(i);
1877
0
        key_count = sname ? gf_opts_get_key_count(sname) : 0;
1878
0
        if (!key_count) continue;
1879
1880
0
        if (sec_len) {
1881
0
          if (!strncmp(val, "*", sec_len) || !strncmp(val, "@", sec_len)) {
1882
0
          } else if (strncmp(val, sname, sec_len) || (sec_len != (u32) strlen(sname) ) ) {
1883
0
            continue;
1884
0
          }
1885
0
        }
1886
0
        for (k=0; k<key_count; k++) {
1887
0
          const char *kname = gf_opts_get_key_name(sname, k);
1888
0
          const char *kval = kname ? gf_opts_get_key(sname, kname) : NULL;
1889
0
          if (!kval) continue;
1890
0
          if (sep && strcmp(sep, kname)) continue;
1891
1892
0
          if (!sec_hdr_done) {
1893
0
            sec_hdr_done = GF_TRUE;
1894
0
            fprintf(stdout, "[%s]\n", sname);
1895
0
          }
1896
0
          fprintf(stdout, "%s=%s\n", kname, kval);
1897
0
        }
1898
0
        if (sec_hdr_done)
1899
0
          fprintf(stdout, "\n");
1900
0
      }
1901
0
    }
1902
0
    return GF_TRUE;
1903
0
  }
1904
0
  if (!strcmp(arg->name, "strict-error")) {
1905
0
    gf_log_set_strict_error(1);
1906
0
    return GF_TRUE;
1907
0
  }
1908
1909
0
  if (arg->type==GF_ARG_BOOL) {
1910
0
    if (!val) gf_opts_set_key("temp", arg->name, "yes");
1911
0
    else {
1912
0
      if (!strcmp(val, "yes") || !strcmp(val, "true") || !strcmp(val, "1")) {
1913
0
        *consumed_next = GF_TRUE;
1914
0
        gf_opts_set_key("temp", arg->name, "yes");
1915
0
      } else {
1916
0
        if (!strcmp(val, "no") || !strcmp(val, "false") || !strcmp(val, "0")) {
1917
0
          *consumed_next = GF_TRUE;
1918
0
          gf_opts_set_key("temp", arg->name, "no");
1919
0
        } else {
1920
0
          gf_opts_set_key("temp", arg->name, "yes");
1921
0
        }
1922
0
      }
1923
0
    }
1924
0
  } else {
1925
0
    *consumed_next = GF_TRUE;
1926
0
    if (!val && (arg->type==GF_ARG_BOOL))
1927
0
      gf_opts_set_key("temp", arg->name, "true");
1928
0
    else {
1929
0
      gf_opts_set_key("temp", arg->name, val);
1930
0
      if (!strcmp(arg->name, "mod-dirs")) {
1931
0
        gf_module_reload_dirs();
1932
0
      }
1933
0
    }
1934
0
  }
1935
0
  return GF_TRUE;
1936
0
}
1937
1938
GF_EXPORT
1939
u32 gf_sys_is_gpac_arg(const char *arg_name)
1940
0
{
1941
0
  char *argsep;
1942
0
  u32 arglen;
1943
0
  const GF_GPACArg *arg = NULL;
1944
0
  u32 i=0;
1945
0
  arg_name = arg_name+1;
1946
0
  if (arg_name[0]=='-')
1947
0
    return 1;
1948
0
  if (arg_name[0]=='+')
1949
0
    return 1;
1950
1951
0
  argsep = strchr(arg_name, '=');
1952
0
  if (argsep) arglen = (u32) (argsep - arg_name);
1953
0
  else arglen = (u32) strlen(arg_name);
1954
1955
0
  while (GPAC_Args[i].name) {
1956
0
    arg = &GPAC_Args[i];
1957
0
    i++;
1958
0
    if ((strlen(arg->name) == arglen) && !strncmp(arg->name, arg_name, arglen)) break;
1959
0
    if (arg->altname) {
1960
0
      char *alt = strstr(arg->altname, arg_name);
1961
0
      if (alt) {
1962
0
        char c = alt[strlen(arg_name)];
1963
0
        if (!c || (c==' ')) break;
1964
0
      }
1965
0
    }
1966
0
    arg = NULL;
1967
0
  }
1968
0
  if (!arg) return 0;
1969
0
  if (arg->type==GF_ARG_BOOL) return 1;
1970
0
  if (argsep) return 1;
1971
0
  return 2;
1972
0
}
1973
1974
1975
GF_EXPORT
1976
void gf_sys_print_arg(FILE *helpout, GF_SysPrintArgFlags flags, const GF_GPACArg *arg, const char *arg_subsystem)
1977
0
{
1978
0
  u32 gen_doc = 0;
1979
0
  if (flags & GF_PRINTARG_MD)
1980
0
    gen_doc = 1;
1981
0
  if (!helpout) helpout = stderr;
1982
1983
//#ifdef GPAC_ENABLE_COVERAGE
1984
0
#if 1
1985
0
  if ((arg->name[0]>='A') && (arg->name[0]<='Z')) {
1986
0
    if ((arg->name[1]<'A') || (arg->name[1]>'Z')) {
1987
0
      fprintf(stderr, "\nWARNING: arg %s bad name format, should use lowercase\n", arg->name);
1988
0
      exit(1);
1989
0
    }
1990
0
  }
1991
0
  if (arg->description) {
1992
0
    char *sep;
1993
1994
0
    if ((arg->description[0]>='A') && (arg->description[0]<='Z')) {
1995
0
      if ((arg->description[1]<'A') || (arg->description[1]>'Z')) {
1996
0
        fprintf(stderr, "\nWARNING: arg %s bad name format \"%s\", should use lowercase\n", arg->name, arg->description);
1997
0
        exit(1);
1998
0
      }
1999
0
    }
2000
0
    if (strchr(arg->description, '\t')) {
2001
0
      fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should not use tab\n", arg->name, arg->description);
2002
0
      exit(1);
2003
0
    }
2004
2005
0
    u8 achar = arg->description[strlen(arg->description)-1];
2006
0
    if (achar == '.') {
2007
0
      fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should not end with .\n", arg->name, arg->description);
2008
0
      exit(1);
2009
0
    }
2010
0
    sep = strstr(arg->description, ".\n");
2011
0
    if (sep) {
2012
0
      fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should not contain .\\n \n", arg->name, arg->description);
2013
0
      exit(1);
2014
0
    }
2015
0
    sep = strstr(arg->description, "- ");
2016
0
    if (sep && (sep[-1]!='\n') && !strcmp(sep, "- see")) {
2017
0
      fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should have \\n before first bullet\n", arg->name, arg->description);
2018
0
      exit(1);
2019
0
    }
2020
2021
0
    sep = strchr(arg->description, ' ');
2022
0
    if (sep && (sep>arg->description)) {
2023
0
      sep--;
2024
0
      if ((sep[0] == 's') && (sep[-1] != 's')) {
2025
0
        fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should use infintive\n", arg->name, arg->description);
2026
0
        exit(1);
2027
0
      }
2028
0
    }
2029
0
  }
2030
0
#endif
2031
2032
0
  if (arg->flags & GF_ARG_HINT_HIDE)
2033
0
    return;
2034
2035
0
  const char *syntax=strchr(arg->name, ' ');
2036
0
  char *arg_name=NULL;
2037
0
  if (syntax) {
2038
0
    arg_name = gf_strdup(arg->name);
2039
0
    char *sep = strchr(arg_name, ' ');
2040
0
    sep[0]=0;
2041
0
  }
2042
2043
0
  if (flags & GF_PRINTARG_MAN) {
2044
0
    fprintf(helpout, ".TP\n.B %s%s", (flags&GF_PRINTARG_NO_DASH) ? "" : "\\-", arg_name ? arg_name : arg->name);
2045
0
  }
2046
0
  else if (gen_doc==1) {
2047
0
    fprintf(helpout,"<div markdown class=\"option\">\n");
2048
0
    if (flags&GF_PRINTARG_NO_DASH) {
2049
0
      gf_sys_format_help(helpout, flags | GF_PRINTARG_HIGHLIGHT_FIRST, "%s", arg_name ? arg_name : arg->name);
2050
0
    } else {
2051
0
      if (arg->flags & (GF_ARG_HINT_ADVANCED|GF_ARG_HINT_EXPERT)) {
2052
0
        gf_sys_format_help(helpout, flags, "<a id=\"%s\">", arg_name ? arg_name : arg->name);
2053
0
      } else {
2054
0
        gf_sys_format_help(helpout, flags, "<a id=\"%s\" data-level=\"basic\">", arg_name ? arg_name : arg->name);
2055
0
      }
2056
0
      gf_sys_format_help(helpout, flags | GF_PRINTARG_HIGHLIGHT_FIRST, "-%s", arg_name ? arg_name : arg->name);
2057
0
      gf_sys_format_help(helpout, flags, "</a>");
2058
0
    }
2059
0
  } else {
2060
0
    gf_sys_format_help(helpout, flags | GF_PRINTARG_HIGHLIGHT_FIRST, "%s%s%s",
2061
0
      (flags&GF_PRINTARG_ADD_DASH) ? "-" : "",
2062
0
      (flags&GF_PRINTARG_NO_DASH) ? "" : ((flags&GF_PRINTARG_COLON) ? ":" : "-"),
2063
0
      arg_name ? arg_name : arg->name
2064
0
    );
2065
0
  }
2066
0
  if (arg->altname) {
2067
0
    gf_sys_format_help(helpout, flags, ",");
2068
0
    gf_sys_format_help(helpout, flags | GF_PRINTARG_HIGHLIGHT_FIRST, "%s-%s", (flags&GF_PRINTARG_ADD_DASH) ? "-" : "", arg->altname);
2069
0
  }
2070
0
  if (syntax) {
2071
0
    gf_sys_format_help(helpout, flags, " %s", syntax);
2072
0
    gf_free(arg_name);
2073
0
  }
2074
2075
0
  if (arg->type==GF_ARG_CUSTOM) {
2076
0
    if (arg->val)
2077
0
      gf_sys_format_help(helpout, flags, " `%s`", arg->val);
2078
0
  } else if (arg->type==GF_ARG_INT && arg->values && strchr(arg->values, '|')) {
2079
0
    gf_sys_format_help(helpout, flags, " (Enum");
2080
0
    if (arg->val)
2081
0
      gf_sys_format_help(helpout, flags, ", default: **%s**", arg->val);
2082
0
    gf_sys_format_help(helpout, flags, ")");
2083
0
  } else if (arg->type != GF_ARG_BOOL) {
2084
0
    gf_sys_format_help(helpout, flags, " (");
2085
0
    switch (arg->type) {
2086
0
    case GF_ARG_BOOL: gf_sys_format_help(helpout, flags, "boolean"); break;
2087
0
    case GF_ARG_INT: gf_sys_format_help(helpout, flags, "int"); break;
2088
0
    case GF_ARG_DOUBLE: gf_sys_format_help(helpout, flags, "number"); break;
2089
0
    case GF_ARG_STRING: gf_sys_format_help(helpout, flags, "string"); break;
2090
0
    case GF_ARG_STRINGS: gf_sys_format_help(helpout, flags, "string list"); break;
2091
0
    case GF_ARG_4CC: gf_sys_format_help(helpout, flags, "4CC"); break;
2092
0
    case GF_ARG_4CCS: gf_sys_format_help(helpout, flags, "4CC list"); break;
2093
0
    default: break;
2094
0
    }
2095
0
    if (arg->val)
2096
0
      gf_sys_format_help(helpout, flags, ", default: **%s**", arg->val);
2097
0
    if (arg->values)
2098
0
      gf_sys_format_help(helpout, flags, ", values: __%s__", arg->values);
2099
0
    gf_sys_format_help(helpout, flags, ")");
2100
0
  }
2101
2102
0
  if (flags & GF_PRINTARG_MAN) {
2103
0
    gf_sys_format_help(helpout, flags, "\n%s\n", gf_sys_localized(arg_subsystem, arg->name, arg->description) );
2104
0
  } else {
2105
0
    if (arg->description) {
2106
0
      gf_sys_format_help(helpout, flags | GF_PRINTARG_OPT_DESC, ": %s", gf_sys_localized(arg_subsystem, arg->name, arg->description) );
2107
0
    }
2108
0
    gf_sys_format_help(helpout, flags, "\n");
2109
0
    if(gen_doc==1) {
2110
0
      fprintf(helpout, "</div>\n");
2111
0
    }
2112
0
  }
2113
2114
0
  if ((gen_doc==1) && arg->description && strstr(arg->description, "- "))
2115
0
    gf_sys_format_help(helpout, flags, "\n");
2116
0
}
2117
2118
2119
GF_EXPORT
2120
void gf_sys_print_core_help(FILE *helpout, GF_SysPrintArgFlags flags, GF_SysArgMode mode, u32 subsystem_flags)
2121
0
{
2122
0
  u32 i=0;
2123
0
  const GF_GPACArg *args = gf_sys_get_options();
2124
2125
0
  while (args[i].name) {
2126
0
    const GF_GPACArg *arg = &args[i];
2127
0
    i++;
2128
0
    if (arg->flags & GF_ARG_HINT_HIDE) continue;
2129
2130
0
    if (subsystem_flags && !(arg->flags & subsystem_flags)) {
2131
0
      continue;
2132
0
    }
2133
0
    if (mode != GF_ARGMODE_ALL) {
2134
0
      if ((mode==GF_ARGMODE_EXPERT) && !(arg->flags & GF_ARG_HINT_EXPERT)) continue;
2135
0
      else if ((mode==GF_ARGMODE_ADVANCED) && !(arg->flags & GF_ARG_HINT_ADVANCED)) continue;
2136
0
      else if ((mode==GF_ARGMODE_BASE) && (arg->flags & (GF_ARG_HINT_ADVANCED|GF_ARG_HINT_EXPERT) )) continue;
2137
0
    }
2138
0
    gf_sys_print_arg(helpout, flags, arg, "core");
2139
0
  }
2140
0
}
2141
2142
2143
0
#define LINE_OFFSET_DESCR 30
2144
2145
static char *help_buf = NULL;
2146
static u32 help_buf_size=0;
2147
2148
void gf_sys_cleanup_help()
2149
3.05k
{
2150
3.05k
  if (help_buf) {
2151
0
    gf_free(help_buf);
2152
0
    help_buf = NULL;
2153
0
    help_buf_size = 0;
2154
0
  }
2155
3.05k
}
2156
2157
2158
enum
2159
{
2160
  TOK_CODE,
2161
  TOK_BOLD,
2162
  TOK_ITALIC,
2163
  TOK_STRIKE,
2164
  TOK_OPTLINK,
2165
  TOK_LINKSTART,
2166
};
2167
struct _token {
2168
  char *tok;
2169
  GF_ConsoleCodes cmd_type;
2170
} Tokens[] =
2171
{
2172
 {"`", GF_CONSOLE_YELLOW|GF_CONSOLE_ITALIC},
2173
 {"**", GF_CONSOLE_BOLD},
2174
 {"__", GF_CONSOLE_ITALIC},
2175
 {"~~", GF_CONSOLE_STRIKE},
2176
 {"[-", GF_CONSOLE_YELLOW|GF_CONSOLE_ITALIC},
2177
 {"[", GF_CONSOLE_YELLOW|GF_CONSOLE_ITALIC},
2178
};
2179
static u32 nb_tokens = sizeof(Tokens) / sizeof(struct _token);
2180
2181
static u32 line_pos = 0;
2182
2183
//#define CHECK_BALANCED_SEPS
2184
2185
#ifdef CHECK_BALANCED_SEPS
2186
static void check_char_balanced(char *buf, char c)
2187
{
2188
  char *txt = buf;
2189
  while (txt) {
2190
    char *bquote_next;
2191
    char *bquote = strchr(txt, c);
2192
    if (!bquote) break;
2193
    if (c=='\'') {
2194
      if ((bquote[1]=='s') && (bquote[2]==' ')) {
2195
        txt = bquote + 1;
2196
        continue;
2197
      }
2198
    }
2199
    bquote_next = strchr(bquote+1, c);
2200
    if (!bquote_next) {
2201
      fprintf(stderr, "Missing closing %c after %s\n", c, bquote);
2202
      exit(1);
2203
    }
2204
    switch (bquote_next[1] ) {
2205
    case 0:
2206
    case '\n':
2207
    case ' ':
2208
    case ',':
2209
    case '.':
2210
    case ')':
2211
    case ']':
2212
    case ':':
2213
      break;
2214
    default:
2215
      if (c=='\'') {
2216
        if ((bquote_next[1]>='A') && (bquote_next[1]<='Z'))
2217
          break;
2218
      }
2219
      fprintf(stderr, "Missing space after closing %c %s\n", c, bquote_next);
2220
      exit(1);
2221
    }
2222
    txt = bquote_next + 1;
2223
  }
2224
}
2225
#endif
2226
2227
GF_EXPORT
2228
void gf_sys_format_help(FILE *helpout, GF_SysPrintArgFlags flags, const char *fmt, ...)
2229
0
{
2230
0
  char *line;
2231
0
  u32 len;
2232
0
  va_list vlist;
2233
0
  Bool escape_xml = GF_FALSE;
2234
0
  Bool escape_pipe = GF_FALSE;
2235
0
  Bool prev_was_example = GF_FALSE;
2236
0
  Bool prev_has_line_after = GF_FALSE;
2237
0
  Bool prev_has_colon = GF_FALSE;
2238
0
  u32 list_depth = 0;
2239
0
  u32 gen_doc = 0;
2240
0
  u32 is_app_opts = 0;
2241
0
  if (flags & GF_PRINTARG_MD) {
2242
0
    gen_doc = 1;
2243
0
    if (flags & GF_PRINTARG_ESCAPE_XML)
2244
0
      escape_xml = GF_TRUE;
2245
0
    if (flags & GF_PRINTARG_ESCAPE_PIPE)
2246
0
      escape_pipe = GF_TRUE;
2247
0
  }
2248
0
  if (flags & GF_PRINTARG_MAN)
2249
0
    gen_doc = 2;
2250
0
  if (flags & GF_PRINTARG_IS_APP)
2251
0
    is_app_opts = 1;
2252
0
  if (!helpout) helpout = stderr;
2253
2254
0
  va_start(vlist, fmt);
2255
0
  len=vsnprintf(NULL, 0, fmt, vlist);
2256
0
  va_end(vlist);
2257
0
  if (help_buf_size < len+2) {
2258
0
    help_buf_size = len+2;
2259
0
    help_buf = gf_realloc(help_buf, help_buf_size);
2260
0
  }
2261
0
  va_start(vlist, fmt);
2262
0
  vsprintf(help_buf, fmt, vlist);
2263
0
  va_end(vlist);
2264
2265
#ifdef CHECK_BALANCED_SEPS
2266
  if (gen_doc) {
2267
    check_char_balanced(help_buf, '`');
2268
    check_char_balanced(help_buf, '\'');
2269
  }
2270
#endif
2271
2272
/*#ifdef GPAC_CONFIG_ANDROID
2273
  //on android use logs for help print
2274
  if (!gen_doc) {
2275
    GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("%s", help_buf));
2276
    return;
2277
  }
2278
#endif
2279
*/
2280
2281
0
  line = help_buf;
2282
0
  while (line[0]) {
2283
0
    u32 att_len = 0;
2284
0
    char *tok_sep = NULL;
2285
0
    GF_ConsoleCodes console_code = GF_CONSOLE_RESET;
2286
0
    Bool line_before = GF_FALSE;
2287
0
    Bool line_after = GF_FALSE;
2288
0
    Bool add_backquote = GF_FALSE;
2289
2290
0
    const char *footer_string = NULL;
2291
0
    const char *header_string = NULL;
2292
0
    char *next_line = strchr(line, '\n');
2293
0
    Bool has_token=GF_FALSE;
2294
2295
0
    if (next_line) next_line[0]=0;
2296
2297
0
    if (prev_has_line_after && !strlen(line)) {
2298
0
      if (!next_line) break;
2299
0
      line = next_line+1;
2300
0
      line_pos=0;
2301
0
      continue;
2302
0
    }
2303
0
    if (!line[0]) flags &= ~GF_PRINTARG_HIGHLIGHT_FIRST;
2304
2305
    //detect list start/end for mkdocs
2306
0
    if (gen_doc==1) {
2307
0
      u32 llev = 0;
2308
0
      if (!strncmp(line, "- ", 2)) llev=1;
2309
0
      else if (!strncmp(line, " - ", 3) || !strncmp(line, "  - ", 3)) llev=2;
2310
0
      else if (!strncmp(line, "    - ", 6)) llev=3;
2311
0
      if (llev>list_depth) {
2312
0
        list_depth = llev;
2313
0
        fprintf(helpout, "\n");
2314
0
      }
2315
0
      else if (llev<list_depth) {
2316
0
        list_depth = llev;
2317
0
        fprintf(helpout, "\n");
2318
0
      }
2319
0
    }
2320
2321
0
    if ((line[0]=='#') && (line[1]==' ')) {
2322
0
      if (!gen_doc)
2323
0
        line+=2;
2324
0
      else if (gen_doc==2) {
2325
0
        header_string = ".SH ";
2326
0
        footer_string = "\n.LP";
2327
0
        line+=2;
2328
0
      }
2329
2330
0
      console_code = GF_CONSOLE_GREEN;
2331
0
      line_after = line_before = GF_TRUE;
2332
0
    } else if ((line[0]=='#') && (line[1]=='#') && (line[2]==' ')) {
2333
0
      if (!gen_doc)
2334
0
        line+=3;
2335
0
      else if (gen_doc==2) {
2336
0
        line+=3;
2337
0
        header_string = ".SS ";
2338
0
      }
2339
2340
0
      console_code = GF_CONSOLE_MAGENTA;
2341
0
      line_before = GF_TRUE;
2342
0
    } else if ((line[0]=='#') && (line[1]=='#') && (line[2]=='#') && (line[3]==' ')) {
2343
0
      if (!gen_doc)
2344
0
        line+=4;
2345
0
      else if (gen_doc==2) {
2346
0
        line+=4;
2347
0
        header_string = ".P\n.B\n";
2348
0
      }
2349
2350
0
      console_code = GF_CONSOLE_CYAN;
2351
0
      line_after = GF_TRUE;
2352
0
    } else if ((line[0]=='E') && (line[1]=='X') && (line[2]==' ')) {
2353
0
      line+=3;
2354
0
      console_code = GF_CONSOLE_YELLOW;
2355
2356
0
      if (gen_doc==1) {
2357
0
        header_string = prev_has_colon ? "```\n" : "Example\n```\n";
2358
0
        footer_string = "\n```\n";
2359
0
      } else if (gen_doc==2) {
2360
0
        header_string = prev_has_colon ? ".br\n" : "Example\n.br\n";
2361
0
        footer_string = "\n.br\n";
2362
0
      } else {
2363
0
        header_string = prev_has_colon ? NULL : "Example:\n";
2364
0
      }
2365
2366
0
      if (prev_was_example) {
2367
0
        header_string = NULL;
2368
0
      }
2369
2370
0
      if (next_line && (next_line[1]=='E') && (next_line[2]=='X') && (next_line[3]==' ')) {
2371
0
        prev_was_example = GF_TRUE;
2372
0
        footer_string = NULL;
2373
0
      } else {
2374
0
        prev_was_example = GF_FALSE;
2375
0
      }
2376
0
    } else if (!strncmp(line, "Note: ", 6)) {
2377
0
      console_code = GF_CONSOLE_CYAN | GF_CONSOLE_ITALIC;
2378
0
    } else if (!strncmp(line, "Warning: ", 9)) {
2379
0
      line_after = line_before = GF_TRUE;
2380
0
      console_code = GF_CONSOLE_RED | GF_CONSOLE_BOLD;
2381
0
    } else if ( (
2382
0
       ((line[0]=='-') && (line[1]==' '))
2383
0
       || ((line[0]==' ') && (line[1]=='-') && (line[2]==' '))
2384
0
       || ((line[0]==' ') && (line[1]==' ') && (line[2]=='-') && (line[3]==' '))
2385
0
      )
2386
2387
      //look for ": "
2388
0
      && ( ((tok_sep=strstr(line, ": ")) != NULL ) || list_depth)
2389
0
    ) {
2390
0
      if (!gen_doc)
2391
0
        fprintf(helpout, "\t");
2392
0
      while (line[0] != '-') {
2393
0
        if (!list_depth) {
2394
0
          fprintf(helpout, " ");
2395
0
        }
2396
0
        line++;
2397
0
        line_pos++;
2398
2399
0
      }
2400
0
      if (list_depth && (gen_doc==1)) {
2401
0
        if (list_depth==3)
2402
0
          fprintf(helpout, "        - ");
2403
0
        else if (list_depth==2)
2404
0
          fprintf(helpout, "    - ");
2405
0
        else fprintf(helpout, "- ");
2406
        //for MD avoid "- #" which corrupts heading levels, enclose with backquote
2407
0
        if (tok_sep && ((line[2]=='#') || (line[3]=='#') || (line[4]=='#'))) {
2408
0
          fprintf(helpout, "`");
2409
0
          add_backquote=GF_TRUE;
2410
0
        }
2411
0
      } else {
2412
0
        fprintf(helpout, "* ");
2413
0
      }
2414
0
      line_pos+=2;
2415
0
      if (!gen_doc)
2416
0
        gf_sys_set_console_code(helpout, GF_CONSOLE_YELLOW);
2417
0
      if (tok_sep) {
2418
0
        tok_sep[0] = 0;
2419
0
        fprintf(helpout, "%s", line+2);
2420
0
        if (add_backquote) {
2421
0
          fprintf(helpout, "`");
2422
0
          add_backquote = GF_FALSE;
2423
0
        }
2424
0
        line_pos += (u32) strlen(line+2);
2425
0
        tok_sep[0] = ':';
2426
0
        line = tok_sep;
2427
0
      } else {
2428
0
        line += 2;
2429
0
      }
2430
0
      if (!gen_doc)
2431
0
        gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2432
0
    } else if (flags & (GF_PRINTARG_HIGHLIGHT_FIRST | GF_PRINTARG_OPT_DESC)) {
2433
0
      char *sep = strchr(line, ' ');
2434
2435
0
      if (sep) sep[0] = 0;
2436
2437
0
      if (!gen_doc && !(flags & GF_PRINTARG_OPT_DESC) )
2438
0
        gf_sys_set_console_code(helpout, GF_CONSOLE_GREEN);
2439
2440
0
      if ((gen_doc==1) && !(flags & GF_PRINTARG_OPT_DESC) ) {
2441
0
        fprintf(helpout, "__%s__", line);
2442
0
        line_pos += 4+ (u32) strlen(line);
2443
0
      } else {
2444
0
        fprintf(helpout, "%s", line);
2445
0
        line_pos += (u32) strlen(line);
2446
0
      }
2447
2448
0
      if (!gen_doc && !(flags & GF_PRINTARG_OPT_DESC) )
2449
0
        gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2450
2451
0
      if (flags & GF_PRINTARG_OPT_DESC) {
2452
0
        flags = 0;
2453
0
        att_len = line_pos;
2454
0
      }
2455
2456
2457
0
      if (sep) {
2458
0
        sep[0] = ' ';
2459
0
        line = sep;
2460
0
      } else {
2461
0
        line = NULL;
2462
0
      }
2463
0
    }
2464
0
    if (!line) break;
2465
0
    if (gen_doc==2) {
2466
0
      line_before = line_after = GF_FALSE;
2467
0
    }
2468
2469
0
    if (prev_has_line_after) line_before = GF_FALSE;
2470
0
    prev_has_line_after = GF_FALSE;
2471
0
    if (!strlen(line))
2472
0
      prev_has_line_after = GF_TRUE;
2473
2474
0
    if (line_before) {
2475
0
      fprintf(helpout, "\n");
2476
0
      line_pos=0;
2477
0
    }
2478
2479
0
    if (console_code != GF_CONSOLE_RESET) {
2480
0
      if (gen_doc==1) {
2481
0
        if (console_code & GF_CONSOLE_BOLD) {
2482
0
          fprintf(helpout, "__");
2483
0
          line_pos+=2;
2484
0
        }
2485
0
        else if (console_code & GF_CONSOLE_ITALIC) {
2486
0
          fprintf(helpout, "_");
2487
0
          line_pos++;
2488
0
        }
2489
0
      } else if (!gen_doc) {
2490
0
        gf_sys_set_console_code(helpout, console_code);
2491
0
      }
2492
0
    }
2493
2494
0
    if (att_len) {
2495
0
      while (att_len < LINE_OFFSET_DESCR) {
2496
0
        fprintf(helpout, " ");
2497
0
        att_len++;
2498
0
        line_pos++;
2499
0
      }
2500
0
    }
2501
2502
2503
0
    if (header_string) {
2504
0
      fprintf(helpout, "%s", header_string);
2505
0
      line_pos += (u32) strlen(header_string);
2506
0
    }
2507
2508
0
    while (line) {
2509
0
      char *skip_url = NULL;
2510
0
      char *link_start = NULL;
2511
0
      u32 tid=0, i;
2512
0
      char *next_token = NULL;
2513
0
      for (i=0; i<nb_tokens; i++) {
2514
0
        char *tok = strstr(line, Tokens[i].tok);
2515
0
        if (!tok) continue;
2516
0
        if (next_token && ((next_token-line) < (tok-line)) ) continue;
2517
        //check we have an end of token, otherwise consider this regular text
2518
0
        if ((i == TOK_LINKSTART) || (i == TOK_OPTLINK)) {
2519
0
          char *end_tok = strstr(tok, "](");
2520
0
          if (!end_tok) continue;
2521
0
        }
2522
2523
0
        if (i == TOK_LINKSTART) {
2524
0
          if (tid == TOK_OPTLINK) continue;
2525
0
          if (gen_doc!=1) {
2526
0
            char *link_end;
2527
0
            skip_url = strstr(tok, "](");
2528
0
            link_end = skip_url;
2529
0
            if (skip_url) skip_url = strstr(skip_url, ")");
2530
0
            if (skip_url) skip_url ++;
2531
2532
0
            if (!skip_url) continue;
2533
0
            link_start = tok+1;
2534
0
            link_end[0] = 0;
2535
0
          } else {
2536
0
            continue;
2537
0
          }
2538
0
        }
2539
0
        next_token=tok;
2540
0
        tid=i;
2541
0
      }
2542
0
      if (next_token) {
2543
0
        next_token[0]=0;
2544
0
      }
2545
0
      if ((gen_doc==1) && has_token) {
2546
0
        if (tid==TOK_CODE) {
2547
0
          fprintf(helpout, "`%s`", line);
2548
0
          line_pos+=2;
2549
0
        } else if (tid==TOK_ITALIC) {
2550
0
          fprintf(helpout, "_%s_", line);
2551
0
          line_pos+=2;
2552
0
        } else if (tid==TOK_BOLD) {
2553
0
          fprintf(helpout, "__%s__", line);
2554
0
          line_pos+=4;
2555
0
        } else {
2556
0
          fprintf(helpout, "%s", line);
2557
0
        }
2558
0
      } else if (escape_xml) {
2559
0
        char *xml_line = line;
2560
0
        while (xml_line) {
2561
0
          char *xml_start = strchr(xml_line, '<');
2562
0
          char *xml_end = strchr(xml_line, '>');
2563
2564
0
          if (xml_end && (xml_start > xml_end)) xml_start = xml_end;
2565
0
          else if (!xml_start && xml_end) xml_start = xml_end;
2566
0
          else if (xml_start && xml_end) xml_end = NULL;
2567
2568
0
          if (xml_start) {
2569
0
            u8 c = xml_start[0];
2570
0
            xml_start[0] = 0;
2571
0
            fprintf(helpout, "%s", xml_line);
2572
0
            fprintf(helpout, xml_end ? "&gt;" : "&lt;");
2573
0
            xml_start[0] = c;
2574
0
            xml_line = xml_start+1;
2575
0
          } else {
2576
0
            fprintf(helpout, "%s", xml_line);
2577
0
            break;
2578
0
          }
2579
0
        }
2580
0
      } else if (escape_pipe) {
2581
0
        char *src_line = line;
2582
0
        while (src_line) {
2583
0
          char *pipe_start = strchr(src_line, '|');
2584
0
          if (pipe_start && (pipe_start[1]==' '))
2585
0
            pipe_start = NULL;
2586
2587
0
          if (pipe_start) {
2588
0
            pipe_start[0] = 0;
2589
0
            fprintf(helpout, "%s ", src_line);
2590
0
            pipe_start[0] = '|';
2591
0
            src_line = pipe_start+1;
2592
0
          } else {
2593
0
            fprintf(helpout, "%s", src_line);
2594
0
            break;
2595
0
          }
2596
0
        }
2597
0
      } else {
2598
0
        fprintf(helpout, "%s", line);
2599
0
      }
2600
0
      line_pos+=(u32) strlen(line);
2601
2602
0
      if (!next_token) break;
2603
0
      has_token = !has_token;
2604
2605
0
      if (!gen_doc) {
2606
0
        if (has_token) {
2607
0
          u32 cmd;
2608
0
          if (Tokens[tid].cmd_type & 0xFFFF) {
2609
0
            cmd = Tokens[tid].cmd_type;
2610
0
          } else {
2611
0
            cmd = Tokens[tid].cmd_type | console_code;
2612
0
          }
2613
2614
0
          if (console_code&GF_CONSOLE_ITALIC) {
2615
0
            if (Tokens[tid].cmd_type & GF_CONSOLE_ITALIC) {
2616
0
              cmd &= ~GF_CONSOLE_ITALIC;
2617
0
              cmd |= GF_CONSOLE_BOLD;
2618
0
            }
2619
0
          }
2620
0
          else if (console_code&GF_CONSOLE_BOLD) {
2621
0
            if (Tokens[tid].cmd_type & GF_CONSOLE_BOLD) {
2622
0
              cmd &= ~GF_CONSOLE_BOLD;
2623
0
              cmd |= GF_CONSOLE_ITALIC;
2624
0
            }
2625
0
          }
2626
0
          gf_sys_set_console_code(helpout, cmd);
2627
0
        } else {
2628
0
          gf_sys_set_console_code(helpout, console_code);
2629
0
        }
2630
0
      }
2631
0
      line = next_token + (u32) strlen(Tokens[tid].tok);
2632
2633
0
      if (skip_url) {
2634
0
        if (link_start)
2635
0
          fprintf(helpout, "%s", link_start);
2636
0
        if (!gen_doc)
2637
0
          gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2638
0
        has_token = GF_FALSE;
2639
0
        line = skip_url;
2640
2641
0
      }
2642
2643
0
      if (has_token && tid==TOK_OPTLINK) {
2644
0
        char *link = strchr(line, '(');
2645
0
        gf_assert(link);
2646
0
        link++;
2647
0
        char *end_link = strchr(line, ')');
2648
0
        if (end_link) end_link[0] = 0;
2649
0
        char *end_tok = strchr(line, ']');
2650
0
        if (end_tok) end_tok[0] = 0;
2651
2652
0
        if (gen_doc==1) {
2653
0
          if (!strncmp(link, "GPAC", 4)) {
2654
0
            fprintf(helpout, "[-%s](gpac_general/#%s)", line, line);
2655
0
            line_pos+=7 + 2*(u32)strlen(line) + (u32)strlen("gpac_general");
2656
0
          } else if (!strncmp(link, "LOG", 3)) {
2657
0
            fprintf(helpout, "[-%s](core_logs/#%s)", line, line);
2658
0
            line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen("core_logs");
2659
0
          } else if (!strncmp(link, "CORE", 4)) {
2660
0
            fprintf(helpout, "[-%s](core_options/#%s)", line, line);
2661
0
            line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen("core_options");
2662
0
          } else if (!strncmp(link, "CFG", 3)) {
2663
0
            fprintf(helpout, "[-%s](core_config/#%s)", line, line);
2664
0
            line_pos+=7 + 2*(u32)strlen(line) + (u32)strlen("core_config");
2665
0
          } else if (!strncmp(link, "MP4B_GEN", 8)) {
2666
0
            fprintf(helpout, "[-%s](mp4box-gen-opts/#%s)", line, line);
2667
0
            line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen("mp4box-gen-opts");
2668
0
          } else if (!strncmp(link, "MP4B_IMP", 8)) {
2669
0
            fprintf(helpout, "[-%s](mp4box-import-opts/#%s)", line, line);
2670
0
            line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen("mp4box-import-opts");
2671
0
          } else if (strlen(link)) {
2672
0
            fprintf(helpout, "[-%s](%s/#%s)", line, link, line);
2673
0
            line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen(link);
2674
0
          } else if (is_app_opts || !strcmp(line, "i") || !strcmp(line, "o") || !strcmp(line, "h")) {
2675
0
            fprintf(helpout, "[-%s](#%s)", line, line);
2676
0
            line_pos+=6 + 2* (u32)strlen(line);
2677
0
          } else {
2678
            //this is a filter opt, don't print '-'
2679
0
            fprintf(helpout, "[%s](#%s)", line, line);
2680
0
            line_pos+=5 + 2* (u32)strlen(line);
2681
0
          }
2682
0
        } else {
2683
0
          if (gen_doc==2)
2684
0
            fprintf(helpout, ".I ");
2685
2686
0
          if (!strncmp(link, "GPAC", 4)
2687
0
            || !strncmp(link, "LOG", 3)
2688
0
            || !strncmp(link, "CORE", 4)
2689
0
            || strlen(link)
2690
0
            || !strcmp(line, "i") || !strcmp(line, "o") || !strcmp(line, "h")
2691
0
          ) {
2692
0
            fprintf(helpout, "-%s", line);
2693
0
            line_pos+=1+ (u32)strlen(line);
2694
0
          } else {
2695
0
            fprintf(helpout, "%s", line);
2696
0
            line_pos+= (u32)strlen(line);
2697
0
          }
2698
0
          if (!gen_doc)
2699
0
            gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2700
0
        }
2701
0
        if (!end_link) break;
2702
0
        line = end_link+1;
2703
0
        has_token = GF_FALSE;
2704
0
      }
2705
0
    }
2706
2707
0
    if (has_token && !gen_doc)
2708
0
      gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2709
2710
0
    if (footer_string) {
2711
0
      fprintf(helpout, "%s", footer_string);
2712
0
      line_pos += (u32) strlen(footer_string);
2713
0
    }
2714
0
    if (console_code != GF_CONSOLE_RESET) {
2715
0
      if (gen_doc==1) {
2716
0
        if (console_code & GF_CONSOLE_BOLD) {
2717
0
          fprintf(helpout, "__");
2718
0
          line_pos+=2;
2719
0
        } else if (console_code & GF_CONSOLE_ITALIC) {
2720
0
          fprintf(helpout, "_");
2721
0
          line_pos++;
2722
0
        }
2723
0
      } else if (!gen_doc) {
2724
0
        gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2725
0
      }
2726
0
    }
2727
2728
0
    if (line_after) {
2729
0
      if (gen_doc==1) fprintf(helpout, "  ");
2730
0
      fprintf(helpout, (flags & GF_PRINTARG_NL_TO_BR) ? "<br/>" : "\n");
2731
0
      line_pos=0;
2732
0
      prev_has_line_after = GF_TRUE;
2733
0
    }
2734
2735
0
    if (!next_line) break;
2736
0
    prev_has_colon = GF_FALSE;
2737
0
    if (line[0] && line[strlen(line)-1]==':')
2738
0
      prev_has_colon = GF_TRUE;
2739
2740
0
    next_line[0]=0;
2741
0
    if (gen_doc==1) fprintf(helpout, "  ");
2742
0
    line = next_line+1;
2743
0
    if (gen_doc==2) {
2744
0
      if (line[0] != '.')
2745
0
        fprintf(helpout, "\n.br\n");
2746
0
      else
2747
0
        fprintf(helpout, "\n");
2748
0
    } else
2749
0
      fprintf(helpout, (line[0] && (flags & GF_PRINTARG_NL_TO_BR)) ? "<br/>" : "\n");
2750
0
    line_pos=0;
2751
0
  }
2752
0
}
2753
2754
GF_EXPORT
2755
Bool gf_strnistr(const char *text, const char *subtext, u32 subtext_len)
2756
0
{
2757
0
  if (!*text || !subtext || !subtext_len)
2758
0
    return GF_FALSE;
2759
2760
0
  while (*text) {
2761
0
    if (tolower(*text) == *subtext) {
2762
0
      if (!strnicmp(text, subtext, subtext_len))
2763
0
        return GF_TRUE;
2764
2765
0
    }
2766
0
    text++;
2767
0
  }
2768
0
  return GF_FALSE;
2769
0
}
2770
2771
GF_EXPORT
2772
Bool gf_sys_word_match(const char *orig, const char *dst)
2773
0
{
2774
0
  s32 dist = 0;
2775
0
  u32 match = 0;
2776
0
  u32 i;
2777
0
  u32 olen, dlen;
2778
0
  u32 *run;
2779
2780
0
  if (orig == NULL && dst == NULL) {
2781
0
    GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, (" gf_sys_word_match: NULL arguments \n"));
2782
0
    return GF_TRUE;
2783
0
  }
2784
0
  if (orig == NULL || dst == NULL) {
2785
0
    GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, (" gf_sys_word_match: NULL argument \n"));
2786
0
    return GF_FALSE;
2787
0
  }
2788
2789
0
  olen = (u32) strlen(orig);
2790
0
  dlen = (u32) strlen(dst);
2791
2792
0
  if ((olen>=3) && (olen<dlen) && !strncmp(orig, dst, olen))
2793
0
    return GF_TRUE;
2794
0
  if ((dlen>=3) && (dlen<olen) && !strncmp(orig, dst, dlen))
2795
0
    return GF_TRUE;
2796
2797
0
  if (olen*2 < dlen) {
2798
0
    char *s1 = strchr(orig, ':');
2799
0
    char *s2 = strchr(dst, ':');
2800
0
    if (s1 && !s2) return GF_FALSE;
2801
0
    if (!s1 && s2) return GF_FALSE;
2802
2803
0
    if (gf_strnistr(dst, orig, MIN(olen, dlen)))
2804
0
      return GF_TRUE;
2805
0
    return GF_FALSE;
2806
0
  }
2807
2808
0
  if ((dlen>=3) && gf_strnistr(orig, dst, dlen))
2809
0
    return GF_TRUE;
2810
0
  if ((olen>=3) && gf_strnistr(dst, orig, olen))
2811
0
    return GF_TRUE;
2812
2813
0
  run = gf_malloc(sizeof(u32) * olen);
2814
0
  memset(run, 0, sizeof(u32) * olen);
2815
2816
0
  for (i=0; i<dlen; i++) {
2817
0
    u32 dist_char;
2818
0
    u32 offset=0;
2819
0
    char *pos;
2820
2821
0
retry_char:
2822
0
    pos = strchr(orig+offset, dst[i]);
2823
0
    if (!pos) continue;
2824
0
    dist_char = (u32) (pos - orig);
2825
0
    if (!run[dist_char]) {
2826
0
      run[dist_char] = i+1;
2827
0
      match++;
2828
0
    } else if (run[dist_char] > i) {
2829
0
      run[dist_char] = i+1;
2830
0
      match++;
2831
0
    } else {
2832
      //this can be a repeated character
2833
0
      offset++;
2834
0
      goto retry_char;
2835
2836
0
    }
2837
0
  }
2838
0
  if (match*2<olen) {
2839
0
    gf_free(run);
2840
0
    return GF_FALSE;
2841
0
  }
2842
/*
2843
  //if 4/5 of characters are matched, suggest it
2844
  if (match * 5 >= 4 * dlen ) {
2845
    gf_free(run);
2846
    return GF_TRUE;
2847
  }
2848
  if ((olen<=4) && (match>=3) && (dlen*2<olen*3) ) {
2849
    gf_free(run);
2850
    return GF_TRUE;
2851
  }
2852
*/
2853
0
  for (i=0; i<olen; i++) {
2854
0
    if (!i) {
2855
0
      if (run[0]==1)
2856
0
        dist++;
2857
0
    } else if (run[i-1] + 1 == run[i]) {
2858
0
      dist++;
2859
0
    }
2860
0
  }
2861
0
  gf_free(run);
2862
  //if half the characters are in order, consider a match
2863
  //if arg is small only check dst
2864
0
  if ((olen<=4) && (dist >= 2))
2865
0
    return GF_TRUE;
2866
0
  if ((dist*2 >= (s32) olen) && (dist*2 >= (s32) dlen))
2867
0
    return GF_TRUE;
2868
0
  return GF_FALSE;
2869
0
}
2870
2871
2872
// sanitizer suppression list to avoid false-positives on gl calls
2873
#ifdef __SANITIZE_ADDRESS__
2874
#include <sanitizer/lsan_interface.h>
2875
__attribute__((visibility("default")))
2876
const char *__lsan_default_suppressions(void) {
2877
    return "leak:libgallium\n"
2878
           "leak:libGLX_mesa\n"
2879
           "leak:libGLX.so\n"
2880
           "leak:libGL\n"
2881
           "leak:libSDL2\n";
2882
}
2883
#endif