1 /*
2  * scf.c, SCFF library, tab=4
3  *
4  * Copyright (C) Rasca, Berlin 1998
5  * EMail: thron@gmx.de
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Library General Public License as published
9  * by the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program 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
15  * GNU Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21 
22 #include <stdio.h>			/* fprintf() */
23 #include <stdlib.h>
24 #include <limits.h>
25 #include <string.h>
26 #include <unistd.h>			/* getuid(), getpid() */
27 #include <pwd.h>
28 #include <stdarg.h>
29 #include <sys/utsname.h>	/* uname() */
30 #include <scf.h>
31 
32 extern int strncasecmp (const char *s1, const char *s2, size_t n);
33 /* extern char * strdup (const char *s); */
34 
35 /* undef if you don't have strdup()
36  */
37 #define HAS_STRDUP
38 
39 #define MAX_LINE			1024
40 #define SCF_COMMENT_CHAR	'#'
41 #define SCF_KVP_SEPARATOR	'='
42 #define SCF_HEADER			"#SCFF 1.0"
43 
44 /* a section name
45  */
46 typedef struct SectionName {
47 	string *names;
48 	int depth;
49 } scf_secnam;
50 
51 /* data entry */
52 typedef struct Entry {
53 	int type;			/* type of entry, eg. SCF_STRING */
54 	int val_int;		/* used for INT, Bool and as counter for arrays */
55 	string val_str;		/* for string values */
56 	string key;
57 	string comment;
58 	struct Entry **array;
59 	struct Entry *next;
60 } scf_entry;
61 
62 /* a section entry/node
63  */
64 typedef struct Section {
65 	unsigned int no_of_childs;		/* number of children */
66 	unsigned int no_of_entries;
67 	string comment;
68 	scf_secnam *sn;				/* section name */
69 	struct Section *previous;
70 	struct Section *child;
71 	struct Section *neighbour;
72 	struct Entry *entries;
73 } scf_section;
74 
75 typedef struct Scf {
76 	unsigned int id;
77 	unsigned int mode;
78 	unsigned char key_value_separator;
79 	unsigned char comment_char;
80 	unsigned char array_chars[2];	/* left and right array character */
81 	unsigned char array_separator;
82 	scf_section *root;
83 	/* */
84 	char *fname;
85 	FILE *fp;
86 	/* for the linked list
87 	 */
88 	struct Scf *next;
89 	struct Scf *prev;
90 } SCF;
91 
92 
93 /* for the linked list of used SCFF files
94  */
95 static SCF *first = NULL;
96 static SCF *last = NULL;
97 static int last_id = 0;
98 
99 /*
100  * multi free(), returns allways NULL
101  */
102 void *
mfree(int num,void * ptr,...)103 mfree (int num, void *ptr, ...)
104 {
105 	va_list ap;
106 	va_start (ap, ptr);
107 	while (num--) {
108 		free (ptr);
109 		if (num)
110 			ptr = va_arg(ap, void*);
111 	}
112 	va_end(ap);
113 	return (NULL);
114 }
115 
116 /*
117  * parse a section name and return a filled "sn" structure
118  */
119 static scf_secnam *
parse_section_name(const string name)120 parse_section_name (const string name)
121 {
122 	scf_secnam *sn;
123 	string buff, p;
124 
125 	sn = (scf_secnam *) calloc (1, sizeof (scf_secnam));
126 	if (!sn)
127 		return (NULL);
128 	if (name == SCF_ROOT) {
129 		sn->depth = 0;
130 		sn->names = SCF_ROOT;
131 	} else {
132 		sn->depth = 1;
133 		buff = (string) malloc (strlen(name)+1);
134 		if (!buff) {
135 			free (sn);
136 			return (NULL);
137 		}
138 		strcpy (buff, name);
139 		p = strtok (buff, ".");
140 		if (p) {
141 #ifdef DEBUG_SCF
142 			fprintf (stderr, "parse_section_name() %s.", p);
143 #endif
144 			sn->names = (string *) malloc ((sn->depth) * sizeof (string));
145 			if (!sn->names)
146 				return ((scf_secnam *)mfree(2,buff,sn));
147 			sn->names[0] = (string) malloc ((strlen(p)+1) * sizeof (uchar));
148 			if (!sn->names[0])
149 				return ((scf_secnam *)mfree(3,buff,sn->names,sn));
150 			strcpy (sn->names[0], p);
151 			while (1) {
152 				p = strtok (NULL, ".");
153 				if (!p)
154 					break;
155 				sn->depth++;
156 				sn->names = (string *) realloc (
157 									sn->names, sn->depth * sizeof(string));
158 				if (!sn->names)
159 					return ((scf_secnam *)mfree(2, buff, sn));
160 				sn->names[sn->depth-1] = (string) malloc (strlen(p)+1);
161 				strcpy (sn->names[sn->depth-1], p);
162 #ifdef DEBUG_SCF
163 				fprintf (stderr, "%s.", p);
164 #endif
165 			}
166 		}
167 		free (buff);
168 #ifdef DEBUG_SCF
169 		fprintf (stderr, "(%d)\n", sn->depth);
170 #endif
171 	}
172 	return (sn);
173 }
174 
175 /*
176  */
177 static void
delete_section_name(scf_secnam * sn)178 delete_section_name (scf_secnam *sn)
179 {
180 	while (sn->depth--) {
181 		free (sn->names[sn->depth]);
182 	}
183 	free (sn->names);
184 	free (sn);
185 }
186 
187 /*
188  * alloc RAM for a new section and initialize with
189  * the section name: section.subsection[.*]
190  */
191 static scf_section *
new_section(SCF * scf,const string name)192 new_section (SCF *scf, const string name)
193 {
194 	scf_section *sec;
195 
196 	sec = (scf_section *) calloc (1, sizeof (scf_section));
197 	if (!sec) {
198 		return (NULL);
199 	}
200 	sec->sn = parse_section_name (name);
201 	if (!sec->sn)
202 		return (NULL);
203 #ifdef DEBUG_SCF
204 	fprintf (stderr, "new_section() %s (%d)\n", sec->sn->names[0], sec->sn->depth);
205 #endif
206 	return (sec);
207 }
208 
209 /*
210  * free the RAM of a section, will also destroy all data entries
211  */
212 static void
delete_section(scf_section * sec)213 delete_section (scf_section *sec)
214 {
215 	scf_section *child;
216 	delete_section_name (sec->sn);
217 	while (sec->no_of_childs--) {
218 		child = sec->child;
219 		sec->child = child->neighbour;
220 		delete_section (child);
221 	}
222 	free (sec->child);
223 	free (sec);
224 }
225 
226 
227 /*
228  * create and initialize a new SCF structure
229  */
230 static SCF *
new_scf(void)231 new_scf (void)
232 {
233 	SCF *scf;
234 	scf = (SCF *) calloc (1, sizeof (SCF));
235 	if (scf) {
236 		scf->id = ++last_id;
237 		scf->comment_char = SCF_COMMENT_CHAR;
238 		scf->key_value_separator = SCF_KVP_SEPARATOR;
239 		scf->array_chars[0] = '{';
240 		scf->array_chars[1] = '}';
241 		scf->array_separator= SCF_WHITE;
242 		scf->root = new_section (scf, SCF_ROOT);
243 	}
244 	return (scf);
245 }
246 
247 /*
248  * delete a SCF structure
249  */
250 static void
delete_scf(SCF * scf)251 delete_scf (SCF *scf)
252 {
253 	delete_section (scf->root);
254 	free (scf->fname);
255 	if (scf->id == last_id)
256 		last_id--;
257 	free (scf);
258 }
259 
260 /*
261  * add a SCF structure to the linked list
262  */
263 static void
add_to_list(SCF * scf)264 add_to_list (SCF *scf)
265 {
266 	if (first == NULL) {
267 		first = scf;
268 		last = scf;
269 		last->next = NULL;
270 		first->prev= NULL;
271 	} else {
272 		last->next = scf;
273 		scf->prev = last;
274 		last = scf;
275 	}
276 }
277 
278 /*
279  * remove a scf structure from the linked list of the SCFs
280  */
281 static void
remove_from_list(SCF * scf)282 remove_from_list (SCF *scf)
283 {
284 	if (scf->prev != NULL) {
285 		scf->prev->next = scf->next;
286 	}
287 	if (scf->next != NULL) {
288 		scf->next->prev = scf->prev;
289 	}
290 	if (scf == first)
291 		first = scf->next;
292 	if (scf == last)
293 		last = scf->prev;
294 }
295 
296 /*
297  * find the corresponding SCF structure named by the id
298  */
299 static SCF *
find_scf(int id)300 find_scf (int id)
301 {
302 	SCF *tmp;
303 	tmp = first;
304 	while (tmp) {
305 		if (tmp->id == id)
306 			return (tmp);
307 		tmp = tmp->next;
308 	}
309 	return (NULL);
310 }
311 
312 /*
313  */
314 static scf_entry *
new_entry(void)315 new_entry (void)
316 {
317 	scf_entry *se;
318 	se = (scf_entry *) calloc (1, sizeof(scf_entry));
319 	return (se);
320 }
321 
322 /*
323  */
324 int
scf_set_mode(scf_id id,int mode,const char * kvps,const char * comm)325 scf_set_mode (scf_id id, int mode, const char *kvps, const char *comm )
326 {
327 	SCF *scf;
328 
329 	scf = find_scf (id);
330 	if (!scf) {
331 		return (SCF_NO_SCF);
332 	}
333 	switch (mode) {
334 		case SCF_STD:
335 			scf->key_value_separator = SCF_KVP_SEPARATOR ;
336 			scf->comment_char = SCF_COMMENT_CHAR;
337 			break;
338 		case SCF_CAP:
339 			scf->key_value_separator = ':';
340 			scf->comment_char = '#';
341 			break;
342 		case SCF_X11:
343 			scf->key_value_separator = ':';
344 			scf->comment_char = '!';
345 			break;
346 		case SCF_INI:
347 			scf->key_value_separator = '=';
348 			scf->comment_char = ';';
349 			break;
350 		default:
351 			fprintf (stderr, "scf_set_mode() Unknown mode: %d!\n", mode);
352 			return (SCF_FALSE);
353 			break;
354 	}
355 	scf->mode = mode;
356 	if (kvps)
357 		scf->key_value_separator = *kvps;
358 	if (comm)
359 		scf->comment_char = *comm;
360 	return (SCF_TRUE);
361 }
362 
363 
364 /*
365  */
366 int
scf_set_array_mode(scf_id id,const char * aoc,const char * as)367 scf_set_array_mode (scf_id id, const char *aoc, const char *as)
368 {
369 	SCF *scf;
370 
371 	scf = find_scf (id);
372 	if (!scf) {
373 		return (SCF_NO_SCF);
374 	}
375 	if (aoc) {
376 		if (*aoc == '\0') {
377 			scf->array_chars[0] = aoc[0];
378 			scf->array_chars[0] = aoc[0];
379 		} else {
380 			scf->array_chars[0] = aoc[0];
381 			scf->array_chars[1] = aoc[1];
382 		}
383 	}
384 	if (as) {
385 		scf->array_separator = *as;
386 	}
387 	return (SCF_TRUE);
388 }
389 
390 
391 /*
392  */
393 const char *
scf_file_name(const char * pname,int flags)394 scf_file_name (const char *pname, int flags)
395 {
396 	static char buff[PATH_MAX+NAME_MAX+1];
397 	char *p, *home;
398 	int len;
399 
400 	*buff = '\0';
401 	if (!pname)
402 		return (NULL);
403 	p = strrchr (pname, '/');
404 	if (flags == SCF_PRIVAT) {
405 		home = getenv ("HOME");
406 		if (home) {
407 			len = strlen (home);
408 			if (len < PATH_MAX) {
409 				strcpy (buff, home);
410 			}
411 		}
412 		strcat (buff, "/.");
413 		if (p != NULL) {
414 			strcat (buff, p+1);
415 		} else {
416 			strcat (buff, pname);
417 		}
418 	} else {
419 		/* SCF_SYSTEM */
420 		if (p != NULL) {
421 			strncpy (buff, pname, p-pname+1);
422 			strcpy (buff+(p-pname+1), "../etc/");
423 			strcat (buff, p+1);
424 		} else {
425 			strcpy (buff, "../etc/");
426 			strcat (buff, pname);
427 		}
428 	}
429 	strcat (buff, ".scf");
430 #ifdef DEBUG_SCF
431 	fprintf (stderr, "scf_file_name(): %s\n", buff);
432 #endif
433 	return (buff);
434 }
435 
436 /*
437  * this must be called first!
438  */
439 unsigned int
scf_init(const char * pname,const char * scf_file)440 scf_init (const char *pname, const char *scf_file)
441 {
442 	SCF *scf;
443 	int len;
444 
445 	scf = new_scf();
446 	if (!scf)
447 		return (SCF_FALSE);
448 	add_to_list (scf);
449 	if (scf_file == NULL) {
450 		scf_file = scf_file_name (pname, SCF_PRIVAT);
451 	} else if (scf_file == pname) {
452 		scf_file = scf_file_name (pname, SCF_SYSTEM);
453 	}
454 	len = strlen (scf_file);
455 	scf->fname = (char *) malloc (len+1);
456 	if (!scf->fname) {
457 		remove_from_list (scf);
458 		delete_scf (scf);
459 		return (SCF_FALSE);
460 	}
461 	strcpy (scf->fname, scf_file);
462 #ifdef DEBUG_SCF
463 	fprintf (stderr, "scf_init() %s\n", scf->fname);
464 #endif
465 	return (scf->id);
466 }
467 
468 /*
469  */
470 int
scf_fini(unsigned int id)471 scf_fini (unsigned int id)
472 {
473 	SCF *scf;
474 	scf = find_scf (id);
475 	if (!scf)
476 		return (SCF_NO_SCF);
477 	if (scf->fp)
478 		fclose (scf->fp);
479 	remove_from_list (scf);
480 	delete_scf (scf);
481 	return (SCF_TRUE);
482 }
483 
484 /*
485  * for raw access
486  */
scf_open(unsigned int id,int mode)487 int scf_open (unsigned int id, int mode) {
488 	SCF *scf;
489 
490 	scf = find_scf(id);
491 	if (!scf) {
492 		return (SCF_NO_SCF);
493 	}
494 	switch (mode) {
495 		case SCF_READ:
496 			scf->fp = fopen (scf->fname, "rb");
497 			break;
498 		case SCF_WRITE:
499 			scf->fp = fopen (scf->fname, "wb");
500 			break;
501 		case SCF_APPEND:
502 			scf->fp = fopen (scf->fname, "wb+");
503 			break;
504 	}
505 	if (!scf->fp) {
506 #ifdef DEBUG_SCF
507 		perror (scf->fname);
508 #endif
509 		return (SCF_IO_ERR);
510 	}
511 	return (scf->id);
512 }
513 
514 /*
515  */
516 int
scf_close(unsigned int id)517 scf_close (unsigned int id)
518 {
519 	SCF *scf;
520 
521 	scf = find_scf (id);
522 	if (!scf) {
523 		return (SCF_NO_SCF);
524 	}
525 	fclose (scf->fp);
526 	scf->fp = NULL;
527 	return (SCF_TRUE);
528 }
529 
530 /*
531  * write all the data out
532  */
533 int
scf_sync(scf_id id)534 scf_sync (scf_id id) {
535 	SCF *scf;
536 	scf = find_scf (id);
537 	if (!scf)
538 		return (SCF_NO_SCF);
539 	scf->fp = fopen (scf->fname, "wb");
540 	if (!scf->fp)
541 		return (SCF_FALSE);
542 	fprintf (scf->fp, "%s\n\n", SCF_HEADER);
543 	fclose (scf->fp);
544 	return (SCF_TRUE);
545 }
546 
547 /*
548  */
549 int
scf_strip_eol(string buff)550 scf_strip_eol (string buff)
551 {
552 	string p;
553 	int len;
554 
555 	if (!buff) {
556 		/* null pointer
557 		 */
558 		return (SCF_NO_PTR);
559 	}
560 	len = strlen (buff);
561 	if (!len) {
562 		/* empty string
563 		 */
564 		return (SCF_NO_VAL);
565 	}
566 	p = buff+len-1;
567 	if ((*p == '\n') || (*p == '\r')) {
568 		*p-- = '\0';
569 		len--;
570 		if (p > buff) {
571 			if ((*p == '\n') || (*p == '\r')) {
572 				/* for dos files (\r\n)
573 				 */
574 				*p = '\0';
575 				len--;
576 			}
577 		}
578 		return (len);
579 	}
580 	return (SCF_NO_EOL);
581 }
582 
583 
584 /*
585  * read the next line and strip of the newline characters at the end
586  * of the line. returned string must be freed.
587  */
588 static int
read_line(SCF * scf,unsigned char ** rbuff)589 read_line (SCF *scf, unsigned char **rbuff)
590 {
591 	static unsigned char buff[MAX_LINE+2];
592 	int len;
593 
594 	*rbuff = NULL;
595 	if (fgets (buff, MAX_LINE+1, scf->fp) == NULL)
596 		return (SCF_NO_VAL);
597 
598 	len = scf_strip_eol (buff);
599 	if (len == SCF_NO_EOL) {
600 		fprintf (stderr, "Error: line to long!?\n");
601 		return (SCF_NO_EOL);
602 	}
603 	*rbuff = (unsigned char *) malloc (len+1);
604 	if (!*rbuff) {
605 		return (SCF_NO_RAM);
606 	}
607 	strcpy (*rbuff, buff);
608 #ifdef DEBUG_SCF
609 	fprintf (stderr, "read_line(): rbuff=%s\n", *rbuff);
610 #endif
611 	return (len);
612 }
613 
614 /*
615  */
616 static int
next_line(SCF * scf,string * rline)617 next_line (SCF *scf, string *rline)
618 {
619 	unsigned char *iline = NULL;
620 	unsigned char *oline = NULL;
621 	unsigned char *p;
622 	int ilen, olen = SCF_NO_VAL, spaces, first;
623 
624 	*rline = NULL;
625 	first = SCF_TRUE;
626 
627 	while (SCF_TRUE) {
628 		ilen = read_line (scf, &iline);
629 		if (first && ( ilen == SCF_NO_VAL)) {
630 			return (SCF_NO_VAL);
631 		}
632 		if (first && ( ilen == 0)) {
633 			/* skip empty lines */
634 			continue;
635 		}
636 		if (ilen == SCF_NO_RAM) {
637 			free (iline);
638 			free (rline);
639 			return (SCF_NO_RAM);
640 		}
641 		if (ilen > 0) {
642 			if (first) {
643 				oline = malloc (ilen+1);
644 				strcpy (oline, iline);
645 				olen = ilen;
646 				first = 0;
647 			} else {
648 				oline = realloc (oline, olen+ilen+1);
649 				p = iline;
650 				while (*p) {
651 					if ((*p == ' ') || (*p == '\t')) {
652 						p++;
653 						ilen--;
654 					} else
655 						break;
656 				}
657 				strcat (oline, p);
658 				olen = olen+ilen;
659 			}
660 			free (iline);
661 		}
662 		if (oline[olen-1] == '\\') {
663 			oline[olen-1] = '\0';
664 			olen--;
665 			continue;
666 		}
667 		break;
668 	}
669 	/* check for double :: in termcap line */
670 	if (scf->mode == SCF_CAP) {
671 		unsigned char *ep;
672 		p = oline;
673 		ep= p+olen;
674 		while (*p) {
675 			if ((*p == ':') && (*(p+1) == ':')) {
676 				memmove (p, p+1, ep-p);
677 				olen--;
678 			}
679 			p++;
680 		}
681 	}
682 	/* remove spaces */
683 	p = oline;
684 	spaces = 0;
685 	while (*p && ((*p == ' ') || (*p == '\t'))) {
686 		spaces++;
687 		p++;
688 	}
689 	if (spaces) {
690 		olen = olen - spaces;
691 		memmove (oline, oline+spaces, olen+1);
692 	}
693 	p = oline+olen-1;
694 	while (p > oline && (*p == ' ' || *p == '\t')) {
695 		olen--;
696 		*p-- = '\0';
697 	}
698 
699 	*rline = oline;
700 #ifdef DEBUG_SCF
701 	fprintf (stderr, "next_line() line=\"%s\"\n", oline);
702 #endif
703 	return (olen);
704 }
705 
706 /*
707  * for raw access: skippes comment and empty lines, merges breaked lines
708  * with the '\' character and removes CR and NL characters at the end
709  */
710 int
scf_next_line(unsigned int id,string * rline)711 scf_next_line (unsigned int id, string *rline)
712 {
713 	SCF *scf;
714 
715 	scf = find_scf (id);
716 	if (!scf)
717 		return (SCF_NO_SCF);
718 	return (next_line (scf, rline));
719 }
720 
721 /*
722  */
723 static int
strip_quotes(uchar * s,char c)724 strip_quotes (uchar *s, char c)
725 {
726 	int len = 0;
727 
728 	if (!s || !*s)
729 		return (SCF_FALSE);
730 	len = strlen (s);
731 	if (*s == c) {
732 		if (s[len-1] == c) {
733 			s[len-1] = '\0';
734 			memmove (s, s+1, len-1);
735 			len -= 2;
736 		} else {
737 			fprintf (stderr, "strip_quotes() can't find ending quotes!\n");
738 			return (SCF_FALSE);
739 		}
740 	}
741 	return (len);
742 }
743 
744 /*
745  */
un_escape_val(SCF * scf,unsigned char * val)746 unsigned char *un_escape_val (SCF *scf, unsigned char *val) {
747 	unsigned char buff[256], *nval;
748 	int len0, len1, i, j, len;
749 	uid_t uid;
750 	pid_t pid;
751 	struct utsname uts;
752 	struct passwd *pw;
753 
754 	if (!val)
755 		return (NULL);
756 	len0 = len1 = strlen (val);
757 	nval = (uchar *) malloc (len0+1);
758 	if (!nval)
759 		return (NULL);
760 	for (i = 0, j = 0; i < len0; i++) {
761 		if (val[i] == '\\') {
762 			switch (val[i+1]) {
763 				case '\\':
764 				case '"':
765 					nval[j++] = val[i+1];
766 					len1--;
767 					i++;
768 					break;
769 				case 'n':
770 					/* NL */
771 					nval[j++] = 0x0A;
772 					len1--;
773 					i++;
774 					break;
775 				case 'r':
776 					/* CR */
777 					nval[j++] = 0x0D;
778 					len1--;
779 					i++;
780 					break;
781 				case 'h':
782 					/* hostname */
783 					uname (&uts);
784 					len = strlen (uts.nodename);
785 					len1 = len1 - 2 + len;
786 					nval = realloc (nval, len1+1);
787 					strcpy (nval+j, uts.nodename);
788 					j += len;
789 					i++;
790 					break;
791 				case 'o':
792 					/* operating system name */
793 					uname (&uts);
794 					len = strlen (uts.sysname);
795 					len1 = len1 - 2 + len;
796 					nval = realloc (nval, len1+1);
797 					strcpy (nval+j, uts.sysname);
798 					j += len;
799 					i++;
800 					break;
801 				case 'm':
802 					/* machine type */
803 					uname (&uts);
804 					len = strlen (uts.machine);
805 					len1 = len1 - 2 + len;
806 					nval = realloc (nval, len1+1);
807 					strcpy (nval+j, uts.machine);
808 					j += len;
809 					i++;
810 					break;
811 				case 'd':
812 					/* home directory */
813 					uid = getuid();
814 					pw = getpwuid (uid);
815 					len = strlen (pw->pw_dir);
816 					len1 = len1 - 2 + len;
817 					nval = realloc (nval, len1+1);
818 					strcpy (nval+j, pw->pw_dir);
819 					j += len;
820 					i++;
821 					break;
822 				case 'u':
823 					/* login name */
824 					uid = getuid();
825 					pw = getpwuid (uid);
826 					len = strlen (pw->pw_name);
827 					len1 = len1 - 2 + len;
828 					nval = realloc (nval, len1+1);
829 					strcpy (nval+j, pw->pw_name);
830 					j += len;
831 					i++;
832 					break;
833 				case 'p':
834 					/* process id */
835 					pid = getpid ();
836 					sprintf (buff, "%d", pid);
837 					len = strlen (buff);
838 					len1 = len1 - 2 + len;
839 					nval = realloc (nval, len1+1);
840 					strcpy (nval+j, buff);
841 					j+= len;
842 					i++;
843 					break;
844 				default:
845 					nval[j++] = val[i];
846 					break;
847 			}
848 		} else {
849 			nval[j++] = val[i];
850 		}
851 	}
852 	nval[len1] = '\0';
853 	return (nval);
854 }
855 
856 /*
857  * check for a boolean value in string 's'
858  * return values:
859  *	0: the string contains no boolean value
860  *	1 = boolean value is no
861  *	2 = boolean value is yes
862  */
863 static int
str_is_bool(const uchar * s,int len)864 str_is_bool (const uchar *s, int len)
865 {
866 	uchar *p[] = { "FALSE", "NO", "TRUE", "YES" };
867 	int i;
868 
869 	for (i = 0; i < 4; i++) {
870 		if (strncasecmp (s, p[i], len) == 0)
871 			return (i < 2 ? 1 : 2);
872 	}
873 	return (0);
874 }
875 
876 /*
877  * looks for pattern like "0xHH0xHH[..]" or "\OOO\OOO[..]"
878  */
879 static int
str_is_binary(const uchar * s,int len)880 str_is_binary (const uchar *s, int len)
881 {
882 
883 	if (len < 8)
884 		return (0);
885 	if (*s == '\\') {
886 		/* octal */
887 		if (strchr (s+1, '\\'))
888 			return (1);
889 	} else if (*s == '0' && *(s+1) == 'x') {
890 		/* hex */
891 		if (strchr (s+2, 'x'))
892 			return (1);
893 	}
894 	return (0);
895 }
896 
897 /*
898  */
899 static int
add_array_element(scf_entry * en,string line,int type)900 add_array_element (scf_entry *en, string line, int type) {
901 	int len = 0;
902 	string p;
903 	scf_entry *new;
904 
905 	new = new_entry ();
906 	new->type = type;
907 	en->val_int++;
908 	if (!en->array)
909 		en->array = (scf_entry **) malloc (en->val_int * sizeof (scf_entry *));
910 	else
911 		en->array = (scf_entry **) realloc (en->array, en->val_int * sizeof (scf_entry *));
912 	en->array[en->val_int-1] = new;
913 	switch (type) {
914 		case SCF_STRING:
915 			p = strrchr (line, '"');
916 			if (!p)
917 				return (0);
918 			break;
919 		case SCF_INT:
920 			p = line;
921 			while (*p && *p != ' ')
922 				p++;
923 			len = p - line;
924 			sscanf (line, "%d", &new->val_int);
925 #ifdef DEBUG_SCF
926 			fprintf (stderr, "add_array_element() %s->%d (len=%d)\n",
927 						line, new->val_int, len);
928 #endif
929 			break;
930 		default:
931 			printf ("oops\n");
932 			break;
933 	}
934 	return (len);
935 }
936 
937 /*
938  */
939 static int
key_value_pair(SCF * scf,uchar * line,scf_entry * entry)940 key_value_pair (SCF *scf, uchar *line, scf_entry *entry)
941 {
942 	uchar *p, *s;
943 	int len;
944 
945 	if (!line)
946 		return (SCF_FALSE);
947 	s = strchr (line, scf->key_value_separator);
948 	if (!s)
949 		return (SCF_FALSE);
950 
951 	/* process the value part
952 	 */
953 	p = s + 1;
954 	while ((*p == ' ') || (*p == '\t')) {
955 		p++;
956 	}
957 	len = strlen (p);
958 	if (scf->mode & SCF_INI) {
959 		/* in this mode we use only strings
960 		 */
961 		entry->val_str = (uchar *) malloc (len+1);
962 		if (entry->val_str)
963 			strcpy (entry->val_str, p);
964 	} else if (*p == '"') {
965 		/* it's a string
966 		 */
967 		strip_quotes (p, '"');
968 		entry->val_str = un_escape_val (scf, p);
969 		entry->type = SCF_STRING;
970 	} else if ((entry->val_int = str_is_bool (p, len)) > 0) {
971 		/* it's a boolean
972 		 */
973 		if (entry->val_int == 1) {
974 			entry->val_int = 0;
975 		} else {
976 			entry->val_int = 1;
977 		}
978 		entry->type = SCF_BOOL;
979 	} else if (str_is_binary (p, len)) {
980 		/* it's a binary
981 		 */
982 		entry->type = SCF_BIN;
983 	} else if (*p == scf->array_chars[0]) {
984 		/* it's a array
985 		 */
986 		entry->type = SCF_ARRAY;
987 		p[len-1] = '\0';
988 		p++;
989 		/* count array elements
990 		 */
991 		while (*p) {
992 			while (*p == ' ')
993 				p++;
994 			if (*p == '"') {
995 				/* it's a string .. */
996 				len = add_array_element (entry, p+1, SCF_STRING);
997 				if (!len)
998 					goto STOP;
999 				p += len;
1000 			} else {
1001 				/* INT ? */
1002 				len = add_array_element (entry, p, SCF_INT);
1003 				if (!len)
1004 					goto STOP;
1005 				p += len;
1006 			}
1007 			while (*p && *p != ' ')
1008 				p++;
1009 			if (*(p+1) == '\0')
1010 				break;
1011 		}
1012 		STOP:;
1013 	} else {
1014 		/* it's a int
1015 		 */
1016 		sscanf (p, "%d", &entry->val_int);
1017 		entry->type = SCF_INT;
1018 #ifdef DEBUG_SCF
1019 		fprintf (stderr, "kvp() %s->%d\n", p, entry->val_int);
1020 #endif
1021 	}
1022 
1023 	/* process the key part
1024 	 */
1025 	p = s - 1;
1026 	*(p+1) = '\0';
1027 	while ((p > line) && ((*p == '\t') || (*p == ' '))) {
1028 		*p = '\0';
1029 		p--;
1030 	}
1031 	p = line;
1032 	while ((*p == ' ') || (*p == '\t' )) {
1033 		p++;
1034 	}
1035 	len = strlen (p);
1036 	entry->key = (uchar *) malloc (len+1);
1037 	if (!entry->key) {
1038 		free (entry->val_str);
1039 		return (SCF_FALSE);
1040 	}
1041 	strcpy (entry->key, p);
1042 	return (SCF_TRUE);
1043 }
1044 
1045 /*
1046  * find the parent of section 'sec' starting at section 'root'
1047  */
1048 scf_section *
find_parent_section(scf_section * root,scf_section * sec)1049 find_parent_section (scf_section *root, scf_section *sec)
1050 {
1051 	scf_section *tsec, *t2sec;
1052 	int i, level = 0;
1053 
1054 	if (!root || !sec) {
1055 		return (NULL);
1056 	}
1057 	if (sec->sn->depth == 1)
1058 		return (root);
1059 
1060 	tsec = root;
1061 	NEXT:
1062 #ifdef DEBUG_SCF
1063 	fprintf (stderr, "find_parent_section() %s.%s (%d)\n",
1064 				sec->sn->names[0], sec->sn->names[1], sec->sn->depth);
1065 #endif
1066 	while (level < (sec->sn->depth - 1)) {
1067 		t2sec = tsec->child;
1068 		for (i = 0; i < tsec->no_of_childs; i++) {
1069 			if (strcmp (t2sec->sn->names[level], sec->sn->names[level]) == 0) {
1070 				tsec = t2sec;
1071 				level++;
1072 				if (level == sec->sn->depth - 1) {
1073 #ifdef DEBUG_SCF
1074 					fprintf (stderr, "find_parent_section() %d %s\n",
1075 						level, sec->sn->names[level]);
1076 #endif
1077 					return (tsec);
1078 				}
1079 				goto NEXT;
1080 			}
1081 			t2sec = t2sec->neighbour;
1082 		}
1083 		level++;
1084 	}
1085 	return (NULL);
1086 }
1087 
1088 /*
1089  * add a named section
1090  */
1091 int
add_section(SCF * scf,scf_section * sec)1092 add_section (SCF *scf, scf_section *sec)
1093 {
1094 	scf_section *tsec, *t2sec;
1095 
1096 	tsec = find_parent_section (scf->root, sec);
1097 	if (tsec) {
1098 		t2sec = tsec->child;
1099 		if (!t2sec) {
1100 			tsec->child = sec;
1101 		} else {
1102 			while (t2sec->neighbour) {
1103 				t2sec = t2sec->neighbour;
1104 			}
1105 			t2sec->neighbour = sec;
1106 		}
1107 		sec->neighbour = NULL;
1108 		tsec->no_of_childs++;
1109 		return (SCF_TRUE);
1110 	} else {
1111 		fprintf (stderr, "can't find parent section\n");
1112 	}
1113 	return (SCF_FALSE);
1114 }
1115 
1116 /*
1117  * add a data entry to named section
1118  */
1119 int
add_entry(scf_section * sec,scf_entry * node)1120 add_entry (scf_section *sec, scf_entry *node)
1121 {
1122 	scf_entry *ten;
1123 
1124 	sec->no_of_entries++;
1125 #ifdef DEBUG_SCF
1126 	fprintf (stderr, "add_entry() sec=%s noe=%d\n",
1127 			sec->sn->depth ? sec->sn->names[0]:"ROOT", sec->no_of_entries);
1128 #endif
1129 	ten = sec->entries;
1130 	if (!ten) {
1131 		sec->entries = node;
1132 	} else {
1133 		while (ten) {
1134 			if (!ten->next)
1135 				break;
1136 			ten = ten->next;
1137 		}
1138 		ten->next = node;
1139 	}
1140 	node->next = NULL;
1141 	return (SCF_TRUE);
1142 }
1143 
1144 /*
1145  * find the named section in the tree
1146  */
1147 static scf_section *
find_section(SCF * scf,scf_secnam * sn)1148 find_section (SCF *scf, scf_secnam *sn)
1149 {
1150 	scf_section *sec = scf->root, *child;
1151 	int level;
1152 
1153 	if (sn->depth == 0) {
1154 		return (sec);
1155 	}
1156 	level = 0;
1157 	child = sec->child;
1158 	while (child && (level < sn->depth))  {
1159 		if (strcmp (child->sn->names[level], sn->names[level]) == 0) {
1160 			if (child->sn->depth == sn->depth)
1161 				return (child);
1162 			else {
1163 				child = child->child;
1164 				level++;
1165 				continue;
1166 			}
1167 		}
1168 		child = child->neighbour;
1169 	}
1170 	return (NULL);
1171 }
1172 
1173 /*
1174  */
1175 static scf_section *
find_section_by_name(SCF * scf,const string section)1176 find_section_by_name (SCF *scf, const string section)
1177 {
1178 	scf_section *sec = NULL;
1179 	scf_secnam *sn;
1180 
1181 	if (section == SCF_ROOT)
1182 		return (scf->root);
1183 	sn = parse_section_name (section);
1184 	if (sn)
1185 		sec = find_section (scf, sn);
1186 	delete_section_name (sn);
1187 	return (sec);
1188 }
1189 
1190 /*
1191  * used for INT and BOOL entries
1192  */
1193 static int
get_int_val(SCF * scf,const string section,const string key,int type,int * rv)1194 get_int_val (SCF *scf, const string section, const string key, int type, int *rv)
1195 {
1196 	scf_section *sec;
1197 	scf_entry *en;
1198 	int i;
1199 
1200 	if (!scf || !key)
1201 		return (SCF_FALSE);
1202 	sec = find_section_by_name (scf, section);
1203 	if (!sec) {
1204 #ifdef DEBUG_SCF
1205 		fprintf (stderr, "get_int_val(): Can't find section: %s\n", section);
1206 #endif
1207 		return (SCF_FALSE);
1208 	}
1209 	en = sec->entries;
1210 	for (i =0; i < sec->no_of_entries; i++) {
1211 		if (en->type == type) {
1212 #ifdef DEBUG_SCF
1213 			fprintf (stderr, "** sec=%s noe=%d key=%s en->key=%s\n",
1214 						sec->sn->depth?sec->sn->names[0]:"*",
1215 						sec->no_of_entries, key, en->key);
1216 #endif
1217 			if (strcmp (en->key, key) == 0) {
1218 				*rv = en->val_int;
1219 				return (SCF_TRUE);
1220 			}
1221 		}
1222 		en = en->next;
1223 	}
1224 	return (SCF_FALSE);
1225 }
1226 
1227 /*
1228  */
1229 static string
get_string_val(SCF * scf,const string section,const char * key)1230 get_string_val (SCF *scf, const string section, const char *key)
1231 {
1232 	scf_section *sec;
1233 	scf_entry *en;
1234 	int i;
1235 	unsigned char *value;
1236 
1237 	if (!scf || !key)
1238 		return (NULL);
1239 	sec = find_section_by_name (scf, section);
1240 	if (!sec) {
1241 #ifdef DEBUG_SCF
1242 		fprintf (stderr, "Can't find section: %s\n", section);
1243 #endif
1244 		return (NULL);
1245 	}
1246 	en = sec->entries;
1247 	for (i = 0; i < sec->no_of_entries; i++) {
1248 #ifdef DEBUG_SCF
1249 		fprintf (stderr, "get_string_val() i=%d %s %d\n", i, en->key, en->type);
1250 #endif
1251 		if (en->type == SCF_STRING) {
1252 			if (strcmp (en->key, key) == 0)
1253 			{
1254 				value = un_escape_val (scf, en->val_str);
1255 				return (value);
1256 			}
1257 		}
1258 		en = en->next;
1259 	}
1260 	return (NULL);
1261 }
1262 
1263 /*
1264  */
1265 static int
get_array_int_val(SCF * scf,const string section,const string key,int n,int * val)1266 get_array_int_val (SCF *scf, const string section, const string key, int n, int *val)
1267 {
1268 	scf_section *sec;
1269 	scf_entry *en;
1270 	int i;
1271 
1272 	if (!scf || !key)
1273 		return (SCF_FALSE);
1274 	sec = find_section_by_name (scf, section);
1275 	if (!sec) {
1276 		return (SCF_FALSE);
1277 	}
1278 	en = sec->entries;
1279 	for (i = 0; i < sec->no_of_entries; i++) {
1280 #ifdef DEBUG_SCF
1281 		fprintf (stderr, "get_array_int_val() key=%s en->key=%s type=%d\n",
1282 					key, en->key, en->type);
1283 #endif
1284 		if (en->type == SCF_ARRAY) {
1285 			if (strcmp (en->key, key) == 0) {
1286 				if (n <= en->val_int) {
1287 					*val = en->array[n]->val_int;
1288 					return (SCF_TRUE);
1289 				}
1290 			}
1291 		}
1292 		en = en->next;
1293 	}
1294 	return (SCF_FALSE);
1295 }
1296 
1297 /*
1298  */
1299 static string
get_array_val(SCF * scf,const string section,const string key,int n)1300 get_array_val (SCF *scf, const string section, const string key, int n)
1301 {
1302 	return (NULL);
1303 }
1304 
1305 /*
1306  * read a complete SCFF file into RAM
1307  */
1308 int
scf_read(scf_id id)1309 scf_read (scf_id id)
1310 {
1311 	SCF *scf;
1312 	int len, ptype;
1313 	string line, pline;
1314 	scf_section *base, *sec;
1315 	scf_entry *entry;
1316 	scf_secnam *sn;
1317 
1318 	scf = find_scf (id);
1319 	if (!scf)
1320 		return (SCF_NO_SCF);
1321 	scf->fp = fopen (scf->fname, "rb");
1322 	if (!scf->fp) {
1323 #ifdef DEBUG
1324 		perror (scf->fname);
1325 #endif
1326 		return (SCF_FALSE);
1327 	}
1328 	sec = base = scf->root;
1329 
1330 	pline = NULL;
1331 	ptype = 0;
1332 	while ((len = next_line (scf, &line)) > 0) {
1333 		if (*line == scf->comment_char) {
1334 			/* comment
1335 			 */
1336 #ifdef DEBUG_SCF
1337 			fprintf (stderr, "C:%s\n", line);
1338 #endif
1339 			if (pline != NULL)
1340 				free (pline);
1341 			pline = strdup (line);
1342 			ptype = SCF_COMMENT;
1343 
1344 		} else if ((*line == '[') && (line[len-1] == ']')) {
1345 			/* it's a section
1346 			 */
1347 #ifdef DEBUG_SCF
1348 			fprintf (stderr, "S:%s\n", line);
1349 #endif
1350 			line[len-1] = '\0';
1351 			sn = parse_section_name (line+1);
1352 			if ((sec = find_section (scf, sn)) != NULL) {
1353 				/* dupe */
1354 				base = sec;
1355 			} else {
1356 				sec = new_section (scf, line+1);
1357 				if (sec) {
1358 					add_section (scf, sec);
1359 					if (ptype == SCF_COMMENT) {
1360 						sec->comment = pline;
1361 						pline = NULL;
1362 					}
1363 					ptype = SCF_SECTION;
1364 					base = sec;
1365 				}
1366 			}
1367 			free (sn);
1368 		} else {
1369 			/* data entries ??
1370 			 */
1371 #ifdef DEBUG_SCF
1372 			fprintf (stderr, "D:%s\n", line);
1373 #endif
1374 			entry = new_entry ();
1375 			if (key_value_pair (scf, line, entry) == SCF_TRUE) {
1376 			/* key value pairs
1377 			 */
1378 				add_entry (base, entry);
1379 				if (ptype == SCF_COMMENT) {
1380 					entry->comment = pline;
1381 					pline = NULL;
1382 				}
1383 				ptype = entry->type;
1384 			}
1385 		}
1386 		free (line);
1387 	}
1388 	if (pline)
1389 		free (pline);
1390 	fclose (scf->fp);
1391 	scf->fp = NULL;
1392 	return (SCF_TRUE);
1393 }
1394 
1395 /*
1396  * add a named section
1397  */
1398 int
scf_add_section(scf_id id,char * sec,char * comment)1399 scf_add_section (scf_id id, char *sec, char *comment)
1400 {
1401 	SCF *scf;
1402 	scf_section *se;
1403 
1404 	scf = find_scf (id);
1405 	if (!scf)
1406 		return (SCF_FALSE);
1407 	if (find_section_by_name (scf, sec) == NULL) {
1408 		/* add it .. */
1409 		se = new_section (scf, sec);
1410 		if (se) {
1411 			add_section (scf, se);
1412 			if (comment)
1413 			se->comment = strdup (comment);
1414 		}
1415 	}
1416 	return (SCF_FALSE);
1417 }
1418 
1419 /*
1420  */
1421 int
scf_has_section(scf_id id,char * sec)1422 scf_has_section (scf_id id, char *sec)
1423 {
1424 	SCF *scf;
1425 	scf = find_scf(id);
1426 	if (!scf)
1427 		return (SCF_FALSE);
1428 	if (find_section_by_name (scf, sec) == NULL)
1429 		return (SCF_FALSE);
1430 	return (SCF_TRUE);
1431 }
1432 
1433 /*
1434  * not ready!
1435  */
1436 int
scf_section_has_key(scf_id id,const char * sec,const char * key)1437 scf_section_has_key (scf_id id, const char *sec, const char *key)
1438 {
1439 	return (SCF_FALSE);
1440 }
1441 
1442 /*
1443  */
1444 string
scf_get_val(scf_id id,const string section,const string key)1445 scf_get_val (scf_id id, const string section, const string key)
1446 {
1447 	SCF *scf;
1448 	scf = find_scf (id);
1449 	if (!scf)
1450 		return (NULL);
1451 	return (get_string_val (scf, section, key));
1452 }
1453 
1454 /*
1455  */
1456 string
scf_get_array_val(scf_id id,const string section,const string key,int n)1457 scf_get_array_val (scf_id id, const string section, const string key, int n)
1458 {
1459 	SCF *scf;
1460 	scf = find_scf (id);
1461 	if (!scf)
1462 		return (NULL);
1463 	return (get_array_val (scf, section, key, n));
1464 }
1465 
1466 /*
1467  */
1468 int
scf_get_array_int_val(scf_id id,const string section,const string key,int n,int * val)1469 scf_get_array_int_val (scf_id id, const string section, const string key, int n, int *val)
1470 {
1471 	SCF *scf;
1472 
1473 	scf = find_scf (id);
1474 	if (!scf)
1475 		return (SCF_FALSE);
1476 	return (get_array_int_val (scf, section, key, n, val));
1477 }
1478 
1479 /*
1480  */
1481 int
scf_get_bool_val(scf_id id,const string section,const string key,int * rv)1482 scf_get_bool_val (scf_id id, const string section, const string key, int *rv)
1483 {
1484 	SCF *scf;
1485 
1486 	scf = find_scf (id);
1487 	if (!scf)
1488 		return (SCF_FALSE);
1489 	return (get_int_val (scf, section, key, SCF_BOOL, rv));
1490 }
1491 
1492 /*
1493  */
1494 int
scf_get_int_val(scf_id id,const string section,const string key,int * rv)1495 scf_get_int_val (scf_id id, const string section, const string key, int *rv)
1496 {
1497 	SCF *scf;
1498 
1499 	scf = find_scf (id);
1500 	if (!scf)
1501 		return (SCF_FALSE);
1502 	return (get_int_val (scf, section, key, SCF_INT, rv));
1503 }
1504 
1505 /*
1506  */
1507 double
scf_get_val_as_double(scf_id id,const string section,const string key)1508 scf_get_val_as_double (scf_id id, const string section, const string key)
1509 {
1510 	SCF *scf;
1511 	unsigned char *s;
1512 	double i;
1513 
1514 	scf = find_scf (id);
1515 	if (!scf)
1516 		return (0);
1517 	s = get_string_val (scf, section, key);
1518 	if (!s)
1519 		return (0);
1520 	sscanf (s, "%lf", &i);
1521 	free (s);
1522 	return (i);
1523 }
1524 
1525 
1526 /*
1527  * changed the comment char for the named file (id)
1528  */
1529 void
scf_set_comment_char(unsigned int id,char c)1530 scf_set_comment_char (unsigned int id, char c)
1531 {
1532 	SCF *scf;
1533 
1534 	scf = find_scf (id);
1535 	if (scf)
1536 		scf->comment_char = c;
1537 }
1538 
1539 /*
1540  */
1541 void
scf_set_key_value_separator(unsigned int id,char c)1542 scf_set_key_value_separator (unsigned int id, char c)
1543 {
1544 	SCF *scf;
1545 
1546 	scf = find_scf (id);
1547 	if (scf)
1548 		scf->key_value_separator = c;
1549 }
1550 
1551 /*
1552  * returns the number of subsections or 0 if there are no
1553  * subsections
1554  */
1555 int
scf_no_of_subsections(scf_id id,const string section)1556 scf_no_of_subsections (scf_id id, const string section)
1557 {
1558 	SCF *scf;
1559 	scf_secnam *sn;
1560 	scf_section *sec;
1561 
1562 	scf =  find_scf (id);
1563 	if (!scf)
1564 		return (SCF_FALSE);
1565 	sn = parse_section_name (section);
1566 	sec = find_section (scf, sn);
1567 	delete_section_name (sn);
1568 	if (!sec)
1569 		return (SCF_FALSE);
1570 	return (sec->no_of_childs);
1571 }
1572 
1573 /*
1574  */
1575 int
scf_get_type(scf_id id,const string section,const string key)1576 scf_get_type (scf_id id, const string section, const string key)
1577 {
1578 	SCF *scf;
1579 
1580 	scf = find_scf (id);
1581 	if (!scf)
1582 		return (SCF_FALSE);
1583 	return (SCF_FALSE);
1584 }
1585 
1586 /*
1587  * add a key if needed and add the value
1588  */
1589 int
scf_set_val(scf_id id,string section,string key,string val,string comment)1590 scf_set_val (scf_id id, string section, string key, string val, string comment)
1591 {
1592 	return (SCF_FALSE);
1593 }
1594 
1595 /*
1596  */
1597 #ifndef HAS_STRDUP
1598 char *
strdup(const char * s)1599 strdup (const char *s)
1600 {
1601 	int len;
1602 	char *r;
1603 
1604 	if (!s)
1605 		return (NULL);
1606 	len = strlen (s);
1607 	r = (char *) malloc (len +1);
1608 	if (!r)
1609 		return (NULL);
1610 	strcpy (r, s);
1611 	return (r);
1612 }
1613 #endif
1614 
1615