Coverage Report

Created: 2024-02-11 06:48

/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
822
#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
4.11k
{
85
4.11k
  char szPath[GF_MAX_PATH];
86
4.11k
  FILE *f;
87
4.11k
  int concatres;
88
89
4.11k
  if (! gf_dir_exists(path)) return 0;
90
91
822
  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
822
  concatres = snprintf(szPath, GF_MAX_PATH, "%s%c%s", path, GF_PATH_SEPARATOR, name);
104
822
  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
822
  f = fopen(szPath, "rb");
110
822
  if (!f) return GF_FALSE;
111
0
  fclose(f);
112
0
  if (outPath != path) strcpy(outPath, path);
113
0
  return GF_TRUE;
114
822
}
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
  gf_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
4.93k
{
358
4.93k
  char app_path[GF_MAX_PATH];
359
4.93k
  char *sep;
360
4.93k
#if (defined(__DARWIN__) || defined(__APPLE__) || defined(GPAC_CONFIG_LINUX))
361
4.93k
  u32 size;
362
4.93k
#endif
363
364
  /*on OSX, Linux & co, user home is where we store the cfg file*/
365
4.93k
  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
822
    char *user_home = getenv("HOME");
378
#ifdef GPAC_CONFIG_IOS
379
    char buf[PATH_MAX];
380
    char *res;
381
#endif
382
383
822
    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
822
      strcpy(file_path, user_home);
395
396
822
    if (file_path[strlen(file_path)-1] == '/') file_path[strlen(file_path)-1] = 0;
397
398
    //cleanup of old install in .gpacrc
399
822
    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
822
    strcat(file_path, "/.gpac");
406
822
    if (!gf_dir_exists(file_path)) {
407
0
      gf_mkdir(file_path);
408
0
    }
409
822
    return 1;
410
822
  }
411
412
4.11k
  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
1.64k
    size = readlink("/proc/self/exe", file_path, GF_MAX_PATH-1);
424
1.64k
    if (size>0) {
425
1.64k
      file_path[size] = 0;
426
1.64k
      sep = strrchr(file_path, '/');
427
1.64k
      if (sep) sep[0] = 0;
428
1.64k
      return 1;
429
1.64k
    }
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
1.64k
  }
457
458
2.46k
  if (path_type==GF_PATH_LIB) {
459
1.64k
#if defined(__DARWIN__) || defined(__APPLE__) || defined(GPAC_CONFIG_LINUX)
460
1.64k
    _Dl_info dl_info;
461
1.64k
    dl_info.dli_fname = NULL;
462
1.64k
    if (dladdr((void *)get_default_install_path, &dl_info)
463
1.64k
      && dl_info.dli_fname
464
1.64k
    ) {
465
1.64k
      strcpy(file_path, dl_info.dli_fname);
466
1.64k
      sep = strrchr(file_path, '/');
467
1.64k
      if (sep) sep[0] = 0;
468
1.64k
      return 1;
469
1.64k
    }
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
1.64k
  }
479
480
#if defined(GPAC_CONFIG_EMSCRIPTEN)
481
  strcpy(app_path, "/usr/");
482
#else
483
  /*locate the app*/
484
822
  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
822
#endif
489
490
  /*installed or symlink on system, user user home directory*/
491
822
  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
822
  if (path_type==GF_PATH_SHARE) {
511
822
    Bool try_lib=GF_TRUE;
512
822
    if (get_default_install_path(app_path, GF_PATH_CFG)) {
513
822
      sep = strstr(app_path, ".gpac/");
514
822
      if (sep) sep[5]=0;
515
      /*GUI not found, look in ~/.gpac/share/gui/ */
516
822
      strcat(app_path, "/share");
517
822
      if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
518
822
    }
519
520
    /*GUI not found, look in gpac distribution if any */
521
822
    if (get_default_install_path(app_path, GF_PATH_APP)) {
522
822
      GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[core] trying to locate share from application dir %s\n", app_path));
523
822
      strcat(app_path, "/");
524
1.64k
retry_lib:
525
1.64k
      sep = strstr(app_path, "/bin/");
526
1.64k
      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
1.64k
      sep = strstr(app_path, "/build/");
534
1.64k
      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
1.64k
    }
540
1.64k
    if (get_default_install_path(app_path, GF_PATH_LIB)) {
541
1.64k
      GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[core] trying to locate share from dynamic libgpac dir %s\n", app_path));
542
1.64k
      sep = strstr(app_path, "/lib");
543
1.64k
      if (sep) {
544
1.64k
        sep[0] = 0;
545
1.64k
        strcat(app_path, "/share");
546
1.64k
        if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
547
1.64k
      }
548
1.64k
      if (try_lib) {
549
822
        try_lib = GF_FALSE;
550
822
        goto retry_lib;
551
822
      }
552
1.64k
    }
553
    /*GUI not found, look in .app for OSX case*/
554
1.64k
  }
555
556
822
  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
822
  sep = strstr(app_path, ".app/");
601
822
  if (sep) sep[4] = 0;
602
603
  /*we are looking for .app install path, or GUI */
604
822
  if (path_type==GF_PATH_SHARE) {
605
822
#ifndef GPAC_CONFIG_IOS
606
822
    strcat(app_path, "/Contents/MacOS/share");
607
822
    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
822
  }
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
822
  return 0;
622
822
}
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
  gf_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 && strpbrk(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
822
{
1182
822
  return get_default_install_path(path_buffer, GF_PATH_SHARE);
1183
822
}
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
2.70M
{
1247
2.70M
  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
18.1k
{
1258
18.1k
  if (!gpac_global_config) return GF_BAD_PARAM;
1259
0
  return gf_cfg_set_key(gpac_global_config, secName, keyName, keyValue);
1260
18.1k
}
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
76.4k
{
1295
76.4k
  const char *res = NULL;
1296
76.4k
  const char *gf_cfg_get_key_internal(GF_Config *iniFile, const char *secName, const char *keyName, Bool restricted_only);
1297
76.4k
  if (gpac_global_config)
1298
0
    res = gf_cfg_get_key_internal(gpac_global_config, secName, keyName, GF_TRUE);
1299
76.4k
  return res;
1300
76.4k
}
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", NULL, "set packet capture and filtering rules formatted as [CFG][RULES]. Each `-netcap` argument will define a configuration\n"
1415
 "[CFG] is an optional comma-separated list of:\n"
1416
 "- id=ID: ID (string) for this configuration. If NULL, configuration will apply to all sockets not specifying a netcap ID\n"
1417
 "- src=F: read packets from `F`, as produced by GPAC or a pcap or pcapng file\n"
1418
 "- dst=F: output packets to `F` (no pcap/pcapng support), cannot be set if src is set\n"
1419
 "- loop[=N]: loop capture file N times, or forever if N is not set or negative\n"
1420
 "- nrt: disable real-time playback\n"
1421
 "[RULES] is an optional list of `[OPT,OPT2...]` with OPT in:\n"
1422
 "- m=N: set rule mode - `N` can be `r` for reception only (default), `w` for send only or `rw` for both\n"
1423
 "- s=N: set packet start range to `N`\n"
1424
 "- e=N: set packet end range to `N` (only used for `r` and `f` rules)\n"
1425
 "- n=N: set number of packets to drop to `N` - not set, 0 or 1 means single packet\n"
1426
 "- r=N: random drop one packet every `N`\n"
1427
 "- f=N: drop first packet every `N`\n"
1428
 "- p=P: local port number to filter, if not set the rule applies to all packets\n"
1429
 "- o=N: patch packet instead of droping (always true for TCP), replacing byte at offset `N` (0 is first byte, <0 for random)\n"
1430
 "- v=N: set patch byte value to `N` (hexa) or negative value for random (default)\n"
1431
 "\nEX -netcap=dst=dump.gpc\n"
1432
 "This will record packets to dump.gpc\n"
1433
 "\nEX -netcap=src=dump.gpc,id=NC1 -i session1.sdp:NCID=NC1 -i session2.sdp\n"
1434
 "This will read packets from dump.gpc only for session1.sdp and let session2.sdp use regular sockets\n"
1435
 "\nEX -netcap=[p=1234,s=100,n=20][r=200,s=500,o=10,v=FE]\n"
1436
 "This will use regular network interface and drop packets 100 to 119 on port 1234 and patch one random packet every 200 starting from packet 500, setting byte 10 to FE", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1437
#endif
1438
1439
 GF_DEF_ARG("cache", NULL, "cache directory location", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1440
 GF_DEF_ARG("proxy-on", NULL, "enable HTTP proxy", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1441
 GF_DEF_ARG("proxy-name", NULL, "set HTTP proxy address", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1442
 GF_DEF_ARG("proxy-port", NULL, "set HTTP proxy port", "80", NULL, GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1443
 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),
1444
 GF_DEF_ARG("no-cache", NULL, "disable HTTP caching", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1445
 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),
1446
 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),
1447
 GF_DEF_ARG("cache-size", NULL, "specify cache size in bytes", "100M", NULL, GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1448
 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),
1449
 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),
1450
 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),
1451
 GF_DEF_ARG("broken-cert", NULL, "enable accepting broken SSL certificates", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1452
 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),
1453
 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),
1454
 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),
