Coverage Report

Created: 2023-11-19 06:24

/src/gpac/src/utils/os_config_init.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2000-2023
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
0
#define CFG_FILE_NAME "GPAC.cfg"
62
63
#if defined(GPAC_CONFIG_WIN32)
64
#define TEST_MODULE   "gm_"
65
#else
66
0
#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)
84
0
{
85
0
  char szPath[GF_MAX_PATH];
86
0
  FILE *f;
87
0
  int concatres;
88
89
0
  if (! gf_dir_exists(path)) return 0;
90
91
0
  if (!strcmp(name, TEST_MODULE)) {
92
0
    Bool res = GF_FALSE;
93
0
#if defined(GPAC_STATIC_MODULES) || defined(GPAC_MP4BOX_MINI)
94
0
    res = GF_TRUE;
95
#else
96
    gf_enum_directory(path, GF_FALSE, mod_enum, &res, NULL);
97
#endif
98
0
    if (!res) return GF_FALSE;
99
0
    if (outPath != path) strcpy(outPath, path);
100
0
    return 1;
101
0
  }
102
103
0
  concatres = snprintf(szPath, GF_MAX_PATH, "%s%c%s", path, GF_PATH_SEPARATOR, name);
104
0
  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
0
  f = fopen(szPath, "rb");
110
0
  if (!f) return GF_FALSE;
111
0
  fclose(f);
112
0
  if (outPath != path) strcpy(outPath, path);
113
0
  return GF_TRUE;
114
0
}
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, u32 path_type)
128
{
129
  FILE *f;
130
  char szPath[GF_MAX_PATH];
131
132
#ifdef _WIN32_WCE
133
  TCHAR w_szPath[GF_MAX_PATH];
134
  GetModuleFileName(NULL, w_szPath, GF_MAX_PATH);
135
  CE_WideToChar((u16 *) w_szPath, file_path);
136
#else
137
  wchar_t wtmp_file_path[GF_MAX_PATH];
138
  char* tmp_file_path;
139
140
  GetModuleFileNameA(NULL, file_path, GF_MAX_PATH);
141
#endif
142
143
  /*remove exe name*/
144
  if (strstr(file_path, ".exe")) {
145
    char *sep = strrchr(file_path, '\\');
146
    if (sep) sep[0] = 0;
147
  }
148
149
  strcpy(szPath, file_path);
150
  strlwr(szPath);
151
152
  /*if this is run from a browser, we do not get our app path - fortunately on Windows, we always use 'GPAC' in the
153
  installation path*/
154
  if (!strstr(file_path, "gpac") && !strstr(file_path, "GPAC") ) {
155
    HKEY hKey = NULL;
156
    DWORD dwSize = GF_MAX_PATH;
157
    file_path[0] = 0;
158
159
    /*locate the key in current user, then in local machine*/
160
#ifdef _WIN32_WCE
161
    DWORD dwType = REG_SZ;
162
    u16 w_path[1024];
163
    RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\GPAC"), 0, KEY_READ, &hKey);
164
#ifdef _DEBUG
165
    if (RegQueryValueEx(hKey, TEXT("DebugDir"), 0, &dwType, (LPBYTE) w_path, &dwSize) != ERROR_SUCCESS)
166
#endif
167
      RegQueryValueEx(hKey, TEXT("InstallDir"), 0, &dwType, (LPBYTE) w_path, &dwSize);
168
    CE_WideToChar(w_path, (char *)file_path);
169
    RegCloseKey(hKey);
170
#else
171
    if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\GPAC", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
172
      RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\GPAC", 0, KEY_READ, &hKey);
173
174
    dwSize = GF_MAX_PATH;
175
176
#ifdef _DEBUG
177
    if (RegQueryValueEx(hKey, "DebugDir", NULL, NULL,(unsigned char*) file_path, &dwSize) != ERROR_SUCCESS)
178
#endif
179
      RegQueryValueEx(hKey, "InstallDir", NULL, NULL,(unsigned char*) file_path, &dwSize);
180
181
    RegCloseKey(hKey);
182
#endif
183
  }
184
  //empty path, try DLL
185
  if (!file_path[0] && (path_type != GF_PATH_LIB)) {
186
    get_default_install_path(file_path, GF_PATH_LIB);
187
  }
188
189
  if (path_type==GF_PATH_APP) return GF_TRUE;
190
191
  if (path_type==GF_PATH_SHARE) {
192
    char *sep;
193
    strcat(file_path, "\\share");
194
    if (check_file_exists("gui\\gui.bt", file_path, file_path)) return GF_TRUE;
195
    sep = strstr(file_path, "\\bin\\");
196
    if (sep) {
197
      sep[0] = 0;
198
      strcat(file_path, "\\share");
199
      if (check_file_exists("gui\\gui.bt", file_path, file_path)) return GF_TRUE;
200
    }
201
    return GF_FALSE;
202
  }
203
  /*modules are stored in the GPAC directory (should be changed to GPAC/modules)*/
204
  if (path_type==GF_PATH_MODULES) return GF_TRUE;
205
206
  if (path_type == GF_PATH_LIB) {
207
    HMODULE hm=NULL;
208
    if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
209
      (LPCSTR)&get_default_install_path, &hm) == 0) {
210
      return 0;
211
    }
212
    if (GetModuleFileName(hm, file_path, GF_MAX_PATH) == 0) {
213
      return 0;
214
    }
215
    char *sep = strrchr(file_path, '\\');
216
    if (!sep) sep = strrchr(file_path, '/');
217
    if (sep) sep[0] = 0;
218
    return 1;
219
  }
220
221
  /*we are looking for the config file path - make sure it is writable*/
222
  assert(path_type == GF_PATH_CFG);
223
224
  strcpy(szPath, file_path);
225
  strcat(szPath, "\\gpaccfgtest.txt");
226
  //do not use gf_fopen here, we don't want to through any error if failure
227
  f = fopen(szPath, "wb");
228
  if (f != NULL) {
229
    fclose(f);
230
    gf_file_delete(szPath);
231
    return GF_TRUE;
232
  }
233
#ifdef _WIN32_WCE
234
  return 0;
235
#else
236
  /*no write access, get user home directory*/
237
  SHGetSpecialFolderPathW(NULL, wtmp_file_path, CSIDL_APPDATA, 1);
238
  tmp_file_path = gf_wcs_to_utf8(wtmp_file_path);
239
  strncpy(file_path, tmp_file_path, GF_MAX_PATH);
240
  file_path[GF_MAX_PATH-1] = 0;
241
  gf_free(tmp_file_path);
242
243
  if (file_path[strlen(file_path)-1] != '\\') strcat(file_path, "\\");
244
  strcat(file_path, "GPAC");
245
  /*create GPAC dir*/
246
  gf_mkdir(file_path);
247
  strcpy(szPath, file_path);
248
  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
    strcpy(android_app_data, app_data);
271
  }
272
  if (ext_storage && (strlen(ext_storage)<512)) {
273
    strcpy(android_external_storage, ext_storage);
274
  }
275
}
276
277
278
static Bool get_default_install_path(char *file_path, u32 path_type)
279
{
280
  if (!file_path) return 0;
281
282
  if (path_type==GF_PATH_APP) {
283
    strcpy(file_path, android_app_data[0] ? android_app_data : "/data/data/io.gpac.gpac");
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
    strcpy(file_path, res);
289
    strcat(file_path, "/GPAC");
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
    strcpy(file_path, android_app_data[0] ? android_app_data : "/data/data/io.gpac.gpac");
296
    strcat(file_path, "/GPAC");
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
    strcat(file_path, "/share");
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
    strcat(file_path, "/lib");
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, u32 path_type)
326
{
327
  if (path_type==GF_PATH_APP) strcpy(file_path, SYMBIAN_GPAC_MODULES_DIR);
328
  else if (path_type==GF_PATH_CFG) strcpy(file_path, SYMBIAN_GPAC_CFG_DIR);
329
  else if (path_type==GF_PATH_GUI) strcpy(file_path, SYMBIAN_GPAC_GUI_DIR);
330
  else if (path_type==GF_PATH_MODULES) strcpy(file_path, SYMBIAN_GPAC_MODULES_DIR);
331
  return 1;
332
}
333
334
/*Linux, OSX, iOS*/
335
#else
336
337
//dlinfo
338
#if defined(__DARWIN__) || defined(__APPLE__)
339
#include <dlfcn.h>
340
341
typedef Dl_info _Dl_info;
342
#elif defined(GPAC_CONFIG_LINUX)
343
344
345
typedef struct
346
{
347
  const char *dli_fname;
348
  void *dli_fbase;
349
  const char *dli_sname;
350
  void *dli_saddr;
351
} _Dl_info;
352
int dladdr(void *, _Dl_info *);
353
354
#endif
355
356
static Bool get_default_install_path(char *file_path, u32 path_type)
357
0
{
358
0
  char app_path[GF_MAX_PATH];
359
0
  char *sep;
360
0
#if (defined(__DARWIN__) || defined(__APPLE__) || defined(GPAC_CONFIG_LINUX))
361
0
  u32 size;
362
0
#endif
363
364
  /*on OSX, Linux & co, user home is where we store the cfg file*/
365
0
  if (path_type==GF_PATH_CFG) {
366
367
#ifdef GPAC_CONFIG_EMSCRIPTEN
368
    if (gf_dir_exists("/idbfs")) {
369
      if (!gf_dir_exists("/idbfs/.gpac")) {
370
        gf_mkdir("/idbfs/.gpac");
371
      }
372
      strcpy(file_path, "/idbfs/.gpac");
373
      return 1;
374
    }
375
#endif
376
377
0
    char *user_home = getenv("HOME");
378
#ifdef GPAC_CONFIG_IOS
379
    char buf[PATH_MAX];
380
    char *res;
381
#endif
382
383
0
    if (!user_home) {
384
0
      GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("Couldn't find HOME directory\n"));
385
0
      return 0;
386
0
    }
387
#ifdef GPAC_CONFIG_IOS
388
    res = realpath(user_home, buf);
389
    if (res) {
390
      strcpy(file_path, buf);
391
      strcat(file_path, "/Documents");
392
    } else
393
#endif
394
0
      strcpy(file_path, user_home);
395
396
0
    if (file_path[strlen(file_path)-1] == '/') file_path[strlen(file_path)-1] = 0;
397
398
    //cleanup of old install in .gpacrc
399
0
    if (check_file_exists(".gpacrc", file_path, file_path)) {
400
0
      strcpy(app_path, file_path);
401
0
      strcat(app_path, "/.gpacrc");
402
0
      gf_file_delete(app_path);
403
0
    }
404
405
0
    strcat(file_path, "/.gpac");
406
0
    if (!gf_dir_exists(file_path)) {
407
0
      gf_mkdir(file_path);
408
0
    }
409
0
    return 1;
410
0
  }
411
412
0
  if (path_type==GF_PATH_APP) {
413
#if (defined(__DARWIN__) || defined(__APPLE__) )
414
    size = GF_MAX_PATH-1;
415
    if (_NSGetExecutablePath(app_path, &size) ==0) {
416
      realpath(app_path, file_path);
417
      sep = strrchr(file_path, '/');
418
      if (sep) sep[0] = 0;
419
      return 1;
420
    }
421
422
#elif defined(GPAC_CONFIG_LINUX)
423
0
    size = readlink("/proc/self/exe", file_path, GF_MAX_PATH-1);
424
0
    if (size>0) {
425
0
      file_path[size] = 0;
426
0
      sep = strrchr(file_path, '/');
427
0
      if (sep) sep[0] = 0;
428
0
      return 1;
429
0
    }
430
431
#elif defined(GPAC_CONFIG_WIN32)
432
    GetModuleFileNameA(NULL, file_path, GF_MAX_PATH);
433
    if (strstr(file_path, ".exe")) {
434
      sep = strrchr(file_path, '\\');
435
      if (sep) sep[0] = 0;
436
      if ((file_path[1]==':') && (file_path[2]=='\\')) {
437
        strcpy(file_path, &file_path[2]);
438
      }
439
      sep = file_path;
440
      while ( sep[0] ) {
441
        if (sep[0]=='\\') sep[0]='/';
442
        sep++;
443
      }
444
      //get rid of /mingw32 or /mingw64
445
      sep = strstr(file_path, "/usr/");
446
      if (sep) {
447
        strcpy(file_path, sep);
448
      }
449
      return 1;
450
    }
451
#elif defined(GPAC_CONFIG_EMSCRIPTEN)
452
    return 0;
453
#endif
454
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Unknown arch, cannot find executable path\n"));
455
0
    return 0;
456
0
  }
457
458
0
  if (path_type==GF_PATH_LIB) {
459
0
#if defined(__DARWIN__) || defined(__APPLE__) || defined(GPAC_CONFIG_LINUX)
460
0
    _Dl_info dl_info;
461
0
    dl_info.dli_fname = NULL;
462
0
    if (dladdr((void *)get_default_install_path, &dl_info)
463
0
      && dl_info.dli_fname
464
0
    ) {
465
0
      strcpy(file_path, dl_info.dli_fname);
466
0
      sep = strrchr(file_path, '/');
467
0
      if (sep) sep[0] = 0;
468
0
      return 1;
469
0
    }
470
0
    return 0;
471
0
#endif
472
473
    //for emscripten we use a static load for now
474
0
#if !defined(GPAC_CONFIG_EMSCRIPTEN)
475
0
    GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("Unknown arch, cannot find library path\n"));
476
0
#endif
477
0
    return 0;
478
0
  }
479
480
#if defined(GPAC_CONFIG_EMSCRIPTEN)
481
  strcpy(app_path, "/usr/");
482
#else
483
  /*locate the app*/
484
0
  if (!get_default_install_path(app_path, GF_PATH_APP)) {
485
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Couldn't find GPAC binaries install directory\n"));
486
0
    return 0;
487
0
  }
488
0
#endif
489
490
  /*installed or symlink on system, user user home directory*/
