Coverage Report

Created: 2025-10-10 06:43

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