1 /*
2 * GPAC - Multimedia Framework C SDK
3 *
4 * Authors: Jean Le Feuvre
5 * Copyright (c) Telecom ParisTech 2000-2020
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 #ifndef GPAC_DISABLE_CORE_TOOLS
27
28 #include <gpac/config_file.h>
29
30
31 #if defined(WIN32) || defined(_WIN32_WCE)
32 #include <windows.h> /*for GetModuleFileName*/
33
34 #ifndef _WIN32_WCE
35 #include <direct.h> /*for _mkdir*/
36 #include <gpac/utf.h>
37 #include <shlobj.h> /*for getting user-dir*/
38
39 #ifndef SHGFP_TYPE_CURRENT
40 #define SHGFP_TYPE_CURRENT 0 /*needed for MinGW*/
41 #endif
42
43 #endif
44
45 #define CFG_FILE_NAME "GPAC.cfg"
46 #define TEST_MODULE "gm_"
47
48 #elif (defined(__DARWIN__) || defined(__APPLE__) )
49 #include <mach-o/dyld.h> /*for _NSGetExecutablePath */
50
51 #ifdef GPAC_CONFIG_IOS
52 #define TEST_MODULE "osmo4ios"
53 #else
54 #define TEST_MODULE "gm_"
55 #endif
56 #define CFG_FILE_NAME "GPAC.cfg"
57
58 #else
59 #ifdef GPAC_CONFIG_LINUX
60 #include <unistd.h>
61 #endif
62 #ifdef GPAC_CONFIG_ANDROID
63 #define DEFAULT_ANDROID_PATH_APP "/data/data/com.gpac.Osmo4"
64 #define DEFAULT_ANDROID_PATH_CFG "/sdcard/osmo"
65 #endif
66 #define CFG_FILE_NAME "GPAC.cfg"
67
68 #if defined(GPAC_CONFIG_WIN32)
69 #define TEST_MODULE "gm_"
70 #else
71 #define TEST_MODULE "gm_"
72 #endif
73
74 #endif
75
76 #if !defined(GPAC_STATIC_MODULES) && !defined(GPAC_MP4BOX_MINI)
77
mod_enum(void * cbck,char * item_name,char * item_path,GF_FileEnumInfo * file_info)78 static Bool mod_enum(void *cbck, char *item_name, char *item_path, GF_FileEnumInfo *file_info)
79 {
80 if (!strncmp(item_name, "gm_", 3) || !strncmp(item_name, "gf_", 3)) {
81 *(Bool*)cbck = GF_TRUE;
82 return GF_TRUE;
83 }
84 return GF_FALSE;
85 }
86 #endif
87
check_file_exists(char * name,char * path,char * outPath)88 static Bool check_file_exists(char *name, char *path, char *outPath)
89 {
90 char szPath[GF_MAX_PATH];
91 FILE *f;
92
93 if (! gf_dir_exists(path)) return 0;
94
95 if (!strcmp(name, TEST_MODULE)) {
96 Bool res = GF_FALSE;
97 #if defined(GPAC_STATIC_MODULES) || defined(GPAC_MP4BOX_MINI)
98 if (gf_dir_exists(path)) res = GF_TRUE;
99 #else
100 gf_enum_directory(path, GF_FALSE, mod_enum, &res, NULL);
101 #endif
102 if (!res) return GF_FALSE;
103 if (outPath != path) strcpy(outPath, path);
104 return 1;
105 }
106
107 sprintf(szPath, "%s%c%s", path, GF_PATH_SEPARATOR, name);
108 //do not use gf_fopen here, we don't want to throw en error if failure
109 f = fopen(szPath, "rb");
110 if (!f) return GF_FALSE;
111 fclose(f);
112 if (outPath != path) strcpy(outPath, path);
113 return GF_TRUE;
114 }
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 };
124
125 #if defined(WIN32) || defined(_WIN32_WCE)
get_default_install_path(char * file_path,u32 path_type)126 static Bool get_default_install_path(char *file_path, u32 path_type)
127 {
128 FILE *f;
129 char szPath[GF_MAX_PATH];
130
131 #ifdef _WIN32_WCE
132 TCHAR w_szPath[GF_MAX_PATH];
133 GetModuleFileName(NULL, w_szPath, GF_MAX_PATH);
134 CE_WideToChar((u16 *) w_szPath, file_path);
135 #else
136 wchar_t wtmp_file_path[GF_MAX_PATH];
137 char* tmp_file_path;
138
139 GetModuleFileNameA(NULL, file_path, GF_MAX_PATH);
140 #endif
141
142 /*remove exe name*/
143 if (strstr(file_path, ".exe")) {
144 char *sep = strrchr(file_path, '\\');
145 if (sep) sep[0] = 0;
146 }
147
148 strcpy(szPath, file_path);
149 strlwr(szPath);
150
151 /*if this is run from a browser, we do not get our app path - fortunately on Windows, we always use 'GPAC' in the
152 installation path*/
153 if (!strstr(file_path, "gpac") && !strstr(file_path, "GPAC") ) {
154 HKEY hKey = NULL;
155 DWORD dwSize = GF_MAX_PATH;
156
157 /*locate the key in current user, then in local machine*/
158 #ifdef _WIN32_WCE
159 DWORD dwType = REG_SZ;
160 u16 w_path[1024];
161 RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\\GPAC"), 0, KEY_READ, &hKey);
162 #ifdef _DEBUG
163 if (RegQueryValueEx(hKey, TEXT("DebugDir"), 0, &dwType, (LPBYTE) w_path, &dwSize) != ERROR_SUCCESS)
164 #endif
165 RegQueryValueEx(hKey, TEXT("InstallDir"), 0, &dwType, (LPBYTE) w_path, &dwSize);
166 CE_WideToChar(w_path, (char *)file_path);
167 RegCloseKey(hKey);
168 #else
169 if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\GPAC", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
170 RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\GPAC", 0, KEY_READ, &hKey);
171
172 dwSize = GF_MAX_PATH;
173
174 #ifdef _DEBUG
175 if (RegQueryValueEx(hKey, "DebugDir", NULL, NULL,(unsigned char*) file_path, &dwSize) != ERROR_SUCCESS)
176 #endif
177 RegQueryValueEx(hKey, "InstallDir", NULL, NULL,(unsigned char*) file_path, &dwSize);
178
179 RegCloseKey(hKey);
180 #endif
181 }
182
183
184 if (path_type==GF_PATH_APP) return GF_TRUE;
185
186 if (path_type==GF_PATH_SHARE) {
187 char *sep;
188 strcat(file_path, "\\share");
189 if (check_file_exists("gui\\gui.bt", file_path, file_path)) return GF_TRUE;
190 sep = strstr(file_path, "\\bin\\");
191 if (sep) {
192 sep[0] = 0;
193 strcat(file_path, "\\share");
194 if (check_file_exists("gui\\gui.bt", file_path, file_path)) return GF_TRUE;
195 }
196 return GF_FALSE;
197 }
198 /*modules are stored in the GPAC directory (should be changed to GPAC/modules)*/
199 if (path_type==GF_PATH_MODULES) return GF_TRUE;
200
201 /*we are looking for the config file path - make sure it is writable*/
202 assert(path_type == GF_PATH_CFG);
203
204 strcpy(szPath, file_path);
205 strcat(szPath, "\\gpaccfgtest.txt");
206 //do not use gf_fopen here, we don't want to through any error if failure
207 f = fopen(szPath, "wb");
208 if (f != NULL) {
209 fclose(f);
210 gf_file_delete(szPath);
211 return GF_TRUE;
212 }
213 #ifdef _WIN32_WCE
214 return 0;
215 #else
216 /*no write access, get user home directory*/
217 SHGetSpecialFolderPathW(NULL, wtmp_file_path, CSIDL_APPDATA, 1);
218 tmp_file_path = gf_wcs_to_utf8(wtmp_file_path);
219 strncpy(file_path, tmp_file_path, GF_MAX_PATH);
220 file_path[GF_MAX_PATH-1] = 0;
221 gf_free(tmp_file_path);
222
223 if (file_path[strlen(file_path)-1] != '\\') strcat(file_path, "\\");
224 strcat(file_path, "GPAC");
225 /*create GPAC dir*/
226 gf_mkdir(file_path);
227 strcpy(szPath, file_path);
228 strcat(szPath, "\\gpaccfgtest.txt");
229 f = gf_fopen(szPath, "wb");
230 /*COMPLETE FAILURE*/
231 if (!f) return GF_FALSE;
232
233 gf_fclose(f);
234 gf_file_delete(szPath);
235 return GF_TRUE;
236 #endif
237 }
238
239 /*FIXME - the paths defined here MUST be coherent with the paths defined in applications/osmo4_android/src/com/gpac/Osmo4/GpacConfig.java'*/
240 #elif defined(GPAC_CONFIG_ANDROID)
241
get_default_install_path(char * file_path,u32 path_type)242 static Bool get_default_install_path(char *file_path, u32 path_type)
243 {
244 if (!file_path) return 0;
245
246 if (path_type==GF_PATH_APP) {
247 strcpy(file_path, DEFAULT_ANDROID_PATH_APP);
248 return 1;
249 } else if (path_type==GF_PATH_CFG) {
250 strcpy(file_path, DEFAULT_ANDROID_PATH_CFG);
251 return 1;
252 } else if (path_type==GF_PATH_SHARE) {
253 if (!get_default_install_path(file_path, GF_PATH_APP))
254 return 0;
255 strcat(file_path, "/share");
256 return 1;
257 } else if (path_type==GF_PATH_MODULES) {
258 if (!get_default_install_path(file_path, GF_PATH_APP))
259 return 0;
260 strcat(file_path, "/lib");
261 return 1;
262 }
263 return 0;
264 }
265
266
267 #elif defined(__SYMBIAN__)
268
269 #if defined(__SERIES60_3X__)
270 #define SYMBIAN_GPAC_CFG_DIR "\\private\\F01F9075"
271 #define SYMBIAN_GPAC_GUI_DIR "\\private\\F01F9075\\gui"
272 #define SYMBIAN_GPAC_MODULES_DIR "\\sys\\bin"
273 #else
274 #define SYMBIAN_GPAC_CFG_DIR "\\system\\apps\\Osmo4"
275 #define SYMBIAN_GPAC_GUI_DIR "\\system\\apps\\Osmo4\\gui"
276 #define SYMBIAN_GPAC_MODULES_DIR GPAC_CFG_DIR
277 #endif
278
get_default_install_path(char * file_path,u32 path_type)279 static Bool get_default_install_path(char *file_path, u32 path_type)
280 {
281 if (path_type==GF_PATH_APP) strcpy(file_path, SYMBIAN_GPAC_MODULES_DIR);
282 else if (path_type==GF_PATH_CFG) strcpy(file_path, SYMBIAN_GPAC_CFG_DIR);
283 else if (path_type==GF_PATH_GUI) strcpy(file_path, SYMBIAN_GPAC_GUI_DIR);
284 else if (path_type==GF_PATH_MODULES) strcpy(file_path, SYMBIAN_GPAC_MODULES_DIR);
285 return 1;
286 }
287
288 /*Linux, OSX, iOS*/
289 #else
290
get_default_install_path(char * file_path,u32 path_type)291 static Bool get_default_install_path(char *file_path, u32 path_type)
292 {
293 char app_path[GF_MAX_PATH];
294 char *sep;
295 #if (defined(__DARWIN__) || defined(__APPLE__) || defined(GPAC_CONFIG_LINUX))
296 u32 size;
297 #endif
298
299 /*on OSX, Linux & co, user home is where we store the cfg file*/
300 if (path_type==GF_PATH_CFG) {
301 char *user_home = getenv("HOME");
302 #ifdef GPAC_CONFIG_IOS
303 char buf[PATH_MAX];
304 char *res;
305 #endif
306 if (!user_home) {
307 GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Couldn't find HOME directory\n"));
308 return 0;
309 }
310 #ifdef GPAC_CONFIG_IOS
311 res = realpath(user_home, buf);
312 if (res) {
313 strcpy(file_path, buf);
314 strcat(file_path, "/Documents");
315 } else
316 #endif
317 strcpy(file_path, user_home);
318
319 if (file_path[strlen(file_path)-1] == '/') file_path[strlen(file_path)-1] = 0;
320
321 //cleanup of old install in .gpacrc
322 if (check_file_exists(".gpacrc", file_path, file_path)) {
323 strcpy(app_path, file_path);
324 strcat(app_path, "/.gpacrc");
325 gf_file_delete(app_path);
326 }
327
328 strcat(file_path, "/.gpac");
329 if (!gf_dir_exists(file_path)) {
330 gf_mkdir(file_path);
331 }
332 return 1;
333 }
334
335 if (path_type==GF_PATH_APP) {
336 #if (defined(__DARWIN__) || defined(__APPLE__) )
337 size = GF_MAX_PATH-1;
338 if (_NSGetExecutablePath(app_path, &size) ==0) {
339 realpath(app_path, file_path);
340 sep = strrchr(file_path, '/');
341 if (sep) sep[0] = 0;
342 return 1;
343 }
344
345 #elif defined(GPAC_CONFIG_LINUX)
346 size = readlink("/proc/self/exe", file_path, GF_MAX_PATH-1);
347 if (size>0) {
348 file_path[size] = 0;
349 sep = strrchr(file_path, '/');
350 if (sep) sep[0] = 0;
351 return 1;
352 }
353
354 #elif defined(GPAC_CONFIG_WIN32)
355 GetModuleFileNameA(NULL, file_path, GF_MAX_PATH);
356 if (strstr(file_path, ".exe")) {
357 sep = strrchr(file_path, '\\');
358 if (sep) sep[0] = 0;
359 if ((file_path[1]==':') && (file_path[2]=='\\')) {
360 strcpy(file_path, &file_path[2]);
361 }
362 sep = file_path;
363 while ( sep[0] ) {
364 if (sep[0]=='\\') sep[0]='/';
365 sep++;
366 }
367 //get rid of /mingw32 or /mingw64
368 sep = strstr(file_path, "/usr/");
369 if (sep) {
370 strcpy(file_path, sep);
371 }
372 return 1;
373 }
374 #endif
375 GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Unknown arch, cannot find executable path\n"));
376 return 0;
377 }
378
379
380 /*locate the app*/
381 if (!get_default_install_path(app_path, GF_PATH_APP)) {
382 GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Couldn't find GPAC binaries install directory\n"));
383 return 0;
384 }
385
386 /*installed or symlink on system, user user home directory*/
387 if (!strnicmp(app_path, "/usr/", 5) || !strnicmp(app_path, "/opt/", 5)) {
388 if (path_type==GF_PATH_SHARE) {
389 /*look in possible install dirs ...*/
390 if (check_file_exists("gui/gui.bt", "/usr/share/gpac", file_path)) return 1;
391 if (check_file_exists("gui/gui.bt", "/usr/local/share/gpac", file_path)) return 1;
392 if (check_file_exists("gui/gui.bt", "/opt/share/gpac", file_path)) return 1;
393 if (check_file_exists("gui/gui.bt", "/opt/local/share/gpac", file_path)) return 1;
394 } else if (path_type==GF_PATH_MODULES) {
395 /*look in possible install dirs ...*/
396 if (check_file_exists(TEST_MODULE, "/usr/lib64/gpac", file_path)) return 1;
397 if (check_file_exists(TEST_MODULE, "/usr/lib/gpac", file_path)) return 1;
398 if (check_file_exists(TEST_MODULE, "/usr/local/lib/gpac", file_path)) return 1;
399 if (check_file_exists(TEST_MODULE, "/opt/lib/gpac", file_path)) return 1;
400 if (check_file_exists(TEST_MODULE, "/opt/local/lib/gpac", file_path)) return 1;
401 if (check_file_exists(TEST_MODULE, "/usr/lib/x86_64-linux-gnu/gpac", file_path)) return 1;
402 if (check_file_exists(TEST_MODULE, "/usr/lib/i386-linux-gnu/gpac", file_path)) return 1;
403 }
404 }
405
406 if (path_type==GF_PATH_SHARE) {
407 if (get_default_install_path(app_path, GF_PATH_CFG)) {
408 /*GUI not found, look in ~/.gpac/share/gui/ */
409 strcat(app_path, "/.gpac/share");
410 if (check_file_exists("share/gui.bt", app_path, file_path)) return 1;
411 }
412
413 /*GUI not found, look in gpac distribution if any */
414 if (get_default_install_path(app_path, GF_PATH_APP)) {
415 sep = strstr(app_path, "/bin/");
416 if (sep) {
417 sep[0] = 0;
418 strcat(app_path, "/share");
419 if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
420 }
421 sep = strstr(app_path, "/build/");
422 if (sep) {
423 sep[0] = 0;
424 strcat(app_path, "/share");
425 if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
426 }
427 }
428 /*GUI not found, look in .app for OSX case*/
429 }
430
431 if (path_type==GF_PATH_MODULES) {
432 /*look in gpac compilation tree (modules are output in the same folder as apps) */
433 if (get_default_install_path(app_path, GF_PATH_APP)) {
434 if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
435 /*on OSX check modules subdirectory */
436 strcat(app_path, "/modules");
437 if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
438
439 /*modules not found*/
440 GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("Couldn't find any modules in standard path (app path %s)\n", app_path));
441 }
442 /*modules not found, look in ~/.gpac/modules/ */
443 if (get_default_install_path(app_path, GF_PATH_CFG)) {
444 strcpy(app_path, file_path);
445 strcat(app_path, "/.gpac/modules");
446 if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
447 }
448 /*modules not found, failure*/
449 GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("Couldn't find any modules in HOME path (app path %s)\n", app_path));
450 return 0;
451 }
452
453 /*OSX way vs iPhone*/
454 sep = strstr(app_path, ".app/");
455 if (sep) sep[4] = 0;
456
457 /*we are looking for .app install path, or GUI */
458 if (path_type==GF_PATH_SHARE) {
459 #ifndef GPAC_CONFIG_IOS
460 strcat(app_path, "/Contents/MacOS/share");
461 if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
462 #else /*iOS: for now, everything is set flat within the package*/
463 /*iOS app is distributed with embedded GUI*/
464 get_default_install_path(app_path, GF_PATH_APP);
465 if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
466 strcat(app_path, "/share");
467 if (check_file_exists("gui/gui.bt", app_path, file_path)) return 1;
468 #endif
469 }
470 else { // (path_type==GF_PATH_MODULES)
471 strcat(app_path, "/Contents/MacOS/modules");
472 if (check_file_exists(TEST_MODULE, app_path, file_path)) return 1;
473 }
474 /*not found ...*/
475 return 0;
476 }
477
478 #endif
479
480 //get real path where the .gpac dir has been created, and use this as the default path
481 //for cache (tmp/ dir of ios app) and last working fir
482 #ifdef GPAC_CONFIG_IOS
gf_ios_refresh_cache_directory(GF_Config * cfg,const char * file_path)483 static void gf_ios_refresh_cache_directory( GF_Config *cfg, const char *file_path)
484 {
485 char *cache_dir, *old_cache_dir;
486 char buf[GF_MAX_PATH], *res, *sep;
487 res = realpath(file_path, buf);
488 if (!res) return;
489
490 sep = strstr(res, ".gpac");
491 assert(sep);
492 sep[0] = 0;
493 gf_cfg_set_key(cfg, "General", "LastWorkingDir", res);
494 gf_cfg_set_key(cfg, "General", "iOSDocumentsDir", res);
495
496 strcat(res, "cache/");
497 cache_dir = res;
498 old_cache_dir = (char*) gf_opts_get_key("core", "cache");
499
500 if (!gf_dir_exists(cache_dir)) {
501 if (old_cache_dir && strcmp(old_cache_dir, cache_dir)) {
502 GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("Cache dir changed: old %d -> new %s\n\n", old_cache_dir, cache_dir ));
503 }
504 gf_mkdir(cache_dir);
505 }
506 gf_cfg_set_key(cfg, "core", "cache", cache_dir);
507 }
508
509 #endif
510
511
create_default_config(char * file_path,const char * profile)512 static GF_Config *create_default_config(char *file_path, const char *profile)
513 {
514 GF_Config *cfg;
515 char szProfilePath[GF_MAX_PATH];
516 char szPath[GF_MAX_PATH];
517
518 if (! get_default_install_path(file_path, GF_PATH_CFG)) {
519 return NULL;
520 }
521 /*Create the config file*/
522 if (profile) {
523 sprintf(szPath, "%s%cprofiles%c%s%c%s", file_path, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR, profile, GF_PATH_SEPARATOR, CFG_FILE_NAME);
524 } else {
525 sprintf(szPath, "%s%c%s", file_path, GF_PATH_SEPARATOR, CFG_FILE_NAME);
526 }
527 GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("Trying to create config file: %s\n", szPath ));
528 if (profile && !strcmp(profile, "0")) {
529 cfg = gf_cfg_new(NULL, NULL);
530 } else {
531 FILE *f = gf_fopen(szPath, "wt");
532 if (!f) return NULL;
533 gf_fclose(f);
534
535 cfg = gf_cfg_new(NULL, szPath);
536 }
537
538 if (!cfg) return NULL;
539 strcpy(szProfilePath, szPath);
540
541
542 #ifndef GPAC_CONFIG_IOS
543 if (! get_default_install_path(szPath, GF_PATH_MODULES)) {
544 gf_file_delete(szPath);
545 GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] default modules not found\n"));
546 return NULL;
547 }
548 #else
549 get_default_install_path(szPath, GF_PATH_APP);
550 #endif
551
552 #if defined(GPAC_CONFIG_IOS)
553 gf_cfg_set_key(cfg, "core", "devclass", "ios");
554 #elif defined(GPAC_CONFIG_ANDROID)
555 gf_cfg_set_key(cfg, "core", "devclass", "android");
556 #else
557 gf_cfg_set_key(cfg, "core", "devclass", "desktop");
558 #endif
559
560
561
562 gf_cfg_set_key(cfg, "core", "mod-dirs", szPath);
563
564 #if defined(GPAC_CONFIG_IOS)
565 gf_ios_refresh_cache_directory(cfg, file_path);
566 #elif defined(GPAC_CONFIG_ANDROID)
567 if (get_default_install_path(szPath, GF_PATH_APP)) {
568 strcat(szPath, "/cache");
569 gf_cfg_set_key(cfg, "core", "cache", szPath);
570 }
571 #else
572 /*get default temporary directoy */
573 gf_cfg_set_key(cfg, "core", "cache", gf_get_default_cache_directory());
574 #endif
575
576 gf_cfg_set_key(cfg, "core", "ds-disable-notif", "no");
577
578 /*Setup font engine to FreeType by default, and locate TrueType font directory on the system*/
579 gf_cfg_set_key(cfg, "core", "font-reader", "FreeType Font Reader");
580 gf_cfg_set_key(cfg, "core", "rescan-fonts", "yes");
581
582 gf_cfg_set_key(cfg, "core", "js-dirs", "$GJS");
583
584
585 #if defined(_WIN32_WCE)
586 /*FIXME - is this true on all WinCE systems??*/
587 strcpy(szPath, "\\Windows");
588 #elif defined(WIN32)
589 GetWindowsDirectory((char*)szPath, MAX_PATH);
590 if (szPath[strlen((char*)szPath)-1] != '\\') strcat((char*)szPath, "\\");
591 strcat((char *)szPath, "Fonts");
592 #elif defined(__APPLE__)
593
594 #ifdef GPAC_CONFIG_IOS
595 strcpy(szPath, "/System/Library/Fonts/Cache,/System/Library/Fonts/AppFonts,/System/Library/Fonts/Core,/System/Library/Fonts/Extra");
596 #else
597 strcpy(szPath, "/Library/Fonts");
598 #endif
599
600 #elif defined(GPAC_CONFIG_ANDROID)
601 strcpy(szPath, "/system/fonts/");
602 #else
603 //scan all /usr/share/fonts, not just /usr/share/fonts/truetype/ which does not exist in some distrros
604 strcpy(szPath, "/usr/share/fonts/");
605 #endif
606 gf_cfg_set_key(cfg, "core", "font-dirs", szPath);
607
608 gf_cfg_set_key(cfg, "core", "cache-size", "100M");
609
610 #if defined(_WIN32_WCE)
611 gf_cfg_set_key(cfg, "core", "video-output", "GAPI Video Output");
612 #elif defined(WIN32)
613 gf_cfg_set_key(cfg, "core", "video-output", "DirectX Video Output");
614 #elif defined(__DARWIN__) || defined(__APPLE__)
615 gf_cfg_set_key(cfg, "core", "video-output", "SDL Video Output");
616 #elif defined(GPAC_CONFIG_ANDROID)
617 gf_cfg_set_key(cfg, "core", "video-output", "Android Video Output");
618 gf_cfg_set_key(cfg, "core", "audio-output", "Android Audio Output");
619 #else
620 gf_cfg_set_key(cfg, "core", "video-output", "X11 Video Output");
621 gf_cfg_set_key(cfg, "core", "audio-output", "SDL Audio Output");
622 #endif
623
624 gf_cfg_set_key(cfg, "core", "switch-vres", "no");
625 gf_cfg_set_key(cfg, "core", "hwvmem", "auto");
626
627
628 /*locate GUI*/
629 if ( get_default_install_path(szPath, GF_PATH_SHARE) ) {
630 char gui_path[GF_MAX_PATH+100];
631 sprintf(gui_path, "%s%cgui%cgui.bt", szPath, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
632 if (gf_file_exists(gui_path)) {
633 gf_cfg_set_key(cfg, "General", "StartupFile", gui_path);
634 }
635
636 /*shaders are at the same location*/
637 sprintf(gui_path, "%s%cshaders%cvertex.glsl", szPath, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
638 gf_cfg_set_key(cfg, "filter@compositor", "vertshader", gui_path);
639 sprintf(gui_path, "%s%cshaders%cfragment.glsl", szPath, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
640 gf_cfg_set_key(cfg, "filter@compositor", "fragshader", gui_path);
641
642 //aliases and other defaults
643 sprintf(gui_path, "%s%cdefault.cfg", szPath, GF_PATH_SEPARATOR);
644 if (gf_file_exists(gui_path)) {
645 GF_Config *aliases = gf_cfg_new(NULL, gui_path);
646 if (aliases) {
647 u32 i, count = gf_cfg_get_section_count(aliases);
648 for (i=0; i<count; i++) {
649 u32 j, count2;
650 const char *sec_name = gf_cfg_get_section_name(aliases, i);
651 if (!sec_name) continue;
652 count2 = gf_cfg_get_key_count(aliases, sec_name);
653 for (j=0; j<count2; j++) {
654 const char *name = gf_cfg_get_key_name(aliases, sec_name, j);
655 const char *val = gf_cfg_get_key(aliases, sec_name, name);
656 gf_cfg_set_key(cfg, sec_name, name, val);
657 }
658 }
659 }
660 gf_cfg_del(aliases);
661 }
662 }
663
664 if (profile && !strcmp(profile, "0")) {
665 GF_Err gf_cfg_set_filename(GF_Config *iniFile, const char * fileName);
666 sprintf(szPath, "%s%c%s", file_path, GF_PATH_SEPARATOR, CFG_FILE_NAME);
667 gf_cfg_set_filename(cfg, szPath);
668 gf_cfg_discard_changes(cfg);
669 return cfg;
670 }
671 /*store and reload*/
672 strcpy(szPath, gf_cfg_get_filename(cfg));
673 gf_cfg_del(cfg);
674 return gf_cfg_new(NULL, szPath);
675 }
676
677 /*check if modules directory has changed in the config file
678 */
check_modules_dir(GF_Config * cfg)679 static void check_modules_dir(GF_Config *cfg)
680 {
681 char path[GF_MAX_PATH];
682
683 #ifdef GPAC_CONFIG_IOS
684 const char *cfg_path;
685 if ( get_default_install_path(path, GF_PATH_SHARE) ) {
686 char *sep;
687 char shader_path[GF_MAX_PATH];
688 strcat(path, "/gui/gui.bt");
689 gf_cfg_set_key(cfg, "General", "StartupFile", path);
690 //get rid of "/gui/gui.bt"
691 sep = strrchr(path, '/');
692 sep[0] = 0;
693 sep = strrchr(path, '/');
694 sep[0] = 0;
695
696 sprintf(shader_path, "%s%cshaders%cvertex.glsl", path, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
697 gf_cfg_set_key(cfg, "filter@compositor", "vertshader", shader_path);
698 sprintf(shader_path, "%s%cshaders%cfragment.glsl", path, GF_PATH_SEPARATOR, GF_PATH_SEPARATOR);
699 gf_cfg_set_key(cfg, "filter@compositor", "fragshader", shader_path);
700 }
701 cfg_path = gf_cfg_get_filename(cfg);
702 gf_ios_refresh_cache_directory(cfg, cfg_path);
703
704 #else
705 const char *opt;
706
707 if ( get_default_install_path(path, GF_PATH_MODULES) ) {
708 opt = gf_cfg_get_key(cfg, "core", "mod-dirs");
709 //for OSX, we can have an install in /usr/... and an install in /Applications/Osmo4.app - always change
710 #if defined(__DARWIN__) || defined(__APPLE__)
711 if (!opt || strcmp(opt, path))
712 gf_cfg_set_key(cfg, "core", "mod-dirs", path);
713 #else
714
715 //otherwise only check we didn't switch between a 64 bit version and a 32 bit version
716 if (!opt) {
717 gf_cfg_set_key(cfg, "core", "mod-dirs", path);
718 } else {
719 Bool erase_modules_dir = GF_FALSE;
720 const char *opt64 = gf_cfg_get_key(cfg, "core", "64bits");
721 if (!opt64) {
722 //first run or old versions, erase
723 erase_modules_dir = GF_TRUE;
724 } else if (!strcmp(opt64, "yes") ) {
725 #ifndef GPAC_64_BITS
726 erase_modules_dir = GF_TRUE;
727 #endif
728 } else {
729 #ifdef GPAC_64_BITS
730 erase_modules_dir = GF_TRUE;
731 #endif
732 }
733
734 #ifdef GPAC_64_BITS
735 opt64 = "yes";
736 #else
737 opt64 = "no";
738 #endif
739 gf_cfg_set_key(cfg, "core", "64bits", opt64);
740
741 if (erase_modules_dir) {
742 gf_cfg_set_key(cfg, "core", "mod-dirs", path);
743 }
744 }
745 #endif
746 }
747
748 /*if startup file was disabled, do not attempt to correct it*/
749 if (gf_cfg_get_key(cfg, "General", "StartupFile")==NULL) return;
750
751 if ( get_default_install_path(path, GF_PATH_SHARE) ) {
752 opt = gf_cfg_get_key(cfg, "General", "StartupFile");
753 if (strstr(opt, "gui.bt") && strcmp(opt, path) && strstr(path, ".app") ) {
754 #if defined(__DARWIN__) || defined(__APPLE__)
755 strcat(path, "/gui/gui.bt");
756 gf_cfg_set_key(cfg, "General", "StartupFile", path);
757 #endif
758 }
759 }
760
761 #endif
762 }
763
764
765 /*!
766 \brief configuration file initialization
767 *
768 * Constructs a configuration file from fileName. If fileName is NULL, the default GPAC configuration file is loaded with the
769 * proper module directory, font directory and other default options. If fileName is non-NULL no configuration file is found,
770 * a "light" default configuration file is created.
771 \param profile name or path to existing config file
772 \return the configuration file object, NULL if the file could not be created
773 */
774 #include <gpac/network.h>
775
gf_cfg_init(const char * profile)776 static GF_Config *gf_cfg_init(const char *profile)
777 {
778 GF_Config *cfg=NULL;
779 u32 prof_len=0;
780 Bool force_new_cfg=GF_FALSE;
781 char szPath[GF_MAX_PATH];
782 char *prof_opt = NULL;
783
784 if (profile) {
785 prof_len = (u32) strlen(profile);
786 prof_opt = gf_url_colon_suffix(profile);
787 if (prof_opt) {
788 prof_len -= (u32) strlen(prof_opt);
789 if (strstr(prof_opt, "reload")) force_new_cfg = GF_TRUE;
790
791 prof_opt[0] = 0;
792 }
793 }
794 if (profile && !prof_len)
795 profile = NULL;
796
797 if (profile && (strchr(profile, '/') || strchr(profile, '\\')) ) {
798 if (!gf_file_exists(profile)) {
799 GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Config file %s does not exist\n", profile));
800 goto exit;
801 }
802 cfg = gf_cfg_new(NULL, profile);
803 if (!cfg) {
804 GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Failed to load existing config file %s\n", profile));
805 goto exit;
806 }
807 if (force_new_cfg) {
808 gf_cfg_del(cfg);
809 cfg = create_default_config(NULL, profile);
810 }
811 check_modules_dir(cfg);
812 goto exit;
813 }
814
815 if (!get_default_install_path(szPath, GF_PATH_CFG)) {
816 GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] Fatal error: Cannot create global config file in application or user home directory - no write access\n"));
817 goto exit;
818 }
819
820 if (profile) {
821 strcat(szPath, "/profiles/");
822 strcat(szPath, profile);
823 }
824
825 cfg = gf_cfg_new(szPath, CFG_FILE_NAME);
826 //config file not compatible with old arch, check it:
827 if (cfg) {
828 u32 nb_old_sec = gf_cfg_get_key_count(cfg, "Compositor");
829 nb_old_sec += gf_cfg_get_key_count(cfg, "MimeTypes");
830 nb_old_sec += gf_cfg_get_key_count(cfg, "Video");
831 nb_old_sec += gf_cfg_get_key_count(cfg, "Audio");
832 nb_old_sec += gf_cfg_get_key_count(cfg, "Systems");
833 if (! gf_cfg_get_key_count(cfg, "core"))
834 nb_old_sec += 1;
835
836 if (nb_old_sec || force_new_cfg) {
837 if (nb_old_sec && (!profile || strcmp(profile, "0"))) {
838 GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[core] Incompatible (0.8.0 or older) config file %s found in %s - creating new file\n", CFG_FILE_NAME, szPath ));
839 }
840 gf_cfg_del(cfg);
841 cfg = create_default_config(szPath, profile);
842 }
843 }
844 //no config file found
845 else {
846 if (!profile || strcmp(profile, "0")) {
847 GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] Config file %s not found in %s - creating new file\n", CFG_FILE_NAME, szPath ));
848 }
849 cfg = create_default_config(szPath, profile);
850 }
851 if (!cfg) {
852 GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Cannot create config file %s in %s directory\n", CFG_FILE_NAME, szPath));
853 goto exit;
854 }
855
856 #ifndef GPAC_CONFIG_IOS
857 GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[core] Using global config file in %s directory\n", szPath));
858 #endif
859
860 check_modules_dir(cfg);
861
862 if (!gf_cfg_get_key(cfg, "core", "store-dir")) {
863 char *sep;
864 strcpy(szPath, gf_cfg_get_filename(cfg));
865 sep = strrchr(szPath, '/');
866 if (!sep) sep = strrchr(szPath, '\\');
867 if (sep) sep[0] = 0;
868 strcat(szPath, "/Storage");
869 if (!gf_dir_exists(szPath)) gf_mkdir(szPath);
870 gf_cfg_set_key(cfg, "core", "store-dir", szPath);
871 }
872
873 exit:
874 if (prof_opt) prof_opt[0] = ':';
875 return cfg;
876 }
877
878
879 GF_EXPORT
gf_opts_default_shared_directory(char * path_buffer)880 Bool gf_opts_default_shared_directory(char *path_buffer)
881 {
882 return get_default_install_path(path_buffer, GF_PATH_SHARE);
883 }
884
885 void gf_modules_new(GF_Config *config);
886 void gf_modules_del();
887
888 GF_Config *gpac_global_config = NULL;
889
gf_init_global_config(const char * profile)890 void gf_init_global_config(const char *profile)
891 {
892 if (!gpac_global_config) {
893 gpac_global_config = gf_cfg_init(profile);
894 if (!gpac_global_config) {
895 GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Fatal error: failed to initialize GPAC global configuration\n"));
896 exit(1);
897 }
898
899 gf_modules_new(gpac_global_config);
900 }
901 }
902
gf_uninit_global_config(Bool discard_config)903 void gf_uninit_global_config(Bool discard_config)
904 {
905 if (gpac_global_config) {
906 if (discard_config) gf_cfg_discard_changes(gpac_global_config);
907 gf_cfg_del(gpac_global_config);
908 gpac_global_config = NULL;
909 gf_modules_del();
910 }
911 }
912
913 GF_Err gf_cfg_set_key_internal(GF_Config *iniFile, const char *secName, const char *keyName, const char *keyValue, Bool is_restrict);
914
gf_cfg_load_restrict()915 void gf_cfg_load_restrict()
916 {
917 char szPath[GF_MAX_PATH];
918 if (get_default_install_path(szPath, GF_PATH_SHARE)) {
919 strcat(szPath, "/");
920 strcat(szPath, "restrict.cfg");
921 if (gf_file_exists(szPath)) {
922 GF_Config *rcfg = gf_cfg_new(NULL, szPath);
923 if (rcfg) {
924 u32 i, count = gf_cfg_get_section_count(rcfg);
925 for (i=0; i<count; i++) {
926 u32 j, kcount;
927 const char *sname = gf_cfg_get_section_name(rcfg, i);
928 if (!sname) break;
929 kcount = gf_cfg_get_key_count(rcfg, sname);
930 for (j=0; j<kcount; j++) {
931 const char *kname = gf_cfg_get_key_name(rcfg, sname, j);
932 const char *kval = gf_cfg_get_key(rcfg, sname, kname);
933 gf_cfg_set_key_internal(gpac_global_config, sname, kname, kval, GF_TRUE);
934 }
935 }
936 gf_cfg_del(rcfg);
937 }
938 }
939 }
940 }
941
942 GF_EXPORT
gf_opts_get_key(const char * secName,const char * keyName)943 const char *gf_opts_get_key(const char *secName, const char *keyName)
944 {
945 if (!gpac_global_config) return NULL;
946
947 if (!strcmp(secName, "core")) {
948 const char *opt = gf_cfg_get_key(gpac_global_config, "temp", keyName);
949 if (opt) return opt;
950 }
951 return gf_cfg_get_key(gpac_global_config, secName, keyName);
952 }
953 GF_EXPORT
gf_opts_set_key(const char * secName,const char * keyName,const char * keyValue)954 GF_Err gf_opts_set_key(const char *secName, const char *keyName, const char *keyValue)
955 {
956 if (!gpac_global_config) return GF_BAD_PARAM;
957 return gf_cfg_set_key(gpac_global_config, secName, keyName, keyValue);
958 }
959 GF_EXPORT
gf_opts_del_section(const char * secName)960 void gf_opts_del_section(const char *secName)
961 {
962 if (!gpac_global_config) return;
963 gf_cfg_del_section(gpac_global_config, secName);
964 }
965 GF_EXPORT
gf_opts_get_section_count()966 u32 gf_opts_get_section_count()
967 {
968 if (!gpac_global_config) return 0;
969 return gf_cfg_get_section_count(gpac_global_config);
970 }
971 GF_EXPORT
gf_opts_get_section_name(u32 secIndex)972 const char *gf_opts_get_section_name(u32 secIndex)
973 {
974 if (!gpac_global_config) return NULL;
975 return gf_cfg_get_section_name(gpac_global_config, secIndex);
976 }
977 GF_EXPORT
gf_opts_get_key_count(const char * secName)978 u32 gf_opts_get_key_count(const char *secName)
979 {
980 if (!gpac_global_config) return 0;
981 return gf_cfg_get_key_count(gpac_global_config, secName);
982 }
983 GF_EXPORT
gf_opts_get_key_name(const char * secName,u32 keyIndex)984 const char *gf_opts_get_key_name(const char *secName, u32 keyIndex)
985 {
986 if (!gpac_global_config) return NULL;
987 return gf_cfg_get_key_name(gpac_global_config, secName, keyIndex);
988 }
989
990 GF_EXPORT
gf_opts_get_key_restricted(const char * secName,const char * keyName)991 const char *gf_opts_get_key_restricted(const char *secName, const char *keyName)
992 {
993 const char *res = NULL;
994 const char *gf_cfg_get_key_internal(GF_Config *iniFile, const char *secName, const char *keyName, Bool restricted_only);
995 if (gpac_global_config)
996 res = gf_cfg_get_key_internal(gpac_global_config, secName, keyName, GF_TRUE);
997 return res;
998 }
999
1000 GF_EXPORT
gf_opts_get_filename()1001 const char *gf_opts_get_filename()
1002 {
1003 return gf_cfg_get_filename(gpac_global_config);
1004 }
1005 GF_EXPORT
gf_opts_discard_changes()1006 GF_Err gf_opts_discard_changes()
1007 {
1008 return gf_cfg_discard_changes(gpac_global_config);
1009 }
1010
1011 #include <gpac/main.h>
1012
1013 GF_GPACArg GPAC_Args[] = {
1014 GF_DEF_ARG("noprog", NULL, "disable progress messages", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_LOG),
1015 GF_DEF_ARG("quiet", NULL, "disable all messages, including errors", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_LOG),
1016 GF_DEF_ARG("log-file", "lf", "set output log file", NULL, NULL, GF_ARG_STRING, GF_ARG_SUBSYS_LOG),
1017 GF_DEF_ARG("log-clock", "lc", "log time in micro sec since start time of GPAC before each log line", NULL, NULL, GF_ARG_BOOL, GF_ARG_SUBSYS_LOG),
1018 GF_DEF_ARG("log-utc", "lu", "log UTC time in ms before each log line", NULL, NULL, GF_ARG_BOOL, GF_ARG_SUBSYS_LOG),
1019 GF_DEF_ARG("logs", NULL, "set log tools and levels. \n"\
1020 " \n"\
1021 "You can independently log different tools involved in a session.\n"\
1022 "log_args is formatted as a ':'-separated list of `toolX[:toolZ]@levelX`\n"\
1023 "`levelX` can be one of:\n"\
1024 "- quiet: skip logs\n"\
1025 "- error: logs only error messages\n"\
1026 "- warning: logs error+warning messages\n"\
1027 "- info: logs error+warning+info messages\n"\
1028 "- debug: logs all messages\n"\
1029 "`toolX` can be one of:\n"\
1030 "- core: libgpac core\n"\
1031 "- coding: bitstream formats (audio, video, scene)\n"\
1032 "- container: container formats (ISO File, MPEG-2 TS, AVI, ...)\n"\
1033 "- network: network data except RTP trafic\n"\
1034 "- http: HTTP trafic\n"\
1035 "- rtp: RTP trafic\n"\
1036 "- author: authoring tools (hint, import, export)\n"\
1037 "- sync: terminal sync layer\n"\
1038 "- codec: terminal codec messages\n"\
1039 "- parser: scene parsers (svg, xmt, bt) and other\n"\
1040 "- media: terminal media object management\n"\
1041 "- scene: scene graph and scene manager\n"\
1042 "- script: scripting engine messages\n"\
1043 "- interact: interaction engine (events, scripts, etc)\n"\
1044 "- smil: SMIL timing engine\n"\
1045 "- compose: composition engine (2D, 3D, etc)\n"\
1046 "- mmio: Audio/Video HW I/O management\n"\
1047 "- rti: various run-time stats\n"\
1048 "- cache: HTTP cache subsystem\n"\
1049 "- audio: Audio renderer and mixers\n"\
1050 "- mem: GPAC memory tracker\n"\
1051 "- dash: HTTP streaming logs\n"\
1052 "- module: GPAC modules (av out, font engine, 2D rasterizer)\n"\
1053 "- filter: filters debugging\n"\
1054 "- sched: filter session scheduler debugging\n"\
1055 "- mutex: log all mutex calls\n"\
1056 "- atsc: ATSC3 debugging\n"\
1057 "- all: all tools logged - other tools can be specified afterwards. \n"\
1058 "The special keyword `ncl` can be set to disable color logs. \n"\
1059 "The special keyword `strict` can be set to exit at first error. \n"\
1060 "EX -logs all@info:dash@debug:ncl\n"\
1061 "This moves all log to info level, dash to debug level and disable color logs"\
1062 , NULL, NULL, GF_ARG_STRING, GF_ARG_SUBSYS_LOG),
1063
1064 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),
1065 GF_DEF_ARG("store-dir", NULL, "set storage directory", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_CORE),
1066 GF_DEF_ARG("mod-dirs", NULL, "set module directories", NULL, NULL, GF_ARG_STRINGS, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1067 GF_DEF_ARG("js-dirs", NULL, "set javascript directories", NULL, NULL, GF_ARG_STRINGS, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1068 GF_DEF_ARG("no-js-mods", NULL, "disable javascript module loading", NULL, NULL, GF_ARG_STRINGS, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1069 GF_DEF_ARG("ifce", NULL, "set default multicast interface through interface IP address", NULL, NULL, GF_ARG_STRING, GF_ARG_SUBSYS_CORE),
1070 GF_DEF_ARG("lang", NULL, "set preferred language", NULL, NULL, GF_ARG_STRING, GF_ARG_SUBSYS_CORE),
1071 GF_DEF_ARG("cfg", "opt", "set configuration file value. The string parameter can be formatted as:\n"\
1072 "- `section:key=val`: set the key to a new value\n"\
1073 "- `section:key=null`, `section:key`: remove the key\n"\
1074 "- `section:*=null`: remove the section"\
1075 , NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_CORE),
1076 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),
1077 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),
1078 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),
1079 GF_DEF_ARG("for-test", NULL, "disable all creation/modif dates and GPAC versions in files", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_CORE),
1080 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),
1081 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),
1082 GF_DEF_ARG("devclass", NULL, "set device class\n"
1083 "- ios: iOS-based mobile device\n"
1084 "- android: Android-based mobile device\n"
1085 "- desktop: desktop device", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_HIDE|GF_ARG_SUBSYS_CORE),
1086
1087 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),
1088 GF_DEF_ARG("cache", NULL, "cache directory location", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1089 GF_DEF_ARG("proxy-on", NULL, "enable HTTP proxy", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1090 GF_DEF_ARG("proxy-name", NULL, "set HTTP proxy address", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1091 GF_DEF_ARG("proxy-port", NULL, "set HTTP proxy port", "80", NULL, GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1092 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),
1093 GF_DEF_ARG("no-cache", NULL, "disable HTTP caching", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1094 GF_DEF_ARG("offline-cache", NULL, "enable offline HTTP caching (no revalidation of existing resource in cache)", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1095 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),
1096 GF_DEF_ARG("cache-size", NULL, "specify cache size in bytes", "100M", NULL, GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_HTTP),
1097 GF_DEF_ARG("head-timeout", NULL, "set HTTP head request timeout in milliseconds", "5000", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1098 GF_DEF_ARG("req-timeout", NULL, "set HTTP/RTSP request timeout in milliseconds", "20000", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1099 GF_DEF_ARG("broken-cert", NULL, "enable accepting broken SSL certificates", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_HTTP),
1100 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),
1101 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),
1102 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),
1103 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),
1104 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),
1105
1106 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),
1107 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),
1108
1109 GF_DEF_ARG("no-block", NULL, "disable blocking mode of filters\n"
1110 "- no: enable blocking mode\n"
1111 "- fanout: disable blocking on fanout, unblocking the PID as soon as one of its destinations requires a packet\n"
1112 "- all: disable blocking", "no", "no|fanout|all", GF_ARG_INT, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_FILTERS),
1113 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),
1114 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),
1115 GF_DEF_ARG("sched", NULL, "set scheduler mode\n"\
1116 "- free: lock-free queues except for task list (default)\n"\
1117 "- lock: mutexes for queues when several threads\n"\
1118 "- freex: lock-free queues including for task lists (experimental)\n"\
1119 "- flock: mutexes for queues even when no thread (debug mode)\n"\
1120 "- 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),
1121 GF_DEF_ARG("max-chain", NULL, "set maximum chain length when resolving filter links. Default value covers for __[ in -> ] demux -> reframe -> decode -> encode -> reframe -> mux [ -> out]__. Filter chains loaded for adaptation (eg 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),
1122 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),
1123
1124 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),
1125 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),
1126 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),
1127 GF_DEF_ARG("blacklist", NULL, "blacklist the filters listed in the given string (comma-separated list)", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_FILTERS),
1128 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),
1129 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),
1130
1131 GF_DEF_ARG("switch-vres", NULL, "select smallest video resolution larger than scene size, otherwise use current video resolution", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_VIDEO),
1132 GF_DEF_ARG("hwvmem", NULL, "specify (2D rendering only) memory type of main video backbuffer. Depending on the scene type, this may drastically change the playback speed.\n"
1133 "- always: always on hardware\n"
1134 "- never: always on system memory\n"
1135 "- auto: selected by GPAC based on content type (graphics or video)", "auto", "auto|always|never", GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_VIDEO),
1136 GF_DEF_ARG("pref-yuv4cc", NULL, "set prefered YUV 4CC for overlays (used by DirectX only)", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_VIDEO),
1137 GF_DEF_ARG("yuv-overlay", NULL, "indicate YUV overlay is possible on the video card. Always overriden by video output module", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_HIDE|GF_ARG_SUBSYS_VIDEO),
1138 GF_DEF_ARG("offscreen-yuv", NULL, "indicate if offscreen yuv->rgb is enabled. can be set to false to force disabling", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_VIDEO),
1139 GF_DEF_ARG("overlay-color-key", NULL, "color to use for overlay keying, hex format", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_VIDEO),
1140 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),
1141 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),
1142 GF_DEF_ARG("gl-doublebuf", NULL, "enable openGL double buffering", "yes", NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_VIDEO),
1143 GF_DEF_ARG("sdl-defer", NULL, "use defer rendering for SDL", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_VIDEO),
1144 GF_DEF_ARG("no-colorkey", NULL, "disable color keying at the video output level", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_VIDEO),
1145 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),
1146 GF_DEF_ARG("video-output", NULL, "indicate the name of the video output module to use (see `gpac -h modules`)."
1147 " The reserved name `glfbo` is used in player mode to draw in the openGL texture identified by [-glfbo-txid](). "
1148 " In this mode, the application is responsible for sending event to the terminal"
1149 , NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_VIDEO),
1150 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_VIDEO),
1151 GF_DEF_ARG("alsa-devname", NULL, "set ALSA dev name", NULL, NULL, GF_ARG_STRING, GF_ARG_HINT_ADVANCED|GF_ARG_SUBSYS_AUDIO),
1152 GF_DEF_ARG("force-alsarate", NULL, "force ALSA and OSS output sample rate", NULL, NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_AUDIO),
1153 GF_DEF_ARG("ds-disable-notif", NULL, "disable DirectSound audio buffer notifications when supported", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_AUDIO),
1154 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),
1155 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),
1156 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),
1157 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),
1158
1159 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),
1160 GF_DEF_ARG("rmt-port", NULL, "set remotery port", "17815", NULL, GF_ARG_INT, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1161 GF_DEF_ARG("rmt-reuse", NULL, "allow remotery to reuse port", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1162 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),
1163 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),
1164 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),
1165 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),
1166 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),
1167 GF_DEF_ARG("rmt-ogl", NULL, "make remotery sample opengl calls", NULL, NULL, GF_ARG_BOOL, GF_ARG_HINT_EXPERT|GF_ARG_SUBSYS_RMT),
1168 {0}
1169 };
1170
1171
1172 const GF_Config *gf_sys_get_lang_file();
1173
1174 GF_EXPORT
gf_sys_get_options()1175 const GF_GPACArg *gf_sys_get_options()
1176 {
1177 return GPAC_Args;
1178 }
1179
gpac_opt_default(const char * argname)1180 static const char *gpac_opt_default(const char *argname)
1181 {
1182 const GF_GPACArg *arg = NULL;
1183 u32 i=0;
1184 while (GPAC_Args[i].name) {
1185 arg = &GPAC_Args[i];
1186 i++;
1187 if (!strcmp(arg->name, argname)) break;
1188 arg = NULL;
1189 }
1190 if (!arg) return NULL;
1191 return arg->val;
1192 }
1193
1194 GF_EXPORT
gf_opts_get_bool(const char * secName,const char * keyName)1195 Bool gf_opts_get_bool(const char *secName, const char *keyName)
1196 {
1197 const char *opt = gf_opts_get_key(secName, keyName);
1198
1199 if (!opt && !strcmp(secName, "core")) {
1200 opt = gpac_opt_default(keyName);
1201 }
1202
1203 if (!opt) return GF_FALSE;
1204 if (!strcmp(opt, "yes")) return GF_TRUE;
1205 if (!strcmp(opt, "true")) return GF_TRUE;
1206 if (!strcmp(opt, "1")) return GF_TRUE;
1207 return GF_FALSE;
1208 }
1209 GF_EXPORT
gf_opts_get_int(const char * secName,const char * keyName)1210 u32 gf_opts_get_int(const char *secName, const char *keyName)
1211 {
1212 u32 times=1, val;
1213 char *opt = (char *) gf_opts_get_key(secName, keyName);
1214
1215 if (!opt && !strcmp(secName, "core")) {
1216 opt = (char *) gpac_opt_default(keyName);
1217 }
1218 if (!opt) return 0;
1219 char *sep = strchr(opt, 'k');
1220 if (sep) times=1000;
1221 else {
1222 sep = strchr(opt, 'K');
1223 if (sep) times=1000;
1224 else {
1225 sep = strchr(opt, 'm');
1226 if (sep) times=1000000;
1227 else {
1228 sep = strchr(opt, 'M');
1229 if (sep) times=1000000;
1230 }
1231 }
1232 }
1233 sscanf(opt, "%d", &val);
1234 val = atoi(opt);
1235 return val*times;
1236 }
1237
1238 GF_EXPORT
gf_sys_set_cfg_option(const char * opt_string)1239 Bool gf_sys_set_cfg_option(const char *opt_string)
1240 {
1241 size_t sepIdx;
1242 char *sep, *sep2, szSec[1024], szKey[1024], szVal[1024];
1243 sep = strchr(opt_string, ':');
1244 if (!sep) {
1245 GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[CoreArgs] Badly formatted option %s - expected Section:Name=Value\n", opt_string ) );
1246 return GF_FALSE;
1247 }
1248
1249 sepIdx = sep - opt_string;
1250 if (sepIdx>=1024)
1251 sepIdx = 1023;
1252 strncpy(szSec, opt_string, sepIdx);
1253 szSec[sepIdx] = 0;
1254
1255 sep ++;
1256 sep2 = strchr(sep, '=');
1257 if (!sep2) {
1258 gf_opts_set_key(szSec, sep, NULL);
1259 return GF_TRUE;
1260 }
1261
1262 sepIdx = sep2 - sep;
1263 if (sepIdx>=1024)
1264 sepIdx = 1023;
1265 strncpy(szKey, sep, sepIdx);
1266 szKey[sepIdx] = 0;
1267
1268 sepIdx = strlen(sep2+1);
1269 if (sepIdx>=1024)
1270 sepIdx = 1023;
1271 strncpy(szVal, sep2+1, sepIdx);
1272 szVal[sepIdx] = 0;
1273
1274 if (!stricmp(szKey, "*")) {
1275 if (stricmp(szVal, "null")) {
1276 GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[CoreArgs] Badly formatted option %s - expected Section:*=null\n", opt_string));
1277 return GF_FALSE;
1278 }
1279 gf_opts_del_section(szSec);
1280 return GF_TRUE;
1281 }
1282
1283 if (!stricmp(szVal, "null")) {
1284 szVal[0]=0;
1285 }
1286 gf_opts_set_key(szSec, szKey, szVal[0] ? szVal : NULL);
1287
1288 if (!strcmp(szSec, "core")) {
1289 if (!strcmp(szKey, "noprog") && (!strcmp(szVal, "yes")||!strcmp(szVal, "true")||!strcmp(szVal, "1")) ) {
1290 void gpac_disable_progress();
1291
1292 gpac_disable_progress();
1293 }
1294 }
1295 return GF_TRUE;
1296 }
1297
gf_opts_load_option(const char * arg_name,const char * val,Bool * consumed_next,GF_Err * e)1298 Bool gf_opts_load_option(const char *arg_name, const char *val, Bool *consumed_next, GF_Err *e)
1299 {
1300 const GF_GPACArg *arg = NULL;
1301 u32 i=0;
1302 *e = GF_OK;
1303 *consumed_next = GF_FALSE;
1304 arg_name = arg_name+1;
1305 while (GPAC_Args[i].name) {
1306 arg = &GPAC_Args[i];
1307 i++;
1308 if (!strcmp(arg->name, arg_name)) break;
1309 if (arg->altname && !strcmp(arg->altname, arg_name)) break;
1310
1311 arg = NULL;
1312 }
1313 if (!arg) return GF_FALSE;
1314
1315 if (!strcmp(arg->name, "cfg")) {
1316 *consumed_next = GF_TRUE;
1317 if (! gf_sys_set_cfg_option(val)) *e = GF_BAD_PARAM;
1318 return GF_TRUE;
1319 }
1320 if (!strcmp(arg->name, "strict-error")) {
1321 gf_log_set_strict_error(1);
1322 return GF_TRUE;
1323 }
1324
1325 if (arg->type==GF_ARG_BOOL) {
1326 if (!val) gf_opts_set_key("temp", arg->name, "yes");
1327 else {
1328 if (!strcmp(val, "yes") || !strcmp(val, "true") || !strcmp(val, "1")) {
1329 *consumed_next = GF_TRUE;
1330 gf_opts_set_key("temp", arg->name, "yes");
1331 } else {
1332 if (!strcmp(val, "no") || !strcmp(val, "false") || !strcmp(val, "0")) {
1333 *consumed_next = GF_TRUE;
1334 gf_opts_set_key("temp", arg->name, "no");
1335 } else {
1336 gf_opts_set_key("temp", arg->name, "yes");
1337 }
1338 }
1339 }
1340 } else {
1341 *consumed_next = GF_TRUE;
1342 if (!val && (arg->type==GF_ARG_BOOL))
1343 gf_opts_set_key("temp", arg->name, "true");
1344 else
1345 gf_opts_set_key("temp", arg->name, val);
1346 }
1347 return GF_TRUE;
1348 }
1349
1350 GF_EXPORT
gf_sys_is_gpac_arg(const char * arg_name)1351 u32 gf_sys_is_gpac_arg(const char *arg_name)
1352 {
1353 char *argsep;
1354 u32 arglen;
1355 const GF_GPACArg *arg = NULL;
1356 u32 i=0;
1357 arg_name = arg_name+1;
1358 if (arg_name[0]=='-')
1359 return 1;
1360 if (arg_name[0]=='+')
1361 return 1;
1362
1363 argsep = strchr(arg_name, '=');
1364 if (argsep) arglen = (u32) (argsep - arg_name);
1365 else arglen = (u32) strlen(arg_name);
1366
1367 while (GPAC_Args[i].name) {
1368 arg = &GPAC_Args[i];
1369 i++;
1370 if ((strlen(arg->name) == arglen) && !strncmp(arg->name, arg_name, arglen)) break;
1371 if (arg->altname) {
1372 char *alt = strstr(arg->altname, arg_name);
1373 if (alt) {
1374 char c = alt[strlen(arg_name)];
1375 if (!c || (c==' ')) break;
1376 }
1377 }
1378 arg = NULL;
1379 }
1380 if (!arg) return 0;
1381 if (arg->type==GF_ARG_BOOL) return 1;
1382 if (argsep) return 1;
1383 return 2;
1384 }
1385
1386
1387 GF_EXPORT
gf_sys_print_arg(FILE * helpout,u32 flags,const GF_GPACArg * arg,const char * arg_subsystem)1388 void gf_sys_print_arg(FILE *helpout, u32 flags, const GF_GPACArg *arg, const char *arg_subsystem)
1389 {
1390 u32 gen_doc = 0;
1391 if (flags & GF_PRINTARG_MD)
1392 gen_doc = 1;
1393 if (!helpout) helpout = stderr;
1394
1395 //#ifdef GPAC_ENABLE_COVERAGE
1396 #if 1
1397 if ((arg->name[0]>='A') && (arg->name[0]<='Z')) {
1398 if ((arg->name[1]<'A') || (arg->name[1]>'Z')) {
1399 fprintf(stderr, "\nWARNING: arg %s bad name format, should use lowercase\n", arg->name);
1400 exit(1);
1401 }
1402 }
1403 if (arg->description) {
1404 char *sep;
1405
1406 if ((arg->description[0]>='A') && (arg->description[0]<='Z')) {
1407 if ((arg->description[1]<'A') || (arg->description[1]>'Z')) {
1408 fprintf(stderr, "\nWARNING: arg %s bad name format \"%s\", should use lowercase\n", arg->name, arg->description);
1409 exit(1);
1410 }
1411 }
1412 if (strchr(arg->description, '\t')) {
1413 fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should not use tab\n", arg->name, arg->description);
1414 exit(1);
1415 }
1416 u8 achar = arg->description[strlen(arg->description)-1];
1417 if (achar == '.') {
1418 fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should not end with .\n", arg->name, arg->description);
1419 exit(1);
1420 }
1421 sep = strchr(arg->description, ' ');
1422 if (sep) {
1423 sep--;
1424 if ((sep[0] == 's') && (sep[-1] != 's')) {
1425 fprintf(stderr, "\nWARNING: arg %s bad description format \"%s\", should use infintive\n", arg->name, arg->description);
1426 exit(1);
1427 }
1428 }
1429 }
1430 #endif
1431
1432 if (arg->flags & GF_ARG_HINT_HIDE)
1433 return;
1434
1435 const char *syntax=strchr(arg->name, ' ');
1436 char *arg_name=NULL;
1437 if (syntax) {
1438 arg_name = gf_strdup(arg->name);
1439 char *sep = strchr(arg_name, ' ');
1440 sep[0]=0;
1441 }
1442
1443 if (flags & GF_PRINTARG_MAN) {
1444 fprintf(helpout, ".TP\n.B \\-%s", arg_name ? arg_name : arg->name);
1445 }
1446 else if (gen_doc==1) {
1447 gf_sys_format_help(helpout, flags, "<a id=\"%s\">", arg_name ? arg_name : arg->name);
1448 gf_sys_format_help(helpout, flags | GF_PRINTARG_HIGHLIGHT_FIRST, "-%s", arg_name ? arg_name : arg->name);
1449 gf_sys_format_help(helpout, flags, "</a>");
1450 } else {
1451 gf_sys_format_help(helpout, flags | GF_PRINTARG_HIGHLIGHT_FIRST, "%s-%s", (flags&GF_PRINTARG_ADD_DASH) ? "-" : "", arg_name ? arg_name : arg->name);
1452 }
1453 if (arg->altname) {
1454 gf_sys_format_help(helpout, flags, ",");
1455 gf_sys_format_help(helpout, flags | GF_PRINTARG_HIGHLIGHT_FIRST, "%s-%s", (flags&GF_PRINTARG_ADD_DASH) ? "-" : "", arg->altname);
1456 }
1457 if (syntax) {
1458 gf_sys_format_help(helpout, flags, " %s", syntax);
1459 gf_free(arg_name);
1460 }
1461
1462 if (arg->type==GF_ARG_INT && arg->values && strchr(arg->values, '|')) {
1463 gf_sys_format_help(helpout, flags, " (Enum");
1464 if (arg->val)
1465 gf_sys_format_help(helpout, flags, ", default: **%s**", arg->val);
1466 gf_sys_format_help(helpout, flags, ")");
1467 } else if (arg->type != GF_ARG_BOOL) {
1468 gf_sys_format_help(helpout, flags, " (");
1469 switch (arg->type) {
1470 case GF_ARG_BOOL: gf_sys_format_help(helpout, flags, "boolean"); break;
1471 case GF_ARG_INT: gf_sys_format_help(helpout, flags, "int"); break;
1472 case GF_ARG_DOUBLE: gf_sys_format_help(helpout, flags, "number"); break;
1473 case GF_ARG_STRING: gf_sys_format_help(helpout, flags, "string"); break;
1474 case GF_ARG_STRINGS: gf_sys_format_help(helpout, flags, "string list"); break;
1475 default: break;
1476 }
1477 if (arg->val)
1478 gf_sys_format_help(helpout, flags, ", default: **%s**", arg->val);
1479 if (arg->values)
1480 gf_sys_format_help(helpout, flags, ", values: __%s__", arg->values);
1481 gf_sys_format_help(helpout, flags, ")");
1482 }
1483
1484 if (flags & GF_PRINTARG_MAN) {
1485 gf_sys_format_help(helpout, flags, "\n%s\n", gf_sys_localized(arg_subsystem, arg->name, arg->description) );
1486 } else {
1487 if (arg->description) {
1488 gf_sys_format_help(helpout, flags | GF_PRINTARG_OPT_DESC, ": %s", gf_sys_localized(arg_subsystem, arg->name, arg->description) );
1489 }
1490 gf_sys_format_help(helpout, flags, "\n");
1491 }
1492
1493 if ((gen_doc==1) && arg->description && strstr(arg->description, "- "))
1494 gf_sys_format_help(helpout, flags, "\n");
1495 }
1496
1497
1498 GF_EXPORT
gf_sys_print_core_help(FILE * helpout,u32 flags,GF_SysArgMode mode,u32 subsystem_flags)1499 void gf_sys_print_core_help(FILE *helpout, u32 flags, GF_SysArgMode mode, u32 subsystem_flags)
1500 {
1501 u32 i=0;
1502 const GF_GPACArg *args = gf_sys_get_options();
1503
1504 while (args[i].name) {
1505 const GF_GPACArg *arg = &args[i];
1506 i++;
1507 if (arg->flags & GF_ARG_HINT_HIDE) continue;
1508
1509 if (subsystem_flags && !(arg->flags & subsystem_flags)) {
1510 continue;
1511 }
1512 if (mode != GF_ARGMODE_ALL) {
1513 if ((mode==GF_ARGMODE_EXPERT) && !(arg->flags & GF_ARG_HINT_EXPERT)) continue;
1514 else if ((mode==GF_ARGMODE_ADVANCED) && !(arg->flags & GF_ARG_HINT_ADVANCED)) continue;
1515 else if ((mode==GF_ARGMODE_BASE) && (arg->flags & (GF_ARG_HINT_ADVANCED|GF_ARG_HINT_EXPERT) )) continue;
1516 }
1517 gf_sys_print_arg(helpout, flags, arg, "core");
1518 }
1519 }
1520
1521
1522 #define LINE_OFFSET_DESCR 30
1523
1524 static char *help_buf = NULL;
1525 static u32 help_buf_size=0;
1526
gf_sys_cleanup_help()1527 void gf_sys_cleanup_help()
1528 {
1529 if (help_buf) gf_free(help_buf);
1530 }
1531
1532
1533 enum
1534 {
1535 TOK_CODE,
1536 TOK_BOLD,
1537 TOK_ITALIC,
1538 TOK_STRIKE,
1539 TOK_OPTLINK,
1540 TOK_LINKSTART,
1541 };
1542 struct _token {
1543 char *tok;
1544 GF_ConsoleCodes cmd_type;
1545 } Tokens[] =
1546 {
1547 {"`", GF_CONSOLE_YELLOW|GF_CONSOLE_ITALIC},
1548 {"**", GF_CONSOLE_BOLD},
1549 {"__", GF_CONSOLE_ITALIC},
1550 {"~~", GF_CONSOLE_STRIKE},
1551 {"[-", GF_CONSOLE_YELLOW|GF_CONSOLE_ITALIC},
1552 {"[", GF_CONSOLE_YELLOW|GF_CONSOLE_ITALIC},
1553 };
1554 static u32 nb_tokens = sizeof(Tokens) / sizeof(struct _token);
1555
1556 static u32 line_pos = 0;
1557
1558 GF_EXPORT
gf_sys_format_help(FILE * helpout,u32 flags,const char * fmt,...)1559 void gf_sys_format_help(FILE *helpout, u32 flags, const char *fmt, ...)
1560 {
1561 char *line;
1562 u32 len;
1563 va_list vlist;
1564 Bool escape_xml = GF_FALSE;
1565 Bool escape_pipe = GF_FALSE;
1566 u32 gen_doc = 0;
1567 u32 is_app_opts = 0;
1568 if (flags & GF_PRINTARG_MD) {
1569 gen_doc = 1;
1570 if (flags & GF_PRINTARG_ESCAPE_XML)
1571 escape_xml = GF_TRUE;
1572 if (flags & GF_PRINTARG_ESCAPE_PIPE)
1573 escape_pipe = GF_TRUE;
1574 }
1575 if (flags & GF_PRINTARG_MAN)
1576 gen_doc = 2;
1577 if (flags & GF_PRINTARG_IS_APP)
1578 is_app_opts = 1;
1579 if (!helpout) helpout = stderr;
1580
1581 va_start(vlist, fmt);
1582 len=vsnprintf(NULL, 0, fmt, vlist);
1583 va_end(vlist);
1584 if (help_buf_size < len+2) {
1585 help_buf_size = len+2;
1586 help_buf = gf_realloc(help_buf, help_buf_size);
1587 }
1588 va_start(vlist, fmt);
1589 vsprintf(help_buf, fmt, vlist);
1590 va_end(vlist);
1591
1592
1593 line = help_buf;
1594 while (line[0]) {
1595 u32 att_len = 0;
1596 char *tok_sep = NULL;
1597 GF_ConsoleCodes console_code = GF_CONSOLE_RESET;
1598 Bool line_before = GF_FALSE;
1599 Bool line_after = GF_FALSE;
1600 const char *footer_string = NULL;
1601 const char *header_string = NULL;
1602 char *next_line = strchr(line, '\n');
1603 Bool has_token=GF_FALSE;
1604
1605 if (next_line) next_line[0]=0;
1606
1607
1608 if ((line[0]=='#') && (line[1]==' ')) {
1609 if (!gen_doc)
1610 line+=2;
1611 else if (gen_doc==2) {
1612 header_string = ".SH ";
1613 footer_string = "\n.LP";
1614 line+=2;
1615 }
1616
1617 console_code = GF_CONSOLE_GREEN;
1618 line_after = line_before = GF_TRUE;
1619 } else if ((line[0]=='#') && (line[1]=='#') && (line[2]==' ')) {
1620 if (!gen_doc)
1621 line+=3;
1622 else if (gen_doc==2) {
1623 line+=3;
1624 header_string = ".P\n.B\n";
1625 }
1626
1627 console_code = GF_CONSOLE_MAGENTA;
1628 line_before = GF_TRUE;
1629 } else if ((line[0]=='E') && (line[1]=='X') && (line[2]==' ')) {
1630 line+=3;
1631 console_code = GF_CONSOLE_YELLOW;
1632 if (gen_doc==1) {
1633 header_string = "Example\n```\n";
1634 footer_string = "\n```";
1635 } else if (gen_doc==2) {
1636 header_string = "Example\n.br\n";
1637 footer_string = "\n.br\n";
1638 } else {
1639 header_string = "Example:\n";
1640 }
1641 } else if (!strncmp(line, "Note: ", 6)) {
1642 console_code = GF_CONSOLE_CYAN | GF_CONSOLE_ITALIC;
1643 } else if (!strncmp(line, "Warning: ", 9)) {
1644 line_after = line_before = GF_TRUE;
1645 console_code = GF_CONSOLE_RED | GF_CONSOLE_BOLD;
1646 } else if ( (
1647 ((line[0]=='-') && (line[1]==' '))
1648 || ((line[0]==' ') && (line[1]=='-') && (line[2]==' '))
1649 || ((line[0]==' ') && (line[1]==' ') && (line[2]=='-') && (line[3]==' '))
1650 )
1651
1652 //look for ": "
1653 && ((tok_sep=strstr(line, ": ")) != NULL )
1654 ) {
1655 if (!gen_doc)
1656 fprintf(helpout, "\t");
1657 while (line[0] != '-') {
1658 fprintf(helpout, " ");
1659 line++;
1660 line_pos++;
1661
1662 }
1663 fprintf(helpout, "* ");
1664 line_pos+=2;
1665 if (!gen_doc)
1666 gf_sys_set_console_code(helpout, GF_CONSOLE_YELLOW);
1667 tok_sep[0] = 0;
1668 fprintf(helpout, "%s", line+2);
1669 line_pos += (u32) strlen(line+2);
1670 tok_sep[0] = ':';
1671 line = tok_sep;
1672 if (!gen_doc)
1673 gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
1674 } else if (flags & (GF_PRINTARG_HIGHLIGHT_FIRST | GF_PRINTARG_OPT_DESC)) {
1675 char *sep = strchr(line, ' ');
1676
1677 if (sep) sep[0] = 0;
1678
1679 if (!gen_doc && !(flags & GF_PRINTARG_OPT_DESC) )
1680 gf_sys_set_console_code(helpout, GF_CONSOLE_GREEN);
1681
1682 if ((gen_doc==1) && !(flags & GF_PRINTARG_OPT_DESC) ) {
1683 fprintf(helpout, "__%s__", line);
1684 line_pos += 4+ (u32) strlen(line);
1685 } else {
1686 fprintf(helpout, "%s", line);
1687 line_pos += (u32) strlen(line);
1688 }
1689
1690 if (!gen_doc && !(flags & GF_PRINTARG_OPT_DESC) )
1691 gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
1692
1693 if (flags & GF_PRINTARG_OPT_DESC) {
1694 flags = 0;
1695 att_len = line_pos;
1696 }
1697
1698
1699 if (sep) {
1700 sep[0] = ' ';
1701 line = sep;
1702 } else {
1703 line = NULL;
1704 }
1705 }
1706 if (!line) break;
1707 if (gen_doc==2) {
1708 line_before = line_after = GF_FALSE;
1709 }
1710 if (line_before) {
1711 fprintf(helpout, "\n");
1712 line_pos=0;
1713 }
1714
1715 if (console_code != GF_CONSOLE_RESET) {
1716 if (gen_doc==1) {
1717 if (console_code & GF_CONSOLE_BOLD) {
1718 fprintf(helpout, "__");
1719 line_pos+=2;
1720 }
1721 else if (console_code & GF_CONSOLE_ITALIC) {
1722 fprintf(helpout, "_");
1723 line_pos++;
1724 }
1725 } else if (!gen_doc) {
1726 gf_sys_set_console_code(helpout, console_code);
1727 }
1728 }
1729
1730 if (att_len) {
1731 while (att_len < LINE_OFFSET_DESCR) {
1732 fprintf(helpout, " ");
1733 att_len++;
1734 line_pos++;
1735 }
1736 }
1737
1738
1739 if (header_string) {
1740 fprintf(helpout, "%s", header_string);
1741 line_pos += (u32) strlen(header_string);
1742 }
1743
1744 while (line) {
1745 char *skip_url = NULL;
1746 char *link_start = NULL;
1747 u32 tid=0, i;
1748 char *next_token = NULL;
1749 for (i=0; i<nb_tokens; i++) {
1750 char *tok = strstr(line, Tokens[i].tok);
1751 if (!tok) continue;
1752 if (next_token && ((next_token-line) < (tok-line)) ) continue;
1753 //check we have an end of token, otherwise consider this regular text
1754 if ((i == TOK_LINKSTART) || (i == TOK_OPTLINK)) {
1755 char *end_tok = strstr(tok, "](");
1756 if (!end_tok) continue;
1757 }
1758
1759 if (i == TOK_LINKSTART) {
1760 if (gen_doc!=1) {
1761 char *link_end;
1762 skip_url = strstr(tok, "](");
1763 link_end = skip_url;
1764 if (skip_url) skip_url = strstr(skip_url, ")");
1765 if (skip_url) skip_url ++;
1766
1767 if (!skip_url) continue;
1768 link_start = tok+1;
1769 link_end[0] = 0;
1770 } else {
1771 continue;
1772 }
1773 }
1774 next_token=tok;
1775 tid=i;
1776 }
1777 if (next_token) {
1778 next_token[0]=0;
1779 }
1780 if ((gen_doc==1) && has_token) {
1781 if (tid==TOK_CODE) {
1782 fprintf(helpout, "`%s`", line);
1783 line_pos+=2;
1784 } else if (tid==TOK_ITALIC) {
1785 fprintf(helpout, "_%s_", line);
1786 line_pos+=2;
1787 } else if (tid==TOK_BOLD) {
1788 fprintf(helpout, "__%s__", line);
1789 line_pos+=4;
1790 } else {
1791 fprintf(helpout, "%s", line);
1792 }
1793 } else if (escape_xml) {
1794 char *xml_line = line;
1795 while (xml_line) {
1796 char *xml_start = strchr(xml_line, '<');
1797 char *xml_end = strchr(xml_line, '>');
1798
1799 if (xml_end && (xml_start > xml_end)) xml_start = xml_end;
1800 else if (!xml_start && xml_end) xml_start = xml_end;
1801 else if (xml_start && xml_end) xml_end = NULL;
1802
1803 if (xml_start) {
1804 u8 c = xml_start[0];
1805 xml_start[0] = 0;
1806 fprintf(helpout, "%s", xml_line);
1807 fprintf(helpout, xml_end ? ">" : "<");
1808 xml_start[0] = c;
1809 xml_line = xml_start+1;
1810 } else {
1811 fprintf(helpout, "%s", xml_line);
1812 break;
1813 }
1814 }
1815 } else if (escape_pipe) {
1816 char *src_line = line;
1817 while (src_line) {
1818 char *pipe_start = strchr(src_line, '|');
1819 if (pipe_start && (pipe_start[1]==' '))
1820 pipe_start = NULL;
1821
1822 if (pipe_start) {
1823 pipe_start[0] = 0;
1824 fprintf(helpout, "%s,", src_line);
1825 pipe_start[0] = '|';
1826 src_line = pipe_start+1;
1827 } else {
1828 fprintf(helpout, "%s", src_line);
1829 break;
1830 }
1831 }
1832 } else {
1833 fprintf(helpout, "%s", line);
1834 }
1835 line_pos+=(u32) strlen(line);
1836
1837 if (!next_token) break;
1838 has_token = !has_token;
1839
1840 if (!gen_doc) {
1841 if (has_token) {
1842 u32 cmd;
1843 if (Tokens[tid].cmd_type & 0xFFFF) {
1844 cmd = Tokens[tid].cmd_type;
1845 } else {
1846 cmd = Tokens[tid].cmd_type | console_code;
1847 }
1848
1849 if (console_code&GF_CONSOLE_ITALIC) {
1850 if (Tokens[tid].cmd_type & GF_CONSOLE_ITALIC) {
1851 cmd &= ~GF_CONSOLE_ITALIC;
1852 cmd |= GF_CONSOLE_BOLD;
1853 }
1854 }
1855 else if (console_code&GF_CONSOLE_BOLD) {
1856 if (Tokens[tid].cmd_type & GF_CONSOLE_BOLD) {
1857 cmd &= ~GF_CONSOLE_BOLD;
1858 cmd |= GF_CONSOLE_ITALIC;
1859 }
1860 }
1861 gf_sys_set_console_code(helpout, cmd);
1862 } else {
1863 gf_sys_set_console_code(helpout, console_code);
1864 }
1865 }
1866 line = next_token + (u32) strlen(Tokens[tid].tok);
1867
1868 if (skip_url) {
1869 if (link_start)
1870 fprintf(helpout, "%s", link_start);
1871 if (!gen_doc)
1872 gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
1873 has_token = GF_FALSE;
1874 line = skip_url;
1875
1876 }
1877
1878 if (has_token && tid==TOK_OPTLINK) {
1879 char *link = strchr(line, '(');
1880 assert(link);
1881 link++;
1882 char *end_link = strchr(line, ')');
1883 if (end_link) end_link[0] = 0;
1884 char *end_tok = strchr(line, ']');
1885 if (end_tok) end_tok[0] = 0;
1886
1887 if (gen_doc==1) {
1888 if (!strncmp(link, "GPAC", 4)) {
1889 fprintf(helpout, "[-%s](gpac_general/#%s)", line, line);
1890 line_pos+=7 + 2*(u32)strlen(line) + (u32)strlen("gpac_general");
1891 } else if (!strncmp(link, "LOG", 3)) {
1892 fprintf(helpout, "[-%s](core_logs/#%s)", line, line);
1893 line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen("core_logs");
1894 } else if (!strncmp(link, "CORE", 3)) {
1895 fprintf(helpout, "[-%s](core_options/#%s)", line, line);
1896 line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen("core_options");
1897 line_pos+=7 + 2*(u32)strlen(line) + (u32)strlen("core_options");
1898 } else if (!strncmp(link, "CFG", 3)) {
1899 fprintf(helpout, "[-%s](core_config/#%s)", line, line);
1900 line_pos+=7 + 2*(u32)strlen(line) + (u32)strlen("core_config");
1901 } else if (!strncmp(link, "MP4B_GEN", 3)) {
1902 fprintf(helpout, "[-%s](mp4box-gen-opts/#%s)", line, line);
1903 line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen("mp4box-gen-opts");
1904 } else if (strlen(link)) {
1905 fprintf(helpout, "[-%s](%s/#%s)", line, link, line);
1906 line_pos+=7 + 2* (u32)strlen(line) + (u32)strlen(link);
1907 } else if (is_app_opts || !strcmp(line, "i") || !strcmp(line, "o") || !strcmp(line, "h")) {
1908 fprintf(helpout, "[-%s](#%s)", line, line);
1909 line_pos+=5 + 2* (u32)strlen(line) + (u32)strlen(link);
1910 } else {
1911 //this is a filter opt, don't print '-'
1912 fprintf(helpout, "[%s](#%s)", line, line);
1913 line_pos+=4 + 2* (u32)strlen(line) + (u32)strlen(link);
1914 }
1915 } else {
1916 if (gen_doc==2)
1917 fprintf(helpout, ".I ");
1918
1919 if (!strncmp(link, "GPAC", 4)
1920 || !strncmp(link, "LOG", 3)
1921 || !strncmp(link, "CORE", 3)
1922 || strlen(link)
1923 || !strcmp(line, "i") || !strcmp(line, "o") || !strcmp(line, "h")
1924 ) {
1925 fprintf(helpout, "-%s", line);
1926 line_pos+=1+ (u32)strlen(line);
1927 } else {
1928 fprintf(helpout, "%s", line);
1929 line_pos+= (u32)strlen(line);
1930 }
1931 if (!gen_doc)
1932 gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
1933 }
1934 if (!end_link) break;
1935 line = end_link+1;
1936 has_token = GF_FALSE;
1937 }
1938 }
1939
1940 if (has_token && !gen_doc)
1941 gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
1942
1943 if (footer_string) {
1944 fprintf(helpout, "%s", footer_string);
1945 line_pos += (u32) strlen(footer_string);
1946 }
1947 if (console_code != GF_CONSOLE_RESET) {
1948 if (gen_doc==1) {
1949 if (console_code & GF_CONSOLE_BOLD) {
1950 fprintf(helpout, "__");
1951 line_pos+=2;
1952 } else if (console_code & GF_CONSOLE_ITALIC) {
1953 fprintf(helpout, "_");
1954 line_pos++;
1955 }
1956 } else if (!gen_doc) {
1957 gf_sys_set_console_code(helpout, GF_CONSOLE_RESET);
1958 }
1959 }
1960
1961 if (line_after) {
1962 if (gen_doc==1) fprintf(helpout, " ");
1963 fprintf(helpout, (flags & GF_PRINTARG_NL_TO_BR) ? "<br/>" : "\n");
1964 line_pos=0;
1965 }
1966
1967 if (!next_line) break;
1968 next_line[0]=0;
1969 if (gen_doc==1) fprintf(helpout, " ");
1970 line = next_line+1;
1971 if (gen_doc==2) {
1972 if (line[0] != '.')
1973 fprintf(helpout, "\n.br\n");
1974 else
1975 fprintf(helpout, "\n");
1976 } else
1977 fprintf(helpout, (line[0] && (flags & GF_PRINTARG_NL_TO_BR)) ? "<br/>" : "\n");
1978 line_pos=0;
1979 }
1980 }
1981
1982
1983 GF_EXPORT
gf_sys_word_match(const char * orig,const char * dst)1984 Bool gf_sys_word_match(const char *orig, const char *dst)
1985 {
1986 s32 dist = 0;
1987 u32 match = 0;
1988 u32 i;
1989 u32 olen = (u32) strlen(orig);
1990 u32 dlen = (u32) strlen(dst);
1991 u32 *run;
1992 if (olen*2 < dlen) {
1993 char *s1 = strchr(orig, ':');
1994 char *s2 = strchr(dst, ':');
1995 if (s1 && !s2) return GF_FALSE;
1996 if (!s1 && s2) return GF_FALSE;
1997
1998 if (strstr(dst, orig))
1999 return GF_TRUE;
2000 return GF_FALSE;
2001 }
2002 run = gf_malloc(sizeof(u32) * olen);
2003 memset(run, 0, sizeof(u32) * olen);
2004
2005 for (i=0; i<dlen; i++) {
2006 u32 dist_char;
2007 u32 offset=0;
2008 char *pos;
2009
2010 retry_char:
2011 pos = strchr(orig+offset, dst[i]);
2012 if (!pos) continue;
2013 dist_char = (u32) (pos - orig);
2014 if (!run[dist_char]) {
2015 run[dist_char] = i+1;
2016 match++;
2017 } else if (run[dist_char] > i) {
2018 run[dist_char] = i+1;
2019 match++;
2020 } else {
2021 //this can be a repeated character
2022 offset++;
2023 goto retry_char;
2024
2025 }
2026 }
2027 if (match*2<olen) {
2028 gf_free(run);
2029 return GF_FALSE;
2030 }
2031 //if 4/5 of characters are matched, suggest it
2032 if (match * 5 >= 4 * dlen ) {
2033 gf_free(run);
2034 return GF_TRUE;
2035 }
2036 /* if ((olen<=4) && (match>=3) && (dlen*2<olen*3) ) {
2037 gf_free(run);
2038 return GF_TRUE;
2039 }
2040 */
2041 for (i=0; i<olen; i++) {
2042 if (!i) {
2043 if (run[0]==1)
2044 dist++;
2045 } else if (run[i-1] + 1 == run[i]) {
2046 dist++;
2047 }
2048 }
2049 gf_free(run);
2050 //if half the characters are in order, consider a match
2051 //if arg is small only check dst
2052 if ((olen<=4) && (dist >= 2))
2053 return GF_TRUE;
2054 if ((dist*2 >= (s32) olen) && (dist*2 >= (s32) dlen))
2055 return GF_TRUE;
2056 return GF_FALSE;
2057 }
2058
2059 #endif
2060