491
0
  if (!strnicmp(app_path, "/usr/", 5) || !strnicmp(app_path, "/opt/", 5)) {
492
0
    if (path_type==GF_PATH_SHARE) {
493
      /*look in possible install dirs ...*/
494
0
      if (check_file_exists("gui/gui.bt", "/usr/share/gpac", file_path)) return 1;
495
0
      if (check_file_exists("gui/gui.bt", "/usr/local/share/gpac", file_path)) return 1;
496
0
      if (check_file_exists("gui/gui.bt", "/opt/share/gpac", file_path)) return 1;
497
0
      if (check_file_exists("gui/gui.bt", "/opt/local/share/gpac", file_path)) return 1;
498
0
    } else if (path_type==GF_PATH_MODULES) {
499
      /*look in possible install dirs ...*/
500
0
      if (check_file_exists(TEST_MODULE, "/usr/lib64/gpac", file_path)) return 1;
501
0
      if (check_file_exists(TEST_MODULE, "/usr/lib/gpac", file_path)) return 1;
502
0
      if (check_file_exists(TEST_MODULE, "/usr/local/lib/gpac", file_path)) return 1;
503
0
      if (check_file_exists(TEST_MODULE, "/opt/lib/gpac", file_path)) return 1;
504
0
      if (check_file_exists(TEST_MODULE, "/opt/local/lib/gpac", file_path)) return 1;
505
0
      if (check_file_exists(TEST_MODULE, "/usr/lib/x86_64-linux-gnu/gpac", file_path)) return 1;
506
0
      if (check_file_exists(TEST_MODULE, "/usr/lib/i386-linux-gnu/gpac", file_path)) return 1;
507
0
    }
508
0
  }
509
510
0
  if (path_type==GF_PATH_SHARE) {
511
0
    Bool try_lib=GF_TRUE;
512
0
    if (get_default_install_path(app_path, GF_PATH_CFG)) {
513
0
      sep = strstr(app_path, ".gpac/");
514
0
      if (sep) sep[5]=0;
515
      /*GUI not found, look in ~/.gpac/share/gui/ */
516
0
      strcat(app_path, "/share");
517
0
      if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
518
0
    }
519
520
    /*GUI not found, look in gpac distribution if any */
521
0
    if (get_default_install_path(app_path, GF_PATH_APP)) {
522
0
      GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[core] trying to locate share from application dir %s\n", app_path));
523
0
      strcat(app_path, "/");
524
0
retry_lib:
525
0
      sep = strstr(app_path, "/bin/");
526
0
      if (sep) {
527
0
        sep[0] = 0;
528
0
        strcat(app_path, "/share");
529
0
        if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
530
0
        strcat(app_path, "/gpac");
531
0
        if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
532
0
      }
533
0
      sep = strstr(app_path, "/build/");
534
0
      if (sep) {
535
0
        sep[0] = 0;
536
0
        strcat(app_path, "/share");
537
0
        if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
538
0
      }
539
0
    }
540
0
    if (get_default_install_path(app_path, GF_PATH_LIB)) {
541
0
      GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[core] trying to locate share from dynamic libgpac dir %s\n", app_path));
542
0
      sep = strstr(app_path, "/lib");
543
0
      if (sep) {
544
0
        sep[0] = 0;
545
0
        strcat(app_path, "/share");
546
0
        if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
547
0
      }
548
0
      if (try_lib) {
549
0
        try_lib = GF_FALSE;
550
0
        goto retry_lib;
551
0
      }
552
0
    }
553
    /*GUI not found, look in .app for OSX case*/
554
0
  }
555
556
0
  if (path_type==GF_PATH_MODULES) {
557
    /*look in gpac compilation tree (modules are output in the same folder as apps) and in distrib tree */
558
0
    if (get_default_install_path(app_path, GF_PATH_APP)) {
559
0
      if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
560
561
      /*on OSX check modules subdirectory */
562
0
      strcat(app_path, "/modules");
563
0
      if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
564
565
0
      get_default_install_path(app_path, GF_PATH_APP);
566
0
      strcat(app_path, "/");
567
0
      sep = strstr(app_path, "/bin/");
568
0
      if (sep) {
569
0
        sep[0] = 0;
570
0
        strcat(app_path, "/lib/gpac");
571
0
        if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
572
0
      }
573
574
      /*modules not found*/
575
0
      GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("Couldn't find any modules in standard path (app path %s)\n", app_path));
576
0
    }
577
578
    /*look in lib install */
579
0
    if (get_default_install_path(app_path, GF_PATH_LIB)) {
580
0
      if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
581
0
      strcat(app_path, "/gpac");
582
0
      if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
583
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Couldn't find any modules in lib path %s\n", app_path));
584
0
    }
585
586
587
    /*modules not found, look in ~/.gpac/modules/ */
588
0
    if (get_default_install_path(app_path, GF_PATH_CFG)) {
589
0
      strcat(app_path, "/modules");
590
0
      if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
591
0
    }
592
    /*modules not found, failure*/
593
#ifndef GPAC_STATIC_MODULES
594
    GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("Couldn't find any modules in HOME path (app path %s)\n", app_path));
595
#endif
596
0
    return 0;
597
0
  }
598
599
  /*OSX way vs iPhone*/
600
0
  sep = strstr(app_path, ".app/");
601
0
  if (sep) sep[4] = 0;
602
603
  /*we are looking for .app install path, or GUI */
604
0
  if (path_type==GF_PATH_SHARE) {
605
0
#ifndef GPAC_CONFIG_IOS
606
0
    strcat(app_path, "/Contents/MacOS/share");
607
0
    if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
608
#else /*iOS: for now, everything is set flat within the package*/
609
    /*iOS app is distributed with embedded GUI*/
610
    get_default_install_path(app_path, GF_PATH_APP);
611
    if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
612
    strcat(app_path, "/share");
613
    if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
614
#endif
615
0
  }
616
0
  else { // (path_type==GF_PATH_MODULES)
617
0
    strcat(app_path, "/Contents/MacOS/modules");
618
0
    if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
619
0
  }
620
  /*not found ...*/
621
0
  return 0;
622
0
}
623
624
#endif
625
626
//get real path where the .gpac dir has been created, and use this as the default path
627
//for cache (tmp/ dir of ios app) and last working fir
628
#ifdef GPAC_CONFIG_IOS
629
static void gf_ios_refresh_cache_directory( GF_Config *cfg, const char *file_path)
630
{
631
  char *cache_dir, *old_cache_dir;
632
  char buf[GF_MAX_PATH], *res, *sep;
633
  res = realpath(file_path, buf);
634
  if (!res) return;
635
636
  sep = strstr(res, ".gpac");
637
  assert(sep);
638
  sep[0] = 0;
639
  gf_cfg_set_key(cfg, "core", "docs-dir", res);
640
  if (!gf_cfg_get_key(cfg, "core", "last-dir"))
641
    gf_cfg_set_key(cfg, "core", "last-dir", res);
642
643
  strcat(res, "cache/");
644
  cache_dir = res;
645
  old_cache_dir = (char*) gf_opts_get_key("core", "cache");
646
647
  if (!gf_dir_exists(cache_dir)) {
648
    if (old_cache_dir && strcmp(old_cache_dir, cache_dir)) {
649
      GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("Cache dir changed: old %d -> new %s\n\n", old_cache_dir, cache_dir ));
650
    }
651
    gf_mkdir(cache_dir);
652
  }
653
  gf_cfg_set_key(cfg, "core", "cache", cache_dir);
654
}
655
656
#endif
657
658
const char * gf_get_default_cache_directory_ex(Bool do_create);
659
660
GF_EXPORT
661
void gf_get_default_font_dir(char szPath[GF_MAX_PATH])
662
0
{
663
#if defined(_WIN32_WCE)
664
  strcpy(szPath, "\\Windows");
665
666
#elif defined(WIN32)
667
  GetWindowsDirectory((char*)szPath, MAX_PATH);
668
  if (szPath[strlen((char*)szPath)-1] != '\\') strcat((char*)szPath, "\\");
669
  strcat((char *)szPath, "Fonts");
670
671
#elif defined(__APPLE__) && defined(GPAC_CONFIG_IOS)
672
  strcpy(szPath, "/System/Library/Fonts/Cache,/System/Library/Fonts/AppFonts,/System/Library/Fonts/Core,/System/Library/Fonts/Extra");
673
#elif defined(__APPLE__)
674
  strcpy(szPath, "/System/Library/Fonts,/Library/Fonts");
675
676
#elif defined(GPAC_CONFIG_ANDROID)
677
  strcpy(szPath, "/system/fonts/");
678
#else
679
  //scan all /usr/share/fonts, not just /usr/share/fonts/truetype/ which does not exist in some distrros
680
0
  strcpy(szPath, "/usr/share/fonts/");
681
0
#endif
682
0
}
683
684
685
static GF_Config *create_default_config(char *file_path, const char *profile)
686
0
{
687
0
  Bool moddir_found;
688
0
  GF_Config *cfg;
689
0
  char szPath[GF_MAX_PATH];
690
691
0
  if (file_path && ! get_default_install_path(file_path, GF_PATH_CFG)) {
692
0
    profile = "0";
693
0
  }
694
  /*Create temp config file*/
695
0
  if (profile && (!strcmp(profile, "0") || !stricmp(profile, "n"))) {
696
0
    cfg = gf_cfg_new(NULL, NULL);
697
0
    if (!stricmp(profile, "n")) {
698
0
      gf_cfg_discard_changes(cfg);
699
0
      return cfg;
700
0
    }
701
0
  } else {
702
0
    FILE *f;
703
704
    /*create config file from disk*/
705
0
    if (profile) {
706
0
      sprintf(szPath, "%s%cprofiles%c%s%c%s", file_path, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, profile, GF_PATH_SEPARATOR, CFG_FILE_NAME);
707
0
    } else {
708
0
      sprintf(szPath, "%s%c%s", file_path, GF_PATH_SEPARATOR, CFG_FILE_NAME);
709
0
    }
710
0
    GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("Trying to create config file: %s\n", szPath ));
711
712
0
    f = gf_fopen(szPath, "wt");
713
0
    if (!f) return NULL;
714
0
    gf_fclose(f);
715
716
0
    cfg = gf_cfg_new(NULL, szPath);
717
0
  }
718
719
0
  if (!cfg) return NULL;
720
721
722
0
#ifndef GPAC_CONFIG_IOS
723
0
  moddir_found = get_default_install_path(szPath, GF_PATH_MODULES);
724
#else
725
  moddir_found = get_default_install_path(szPath, GF_PATH_APP);
726
#endif
727
728
#if defined(GPAC_CONFIG_IOS)
729
  gf_cfg_set_key(cfg, "core", "devclass", "ios");
730
#elif defined(GPAC_CONFIG_ANDROID)
731
  gf_cfg_set_key(cfg, "core", "devclass", "android");
732
#elif defined(GPAC_CONFIG_EMSCRIPTEN)
733
  gf_cfg_set_key(cfg, "core", "devclass", "wasm");
734
#else
735
0
  gf_cfg_set_key(cfg, "core", "devclass", "desktop");
736
0
#endif
737
738
739
0
  if (!moddir_found) {
740
0
#if !defined(GPAC_CONFIG_EMSCRIPTEN)
741
0
    GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[Core] default modules directory not found\n"));
742
0
#endif
743
0
  } else {
744
0
    gf_cfg_set_key(cfg, "core", "module-dir", szPath);
745
0
  }
746
747
748
#if defined(GPAC_CONFIG_IOS)
749
  gf_ios_refresh_cache_directory(cfg, file_path);
750
#elif defined(GPAC_CONFIG_ANDROID)
751
  if (get_default_install_path(szPath, GF_PATH_APP)) {
752
    strcat(szPath, "/cache");
753
    gf_cfg_set_key(cfg, "core", "cache", szPath);
754
    strcat(szPath, "/.nomedia");
755
    //create .nomedia in cache and in app dir
756
    if (!gf_file_exists(szPath)) {
757
      FILE *f = gf_fopen(szPath, "w");
758
      if (f) gf_fclose(f);
759
    }
760
    get_default_install_path(szPath, GF_PATH_APP);
761
    strcat(szPath, "/.nomedia");
762
    if (!gf_file_exists(szPath)) {
763
      FILE *f = gf_fopen(szPath, "w");
764
      if (f) gf_fclose(f);
765
    }
766
    //add a tmp as well, tmpfile() does not work on android
767
    get_default_install_path(szPath, GF_PATH_APP);
768
    strcat(szPath, "/gpac_tmp");
769
    gf_cfg_set_key(cfg, "core", "tmp", szPath);
770
    strcat(szPath, "/.nomedia");
771
    if (!gf_file_exists(szPath)) {
772
      FILE *f = gf_fopen(szPath, "w");
773
      if (f) gf_fclose(f);
774
    }
775
  }
776
#else
777
  /*get default temporary directoy */
778
0
  gf_cfg_set_key(cfg, "core", "cache", gf_get_default_cache_directory_ex(GF_FALSE));
779
0
#endif
780
781
  /*Setup font engine to FreeType by default, and locate TrueType font directory on the system*/
782
0
  gf_cfg_set_key(cfg, "core", "font-reader", "freetype");
783
0
  gf_cfg_set_key(cfg, "core", "rescan-fonts", "yes");
784
785
786
0
  gf_get_default_font_dir(szPath);
787
0
  gf_cfg_set_key(cfg, "core", "font-dirs", szPath);
788
789
0
  gf_cfg_set_key(cfg, "core", "cache-size", "100M");
790
791
#if defined(_WIN32_WCE)
792
  gf_cfg_set_key(cfg, "core", "video-output", "gapi");
793
#elif defined(WIN32)
794
  gf_cfg_set_key(cfg, "core", "video-output", "directx");
795
#elif defined(__DARWIN__) || defined(__APPLE__)
796
  gf_cfg_set_key(cfg, "core", "video-output", "sdl");
797
#elif defined(GPAC_CONFIG_ANDROID)
798
  gf_cfg_set_key(cfg, "core", "video-output", "android");
799
  gf_cfg_set_key(cfg, "core", "audio-output", "android");
800
#else
801
  //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)
