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