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