802
0
  gf_cfg_set_key(cfg, "core", "video-output", "sdl");
803
0
  gf_cfg_set_key(cfg, "core", "audio-output", "sdla");
804
0
#endif
805
806
807
0
#if !defined(GPAC_CONFIG_IOS) && !defined(GPAC_CONFIG_ANDROID)
808
0
  gf_cfg_set_key(cfg, "core", "switch-vres", "no");
809
0
  gf_cfg_set_key(cfg, "core", "hwvmem", "auto");
810
0
#endif
811
812
#ifdef GPAC_CONFIG_ANDROID
813
  const char *opt = android_external_storage[0] ? android_external_storage : getenv("EXTERNAL_STORAGE");
814
  if (!opt) opt = "/sdcard";
815
  gf_cfg_set_key(cfg, "core", "docs-dir", opt);
816
  gf_cfg_set_key(cfg, "core", "last-dir", opt);
817
#endif
818
819
  /*locate GUI*/
820
0
  if ( get_default_install_path(szPath, GF_PATH_SHARE) ) {
821
0
    char gui_path[GF_MAX_PATH+100];
822
0
    sprintf(gui_path, "%s%cgui%cgui.bt", szPath, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
823
0
    if (gf_file_exists(gui_path)) {
824
0
      gf_cfg_set_key(cfg, "core", "startup-file", gui_path);
825
0
    }
826
827
    /*shaders are at the same location*/
828
0
    sprintf(gui_path, "%s%cshaders%cvertex.glsl", szPath, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
829
0
    gf_cfg_set_key(cfg, "filter@compositor", "vertshader", gui_path);
830
0
    sprintf(gui_path, "%s%cshaders%cfragment.glsl", szPath, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
831
0
    gf_cfg_set_key(cfg, "filter@compositor", "fragshader", gui_path);
832
833
    //aliases and other defaults
834
0
    sprintf(gui_path, "%s%cdefault.cfg", szPath, GF_PATH_SEPARATOR);
835
0
    if (gf_file_exists(gui_path)) {
836
0
      GF_Config *aliases = gf_cfg_new(NULL, gui_path);
837
0
      if (aliases) {
838
0
        u32 i, count = gf_cfg_get_section_count(aliases);
839
0
        for (i=0; i<count; i++) {
840
0
          u32 j, count2;
841
0
          const char *sec_name = gf_cfg_get_section_name(aliases, i);
842
0
          if (!sec_name) continue;
843
0
          count2 = gf_cfg_get_key_count(aliases, sec_name);
844
0
          for (j=0; j<count2; j++) {
845
0
            const char *name = gf_cfg_get_key_name(aliases, sec_name, j);
846
0
            const char *val = gf_cfg_get_key(aliases, sec_name, name);
847
0
            gf_cfg_set_key(cfg, sec_name, name, val);
848
0
          }
849
0
        }
850
0
      }
851
0
      gf_cfg_del(aliases);
852
0
    }
853
0
  }
854
855
0
  if (profile && !strcmp(profile, "0")) {
856
0
    GF_Err gf_cfg_set_filename(GF_Config *iniFile, const char * fileName);
857
//    sprintf(szPath, "%s%c%s", gf_get_default_cache_directory(), GF_PATH_SEPARATOR, CFG_FILE_NAME);
858
0
    gf_cfg_set_filename(cfg, CFG_FILE_NAME);
859
0
    gf_cfg_discard_changes(cfg);
860
0
    return cfg;
861
0
  }
862
  /*store and reload*/
863
0
  strcpy(szPath, gf_cfg_get_filename(cfg));
864
0
  gf_cfg_del(cfg);
865
0
  return gf_cfg_new(NULL, szPath);
866
0
}
867
868
/*check if modules directory has changed in the config file
869
*/
870
static void check_modules_dir(GF_Config *cfg)
871
0
{
872
0
  char path[GF_MAX_PATH];
873
874
#ifdef GPAC_CONFIG_IOS
875
  const char *cfg_path;
876
  if ( get_default_install_path(path, GF_PATH_SHARE) ) {
877
    char *sep;
878
    char shader_path[GF_MAX_PATH];
879
    strcat(path, "/gui/gui.bt");
880
    gf_cfg_set_key(cfg, "core", "startup-file", path);
881
    //get rid of "/gui/gui.bt"
882
    sep = strrchr(path, '/');
883
    sep[0] = 0;
884
    sep = strrchr(path, '/');
885
    sep[0] = 0;
886
887
    sprintf(shader_path, "%s%cshaders%cvertex.glsl", path, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
888
    gf_cfg_set_key(cfg, "filter@compositor", "vertshader", shader_path);
889
    sprintf(shader_path, "%s%cshaders%cfragment.glsl", path, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
890
    gf_cfg_set_key(cfg, "filter@compositor", "fragshader", shader_path);
891
  }
892
  cfg_path = gf_cfg_get_filename(cfg);
893
  gf_ios_refresh_cache_directory(cfg, cfg_path);
894
895
#else
896
0
  const char *opt;
897
898
0
  if ( get_default_install_path(path, GF_PATH_MODULES) ) {
899
0
    opt = gf_cfg_get_key(cfg, "core", "module-dir");
900
    //for OSX, we can have an install in /usr/... and an install in /Applications/GPAC.app - always change
901
#if defined(__DARWIN__) || defined(__APPLE__)
902
    if (!opt || strcmp(opt, path))
903
      gf_cfg_set_key(cfg, "core", "module-dir", path);
904
#else
905
906
    //otherwise only check we didn't switch between a 64 bit version and a 32 bit version
907
0
    if (!opt) {
908
0
      gf_cfg_set_key(cfg, "core", "module-dir", path);
909
0
    } else  {
910
0
      Bool erase_modules_dir = GF_FALSE;
911
0
      const char *opt64 = gf_cfg_get_key(cfg, "core", "64bits");
912
0
      if (!opt64) {
913
        //first run or old versions, erase
914
0
        erase_modules_dir = GF_TRUE;
915
0
      } else if (!strcmp(opt64, "yes") ) {
916
#ifndef GPAC_64_BITS
917
        erase_modules_dir = GF_TRUE;
918
#endif
919
0
      } else {
920
0
#ifdef GPAC_64_BITS
921
0
        erase_modules_dir = GF_TRUE;
922
0
#endif
923
0
      }
924
925
0
#ifdef GPAC_64_BITS
926
0
      opt64 = "yes";
927
#else
928
      opt64 = "no";
929
#endif
930
0
      gf_cfg_set_key(cfg, "core", "64bits", opt64);
931
932
0
      if (erase_modules_dir) {
933
0
        gf_cfg_set_key(cfg, "core", "module-dir", path);
934
0
      }
935
0
    }
936
0
#endif
937
0
  }
938
939
  /*if startup file was disabled, do not attempt to correct it*/
940
0
  if (gf_cfg_get_key(cfg, "core", "startup-file")==NULL) return;
941
942
0
  if ( get_default_install_path(path, GF_PATH_SHARE) ) {
943
0
    opt = gf_cfg_get_key(cfg, "core", "startup-file");
944
0
    if (strstr(opt, "gui.bt") && strcmp(opt, path) && strstr(path, ".app") ) {
945
#if defined(__DARWIN__) || defined(__APPLE__)
946
      strcat(path, "/gui/gui.bt");
947
      gf_cfg_set_key(cfg, "core", "startup-file", path);
948
#endif
949
0
    }
950
0
  }
951
952
0
#endif
953
0
}
954
955
#ifdef GPAC_CONFIG_ANDROID
956
957
static Bool delete_tmp_files(void *cbck, char *item_name, char *item_path, GF_FileEnumInfo *file_info)
958
{
959
  if (gf_file_delete(item_path) != GF_OK) {
960
    GF_LOG(GF_LOG_ERROR, GF_LOG_CACHE, ("[CORE] Failed to cleanup temp file %s\n", item_path));
961
  }
962
  return GF_FALSE;
963
}
964
#endif
965
966
static void check_default_cred_file(GF_Config *cfg, char szPath[GF_MAX_PATH])
967
0
{
968
0
#ifndef GPAC_CONFIG_EMSCRIPTEN
969
0
  char key[16];
970
0
  u64 v1, v2;
971
0
  const char *opt = gf_cfg_get_key(cfg, "core", "cred");
972
0
  if (opt) return;
973
  //when running as service, the config file path may be unknown
974
0
  if (!szPath[0] || !gf_dir_exists(szPath))
975
0
    return;
976
0
  strcat(szPath, "/creds.key");
977
0
  if (gf_file_exists(szPath)) return;
978
979
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));
980
981
0
  v1 = gf_rand(); v1<<=32; v1 |= gf_rand();
982
0
  v2 = gf_rand(); v2<<=32; v2 |= gf_rand();
983
0
  v1 |= gf_sys_clock_high_res();
984
#ifndef GPAC_64_BITS
985
  v2 |= (u64) (u32) cfg;
986
  v2 ^= (u64) (u32) szPath;
987
#else
988
0
  v2 |= (u64) cfg;
989
0
  v2 ^= (u64) szPath;
990
0
#endif
991
0
  * ( (u64*) &key[0] ) = v1;
992
0
  * ( (u64*) &key[8] ) = v2;
993
0
  FILE *crd = gf_fopen(szPath, "w");
994
0
  if (!crd) {
995
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Failed to create credential key in %s, credentials will not be stored\n", szPath));
996
0
    return;
997
0
  }
998
0
  fwrite(key, 16, 1, crd);
999
0
  fclose(crd);
1000
0
  gf_cfg_set_key(cfg, "core", "cred", szPath);
1001
0
#endif //GPAC_CONFIF_EMSCRIPTEN
1002
0
}
1003
1004
/*!
1005
\brief configuration file initialization
1006
 *
1007
 * Constructs a configuration file from fileName. If fileName is NULL, the default GPAC configuration file is loaded with the
1008
 * proper module directory, font directory and other default options. If fileName is non-NULL no configuration file is found,
1009
 * a "light" default configuration file is created.
1010
\param profile name or path to existing config file
1011
\return the configuration file object, NULL if the file could not be created
1012
 */
1013
 #include <gpac/network.h>
1014
1015
static GF_Config *gf_cfg_init(const char *profile)
1016
0
{
1017
0
  GF_Config *cfg=NULL;
1018
0
  u32 prof_len=0;
1019
0
  Bool force_new_cfg=GF_FALSE;
1020
0
  Bool fast_profile=GF_FALSE;
1021
0
  char szPath[GF_MAX_PATH];
1022
0
  char *prof_opt = NULL;
1023
1024
0
  if (profile) {
1025
0
    prof_len = (u32) strlen(profile);
1026
0
    prof_opt = gf_url_colon_suffix(profile, 0);
1027
0
    if (prof_opt) {
1028
0
      prof_len -= (u32) strlen(prof_opt);
1029
0
      if (strstr(prof_opt, "reload")) force_new_cfg = GF_TRUE;
1030
0
      prof_opt[0] = 0;
1031
0
    }
1032
0
    if (!stricmp(profile, "n")) {
1033
0
      fast_profile = GF_TRUE;
1034
0
      cfg = create_default_config(NULL, "n");
1035
0
      goto skip_cfg;
1036
0
    }
1037
0
  }
1038
0
  if (profile && !prof_len)
1039
0
    profile = NULL;
1040
1041
0
  if (profile && (strchr(profile, '/') || strchr(profile, '\\')) ) {
1042
0
    if (!gf_file_exists(profile)) {
1043
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Config file %s does not exist\n", profile));
1044
0
      goto exit;
1045
0
    }
1046
0
    cfg = gf_cfg_new(NULL, profile);
1047
0
    if (!cfg) {
1048
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Failed to load existing config file %s\n", profile));
1049
0
      goto exit;
1050
0
    }
1051
0
    if (force_new_cfg) {
1052
0
      gf_cfg_del(cfg);
1053
0
      cfg = create_default_config(NULL, profile);
1054
0
    }
1055
0
    check_modules_dir(cfg);
1056
0
    goto exit;
1057
0
  }
1058
1059
0
  if (!get_default_install_path(szPath, GF_PATH_CFG)) {
1060
0
    if (!profile || strcmp(profile, "0")) {
1061
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"));
1062
0
    }
1063
0
    profile="0";
1064
0
    cfg = create_default_config(szPath, profile);
1065
0
    goto skip_cfg;
1066
0
  }
1067
1068
0
  if (profile && !strcmp(profile, "0")) {
1069
0
    cfg = create_default_config(NULL, "0");
1070
0
    goto skip_cfg;
1071
0
  }
1072
1073
0
  if (profile) {
1074
0
    strcat(szPath, "/profiles/");
1075
0
    strcat(szPath, profile);
1076
0
  }
1077
1078
0
  cfg = gf_cfg_new(szPath, CFG_FILE_NAME);
1079
  //config file not compatible with old arch, check it:
1080
0
  if (cfg) {
1081
0
    const char *key;
1082
0
    u32 nb_old_sec = gf_cfg_get_key_count(cfg, "Compositor");
1083
0
    nb_old_sec += gf_cfg_get_key_count(cfg, "MimeTypes");
1084
0
    nb_old_sec += gf_cfg_get_key_count(cfg, "Video");
1085
0
    nb_old_sec += gf_cfg_get_key_count(cfg, "Audio");
1086
0
    nb_old_sec += gf_cfg_get_key_count(cfg, "Systems");
1087
0
    nb_old_sec += gf_cfg_get_key_count(cfg, "General");
1088
0
    if (! gf_cfg_get_key_count(cfg, "core"))
1089
0
      nb_old_sec += 1;
1090
1091
    //check GUI is valid, if not recreate a config
1092
0
    key = gf_cfg_get_key(cfg, "core", "startup-file");
1093
0
    if (key && !gf_file_exists(key))
1094
0
      force_new_cfg = GF_TRUE;
1095
1096
    //check if reset flag is set in existing config
1097
0
    if (gf_cfg_get_key(cfg, "core", "reset"))
1098
0
      force_new_cfg = GF_TRUE;
1099
1100
0
    if (nb_old_sec || force_new_cfg) {
1101
0
      if (nb_old_sec && (!profile || strcmp(profile, "0"))) {
1102
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 ));
1103
0
      }
1104
0
      gf_cfg_del(cfg);
1105
0
      cfg = create_default_config(szPath, profile);
1106
0
    } else {
1107
      //check fonts are valid, if not reload fonts
1108
0
      Bool rescan_fonts = GF_FALSE;
1109
0
      key = gf_cfg_get_key_name(cfg, "FontCache", 0);
1110
0
      if (!key)
1111
0
        rescan_fonts = GF_TRUE;
1112
0
      else {
1113
0
        key = gf_cfg_get_key(cfg, "FontCache", key);
1114
0
        if (key && !gf_file_exists(key))
1115
0
          rescan_fonts = GF_TRUE;
1116
0
      }
1117
0
      if (rescan_fonts)
1118
0
        gf_opts_set_key("core", "rescan-fonts", "yes");
1119
0
    }
1120
0
  }
1121
  //no config file found
1122
0
  else {
1123
0
    if (!profile || strcmp(profile, "0")) {
1124
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 ));
1125
0
    }
