Coverage Report

Created: 2025-07-01 06:08

/src/opensc/src/scconf/parse.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * $Id$
3
 *
4
 * Copyright (C) 2002
5
 *  Antti Tapaninen <aet@cc.hut.fi>
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21
22
#include "config.h"
23
24
#include <stdio.h>
25
#include <stdlib.h>
26
#include <string.h>
27
#ifdef HAVE_STRINGS_H
28
#include <strings.h>
29
#endif
30
#include <errno.h>
31
32
#include "common/compat_strlcpy.h"
33
#include "internal.h"
34
#include "scconf.h"
35
36
0
#define STATE_NAME  0x01
37
0
#define STATE_VALUE 0x02
38
0
#define STATE_SET 0x10
39
40
static scconf_item *scconf_get_last_item(scconf_block *root)
41
0
{
42
0
  scconf_block *block = root;
43
0
  scconf_item *item;
44
45
0
  for (item = root->items; item; item = item->next) {
46
0
    if (!item->next) {
47
0
      return item;
48
0
    }
49
0
  }
50
0
  return block->items;
51
0
}
52
53
static void scconf_parse_error(scconf_parser * parser, const char *error)
54
0
{
55
  /* FIXME: save the error somewhere */
56
0
  parser->error = 1;
57
58
0
  snprintf(parser->emesg, sizeof(parser->emesg), "Line %d: %s\n", parser->line, error);
59
0
}
60
61
static void scconf_parse_error_not_expect(scconf_parser * parser,
62
            const char *token)
