]> mj.ucw.cz Git - libucw.git/blob - ucw/conf-section.c
d006643b1e360789c570907ea2760abcb6cdf305
[libucw.git] / ucw / conf-section.c
1 /*
2  *      UCW Library -- Configuration files: sections
3  *
4  *      (c) 2001--2006 Robert Spalek <robert@ucw.cz>
5  *      (c) 2003--2012 Martin Mares <mj@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 #include <ucw/lib.h>
12 #include <ucw/conf.h>
13 #include <ucw/conf-internal.h>
14 #include <ucw/clists.h>
15 #include <ucw/binsearch.h>
16
17 #include <string.h>
18
19 /* Dirty sections */
20
21 // FIXME!!!
22
23 struct dirty_section {
24   struct cf_section *sec;
25   void *ptr;
26 };
27 #define GBUF_TYPE       struct dirty_section
28 #define GBUF_PREFIX(x)  dirtsec_##x
29 #include <ucw/gbuf.h>
30 static dirtsec_t dirty;
31 static uns dirties;
32
33 void
34 cf_add_dirty(struct cf_section *sec, void *ptr)
35 {
36   dirtsec_grow(&dirty, dirties+1);
37   struct dirty_section *dest = dirty.ptr + dirties;
38   if (dirties && dest[-1].sec == sec && dest[-1].ptr == ptr)
39     return;
40   dest->sec = sec;
41   dest->ptr = ptr;
42   dirties++;
43 }
44
45 #define ASORT_PREFIX(x) dirtsec_##x
46 #define ASORT_KEY_TYPE  struct dirty_section
47 #define ASORT_ELT(i)    dirty.ptr[i]
48 #define ASORT_LT(x,y)   x.sec < y.sec || x.sec == y.sec && x.ptr < y.ptr
49 #include <ucw/sorter/array-simple.h>
50
51 static void
52 sort_dirty(void)
53 {
54   if (dirties <= 1)
55     return;
56   dirtsec_sort(dirties);
57   // and compress the list
58   struct dirty_section *read = dirty.ptr + 1, *write = dirty.ptr + 1, *limit = dirty.ptr + dirties;
59   while (read < limit) {
60     if (read->sec != read[-1].sec || read->ptr != read[-1].ptr) {
61       if (read != write)
62         *write = *read;
63       write++;
64     }
65     read++;
66   }
67   dirties = write - dirty.ptr;
68 }
69
70 /* Initialization */
71
72 struct cf_item *
73 cf_find_subitem(struct cf_section *sec, const char *name)
74 {
75   struct cf_item *ci = sec->cfg;
76   for (; ci->cls; ci++)
77     if (!strcasecmp(ci->name, name))
78       return ci;
79   return ci;
80 }
81
82 static void
83 inspect_section(struct cf_section *sec)
84 {
85   sec->flags = 0;
86   struct cf_item *ci;
87   for (ci=sec->cfg; ci->cls; ci++)
88     if (ci->cls == CC_SECTION) {
89       inspect_section(ci->u.sec);
90       sec->flags |= ci->u.sec->flags & (SEC_FLAG_DYNAMIC | SEC_FLAG_CANT_COPY);
91     } else if (ci->cls == CC_LIST) {
92       inspect_section(ci->u.sec);
93       sec->flags |= SEC_FLAG_DYNAMIC | SEC_FLAG_CANT_COPY;
94     } else if (ci->cls == CC_DYNAMIC || ci->cls == CC_BITMAP)
95       sec->flags |= SEC_FLAG_DYNAMIC;
96     else if (ci->cls == CC_PARSER) {
97       sec->flags |= SEC_FLAG_CANT_COPY;
98       if (ci->number < 0)
99         sec->flags |= SEC_FLAG_DYNAMIC;
100     }
101   if (sec->copy)
102     sec->flags &= ~SEC_FLAG_CANT_COPY;
103   sec->flags |= ci - sec->cfg;          // record the number of entries
104 }
105
106 void
107 cf_declare_section(const char *name, struct cf_section *sec, uns allow_unknown)
108 {
109   struct cf_context *cc = cf_get_context();
110   if (!cc->sections.cfg)
111   {
112     cc->sections.size = 50;
113     cc->sections.cfg = xmalloc_zero(cc->sections.size * sizeof(struct cf_item));
114   }
115   struct cf_item *ci = cf_find_subitem(&cc->sections, name);
116   if (ci->cls)
117     die("Cannot register section %s twice", name);
118   ci->cls = CC_SECTION;
119   ci->name = name;
120   ci->number = 1;
121   ci->ptr = NULL;
122   ci->u.sec = sec;
123   inspect_section(sec);
124   if (allow_unknown)
125     sec->flags |= SEC_FLAG_UNKNOWN;
126   ci++;
127   if (ci - cc->sections.cfg >= (int) cc->sections.size)
128   {
129     cc->sections.cfg = xrealloc(cc->sections.cfg, 2*cc->sections.size * sizeof(struct cf_item));
130     bzero(cc->sections.cfg + cc->sections.size, cc->sections.size * sizeof(struct cf_item));
131     cc->sections.size *= 2;
132   }
133 }
134
135 void
136 cf_init_section(const char *name, struct cf_section *sec, void *ptr, uns do_bzero)
137 {
138   if (do_bzero) {
139     ASSERT(sec->size);
140     bzero(ptr, sec->size);
141   }
142   for (struct cf_item *ci=sec->cfg; ci->cls; ci++)
143     if (ci->cls == CC_SECTION)
144       cf_init_section(ci->name, ci->u.sec, ptr + (uintptr_t) ci->ptr, 0);
145     else if (ci->cls == CC_LIST)
146       clist_init(ptr + (uintptr_t) ci->ptr);
147     else if (ci->cls == CC_DYNAMIC) {
148       void **dyn = ptr + (uintptr_t) ci->ptr;
149       if (!*dyn) {                      // replace NULL by an empty array
150         static uns zero = 0;
151         *dyn = (&zero) + 1;
152       }
153     }
154   if (sec->init) {
155     char *msg = sec->init(ptr);
156     if (msg)
157       die("Cannot initialize section %s: %s", name, msg);
158   }
159 }
160
161 static char *
162 commit_section(struct cf_section *sec, void *ptr, uns commit_all)
163 {
164   char *err;
165   for (struct cf_item *ci=sec->cfg; ci->cls; ci++)
166     if (ci->cls == CC_SECTION) {
167       if ((err = commit_section(ci->u.sec, ptr + (uintptr_t) ci->ptr, commit_all))) {
168         msg(L_ERROR, "Cannot commit section %s: %s", ci->name, err);
169         return "commit of a subsection failed";
170       }
171     } else if (ci->cls == CC_LIST) {
172       uns idx = 0;
173       CLIST_FOR_EACH(cnode *, n, * (clist*) (ptr + (uintptr_t) ci->ptr))
174         if (idx++, err = commit_section(ci->u.sec, n, commit_all)) {
175           msg(L_ERROR, "Cannot commit node #%d of list %s: %s", idx, ci->name, err);
176           return "commit of a list failed";
177         }
178     }
179   if (sec->commit) {
180     /* We have to process the whole tree of sections even if just a few changes
181      * have been made, because there are dependencies between commit-hooks and
182      * hence we need to call them in a fixed order.  */
183 #define ARY_LT_X(ary,i,x) ary[i].sec < x.sec || ary[i].sec == x.sec && ary[i].ptr < x.ptr
184     struct dirty_section comp = { sec, ptr };
185     uns pos = BIN_SEARCH_FIRST_GE_CMP(dirty.ptr, dirties, comp, ARY_LT_X);
186
187     if (commit_all
188         || (pos < dirties && dirty.ptr[pos].sec == sec && dirty.ptr[pos].ptr == ptr))
189       return sec->commit(ptr);
190   }
191   return 0;
192 }
193
194 int
195 cf_commit_all(enum cf_commit_mode cm)
196 {
197   struct cf_context *cc = cf_get_context();
198   sort_dirty();
199   if (cm == CF_NO_COMMIT)
200     return 0;
201   if (commit_section(&cc->sections, NULL, cm == CF_COMMIT_ALL))
202     return 1;
203   dirties = 0;
204   return 0;
205 }