1126
0
    cfg = create_default_config(szPath, profile);
1127
0
  }
1128
1129
0
skip_cfg:
1130
0
  if (!cfg) {
1131
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Cannot create config file %s in %s directory\n", CFG_FILE_NAME, szPath));
1132
0
    goto exit;
1133
0
  }
1134
1135
0
#ifndef GPAC_CONFIG_IOS
1136
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[core] Using global config file in %s directory\n", szPath));
1137
0
#endif
1138
1139
0
  if (fast_profile) goto exit;
1140
1141
0
  check_modules_dir(cfg);
1142
0
  if (!profile || strcmp(profile, "0"))
1143
0
    check_default_cred_file(cfg, szPath);
1144
1145
0
  if (!gf_cfg_get_key(cfg, "core", "store-dir")) {
1146
0
    if (profile && !strcmp(profile, "0")) {
1147
0
      strcpy(szPath, gf_get_default_cache_directory_ex(GF_FALSE) );
1148
0
      strcat(szPath, "/Storage");
1149
0
    } else {
1150
0
      char *sep;
1151
0
      strcpy(szPath, gf_cfg_get_filename(cfg));
1152
0
      sep = strrchr(szPath, '/');
1153
0
      if (!sep) sep = strrchr(szPath, '\\');
1154
0
      if (sep) sep[0] = 0;
1155
0
      strcat(szPath, "/Storage");
1156
0
      if (!gf_dir_exists(szPath)) gf_mkdir(szPath);
1157
0
    }
1158
0
    gf_cfg_set_key(cfg, "core", "store-dir", szPath);
1159
0
  }
1160
1161
0
exit:
1162
0
  if (prof_opt) prof_opt[0] = ':';
1163
1164
  //clean tmp
1165
#ifdef GPAC_CONFIG_ANDROID
1166
  if (cfg) {
1167
    const char *tmp = gf_cfg_get_key(cfg, "core", "tmp");
1168
    if (tmp && !strstr(tmp, "/gpac_tmp")) tmp=NULL;
1169
    if (tmp) {
1170
      gf_enum_directory(tmp, GF_FALSE, delete_tmp_files, (void*)cfg, NULL);
1171
    }
1172
  }
1173
#endif
1174
1175
0
  return cfg;
1176
0
}
1177
1178
1179
GF_EXPORT
1180
Bool gf_opts_default_shared_directory(char *path_buffer)
1181
0
{
1182
0
  return get_default_install_path(path_buffer, GF_PATH_SHARE);
1183
0
}
1184
1185
void gf_modules_new(GF_Config *config);
1186
void gf_modules_del();
1187
1188
GF_Config *gpac_global_config = NULL;
1189
1190
void gf_init_global_config(const char *profile)
1191
0
{
1192
0
  if (!gpac_global_config) {
1193
0
    gpac_global_config = gf_cfg_init(profile);
1194
0
    if (!gpac_global_config) {
1195
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Fatal error: failed to initialize GPAC global configuration\n"));
1196
0
      exit(1);
1197
0
    }
1198
0
    if (!profile || stricmp(profile, "n"))
1199
0
      gf_modules_new(gpac_global_config);
1200
0
  }
1201
0
}
1202
1203
void gf_uninit_global_config(Bool discard_config)
1204
0
{
1205
0
  if (gpac_global_config) {
1206
0
    if (discard_config) gf_cfg_discard_changes(gpac_global_config);
1207
0
    gf_cfg_del(gpac_global_config);
1208
0
    gpac_global_config = NULL;
1209
0
    gf_modules_del();
1210
0
  }
1211
0
}
1212
1213
GF_Err gf_cfg_set_key_internal(GF_Config *iniFile, const char *secName, const char *keyName, const char *keyValue, Bool is_restrict);
1214
1215
#ifdef GPAC_ENABLE_RESTRICT
1216
void gf_cfg_load_restrict()
1217
{
1218
  char szPath[GF_MAX_PATH];
1219
  if (get_default_install_path(szPath, GF_PATH_SHARE)) {
1220
    strcat(szPath, "/");
1221
    strcat(szPath, "restrict.cfg");
1222
    if (gf_file_exists(szPath)) {
1223
      GF_Config *rcfg = gf_cfg_new(NULL, szPath);
1224
      if (rcfg) {
1225
        u32 i, count = gf_cfg_get_section_count(rcfg);
1226
        for (i=0; i<count; i++) {
1227
          u32 j, kcount;
1228
          const char *sname = gf_cfg_get_section_name(rcfg, i);
1229
          if (!sname) break;
1230
          kcount = gf_cfg_get_key_count(rcfg, sname);
1231
          for (j=0; j<kcount; j++) {
1232
            const char *kname = gf_cfg_get_key_name(rcfg, sname, j);
1233
            const char *kval = gf_cfg_get_key(rcfg, sname, kname);
1234
            gf_cfg_set_key_internal(gpac_global_config, sname, kname, kval, GF_TRUE);
1235
          }
1236
        }
1237
        gf_cfg_del(rcfg);
1238
      }
1239
    }
1240
  }
1241
}
1242
#endif
1243
1244
GF_EXPORT
1245
const char *gf_opts_get_key(const char *secName, const char *keyName)
1246
939k
{
1247
939k
  if (!gpac_global_config) return NULL;
1248
1249
0
  if (!strcmp(secName, "core")) {
1250
0
    const char *opt = gf_cfg_get_key(gpac_global_config, "temp", keyName);
1251
0
    if (opt) return opt;
1252
0
  }
1253
0
  return gf_cfg_get_key(gpac_global_config, secName, keyName);
1254
0
}
1255
GF_EXPORT
1256
GF_Err gf_opts_set_key(const char *secName, const char *keyName, const char *keyValue)
1257
0
{
1258
0
  if (!gpac_global_config) return GF_BAD_PARAM;
1259
0
  return gf_cfg_set_key(gpac_global_config, secName, keyName, keyValue);
1260
0
}
1261
GF_EXPORT
1262
void gf_opts_del_section(const char *secName)
1263
0
{
1264
0
  if (!gpac_global_config) return;
1265
0
  gf_cfg_del_section(gpac_global_config, secName);
1266
0
}
1267
GF_EXPORT
1268
u32 gf_opts_get_section_count()
1269
0
{
1270
0
  if (!gpac_global_config) return 0;
1271
0
  return gf_cfg_get_section_count(gpac_global_config);
1272
0
}
1273
GF_EXPORT
1274
const char *gf_opts_get_section_name(u32 secIndex)
1275
0
{
1276
0
  if (!gpac_global_config) return NULL;
1277
0
  return gf_cfg_get_section_name(gpac_global_config, secIndex);
1278
0
}
1279
GF_EXPORT
1280
u32 gf_opts_get_key_count(const char *secName)
1281
0
{
1282
0
  if (!gpac_global_config) return 0;
1283
0
  return gf_cfg_get_key_count(gpac_global_config, secName);
1284
0
}
1285
GF_EXPORT
1286
const char *gf_opts_get_key_name(const char *secName, u32 keyIndex)
1287
0
{
1288
0
  if (!gpac_global_config) return NULL;
1289
0
  return gf_cfg_get_key_name(gpac_global_config, secName, keyIndex);
1290
0
}
1291
1292
GF_EXPORT
1293
const char *gf_opts_get_key_restricted(const char *secName, const char *keyName)
1294
0
{
1295
0
  const char *res = NULL;
1296
0
  const char *gf_cfg_get_key_internal(GF_Config *iniFile, const char *secName, const char *keyName, Bool restricted_only);
1297
0
  if (gpac_global_config)
1298
0
    res = gf_cfg_get_key_internal(gpac_global_config, secName, keyName, GF_TRUE);
1299
0
  return res;
1300
0
}
1301
1302
GF_EXPORT
1303
const char *gf_opts_get_filename()
1304
0
{
1305
0
  return gf_cfg_get_filename(gpac_global_config);
1306
0
}
1307
GF_EXPORT
1308
GF_Err gf_opts_discard_changes()
1309
0
{
1310
0
  return gf_cfg_discard_changes(gpac_global_config);
1311
0
}
1312
1313
GF_EXPORT
1314
GF_Err gf_opts_save()
1315
0
{
1316
0
  return gf_cfg_save(gpac_global_config);
1317
0
}
1318
1319
#include <gpac/main.h>
1320
1321
GF_GPACArg GPAC_Args[] = {
1322
 GF_DEF_ARG("tmp", NULL, "specify directory for temporary file creation instead of OS-default temporary file management", NULL, NULL, GF_ARG_STRING, 0),
1323
 GF_DEF_ARG("noprog", NULL, "disable progress messages", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_LOG),
1324
 GF_DEF_ARG("quiet", NULL, "disable all messages, including errors", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_LOG),
1325
 GF_DEF_ARG("log-file", "lf", "set output log file", NULL, NULL, GF_ARG_STRING, GF_ARG_SUBSYS_LOG),
1326
 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),
1327
 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),
1328
 GF_DEF_ARG("logs", NULL, "set log tools and levels.  \n"
1329
      "  \n"
1330
      "You can independently log different tools involved in a session.  \n"
1331
      "log_args is formatted as a colon (':') separated list of `toolX[:toolZ]@levelX`  \n"
1332
          "`levelX` can be one of:\n"
1333
          "- quiet: skip logs\n"
1334
          "- error: logs only error messages\n"
1335
          "- warning: logs error+warning messages\n"
1336
          "- info: logs error+warning+info messages\n"
1337
          "- debug: logs all messages\n"
1338
          "\n`toolX` can be one of:\n"
1339
          "- core: libgpac core\n"
1340
          "- mutex: log all mutex calls\n"
1341
          "- mem: GPAC memory tracker\n"
1342
          "- module: GPAC modules (av out, font engine, 2D rasterizer)\n"
1343
          "- filter: filter session debugging\n"
1344
          "- sched: filter session scheduler debugging\n"
1345
          "- codec: codec messages (used by encoder and decoder filters)\n"
1346
          "- coding: bitstream formats (audio, video, scene)\n"
1347
          "- container: container formats (ISO File, MPEG-2 TS, AVI, ...) and multiplexer/demultiplexer filters\n"
1348
          "- network: TCP/UDP sockets and TLS\n"
1349
          "- http: HTTP traffic\n"
1350
          "- cache: HTTP cache subsystem\n"
1351
          "- rtp: RTP traffic\n"
1352
          "- dash: HTTP streaming logs\n"
1353
          "- route: ROUTE (ATSC3) debugging\n"
1354
          "- media: messages from generic filters and reframer/rewriter filters\n"
1355
          "- parser: textual parsers (svg, xmt, bt, ...)\n"
1356
          "- mmio: I/O management (AV devices, file, pipes, OpenGL)\n"
1357
          "- audio: audio renderer/mixer/output\n"
1358
          "- script: script engine except console log\n"
1359
          "- console: script console log\n"
1360
          "- scene: scene graph and scene manager\n"
1361
          "- compose: composition engine (2D, 3D, etc)\n"
1362
          "- ctime: media and SMIL timing info from composition engine\n"
1363
          "- interact: interaction messages (UI events and triggered DOM events and VRML route)\n"
1364
          "- rti: run-time stats of compositor\n"
1365
          "- all: all tools logged - other tools can be specified afterwards.  \n"
1366
          "The special keyword `ncl` can be set to disable color logs.  \n"
1367
          "The special keyword `strict` can be set to exit at first error.  \n"
1368
          "\nEX -logs=all@info:dash@debug:ncl\n"
1369
      "This moves all log to info level, dash to debug level and disable color logs"
1370
      , NULL, NULL, GF_ARG_STRING, GF_ARG_SUBSYS_LOG),
1371
 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),
1372
 GF_DEF_ARG("log-dual", "ld", "output to both file and stderr", NULL, NULL, GF_ARG_BOOL, GF_ARG_SUBSYS_LOG),
1373
1374
 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),
1375
 GF_DEF_ARG("store-dir", NULL, "set storage directory", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_CORE),
1376
 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),
1377
 GF_DEF_ARG("js-dirs", NULL, "set javascript directories", NULL, NULL, GF_ARG_STRINGS, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1378
 GF_DEF_ARG("no-js-mods", NULL, "disable javascript module loading", NULL, NULL, GF_ARG_STRINGS, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1379
 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),
1380
 GF_DEF_ARG("lang", NULL, "set preferred language", NULL, NULL, GF_ARG_STRING, GF_ARG_SUBSYS_CORE),
1381
 GF_DEF_ARG("cfg", "opt", "get or set configuration file value. The string parameter can be formatted as:\n"
1382
          "- `section:key=val`: set the key to a new value\n"
1383
          "- `section:key=null`, `section:key`: remove the key\n"
1384
          "- `section=null`: remove the section\n"
1385
          "- no argument: print the entire configuration file\n"
1386
          "- `section`: print the given section\n"
1387
          "- `section:key`: print the given `key` in `section` (section can be set to `*`)"
1388
          "- `*:key`: print the given `key` in all sections"
1389
      , NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_CORE),
1390
 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),
