]> mj.ucw.cz Git - libucw.git/blob - lib/conf-section.c
conf2: initialize dynamic arrays so that they are not NULL
[libucw.git] / lib / conf-section.c
1 /*
2  *      UCW Library -- Configuration files: sections
3  *
4  *      (c) 2001--2006 Robert Spalek <robert@ucw.cz>
5  *      (c) 2003--2006 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 "lib/lib.h"
12 #include "lib/conf.h"
13 #include "lib/conf-internal.h"
14 #include "lib/clists.h"
15 #include "lib/binsearch.h"
16
17 #include <string.h>
18
19 #define TRY(f)  do { byte *_msg = f; if (_msg) return _msg; } while (0)
20
21 /* Dirty sections */
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 "lib/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 "lib/arraysort.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_section cf_sections;  // root section
73
74 struct cf_item *
75 cf_find_subitem(struct cf_section *sec, byte *name)
76 {
77   struct cf_item *ci = sec->cfg;
78   for (; ci->cls; ci++)
79     if (!strcasecmp(ci->name, name))
80       return ci;
81   return ci;
82 }
83
84 static void
85 inspect_section(struct cf_section *sec)
86 {
87   sec->flags = 0;
88   struct cf_item *ci;
89   for (ci=sec->cfg; ci->cls; ci++)
90     if (ci->cls == CC_SECTION) {
91       inspect_section(ci->u.sec);
92       sec->flags |= ci->u.sec->flags & (SEC_FLAG_DYNAMIC | SEC_FLAG_CANT_COPY);
93     } else if (ci->cls == CC_LIST) {
94       inspect_section(ci->u.sec);
95       sec->flags |= SEC_FLAG_DYNAMIC | SEC_FLAG_CANT_COPY;
96     } else if (ci->cls == CC_DYNAMIC)
97       sec->flags |= SEC_FLAG_DYNAMIC;
98     else if (ci->cls == CC_PARSER) {
99       sec->flags |= SEC_FLAG_CANT_COPY;
100       if (ci->number < 0)
101         sec->flags |= SEC_FLAG_DYNAMIC;
102     }
103   if (sec->copy)
104     sec->flags &= ~SEC_FLAG_CANT_COPY;
105   sec->flags |= ci - sec->cfg;          // record the number of entries
106 }
107
108 void
109 cf_declare_section(byte *name, struct cf_section *sec, uns allow_unknown)
110 {
111   if (!cf_sections.cfg)
112   {
113     cf_sections.size = 50;
114     cf_sections.cfg = xmalloc_zero(cf_sections.size * sizeof(struct cf_item));
115   }
116   struct cf_item *ci = cf_find_subitem(&cf_sections, name);
117   if (ci->cls)
118     die("Cannot register section %s twice", name);
119   ci->cls = CC_SECTION;
120   ci->name = name;
121   ci->number = 1;
122   ci->ptr = NULL;
123   ci->u.sec = sec;
124   inspect_section(sec);
125   if (allow_unknown)
126     sec->flags |= SEC_FLAG_UNKNOWN;
127   ci++;
128   if (ci - cf_sections.cfg >= (int) cf_sections.size)
129   {
130     cf_sections.cfg = xrealloc(cf_sections.cfg, 2*cf_sections.size * sizeof(struct cf_item));
131     bzero(cf_sections.cfg + cf_sections.size, cf_sections.size * sizeof(struct cf_item));
132     cf_sections.size *= 2;
133   }
134 }
135
136 void
137 cf_init_section(byte *name, struct cf_section *sec, void *ptr, uns do_bzero)
138 {
139   if (do_bzero) {
140     ASSERT(sec->size);
141     bzero(ptr, sec->size);
142   }
143   for (uns i=0; sec->cfg[i].cls; i++)
144     if (sec->cfg[i].cls == CC_SECTION)
145       cf_init_section(sec->cfg[i].name, sec->cfg[i].u.sec, ptr + (addr_int_t) sec->cfg[i].ptr, 0);
146     else if (sec->cfg[i].cls == CC_LIST)
147       clist_init(ptr + (addr_int_t) sec->cfg[i].ptr);
148   if (sec->init) {
149     byte *msg = sec->init(ptr);
150     if (msg)
151       die("Cannot initialize section %s: %s", name, msg);
152   }
153 }
154
155 static void
156 replace_null_dary(struct cf_item *item, void **ptr)
157 {
158   static u64 zero = 0;
159   if (*ptr)
160     return;
161   uns size = cf_type_size(item->type, item->u.utype);
162   cf_journal_block(ptr, size);
163   if (size <= sizeof(zero))
164     *ptr = (&zero) + 1;
165   else
166     *ptr = cf_malloc_zero(size) + size;
167 }
168
169 static byte *
170 commit_section(struct cf_section *sec, void *ptr, uns commit_all)
171 {
172   struct cf_item *ci;
173   byte *err;
174   for (ci=sec->cfg; ci->cls; ci++)
175     if (ci->cls == CC_SECTION) {
176       if ((err = commit_section(ci->u.sec, ptr + (addr_int_t) ci->ptr, commit_all))) {
177         log(L_ERROR, "Cannot commit section %s: %s", ci->name, err);
178         return "commit of a subsection failed";
179       }
180     } else if (ci->cls == CC_LIST) {
181       uns idx = 0;
182       CLIST_FOR_EACH(cnode *, n, * (clist*) (ptr + (addr_int_t) ci->ptr))
183         if (idx++, err = commit_section(ci->u.sec, n, commit_all)) {
184           log(L_ERROR, "Cannot commit node #%d of list %s: %s", idx, ci->name, err);
185           return "commit of a list failed";
186         }
187     }
188   if (sec->commit) {
189     /* We have to process the whole tree of sections even if just a few changes
190      * have been made, because there are dependencies between commit-hooks and
191      * hence we need to call them in a fixed order.  */
192 #define ARY_LT_X(ary,i,x) ary[i].sec < x.sec || ary[i].sec == x.sec && ary[i].ptr < x.ptr
193     struct dirty_section comp = { sec, ptr };
194     uns pos = BIN_SEARCH_FIRST_GE_CMP(dirty.ptr, dirties, comp, ARY_LT_X);
195
196     if (commit_all
197         || (pos < dirties && dirty.ptr[pos].sec == sec && dirty.ptr[pos].ptr == ptr))
198       TRY( sec->commit(ptr) );
199   }
200   for (ci=sec->cfg; ci->cls; ci++)
201     if (ci->cls == CC_DYNAMIC)
202       replace_null_dary(ci, ptr + (addr_int_t) ci->ptr);
203   return 0;
204 }
205
206 int
207 cf_commit_all(enum cf_commit_mode cm)
208 {
209   sort_dirty();
210   if (cm == CF_NO_COMMIT)
211     return 0;
212   if (commit_section(&cf_sections, NULL, cm == CF_COMMIT_ALL))
213     return 1;
214   dirties = 0;
215   return 0;
216 }