1 /*
2  * Copyright (c) 2000 Andrew Ferguson <andrew@owsla.cjb.net>
3  * Copyright (c) 1998 Sasha Vasko <sasha at aftercode.net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  */
20 
21 #undef LOCAL_DEBUG
22 #undef DO_CLOCKING
23 #undef UNKNOWN_KEYWORD_WARNING
24 
25 #include "../configure.h"
26 
27 #include <fcntl.h>
28 #if HAVE_SYS_TYPES_H
29 #include <sys/types.h>
30 #endif
31 #if HAVE_SYS_STAT_H
32 #include <sys/stat.h>
33 #endif
34 #include <unistd.h>
35 
36 #ifdef DO_CLOCKING
37 #if TIME_WITH_SYS_TIME
38 #include <sys/time.h>
39 #include <time.h>
40 #else
41 #if HAVE_SYS_TIME_H
42 #include <sys/time.h>
43 #else
44 #include <time.h>
45 #endif
46 #endif
47 #endif
48 #include "asapp.h"
49 #include "afterstep.h"
50 #include "parser.h"
51 #include "freestor.h"
52 #include "functions.h"
53 #include "../libAfterBase/xml.h"
54 
55 char *_disabled_keyword = DISABLED_KEYWORD;
56 char *_unknown_keyword = "unknown";
57 
58 static ASHashTable *Keyword2IDHash = NULL;
59 
60 
register_keyword_id(const char * keyword,int id)61 void register_keyword_id (const char *keyword, int id)
62 {
63 	ASHashData hdata = { 0 };
64 
65 	if (Keyword2IDHash == NULL)
66 		Keyword2IDHash = create_ashash (0, NULL, NULL, NULL);
67 
68 	hdata.cptr = (char *)keyword;
69 	add_hash_item (Keyword2IDHash, AS_HASHABLE (id), hdata.vptr);
70 }
71 
keyword_id2keyword(int id)72 const char *keyword_id2keyword (int id)
73 {
74 	ASHashData hdata = { 0 };
75 
76 	if (Keyword2IDHash == NULL)
77 		return _unknown_keyword;
78 
79 	if (get_hash_item (Keyword2IDHash, AS_HASHABLE (id), &hdata.vptr) ==
80 			ASH_Success)
81 		return hdata.cptr;
82 
83 	return _unknown_keyword;
84 }
85 
86 
flush_keyword_ids()87 void flush_keyword_ids ()
88 {
89 	if (Keyword2IDHash != NULL)
90 		destroy_ashash (&Keyword2IDHash);
91 }
92 
BuildHash(SyntaxDef * syntax)93 void BuildHash (SyntaxDef * syntax)
94 {
95 	register int i;
96 
97 	LOCAL_DEBUG_CALLER_OUT ("syntax = \"%s\"", syntax->display_name);
98 
99 	if (syntax->term_hash_size <= 0)
100 		syntax->term_hash_size = TERM_HASH_SIZE;
101 	if (syntax->term_hash == NULL) {
102 		syntax->term_hash =
103 				create_ashash (syntax->term_hash_size, option_hash_value,
104 											 option_compare, NULL);
105 		LOCAL_DEBUG_OUT ("created hash %p", syntax->term_hash);
106 	}
107 	LOCAL_DEBUG_OUT ("adding hash entries ... %s", "");
108 	for (i = 0; syntax->terms[i].keyword; i++) {
109 		add_hash_item (syntax->term_hash,
110 									 AS_HASHABLE (syntax->terms[i].keyword),
111 									 (void *)&(syntax->terms[i]));
112 		register_keyword_id (syntax->terms[i].keyword, syntax->terms[i].id);
113 	}
114 	LOCAL_DEBUG_OUT ("%d hash entries added.", i);
115 }
116 
PrepareSyntax(SyntaxDef * syntax)117 void PrepareSyntax (SyntaxDef * syntax)
118 {
119 	if (syntax) {
120 		register int i;
121 
122 		LOCAL_DEBUG_OUT ("syntax = \"%s\", recursion = %d",
123 										 syntax->display_name, syntax->recursion);
124 		if (syntax->recursion > 0)
125 			return;
126 		syntax->recursion++;
127 
128 		if (syntax->term_hash == NULL)
129 			BuildHash (syntax);
130 
131 		for (i = 0; syntax->terms[i].keyword; i++)
132 			if (syntax->terms[i].sub_syntax)
133 				PrepareSyntax (syntax->terms[i].sub_syntax);
134 
135 		syntax->recursion--;
136 	}
137 }
138 
FreeSyntaxHash(SyntaxDef * syntax)139 void FreeSyntaxHash (SyntaxDef * syntax)
140 {
141 	if (syntax) {
142 		register int i;
143 
144 		LOCAL_DEBUG_CALLER_OUT ("syntax = \"%s\", recursion = %d",
145 														syntax->display_name, syntax->recursion);
146 
147 		if (syntax->recursion > 0)
148 			return;
149 		syntax->recursion++;
150 
151 		if (syntax->term_hash)
152 			destroy_ashash (&(syntax->term_hash));
153 
154 		for (i = 0; syntax->terms[i].keyword; i++)
155 			if (syntax->terms[i].sub_syntax)
156 				/* this should prevent us from endless recursion */
157 				if (syntax->terms[i].sub_syntax->term_hash != NULL)
158 					FreeSyntaxHash (syntax->terms[i].sub_syntax);
159 
160 		syntax->recursion--;
161 
162 	}
163 }
164 
165 /* Syntax and storage stack operations */
PushSyntax(ConfigDef * config,SyntaxDef * syntax)166 void PushSyntax (ConfigDef * config, SyntaxDef * syntax)
167 {
168 	SyntaxStack *pnew = (SyntaxStack *) safecalloc (1, sizeof (SyntaxStack));
169 
170 	/* saving our status to be able to come back to it later */
171 	if (config->current_syntax) {
172 		config->current_syntax->current_term = config->current_term;
173 		config->current_syntax->current_flags = config->current_flags;
174 	}
175 	config->current_flags = CF_NONE;
176 	config->current_term = NULL;
177 	pnew->next = config->current_syntax;
178 	pnew->syntax = syntax;
179 	if (config->syntax && syntax->terminator == '\n') {	/* handling prepending */
180 		SyntaxDef *csyntax = config->syntax;
181 		int len = strlen (csyntax->prepend_sub);
182 		register int i;
183 		char *tmp;
184 
185 		if (config->current_prepend_allocated - config->current_prepend_size <=
186 				len) {
187 			config->current_prepend_allocated += len + 1;
188 			tmp = safemalloc (config->current_prepend_allocated);
189 			if (config->current_prepend) {
190 				strcpy (tmp, config->current_prepend);
191 				free (config->current_prepend);
192 			}
193 			config->current_prepend = tmp;
194 		}
195 		tmp = &(config->current_prepend[config->current_prepend_size]);
196 
197 		for (i = 0; i <= len; i++)
198 			tmp[i] = csyntax->prepend_sub[i];
199 
200 		config->current_prepend_size += len;
201 
202 	}
203 	LOCAL_DEBUG_OUT ("%p: \"%s\", old is %p:  current_prepend = [%s]\n",
204 									 syntax, syntax->display_name, config->syntax,
205 									 config->current_prepend);
206 	config->current_syntax = pnew;
207 	config->syntax = syntax;
208 }
209 
PopSyntax(ConfigDef * config)210 int PopSyntax (ConfigDef * config)
211 {
212 	if (config->current_syntax->next) {
213 		SyntaxStack *pold = config->current_syntax;
214 
215 		config->current_syntax = config->current_syntax->next;
216 		config->syntax = config->current_syntax->syntax;
217 
218 		/* restoring our status */
219 		config->current_term = config->current_syntax->current_term;
220 		config->current_flags = config->current_syntax->current_flags;
221 
222 		LOCAL_DEBUG_OUT ("%p: \"%s\", old is %p: term = \"%s\"\n",
223 										 config->syntax, config->syntax->display_name,
224 										 pold->syntax,
225 										 config->current_term ? config->
226 										 current_term->keyword : "");
227 
228 		if (pold->syntax->terminator == '\n') {
229 			if ((config->current_prepend_size -=
230 					 strlen (config->syntax->prepend_sub)) >= 0)
231 				config->current_prepend[config->current_prepend_size] = '\0';
232 		}
233 /*fprintf( stderr, "PopSyntax(%s) current_prepend = [%s]\n", pold->syntax->display_name, config->current_prepend );
234 */
235 		free (pold);
236 		return 1;
237 	}
238 	return 0;
239 }
240 
PushStorage(ConfigDef * config,void * storage)241 void PushStorage (ConfigDef * config, void *storage)
242 {
243 	StorageStack *pnew =
244 			(StorageStack *) safecalloc (1, sizeof (StorageStack));
245 
246 	pnew->storage = storage;
247 	pnew->next = config->current_tail;
248 	config->current_tail = pnew;
249 }
250 
PopStorage(ConfigDef * config)251 int PopStorage (ConfigDef * config)
252 {
253 	if (config->current_tail)
254 		if (config->current_tail->next) {
255 			StorageStack *pold = config->current_tail;
256 
257 			config->current_tail = config->current_tail->next;
258 			free (pold);
259 			return 1;
260 		}
261 	return 0;
262 }
263 
264 
config_error(ConfigDef * config,char * err_text)265 void config_error (ConfigDef * config, char *err_text)
266 {
267 	if (config) {
268 		char *eol = strchr (config->tline, '\n');
269 
270 		if (eol)
271 			*eol = '\0';
272 		show_error ("in %s (line %d):%s[%.50s]",
273 								config->current_syntax->syntax->display_name,
274 								config->line_count, err_text, config->tline);
275 		if (eol)
276 			*eol = '\n';
277 	}
278 }
279 
280 /* Creating and Initializing new ConfigDef */
NewConfig(char * myname,SyntaxDef * syntax,ConfigDataType type,ConfigData source,SpecialFunc special,int create)281 ConfigDef *NewConfig (char *myname, SyntaxDef * syntax,
282 											ConfigDataType type, ConfigData source,
283 											SpecialFunc special, int create)
284 {
285 	ConfigDef *new_conf;
286 
287 	if (myname == NULL)
288 		return NULL;
289 
290 	new_conf = (ConfigDef *) safecalloc (1, sizeof (ConfigDef));
291 	new_conf->special = special;
292 	new_conf->fd = -1;
293 	new_conf->fp = NULL;
294 	new_conf->flags = 0;
295 	if (source.vptr != NULL)
296 		switch (type) {
297 		case CDT_Filename:
298 			{
299 				char *realfilename = put_file_home (source.filename);
300 
301 				if (!realfilename) {
302 					free (new_conf);
303 					return NULL;
304 				}
305 				new_conf->fd =
306 #ifdef __CYGWIN__
307 						open (realfilename,
308 									create ? O_CREAT | O_RDONLY | O_BINARY : O_RDONLY,
309 									S_IRUSR | S_IWUSR | S_IRGRP | O_BINARY);
310 #else
311 						open (realfilename, create ? O_CREAT | O_RDONLY : O_RDONLY,
312 									S_IRUSR | S_IWUSR | S_IRGRP);
313 #endif
314 				free (realfilename);
315 				set_flags (new_conf->flags, CP_NeedToCloseFile);
316 			}
317 			break;
318 		case CDT_FilePtr:
319 			new_conf->fp = source.fileptr;
320 			new_conf->fd = fileno (new_conf->fp);
321 			break;
322 		case CDT_FileDesc:
323 			new_conf->fd = *source.filedesc;
324 			break;
325 		case CDT_Data:
326 			break;
327 		case CDT_FilePtrAndData:
328 			new_conf->fp = source.fileptranddata->fp;
329 			new_conf->fd = fileno (new_conf->fp);
330 			set_flags (new_conf->flags, CP_ReadLines);
331 			break;
332 
333 		}
334 
335 	if (new_conf->fd != -1 && new_conf->fp == NULL) {
336 		new_conf->fp =
337 				fdopen (new_conf->fd,
338 								get_flags (new_conf->flags, CP_ReadLines) ? "rt" : "rb");
339 		set_flags (new_conf->flags, CP_NeedToFCloseFile);
340 	}
341 
342 
343 	new_conf->myname = mystrdup (myname);
344 	new_conf->current_syntax = NULL;
345 	new_conf->current_tail = NULL;
346 	new_conf->current_prepend = NULL;
347 	new_conf->current_prepend_size = 0;
348 	new_conf->current_prepend_allocated = 0;
349 
350 	PushSyntax (new_conf, syntax);
351 
352 	PrepareSyntax (syntax);
353 
354 	/* allocated to store lines read from the file */
355 	new_conf->buffer = NULL;
356 	new_conf->buffer_size = 0;
357 	new_conf->bytes_in = 0;
358 
359 	/* this is the current parsing information */
360 	new_conf->tline = new_conf->tdata = new_conf->tline_start = NULL;
361 	new_conf->current_term = NULL;
362 	new_conf->current_data_size = MAXLINELENGTH + 1;
363 	new_conf->current_data =
364 			(char *)safemalloc (new_conf->current_data_size);
365 	new_conf->current_data_len = 0;
366 	new_conf->cursor = NULL;
367 	return new_conf;
368 }
369 
370 /* reader initialization */
InitConfigReader(char * myname,SyntaxDef * syntax,ConfigDataType type,ConfigData source,SpecialFunc special)371 ConfigDef *InitConfigReader (char *myname, SyntaxDef * syntax,
372 														 ConfigDataType type, ConfigData source,
373 														 SpecialFunc special)
374 {
375 	ConfigDef *new_conf =
376 			NewConfig (myname, syntax, type, source, special, False);
377 
378 	if (new_conf == NULL)
379 		return NULL;
380 	if (source.vptr == NULL) {
381 		DestroyConfig (new_conf);
382 		return NULL;
383 	}
384 
385 	if (type == CDT_Data) {
386 		/* allocate to store entire data */
387 		new_conf->buffer_size = strlen (source.data) + 1;
388 		new_conf->buffer = (char *)safemalloc (new_conf->buffer_size);
389 		strcpy (new_conf->buffer, source.data);
390 		new_conf->bytes_in = new_conf->buffer_size - 1;
391 	} else if (type == CDT_FilePtrAndData) {
392 		FilePtrAndData *fpd = source.fileptranddata;
393 		int buf_len = fpd->data ? strlen (fpd->data) : 0;
394 
395 		if (buf_len < MAXLINELENGTH)
396 			buf_len = MAXLINELENGTH;
397 		new_conf->buffer_size = buf_len;
398 		new_conf->buffer = (char *)safemalloc (buf_len + 1);
399 		if (fpd->data)
400 			strcpy (new_conf->buffer, fpd->data);
401 		else
402 			new_conf->buffer[0] = '\0';
403 	} else {
404 		new_conf->buffer_size = MAXLINELENGTH + 1;
405 		new_conf->buffer = (char *)safemalloc (new_conf->buffer_size);
406 		new_conf->buffer[0] = '\0';
407 	}
408 
409 	/* this is the current parsing information */
410 	new_conf->cursor = &(new_conf->buffer[0]);
411 	new_conf->line_count = 1;
412 
413 	return new_conf;
414 }
415 
416 /* debugging stuff */
417 #ifdef DEBUG_PARSER
PrintSyntax(SyntaxDef * syntax)418 void PrintSyntax (SyntaxDef * syntax)
419 {
420 	if (syntax->recursion > 0)
421 		return;
422 	syntax->recursion++;
423 
424 	fprintf (stderr, "\nSentence Terminator: [0x%2.2x]", syntax->terminator);
425 	fprintf (stderr, "\nConfig Terminator:   [0x%2.2x]",
426 					 syntax->file_terminator);
427 	syntax->recursion--;
428 }
429 
430 
PrintConfigReader(ConfigDef * config)431 void PrintConfigReader (ConfigDef * config)
432 {
433 	PrintSyntax (config->syntax);
434 }
435 
436 #endif
437 
438 
439 /* reader de-initialization */
DestroyConfig(ConfigDef * config)440 void DestroyConfig (ConfigDef * config)
441 {
442 	free (config->myname);
443 	if (config->buffer)
444 		free (config->buffer);
445 	if (config->current_data)
446 		free (config->current_data);
447 	while (PopSyntax (config)) ;
448 	if (config->current_syntax)
449 		free (config->current_syntax);
450 	while (PopStorage (config)) ;
451 	if (config->current_prepend)
452 		free (config->current_prepend);
453 	if (config->current_tail)
454 		free (config->current_tail);
455 	if (config->syntax)
456 		FreeSyntaxHash (config->syntax);
457 	if (get_flags (config->flags, CP_NeedToCloseFile) && config->fd != -1)
458 		close (config->fd);
459 	if (get_flags (config->flags, CP_NeedToFCloseFile) && config->fp != NULL)
460 		fclose (config->fp);
461 	free (config);
462 }
463 
print_trimmed_str(char * prompt,char * str)464 void print_trimmed_str (char *prompt, char *str)
465 {
466 #ifdef LOCAL_DEBUG
467 	int i = 0;
468 	char tmp;
469 
470 	while (str[i] && i < 20) {
471 		if (str[i] == '\n')
472 			str[i] = '}';
473 		++i;
474 	}
475 	tmp = str[i];
476 	str[i] = '\0';
477 	LOCAL_DEBUG_OUT ("first %d chars of %s:\"%s\"", i, prompt, str);
478 	str[i] = tmp;
479 	while (--i >= 0)
480 		if (str[i] == '}')
481 			str[i] = '\n';
482 #endif
483 }
484 
485 void
ProcessSubSyntax(ConfigDef * config,void * storage,SyntaxDef * syntax)486 ProcessSubSyntax (ConfigDef * config, void *storage, SyntaxDef * syntax)
487 {
488 	PushStorage (config, storage);
489 	LOCAL_DEBUG_OUT ("Old_syntax = \"%s\", new_syntax = \"%s\"",
490 									 config->syntax->display_name, syntax->display_name);
491 	if (config->syntax->terminator == syntax->file_terminator) {	/* need to push back term's data into config buffer */
492 		int skip_tokens = 0;
493 
494 		config->cursor = config->tdata;
495 		skip_tokens = GetTermUseTokensCount (config->current_term);
496 		if (get_flags
497 				(config->current_term->flags,
498 				 TF_NAMED | TF_INDEXED | TF_DIRECTION_INDEXED))
499 			++skip_tokens;
500 		if (skip_tokens > 0)
501 			config->cursor = tokenskip (config->cursor, skip_tokens);
502 	} else if (config->syntax->terminator == syntax->terminator) {	/* need to push back entire term's line into config buffer */
503 		config->cursor = config->tline_start;
504 	}
505 
506 /*  fprintf( stderr, "\nprocessing as subsyntax: [%s]", config->cursor );
507 */
508 	PushSyntax (config, syntax);
509 }
510 
ProcessStatement(ConfigDef * config)511 void ProcessStatement (ConfigDef * config)
512 {
513 	if (config && config->statement_handler)
514 		config->statement_handler (config);
515 }
516 
GetToNextLine(ConfigDef * config)517 char *GetToNextLine (ConfigDef * config)
518 {
519 	register char terminator = config->syntax->terminator;
520 	register char file_terminator = config->syntax->file_terminator;
521 	register char *cur = config->cursor, *buffer_end =
522 			&(config->buffer[config->bytes_in]);
523 
524 	for (; *cur != '\0' && cur < buffer_end; cur++) {
525 		if (*cur == '\n')
526 			config->line_count++;
527 		if (*cur == terminator)
528 			break;
529 		if (*cur == file_terminator)
530 			break;
531 	}
532 	if (cur < buffer_end && *cur != '\0')
533 		cur++;
534 
535 	config->cursor = cur;
536 
537 	if (cur >= buffer_end) {
538 		if (config->fp) {
539 			if (get_flags (config->flags, CP_ReadLines)) {
540 				LOCAL_DEBUG_OUT ("Reading Lines ...%s", "");
541 				if (!fgets (config->buffer, config->buffer_size - 1, config->fp))
542 					return NULL;
543 				config->bytes_in = strlen (config->buffer);
544 				config->cursor = &(config->buffer[0]);
545 			} else {
546 				register int i;
547 				register char *ptr = config->buffer;
548 
549 				LOCAL_DEBUG_OUT ("Reading Buffer ...%s", "");
550 				config->bytes_in = read (config->fd, ptr, config->buffer_size - 1);
551 				if (config->bytes_in <= 0)
552 					return NULL;
553 				print_trimmed_str ("new data begins with", ptr);
554 				/* now we want to get back to the last end-of-line
555 				   so not to break statements in half */
556 				for (i = config->bytes_in - 1; i >= 0; i--)
557 					if (ptr[i] == '\n')
558 						break;
559 				i++;
560 				if (i > 0) {
561 					lseek (config->fd, i - (config->bytes_in), SEEK_CUR);
562 					config->bytes_in = i;
563 				}
564 				ptr[config->bytes_in] = '\0';
565 #ifdef __CYGWIN__								/* fuck Microsoft !!!!! */
566 				while (--i >= 0)
567 					if (ptr[i] == 0x0D)
568 						ptr[i] = 0x0A;
569 #endif
570 			}
571 			config->cursor = &(config->buffer[0]);
572 		} else
573 			return NULL;
574 	} else if (*cur == file_terminator)
575 		return NULL;
576 
577 	return (config->cursor);
578 }
579 
580 /* this function finds next valid statement:
581    - not comments,
582    - not an empty line,
583    - if prepended with * - should have MyName at the beginning
584    and :
585    - sets tline to the beginning of the keyword (not MyName),
586    - copies arguments into the current_data
587    - sets current_data_len
588    it will read data from the file as needed untill the end of file
589    or config->syntax.file_terminator or EOF is reached
590    Return: NULL if end of config reached, otherwise same as tline.
591  */
GetNextStatement(ConfigDef * config)592 char *GetNextStatement (ConfigDef * config)
593 {
594 	char *cur = config->cursor;		/* don't forget to save it back ! */
595 	register char *data;
596 	char terminator = config->syntax->terminator;
597 	char file_terminator = config->syntax->file_terminator;
598 
599 	while (1) {
600 		config->tline_start = cur;	/* remember beginning of the entire config option */
601 		for (; *cur != '\0' && *cur != terminator; cur++) {
602 			if (*cur == file_terminator)
603 				return NULL;
604 #ifdef __CYGWIN__
605 			if (!isspace ((int)*cur) && *cur != 0x0D)
606 #else
607 			if (!isspace ((int)*cur))
608 #endif
609 			{
610 				register int i;
611 
612 				config->current_flags = CF_NONE;
613 
614 				if (*cur == COMMENTS_CHAR) {	/* let's check for DISABLE keyword */
615 					print_trimmed_str ("comments at", cur);
616 					for (i = 1; i < DISABLED_KEYWORD_SIZE; i++)
617 						if (cur[i] == '\0' || cur[i] != _disabled_keyword[i])
618 							break;
619 					if (i == DISABLED_KEYWORD_SIZE) {	/* comments - skip entire line */
620 						config->current_flags |= CF_DISABLED_OPTION;
621 						/* let's skip few spaces here */
622 						while (isspace ((int)cur[i]) && cur[i] != terminator)
623 							++i;
624 						if (cur[i] == '\0' || cur[i] == terminator)
625 							break;						/* not a valid option */
626 						cur = &cur[i];
627 					} else
628 						set_flags (config->current_flags, CF_PUBLIC_OPTION);	/* comments are always public */
629 				}
630 
631 				if (*cur == MYNAME_CHAR) {	/* check if we have MyName here */
632 					register char *mname = config->myname;
633 
634 					print_trimmed_str ("private option at", cur);
635 					++cur;
636 					for (i = 0; mname[i] != '\0' && cur[i] != '\0'; ++i)
637 						if (tolower (mname[i]) != tolower (cur[i]))
638 							break;
639 					if (mname[i] != '\0') {	/* that was a foreign optiion - belongs to the other executable */
640 						if (get_flags (config->flags, CP_IgnoreForeign))
641 							break;
642 						set_flags (config->current_flags, CF_FOREIGN_OPTION);
643 						/* keeping *MyName part of the token  without leading * for foreign options */
644 					} else
645 						cur += i;						/* skipping *MyName part of the token */
646 				} else {
647 					if (*cur == terminator || *cur == file_terminator)	/* public option keyword may not be empty ! */
648 						break;
649 					if (get_flags (config->flags, CP_IgnorePublic))
650 						break;
651 					set_flags (config->current_flags, CF_PUBLIC_OPTION);
652 					print_trimmed_str ("public option at", cur);
653 				}
654 				config->tline = cur;		/*that will be the begginnig of the term */
655 
656 				/* now we should copy everything from after the first space to
657 				   config->current_data and set current_data_len ; (unless its a comment) */
658 				if (*cur != COMMENTS_CHAR) {
659 					i = 0;
660 					while (cur[i] && !isspace ((int)cur[i]) && cur[i] != terminator
661 								 && cur[i] != file_terminator)
662 						++i;
663 					while (isspace ((int)cur[i]) && cur[i] != terminator
664 								 && cur[i] != file_terminator)
665 						++i;
666 				} else
667 					i = 1;
668 
669 				cur = &(cur[i]);				/* that will be the beginning of our data */
670 				config->tdata = cur;
671 				print_trimmed_str ("data start at :", cur);
672 				data = config->current_data;
673 				for (i = 0;
674 						 cur[i] && cur[i] != terminator && cur[i] != file_terminator;
675 						 ++i) {
676 					/* buffer overrun prevention */
677 					if (i >= config->current_data_size) {
678 						config->current_data_size += MAXLINELENGTH >> 3;
679 						config->current_data =
680 								(char *)realloc (config->current_data,
681 																 config->current_data_size);
682 						if (config->current_data == NULL) {
683 							config_error (config,
684 														"Not enough memory to hold option's arguments");
685 							return NULL;
686 						}
687 						data = config->current_data;
688 					}
689 					data[i] = cur[i];
690 				}
691 				LOCAL_DEBUG_OUT ("%d bytes of data stored", i);
692 				cur = &(cur[i]);
693 				/* now let's go back and remove trailing spaces */
694 				if (config->tdata[0] == file_terminator)
695 					set_flags (config->current_flags, CF_LAST_OPTION);
696 				else {
697 					while (--i >= 0) {
698 						if (config->tdata[i] == file_terminator)
699 							set_flags (config->current_flags, CF_LAST_OPTION);
700 						if (!isspace ((int)data[i]))
701 							break;
702 					}
703 					i++;
704 				}
705 				/* since \0 is our normal data terminator we really should trigger
706 				   end of the configuration when the file ends : */
707 				if (file_terminator == '\0')
708 					clear_flags (config->current_flags, CF_LAST_OPTION);
709 				LOCAL_DEBUG_OUT ("%d bytes of clean data stored", i);
710 				config->current_data_len = i;
711 				config->current_data[i] = '\0';
712 
713 				if (*cur && *cur == terminator)
714 					cur++;
715 				config->cursor = cur;		/* Saving position for future use */
716 				return config->tline;
717 			}
718 		}
719 		/* reading file here */
720 		if ((cur = GetToNextLine (config)) == NULL)
721 			return NULL;
722 	}
723 	return NULL;
724 }
725 
726 
727 TermDef _as_comments_term =
728 		{ TF_NO_MYNAME_PREPENDING, "#", 1, TT_COMMENT, TT_COMMENT, NULL };
729 TermDef _as_unknown_term_public =
730 		{ TF_NO_MYNAME_PREPENDING, "unknown", 7, TT_ANY, TT_ANY, NULL };
731 TermDef _as_unknown_term_private =
732 		{ 0, "unknown", 7, TT_ANY, TT_ANY, NULL };
733 
FindStatementTerm(char * tline,SyntaxDef * syntax)734 TermDef *FindStatementTerm (char *tline, SyntaxDef * syntax)
735 {
736 	ASHashData hdata = { 0 };
737 	LOCAL_DEBUG_OUT
738 			("looking for pterm for [%c%c%c...]in hash table  %p of the syntax %s ",
739 			 tline[0], tline[1], tline[2], syntax->term_hash,
740 			 syntax->display_name);
741 	if (tline[0] == COMMENTS_CHAR)
742 		return &_as_comments_term;
743 
744 	if (isspace (tline[0])) {
745 		int i = 0;
746 
747 		while (isspace (tline[i]))
748 			++i;
749 		if (tline[i] == '~')
750 			if (get_hash_item
751 					(syntax->term_hash, AS_HASHABLE (&tline[i]),
752 					 &hdata.vptr) == ASH_Success)
753 				return hdata.vptr;
754 	}
755 	if (get_hash_item (syntax->term_hash, AS_HASHABLE (tline), &hdata.vptr)
756 			!= ASH_Success)
757 		hdata.vptr = NULL;
758 	LOCAL_DEBUG_OUT ("FOUND pterm %p", hdata.vptr);
759 	return hdata.vptr;
760 }
761 
762 
763 
764 /* main parsing procedure */
config2tree_storage(ConfigDef * config,ASTreeStorageModel ** tail)765 int config2tree_storage (ConfigDef * config, ASTreeStorageModel ** tail)
766 {
767 	int TopLevel = 0;
768 
769 	PushStorage (config, tail);
770 	/* get line */
771 	while (!TopLevel) {
772 		while (GetNextStatement (config)) {	/* untill not end of text */
773 			TermDef *pterm = NULL;
774 			unsigned long flags = 0;
775 
776 #ifdef DEBUG_PARSER
777 			fprintf (stderr, "\nSentence Found:[%.50s ...]\n,\tData=\t[%s]",
778 							 config->tline, config->current_data);
779 			fprintf (stderr, "\nLooking for the Term...");
780 #endif
781 			/* find term */
782 			if (get_flags (config->current_flags, CF_FOREIGN_OPTION)) {
783 				int i = 0;
784 
785 				do {
786 					++i;									/* it's ok - we can start with 1, since myname should have at least 1 char */
787 					pterm = FindStatementTerm (&(config->tline[i]), config->syntax);
788 				}
789 				while (pterm == NULL && !isspace (config->tline[i])
790 							 && config->tline[i] != '\0');
791 			} else
792 				pterm = FindStatementTerm (config->tline, config->syntax);
793 
794 			if (pterm == NULL)
795 				pterm = get_flags (config->current_flags, CF_PUBLIC_OPTION) ?
796 						&_as_unknown_term_public : &_as_unknown_term_private;
797 
798 			config->current_term = pterm;
799 
800 			LOCAL_DEBUG_OUT ("Term:[%s]", config->current_term->keyword);
801 			if (isspace (config->tline[0]) && pterm->keyword_len > 0 && mystrncasecmp (pterm->keyword, config->current_data, pterm->keyword_len) == 0) {	/* we need to skip one token in current_data :  */
802 				char *src = tokenskip (config->current_data, 1);
803 				char *dst = config->current_data;
804 
805 				if (src != dst) {
806 					int i = 0;
807 
808 					do {
809 						dst[i] = src[i];
810 					}
811 					while (src[++i]);
812 					dst[i] = '\0';
813 				}
814 			}
815 			if (get_flags (pterm->flags, TF_OBSOLETE))
816 				config_error (config,
817 											"Heh, It seems that I've encountered obsolete config option. I'll ignore it for now, Ok ?!");
818 			if (get_flags (pterm->flags, TF_PHONY))
819 				set_flags (config->flags, CF_PHONY_OPTION);
820 			if (get_flags (pterm->flags, TF_SPECIAL_PROCESSING)) {
821 				if (config->special) {
822 					flags = (*(config->special)) (config);
823 					if (get_flags (flags, SPECIAL_BREAK))
824 						break;
825 					if (get_flags (flags, SPECIAL_STORAGE_ADDED)) {
826 						ASTreeStorageModel **ctail = config->current_tail->storage;
827 
828 						while (*ctail)
829 							ctail = &((*ctail)->next);
830 						tail = config->current_tail->storage = ctail;
831 					}
832 				}
833 			}
834 			if (!get_flags (flags, SPECIAL_SKIP))
835 				ProcessStatement (config);
836 			/* Process Statement may alter config's state as it may dive into subconfig -
837 			 * must make sure that did not happen : */
838 			if (config->current_term) {
839 				if (get_flags (config->current_term->flags, TF_SYNTAX_TERMINATOR)
840 						|| IsLastOption (config))
841 					break;
842 			}
843 		}														/* end while( GetNextStatement() ) */
844 		/* trying to see if we can get to higher level syntax */
845 		if (!PopSyntax (config))
846 			TopLevel = 1;
847 		if (!PopStorage (config))
848 			TopLevel = 1;
849 		while ((TopLevel != 1) &&
850 					 ((config->current_term &&
851 						 (config->current_term->flags & TF_SYNTAX_TERMINATOR))
852 						|| IsLastOption (config))) {
853 			if (!PopSyntax (config))
854 				TopLevel = 1;
855 			if (!PopStorage (config))
856 				TopLevel = 1;
857 		}
858 	}															/* end while( !TopLevel ) */
859 
860 	return 1;
861 }
862 
863 
864 
865 /****************************************************************************************/
866 /*                        Assorted utility functions                                    */
867 /****************************************************************************************/
868 /* service functions */
FlushConfigBuffer(ConfigDef * config)869 void FlushConfigBuffer (ConfigDef * config)
870 {
871 	config->buffer[0] = '\0';
872 	config->cursor = &(config->buffer[0]);
873 }
874 
875 /***************************************************************************/
876 /***************************************************************************/
877 /*                   config Writing stuff                                  */
878 /***************************************************************************/
879 /* writer initialization */
InitConfigWriter(char * myname,SyntaxDef * syntax,ConfigDataType type,ConfigData source)880 ConfigDef *InitConfigWriter (char *myname, SyntaxDef * syntax,
881 														 ConfigDataType type, ConfigData source)
882 {
883 #ifdef WITH_CONFIG_WRITER
884 	ConfigDef *new_conf =
885 			NewConfig (myname, syntax, type, source, NULL, True);
886 
887 	if (new_conf == NULL)
888 		return NULL;
889 	if (source.vptr != NULL) {
890 		if (type == CDT_Data) {
891 			if ((new_conf->buffer_size = strlen (source.data)) > 0) {
892 				new_conf->buffer = safemalloc (++(new_conf->buffer_size));
893 				strcpy (new_conf->buffer, source.data);
894 			}
895 		} else {										/* reading entire file in memory here */
896 			struct stat file_stats;
897 
898 			if (new_conf->fd != -1)
899 				if (fstat (new_conf->fd, &file_stats) == 0) {
900 					if ((new_conf->buffer_size = file_stats.st_size) > 0) {
901 						int bytes_read;
902 
903 						new_conf->buffer =
904 								(char *)safemalloc (++new_conf->buffer_size);
905 						if ((bytes_read =
906 								 read (new_conf->fd, new_conf->buffer,
907 											 file_stats.st_size)) > 0)
908 							new_conf->buffer[bytes_read] = '\0';
909 						else {
910 							free (new_conf->buffer);
911 							new_conf->buffer = NULL;
912 							new_conf->buffer_size = 0;
913 						}
914 					} else {							/* file is empty, so continuing onto creating a new file */
915 						new_conf->buffer = safemalloc (1);
916 						new_conf->buffer[0] = '\0';
917 						new_conf->buffer_size = 1;
918 					}
919 				}
920 		}
921 		if (new_conf->buffer == NULL) {
922 			DestroyConfig (new_conf);
923 			return NULL;
924 		}
925 		new_conf->bytes_in = new_conf->buffer_size - 1;
926 
927 		new_conf->cursor = &(new_conf->buffer[0]);
928 	}
929 
930 	return new_conf;
931 #else
932 	return NULL;
933 #endif
934 }
935 
936 /***************************************************************************/
937 /* writes block of text in to the output buffer, enlarging it if needed.
938  * it starts with block_start and end with block_end or '\0'
939  * if block_end is NULL
940  */
941 
942 void
WriteBlock(struct WriteBuffer * t_buffer,char * block_start,char * block_end)943 WriteBlock (struct WriteBuffer *t_buffer, char *block_start,
944 						char *block_end)
945 {
946 	size_t bytes_to_add;
947 
948 	if (t_buffer == NULL || block_start == NULL)
949 		return;
950 	bytes_to_add =
951 			(block_end == NULL) ? strlen (block_start) : block_end - block_start;
952 	if (bytes_to_add <= 0)
953 		return;
954 	/*make sure we always have 1 byte left at the end ( for trailing 0 ) */
955 	/*                     and 1 byte left for file terminator           */
956 	if (t_buffer->allocated < t_buffer->used + bytes_to_add + 2) {	/* growing buffer by 1/8 of it's size */
957 		size_t add_size =
958 				t_buffer->used + bytes_to_add + 2 - t_buffer->allocated;
959 
960 		if (add_size < (t_buffer->allocated >> 3))
961 			add_size = (t_buffer->allocated >> 3);
962 		if (t_buffer->buffer != NULL && t_buffer->allocated > 0)
963 			t_buffer->buffer =
964 					realloc (t_buffer->buffer, t_buffer->allocated + add_size);
965 		else
966 			t_buffer->buffer = safemalloc (t_buffer->allocated + add_size);
967 		t_buffer->allocated += add_size;
968 	}
969 	memcpy (t_buffer->buffer + t_buffer->used, block_start, bytes_to_add);
970 	t_buffer->used += bytes_to_add;
971 }
972 
973 
parser_add_terminator(char * ptr,char terminator)974 char *parser_add_terminator (char *ptr, char terminator)
975 {
976 	if (*ptr != terminator && (*ptr == '\0' && *(ptr - 1) != terminator))
977 		*(ptr++) = terminator;
978 	return ptr;
979 }
980 
981 
982 int
WriteFreeStorageToFile(const char * filename,const char * myname,SyntaxDef * syntax,FreeStorageElem * fs,ASFlagType flags)983 WriteFreeStorageToFile (const char *filename, const char *myname,
984 												SyntaxDef * syntax, FreeStorageElem * fs,
985 												ASFlagType flags)
986 {
987 	ConfigDef *Writer = NULL;
988 	ConfigData cd;
989 
990 	cd.filename = filename;
991 	if ((Writer =
992 			 InitConfigWriter ((char *)myname, syntax, CDT_Filename,
993 												 cd)) == NULL)
994 		return 2;
995 
996 	cd.filename = filename;
997 	WriteConfig (Writer, fs, CDT_Filename, &cd, flags);
998 	DestroyConfig (Writer);
999 
1000 	return 0;
1001 }
1002