1391
 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),
1392
 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),
1393
 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),
1394
 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),
1395
 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),
1396
 GF_DEF_ARG("devclass", NULL, "set device class\n"
1397
 "- ios: iOS-based mobile device\n"
1398
 "- android: Android-based mobile device\n"
1399
 "- desktop: desktop device", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_HIDE|GF_ARG_SUBSYS_CORE),
1400
1401
 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),
1402
 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),
1403
 GF_DEF_ARG("unhandled-rejection", NULL, "dump unhandled promise rejections", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1404
 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),
1405
 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),
1406
 GF_DEF_ARG("last-dir", NULL, "last working directory (for GUI)", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1407
#ifdef GPAC_HAS_POLL
1408
 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),
1409
#endif
1410
 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),
1411
 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),
1412
 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),
1413
#ifndef GPAC_DISABLE_NETCAP
1414
 GF_DEF_ARG("netcap-dst", NULL, "output packets to indicated file", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1415
 GF_DEF_ARG("netcap-src", NULL, "read packets from indicated file, as produced by -netcap-dst or a pcap or pcapng file", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1416
 GF_DEF_ARG("netcap-nrt", NULL, "ignore real-time regulation when reading packet from capture file", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1417
 GF_DEF_ARG("netcap-loop", NULL, "set number of loops of capture file, -1 means forever", NULL, NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1418
 GF_DEF_ARG("net-filter", NULL, "set packet filtering rules (live or from file). Value is a list of `{OPT,OPT2...}` with OPT in:\n"
1419
 "- m=N: set rule mode - `N` can be `r` for reception only (default), `w` for send only or `rw` for both\n"
1420
 "- s=N: set packet start range to `N`\n"
1421
 "- e=N: set packet end range to `N` (only used for `r` and `f` rules)\n"
1422
 "- n=N: set number of packets to drop to `N`, 0 or 1 means single packet\n"
1423
 "- r=N: random drop one packet every `N`\n"
1424
 "- f=N: drop first packet every `N`\n"
1425
 "- p=P: local port number to filter, if not set the rule applies to all packets\n"
1426
 "- o=N: patch packet instead of droping (always true for TCP), replacing byte at offset `N` (0 is first byte, <0 for random)\n"
1427
 "- v=N: set patch byte value to `N` (hexa) or negative value for random (default)\n"
1428
 "\nEX {p=1234,s=100,n=20}{r=200,s=500,o=10,v=FE}\n"
1429
 "This will 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),
1430
#endif
1431
1432
 GF_DEF_ARG("cache", NULL, "cache directory location", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1433
 GF_DEF_ARG("proxy-on", NULL, "enable HTTP proxy", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1434
 GF_DEF_ARG("proxy-name", NULL, "set HTTP proxy address", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1435
 GF_DEF_ARG("proxy-port", NULL, "set HTTP proxy port", "80", NULL, GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1436
 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),
1437
 GF_DEF_ARG("no-cache", NULL, "disable HTTP caching", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1438
 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),
1439
 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),
1440
 GF_DEF_ARG("cache-size", NULL, "specify cache size in bytes", "100M", NULL, GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1441
 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),
1442
 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),
1443
 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),
1444
 GF_DEF_ARG("broken-cert", NULL, "enable accepting broken SSL certificates", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1445
 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),
1446
 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),
1447
 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),
1448
 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),
1449
 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),
1450
 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),
1451
 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),
1452
1453
#ifdef GPAC_HAS_HTTP2
1454
 GF_DEF_ARG("no-h2", NULL, "disable HTTP2", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1455
 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),
1456
 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),
1457
#endif
1458
1459
 GF_DEF_ARG("dbg-edges", NULL, "log edges status in filter graph before dijkstra resolution (for debug). Edges are logged as edge_source(status, weight, src_cap_idx, dst_cap_idx)", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_FILTERS),
1460
 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),
1461
 GF_DEF_ARG("no-dynf", NULL, "disable dynamically loaded filters", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_FILTERS),
1462
1463
 GF_DEF_ARG("no-block", NULL, "disable blocking mode of filters\n"
1464
      "- no: enable blocking mode\n"
1465
      "- fanout: disable blocking on fan-out, unblocking the PID as soon as one of its destinations requires a packet\n"
1466
      "- all: disable blocking", "no", "no|fanout|all", GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_FILTERS),
1467
 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),
1468
 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),
1469
 GF_DEF_ARG("sched", NULL, "set scheduler mode\n"
1470
    "- free: lock-free queues except for task list (default)\n"
1471
    "- lock: mutexes for queues when several threads\n"
1472
    "- freex: lock-free queues including for task lists (experimental)\n"
1473
    "- flock: mutexes for queues even when no thread (debug mode)\n"
1474
    "- direct: no threads and direct dispatch of tasks whenever possible (debug mode)", "free", "free|lock|flock|freex|direct", GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_FILTERS),
1475
 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),
1476
 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),
1477
1478
 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),
1479
 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),
1480
 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),
1481
 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),
1482
 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),
1483
 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),
1484
 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),
1485
 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),
1486
 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),
1487
1488
 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),
1489
 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),
1490
 GF_DEF_ARG("gl-doublebuf", NULL, "enable OpenGL double buffering", "yes", NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_VIDEO),
1491
 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),
1492
 GF_DEF_ARG("video-output", NULL, "indicate the name of the video output module to use (see `gpac -h modules`)."
1493
  " The reserved name `glfbo` is used in player mode to draw in the OpenGL texture identified by [-glfbo-txid](). "
1494
  " 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),
1495
1496
 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),
1497
1498
 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),
1499
 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),
1500
 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),
1501
 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),
1502
 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),