63
0
{
64
  /* FIXME: save the error somewhere */
65
0
  parser->error = 1;
66
67
0
  snprintf(parser->emesg, sizeof(parser->emesg), "Line %d: not expecting '%s'\n", parser->line, token);
68
0
}
69
70
static void scconf_parse_warning_expect(scconf_parser * parser, const char *token)
71
0
{
72
  /* FIXME: save the warnings somewhere */
73
0
  parser->warnings = 1;
74
75
0
  snprintf(parser->emesg, sizeof(parser->emesg),
76
0
    "Line %d: missing '%s', ignoring\n",
77
0
    parser->line, token);
78
0
}
79
80
static scconf_item *scconf_item_find(scconf_parser * parser)
81
0
{
82
0
  scconf_item *item;
83
84
0
  for (item = parser->block->items; item; item = item->next) {
85
0
    if (item && item->type == SCCONF_ITEM_TYPE_VALUE
86
0
          && item->key && parser->key
87
0
          && strcasecmp(item->key, parser->key) == 0) {
88
0
      return item;
89
0
    }
90
0
  }
91
0
  return item;
92
0
}
93
94
static scconf_item *scconf_item_add_internal(scconf_parser * parser, int type)
95
0
{
96
0
  scconf_item *item;
97
98
0
  if (type == SCCONF_ITEM_TYPE_VALUE) {
99
    /* if item with same key already exists, use it */
100
0
    item = scconf_item_find(parser);
101
0
    if (item) {
102
0
      free(parser->key);
103
0
      parser->key = NULL;
104
0
      parser->current_item = item;
105
0
      return item;
106
0
    }
107
0
  }
108
0
  item = calloc(1, sizeof(scconf_item));
109
0
  if (!item) {
110
0
    return NULL;
111
0
  }
112
0
  item->type = type;
113
114
0
  item->key = parser->key;
115
0
  parser->key = NULL;
116
117
0
  if (parser->last_item) {
118
0
    parser->last_item->next = item;
119
0
  } else {
120
0
    parser->block->items = item;
121
0
  }
122
0
  parser->current_item = parser->last_item = item;
123
0
  return item;
124
0
}
125
126
scconf_item *scconf_item_add(scconf_context * config, scconf_block * block, scconf_item * item, int type, const char *key, const void *data)
127
0
{
128
0
  scconf_parser parser;
129
0
  scconf_block *dst = NULL;
130
131
0
  if (!config && !block)
132
0
    return NULL;
133
0
  if (!data)
134
0
    return NULL;
135
136
0
  memset(&parser, 0, sizeof(scconf_parser));
137
0
  parser.config = config ? config : NULL;
138
0
  parser.key = key ? strdup(key) : NULL;
139
0
  parser.block = block ? block : config->root;
140
0
  parser.name = NULL;
141
0
  parser.last_item = scconf_get_last_item(parser.block);
142
0
  parser.current_item = item;
143
144
0
  if (type == SCCONF_ITEM_TYPE_BLOCK) {
145
0
    scconf_block_copy((const scconf_block *) data, &dst);
146
0
    scconf_list_copy(dst->name, &parser.name);
147
0
  }
148
0
  if (scconf_item_add_internal(&parser, type)) {
149
0
    switch (parser.current_item->type) {
150
0
      case SCCONF_ITEM_TYPE_COMMENT:
151
0
        parser.current_item->value.comment = strdup((const char *) data);
152
0
        break;
153
0
      case SCCONF_ITEM_TYPE_BLOCK:
154
0
        if (!dst)
155
0
          return NULL;
156
0
        dst->parent = parser.block;
157
0
        parser.current_item->value.block = dst;
158
0
        scconf_list_destroy(parser.name);
159
0
        break;
160
0
      case SCCONF_ITEM_TYPE_VALUE:
161
0
        scconf_list_copy((const scconf_list *) data, &parser.current_item->value.list);
162
0
        break;
163
0
    }
164
0
  } else {
165
    /* FIXME is it an error if item is NULL? */
166
0
    free(parser.key);
167
0
    parser.key = NULL;
168
0
  }
169
0
  return parser.current_item;
170
0
}
171
172
static void scconf_block_add_internal(scconf_parser * parser)
173
0
{
174
0
  scconf_block *block;
175
0
  scconf_item *item;
176
177
0
  item = scconf_item_add_internal(parser, SCCONF_ITEM_TYPE_BLOCK);
178
0
  if (!item) {
179
0
    return;
180
0
  }
181
182
0
  block = calloc(1, sizeof(scconf_block));
183
0
  if (!block) {
184
0
    return;
185
0
  }
186
0
  block->parent = parser->block;
187
0
  item->value.block = block;
188
189
0
  if (!parser->name) {
190
0
    scconf_list_add(&parser->name, "");
191
0
  }
192
0
  block->name = parser->name;
193
0
  parser->name = NULL;
194
195
0
  parser->block = block;
196
0
  parser->last_item = NULL;
197
0
}
198
199
scconf_block *scconf_block_add(scconf_context * config, scconf_block * block, const char *key, const scconf_list *name)
200
0
{
201
0
  scconf_parser parser;
202
203
0
  if (!config)
204
0
    return NULL;
205
206
0
  memset(&parser, 0, sizeof(scconf_parser));
207
0
  parser.config = config;
208
0
  parser.key = key ? strdup(key) : NULL;
209
0
  parser.block = block ? block : config->root;
210
0
  scconf_list_copy(name, &parser.name);
211
0
  parser.last_item = scconf_get_last_item(parser.block);
212
0
  parser.current_item = parser.block->items;
213
214
0
  scconf_block_add_internal(&parser);
215
0
  return parser.block;
216
0
}
217
218
static void scconf_parse_parent(scconf_parser * parser)
219
0
{
220
0
  parser->block = parser->block->parent;
221
222
0
  parser->last_item = parser->block->items;
223
0
  if (parser->last_item) {
224
0
    while (parser->last_item->next) {
225
0
      parser->last_item = parser->last_item->next;
226
0
    }
227
0
  }
228
0
}
229
230
static void scconf_parse_reset_state(scconf_parser * parser)
231
0
{
232
0
  if (parser) {
233
0
    if (parser->key) {
234
0
      free(parser->key);
235
0
    }
236
0
    scconf_list_destroy(parser->name);
237
238
0
    parser->key = NULL;
239
0
    parser->name = NULL;
240
0
    parser->state = 0;
241
0
  }
242
0
}
243
244
void scconf_skip_block(scconf_parser * parser)
245
0
{
246
0
  scconf_parse_error(parser, "too many nested blocks");
247
0
  scconf_parse_reset_state(parser);
248
0
}
249
250
void scconf_parse_token(scconf_parser * parser, int token_type, const char *token)
251
0
{
252
0
  scconf_item *item;
253
0
  size_t len;
254
255
0
  if (parser->error) {
256
    /* fatal error */
257
0
    return;
258
0
  }
259
0
  switch (token_type) {
260
0
  case TOKEN_TYPE_NEWLINE:
261
0
    parser->line++;
262
0
    if (parser->last_token_type != TOKEN_TYPE_NEWLINE) {
263
0
      break;
264
0
    }
265
    /* fall through - treat empty lines as comments */
266
0
  case TOKEN_TYPE_COMMENT:
267
0
    item = scconf_item_add_internal(parser, SCCONF_ITEM_TYPE_COMMENT);
268
0
    if (!item) {
269
0
      return;
270
0
    }
271
0
    item->value.comment = token ? strdup(token) : NULL;
272
0
    break;
273
0
  case TOKEN_TYPE_STRING:
274
0
    {
275
0
      char *stoken = NULL;
276
277
0
      if ((parser->state & (STATE_VALUE | STATE_SET)) ==
278
0
          (STATE_VALUE | STATE_SET)) {
279
0
        scconf_parse_warning_expect(parser, ";");
280
0
        scconf_parse_reset_state(parser);
281
0
      }
282
0
      if (token && *token == '"') {
283
        /* quoted string, remove them */
284
0
        token++;
285
0
        len = strlen(token);
286
0
        if (len < 1 || token[len - 1] != '"') {
287
0
          scconf_parse_warning_expect(parser, "\"");
288
0
        } else {
289
          /* stoken */
290
0
          stoken = strdup(token);
291
0
          if (stoken) {
292
0
            stoken[len - 1] = '\0';
293
0
          }
294
0
        }
295
0
      }
296
0
      if (!stoken) {
297
0
        stoken = token ? strdup(token) : NULL;
298
0
      }
299
0
      if (parser->state == 0) {
300
        /* key */
301
0
        parser->key = stoken ? strdup(stoken) : NULL;
302
0
        parser->state = STATE_NAME;
303
0
      } else if (parser->state == STATE_NAME) {
304
        /* name */
305
0
        parser->state |= STATE_SET;
306
0
        scconf_list_add(&parser->name, stoken);
307
0
      } else if (parser->state == STATE_VALUE && parser->current_item->type == SCCONF_ITEM_TYPE_VALUE) {
308
        /* value */
309
0
        parser->state |= STATE_SET;
310
0
        scconf_list_add(&parser->current_item->value.list,
311
0
                  stoken);
312
0
      } else {
313
        /* error */
314
0
        scconf_parse_error_not_expect(parser, stoken);
315
0
      }
316
0
      if (stoken) {
317
0
        free(stoken);
318
0
      }
319
0
      stoken = NULL;
320
0
    }
321
0
    break;
322
0
  case TOKEN_TYPE_PUNCT:
323
0
    switch (*token) {
324
0
    case '{':
325
0
      if ((parser->state & STATE_NAME) == 0) {
326
0
        scconf_parse_error_not_expect(parser, "{");
327
0
        break;
328
0
      }
329
0
      parser->nested_blocks++;
330
0
      scconf_block_add_internal(parser);
331
0
      scconf_parse_reset_state(parser);
332
0
      break;
333
0
    case '}':
334
0
      parser->nested_blocks--;
335
0
      if (parser->state != 0) {
336
0
        if ((parser->state & STATE_VALUE) == 0 ||
337
0
            (parser->state & STATE_SET) == 0) {
338
0
          scconf_parse_error_not_expect(parser,
339
0
                      "}");
340
0
          break;
341
0
        }
342
        /* foo = bar } */
343
0
        scconf_parse_warning_expect(parser, ";");
344
0
        scconf_parse_reset_state(parser);
345
0
      }
346
0
      if (!parser->block->parent) {
347
        /* too many '}' */
348
0
        scconf_parse_error(parser,
349
0
               "missing matching '{'");
350
0
        break;
351
0
      }
352
0
      scconf_parse_parent(parser);
353
0
      break;
354
0
    case ',':
355
0
      if ((parser->state & (STATE_NAME | STATE_VALUE)) == 0) {
356
0
        scconf_parse_error_not_expect(parser, ",");
357
0
      }
358
0
      parser->state &= ~STATE_SET;
359
0
      break;
360
0
    case '=':
361
0
      if ((parser->state & STATE_NAME) == 0) {
362
0
        scconf_parse_error_not_expect(parser, "=");
363
0
        break;
364
0
      }
365
0
      scconf_item_add_internal(parser, SCCONF_ITEM_TYPE_VALUE);
366
0
      parser->state = STATE_VALUE;
367
0
      break;
368
0
    case ';':
369
0
      scconf_parse_reset_state(parser);
370
0
      break;
371
0
    default:
372
0
      snprintf(parser->emesg, sizeof(parser->emesg),
373
0
        "Line %d: bad token ignoring\n",
374
0
        parser->line);
375
0
    }
376
0
    break;
377
0
  }
378
379
0
  parser->last_token_type = token_type;
380
0
}
381
382
int scconf_parse(scconf_context * config)
383
0
{
384
0
  static char buffer[256];
385
0
  scconf_parser p;
386
0
  int r = 1;
387
388
0
  memset(&p, 0, sizeof(p));
389
0
  p.config = config;
390
0
  p.block = config->root;
391
0
  p.line = 1;
392
0
  p.nested_blocks = 0;
393
394
0
  if (!scconf_lex_parse(&p, config->filename)) {
395
0
    snprintf(buffer, sizeof(buffer),
396
0
        "Unable to open \"%s\": %s",
397
0
        config->filename, strerror(errno));
398
0
    r = -1;
399
0
  } else if (p.error) {
400
0
    strlcpy(buffer, p.emesg, sizeof(buffer));
401
0
    r = 0;
402
0
  } else {
403
0
    r = 1;
404
0
  }
405
406
0
  if (r <= 0)
407
0
    config->errmsg = buffer;
408
0
  return r;
409
0
}
410
411
int scconf_parse_string(scconf_context * config, const char *string)
412
0
{
413
0
  static char buffer[256];
414
0
  scconf_parser p;
415
0
  int r;
416
417
0
  memset(&p, 0, sizeof(p));
418
0
  p.config = config;
419
0
  p.block = config->root;
420
0
  p.line = 1;
421
0
  p.nested_blocks = 0;
422
423
0
  if (!scconf_lex_parse_string(&p, string)) {
424
0
    snprintf(buffer, sizeof(buffer),
425
0
        "Failed to parse configuration string");
426
0
    r = -1;
427
0
  } else if (p.error) {
428
0
    strlcpy(buffer, p.emesg, sizeof(buffer));
429
0
    r = 0;
430
0
  } else {
431
0
    r = 1;
432
0
  }
433
434
0
  scconf_parse_reset_state(&p);
435
436
0
  if (r <= 0)
437
0
    config->errmsg = buffer;
438
0
  return r;
439
0
}