Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2012
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / common tools sub-project
9 : *
10 : * GPAC is free software; you can redistribute it and/or modify
11 : * it under the terms of the GNU Lesser General Public License as published by
12 : * the Free Software Foundation; either version 2, or (at your option)
13 : * any later version.
14 : *
15 : * GPAC is distributed in the hope that it will be useful,
16 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : * GNU Lesser General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Lesser General Public
21 : * License along with this library; see the file COPYING. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 : #include <gpac/config_file.h>
27 : #include <gpac/list.h>
28 :
29 : #define MAX_INI_LINE 2046
30 :
31 : typedef struct
32 : {
33 : Bool do_restrict;
34 : char *name;
35 : char *value;
36 : } IniKey;
37 :
38 : typedef struct
39 : {
40 : char *section_name;
41 : GF_List *keys;
42 : } IniSection;
43 :
44 : struct __tag_config
45 : {
46 : char *fileName;
47 : GF_List *sections;
48 : Bool hasChanged, skip_changes;
49 : };
50 :
51 :
52 47166 : static void DelSection(IniSection *ptr)
53 : {
54 47166 : if (!ptr) return;
55 47166 : if (ptr->keys) {
56 365385 : while (gf_list_count(ptr->keys)) {
57 318219 : IniKey *k = (IniKey *) gf_list_get(ptr->keys, 0);
58 318219 : if (k->value) gf_free(k->value);
59 318219 : if (k->name) gf_free(k->name);
60 318219 : gf_free(k);
61 318219 : gf_list_rem(ptr->keys, 0);
62 : }
63 47166 : gf_list_del(ptr->keys);
64 : }
65 47166 : if (ptr->section_name) gf_free(ptr->section_name);
66 47166 : gf_free(ptr);
67 : }
68 :
69 : /*!
70 : * \brief Clear the structure
71 : \param iniFile The structure to clear
72 : */
73 31375 : static void gf_cfg_clear(GF_Config * iniFile)
74 : {
75 31375 : if (!iniFile) return;
76 31375 : if (iniFile->sections) {
77 65957 : while (gf_list_count(iniFile->sections)) {
78 47166 : IniSection *p = (IniSection *) gf_list_get(iniFile->sections, 0);
79 47166 : DelSection(p);
80 47166 : gf_list_rem(iniFile->sections, 0);
81 : }
82 18791 : gf_list_del(iniFile->sections);
83 : }
84 31375 : if (iniFile->fileName)
85 18791 : gf_free(iniFile->fileName);
86 : memset((void *)iniFile, 0, sizeof(GF_Config));
87 : }
88 :
89 : /*!
90 : * \brief Parses the config file if any and clears the existing structure
91 : */
92 12584 : GF_Err gf_cfg_parse_config_file(GF_Config * tmp, const char * filePath, const char * file_name)
93 : {
94 : IniSection *p;
95 : IniKey *k=NULL;
96 : FILE *file;
97 : char *line;
98 : u32 line_alloc = MAX_INI_LINE;
99 : char fileName[GF_MAX_PATH];
100 : Bool line_done = GF_FALSE;
101 : u32 nb_empty_lines=0;
102 : Bool is_opts_probe = GF_FALSE;
103 : Bool in_multiline = GF_FALSE;;
104 12584 : gf_cfg_clear(tmp);
105 :
106 12584 : if (strstr(file_name, "/all_opts.txt")) {
107 26 : if (filePath && !strcmp(filePath, "probe")) {
108 : filePath = NULL;
109 : is_opts_probe = GF_TRUE;
110 : }
111 : }
112 :
113 12558 : if (filePath && ((filePath[strlen(filePath)-1] == '/') || (filePath[strlen(filePath)-1] == '\\')) ) {
114 : strcpy(fileName, filePath);
115 : strcat(fileName, file_name);
116 12519 : } else if (filePath) {
117 : sprintf(fileName, "%s%c%s", filePath, GF_PATH_SEPARATOR, file_name);
118 : } else {
119 : strcpy(fileName, file_name);
120 : }
121 :
122 12584 : tmp->fileName = gf_strdup(fileName);
123 12584 : tmp->sections = gf_list_new();
124 12584 : file = gf_fopen(fileName, "rt");
125 12584 : if (!file)
126 : return GF_IO_ERR;
127 : /* load the file */
128 : p = NULL;
129 6316 : line = (char*)gf_malloc(sizeof(char)*line_alloc);
130 : memset(line, 0, sizeof(char)*line_alloc);
131 :
132 : #define FLUSH_EMPTY_LINES \
133 : if (k&& nb_empty_lines) {\
134 : u32 klen = (u32) strlen(k->value)+1+nb_empty_lines;\
135 : k->value = gf_realloc(k->value, sizeof(char)*klen);\
136 : while (nb_empty_lines) {\
137 : nb_empty_lines--;\
138 : strcat(k->value, "\n");\
139 : }\
140 : k=NULL;\
141 : }\
142 : nb_empty_lines=0;\
143 :
144 114134 : while (!gf_feof(file)) {
145 : char *ret;
146 : u32 read, nb_pass;
147 : char *fsep;
148 107842 : ret = gf_fgets(line, line_alloc, file);
149 107842 : read = (u32) strlen(line);
150 :
151 : nb_pass = 1;
152 215684 : while (read + nb_pass == line_alloc) {
153 0 : line_alloc += MAX_INI_LINE;
154 0 : line = (char*)gf_realloc(line, sizeof(char)*line_alloc);
155 0 : ret = gf_fgets(line+read, MAX_INI_LINE, file);
156 0 : read = (u32) strlen(line);
157 0 : nb_pass++;
158 : }
159 107842 : if (!ret) continue;
160 :
161 : /* get rid of the end of line stuff */
162 101550 : fsep = strchr(line, '=');
163 101550 : if (!fsep || (fsep[1]!='@')) {
164 203100 : while (!in_multiline) {
165 203100 : u32 len = (u32) strlen(line);
166 203100 : if (!len) break;
167 203100 : if ((line[len-1] != '\n') && (line[len-1] != '\r')) break;
168 101550 : line[len-1] = 0;
169 : }
170 : }
171 101550 : if (!strlen(line)) {
172 0 : if (k) nb_empty_lines++;
173 0 : continue;
174 : }
175 101550 : if (line[0] == '#') {
176 0 : FLUSH_EMPTY_LINES
177 0 : continue;
178 : }
179 :
180 : /* new section */
181 101550 : if (line[0] == '[') {
182 : //compat with old arch
183 12691 : if (nb_empty_lines) nb_empty_lines--;
184 :
185 12691 : FLUSH_EMPTY_LINES
186 :
187 12691 : p = (IniSection *) gf_malloc(sizeof(IniSection));
188 12691 : p->keys = gf_list_new();
189 12691 : p->section_name = gf_strdup(line + 1);
190 12691 : p->section_name[strlen(line) - 2] = 0;
191 12691 : while (p->section_name[strlen(p->section_name) - 1] == ']' || p->section_name[strlen(p->section_name) - 1] == ' ') p->section_name[strlen(p->section_name) - 1] = 0;
192 12691 : gf_list_add(tmp->sections, p);
193 : }
194 88859 : else if (strlen(line) && !in_multiline && (strchr(line, '=') != NULL) ) {
195 88859 : if (!p) {
196 0 : gf_fclose(file);
197 0 : gf_free(line);
198 0 : gf_cfg_clear(tmp);
199 0 : return GF_IO_ERR;
200 : }
201 88859 : FLUSH_EMPTY_LINES
202 :
203 88859 : k = (IniKey *) gf_malloc(sizeof(IniKey));
204 : memset((void *)k, 0, sizeof(IniKey));
205 88859 : ret = strchr(line, '=');
206 88859 : if (ret) {
207 88859 : ret[0] = 0;
208 88859 : k->name = gf_strdup(line);
209 88859 : while (k->name[strlen(k->name) - 1] == ' ') k->name[strlen(k->name) - 1] = 0;
210 88859 : ret[0] = '=';
211 88859 : ret += 1;
212 88859 : while (ret[0] == ' ') ret++;
213 88859 : if (ret[0]=='@') {
214 0 : ret++;
215 : in_multiline = GF_TRUE;
216 : }
217 88859 : if ( ret[0] != 0) {
218 88859 : k->value = gf_strdup(ret);
219 88859 : while (k->value[strlen(k->value) - 1] == ' ') k->value[strlen(k->value) - 1] = 0;
220 : } else {
221 0 : k->value = gf_strdup("");
222 : }
223 : line_done = GF_FALSE;
224 : }
225 88859 : gf_list_add(p->keys, k);
226 : if (line_done) k=NULL;
227 : line_done=GF_FALSE;
228 :
229 88859 : if (is_opts_probe) {
230 24 : if (!strcmp(p->section_name, "version") && !strcmp(k->name, "version")) {
231 : break;
232 : }
233 : }
234 :
235 0 : } else if (k) {
236 0 : u32 l1 = (u32) strlen(k->value);
237 0 : u32 l2 = (u32) strlen(line);
238 0 : k->value = gf_realloc(k->value, sizeof(char) * (l1 + l2 + 1 + nb_empty_lines) );
239 0 : l2 += l1 + nb_empty_lines;
240 0 : while (nb_empty_lines) {
241 0 : strcat(k->value, "\n");
242 0 : nb_empty_lines--;
243 : }
244 0 : strcat(k->value, line);
245 0 : l2 -= 2; //@ and \n
246 0 : if (k->value[l2] == '@') {
247 0 : k->value[l2] = 0;
248 : k=NULL;
249 : in_multiline = GF_FALSE;
250 : }
251 : } else {
252 : k = NULL;
253 : }
254 : }
255 6316 : gf_free(line);
256 6316 : gf_fclose(file);
257 6316 : return GF_OK;
258 : }
259 :
260 : GF_EXPORT
261 96 : GF_Config *gf_cfg_force_new(const char *filePath, const char* file_name) {
262 96 : GF_Config *tmp = (GF_Config *)gf_malloc(sizeof(GF_Config));
263 : memset((void *)tmp, 0, sizeof(GF_Config));
264 96 : gf_cfg_parse_config_file(tmp, filePath, file_name);
265 96 : return tmp;
266 : }
267 :
268 :
269 : GF_EXPORT
270 18697 : GF_Config *gf_cfg_new(const char *filePath, const char* file_name)
271 : {
272 18697 : GF_Config *tmp = (GF_Config *)gf_malloc(sizeof(GF_Config));
273 : memset((void *)tmp, 0, sizeof(GF_Config));
274 18697 : if (!filePath && !file_name) {
275 6209 : tmp->sections = gf_list_new();
276 6209 : return tmp;
277 : }
278 :
279 12488 : if (gf_cfg_parse_config_file(tmp, filePath, file_name)) {
280 6223 : gf_cfg_clear(tmp);
281 6223 : gf_free(tmp);
282 : tmp = NULL;
283 : }
284 : return tmp;
285 : }
286 :
287 : GF_EXPORT
288 55 : const char * gf_cfg_get_filename(GF_Config *iniFile)
289 : {
290 55 : if (!iniFile)
291 : return NULL;
292 55 : return iniFile->fileName;
293 : }
294 :
295 : GF_EXPORT
296 12710 : GF_Err gf_cfg_save(GF_Config *iniFile)
297 : {
298 : u32 i, j;
299 : IniSection *sec;
300 : IniKey *key;
301 : FILE *file;
302 :
303 12710 : if (!iniFile->hasChanged) return GF_OK;
304 6366 : if (iniFile->skip_changes) return GF_OK;
305 157 : if (!iniFile->fileName) return GF_OK;
306 :
307 157 : file = gf_fopen(iniFile->fileName, "wt");
308 157 : if (!file) return GF_IO_ERR;
309 :
310 157 : i=0;
311 625 : while ( (sec = (IniSection *) gf_list_enum(iniFile->sections, &i)) ) {
312 : /*Temporary sections are not saved*/
313 311 : if (!strnicmp(sec->section_name, "temp", 4)) continue;
314 :
315 310 : gf_fprintf(file, "[%s]\n", sec->section_name);
316 310 : j=0;
317 7085 : while ( (key = (IniKey *) gf_list_enum(sec->keys, &j)) ) {
318 6465 : if (strchr(key->value, '\n')) {
319 160 : gf_fprintf(file, "%s=@%s@\n", key->name, key->value);
320 : } else {
321 6305 : gf_fprintf(file, "%s=%s\n", key->name, key->value);
322 : }
323 : }
324 : }
325 157 : gf_fclose(file);
326 157 : return GF_OK;
327 : }
328 :
329 : GF_EXPORT
330 6212 : GF_Err gf_cfg_discard_changes(GF_Config *iniFile)
331 : {
332 6212 : if (!iniFile) return GF_BAD_PARAM;
333 6212 : iniFile->skip_changes = GF_TRUE;
334 6212 : return GF_OK;
335 : }
336 :
337 : GF_EXPORT
338 12568 : void gf_cfg_del(GF_Config *iniFile)
339 : {
340 12568 : if (!iniFile) return;
341 12568 : gf_cfg_save(iniFile);
342 12568 : gf_cfg_clear(iniFile);
343 12568 : gf_free(iniFile);
344 : }
345 :
346 : #if 0 //unused
347 : void gf_cfg_remove(GF_Config *iniFile)
348 : {
349 : if (!iniFile) return;
350 : gf_file_delete(iniFile->fileName);
351 : gf_cfg_clear(iniFile);
352 : gf_free(iniFile);
353 : }
354 : #endif
355 :
356 708544 : const char *gf_cfg_get_key_internal(GF_Config *iniFile, const char *secName, const char *keyName, Bool restricted_only)
357 : {
358 : u32 i;
359 : IniSection *sec;
360 : IniKey *key;
361 :
362 708544 : i=0;
363 4163633 : while ( (sec = (IniSection *) gf_list_enum(iniFile->sections, &i)) ) {
364 3160694 : if (!strcmp(secName, sec->section_name)) goto get_key;
365 : }
366 : return NULL;
367 :
368 414149 : get_key:
369 414149 : i=0;
370 8153502 : while ( (key = (IniKey *) gf_list_enum(sec->keys, &i)) ) {
371 7484529 : if (!strcmp(key->name, keyName)) {
372 159325 : if (!restricted_only || key->do_restrict)
373 159322 : return key->value;
374 : return NULL;
375 : }
376 : }
377 : return NULL;
378 : }
379 : GF_EXPORT
380 690484 : const char *gf_cfg_get_key(GF_Config *iniFile, const char *secName, const char *keyName)
381 : {
382 690484 : return gf_cfg_get_key_internal(iniFile, secName, keyName, GF_FALSE);
383 : }
384 :
385 : #if 0 //unused
386 : const char *gf_cfg_get_ikey(GF_Config *iniFile, const char *secName, const char *keyName)
387 : {
388 : u32 i;
389 : IniSection *sec;
390 : IniKey *key;
391 :
392 : i=0;
393 : while ( (sec = (IniSection *) gf_list_enum(iniFile->sections, &i)) ) {
394 : if (!stricmp(secName, sec->section_name)) goto get_key;
395 : }
396 : return NULL;
397 :
398 : get_key:
399 : i=0;
400 : while ( (key = (IniKey *) gf_list_enum(sec->keys, &i)) ) {
401 : if (!stricmp(key->name, keyName)) return key->value;
402 : }
403 : return NULL;
404 : }
405 : #endif
406 :
407 240439 : GF_Err gf_cfg_set_key_internal(GF_Config *iniFile, const char *secName, const char *keyName, const char *keyValue, Bool is_restrict)
408 : {
409 : u32 i;
410 : Bool has_changed = GF_TRUE;
411 : IniSection *sec;
412 : IniKey *key;
413 :
414 240439 : if (!iniFile || !secName || !keyName) return GF_BAD_PARAM;
415 :
416 240439 : if (!strnicmp(secName, "temp", 4)) has_changed = GF_FALSE;
417 :
418 240439 : i=0;
419 1183333 : while ((sec = (IniSection *) gf_list_enum(iniFile->sections, &i)) ) {
420 908409 : if (!strcmp(secName, sec->section_name)) goto get_key;
421 : }
422 : /* need a new key */
423 34485 : sec = (IniSection *) gf_malloc(sizeof(IniSection));
424 34485 : sec->section_name = gf_strdup(secName);
425 34485 : sec->keys = gf_list_new();
426 34485 : if (has_changed)
427 32703 : iniFile->hasChanged = GF_TRUE;
428 34485 : gf_list_add(iniFile->sections, sec);
429 :
430 446393 : get_key:
431 240439 : i=0;
432 6321617 : while ( (key = (IniKey *) gf_list_enum(sec->keys, &i) ) ) {
433 5851723 : if (!strcmp(key->name, keyName)) goto set_value;
434 : }
435 229455 : if (!keyValue) return GF_OK;
436 : /* need a new key */
437 229443 : key = (IniKey *) gf_malloc(sizeof(IniKey));
438 229443 : key->name = gf_strdup(keyName);
439 229443 : key->value = gf_strdup("");
440 229443 : if (has_changed)
441 226805 : iniFile->hasChanged = GF_TRUE;
442 229443 : gf_list_add(sec->keys, key);
443 :
444 251411 : set_value:
445 240427 : if (!keyValue) {
446 0 : gf_list_del_item(sec->keys, key);
447 0 : if (key->name) gf_free(key->name);
448 0 : if (key->value) gf_free(key->value);
449 0 : gf_free(key);
450 0 : if (has_changed)
451 0 : iniFile->hasChanged = GF_TRUE;
452 : return GF_OK;
453 : }
454 240427 : key->do_restrict = is_restrict;
455 : /* same value, don't update */
456 240427 : if (!strcmp(key->value, keyValue)) return GF_OK;
457 :
458 233769 : if (key->value) gf_free(key->value);
459 233769 : key->value = gf_strdup(keyValue);
460 233769 : if (has_changed)
461 231120 : iniFile->hasChanged = GF_TRUE;
462 : return GF_OK;
463 : }
464 :
465 : GF_EXPORT
466 240439 : GF_Err gf_cfg_set_key(GF_Config *iniFile, const char *secName, const char *keyName, const char *keyValue)
467 : {
468 240439 : return gf_cfg_set_key_internal(iniFile, secName, keyName, keyValue, GF_FALSE);
469 :
470 : }
471 :
472 : GF_EXPORT
473 6480 : u32 gf_cfg_get_section_count(GF_Config *iniFile)
474 : {
475 6480 : return gf_list_count(iniFile->sections);
476 : }
477 :
478 : GF_EXPORT
479 14696 : const char *gf_cfg_get_section_name(GF_Config *iniFile, u32 secIndex)
480 : {
481 14696 : IniSection *is = (IniSection *) gf_list_get(iniFile->sections, secIndex);
482 14696 : if (!is) return NULL;
483 14696 : return is->section_name;
484 : }
485 :
486 : GF_EXPORT
487 17696 : u32 gf_cfg_get_key_count(GF_Config *iniFile, const char *secName)
488 : {
489 17696 : u32 i = 0;
490 : IniSection *sec;
491 72166 : while ( (sec = (IniSection *) gf_list_enum(iniFile->sections, &i)) ) {
492 51617 : if (!strcmp(secName, sec->section_name)) return gf_list_count(sec->keys);
493 : }
494 : return 0;
495 : }
496 :
497 : GF_EXPORT
498 99677 : const char *gf_cfg_get_key_name(GF_Config *iniFile, const char *secName, u32 keyIndex)
499 : {
500 99677 : u32 i = 0;
501 : IniSection *sec;
502 334002 : while ( (sec = (IniSection *) gf_list_enum(iniFile->sections, &i) ) ) {
503 234325 : if (!strcmp(secName, sec->section_name)) {
504 99677 : IniKey *key = (IniKey *) gf_list_get(sec->keys, keyIndex);
505 99677 : return key ? key->name : NULL;
506 : }
507 : }
508 : return NULL;
509 : }
510 :
511 : GF_EXPORT
512 7503 : void gf_cfg_del_section(GF_Config *iniFile, const char *secName)
513 : {
514 : u32 i;
515 : IniSection *p;
516 7503 : if (!iniFile) return;
517 :
518 7503 : i = 0;
519 55345 : while ((p = (IniSection*)gf_list_enum(iniFile->sections, &i))) {
520 40339 : if (!strcmp(secName, p->section_name)) {
521 0 : DelSection(p);
522 0 : gf_list_rem(iniFile->sections, i-1);
523 0 : iniFile->hasChanged = GF_TRUE;
524 0 : return;
525 : }
526 : }
527 : }
528 :
529 : #if 0 //unused
530 : GF_Err gf_cfg_insert_key(GF_Config *iniFile, const char *secName, const char *keyName, const char *keyValue, u32 index)
531 : {
532 : u32 i;
533 : IniSection *sec;
534 : IniKey *key;
535 :
536 : if (!iniFile || !secName || !keyName|| !keyValue) return GF_BAD_PARAM;
537 :
538 : i=0;
539 : while ( (sec = (IniSection *) gf_list_enum(iniFile->sections, &i) ) ) {
540 : if (!strcmp(secName, sec->section_name)) break;
541 : }
542 : if (!sec) return GF_BAD_PARAM;
543 :
544 : i=0;
545 : while ( (key = (IniKey *) gf_list_enum(sec->keys, &i) ) ) {
546 : if (!strcmp(key->name, keyName)) return GF_BAD_PARAM;
547 : }
548 :
549 : key = (IniKey *) gf_malloc(sizeof(IniKey));
550 : key->name = gf_strdup(keyName);
551 : key->value = gf_strdup(keyValue);
552 : gf_list_insert(sec->keys, key, index);
553 : iniFile->hasChanged = GF_TRUE;
554 : return GF_OK;
555 : }
556 : #endif
557 :
558 : #if 0 //unused
559 : const char *gf_cfg_get_sub_key(GF_Config *iniFile, const char *secName, const char *keyName, u32 sub_index)
560 : {
561 : u32 j;
562 : char *subKeyValue, *returnKey;
563 : char *keyValue;
564 :
565 :
566 : keyValue = gf_strdup(gf_cfg_get_key(iniFile, secName, keyName));
567 : if (!keyValue) {
568 : return NULL;
569 : }
570 :
571 : j = 0;
572 : subKeyValue = strtok((char*)keyValue,";");
573 : while (subKeyValue!=NULL) {
574 : if (j==sub_index) {
575 : returnKey = gf_strdup(subKeyValue);
576 : gf_free(keyValue);
577 : return returnKey;
578 : }
579 : j++;
580 : subKeyValue = strtok (NULL, ";");
581 : }
582 : gf_free(keyValue);
583 : return NULL;
584 : }
585 : #endif
586 :
587 6209 : GF_Err gf_cfg_set_filename(GF_Config *iniFile, const char * fileName)
588 : {
589 6209 : if (!fileName) return GF_OK;
590 6209 : if (iniFile->fileName) gf_free(iniFile->fileName);
591 6209 : iniFile->fileName = gf_strdup(fileName);
592 6209 : return iniFile->fileName ? GF_OK : GF_OUT_OF_MEM;
593 : }
594 :
595 :
|