Browse code

carrierroute: Remove dependency on libconfuse, based on the patch by Lucian Balaceanu with the Comments based on Marius Zbihlei

Carsten Bock authored on 13/03/2014 13:52:49
Showing 6 changed files
... ...
@@ -126,8 +126,6 @@ Requirements:
126 126
 - libsnmp9 - libs and devel headers - if you want SNMP client functionality 
127 127
   (SNMP AgentX subagent) for Kamailio
128 128
 - libldap libs and devel headers v2.1 or greater - if you want LDAP support
129
-- libconfuse and devel headers - if you want to compile the carrierroute
130
-  module
131 129
 - libpcre libs and devel headers - if you want to compile the lcr and dialplan
132 130
   modules
133 131
 - libsctp devel headers - if you want to compile the SCTP transport in the core
... ...
@@ -9,23 +9,6 @@ include ../../Makefile.defs
9 9
 auto_gen=
10 10
 NAME=carrierroute.so
11 11
 
12
-ifeq ($(CROSS_COMPILE),)
13
-CONFUSE_BUILDER = $(shell \
14
-	if which confuse-config &>/dev/null;then \
15
-		echo 'confuse-config'; \
16
-	elif pkg-config --exists libconfuse; then \
17
-		echo 'pkg-config libconfuse'; \
18
-	fi)
19
-endif
20
-
21
-ifeq ($(CONFUSE_BUILDER),)
22
-	DEFS += -I$(LOCALBASE)/include
23
-	LIBS += -L$(LOCALBASE)/lib -lconfuse
24
-else
25
-	DEFS += $(shell $(CONFUSE_BUILDER) --cflags)
26
-	LIBS += $(shell $(CONFUSE_BUILDER) --libs)
27
-endif
28
-
29 12
 DEFS+=-DKAMAILIO_MOD_INTERFACE
30 13
 
31 14
 SERLIBPATH=../../lib
... ...
@@ -20,14 +20,13 @@
20 20
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 21
  */
22 22
 
23
-/**
23
+/*!
24 24
  * \file cr_config.c
25 25
  * \brief Functions for load and save routing data from a config file.
26 26
  * \ingroup carrierroute
27 27
  * - Module; \ref carrierroute
28 28
  */
29 29
 
30
-#include <confuse.h>
31 30
 #include <sys/types.h>
32 31
 #include <sys/stat.h>
33 32
 #include <unistd.h>
... ...
@@ -42,87 +41,76 @@
42 41
 #include "cr_rule.h"
43 42
 #include "cr_domain.h"
44 43
 #include "cr_carrier.h"
44
+#include "parser_carrierroute.h"
45 45
 
46
+enum target_opt_ids { TO_ID_COMMENT = 0, TO_ID_STRIP, TO_ID_REWR_PREFIX, TO_ID_PROB, TO_ID_HASH_INDEX,
47
+					  TO_ID_REWR_SUFFIX, TO_ID_STATUS, TO_ID_BACKED_UP, TO_ID_BACKUP, TO_MAX_IDS };
48
+enum prefix_opt_ids { PO_MAX_TARGETS = 0, PO_MAX_IDS };
46 49
 