1455
 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),
1456
 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),
1457
 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),
1458
 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),
1459
1460
#ifdef GPAC_HAS_HTTP2
1461
 GF_DEF_ARG("no-h2", NULL, "disable HTTP2", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1462
 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),
1463
 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),
1464
#endif
1465
1466
 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),
1467
 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),
1468
 GF_DEF_ARG("no-dynf", NULL, "disable dynamically loaded filters", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_FILTERS),
1469
1470
 GF_DEF_ARG("no-block", NULL, "disable blocking mode of filters\n"
1471
      "- no: enable blocking mode\n"
1472
      "- fanout: disable blocking on fan-out, unblocking the PID as soon as one of its destinations requires a packet\n"
1473
      "- all: disable blocking", "no", "no|fanout|all", GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_FILTERS),
1474
 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),
1475
 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),
1476
 GF_DEF_ARG("sched", NULL, "set scheduler mode\n"
1477
    "- free: lock-free queues except for task list (default)\n"
1478
    "- lock: mutexes for queues when several threads\n"
1479
    "- freex: lock-free queues including for task lists (experimental)\n"
1480
    "- flock: mutexes for queues even when no thread (debug mode)\n"
1481
    "- 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),
1482
 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),
1483
 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),
1484
1485
 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),
1486
 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),
1487
 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),
1488
 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),
1489
 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),
1490
 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),
1491
 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),
1492
 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),
1493
 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),
1494
1495
 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),
1496
 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),
1497
 GF_DEF_ARG("gl-doublebuf", NULL, "enable OpenGL double buffering", "yes", NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_VIDEO),
1498
 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),
1499
 GF_DEF_ARG("video-output", NULL, "indicate the name of the video output module to use (see `gpac -h modules`)."
1500
  " The reserved name `glfbo` is used in player mode to draw in the OpenGL texture identified by [-glfbo-txid](). "
1501
  " 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),
1502
1503
 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),
1504
1505
 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),
1506
 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),
1507
 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),
1508
 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),
1509
 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),
1510
GF_DEF_ARG("charset", NULL, "set charset when not recognized from input. Possible values are:\n"
1511
"- utf8: force UTF-8\n"
1512
"- utf16: force UTF-16 little endian\n"
1513
"- utf16be: force UTF-16 big endian\n"
1514
"- other: attempt to parse anyway", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_TEXT),
1515
1516
 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),
1517
 GF_DEF_ARG("rmt-port", NULL, "set remotery port", "17815", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1518
 GF_DEF_ARG("rmt-reuse", NULL, "allow remotery to reuse port", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1519
 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),
1520
 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),
1521
 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),
1522
 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),
1523
 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),
1524
 GF_DEF_ARG("rmt-ogl", NULL, "make remotery sample opengl calls", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1525
1526
 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),
1527
 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),
1528
 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),
1529
 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),
1530
 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),
1531
1532
1533
 {0}
