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 ? "&gt;" : "&lt;");
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