47
-/**
48
- * reports errors during config file parsing using LOG macro
49
- *
50
- * @param cfg points to the current config data structure
51
- * @param fmt a format string
52
- * @param ap format arguments
53
- */
54
-static void conf_error(cfg_t *cfg, const char * fmt, va_list ap) {
55
-	int ret;
56
-	static char buf[1024];
57
-
58
-	ret = vsnprintf(buf, sizeof(buf), fmt, ap);
59
-	if (ret < 0 || ret >= sizeof(buf)) {
60
-		LM_ERR("could not print error message\n");
61
-	} else {
62
-		// FIXME this don't seems to work reliable in all cases, charset 
63
-		// problems
64
-		LM_GEN1(L_ERR, "%s", buf);
50
+option_description target_options[TO_MAX_IDS];
51
+option_description prefix_options[PO_MAX_IDS];
52
+
53
+static void reset_opts(option_description * opts, int size){
54
+	int i;
55
+	if ( NULL == opts){
56
+		LM_ERR("Trying to init a NULL pointer location \n");
57
+		return;
65 58
 	}
59
+	for (i=0; i < size; i++){
60
+		memset(&(opts[i].value),'\0', sizeof(union opt_data));
61
+		opts[i].visited = 0;
62
+		opts[i].no_elems = 0;
63
+		if ( CFG_STR == opts[i].type ){
64
+			opts[i].value.string_data.s = opts[i].str_buf;
65
+			strcpy(opts[i].str_buf,"");
66
+			opts[i].value.string_data.len = 0;
67
+		}
68
+	}
69
+	return;
66 70
 }
67 71
 
68
-
69
-/**
70
- * Parses the config file
71
- *
72
- * @return a pointer to the configuration data structure, NULL on failure
73
- */
74
-static cfg_t * parse_config(void) {
75
-	cfg_t * cfg = NULL;
76
-
77
-	cfg_opt_t target_opts[] = {
78
-	                              CFG_STR("comment", 0, CFGF_NONE),
79
-	                              CFG_INT("strip", 0, CFGF_NONE),
80
-	                              CFG_STR("rewrite_prefix", 0, CFGF_NONE),
81
-	                              CFG_FLOAT("prob", 0, CFGF_NONE),
82
-	                              CFG_INT("hash_index", 0, CFGF_NONE),
83
-	                              CFG_STR("rewrite_suffix", 0, CFGF_NONE),
84
-	                              CFG_INT("status", 1, CFGF_NONE),
85
-	                              CFG_INT_LIST("backed_up", NULL, CFGF_NONE),
86
-	                              CFG_INT("backup", -1, CFGF_NONE),
87
-	                              CFG_END()
88
-	                          };
89
-
90
-	cfg_opt_t prefix_opts[] = {
91
-	                              CFG_SEC("target", target_opts, CFGF_MULTI | CFGF_TITLE),
92
-	                              CFG_INT("max_targets", -1, CFGF_NONE),
93
-	                              CFG_END()
94
-	                          };
95
-
96
-	cfg_opt_t domain_opts[] = {
97
-	                              CFG_SEC("prefix", prefix_opts, CFGF_MULTI | CFGF_TITLE),
98
-	                              CFG_END()
99
-	                          };
100
-
101
-	cfg_opt_t opts[] = {
102
-	                       CFG_SEC("domain", domain_opts, CFGF_MULTI | CFGF_TITLE),
103
-	                       CFG_END()
104
-	                   };
105
-
106
-	cfg = cfg_init(opts, CFGF_NONE);
107
-	if (cfg == NULL) {
108
-		LM_ERR("could not initialize configuration\n");
109
-		return NULL;
72
+static int init_target_opts(option_description * opts){
73
+	if ( NULL == opts){
74
+		LM_DBG("Trying to init a NULL pointer location \n");
75
+		return -1;
110 76
 	}
77
+	memset(opts, '\0', sizeof(option_description) * TO_MAX_IDS);
78
+
79
+	strcpy((char*)(opts[TO_ID_COMMENT].name),    "comment");
80
+	strcpy((char*)(opts[TO_ID_STRIP].name),      "strip");
81
+	strcpy((char*)(opts[TO_ID_REWR_PREFIX].name),"rewrite_prefix");
82
+	strcpy((char*)(opts[TO_ID_PROB].name),       "prob");
83
+	strcpy((char*)(opts[TO_ID_HASH_INDEX].name), "hash_index");
84
+	strcpy((char*)(opts[TO_ID_REWR_SUFFIX].name),"rewrite_suffix");
85
+	strcpy((char*)(opts[TO_ID_STATUS].name),     "status");
86
+	strcpy((char*)(opts[TO_ID_BACKED_UP].name),  "backed_up");
87
+	strcpy((char*)(opts[TO_ID_BACKUP].name),     "backup");
88
+
89
+	opts[TO_ID_COMMENT    ].type=CFG_STR;
90
+	opts[TO_ID_STRIP      ].type=CFG_INT;
91
+	opts[TO_ID_REWR_PREFIX].type=CFG_STR;
92
+	opts[TO_ID_PROB       ].type=CFG_FLOAT;
93
+	opts[TO_ID_HASH_INDEX ].type=CFG_INT;
94
+	opts[TO_ID_REWR_SUFFIX].type=CFG_STR;
95
+	opts[TO_ID_STATUS     ].type=CFG_INT;
96
+	opts[TO_ID_BACKED_UP  ].type=CFG_INT_LIST;
97
+	opts[TO_ID_BACKUP     ].type=CFG_INT;
98
+
99
+	reset_opts(opts, TO_MAX_IDS);
100
+	return 0;
101
+}
111 102
 
112
-	cfg_set_error_function(cfg, conf_error);
113
-
114
-	switch (cfg_parse(cfg, config_file)) {
115
-		case CFG_FILE_ERROR: LM_ERR("file not found: %s\n", config_file);
116
-			return NULL;
117
-		case CFG_PARSE_ERROR: LM_ERR("error while parsing %s in line %i, section %s\n",
118
-			                          cfg->filename, cfg->line, cfg->name);
119
-			return NULL;
120
-		case CFG_SUCCESS: break;
103
+static int init_prefix_opts(option_description * opts){
104
+	if ( NULL == opts){
105
+		LM_DBG("Trying to init a NULL pointer location \n");
106
+		return -1;
121 107
 	}
122
-	return cfg;
108
+	memset(opts, '\0', sizeof(option_description) * PO_MAX_IDS);
109
+	strcpy((char*)(opts[PO_MAX_TARGETS].name), "max_targets");
110
+	opts[PO_MAX_TARGETS].type=CFG_INT;
111
+	return 0;
123 112
 }
124 113
 
125
-
126 114
 static int backup_config(void) {
127 115
 	FILE * from, * to;
128 116
 	char * backup_file, ch;
... ...
@@ -188,10 +176,11 @@ errout:
188 176
 	return -1;
189 177
 }
190 178
 
191
-
192 179
 /**
193 180
  * Loads the routing data from the config file given in global
194 181
  * variable config_data and stores it in routing tree rd.
182
+ * The function mixes code parsing calls with rd structure
183
+ * completion.
195 184
  *
196 185
  * @param rd Pointer to the route data tree where the routing data
197 186
  * shall be loaded into
... ...
@@ -200,173 +189,277 @@ errout:
200 189
  *
201 190
  */
202 191
 int load_config(struct route_data_t * rd) {
203
-	cfg_t * cfg = NULL;
204
-	int m, o, i, j, k,l, status, hash_index, max_targets, strip;
205
-	cfg_t * d, * p, * t;
192
+	FILE * file;
193
+
194
+	int ret_domain, ret_prefix, ret_target, ret_prefix_opts, ret_target_opts;
195
+	int domain_id, allocated_domain_num = DEFAULT_DOMAIN_NUM;
196
+	str domain_name, prefix_name, rewrite_host;
197
+	char domain_buf[CR_MAX_LINE_SIZE], prefix_buf[CR_MAX_LINE_SIZE],  rewrite_buf[CR_MAX_LINE_SIZE];
198
+
199
+	str rewrite_prefix, rewrite_suffix, comment;
200
+	struct domain_data_t *domain_data = NULL;
206 201
 	struct carrier_data_t * tmp_carrier_data;
207
-	int domain_id;
208
-	str domain, prefix, rewrite_prefix, rewrite_suffix, rewrite_host, comment;
202
+	int hash_index, max_targets = 0, strip;
209 203
 	double prob;
210 204
 	int * backed_up = NULL;
211
-	int backed_up_size, backup;
212
-	backed_up_size = backup = 0;
205
+	int backed_up_size = 0, backup = 0, status;
206
+	void* p_realloc;
207
+	int i=0, l, k;
208
+
209
+	domain_name.s = domain_buf; domain_name.len = CR_MAX_LINE_SIZE;
210
+	prefix_name.s = prefix_buf; prefix_name.len = CR_MAX_LINE_SIZE;
211
+	rewrite_host.s = rewrite_buf; rewrite_host.len = CR_MAX_LINE_SIZE;
213 212
 
214
-	if ((cfg = parse_config()) == NULL) {
213
+	/* open configuration file */
214
+	if ((file = fopen(config_file, "rb"))==NULL) {
215
+		LM_ERR("Cannot open source file.\n");
215 216
 		return -1;
216 217
 	}
217 218
 
218 219
 	rd->carrier_num = 1;
219 220
 	rd->first_empty_carrier = 0;
220
-	rd->domain_num = cfg_size(cfg, "domain");
221
+	rd->domain_num = 0;
221 222
 
222 223
 	if ((rd->carriers = shm_malloc(sizeof(struct carrier_data_t *))) == NULL) {
223 224
 		SHM_MEM_ERROR;
224
-		return -1;
225
+		goto errclose;
225 226
 	}
226 227
 	memset(rd->carriers, 0, sizeof(struct carrier_data_t *));
227 228
 
228 229
 	/* Create carrier map */
229 230
 	if ((rd->carrier_map = shm_malloc(sizeof(struct name_map_t))) == NULL) {
230 231
 		SHM_MEM_ERROR;
231
-		return -1;
232
+		goto errclose;
232 233
 	}
234
+
233 235
 	memset(rd->carrier_map, 0, sizeof(struct name_map_t));
234 236
 	rd->carrier_map[0].id = 1;
235 237
 	rd->carrier_map[0].name.len = default_tree.len;
236 238
 	rd->carrier_map[0].name.s = shm_malloc(rd->carrier_map[0].name.len);
239
+
237 240
 	if (rd->carrier_map[0].name.s == NULL) {
238 241
 		SHM_MEM_ERROR;
239
-		return -1;
242
+		goto errclose;
240 243
 	}
241 244
 	memcpy(rd->carrier_map[0].name.s, default_tree.s, rd->carrier_map[0].name.len);
242 245
 
243 246
 	/* Create domain map */
244
-	if ((rd->domain_map = shm_malloc(sizeof(struct name_map_t) * rd->domain_num)) == NULL) {
247
+	if ((rd->domain_map = shm_malloc(sizeof(struct name_map_t) * allocated_domain_num)) == NULL) {
245 248
 		SHM_MEM_ERROR;
246
-		return -1;
249
+		goto errclose;
247 250
 	}
248
-	memset(rd->domain_map, 0, sizeof(struct name_map_t) * rd->domain_num);
249
-	for (i=0; i<rd->domain_num; i++) {
250
-		d = cfg_getnsec(cfg, "domain", i);
251
-		domain.s = (char *)cfg_title(d);
252
-		if (domain.s==NULL) domain.s="";
253
-		domain.len = strlen(domain.s);
254
-		rd->domain_map[i].id = i+1;
255
-		rd->domain_map[i].name.len = domain.len;
256
-		rd->domain_map[i].name.s = shm_malloc(rd->domain_map[i].name.len);
257
-		if (rd->domain_map[i].name.s == NULL) {
258
-			SHM_MEM_ERROR;
259
-			return -1;
260
-		}
261
-		memcpy(rd->domain_map[i].name.s, domain.s, rd->domain_map[i].name.len);
262
-	}
263
-	/* sort domain map by id for faster access */
264
-	qsort(rd->domain_map, rd->domain_num, sizeof(rd->domain_map[0]), compare_name_map);
251
+	memset(rd->domain_map, 0, sizeof(struct name_map_t) * allocated_domain_num);
265 252
 
266 253
 	/* Create and insert carrier data structure */
267
-	tmp_carrier_data = create_carrier_data(1, &rd->carrier_map[0].name, rd->domain_num);
254
+	tmp_carrier_data = create_carrier_data(1, &rd->carrier_map[0].name, allocated_domain_num);
255
+	tmp_carrier_data->domain_num = 0;
256
+	tmp_carrier_data->id = 1;
257
+	tmp_carrier_data->name = &(rd->carrier_map[0].name);
258
+
268 259
 	if (tmp_carrier_data == NULL) {
269 260
 		LM_ERR("can't create new carrier\n");
270
-		return -1;
261
+		goto errclose;
271 262
 	}
272 263
 	if (add_carrier_data(rd, tmp_carrier_data) < 0) {
273 264
 		LM_ERR("couldn't add carrier data\n");
274 265
 		destroy_carrier_data(tmp_carrier_data);
275
-		return -1;
266
+		goto errclose;
276 267
 	}
277 268
 
278
-	/* add all routes */
279
-	for (i = 0; i < rd->domain_num; i++) {
280
-		d = cfg_getnsec(cfg, "domain", i);
281
-		domain.s = (char *)cfg_title(d);
282
-		if (domain.s==NULL) domain.s="";
283
-		domain.len = strlen(domain.s);
284
-		m = cfg_size(d, "prefix");
285
-
286
-		LM_INFO("loading domain %.*s\n", domain.len, domain.s);
287
-		for (j = 0; j < m; j++) {
288
-			p = cfg_getnsec(d, "prefix", j);
289
-			prefix.s = (char *)cfg_title(p);
290
-			if (prefix.s==NULL) prefix.s="";
291
-			prefix.len = strlen(prefix.s);
292
-			if (str_strcasecmp(&prefix, &CR_EMPTY_PREFIX) == 0) {
293
-				prefix.s = "";
294
-				prefix.len = 0;
269
+	init_prefix_opts(prefix_options);
270
+	init_target_opts(target_options);
271
+
272
+	/* add all routes by parsing the route conf file */
273
+	/* while there are domain structures, get name and parse the structure*/
274
+	while ((ret_domain = parse_struct_header(file, "domain", &domain_name))
275
+			== SUCCESSFUL_PARSING) {
276
+
277
+		domain_id = ++rd->domain_num;
278
+		tmp_carrier_data->domain_num++;
279
+
280
+		/* (re)allocate memory for a maximum of MAX_DOMAIN_NUM domains
281
+		 rd is not fully allocated from the start as this would require the preparsing
282
+		 of the entire route file */
283
+		if ( rd->domain_num > allocated_domain_num){
284
+
285
+			if (MAX_DOMAIN_NUM <= allocated_domain_num){
286
+				LM_ERR("Maximum number of domains reached");
287
+				break;
295 288
 			}
296 289
 
297
-			LM_INFO("loading prefix %.*s\n", prefix.len, prefix.s);
298
-			max_targets = cfg_getint(p, "max_targets");
299
-			o = cfg_size(p, "target");
300
-			for (k = 0; k < o; k++) {
301
-				t = cfg_getnsec(p, "target", k);
302
-				rewrite_host.s = (char *)cfg_title(t);
303
-				if (rewrite_host.s==NULL) rewrite_host.s="";
304
-				rewrite_host.len = strlen(rewrite_host.s);
290
+			LM_INFO("crt_alloc_size=%d must be increased \n", allocated_domain_num);
291
+			allocated_domain_num *= 2;
292
+
293
+			if ( ( p_realloc = shm_realloc(rd->domain_map,
294
+					sizeof(struct name_map_t) * allocated_domain_num) ) == NULL)
295
+			{
296
+				SHM_MEM_ERROR;
297
+				goto errclose;
298
+			}
299
+
300
+			rd->domain_map = (struct name_map_t *)p_realloc;
301
+
302
+			if (( p_realloc = shm_realloc( rd->carriers[0]->domains,
303
+					sizeof(struct domain_data_t *) * allocated_domain_num)) == NULL) {
304
+				SHM_MEM_ERROR;
305
+				goto errclose;
306
+			}
307
+			rd->carriers[0]->domains = (struct domain_data_t **)p_realloc;
308
+
309
+			for (i=0; i<rd->domain_num-1; i++){
310
+				rd->carriers[0]->domains[i]->name = &(rd->domain_map[i].name);
311
+			}
312
+		}// end of mem (re)allocation for domains
313
+
314
+		/*insert domain in domain map*/
315
+		rd->domain_map[domain_id-1].id = domain_id;
316
+		rd->domain_map[domain_id-1].name.len = domain_name.len;
317
+		rd->domain_map[domain_id-1].name.s = shm_malloc(rd->domain_map[domain_id-1].name.len);
318
+		if (rd->domain_map[domain_id-1].name.s == NULL) {
319
+			SHM_MEM_ERROR;
320
+			goto errclose;
321
+		}
322
+		memcpy(rd->domain_map[domain_id-1].name.s, domain_name.s, rd->domain_map[domain_id-1].name.len);
323
+
324
+		/* create new domain data */
325
+		if ((domain_data = create_domain_data(domain_id,&(rd->domain_map[domain_id-1].name))) == NULL) {
326
+			LM_ERR("could not create new domain data\n");
327
+			goto errclose;
328
+		}
329
+
330
+		if (add_domain_data(tmp_carrier_data, domain_data, domain_id-1) < 0) {
331
+			LM_ERR("could not add domain data\n");
332
+			destroy_domain_data(domain_data);
333
+			goto errclose;
334
+		}
335
+		LM_DBG("added domain %d '%.*s' to carrier %d '%.*s'\n",
336
+				domain_id, domain_name.len, domain_name.s,
337
+				tmp_carrier_data->id, tmp_carrier_data->name->len, tmp_carrier_data->name->s);
338
+
339
+		/* while there are prefix structures, get name and parse the structure */
340
+		while ((ret_prefix = parse_struct_header(file, "prefix", &prefix_name))
341
+				== SUCCESSFUL_PARSING) {
342
+
343
+			reset_opts(prefix_options, PO_MAX_IDS);
344
+			if (str_strcasecmp(&prefix_name, &CR_EMPTY_PREFIX) == 0) {
345
+				prefix_name.s[0] = '\0';
346
+				prefix_name.len = 0;
347
+			}
348
+
349
+			/* look for max_targets = value which is described in prefix_options */
350
+			if ((ret_prefix_opts = parse_options(file, prefix_options,
351
+					PO_MAX_IDS, "target")) != SUCCESSFUL_PARSING) {
352
+				LM_ERR("Error in parsing \n");
353
+				goto errclose;
354
+			}
355
+
356
+			max_targets = prefix_options[PO_MAX_TARGETS].value.int_data;
357
+			/* look for max_targets target structures */
358
+			for ( k = 0; k < max_targets; k++) {
359
+				/* parse the target header, get name and continue*/
360
+				ret_target = parse_struct_header(file, "target", &rewrite_host);
361
+				if (ret_target != SUCCESSFUL_PARSING) {
362
+					LM_ERR("Error in parsing \n");
363
+					goto errclose;
364
+				}
365
+
366
+				reset_opts(target_options, TO_MAX_IDS);
367
+				/* look for the target options: prob, hash index, status, etc*/
368
+				ret_target_opts = parse_options(file, target_options, TO_MAX_IDS, "}");
369
+				if ( SUCCESSFUL_PARSING == ret_target_opts ){
370
+					/* parsing target structure closing bracket*/
371
+					parse_struct_stop(file);
372
+				}else{
373
+					LM_ERR("Error in parsing in target options \n");
374
+					goto errclose;
375
+				}
376
+				/* intermediary variables for more lisibility */
305 377
 				if (str_strcasecmp(&rewrite_host, &CR_EMPTY_PREFIX) == 0) {
306
-					rewrite_host.s = "";
378
+					rewrite_host.s[0] = '\0';
307 379
 					rewrite_host.len = 0;
308 380
 				}
309
-
310
-				LM_INFO("loading target %.*s\n", rewrite_host.len, rewrite_host.s);
311
-				prob = cfg_getfloat(t, "prob");
312
-				strip = cfg_getint(t, "strip");
313
-				rewrite_prefix.s = (char *)cfg_getstr(t, "rewrite_prefix");
314
-				if (rewrite_prefix.s==NULL) rewrite_prefix.s="";
315
-				rewrite_prefix.len = strlen(rewrite_prefix.s);
316
-				rewrite_suffix.s = (char *)cfg_getstr(t, "rewrite_suffix");
317
-				if (rewrite_suffix.s==NULL) rewrite_suffix.s="";
318
-				rewrite_suffix.len = strlen(rewrite_suffix.s);
319
-				hash_index = cfg_getint(t, "hash_index");
320
-				comment.s = (char *)cfg_getstr(t, "comment");
321
-				if (comment.s==NULL) comment.s="";
322
-				comment.len = strlen(comment.s);
323
-				status = cfg_getint(t, "status");
324
-
325
-				if ((backed_up_size = cfg_size(t, "backed_up")) > 0) {
381
+				LM_DBG("loading target %.*s\n", rewrite_host.len, rewrite_host.s);
382
+				prob = target_options[TO_ID_PROB].value.float_data;
383
+				strip = target_options[TO_ID_STRIP].value.int_data;
384
+				rewrite_prefix.s = target_options[TO_ID_REWR_PREFIX].value.string_data.s;
385
+				rewrite_prefix.len = target_options[TO_ID_REWR_PREFIX].value.string_data.len;
386
+				rewrite_suffix.s = target_options[TO_ID_REWR_SUFFIX].value.string_data.s;
387
+				rewrite_suffix.len = target_options[TO_ID_REWR_SUFFIX].value.string_data.len;
388
+				hash_index = target_options[TO_ID_HASH_INDEX].value.int_data;
389
+				comment.s = target_options[TO_ID_COMMENT].value.string_data.s;
390
+				comment.len = target_options[TO_ID_COMMENT].value.string_data.len;
391
+				status = target_options[TO_ID_STATUS].value.int_data;
392
+
393
+				if ( (backed_up_size = target_options[TO_ID_BACKED_UP].no_elems) > 0){
326 394
 					if ((backed_up = pkg_malloc(sizeof(int) * (backed_up_size + 1))) == NULL) {
327 395
 						PKG_MEM_ERROR;
328
-						return -1;
396
+						goto errclose;
329 397
 					}
330 398
 					for (l = 0; l < backed_up_size; l++) {
331
-						backed_up[l] = cfg_getnint(t, "backed_up", l);
399
+						backed_up[l] = target_options[TO_ID_BACKED_UP].value.int_list[l];
332 400
 					}
333 401
 					backed_up[backed_up_size] = -1;
334 402
 				}
335
-				backup = cfg_getint(t, "backup");
336
-
337
-				domain_id = map_name2id(rd->domain_map, rd->domain_num, &domain);
338
-				if (domain_id < 0) {
339
-					LM_ERR("cannot find id for domain '%.*s'", domain.len, domain.s);
340
-					if (backed_up) {
341
-						pkg_free(backed_up);
342
-					}
343
-					return -1;
344
-				}
345
-
346
-				LM_INFO("adding route for prefix %.*s, to host %.*s, prob %f, backed up: %i, backup: %i\n",
347
-				    prefix.len, prefix.s, rewrite_host.len, rewrite_host.s, prob, backed_up_size, backup);
348
-				if (add_route(rd, 1, domain_id, &prefix, 0, 0, max_targets, prob, &rewrite_host,
349
-				              strip, &rewrite_prefix, &rewrite_suffix, status,
350
-				              hash_index, backup, backed_up, &comment) < 0) {
403
+				backup = target_options[TO_ID_BACKUP].value.int_data;
404
+
405
+				LM_ERR("\n Adding route to tree <'%.*s'>: prefix_name:%s\n,"
406
+						" max_targets =%d\n, prob=%f\n, rewr_host=%s\n,"
407
+						" strip=%i\n, rwr_prefix=%s\n, rwr_suff=%s\n,"
408
+						" status=%i\n, hash_index=%i\n, comment=%s \n",
409
+						domain_data->name->len, domain_data->name->s, prefix_name.s,
410
+						max_targets, prob, rewrite_host.s, strip, rewrite_prefix.s,
411
+						rewrite_suffix.s, status, hash_index, comment.s);
412
+
413
+				if (add_route_to_tree(domain_data->tree, &prefix_name, 0, 0,
414
+						&prefix_name, max_targets, prob, &rewrite_host,
415
+						strip, &rewrite_prefix, &rewrite_suffix, status,
416
+						hash_index, backup, backed_up, &comment) < 0) {
351 417
 					LM_INFO("Error while adding route\n");
352 418
 					if (backed_up) {
353 419
 						pkg_free(backed_up);
354 420
 					}
355
-					return -1;
421
+					goto errclose;
356 422
 				}
423
+
357 424
 				if (backed_up) {
358 425
 					pkg_free(backed_up);
359 426
 				}
360 427
 				backed_up = NULL;
361 428
 			}
429
+
430
+			if (k != prefix_options[0].value.int_data ) {
431
+				LM_ERR("Error in parsing: max_targets =%i, actual targets =%i \n",
432
+						prefix_options[0].value.int_data, i);
433
+				goto errclose;
434
+			}
435
+			/* parsing prefix structure closing bracket */
436
+			if (parse_struct_stop(file) != SUCCESSFUL_PARSING) {
437
+				LM_ERR("Error in parsing targets, expecting } \n");
438
+				goto errclose;
439
+
440
+			}
441
+		} // END OF PREFIX part
442
+
443
+		/* parsing domain structure closing bracket */
444
+		if (parse_struct_stop(file) != SUCCESSFUL_PARSING) {
445
+			LM_ERR("Error in parsing targets, expecting } \n");
446
+			goto errclose;
362 447
 		}
448
+	}
363 449
 
450
+	if (EOF_REACHED != ret_domain){
451
+		LM_ERR("Error appeared while parsing domain header \n");
452
+		goto errclose;
364 453
 	}
365
-	cfg_free(cfg);
454
+
455
+	LM_ERR("File parsed successfully \n");
456
+	fclose(file);
366 457
 	return 0;
458
+errclose:
459
+	fclose(file);
460
+	return -1;
367 461
 }
368 462
 
369
-
370 463
 /**
371 464
  * Does the work for save_config, traverses the routing data tree
372 465
  * and writes each rule to file.
... ...
@@ -95,21 +95,6 @@
95 95
 	    	</itemizedlist>
96 96
 	    </para>
97 97
 	</section>
98
-	<section>
99
-	    <title>External Libraries or Applications</title>
100
-	    <para>
101
-		The following libraries or applications must be installed before running
102
-		&kamailio; with this module loaded:
103
-		<itemizedlist>
104
-		    <listitem>
105
-			<para>
106
-				<emphasis>libconfuse</emphasis>, a configuration file parser library.
107
-				( http://www.nongnu.org/confuse/ )
108
-			</para>
109
-		    </listitem>
110
-		</itemizedlist>
111
-	    </para>
112
-	</section>
113 98
     </section>
114 99
     <section>
115 100
 	<title>Parameters</title>
116 101
new file mode 100644
... ...
@@ -0,0 +1,411 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 1&1 Internet AG
5
+ *
6
+ * This file is part of SIP-router, a free SIP server.
7
+ *
8
+ * SIP-router is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * SIP-router is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program; if not, write to the Free Software
20
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
+ */
22
+
23
+/*!
24
+ * \file parser_carrierroute.c
25
+ * \brief Functions for parsing the config file of cr when using file mode.
26
+ * \ingroup carrierroute
27
+ * - Module; \ref carrierroute
28
+ */
29
+
30
+#include <stdio.h>
31
+#include <string.h>
32
+#include <ctype.h>
33
+#include <stdlib.h>
34
+#include <limits.h>
35
+#include <float.h>
36
+#include <math.h>
37
+#include <errno.h>
38
+#include "parser_carrierroute.h"
39
+
40
+#include "../../ut.h"
41
+#include "../../trim.h"
42
+
43
+#define assert_not_null(s) do { \
44
+	if ( NULL == (s) ){ \
45
+		LM_INFO("Unexpected null option \n"); \
46
+		return ERROR_IN_PARSING;\
47
+	} \
48
+} while (0)
49
+
50
+
51
+/**
52
+ * Gets index of given option inside option list
53
+ *
54
+ * @param opt_name    name of option
55
+ * @param opt_list    points to the option list
56
+ * @param no_options  size of option list
57
+ *
58
+ * @return index of option inside option list, -1 not found
59
+ */
60
+int get_option_position(char* opt_name, option_description* opt_list, int no_options){
61
+	int i;
62
+	for (i = 0; i<no_options; i++){
63
+		if (strcmp(opt_name, opt_list[i].name) == 0){
64
+			return i;
65
+		}
66
+	}
67
+	return -1;
68
+}
69
+
70
+
71
+/**
72
+ * Fills special structure with params from src string
73
+ *
74
+ * @param int_list    string of type {INT_VAL [, INT_VAL]*}
75
+ * @param opts        destination option_description variable
76
+ *
77
+ * @return number of integers in int_list, on success
78
+ *         ERROR_IN_PARSING, on error
79
+ */
80
+int parse_int_list(char *int_list, option_description* opts){
81
+	char *pch, *endptr;
82
+	long val;
83
+
84
+	pch = strtok (int_list,", \t");
85
+
86
+	while ( pch != NULL )
87
+	{
88
+		LM_DBG("Parsing [%s] \n", pch);
89
+		if ( INT_LIST_MAX_SIZE == opts->no_elems){
90
+			LM_ERR("INT LIST exceeds max allowed size of: %d \n", INT_LIST_MAX_SIZE);
91
+			return ERROR_IN_PARSING;
92
+		}
93
+
94
+		endptr = NULL;
95
+		val = strtol(pch, &endptr, 10);
96
+
97
+		if ( val < 0 || val> INT_MAX){
98
+			LM_ERR("Parsed value is out of bounds \n");
99
+			return ERROR_IN_PARSING;
100
+		}
101
+
102
+		if (*endptr != '\0'){
103
+			LM_ERR("Parsed value is not integer \n");
104
+			return ERROR_IN_PARSING;
105
+		}
106
+
107
+		opts->value.int_list[opts->no_elems] = (int) val;
108
+		opts->no_elems ++;
109
+		pch = strtok (NULL, ", \t");
110
+	}
111
+
112
+	if ( 0 == opts->no_elems ){
113
+		LM_ERR("The number of int elements cannot be 0 \n");
114
+		return ERROR_IN_PARSING;
115
+	}
116
+	return opts->no_elems;
117
+}
118
+
119
+/**
120
+ * Tries to parse right value string into an option
121
+ *
122
+ * @param src   str source
123
+ * @param opt   the option to be filled
124
+ *
125
+ * @return SUCCESSFUL_PARSING, ERROR_IN_PARSING
126
+ */
127
+int parse_rv_option(str src, option_description* opt){
128
+	long l_val;
129
+	double d_val;
130
+	char* endptr;
131
+	int i, ret = ERROR_IN_PARSING;
132
+
133
+	switch (opt->type)
134
+	{
135
+		case CFG_INT:
136
+			l_val = strtol(src.s, &endptr, 10);
137
+
138
+			if (errno == ERANGE && (l_val == LONG_MAX || l_val == LONG_MIN)) {
139
+					LM_ERR("Conversion error for %s\n", src.s);
140
+			}else
141
+			if (*endptr != '\0'){
142
+				LM_ERR("Value is not integer \n");
143
+			}else
144
+			if ( l_val < 0 || l_val> INT_MAX){
145
+				LM_ERR("The number is out of bounds \n");
146
+			}
147
+			else{// successful rv conversion
148
+				opt->value.int_data = l_val;
149
+				LM_DBG("Key=<%s>, value=<%i> \n", opt->name, opt->value.int_data);
150
+				ret = SUCCESSFUL_PARSING;
151
+			}
152
+			break;
153
+		case CFG_FLOAT:
154
+			d_val = strtod(src.s, &endptr);
155
+
156
+			if (errno == ERANGE && (d_val == -HUGE_VAL || d_val == HUGE_VAL)) {
157
+				LM_ERR("Conversion error for %s\n", src.s);
158
+			}else
159
+			if (*endptr != '\0'){
160
+				LM_ERR("Value is not float \n");
161
+			}else
162
+			if ( d_val < 0.0 || d_val> FLT_MAX){
163
+				LM_ERR("The number is out of bounds \n");
164
+			}else{
165
+				opt->value.float_data = d_val;
166
+				LM_DBG("Key=<%s>, value=<%f> \n", opt->name, opt->value.float_data);
167
+				ret = SUCCESSFUL_PARSING;
168
+			}
169
+			break;
170
+		case CFG_STR:
171
+			if ((src.s[0] != '"') && (src.s[src.len-1] != '"')){
172
+				LM_ERR("Not a string \n");
173
+			}
174
+			else{
175
+				// we now expect a string with NO " inside
176
+				for (i=1; i< src.len-2; i++)
177
+					if (src.s[i] == '"') {
178
+						LM_ERR("Not a string \n");
179
+						return ERROR_IN_PARSING;
180
+					}
181
+				strcpy( opt->value.string_data.s, src.s);
182
+				opt->value.string_data.len = src.len;
183
+				LM_DBG("String Key=<%s>, value=<%s> \n", opt->name, opt->value.string_data.s);
184
+				ret = SUCCESSFUL_PARSING;
185
+			}
186
+			break;
187
+		case CFG_INT_LIST:
188
+			// int list looks like: lv = {NO1 [,NO]*}
189
+			if ((src.len == 2) || ((src.s[0] != '{') && (src.s[src.len-1] != '}'))){
190
+				LM_ERR("Not an int list \n");
191
+			}
192
+			else{
193
+				src.s[src.len-1]='\0';src.s++; src.len = src.len-2;
194
+				//parse a list of comma separated integers
195
+				if ( parse_int_list(src.s, opt) != ERROR_IN_PARSING ){
196
+					// dbg printing of parsed values
197
+					LM_DBG("The number of elements in int_list: %d \n", opt->no_elems);
198
+					for ( i =0; i< opt->no_elems; i++ ){
199
+						LM_DBG("	value=<%d> \n", opt->value.int_list[i]);
200
+					}
201
+					ret = SUCCESSFUL_PARSING;
202
+				}
203
+			}
204
+			break;
205
+		default:
206
+			break;
207
+	}
208
+
209
+	opt->visited = VISITED;
210
+	return ret;
211
+}
212
+
213
+/**
214
+ * Parses the options part in a file and populates a destination structure.
215
+ * The end of the options part should be signaled by end_str string.
216
+ *
217
+ * @param file pointer to source file
218
+ * @param opts destiation option structure to be populated
219
+ * @param no_options expected number of options
220
+ * @param end_str the delimiter that signals end of options zone
221
+ *
222
+ * @return  SUCCESSFUL_PARSING on success, ERROR_IN_PARSING on error
223
+ */
224
+int parse_options(FILE* file, option_description* opts, int no_options, char* end_str){
225
+	str  data, lv_str, rv_str;
226
+	char *pch, buf[CR_MAX_LINE_SIZE], lv[CR_MAX_LINE_SIZE], rv[CR_MAX_LINE_SIZE];
227
+	int opt_pos, full_line_len, ret;
228
+
229
+	ret = ERROR_IN_PARSING;
230
+	data.s = buf;
231
+
232
+	/* refactor data.s = buf using get_non_blank_line */
233
+	while ( get_non_blank_line( &data, CR_MAX_LINE_SIZE, file, &full_line_len) == 0  ) /* read a line */
234
+	{
235
+		LM_DBG("Parsing line: %.*s \n", data.len, data.s);
236
+
237
+		/* parsing stops when end_str is reached: e.g: }, target */
238
+		if ( strncmp(data.s, end_str, strlen(end_str)) == 0){
239
+			LM_DBG("End of options list received \n");
240
+			fseek(file, -full_line_len, SEEK_CUR);
241
+			ret = SUCCESSFUL_PARSING;
242
+			break;
243
+		}
244
+
245
+		/* Line must be of type lv = rv */
246
+		if ( (pch =  strchr(data.s,'=')) == NULL){
247
+			LM_ERR("Parsed line does not contain = delimiter \n");
248
+			break;
249
+		}
250
+		else{ /* parse lv, rv */
251
+			strncpy(lv, data.s, pch-data.s); lv[pch-data.s]='\0';
252
+			strncpy(rv, pch+1, CR_MAX_LINE_SIZE-1); rv[CR_MAX_LINE_SIZE-1]='\0';
253
+			LM_DBG("lv=%s, rv=%s\n", lv, rv);
254
+			lv_str.s=lv; lv_str.len=(int)strlen(lv); trim(&lv_str); lv_str.s[lv_str.len] = '\0';
255
+			rv_str.s=rv; rv_str.len=(int)strlen(rv); trim(&rv_str); rv_str.s[rv_str.len] = '\0';
256
+
257
+			if ( (lv_str.len == 0) || (rv_str.len == 0)){
258
+				LM_ERR("String is not lv = rv \n");
259
+				break;
260
+			}
261
+
262
+			/* Parsing lv */
263
+			if ( (opt_pos = get_option_position(lv_str.s, opts, no_options )) < 0){
264
+				LM_ERR("Unexpected option received: %s \n", lv);
265
+				break;
266
+			}
267
+
268
+			if ( VISITED == opts[opt_pos].visited ){
269
+				LM_ERR("Duplicate option definition %s \n", opts[opt_pos].name);
270
+				break;
271
+			}
272
+
273
+			/* Parsing rv: this is the only case the options parsing continues */
274
+			if ( (rv_str.len != 0 ) && (parse_rv_option(rv_str, &opts[opt_pos]) == ERROR_IN_PARSING ))
275
+			{
276
+				LM_ERR("Error in parsing rv value \n");
277
+				break;
278
+			}
279
+		}
280
+	}
281
+	return ret;
282
+}
283
+
284
+
285
+/**
286
+ * Searches for next content line in the src file
287
+ *
288
+ * @param data the destination trimmed non blank line
289
+ * @param size maximum accepted line length
290
+ * @param file source file
291
+ * @param p_full_len initial lenght of contents line
292
+ *
293
+ * @return  0 on success, -1 on error, 1 on EOF
294
+ */
295
+int get_non_blank_line(str* line, int size, FILE* file, int* pFull_len ){
296
+	char* buf = line->s;
297
+
298
+	while ( line->s = buf, fgets( line->s, size, file) != NULL ) /* read a line */
299
+	{
300
+		*pFull_len = line->len = strlen(line->s);
301
+		LM_DBG("line is %s ", line->s);
302
+		/*  missing newline indicates the line length was too big */
303
+		if ( line->s[line->len-1] != '\n' ){
304
+			LM_ERR("Unaccepted line length \n");
305
+			return -1;
306
+		}
307
+		trim(line);
308
+		if( line->len != 0 ){ /* we have a non blank line */
309
+			line->s[line->len] = '\0'; /* just mark end of string*/
310
+			return 0;
311
+		}
312
+	}
313
+	//EOF
314
+	return 1;
315
+}
316
+
317
+
318
+/**
319
+ * Parses the header of structure part in the source file and retrieves name.
320
+ *
321
+ * @param file pointer to source file
322
+ * @param expected_struct_type name of expected structure
323
+ * @param struct_name the parsed name of the structure.
324
+ *
325
+ * @return SUCCESSFUL_PARSING, EOF_REACHED, ERROR_IN_PARSING
326
+ */
327
+int parse_struct_header(FILE* file, char* expected_struct_type, str* struct_name){
328
+	str data;
329
+	char buf[CR_MAX_LINE_SIZE], name[CR_MAX_LINE_SIZE];
330
+	char str2[CR_MAX_LINE_SIZE], format[CR_MAX_LINE_SIZE];
331
+	int no_toks, full_line_len, ret;
332
+
333
+	ret = ERROR_IN_PARSING;
334
+
335
+	data.s = buf;
336
+	if( get_non_blank_line( &data, CR_MAX_LINE_SIZE, file, &full_line_len) == 1 ) {/* read a line */
337
+		LM_DBG("Graceful exit out of parse struct \n");
338
+		return EOF_REACHED;
339
+	}
340
+
341
+	snprintf(format, CR_MAX_LINE_SIZE-1, " %s %%s %%s %%*s", expected_struct_type);
342
+	no_toks = sscanf(data.s, format, name, str2);
343
+	LM_DBG("no_tok=<%d>, name=<%s> , delim=<%s>\n", no_toks, name, str2);
344
+
345
+	switch (no_toks) {
346
+	/* With 1 token parsed, correct is: "domain_name" OR "domain_name{" */
347
+	case 1:
348
+		if ( name[strlen(name)-1] == '{' ) {
349
+			if (strlen(name) > 1) {
350
+				name[strlen(name)-1]='\0';
351
+				ret = SUCCESSFUL_PARSING;
352
+			}
353
+			else {
354
+				LM_ERR("Domain name seems to be empty \n");
355
+			}
356
+		}
357
+		else{ /* is the following non blank line a "{" ? */
358
+			str new_line;
359
+			char new_line_buf[CR_MAX_LINE_SIZE];
360
+			new_line.s = new_line_buf;
361
+
362
+			if ( get_non_blank_line(&new_line, CR_MAX_LINE_SIZE, file, &full_line_len) != 0 ) {
363
+				LM_ERR("Unexpected end of file while waiting for { \n");
364
+			} else
365
+			if ( strncmp(new_line.s, "{", 1) != 0) {
366
+				LM_ERR("Unexpected token while waiting for { \n");
367
+			}
368
+			else
369
+				ret = SUCCESSFUL_PARSING;
370
+		}
371
+		break;
372
+	/* with 2 tokens parsed, the second must be "{" */
373
+	case 2:
374
+		if (( strncmp(str2, "{", 1) != 0))
375
+			LM_ERR("Wrongly formatted line: %s\n", data.s);
376
+		else
377
+			ret = SUCCESSFUL_PARSING;
378
+		break;
379
+	default:
380
+		LM_ERR("Wrong number of tokens in line: %s\n", data.s);
381
+	}
382
+
383
+	if ( SUCCESSFUL_PARSING == ret ){
384
+		LM_DBG( "Sucessfully parsed struct %s - <%s> header\n", expected_struct_type, name);
385
+		struct_name->len = strlen(name);
386
+		memcpy(struct_name->s, name, struct_name->len);
387
+		struct_name->s[struct_name->len]='\0';
388
+	}
389
+	else
390
+		fseek(file, -full_line_len, SEEK_CUR);
391
+
392
+	return ret;
393
+}
394
+
395
+int parse_struct_stop(FILE* file){
396
+	char buf[CR_MAX_LINE_SIZE];
397
+	str data;
398
+	int full_line_len;
399
+	data.s = buf;
400
+
401
+	if ( get_non_blank_line(&data, CR_MAX_LINE_SIZE, file, &full_line_len) == -1 )	{
402
+		LM_INFO("EOF received \n");
403
+		return ERROR_IN_PARSING;
404
+	}
405
+
406
+	if (strcmp(data.s, "}") != 0){
407
+		LM_INFO("Unexpected <%s> while waiting for } \n", data.s);
408
+		return ERROR_IN_PARSING;
409
+	}
410
+	return SUCCESSFUL_PARSING;
411
+}
0 412
new file mode 100644
... ...
@@ -0,0 +1,92 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 1&1 Internet AG
5
+ *
6
+ * This file is part of SIP-router, a free SIP server.
7
+ *
8
+ * SIP-router is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * SIP-router is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program; if not, write to the Free Software
20
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
+ */
22
+
23
+/*!
24
+ * \file parser_carrierroute.h
25
+ * \brief Functions for parsing the config file of cr when using file mode.
26
+ * \ingroup carrierroute
27
+ * - Module; \ref carrierroute
28
+ */
29
+
30
+#ifndef PARSER_CARRIERROUTE_H_
31
+#define PARSER_CARRIERROUTE_H_
32
+
33
+#include <stdio.h>
34
+#include <string.h>
35
+#include <ctype.h>
36
+#include <stdlib.h>
37
+#include <limits.h>
38
+#include <float.h>
39
+#include <math.h>
40
+#include <errno.h>
41
+#include "../../str.h"
42
+#include "../../mem/shm_mem.h"
43
+#include "../../mem/mem.h"
44
+
45
+#define CR_MAX_LINE_SIZE 256
46
+#define MAX_FIELD_NAME 60
47
+#define INT_LIST_MAX_SIZE 10
48
+
49
+#define NOT_VISITED 0
50
+#define VISITED 1
51
+
52
+#define EXPECTED_END_OF_OPTS 0
53
+#define ERROR_IN_PARSING  -1
54
+#define SUCCESSFUL_PARSING 1
55
+#define EOF_REACHED 2
56
+
57
+#define DEFAULT_DOMAIN_NUM 16
58
+#define MAX_DOMAIN_NUM 64 // must be a power of 2
59
+
60
+enum opt_type { CFG_STR=0, CFG_INT, CFG_FLOAT, CFG_INT_LIST, MAX_OPTS};
61
+
62
+union opt_data{
63
+	int int_data;
64
+	float float_data;
65
+	str string_data;
66
+	int  int_list[INT_LIST_MAX_SIZE];
67
+};
68
+
69
+typedef struct {
70
+	char           	name[MAX_FIELD_NAME];
71
+	enum opt_type	type;
72
+	union opt_data	value;
73
+	int         	visited;
74
+	int 		no_elems; // TBD: name should suggest int_list_no_elems
75
+	char		str_buf[CR_MAX_LINE_SIZE];
76
+} option_description;
77
+
78
+int get_non_blank_line(str* data, int size, FILE* file, int* full_line_len);
79
+
80
+int get_option_position(const char* opt_name, const option_description* opt_list, int no_options);
81
+
82
+int parse_options(FILE* file, option_description* opts, int no_options, char* end_str);
83
+
84
+int parse_struct_header(FILE* file, char* expected_struct_type, str* struct_name);
85
+
86
+int next_token_is_eof(FILE* file);
87
+
88
+int next_token_is_struct_stop(FILE* file);
89
+
90
+int parse_struct_stop(FILE* file);
91
+
92
+#endif /* PARSER_CARRIERROUTE_H_ */