1503
GF_DEF_ARG("charset", NULL, "set charset when not recognized from input. Possible values are:\n"
1504
"- utf8: force UTF-8\n"
1505
"- utf16: force UTF-16 little endian\n"
1506
"- utf16be: force UTF-16 big endian\n"
1507
"- other: attempt to parse anyway", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_TEXT),
1508
1509
 GF_DEF_ARG("rmt", NULL, "enable profiling through [Remotery](https://github.com/Celtoys/Remotery). A copy of Remotery visualizer is in gpac/share/vis, usually installed in __/usr/share/gpac/vis__ or __Program Files/GPAC/vis__", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1510
 GF_DEF_ARG("rmt-port", NULL, "set remotery port", "17815", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1511
 GF_DEF_ARG("rmt-reuse", NULL, "allow remotery to reuse port", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1512
 GF_DEF_ARG("rmt-localhost", NULL, "make remotery only accepts localhost connection", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1513
 GF_DEF_ARG("rmt-sleep", NULL, "set remotery sleep (ms) between server updates", "10", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1514
 GF_DEF_ARG("rmt-nmsg", NULL, "set remotery number of messages per update", "10", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1515
 GF_DEF_ARG("rmt-qsize", NULL, "set remotery message queue size in bytes", "131072", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1516
 GF_DEF_ARG("rmt-log", NULL, "redirect logs to remotery (experimental, usually not well handled by browser)", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1517
 GF_DEF_ARG("rmt-ogl", NULL, "make remotery sample opengl calls", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1518
1519
 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),
1520
 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),
1521
 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),
1522
 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),
1523
 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),
1524
1525
1526
 {0}
1527
};
1528
1529
1530
const GF_Config *gf_sys_get_lang_file();
1531
1532
GF_EXPORT
1533
const GF_GPACArg *gf_sys_get_options()
1534
0
{
1535
0
  return GPAC_Args;
1536
0
}
1537
1538
static const char *gpac_opt_default(const char *argname)
1539
411k
{
1540
411k
  const GF_GPACArg *arg = NULL;
1541
411k
  u32 i=0;
1542
10.8M
  while (GPAC_Args[i].name) {
1543
10.8M
    arg = &GPAC_Args[i];
1544
10.8M
    i++;
1545
10.8M
    if (!strcmp(arg->name, argname)) break;
1546
10.4M
    arg = NULL;
1547
10.4M
  }
1548
411k
  if (!arg) return NULL;
1549
411k
  return arg->val;
1550
411k
}
1551
1552
GF_EXPORT
1553
Bool gf_opts_get_bool(const char *secName, const char *keyName)
1554
382k
{
1555
382k
  const char *opt = gf_opts_get_key(secName, keyName);
1556
1557
382k
  if (!opt && !strcmp(secName, "core")) {
1558
382k
    opt = gpac_opt_default(keyName);
1559
382k
  }
1560
1561
382k
  if (!opt) return GF_FALSE;
1562
0
  if (!strcmp(opt, "yes")) return GF_TRUE;
1563
0
  if (!strcmp(opt, "true")) return GF_TRUE;
1564
0
  if (!strcmp(opt, "1")) return GF_TRUE;
1565
0
  return GF_FALSE;
1566
0
}
1567
GF_EXPORT
1568
u32 gf_opts_get_int(const char *secName, const char *keyName)
1569
28.3k
{
1570
28.3k
  u32 times=1, val;
1571
28.3k
  char *opt = (char *) gf_opts_get_key(secName, keyName);
1572
1573
28.3k
  if (!opt && !strcmp(secName, "core")) {
1574
28.3k
    opt = (char *) gpac_opt_default(keyName);
1575
28.3k
  }
1576
28.3k
  if (!opt || !opt[0]) return 0;
1577
28.3k
  val = (u32) strlen(opt);
1578
28.3k
  char c = opt[val-1];
1579
28.3k
  switch (c) {
1580
0
  case 'k':
1581
0
  case 'K':
1582
0
    times=1000;
1583
0
    break;
1584
0
  case 'm':
1585
0
  case 'M':
1586
0
    times=1000000;
1587
0
    break;
1588
28.3k
  }
1589
28.3k
  val = atoi(opt);
1590
28.3k
  return val*times;
1591
28.3k
}
1592
1593
GF_EXPORT
1594
Bool gf_sys_set_cfg_option(const char *opt_string)
1595
0
{
1596
0
  size_t sepIdx;
1597
0
  char *sep, *sep2, szSec[1024], szKey[1024], szVal[1024];
1598
0
  sep = strchr(opt_string, ':');
1599
0
  if (!sep) {
1600
0
    sep = strchr(opt_string, '=');
1601
0
    if (sep && !stricmp(sep, "=null")) {
1602
0
      sepIdx = sep - opt_string;
1603
0
      if (sepIdx>=1024) sepIdx = 1023;
1604
0
      strncpy(szSec, opt_string, sepIdx);
1605
0
      szSec[sepIdx] = 0;
1606
0
      gf_opts_del_section(szSec);
1607
0
      return  GF_TRUE;
1608
0
    }
1609
1610
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[CoreArgs] Badly formatted option %s - expected Section:Name=Value\n", opt_string ) );
1611
0
    return GF_FALSE;
1612
0
  }
1613
1614
0
  sepIdx = sep - opt_string;
1615
0
  if (sepIdx>=1024)
1616
0
    sepIdx = 1023;
1617
0
  strncpy(szSec, opt_string, sepIdx);
1618
0
  szSec[sepIdx] = 0;
1619
1620
0
  sep ++;
1621
0
  sep2 = strchr(sep, '=');
1622
0
  if (!sep2) {
1623
0
    gf_opts_set_key(szSec, sep, NULL);
1624
0
    return  GF_TRUE;
1625
0
  }
1626
1627
0
  sepIdx = sep2 - sep;
1628
0
  if (sepIdx>=1024)
1629
0
    sepIdx = 1023;
1630
0
  strncpy(szKey, sep, sepIdx);
1631
0
  szKey[sepIdx] = 0;
1632
1633
0
  sepIdx = strlen(sep2+1);
1634
0
  if (sepIdx>=1024)
1635
0
    sepIdx = 1023;
1636
0
  memcpy(szVal, sep2+1, sepIdx);
1637
0
  szVal[sepIdx] = 0;
1638
1639
0
  if (!stricmp(szKey, "*")) {
1640
0
    if (stricmp(szVal, "null")) {
1641
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[CoreArgs] Badly formatted option %s - expected Section:*=null\n", opt_string));
1642
0
      return GF_FALSE;
1643
0
    }
1644
0
    gf_opts_del_section(szSec);
1645
0
    return GF_TRUE;
1646
0
  }
1647
1648
0
  if (!stricmp(szVal, "null")) {
1649
0
    szVal[0]=0;
1650
0
  }
1651
0
  gf_opts_set_key(szSec, szKey, szVal[0] ? szVal : NULL);
1652
1653
0
  if (!strcmp(szSec, "core") || !strcmp(szSec, "temp")) {
1654
0
    if (!strcmp(szKey, "noprog") && (!strcmp(szVal, "yes")||!strcmp(szVal, "true")||!strcmp(szVal, "1")) ) {
1655
0
      void gpac_disable_progress();
1656
1657
0
      gpac_disable_progress();
1658
0
    }
1659
0
  }
1660
0
  return GF_TRUE;
1661
0
}
1662
1663
void gf_module_reload_dirs();
1664
1665
Bool gf_opts_load_option(const char *arg_name, const char *val, Bool *consumed_next, GF_Err *e)
1666
0
{
1667
0
  const GF_GPACArg *arg = NULL;
1668
0
  u32 i=0;
1669
0
  *e = GF_OK;
1670
0
  *consumed_next = GF_FALSE;
1671
0
  arg_name = arg_name+1;
1672
0
  while (GPAC_Args[i].name) {
1673
0
    arg = &GPAC_Args[i];
1674
0
    i++;
1675
0
    if (!strcmp(arg->name, arg_name)) break;
1676
0
    if (arg->altname && !strcmp(arg->altname, arg_name)) break;
1677
1678
0
    arg = NULL;
1679
0
  }
1680
0
  if (!arg) return GF_FALSE;
1681
1682
0
  if (!strcmp(arg->name, "cfg")) {
1683
0
    *consumed_next = GF_TRUE;
1684
0
    if (val && strchr(val, '=')) {
1685
0
      if (! gf_sys_set_cfg_option(val)) *e = GF_BAD_PARAM;
1686
0
    } else {
1687
0
      u32 sec_len = 0;
1688
0
      char *sep = val ? strchr(val, ':') : NULL;
1689
0
      u32 sec_count = gf_opts_get_section_count();
1690
0
      if (sep) {
1691
0
        sec_len = (u32) (sep - val - 1);
1692
0
        sep++;
1693
0
      } else if (val) {
1694
0
        sec_len = (u32) strlen(val);
1695
0
      }
1696
0
      for (i=0; i<sec_count; i++) {
1697
0
        u32 k, key_count;
1698
0
        Bool sec_hdr_done = GF_FALSE;
1699
0
        const char *sname = gf_opts_get_section_name(i);
1700
0
        key_count = sname ? gf_opts_get_key_count(sname) : 0;
1701
0
        if (!key_count) continue;
1702
1703
0
        if (sec_len) {
1704
0
          if (!strncmp(val, "*", sec_len) || !strncmp(val, "@", sec_len)) {
1705
0
          } else if (strncmp(val, sname, sec_len) || (sec_len != (u32) strlen(sname) ) ) {
1706
0
            continue;
1707
0
          }
1708
0
        }
1709
0
        for (k=0; k<key_count; k++) {
1710
0
          const char *kname = gf_opts_get_key_name(sname, k);
1711
0
          const char *kval = kname ? gf_opts_get_key(sname, kname) : NULL;
1712
0
          if (!kval) continue;
1713
0
          if (sep && strcmp(sep, kname)) continue;
1714
1715
0
          if (!sec_hdr_done) {
1716
0
            sec_hdr_done = GF_TRUE;
1717
0
            fprintf(stdout, "[%s]\n", sname);
1718
0
          }
1719
0
          fprintf(stdout, "%s=%s\n", kname, kval);
1720
0
        }
1721
0
        if (sec_hdr_done)
1722
0
          fprintf(stdout, "\n");
1723
0
      }
1724
0
    }
1725
0
    return GF_TRUE;
1726
0
  }
1727
0
  if (!strcmp(arg->name, "strict-error")) {
1728
0
    gf_log_set_strict_error(1);
1729
0
    return GF_TRUE;
1730
0
  }
1731
1732
0
  if (arg->type==GF_ARG_BOOL) {
1733
0
    if (!val) gf_opts_set_key("temp", arg->name, "yes");
1734
0
    else {
1735
0
      if (!strcmp(val, "yes") || !strcmp(val, "true") || !strcmp(val, "1")) {
1736
0
        *consumed_next = GF_TRUE;
1737
0
        gf_opts_set_key("temp", arg->name, "yes");
1738
0
      } else {
1739
0
        if (!strcmp(val, "no") || !strcmp(val, "false") || !strcmp(val, "0")) {
1740
0
          *consumed_next = GF_TRUE;
1741
0
          gf_opts_set_key("temp", arg->name, "no");
1742
0
        } else {
1743
0
          gf_opts_set_key("temp", arg->name, "yes");
1744
0
        }
1745
0
      }
1746
0
    }
1747
0
  } else {
1748
0
    *consumed_next = GF_TRUE;
1749
0
    if (!val && (arg->type==GF_ARG_BOOL))
1750
0
      gf_opts_set_key("temp", arg->name, "true");
1751
0
    else {
1752
0
      gf_opts_set_key("temp", arg->name, val);
1753
0
      if (!strcmp(arg->name, "mod-dirs")) {
1754
0
        gf_module_reload_dirs();
1755
0
      }
1756
0
    }
1757
0
  }
1758
0
  return GF_TRUE;
1759
0
}
1760
1761
GF_EXPORT
1762
u32 gf_sys_is_gpac_arg(const char *arg_name)
1763
0
{
1764
0
  char *argsep;
1765
0
  u32 arglen;
1766
0
  const GF_GPACArg *arg = NULL;
1767
0
  u32 i=0;
1768
0
  arg_name = arg_name+1;
1769
0
  if (arg_name[0]=='-')
1770
0
    return 1;
1771
0
  if (arg_name[0]=='+')
1772
0
    return 1;
1773
1774
0
  argsep = strchr(arg_name, '=');
1775
0
  if (argsep) arglen = (u32) (argsep - arg_name);
1776
0
  else arglen = (u32) strlen(arg_name);
1777
1778
0
  while (GPAC_Args[i].name) {
1779
0
    arg = &GPAC_Args[i];
1780
0
    i++;
1781
0
    if ((strlen(arg->name) == arglen) && !strncmp(arg->name, arg_name, arglen)) break;
1782
0
    if (arg->altname) {
1783
0
      char *alt = strstr(arg->altname, arg_name);
1784
0
      if (alt) {
1785
0
        char c = alt[strlen(arg_name)];
1786
0
        if (!c || (c==' ')) break;
1787
0
      }
1788
0
    }
1789
0
    arg = NULL;
1790
0
  }
1791
0
  if (!arg) return 0;
1792
0
  if (arg->type==GF_ARG_BOOL) return 1;
1793
0
  if (argsep) return 1;
1794
0
  return 2;
1795
0
}
1796
1797
1798
GF_EXPORT
1799
void gf_sys_print_arg(FILE *helpout, GF_SysPrintArgFlags flags, const GF_GPACArg *arg, const char *arg_subsystem)
1800
0
{
1801
0
  u32 gen_doc = 0;
1802
0
  if (flags & GF_PRINTARG_MD)
1803
0
    gen_doc = 1;
1804
0
  if (!helpout) helpout = stderr;
1805
1806
//#ifdef GPAC_ENABLE_COVERAGE
1807
0
#if 1
1808
0
  if ((arg->name[0]>='A') && (arg->name[0]<='Z')) {
1809
0
    if ((arg->name[1]<'A') || (arg->name[1]>'Z')) {
1810
0
      fprintf(stderr, "\nWARNING: arg %s bad name format, should use lowercase\n", arg->name);
1811
0
      exit(1);
1812
0
    }
1813
0
  }
1814
0
  if (arg->description) {
1815
0
    char *sep;
1816
1817
0
    if ((arg->description[0]>='A') && (arg->description[0]<='Z')) {
1818
0
      if ((arg->description[1]<'A') || (arg->description[1]>'Z')) {
1819
0
        fprintf(stderr, "\nWARNING: arg %s bad name format \"%s\", should use lowercase\n", arg->name, arg->description);
1820
0
        exit(1);
1821
0
      }
1822
0
    }
1823
0
    if (strchr(arg->description, '\t')) {
1824
0
      fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should not use tab\n", arg->name, arg->description);
1825
0
      exit(1);
1826
0
    }
1827
1828
0
    u8 achar = arg->description[strlen(arg->description)-1];
1829
0
    if (achar == '.') {
1830
0
      fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should not end with .\n", arg->name, arg->description);
1831
0
      exit(1);
1832
0
    }
1833
0
    sep = strstr(arg->description, ".\n");
1834
0
    if (sep) {
1835
0
      fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should not contain .\\n \n", arg->name, arg->description);
1836
0
      exit(1);
1837
0
    }
1838
0
    sep = strstr(arg->description, "- ");
1839
0
    if (sep && (sep[-1]!='\n') && !strcmp(sep, "- see")) {
1840
0
      fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should have \\n before first bullet\n", arg->name, arg->description);
1841
0
      exit(1);
1842
0
    }
1843
1844
0
    sep = strchr(arg->description, ' ');
1845
0
    if (sep && (sep>arg->description)) {
1846
0
      sep--;
1847
0
      if ((sep[0] == 's') && (sep[-1] != 's')) {
1848
0
        fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should use infintive\n", arg->name, arg->description);
1849
0
        exit(1);
1850
0
      }
1851
0
    }
1852
0
  }
1853
0
#endif
1854
1855
0
  if (arg->flags & GF_ARG_HINT_HIDE)
1856
0
    return;
1857
1858
0
  const char *syntax=strchr(arg->name, ' ');
1859
0
  char *arg_name=NULL;
1860
0
  if (syntax) {
1861
0
    arg_name = gf_strdup(arg->name);
1862
0
    char *sep = strchr(arg_name, ' ');
1863
0
    sep[0]=0;
1864
0
  }
1865
1866
0
  if (flags & GF_PRINTARG_MAN) {
1867
0
    fprintf(helpout, ".TP\n.B %s%s", (flags&GF_PRINTARG_NO_DASH) ? "" : "\\-", arg_name ? arg_name : arg->name);
1868
0
  }
1869
0
  else if (gen_doc==1) {
1870
0
    if (flags&GF_PRINTARG_NO_DASH) {
1871
0
      gf_sys_format_help(helpout, flags | GF_PRINTARG_HIGHLIGHT_FIRST, "%s", arg_name ? arg_name : arg->name);
1872
0
    } else {
1873
0
      gf_sys_format_help(helpout, flags, "<a id=\"%s\">", arg_name ? arg_name : arg->name);
1874
0
      gf_sys_format_help(helpout, flags | GF_PRINTARG_HIGHLIGHT_FIRST, "-%s", arg_name ? arg_name : arg->name);
1875
0
      gf_sys_format_help(helpout, flags, "</a>");
1876
0
    }
1877
0
  } else {
1878
0
    gf_sys_format_help(helpout, flags | GF_PRINTARG_HIGHLIGHT_FIRST, "%s%s%s",
1879
0
      (flags&GF_PRINTARG_ADD_DASH) ? "-" : "",
1880
0
      (flags&GF_PRINTARG_NO_DASH) ? "" : ((flags&GF_PRINTARG_COLON) ? ":" : "-"),
1881
0
      arg_name ? arg_name : arg->name
1882
0
    );
1883
0
  }
1884
0
  if (arg->altname) {
1885
0
    gf_sys_format_help(helpout, flags, ",");
1886
0
    gf_sys_format_help(helpout, flags | GF_PRINTARG_HIGHLIGHT_FIRST, "%s-%s", (flags&GF_PRINTARG_ADD_DASH) ? "-" : "", arg->altname);
1887
0
  }
1888
0
  if (syntax) {
1889
0
    gf_sys_format_help(helpout, flags, " %s", syntax);
1890
0
    gf_free(arg_name);
1891
0
  }
1892
1893
0
  if (arg->type==GF_ARG_CUSTOM) {
1894
0
    if (arg->val)
1895
0
      gf_sys_format_help(helpout, flags, " `%s`", arg->val);
1896
0
  } else if (arg->type==GF_ARG_INT && arg->values && strchr(arg->values, '|')) {
1897
0
    gf_sys_format_help(helpout, flags, " (Enum");
1898
0
    if (arg->val)
1899
0
      gf_sys_format_help(helpout, flags, ", default: **%s**", arg->val);
1900
0
    gf_sys_format_help(helpout, flags, ")");
1901
0
  } else if (arg->type != GF_ARG_BOOL) {
1902
0
    gf_sys_format_help(helpout, flags, " (");
1903
0
    switch (arg->type) {
1904
0
    case GF_ARG_BOOL: gf_sys_format_help(helpout, flags, "boolean"); break;
1905
0
    case GF_ARG_INT: gf_sys_format_help(helpout, flags, "int"); break;
1906
0
    case GF_ARG_DOUBLE: gf_sys_format_help(helpout, flags, "number"); break;
1907
0
    case GF_ARG_STRING: gf_sys_format_help(helpout, flags, "string"); break;
1908
0
    case GF_ARG_STRINGS: gf_sys_format_help(helpout, flags, "string list"); break;
1909
0
    case GF_ARG_4CC: gf_sys_format_help(helpout, flags, "4CC"); break;
1910
0
    case GF_ARG_4CCS: gf_sys_format_help(helpout, flags, "4CC list"); break;
1911
0
    default: break;
1912
0
    }
1913
0
    if (arg->val)
1914
0
      gf_sys_format_help(helpout, flags, ", default: **%s**", arg->val);
1915
0
    if (arg->values)
1916
0
      gf_sys_format_help(helpout, flags, ", values: __%s__", arg->values);
1917
0
    gf_sys_format_help(helpout, flags, ")");
1918
0
  }
1919
1920
0
  if (flags & GF_PRINTARG_MAN) {
1921
0
    gf_sys_format_help(helpout, flags, "\n%s\n", gf_sys_localized(arg_subsystem, arg->name, arg->description) );
1922
0
  } else {
1923
0
    if (arg->description) {
1924
0
      gf_sys_format_help(helpout, flags | GF_PRINTARG_OPT_DESC, ": %s", gf_sys_localized(arg_subsystem, arg->name, arg->description) );
1925
0
    }
1926
0
    gf_sys_format_help(helpout, flags, "\n");
1927
0
  }
1928
1929
0
  if ((gen_doc==1) && arg->description && strstr(arg->description, "- "))
1930
0
    gf_sys_format_help(helpout, flags, "\n");
1931
0
}
1932
1933
1934
GF_EXPORT
1935
void gf_sys_print_core_help(FILE *helpout, GF_SysPrintArgFlags flags, GF_SysArgMode mode, u32 subsystem_flags)
1936
0
{
1937
0
  u32 i=0;
1938
0
  const GF_GPACArg *args = gf_sys_get_options();
1939
1940
0
  while (args[i].name) {
1941
0
    const GF_GPACArg *arg = &args[i];
1942
0
    i++;
1943
0
    if (arg->flags & GF_ARG_HINT_HIDE) continue;
1944
1945
0
    if (subsystem_flags && !(arg->flags & subsystem_flags)) {
1946
0
      continue;
1947
0
    }
1948
0
    if (mode != GF_ARGMODE_ALL) {
1949
0
      if ((mode==GF_ARGMODE_EXPERT) && !(arg->flags & GF_ARG_HINT_EXPERT)) continue;
1950
0
      else if ((mode==GF_ARGMODE_ADVANCED) && !(arg->flags & GF_ARG_HINT_ADVANCED)) continue;
1951
0
      else if ((mode==GF_ARGMODE_BASE) && (arg->flags & (GF_ARG_HINT_ADVANCED|GF_ARG_HINT_EXPERT) )) continue;
1952
0
    }
1953
0
    gf_sys_print_arg(helpout, flags, arg, "core");
1954
0
  }
1955
0
}
1956
1957
1958
0
#define LINE_OFFSET_DESCR 30
1959
1960
static char *help_buf = NULL;
1961
static u32 help_buf_size=0;
1962
1963
void gf_sys_cleanup_help()
1964
0
{
1965
0
  if (help_buf) {
1966
0
    gf_free(help_buf);
1967
0
    help_buf = NULL;
1968
0
    help_buf_size = 0;
1969
0
  }
1970
0
}
1971
1972
1973
enum
1974
{
1975
  TOK_CODE,
1976
  TOK_BOLD,
1977
  TOK_ITALIC,
1978
  TOK_STRIKE,
1979
  TOK_OPTLINK,
1980
  TOK_LINKSTART,
1981
};
1982
struct _token {
1983
  char *tok;
1984
  GF_ConsoleCodes cmd_type;
1985
} Tokens[] =
1986
{
1987
 {"`", GF_CONSOLE_YELLOW|GF_CONSOLE_ITALIC},
1988
 {"**", GF_CONSOLE_BOLD},
1989
 {"__", GF_CONSOLE_ITALIC},
1990
 {"~~", GF_CONSOLE_STRIKE},
1991
 {"[-", GF_CONSOLE_YELLOW|GF_CONSOLE_ITALIC},
1992
 {"[", GF_CONSOLE_YELLOW|GF_CONSOLE_ITALIC},
1993
};
1994
static u32 nb_tokens = sizeof(Tokens) / sizeof(struct _token);
1995
1996
static u32 line_pos = 0;
1997
1998
//#define CHECK_BALANCED_SEPS
1999
2000
#ifdef CHECK_BALANCED_SEPS
2001
static void check_char_balanced(char *buf, char c)
2002
{
2003
  char *txt = buf;
2004
  while (txt) {
2005
    char *bquote_next;
2006
    char *bquote = strchr(txt, c);
2007
    if (!bquote) break;
2008
    if (c=='\'') {
2009
      if ((bquote[1]=='s') && (bquote[2]==' ')) {
2010
        txt = bquote + 1;
2011
        continue;
2012
      }
2013
    }
2014
    bquote_next = strchr(bquote+1, c);
2015
    if (!bquote_next) {
2016
      fprintf(stderr, "Missing closing %c after %s\n", c, bquote);
2017
      exit(1);
2018
    }
2019
    switch (bquote_next[1] ) {
2020
    case 0:
2021
    case '\n':
2022
    case ' ':
2023
    case ',':
2024
    case '.':
2025
    case ')':
2026
    case ']':
2027
    case ':':
2028
      break;
2029
    default:
2030
      if (c=='\'') {
2031
        if ((bquote_next[1]>='A') && (bquote_next[1]<='Z'))
2032
          break;
2033
      }
2034
      fprintf(stderr, "Missing space after closing %c %s\n", c, bquote_next);
2035
      exit(1);
2036
    }
2037
    txt = bquote_next + 1;
2038
  }
2039
}
2040
#endif
2041
2042
GF_EXPORT
2043
void gf_sys_format_help(FILE *helpout, GF_SysPrintArgFlags flags, const char *fmt, ...)
2044
0
{
2045
0
  char *line;
2046
0
  u32 len;
2047
0
  va_list vlist;
2048
0
  Bool escape_xml = GF_FALSE;
2049
0
  Bool escape_pipe = GF_FALSE;
2050
0
  Bool prev_was_example = GF_FALSE;
2051
0
  Bool prev_has_line_after = GF_FALSE;
2052
0
  u32 gen_doc = 0;
2053
0
  u32 is_app_opts = 0;
2054
0
  if (flags & GF_PRINTARG_MD) {
2055
0
    gen_doc = 1;
2056
0
    if (flags & GF_PRINTARG_ESCAPE_XML)
2057
0
      escape_xml = GF_TRUE;
2058
0
    if (flags & GF_PRINTARG_ESCAPE_PIPE)
2059
0
      escape_pipe = GF_TRUE;
2060
0
  }
2061
0
  if (flags & GF_PRINTARG_MAN)
2062
0
    gen_doc = 2;
2063
0
  if (flags & GF_PRINTARG_IS_APP)
2064
0
    is_app_opts = 1;
2065
0
  if (!helpout) helpout = stderr;
2066
2067
0
  va_start(vlist, fmt);
2068
0
  len=vsnprintf(NULL, 0, fmt, vlist);
2069
0
  va_end(vlist);
2070
0
  if (help_buf_size < len+2) {
2071
0
    help_buf_size = len+2;
2072
0
    help_buf = gf_realloc(help_buf, help_buf_size);
2073
0
  }
2074
0
  va_start(vlist, fmt);
2075
0
  vsprintf(help_buf, fmt, vlist);
2076
0
  va_end(vlist);
2077
2078
#ifdef CHECK_BALANCED_SEPS
2079
  if (gen_doc) {
2080
    check_char_balanced(help_buf, '`');
2081
    check_char_balanced(help_buf, '\'');
2082
  }
2083
#endif
2084
2085
/*#ifdef GPAC_CONFIG_ANDROID
2086
  //on android use logs for help print
2087
  if (!gen_doc) {
2088
    GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("%s", help_buf));
2089
    return;
2090
  }
2091
#endif
2092
*/
2093
2094
0
  line = help_buf;
2095
0
  while (line[0]) {
2096
0
    u32 att_len = 0;
2097
0
    char *tok_sep = NULL;
2098
0
    GF_ConsoleCodes console_code = GF_CONSOLE_RESET;
2099
0
    Bool line_before = GF_FALSE;
2100
0
    Bool line_after = GF_FALSE;
2101
0
    const char *footer_string = NULL;
2102
0
    const char *header_string = NULL;
2103
0
    char *next_line = strchr(line, '\n');
2104
0
    Bool has_token=GF_FALSE;
2105
2106
0
    if (next_line) next_line[0]=0;
2107
2108
0
    if (prev_has_line_after && !strlen(line)) {
2109
0
      if (!next_line) break;
2110
0
      line = next_line+1;
2111
0
      line_pos=0;
2112
0
      continue;
2113
0
    }
2114
2115
0
    if ((line[0]=='#') && (line[1]==' ')) {
2116
0
      if (!gen_doc)
2117
0
        line+=2;
2118
0
      else if (gen_doc==2) {
2119
0
        header_string = ".SH ";
2120
0
        footer_string = "\n.LP";
2121
0
        line+=2;
2122
0
      }
2123
2124
0
      console_code = GF_CONSOLE_GREEN;
2125
0
      line_after = line_before = GF_TRUE;
2126
0
    } else if ((line[0]=='#') && (line[1]=='#') && (line[2]==' ')) {
2127
0
      if (!gen_doc)
2128
0
        line+=3;
2129
0
      else if (gen_doc==2) {
2130
0
        line+=3;
2131
0
        header_string = ".SS ";
2132
0
      }
2133
2134
0
      console_code = GF_CONSOLE_MAGENTA;
2135
0
      line_before = GF_TRUE;
2136
0
    } else if ((line[0]=='#') && (line[1]=='#') && (line[2]=='#') && (line[3]==' ')) {
2137
0
      if (!gen_doc)
2138
0
        line+=4;
2139
0
      else if (gen_doc==2) {
2140
0
        line+=4;
2141
0
        header_string = ".P\n.B\n";
2142
0
      }
2143
2144
0
      console_code = GF_CONSOLE_CYAN;
2145
0
      line_after = GF_TRUE;
2146
0
    } else if ((line[0]=='E') && (line[1]=='X') && (line[2]==' ')) {
2147
0
      line+=3;
2148
0
      console_code = GF_CONSOLE_YELLOW;
2149
2150
0
      if (gen_doc==1) {
2151
0
        header_string = "Example\n```\n";
2152
0
        footer_string = "\n```";
2153
0
      } else if (gen_doc==2) {
2154
0
        header_string = "Example\n.br\n";
2155
0
        footer_string = "\n.br\n";
2156
0
      } else {
2157
0
        header_string = "Example:\n";
2158
0
      }
2159
2160
0
      if (prev_was_example) {
2161
0
        header_string = NULL;
2162
0
      }
2163
2164
0
      if (next_line && (next_line[1]=='E') && (next_line[2]=='X') && (next_line[3]==' ')) {
2165
0
        prev_was_example = GF_TRUE;
2166
0
        footer_string = NULL;
2167
0
      } else {
2168
0
        prev_was_example = GF_FALSE;
2169
0
      }
2170
0
    } else if (!strncmp(line, "Note: ", 6)) {
2171
0
      console_code = GF_CONSOLE_CYAN | GF_CONSOLE_ITALIC;
2172
0
    } else if (!strncmp(line, "Warning: ", 9)) {
2173
0
      line_after = line_before = GF_TRUE;
2174
0
      console_code = GF_CONSOLE_RED | GF_CONSOLE_BOLD;
2175
0
    } else if ( (
2176
0
       ((line[0]=='-') && (line[1]==' '))
2177
0
       || ((line[0]==' ') && (line[1]=='-') && (line[2]==' '))
2178
0
       || ((line[0]==' ') && (line[1]==' ') && (line[2]=='-') && (line[3]==' '))
2179
0
      )
2180
2181
      //look for ": "
2182
0
      && ((tok_sep=strstr(line, ": ")) != NULL )
2183
0
    ) {
2184
0
      if (!gen_doc)
2185
0
        fprintf(helpout, "\t");
2186
0
      while (line[0] != '-') {
2187
0
        fprintf(helpout, " ");
2188
0
        line++;
2189
0
        line_pos++;
2190
2191
0
      }
2192
0
      fprintf(helpout, "* ");
2193
0
      line_pos+=2;
2194
0
      if (!gen_doc)
2195
0
        gf_sys_set_console_code(helpout, GF_CONSOLE_YELLOW);
2196
0
      tok_sep[0] = 0;
2197
0
      fprintf(helpout, "%s", line+2);
2198
0
      line_pos += (u32) strlen(line+2);
2199
0
      tok_sep[0] = ':';
2200
0
      line = tok_sep;
2201
0
      if (!gen_doc)
2202
0
        gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2203
0
    } else if (flags & (GF_PRINTARG_HIGHLIGHT_FIRST | GF_PRINTARG_OPT_DESC)) {
2204
0
      char *sep = strchr(line, ' ');
2205
2206
0
      if (sep) sep[0] = 0;
2207
2208
0
      if (!gen_doc && !(flags & GF_PRINTARG_OPT_DESC) )
2209
0
        gf_sys_set_console_code(helpout, GF_CONSOLE_GREEN);
2210
2211
0
      if ((gen_doc==1) && !(flags & GF_PRINTARG_OPT_DESC) ) {
2212
0
        fprintf(helpout, "__%s__", line);
2213
0
        line_pos += 4+ (u32) strlen(line);
2214
0
      } else {
2215
0
        fprintf(helpout, "%s", line);
2216
0
        line_pos += (u32) strlen(line);
2217
0
      }
2218
2219
0
      if (!gen_doc && !(flags & GF_PRINTARG_OPT_DESC) )
2220
0
        gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2221
2222
0
      if (flags & GF_PRINTARG_OPT_DESC) {
2223
0
        flags = 0;
2224
0
        att_len = line_pos;
2225
0
      }
2226
2227
2228
0
      if (sep) {
2229
0
        sep[0] = ' ';
2230
0
        line = sep;
2231
0
      } else {
2232
0
        line = NULL;
2233
0
      }
2234
0
    }
2235
0
    if (!line) break;
2236
0
    if (gen_doc==2) {
2237
0
      line_before = line_after = GF_FALSE;
2238
0
    }
2239
2240
0
    if (prev_has_line_after) line_before = GF_FALSE;
2241
0
    prev_has_line_after = GF_FALSE;
2242
0
    if (!strlen(line))
2243
0
      prev_has_line_after = GF_TRUE;
2244
2245
0
    if (line_before) {
2246
0
      fprintf(helpout, "\n");
2247
0
      line_pos=0;
2248
0
    }
2249
2250
0
    if (console_code != GF_CONSOLE_RESET) {
2251
0
      if (gen_doc==1) {
2252
0
        if (console_code & GF_CONSOLE_BOLD) {
2253
0
          fprintf(helpout, "__");
2254
0
          line_pos+=2;
2255
0
        }
2256
0
        else if (console_code & GF_CONSOLE_ITALIC) {
2257
0
          fprintf(helpout, "_");
2258
0
          line_pos++;
2259
0
        }
2260
0
      } else if (!gen_doc) {
2261
0
        gf_sys_set_console_code(helpout, console_code);
2262
0
      }
2263
0
    }
2264
2265
0
    if (att_len) {
2266
0
      while (att_len < LINE_OFFSET_DESCR) {
2267
0
        fprintf(helpout, " ");
2268
0
        att_len++;
2269
0
        line_pos++;
2270
0
      }
2271
0
    }
2272
2273
2274
0
    if (header_string) {
2275
0
      fprintf(helpout, "%s", header_string);
2276
0
      line_pos += (u32) strlen(header_string);
2277
0
    }
2278
2279
0
    while (line) {
2280
0
      char *skip_url = NULL;
2281
0
      char *link_start = NULL;
2282
0
      u32 tid=0, i;
2283
0
      char *next_token = NULL;
2284
0
      for (i=0; i<nb_tokens; i++) {
2285
0
        char *tok = strstr(line, Tokens[i].tok);
2286
0
        if (!tok) continue;
2287
0
        if (next_token && ((next_token-line) < (tok-line)) ) continue;
2288
        //check we have an end of token, otherwise consider this regular text
2289
0
        if ((i == TOK_LINKSTART) || (i == TOK_OPTLINK)) {
2290
0
          char *end_tok = strstr(tok, "](");
2291
0
          if (!end_tok) continue;
2292
0
        }
2293
2294
0
        if (i == TOK_LINKSTART) {
2295
0
          if (tid == TOK_OPTLINK) continue;
2296
0
          if (gen_doc!=1) {
2297
0
            char *link_end;
2298
0
            skip_url = strstr(tok, "](");
2299
0
            link_end = skip_url;
2300
0
            if (skip_url) skip_url = strstr(skip_url, ")");
2301
0
            if (skip_url) skip_url ++;
2302
2303
0
            if (!skip_url) continue;
2304
0
            link_start = tok+1;
2305
0
            link_end[0] = 0;
2306
0
          } else {
2307
0
            continue;
2308
0
          }
2309
0
        }
2310
0
        next_token=tok;
2311
0
        tid=i;
2312
0
      }
2313
0
      if (next_token) {
2314
0
        next_token[0]=0;
2315
0
      }
2316
0
      if ((gen_doc==1) && has_token) {
2317
0
        if (tid==TOK_CODE) {
2318
0
          fprintf(helpout, "`%s`", line);
2319
0
          line_pos+=2;
2320
0
        } else if (tid==TOK_ITALIC) {
2321
0
          fprintf(helpout, "_%s_", line);
2322
0
          line_pos+=2;
2323
0
        } else if (tid==TOK_BOLD) {
2324
0
          fprintf(helpout, "__%s__", line);
2325
0
          line_pos+=4;
2326
0
        } else {
2327
0
          fprintf(helpout, "%s", line);
2328
0
        }
2329
0
      } else if (escape_xml) {
2330
0
        char *xml_line = line;
2331
0
        while (xml_line) {
2332
0
          char *xml_start = strchr(xml_line, '<');
2333
0
          char *xml_end = strchr(xml_line, '>');
2334
2335
0
          if (xml_end && (xml_start > xml_end)) xml_start = xml_end;
2336
0
          else if (!xml_start && xml_end) xml_start = xml_end;
2337
0
          else if (xml_start && xml_end) xml_end = NULL;
2338
2339
0
          if (xml_start) {
2340
0
            u8 c = xml_start[0];
2341
0
            xml_start[0] = 0;
2342
0
            fprintf(helpout, "%s", xml_line);
2343
0
            fprintf(helpout, xml_end ? "&gt;" : "&lt;");
2344
0
            xml_start[0] = c;
2345
0
            xml_line = xml_start+1;
2346
0
          } else {
2347
0
            fprintf(helpout, "%s", xml_line);
2348
0
            break;
2349
0
          }
2350
0
        }
2351
0
      } else if (escape_pipe) {
2352
0
        char *src_line = line;
2353
0
        while (src_line) {
2354
0
          char *pipe_start = strchr(src_line, '|');
2355
0
          if (pipe_start && (pipe_start[1]==' '))
2356
0
            pipe_start = NULL;
2357
2358
0
          if (pipe_start) {
2359
0
            pipe_start[0] = 0;
2360
0
            fprintf(helpout, "%s ", src_line);
2361
0
            pipe_start[0] = '|';
2362
0
            src_line = pipe_start+1;
2363
0
          } else {
2364
0
            fprintf(helpout, "%s", src_line);
2365
0
            break;
2366
0
          }
2367
0
        }
2368
0
      } else {
2369
0
        fprintf(helpout, "%s", line);
2370
0
      }
2371
0
      line_pos+=(u32) strlen(line);
2372
2373
0
      if (!next_token) break;
2374
0
      has_token = !has_token;
2375
2376
0
      if (!gen_doc) {
2377
0
        if (has_token) {
2378
0
          u32 cmd;
2379
0
          if (Tokens[tid].cmd_type & 0xFFFF) {
2380
0
            cmd = Tokens[tid].cmd_type;
2381
0
          } else {
2382
0
            cmd = Tokens[tid].cmd_type | console_code;
2383
0
          }
2384
2385
0
          if (console_code&GF_CONSOLE_ITALIC) {
2386
0
            if (Tokens[tid].cmd_type & GF_CONSOLE_ITALIC) {
2387
0
              cmd &= ~GF_CONSOLE_ITALIC;
2388
0
              cmd |= GF_CONSOLE_BOLD;
2389
0
            }
2390
0
          }
2391
0
          else if (console_code&GF_CONSOLE_BOLD) {
2392
0
            if (Tokens[tid].cmd_type & GF_CONSOLE_BOLD) {
2393
0
              cmd &= ~GF_CONSOLE_BOLD;
2394
0
              cmd |= GF_CONSOLE_ITALIC;
2395
0
            }
2396
0
          }
2397
0
          gf_sys_set_console_code(helpout, cmd);
2398
0
        } else {
2399
0
          gf_sys_set_console_code(helpout, console_code);
2400
0
        }
2401
0
      }
2402
0
      line = next_token + (u32) strlen(Tokens[tid].tok);
2403
2404
0
      if (skip_url) {
2405
0
        if (link_start)
2406
0
          fprintf(helpout, "%s", link_start);
2407
0
        if (!gen_doc)
2408
0
          gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2409
0
        has_token = GF_FALSE;
2410
0
        line = skip_url;
2411
2412
0
      }
2413
2414
0
      if (has_token && tid==TOK_OPTLINK) {
2415
0
        char *link = strchr(line, '(');
2416
0
        assert(link);
2417
0
        link++;
2418
0
        char *end_link = strchr(line, ')');
2419
0
        if (end_link) end_link[0] = 0;
2420
0
        char *end_tok = strchr(line, ']');
2421
0
        if (end_tok) end_tok[0] = 0;
2422
2423
0
        if (gen_doc==1) {
2424
0
          if (!strncmp(link, "GPAC", 4)) {
2425
0
            fprintf(helpout, "[-%s](gpac_general/#%s)", line, line);
2426
0
            line_pos+=7 + 2*(u32)strlen(line) + (u32)strlen("gpac_general");
2427
0
          } else if (!strncmp(link, "LOG", 3)) {
2428
0
            fprintf(helpout, "[-%s](core_logs/#%s)", line, line);
2429
0
            line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen("core_logs");
2430
0
          } else if (!strncmp(link, "CORE", 4)) {
2431
0
            fprintf(helpout, "[-%s](core_options/#%s)", line, line);
2432
0
            line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen("core_options");
2433
0
          } else if (!strncmp(link, "CFG", 3)) {
2434
0
            fprintf(helpout, "[-%s](core_config/#%s)", line, line);
2435
0
            line_pos+=7 + 2*(u32)strlen(line) + (u32)strlen("core_config");
2436
0
          } else if (!strncmp(link, "MP4B_GEN", 8)) {
2437
0
            fprintf(helpout, "[-%s](mp4box-gen-opts/#%s)", line, line);
2438
0
            line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen("mp4box-gen-opts");
2439
0
          } else if (strlen(link)) {
2440
0
            fprintf(helpout, "[-%s](%s/#%s)", line, link, line);
2441
0
            line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen(link);
2442
0
          } else if (is_app_opts || !strcmp(line, "i") || !strcmp(line, "o") || !strcmp(line, "h")) {
2443
0
            fprintf(helpout, "[-%s](#%s)", line, line);
2444
0
            line_pos+=6 + 2* (u32)strlen(line);
2445
0
          } else {
2446
            //this is a filter opt, don't print '-'
2447
0
            fprintf(helpout, "[%s](#%s)", line, line);
2448
0
            line_pos+=5 + 2* (u32)strlen(line);
2449
0
          }
2450
0
        } else {
2451
0
          if (gen_doc==2)
2452
0
            fprintf(helpout, ".I ");
2453
2454
0
          if (!strncmp(link, "GPAC", 4)
2455
0
            || !strncmp(link, "LOG", 3)
2456
0
            || !strncmp(link, "CORE", 4)
2457
0
            || strlen(link)
2458
0
            || !strcmp(line, "i") || !strcmp(line, "o") || !strcmp(line, "h")
2459
0
          ) {
2460
0
            fprintf(helpout, "-%s", line);
2461
0
            line_pos+=1+ (u32)strlen(line);
2462
0
          } else {
2463
0
            fprintf(helpout, "%s", line);
2464
0
            line_pos+= (u32)strlen(line);
2465
0
          }
2466
0
          if (!gen_doc)
2467
0
            gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2468
0
        }
2469
0
        if (!end_link) break;
2470
0
        line = end_link+1;
2471
0
        has_token = GF_FALSE;
2472
0
      }
2473
0
    }
2474
2475
0
    if (has_token && !gen_doc)
2476
0
      gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2477
2478
0
    if (footer_string) {
2479
0
      fprintf(helpout, "%s", footer_string);
2480
0
      line_pos += (u32) strlen(footer_string);
2481
0
    }
2482
0
    if (console_code != GF_CONSOLE_RESET) {
2483
0
      if (gen_doc==1) {
2484
0
        if (console_code & GF_CONSOLE_BOLD) {
2485
0
          fprintf(helpout, "__");
2486
0
          line_pos+=2;
2487
0
        } else if (console_code & GF_CONSOLE_ITALIC) {
2488
0
          fprintf(helpout, "_");
2489
0
          line_pos++;
2490
0
        }
2491
0
      } else if (!gen_doc) {
2492
0
        gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2493
0
      }
2494
0
    }
2495
2496
0
    if (line_after) {
2497
0
      if (gen_doc==1) fprintf(helpout, "  ");
2498
0
      fprintf(helpout, (flags & GF_PRINTARG_NL_TO_BR) ? "<br/>" : "\n");
2499
0
      line_pos=0;
2500
0
      prev_has_line_after = GF_TRUE;
2501
0
    }
2502
2503
0
    if (!next_line) break;
2504
0
    next_line[0]=0;
2505
0
    if (gen_doc==1) fprintf(helpout, "  ");
2506
0
    line = next_line+1;
2507
0
    if (gen_doc==2) {
2508
0
      if (line[0] != '.')
2509
0
        fprintf(helpout, "\n.br\n");
2510
0
      else
2511
0
        fprintf(helpout, "\n");
2512
0
    } else
2513
0
      fprintf(helpout, (line[0] && (flags & GF_PRINTARG_NL_TO_BR)) ? "<br/>" : "\n");
2514
0
    line_pos=0;
2515
0
  }
2516
0
}
2517
2518
GF_EXPORT
2519
Bool gf_strnistr(const char *text, const char *subtext, u32 subtext_len)
2520
0
{
2521
0
  if (!*text || !subtext || !subtext_len)
2522
0
    return GF_FALSE;
2523
2524
0
  while (*text) {
2525
0
    if (tolower(*text) == *subtext) {
2526
0
      if (!strnicmp(text, subtext, subtext_len))
2527
0
        return GF_TRUE;
2528
2529
0
    }
2530
0
    text++;
2531
0
  }
2532
0
  return GF_FALSE;
2533
0
}
2534
2535
GF_EXPORT
2536
Bool gf_sys_word_match(const char *orig, const char *dst)
2537
0
{
2538
0
  s32 dist = 0;
2539
0
  u32 match = 0;
2540
0
  u32 i;
2541
0
  u32 olen = (u32) strlen(orig);
2542
0
  u32 dlen = (u32) strlen(dst);
2543
0
  u32 *run;
2544
2545
0
  if ((olen>=3) && (olen<dlen) && !strncmp(orig, dst, olen))
2546
0
    return GF_TRUE;
2547
0
  if ((dlen>=3) && (dlen<olen) && !strncmp(orig, dst, dlen))
2548
0
    return GF_TRUE;
2549
2550
0
  if (olen*2 < dlen) {
2551
0
    char *s1 = strchr(orig, ':');
2552
0
    char *s2 = strchr(dst, ':');
2553
0
    if (s1 && !s2) return GF_FALSE;
2554
0
    if (!s1 && s2) return GF_FALSE;
2555
2556
0
    if (gf_strnistr(dst, orig, MIN(olen, dlen)))
2557
0
      return GF_TRUE;
2558
0
    return GF_FALSE;
2559
0
  }
2560
2561
0
  if ((dlen>=3) && gf_strnistr(orig, dst, dlen))
2562
0
    return GF_TRUE;
2563
0
  if ((olen>=3) && gf_strnistr(dst, orig, olen))
2564
0
    return GF_TRUE;
2565
2566
0
  run = gf_malloc(sizeof(u32) * olen);
2567
0
  memset(run, 0, sizeof(u32) * olen);
2568
2569
0
  for (i=0; i<dlen; i++) {
2570
0
    u32 dist_char;
2571
0
    u32 offset=0;
2572
0
    char *pos;
2573
2574
0
retry_char:
2575
0
    pos = strchr(orig+offset, dst[i]);
2576
0
    if (!pos) continue;
2577
0
    dist_char = (u32) (pos - orig);
2578
0
    if (!run[dist_char]) {
2579
0
      run[dist_char] = i+1;
2580
0
      match++;
2581
0
    } else if (run[dist_char] > i) {
2582
0
      run[dist_char] = i+1;
2583
0
      match++;
2584
0
    } else {
2585
      //this can be a repeated character
2586
0
      offset++;
2587
0
      goto retry_char;
2588
2589
0
    }
2590
0
  }
2591
0
  if (match*2<olen) {
2592
0
    gf_free(run);
2593
0
    return GF_FALSE;
2594
0
  }
2595
/*
2596
  //if 4/5 of characters are matched, suggest it
2597
  if (match * 5 >= 4 * dlen ) {
2598
    gf_free(run);
2599
    return GF_TRUE;
2600
  }
2601
  if ((olen<=4) && (match>=3) && (dlen*2<olen*3) ) {
2602
    gf_free(run);
2603
    return GF_TRUE;
2604
  }
2605
*/
2606
0
  for (i=0; i<olen; i++) {
2607
0
    if (!i) {
2608
0
      if (run[0]==1)
2609
0
        dist++;
2610
0
    } else if (run[i-1] + 1 == run[i]) {
2611
0
      dist++;
2612
0
    }
2613
0
  }
2614
0
  gf_free(run);
2615
  //if half the characters are in order, consider a match
2616
  //if arg is small only check dst
2617
0
  if ((olen<=4) && (dist >= 2))
2618
0
    return GF_TRUE;
2619
0
  if ((dist*2 >= (s32) olen) && (dist*2 >= (s32) dlen))
2620
0
    return GF_TRUE;
2621
0
  return GF_FALSE;
2622
0
}