1534
};
1535
1536
1537
const GF_Config *gf_sys_get_lang_file();
1538
1539
GF_EXPORT
1540
const GF_GPACArg *gf_sys_get_options()
1541
0
{
1542
0
  return GPAC_Args;
1543
0
}
1544
1545
static const char *gpac_opt_default(const char *argname)
1546
867k
{
1547
867k
  const GF_GPACArg *arg = NULL;
1548
867k
  u32 i=0;
1549
37.5M
  while (GPAC_Args[i].name) {
1550
37.5M
    arg = &GPAC_Args[i];
1551
37.5M
    i++;
1552
37.5M
    if (!strcmp(arg->name, argname)) break;
1553
36.6M
    arg = NULL;
1554
36.6M
  }
1555
867k
  if (!arg) return NULL;
1556
867k
  return arg->val;
1557
867k
}
1558
1559
GF_EXPORT
1560
Bool gf_opts_get_bool(const char *secName, const char *keyName)
1561
1.09M
{
1562
1.09M
  const char *opt = gf_opts_get_key(secName, keyName);
1563
1564
1.09M
  if (!opt && !strcmp(secName, "core")) {
1565
743k
    opt = gpac_opt_default(keyName);
1566
743k
  }
1567
1568
1.09M
  if (!opt) return GF_FALSE;
1569
0
  if (!strcmp(opt, "yes")) return GF_TRUE;
1570
0
  if (!strcmp(opt, "true")) return GF_TRUE;
1571
0
  if (!strcmp(opt, "1")) return GF_TRUE;
1572
0
  return GF_FALSE;
1573
0
}
1574
GF_EXPORT
1575
u32 gf_opts_get_int(const char *secName, const char *keyName)
1576
124k
{
1577
124k
  u32 times=1, val;
1578
124k
  char *opt = (char *) gf_opts_get_key(secName, keyName);
1579
1580
124k
  if (!opt && !strcmp(secName, "core")) {
1581
124k
    opt = (char *) gpac_opt_default(keyName);
1582
124k
  }
1583
124k
  if (!opt || !opt[0]) return 0;
1584
104k
  val = (u32) strlen(opt);
1585
104k
  char c = opt[val-1];
1586
104k
  switch (c) {
1587
0
  case 'k':
1588
0
  case 'K':
1589
0
    times=1000;
1590
0
    break;
1591
0
  case 'm':
1592
1.48k
  case 'M':
1593
1.48k
    times=1000000;
1594
1.48k
    break;
1595
104k
  }
1596
104k
  val = atoi(opt);
1597
104k
  return val*times;
1598
104k
}
1599
1600
GF_EXPORT
1601
Bool gf_sys_set_cfg_option(const char *opt_string)
1602
0
{
1603
0
  size_t sepIdx;
1604
0
  char *sep, *sep2, szSec[1024], szKey[1024], szVal[1024];
1605
0
  sep = strchr(opt_string, ':');
1606
0
  if (!sep) {
1607
0
    sep = strchr(opt_string, '=');
1608
0
    if (sep && !stricmp(sep, "=null")) {
1609
0
      sepIdx = sep - opt_string;
1610
0
      if (sepIdx>=1024) sepIdx = 1023;
1611
0
      strncpy(szSec, opt_string, sepIdx);
1612
0
      szSec[sepIdx] = 0;
1613
0
      gf_opts_del_section(szSec);
1614
0
      return  GF_TRUE;
1615
0
    }
1616
1617
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[CoreArgs] Badly formatted option %s - expected Section:Name=Value\n", opt_string ) );
1618
0
    return GF_FALSE;
1619
0
  }
1620
1621
0
  sepIdx = sep - opt_string;
1622
0
  if (sepIdx>=1024)
1623
0
    sepIdx = 1023;
1624
0
  strncpy(szSec, opt_string, sepIdx);
1625
0
  szSec[sepIdx] = 0;
1626
1627
0
  sep ++;
1628
0
  sep2 = strchr(sep, '=');
1629
0
  if (!sep2) {
1630
0
    gf_opts_set_key(szSec, sep, NULL);
1631
0
    return  GF_TRUE;
1632
0
  }
1633
1634
0
  sepIdx = sep2 - sep;
1635
0
  if (sepIdx>=1024)
1636
0
    sepIdx = 1023;
1637
0
  strncpy(szKey, sep, sepIdx);
1638
0
  szKey[sepIdx] = 0;
1639
1640
0
  sepIdx = strlen(sep2+1);
1641
0
  if (sepIdx>=1024)
1642
0
    sepIdx = 1023;
1643
0
  memcpy(szVal, sep2+1, sepIdx);
1644
0
  szVal[sepIdx] = 0;
1645
1646
0
  if (!stricmp(szKey, "*")) {
1647
0
    if (stricmp(szVal, "null")) {
1648
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[CoreArgs] Badly formatted option %s - expected Section:*=null\n", opt_string));
1649
0
      return GF_FALSE;
1650
0
    }
1651
0
    gf_opts_del_section(szSec);
1652
0
    return GF_TRUE;
1653
0
  }
1654
1655
0
  if (!stricmp(szVal, "null")) {
1656
0
    szVal[0]=0;
1657
0
  }
1658
0
  gf_opts_set_key(szSec, szKey, szVal[0] ? szVal : NULL);
1659
1660
0
  if (!strcmp(szSec, "core") || !strcmp(szSec, "temp")) {
1661
0
    if (!strcmp(szKey, "noprog") && (!strcmp(szVal, "yes")||!strcmp(szVal, "true")||!strcmp(szVal, "1")) ) {
1662
0
      void gpac_disable_progress();
1663
1664
0
      gpac_disable_progress();
1665
0
    }
1666
0
  }
1667
0
  return GF_TRUE;
1668
0
}
1669
1670
void gf_module_reload_dirs();
1671
1672
Bool gf_opts_load_option(const char *arg_name, const char *val, Bool *consumed_next, GF_Err *e)
1673
0
{
1674
0
  const GF_GPACArg *arg = NULL;
1675
0
  u32 i=0;
1676
0
  *e = GF_OK;
1677
0
  *consumed_next = GF_FALSE;
1678
0
  arg_name = arg_name+1;
1679
0
  while (GPAC_Args[i].name) {
1680
0
    arg = &GPAC_Args[i];
1681
0
    i++;
1682
0
    if (!strcmp(arg->name, arg_name)) break;
1683
0
    if (arg->altname && !strcmp(arg->altname, arg_name)) break;
1684
1685
0
    arg = NULL;
1686
0
  }
1687
0
  if (!arg) return GF_FALSE;
1688
1689
0
  if (!strcmp(arg->name, "cfg")) {
1690
0
    *consumed_next = GF_TRUE;
1691
0
    if (val && strchr(val, '=')) {
1692
0
      if (! gf_sys_set_cfg_option(val)) *e = GF_BAD_PARAM;
1693
0
    } else {
1694
0
      u32 sec_len = 0;
1695
0
      char *sep = val ? strchr(val, ':') : NULL;
1696
0
      u32 sec_count = gf_opts_get_section_count();
1697
0
      if (sep) {
1698
0
        sec_len = (u32) (sep - val - 1);
1699
0
        sep++;
1700
0
      } else if (val) {
1701
0
        sec_len = (u32) strlen(val);
1702
0
      }
1703
0
      for (i=0; i<sec_count; i++) {
1704
0
        u32 k, key_count;
1705
0
        Bool sec_hdr_done = GF_FALSE;
1706
0
        const char *sname = gf_opts_get_section_name(i);
1707
0
        key_count = sname ? gf_opts_get_key_count(sname) : 0;
1708
0
        if (!key_count) continue;
1709
1710
0
        if (sec_len) {
1711
0
          if (!strncmp(val, "*", sec_len) || !strncmp(val, "@", sec_len)) {
1712
0
          } else if (strncmp(val, sname, sec_len) || (sec_len != (u32) strlen(sname) ) ) {
1713
0
            continue;
1714
0
          }
1715
0
        }
1716
0
        for (k=0; k<key_count; k++) {
1717
0
          const char *kname = gf_opts_get_key_name(sname, k);
1718
0
          const char *kval = kname ? gf_opts_get_key(sname, kname) : NULL;
1719
0
          if (!kval) continue;
1720
0
          if (sep && strcmp(sep, kname)) continue;
1721
1722
0
          if (!sec_hdr_done) {
1723
0
            sec_hdr_done = GF_TRUE;
1724
0
            fprintf(stdout, "[%s]\n", sname);
1725
0
          }
1726
0
          fprintf(stdout, "%s=%s\n", kname, kval);
1727
0
        }
1728
0
        if (sec_hdr_done)
1729
0
          fprintf(stdout, "\n");
1730
0
      }
1731
0
    }
1732
0
    return GF_TRUE;
1733
0
  }
1734
0
  if (!strcmp(arg->name, "strict-error")) {
1735
0
    gf_log_set_strict_error(1);
1736
0
    return GF_TRUE;
1737
0
  }
1738
1739
0
  if (arg->type==GF_ARG_BOOL) {
1740
0
    if (!val) gf_opts_set_key("temp", arg->name, "yes");
1741
0
    else {
1742
0
      if (!strcmp(val, "yes") || !strcmp(val, "true") || !strcmp(val, "1")) {
1743
0
        *consumed_next = GF_TRUE;
1744
0
        gf_opts_set_key("temp", arg->name, "yes");
1745
0
      } else {
1746
0
        if (!strcmp(val, "no") || !strcmp(val, "false") || !strcmp(val, "0")) {
1747
0
          *consumed_next = GF_TRUE;
1748
0
          gf_opts_set_key("temp", arg->name, "no");
1749
0
        } else {
1750
0
          gf_opts_set_key("temp", arg->name, "yes");
1751
0
        }
1752
0
      }
1753
0
    }
1754
0
  } else {
1755
0
    *consumed_next = GF_TRUE;
1756
0
    if (!val && (arg->type==GF_ARG_BOOL))
1757
0
      gf_opts_set_key("temp", arg->name, "true");
1758
0
    else {
1759
0
      gf_opts_set_key("temp", arg->name, val);
1760
0
      if (!strcmp(arg->name, "mod-dirs")) {
1761
0
        gf_module_reload_dirs();
1762
0
      }
1763
0
    }
1764
0
  }
1765
0
  return GF_TRUE;
1766
0
}
1767
1768
GF_EXPORT
1769
u32 gf_sys_is_gpac_arg(const char *arg_name)
1770
0
{
1771
0
  char *argsep;
1772
0
  u32 arglen;
1773
0
  const GF_GPACArg *arg = NULL;
1774
0
  u32 i=0;
1775
0
  arg_name = arg_name+1;
1776
0
  if (arg_name[0]=='-')
1777
0
    return 1;
1778
0
  if (arg_name[0]=='+')
1779
0
    return 1;
1780
1781
0
  argsep = strchr(arg_name, '=');
1782
0
  if (argsep) arglen = (u32) (argsep - arg_name);
1783
0
  else arglen = (u32) strlen(arg_name);
1784
1785
0
  while (GPAC_Args[i].name) {
1786
0
    arg = &GPAC_Args[i];
1787
0
    i++;
1788
0
    if ((strlen(arg->name) == arglen) && !strncmp(arg->name, arg_name, arglen)) break;
1789
0
    if (arg->altname) {
1790
0
      char *alt = strstr(arg->altname, arg_name);
1791
0
      if (alt) {
1792
0
        char c = alt[strlen(arg_name)];
1793
0
        if (!c || (c==' ')) break;
1794
0
      }
1795
0
    }
1796
0
    arg = NULL;
1797
0
  }
1798
0
  if (!arg) return 0;
1799
0
  if (arg->type==GF_ARG_BOOL) return 1;
1800
0
  if (argsep) return 1;
1801
0
  return 2;
1802
0
}
1803
1804
1805
GF_EXPORT
1806
void gf_sys_print_arg(FILE *helpout, GF_SysPrintArgFlags flags, const GF_GPACArg *arg, const char *arg_subsystem)
1807
0
{
1808
0
  u32 gen_doc = 0;
1809
0
  if (flags & GF_PRINTARG_MD)
1810
0
    gen_doc = 1;
1811
0
  if (!helpout) helpout = stderr;
1812
1813
//#ifdef GPAC_ENABLE_COVERAGE
1814
0
#if 1
1815
0
  if ((arg->name[0]>='A') && (arg->name[0]<='Z')) {
1816
0
    if ((arg->name[1]<'A') || (arg->name[1]>'Z')) {
1817
0
      fprintf(stderr, "\nWARNING: arg %s bad name format, should use lowercase\n", arg->name);
1818
0
      exit(1);
1819
0
    }
1820
0
  }
1821
0
  if (arg->description) {
1822
0
    char *sep;
1823
1824
0
    if ((arg->description[0]>='A') && (arg->description[0]<='Z')) {
1825
0
      if ((arg->description[1]<'A') || (arg->description[1]>'Z')) {
1826
0
        fprintf(stderr, "\nWARNING: arg %s bad name format \"%s\", should use lowercase\n", arg->name, arg->description);
1827
0
        exit(1);
1828
0
      }
1829
0
    }
1830
0
    if (strchr(arg->description, '\t')) {
1831
0
      fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should not use tab\n", arg->name, arg->description);
1832
0
      exit(1);
1833
0
    }
1834
1835
0
    u8 achar = arg->description[strlen(arg->description)-1];
1836
0
    if (achar == '.') {
1837
0
      fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should not end with .\n", arg->name, arg->description);
1838
0
      exit(1);
1839
0
    }
1840
0
    sep = strstr(arg->description, ".\n");
1841
0
    if (sep) {
1842
0
      fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should not contain .\\n \n", arg->name, arg->description);
1843
0
      exit(1);
1844
0
    }
1845
0
    sep = strstr(arg->description, "- ");
1846
0
    if (sep && (sep[-1]!='\n') && !strcmp(sep, "- see")) {
1847
0
      fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should have \\n before first bullet\n", arg->name, arg->description);
1848
0
      exit(1);
1849
0
    }
1850
1851
0
    sep = strchr(arg->description, ' ');
1852
0
    if (sep && (sep>arg->description)) {
1853
0
      sep--;
1854
0
      if ((sep[0] == 's') && (sep[-1] != 's')) {
1855
0
        fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should use infintive\n", arg->name, arg->description);
1856
0
        exit(1);
1857
0
      }
1858
0
    }
1859
0
  }
1860
0
#endif
1861
1862
0
  if (arg->flags & GF_ARG_HINT_HIDE)
1863
0
    return;
1864
1865
0
  const char *syntax=strchr(arg->name, ' ');
1866
0
  char *arg_name=NULL;
1867
0
  if (syntax) {
1868
0
    arg_name = gf_strdup(arg->name);
1869
0
    char *sep = strchr(arg_name, ' ');
1870
0
    sep[0]=0;
1871
0
  }
1872
1873
0
  if (flags & GF_PRINTARG_MAN) {
1874
0
    fprintf(helpout, ".TP\n.B %s%s", (flags&GF_PRINTARG_NO_DASH) ? "" : "\\-", arg_name ? arg_name : arg->name);
1875
0
  }
1876
0
  else if (gen_doc==1) {
1877
0
    if (flags&GF_PRINTARG_NO_DASH) {
1878
0
      gf_sys_format_help(helpout, flags | GF_PRINTARG_HIGHLIGHT_FIRST, "%s", arg_name ? arg_name : arg->name);
1879
0
    } else {
1880
0
      gf_sys_format_help(helpout, flags, "<a id=\"%s\">", arg_name ? arg_name : arg->name);
1881
0
      gf_sys_format_help(helpout, flags | GF_PRINTARG_HIGHLIGHT_FIRST, "-%s", arg_name ? arg_name : arg->name);
1882
0
      gf_sys_format_help(helpout, flags, "</a>");
1883
0
    }
1884
0
  } else {
1885
0
    gf_sys_format_help(helpout, flags | GF_PRINTARG_HIGHLIGHT_FIRST, "%s%s%s",
1886
0
      (flags&GF_PRINTARG_ADD_DASH) ? "-" : "",
1887
0
      (flags&GF_PRINTARG_NO_DASH) ? "" : ((flags&GF_PRINTARG_COLON) ? ":" : "-"),
1888
0
      arg_name ? arg_name : arg->name
1889
0
    );
1890
0
  }
1891
0
  if (arg->altname) {
1892
0
    gf_sys_format_help(helpout, flags, ",");
1893
0
    gf_sys_format_help(helpout, flags | GF_PRINTARG_HIGHLIGHT_FIRST, "%s-%s", (flags&GF_PRINTARG_ADD_DASH) ? "-" : "", arg->altname);
1894
0
  }
1895
0
  if (syntax) {
1896
0
    gf_sys_format_help(helpout, flags, " %s", syntax);
1897
0
    gf_free(arg_name);
1898
0
  }
1899
1900
0
  if (arg->type==GF_ARG_CUSTOM) {
1901
0
    if (arg->val)
1902
0
      gf_sys_format_help(helpout, flags, " `%s`", arg->val);
1903
0
  } else if (arg->type==GF_ARG_INT && arg->values && strchr(arg->values, '|')) {
1904
0
    gf_sys_format_help(helpout, flags, " (Enum");
1905
0
    if (arg->val)
1906
0
      gf_sys_format_help(helpout, flags, ", default: **%s**", arg->val);
1907
0
    gf_sys_format_help(helpout, flags, ")");
1908
0
  } else if (arg->type != GF_ARG_BOOL) {
1909
0
    gf_sys_format_help(helpout, flags, " (");
1910
0
    switch (arg->type) {
1911
0
    case GF_ARG_BOOL: gf_sys_format_help(helpout, flags, "boolean"); break;
1912
0
    case GF_ARG_INT: gf_sys_format_help(helpout, flags, "int"); break;
1913
0
    case GF_ARG_DOUBLE: gf_sys_format_help(helpout, flags, "number"); break;
1914
0
    case GF_ARG_STRING: gf_sys_format_help(helpout, flags, "string"); break;
1915
0
    case GF_ARG_STRINGS: gf_sys_format_help(helpout, flags, "string list"); break;
1916
0
    case GF_ARG_4CC: gf_sys_format_help(helpout, flags, "4CC"); break;
1917
0
    case GF_ARG_4CCS: gf_sys_format_help(helpout, flags, "4CC list"); break;
1918
0
    default: break;
1919
0
    }
1920
0
    if (arg->val)
1921
0
      gf_sys_format_help(helpout, flags, ", default: **%s**", arg->val);
1922
0
    if (arg->values)
1923
0
      gf_sys_format_help(helpout, flags, ", values: __%s__", arg->values);
1924
0
    gf_sys_format_help(helpout, flags, ")");
1925
0
  }
1926
1927
0
  if (flags & GF_PRINTARG_MAN) {
1928
0
    gf_sys_format_help(helpout, flags, "\n%s\n", gf_sys_localized(arg_subsystem, arg->name, arg->description) );
1929
0
  } else {
1930
0
    if (arg->description) {
1931
0
      gf_sys_format_help(helpout, flags | GF_PRINTARG_OPT_DESC, ": %s", gf_sys_localized(arg_subsystem, arg->name, arg->description) );
1932
0
    }
1933
0
    gf_sys_format_help(helpout, flags, "\n");
1934
0
  }
1935
1936
0
  if ((gen_doc==1) && arg->description && strstr(arg->description, "- "))
1937
0
    gf_sys_format_help(helpout, flags, "\n");
1938
0
}
1939
1940
1941
GF_EXPORT
1942
void gf_sys_print_core_help(FILE *helpout, GF_SysPrintArgFlags flags, GF_SysArgMode mode, u32 subsystem_flags)
1943
0
{
1944
0
  u32 i=0;
1945
0
  const GF_GPACArg *args = gf_sys_get_options();
1946
1947
0
  while (args[i].name) {
1948
0
    const GF_GPACArg *arg = &args[i];
1949
0
    i++;
1950
0
    if (arg->flags & GF_ARG_HINT_HIDE) continue;
1951
1952
0
    if (subsystem_flags && !(arg->flags & subsystem_flags)) {
1953
0
      continue;
1954
0
    }
1955
0
    if (mode != GF_ARGMODE_ALL) {
1956
0
      if ((mode==GF_ARGMODE_EXPERT) && !(arg->flags & GF_ARG_HINT_EXPERT)) continue;
1957
0
      else if ((mode==GF_ARGMODE_ADVANCED) && !(arg->flags & GF_ARG_HINT_ADVANCED)) continue;
1958
0
      else if ((mode==GF_ARGMODE_BASE) && (arg->flags & (GF_ARG_HINT_ADVANCED|GF_ARG_HINT_EXPERT) )) continue;
1959
0
    }
1960
0
    gf_sys_print_arg(helpout, flags, arg, "core");
1961
0
  }
1962
0
}
1963
1964
1965
0
#define LINE_OFFSET_DESCR 30
1966
1967
static char *help_buf = NULL;
1968
static u32 help_buf_size=0;
1969
1970
void gf_sys_cleanup_help()
1971
0
{
1972
0
  if (help_buf) {
1973
0
    gf_free(help_buf);
1974
0
    help_buf = NULL;
1975
0
    help_buf_size = 0;
1976
0
  }
1977
0
}
1978
1979
1980
enum
1981
{
1982
  TOK_CODE,
1983
  TOK_BOLD,
1984
  TOK_ITALIC,
1985
  TOK_STRIKE,
1986
  TOK_OPTLINK,
1987
  TOK_LINKSTART,
1988
};
1989
struct _token {
1990
  char *tok;
1991
  GF_ConsoleCodes cmd_type;
1992
} Tokens[] =
1993
{
1994
 {"`", GF_CONSOLE_YELLOW|GF_CONSOLE_ITALIC},
1995
 {"**", GF_CONSOLE_BOLD},
1996
 {"__", GF_CONSOLE_ITALIC},
1997
 {"~~", GF_CONSOLE_STRIKE},
1998
 {"[-", GF_CONSOLE_YELLOW|GF_CONSOLE_ITALIC},
1999
 {"[", GF_CONSOLE_YELLOW|GF_CONSOLE_ITALIC},
2000
};
2001
static u32 nb_tokens = sizeof(Tokens) / sizeof(struct _token);
2002
2003
static u32 line_pos = 0;
2004
2005
//#define CHECK_BALANCED_SEPS
2006
2007
#ifdef CHECK_BALANCED_SEPS
2008
static void check_char_balanced(char *buf, char c)
2009
{
2010
  char *txt = buf;
2011
  while (txt) {
2012
    char *bquote_next;
2013
    char *bquote = strchr(txt, c);
2014
    if (!bquote) break;
2015
    if (c=='\'') {
2016
      if ((bquote[1]=='s') && (bquote[2]==' ')) {
2017
        txt = bquote + 1;
2018
        continue;
2019
      }
2020
    }
2021
    bquote_next = strchr(bquote+1, c);
2022
    if (!bquote_next) {
2023
      fprintf(stderr, "Missing closing %c after %s\n", c, bquote);
2024
      exit(1);
2025
    }
2026
    switch (bquote_next[1] ) {
2027
    case 0:
2028
    case '\n':
2029
    case ' ':
2030
    case ',':
2031
    case '.':
2032
    case ')':
2033
    case ']':
2034
    case ':':
2035
      break;
2036
    default:
2037
      if (c=='\'') {
2038
        if ((bquote_next[1]>='A') && (bquote_next[1]<='Z'))
2039
          break;
2040
      }
2041
      fprintf(stderr, "Missing space after closing %c %s\n", c, bquote_next);
2042
      exit(1);
2043
    }
2044
    txt = bquote_next + 1;
2045
  }
2046
}
2047
#endif
2048
2049
GF_EXPORT
2050
void gf_sys_format_help(FILE *helpout, GF_SysPrintArgFlags flags, const char *fmt, ...)
2051
0
{
2052
0
  char *line;
2053
0
  u32 len;
2054
0
  va_list vlist;
2055
0
  Bool escape_xml = GF_FALSE;
2056
0
  Bool escape_pipe = GF_FALSE;
2057
0
  Bool prev_was_example = GF_FALSE;
2058
0
  Bool prev_has_line_after = GF_FALSE;
2059
0
  u32 gen_doc = 0;
2060
0
  u32 is_app_opts = 0;
2061
0
  if (flags & GF_PRINTARG_MD) {
2062
0
    gen_doc = 1;
2063
0
    if (flags & GF_PRINTARG_ESCAPE_XML)
2064
0
      escape_xml = GF_TRUE;
2065
0
    if (flags & GF_PRINTARG_ESCAPE_PIPE)
2066
0
      escape_pipe = GF_TRUE;
2067
0
  }
2068
0
  if (flags & GF_PRINTARG_MAN)
2069
0
    gen_doc = 2;
2070
0
  if (flags & GF_PRINTARG_IS_APP)
2071
0
    is_app_opts = 1;
2072
0
  if (!helpout) helpout = stderr;
2073
2074
0
  va_start(vlist, fmt);
2075
0
  len=vsnprintf(NULL, 0, fmt, vlist);
2076
0
  va_end(vlist);
2077
0
  if (help_buf_size < len+2) {
2078
0
    help_buf_size = len+2;
2079
0
    help_buf = gf_realloc(help_buf, help_buf_size);
2080
0
  }
2081
0
  va_start(vlist, fmt);
2082
0
  vsprintf(help_buf, fmt, vlist);
2083
0
  va_end(vlist);
2084
2085
#ifdef CHECK_BALANCED_SEPS
2086
  if (gen_doc) {
2087
    check_char_balanced(help_buf, '`');
2088
    check_char_balanced(help_buf, '\'');
2089
  }
2090
#endif
2091
2092
/*#ifdef GPAC_CONFIG_ANDROID
2093
  //on android use logs for help print
2094
  if (!gen_doc) {
2095
    GF_LOG(GF_LOG_INFO, GF_LOG_APP, ("%s", help_buf));
2096
    return;
2097
  }
2098
#endif
2099
*/
2100
2101
0
  line = help_buf;
2102
0
  while (line[0]) {
2103
0
    u32 att_len = 0;
2104
0
    char *tok_sep = NULL;
2105
0
    GF_ConsoleCodes console_code = GF_CONSOLE_RESET;
2106
0
    Bool line_before = GF_FALSE;
2107
0
    Bool line_after = GF_FALSE;
2108
0
    const char *footer_string = NULL;
2109
0
    const char *header_string = NULL;
2110
0
    char *next_line = strchr(line, '\n');
2111
0
    Bool has_token=GF_FALSE;
2112
2113
0
    if (next_line) next_line[0]=0;
2114
2115
0
    if (prev_has_line_after && !strlen(line)) {
2116
0
      if (!next_line) break;
2117
0
      line = next_line+1;
2118
0
      line_pos=0;
2119
0
      continue;
2120
0
    }
2121
0
    if (!line[0]) flags &= ~GF_PRINTARG_HIGHLIGHT_FIRST;
2122
2123
0
    if ((line[0]=='#') && (line[1]==' ')) {
2124
0
      if (!gen_doc)
2125
0
        line+=2;
2126
0
      else if (gen_doc==2) {
2127
0
        header_string = ".SH ";
2128
0
        footer_string = "\n.LP";
2129
0
        line+=2;
2130
0
      }
2131
2132
0
      console_code = GF_CONSOLE_GREEN;
2133
0
      line_after = line_before = GF_TRUE;
2134
0
    } else if ((line[0]=='#') && (line[1]=='#') && (line[2]==' ')) {
2135
0
      if (!gen_doc)
2136
0
        line+=3;
2137
0
      else if (gen_doc==2) {
2138
0
        line+=3;
2139
0
        header_string = ".SS ";
2140
0
      }
2141
2142
0
      console_code = GF_CONSOLE_MAGENTA;
2143
0
      line_before = GF_TRUE;
2144
0
    } else if ((line[0]=='#') && (line[1]=='#') && (line[2]=='#') && (line[3]==' ')) {
2145
0
      if (!gen_doc)
2146
0
        line+=4;
2147
0
      else if (gen_doc==2) {
2148
0
        line+=4;
2149
0
        header_string = ".P\n.B\n";
2150
0
      }
2151
2152
0
      console_code = GF_CONSOLE_CYAN;
2153
0
      line_after = GF_TRUE;
2154
0
    } else if ((line[0]=='E') && (line[1]=='X') && (line[2]==' ')) {
2155
0
      line+=3;
2156
0
      console_code = GF_CONSOLE_YELLOW;
2157
2158
0
      if (gen_doc==1) {
2159
0
        header_string = "Example\n```\n";
2160
0
        footer_string = "\n```";
2161
0
      } else if (gen_doc==2) {
2162
0
        header_string = "Example\n.br\n";
2163
0
        footer_string = "\n.br\n";
2164
0
      } else {
2165
0
        header_string = "Example:\n";
2166
0
      }
2167
2168
0
      if (prev_was_example) {
2169
0
        header_string = NULL;
2170
0
      }
2171
2172
0
      if (next_line && (next_line[1]=='E') && (next_line[2]=='X') && (next_line[3]==' ')) {
2173
0
        prev_was_example = GF_TRUE;
2174
0
        footer_string = NULL;
2175
0
      } else {
2176
0
        prev_was_example = GF_FALSE;
2177
0
      }
2178
0
    } else if (!strncmp(line, "Note: ", 6)) {
2179
0
      console_code = GF_CONSOLE_CYAN | GF_CONSOLE_ITALIC;
2180
0
    } else if (!strncmp(line, "Warning: ", 9)) {
2181
0
      line_after = line_before = GF_TRUE;
2182
0
      console_code = GF_CONSOLE_RED | GF_CONSOLE_BOLD;
2183
0
    } else if ( (
2184
0
       ((line[0]=='-') && (line[1]==' '))
2185
0
       || ((line[0]==' ') && (line[1]=='-') && (line[2]==' '))
2186
0
       || ((line[0]==' ') && (line[1]==' ') && (line[2]=='-') && (line[3]==' '))
2187
0
      )
2188
2189
      //look for ": "
2190
0
      && ((tok_sep=strstr(line, ": ")) != NULL )
2191
0
    ) {
2192
0
      if (!gen_doc)
2193
0
        fprintf(helpout, "\t");
2194
0
      while (line[0] != '-') {
2195
0
        fprintf(helpout, " ");
2196
0
        line++;
2197
0
        line_pos++;
2198
2199
0
      }
2200
0
      fprintf(helpout, "* ");
2201
0
      line_pos+=2;
2202
0
      if (!gen_doc)
2203
0
        gf_sys_set_console_code(helpout, GF_CONSOLE_YELLOW);
2204
0
      tok_sep[0] = 0;
2205
0
      fprintf(helpout, "%s", line+2);
2206
0
      line_pos += (u32) strlen(line+2);
2207
0
      tok_sep[0] = ':';
2208
0
      line = tok_sep;
2209
0
      if (!gen_doc)
2210
0
        gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2211
0
    } else if (flags & (GF_PRINTARG_HIGHLIGHT_FIRST | GF_PRINTARG_OPT_DESC)) {
2212
0
      char *sep = strchr(line, ' ');
2213
2214
0
      if (sep) sep[0] = 0;
2215
2216
0
      if (!gen_doc && !(flags & GF_PRINTARG_OPT_DESC) )
2217
0
        gf_sys_set_console_code(helpout, GF_CONSOLE_GREEN);
2218
2219
0
      if ((gen_doc==1) && !(flags & GF_PRINTARG_OPT_DESC) ) {
2220
0
        fprintf(helpout, "__%s__", line);
2221
0
        line_pos += 4+ (u32) strlen(line);
2222
0
      } else {
2223
0
        fprintf(helpout, "%s", line);
2224
0
        line_pos += (u32) strlen(line);
2225
0
      }
2226
2227
0
      if (!gen_doc && !(flags & GF_PRINTARG_OPT_DESC) )
2228
0
        gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2229
2230
0
      if (flags & GF_PRINTARG_OPT_DESC) {
2231
0
        flags = 0;
2232
0
        att_len = line_pos;
2233
0
      }
2234
2235
2236
0
      if (sep) {
2237
0
        sep[0] = ' ';
2238
0
        line = sep;
2239
0
      } else {
2240
0
        line = NULL;
2241
0
      }
2242
0
    }
2243
0
    if (!line) break;
2244
0
    if (gen_doc==2) {
2245
0
      line_before = line_after = GF_FALSE;
2246
0
    }
2247
2248
0
    if (prev_has_line_after) line_before = GF_FALSE;
2249
0
    prev_has_line_after = GF_FALSE;
2250
0
    if (!strlen(line))
2251
0
      prev_has_line_after = GF_TRUE;
2252
2253
0
    if (line_before) {
2254
0
      fprintf(helpout, "\n");
2255
0
      line_pos=0;
2256
0
    }
2257
2258
0
    if (console_code != GF_CONSOLE_RESET) {
2259
0
      if (gen_doc==1) {
2260
0
        if (console_code & GF_CONSOLE_BOLD) {
2261
0
          fprintf(helpout, "__");
2262
0
          line_pos+=2;
2263
0
        }
2264
0
        else if (console_code & GF_CONSOLE_ITALIC) {
2265
0
          fprintf(helpout, "_");
2266
0
          line_pos++;
2267
0
        }
2268
0
      } else if (!gen_doc) {
2269
0
        gf_sys_set_console_code(helpout, console_code);
2270
0
      }
2271
0
    }
2272
2273
0
    if (att_len) {
2274
0
      while (att_len < LINE_OFFSET_DESCR) {
2275
0
        fprintf(helpout, " ");
2276
0
        att_len++;
2277
0
        line_pos++;
2278
0
      }
2279
0
    }
2280
2281
2282
0
    if (header_string) {
2283
0
      fprintf(helpout, "%s", header_string);
2284
0
      line_pos += (u32) strlen(header_string);
2285
0
    }
2286
2287
0
    while (line) {
2288
0
      char *skip_url = NULL;
2289
0
      char *link_start = NULL;
2290
0
      u32 tid=0, i;
2291
0
      char *next_token = NULL;
2292
0
      for (i=0; i<nb_tokens; i++) {
2293
0
        char *tok = strstr(line, Tokens[i].tok);
2294
0
        if (!tok) continue;
2295
0
        if (next_token && ((next_token-line) < (tok-line)) ) continue;
2296
        //check we have an end of token, otherwise consider this regular text
2297
0
        if ((i == TOK_LINKSTART) || (i == TOK_OPTLINK)) {
2298
0
          char *end_tok = strstr(tok, "](");
2299
0
          if (!end_tok) continue;
2300
0
        }
2301
2302
0
        if (i == TOK_LINKSTART) {
2303
0
          if (tid == TOK_OPTLINK) continue;
2304
0
          if (gen_doc!=1) {
2305
0
            char *link_end;
2306
0
            skip_url = strstr(tok, "](");
2307
0
            link_end = skip_url;
2308
0
            if (skip_url) skip_url = strstr(skip_url, ")");
2309
0
            if (skip_url) skip_url ++;
2310
2311
0
            if (!skip_url) continue;
2312
0
            link_start = tok+1;
2313
0
            link_end[0] = 0;
2314
0
          } else {
2315
0
            continue;
2316
0
          }
2317
0
        }
2318
0
        next_token=tok;
2319
0
        tid=i;
2320
0
      }
2321
0
      if (next_token) {
2322
0
        next_token[0]=0;
2323
0
      }
2324
0
      if ((gen_doc==1) && has_token) {
2325
0
        if (tid==TOK_CODE) {
2326
0
          fprintf(helpout, "`%s`", line);
2327
0
          line_pos+=2;
2328
0
        } else if (tid==TOK_ITALIC) {
2329
0
          fprintf(helpout, "_%s_", line);
2330
0
          line_pos+=2;
2331
0
        } else if (tid==TOK_BOLD) {
2332
0
          fprintf(helpout, "__%s__", line);
2333
0
          line_pos+=4;
2334
0
        } else {
2335
0
          fprintf(helpout, "%s", line);
2336
0
        }
2337
0
      } else if (escape_xml) {
2338
0
        char *xml_line = line;
2339
0
        while (xml_line) {
2340
0
          char *xml_start = strchr(xml_line, '<');
2341
0
          char *xml_end = strchr(xml_line, '>');
2342
2343
0
          if (xml_end && (xml_start > xml_end)) xml_start = xml_end;
2344
0
          else if (!xml_start && xml_end) xml_start = xml_end;
2345
0
          else if (xml_start && xml_end) xml_end = NULL;
2346
2347
0
          if (xml_start) {
2348
0
            u8 c = xml_start[0];
2349
0
            xml_start[0] = 0;
2350
0
            fprintf(helpout, "%s", xml_line);
2351
0
            fprintf(helpout, xml_end ? "&gt;" : "&lt;");
2352
0
            xml_start[0] = c;
2353
0
            xml_line = xml_start+1;
2354
0
          } else {
2355
0
            fprintf(helpout, "%s", xml_line);
2356
0
            break;
2357
0
          }
2358
0
        }
2359
0
      } else if (escape_pipe) {
2360
0
        char *src_line = line;
2361
0
        while (src_line) {
2362
0
          char *pipe_start = strchr(src_line, '|');
2363
0
          if (pipe_start && (pipe_start[1]==' '))
2364
0
            pipe_start = NULL;
2365
2366
0
          if (pipe_start) {
2367
0
            pipe_start[0] = 0;
2368
0
            fprintf(helpout, "%s ", src_line);
2369
0
            pipe_start[0] = '|';
2370
0
            src_line = pipe_start+1;
2371
0
          } else {
2372
0
            fprintf(helpout, "%s", src_line);
2373
0
            break;
2374
0
          }
2375
0
        }
2376
0
      } else {
2377
0
        fprintf(helpout, "%s", line);
2378
0
      }
2379
0
      line_pos+=(u32) strlen(line);
2380
2381
0
      if (!next_token) break;
2382
0
      has_token = !has_token;
2383
2384
0
      if (!gen_doc) {
2385
0
        if (has_token) {
2386
0
          u32 cmd;
2387
0
          if (Tokens[tid].cmd_type & 0xFFFF) {
2388
0
            cmd = Tokens[tid].cmd_type;
2389
0
          } else {
2390
0
            cmd = Tokens[tid].cmd_type | console_code;
2391
0
          }
2392
2393
0
          if (console_code&GF_CONSOLE_ITALIC) {
2394
0
            if (Tokens[tid].cmd_type & GF_CONSOLE_ITALIC) {
2395
0
              cmd &= ~GF_CONSOLE_ITALIC;
2396
0
              cmd |= GF_CONSOLE_BOLD;
2397
0
            }
2398
0
          }
2399
0
          else if (console_code&GF_CONSOLE_BOLD) {
2400
0
            if (Tokens[tid].cmd_type & GF_CONSOLE_BOLD) {
2401
0
              cmd &= ~GF_CONSOLE_BOLD;
2402
0
              cmd |= GF_CONSOLE_ITALIC;
2403
0
            }
2404
0
          }
2405
0
          gf_sys_set_console_code(helpout, cmd);
2406
0
        } else {
2407
0
          gf_sys_set_console_code(helpout, console_code);
2408
0
        }
2409
0
      }
2410
0
      line = next_token + (u32) strlen(Tokens[tid].tok);
2411
2412
0
      if (skip_url) {
2413
0
        if (link_start)
2414
0
          fprintf(helpout, "%s", link_start);
2415
0
        if (!gen_doc)
2416
0
          gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2417
0
        has_token = GF_FALSE;
2418
0
        line = skip_url;
2419
2420
0
      }
2421
2422
0
      if (has_token && tid==TOK_OPTLINK) {
2423
0
        char *link = strchr(line, '(');
2424
0
        gf_assert(link);
2425
0
        link++;
2426
0
        char *end_link = strchr(line, ')');
2427
0
        if (end_link) end_link[0] = 0;
2428
0
        char *end_tok = strchr(line, ']');
2429
0
        if (end_tok) end_tok[0] = 0;
2430
2431
0
        if (gen_doc==1) {
2432
0
          if (!strncmp(link, "GPAC", 4)) {
2433
0
            fprintf(helpout, "[-%s](gpac_general/#%s)", line, line);
2434
0
            line_pos+=7 + 2*(u32)strlen(line) + (u32)strlen("gpac_general");
2435
0
          } else if (!strncmp(link, "LOG", 3)) {
2436
0
            fprintf(helpout, "[-%s](core_logs/#%s)", line, line);
2437
0
            line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen("core_logs");
2438
0
          } else if (!strncmp(link, "CORE", 4)) {
2439
0
            fprintf(helpout, "[-%s](core_options/#%s)", line, line);
2440
0
            line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen("core_options");
2441
0
          } else if (!strncmp(link, "CFG", 3)) {
2442
0
            fprintf(helpout, "[-%s](core_config/#%s)", line, line);
2443
0
            line_pos+=7 + 2*(u32)strlen(line) + (u32)strlen("core_config");
2444
0
          } else if (!strncmp(link, "MP4B_GEN", 8)) {
2445
0
            fprintf(helpout, "[-%s](mp4box-gen-opts/#%s)", line, line);
2446
0
            line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen("mp4box-gen-opts");
2447
0
          } else if (strlen(link)) {
2448
0
            fprintf(helpout, "[-%s](%s/#%s)", line, link, line);
2449
0
            line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen(link);
2450
0
          } else if (is_app_opts || !strcmp(line, "i") || !strcmp(line, "o") || !strcmp(line, "h")) {
2451
0
            fprintf(helpout, "[-%s](#%s)", line, line);
2452
0
            line_pos+=6 + 2* (u32)strlen(line);
2453
0
          } else {
2454
            //this is a filter opt, don't print '-'
2455
0
            fprintf(helpout, "[%s](#%s)", line, line);
2456
0
            line_pos+=5 + 2* (u32)strlen(line);
2457
0
          }
2458
0
        } else {
2459
0
          if (gen_doc==2)
2460
0
            fprintf(helpout, ".I ");
2461
2462
0
          if (!strncmp(link, "GPAC", 4)
2463
0
            || !strncmp(link, "LOG", 3)
2464
0
            || !strncmp(link, "CORE", 4)
2465
0
            || strlen(link)
2466
0
            || !strcmp(line, "i") || !strcmp(line, "o") || !strcmp(line, "h")
2467
0
          ) {
2468
0
            fprintf(helpout, "-%s", line);
2469
0
            line_pos+=1+ (u32)strlen(line);
2470
0
          } else {
2471
0
            fprintf(helpout, "%s", line);
2472
0
            line_pos+= (u32)strlen(line);
2473
0
          }
2474
0
          if (!gen_doc)
2475
0
            gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2476
0
        }
2477
0
        if (!end_link) break;
2478
0
        line = end_link+1;
2479
0
        has_token = GF_FALSE;
2480
0
      }
2481
0
    }
2482
2483
0
    if (has_token && !gen_doc)
2484
0
      gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2485
2486
0
    if (footer_string) {
2487
0
      fprintf(helpout, "%s", footer_string);
2488
0
      line_pos += (u32) strlen(footer_string);
2489
0
    }
2490
0
    if (console_code != GF_CONSOLE_RESET) {
2491
0
      if (gen_doc==1) {
2492
0
        if (console_code & GF_CONSOLE_BOLD) {
2493
0
          fprintf(helpout, "__");
2494
0
          line_pos+=2;
2495
0
        } else if (console_code & GF_CONSOLE_ITALIC) {
2496
0
          fprintf(helpout, "_");
2497
0
          line_pos++;
2498
0
        }
2499
0
      } else if (!gen_doc) {
2500
0
        gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
2501
0
      }
2502
0
    }
2503
2504
0
    if (line_after) {
2505
0
      if (gen_doc==1) fprintf(helpout, "  ");
2506
0
      fprintf(helpout, (flags & GF_PRINTARG_NL_TO_BR) ? "<br/>" : "\n");
2507
0
      line_pos=0;
2508
0
      prev_has_line_after = GF_TRUE;
2509
0
    }
2510
2511
0
    if (!next_line) break;
2512
0
    next_line[0]=0;
2513
0
    if (gen_doc==1) fprintf(helpout, "  ");
2514
0
    line = next_line+1;
2515
0
    if (gen_doc==2) {
2516
0
      if (line[0] != '.')
2517
0
        fprintf(helpout, "\n.br\n");
2518
0
      else
2519
0
        fprintf(helpout, "\n");
2520
0
    } else
2521
0
      fprintf(helpout, (line[0] && (flags & GF_PRINTARG_NL_TO_BR)) ? "<br/>" : "\n");
2522
0
    line_pos=0;
2523
0
  }
2524
0
}
2525
2526
GF_EXPORT
2527
Bool gf_strnistr(const char *text, const char *subtext, u32 subtext_len)
2528
0
{
2529
0
  if (!*text || !subtext || !subtext_len)
2530
0
    return GF_FALSE;
2531
2532
0
  while (*text) {
2533
0
    if (tolower(*text) == *subtext) {
2534
0
      if (!strnicmp(text, subtext, subtext_len))
2535
0
        return GF_TRUE;
2536
2537
0
    }
2538
0
    text++;
2539
0
  }
2540
0
  return GF_FALSE;
2541
0
}
2542
2543
GF_EXPORT
2544
Bool gf_sys_word_match(const char *orig, const char *dst)
2545
0
{
2546
0
  s32 dist = 0;
2547
0
  u32 match = 0;
2548
0
  u32 i;
2549
0
  u32 olen = (u32) strlen(orig);
2550
0
  u32 dlen = (u32) strlen(dst);
2551
0
  u32 *run;
2552
2553
0
  if ((olen>=3) && (olen<dlen) && !strncmp(orig, dst, olen))
2554
0
    return GF_TRUE;
2555
0
  if ((dlen>=3) && (dlen<olen) && !strncmp(orig, dst, dlen))
2556
0
    return GF_TRUE;
2557
2558
0
  if (olen*2 < dlen) {
2559
0
    char *s1 = strchr(orig, ':');
2560
0
    char *s2 = strchr(dst, ':');
2561
0
    if (s1 && !s2) return GF_FALSE;
2562
0
    if (!s1 && s2) return GF_FALSE;
2563
2564
0
    if (gf_strnistr(dst, orig, MIN(olen, dlen)))
2565
0
      return GF_TRUE;
2566
0
    return GF_FALSE;
2567
0
  }
2568
2569
0
  if ((dlen>=3) && gf_strnistr(orig, dst, dlen))
2570
0
    return GF_TRUE;
2571
0
  if ((olen>=3) && gf_strnistr(dst, orig, olen))
2572
0
    return GF_TRUE;
2573
2574
0
  run = gf_malloc(sizeof(u32) * olen);
2575
0
  memset(run, 0, sizeof(u32) * olen);
2576
2577
0
  for (i=0; i<dlen; i++) {
2578
0
    u32 dist_char;
2579
0
    u32 offset=0;
2580
0
    char *pos;
2581
2582
0
retry_char:
2583
0
    pos = strchr(orig+offset, dst[i]);
2584
0
    if (!pos) continue;
2585
0
    dist_char = (u32) (pos - orig);
2586
0
    if (!run[dist_char]) {
2587
0
      run[dist_char] = i+1;
2588
0
      match++;
2589
0
    } else if (run[dist_char] > i) {
2590
0
      run[dist_char] = i+1;
2591
0
      match++;
2592
0
    } else {
2593
      //this can be a repeated character
2594
0
      offset++;
2595
0
      goto retry_char;
2596
2597
0
    }
2598
0
  }
2599
0
  if (match*2<olen) {
2600
0
    gf_free(run);
2601
0
    return GF_FALSE;
2602
0
  }
2603
/*
2604
  //if 4/5 of characters are matched, suggest it
2605
  if (match * 5 >= 4 * dlen ) {
2606
    gf_free(run);
2607
    return GF_TRUE;
2608
  }
2609
  if ((olen<=4) && (match>=3) && (dlen*2<olen*3) ) {
2610
    gf_free(run);
2611
    return GF_TRUE;
2612
  }
2613
*/
2614
0
  for (i=0; i<olen; i++) {
2615
0
    if (!i) {
2616
0
      if (run[0]==1)
2617
0
        dist++;
2618
0
    } else if (run[i-1] + 1 == run[i]) {
2619
0
      dist++;
2620
0
    }
2621
0
  }
2622
0
  gf_free(run);
2623
  //if half the characters are in order, consider a match
2624
  //if arg is small only check dst
2625
0
  if ((olen<=4) && (dist >= 2))
2626
0
    return GF_TRUE;
2627
0
  if ((dist*2 >= (s32) olen) && (dist*2 >= (s32) dlen))
2628
0
    return GF_TRUE;
2629
0
  return GF_FALSE;
2630
0
}