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