Lumiera  0.pre.03
»edit your freedom«
configitem.c
Go to the documentation of this file.
1 /*
2  Configitem - generalised hierarchy of configuration items
3 
4  Copyright (C) Lumiera.org
5  2008, Christian Thaeter <ct@pipapo.org>
6  Simeon Voelkel <simeon_voelkel@arcor.de>
7 
8  This program is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License as
10  published by the Free Software Foundation; either version 2 of
11  the License, or (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22 
23 
32 #include "include/logging.h"
33 #include "lib/llist.h"
34 #include "lib/safeclib.h"
35 #include "lib/tmpbuf.h"
36 
37 #include "common/config.h"
38 #include "common/configitem.h"
39 #include "common/configentry.h"
40 
41 #include <ctype.h>
42 #include <stdint.h>
43 
44 
45 static LumieraConfigitem parse_directive (LumieraConfigitem self, char* itr);
46 
47 static LumieraConfigitem parse_section (LumieraConfigitem self, char* itr);
48 
49 static LumieraConfigitem parse_configentry (LumieraConfigitem self, char* itr);
50 
51 
52 
53 
54 
55 LumieraConfigitem
56 lumiera_configitem_init (LumieraConfigitem self)
57 {
58  TRACE (configitem_dbg);
59  REQUIRE (self);
60 
61  llist_init (&self->link);
62  self->parent = NULL;
63  llist_init (&self->children);
64 
65  llist_init (&self->lookup);
66 
67  self->line = NULL;
68 
69  self->key = NULL;
70  self->key_size = 0;
71  self->delim = NULL;
72  self->vtable = NULL;
73 
74  return self;
75 }
76 
77 
78 LumieraConfigitem
79 lumiera_configitem_destroy (LumieraConfigitem self, LumieraConfigLookup lookup)
80 {
81  TRACE (configitem_dbg);
82 
83  if (self)
84  {
85  LLIST_WHILE_HEAD (&self->children, node)
86  lumiera_configitem_delete ((LumieraConfigitem) node, lookup);
87 
88  ENSURE (llist_is_empty (&self->children), "destructor didn't remove children");
89 
90  if (self->vtable && self->vtable->destroy)
91  self->vtable->destroy (self);
92 
93  if (!llist_is_empty (&self->lookup))
94  lumiera_config_lookup_remove (lookup, self);
95 
96  llist_unlink (&self->link);
97  lumiera_free (self->line);
98  }
99 
100  return self;
101 }
102 
103 
104 LumieraConfigitem
105 lumiera_configitem_new (const char* line)
106 {
107  TRACE (configitem_dbg, "%s", line);
108 
109  lumiera_configitem tmp;
110  lumiera_configitem_init (&tmp);
111 
112  lumiera_configitem_parse (&tmp, line);
113 
114  LumieraConfigitem self = tmp.vtable && tmp.vtable->newitem
115  ? tmp.vtable->newitem (&tmp)
116  : lumiera_configitem_move (lumiera_malloc (sizeof (*self)), &tmp);
117 
118  return self;
119 }
120 
121 
122 void
123 lumiera_configitem_delete (LumieraConfigitem self, LumieraConfigLookup lookup)
124 {
125  TRACE (configitem_dbg);
126  lumiera_free (lumiera_configitem_destroy (self, lookup));
127 }
128 
129 
130 LumieraConfigitem
131 lumiera_configitem_set_value (LumieraConfigitem self, const char* delim_value)
132 {
133  REQUIRE (self->key);
134  REQUIRE (self->delim);
135 
136  char* line = lumiera_tmpbuf_snprintf (SIZE_MAX, "%.*s%s", self->delim - self->line, self->line, delim_value);
137  lumiera_configitem_parse (self, line);
138 
139  return self;
140 }
141 
142 
143 LumieraConfigitem
144 lumiera_configitem_move (LumieraConfigitem self, LumieraConfigitem source)
145 {
146  TRACE (configitem_dbg);
147  REQUIRE (self);
148  REQUIRE (source);
149 
150  llist_init (&self->link);
151  llist_insertlist_next (&self->link, &source->link);
152 
153  self->parent = source->parent;
154 
155  llist_init (&self->children);
156  llist_insertlist_next (&self->children, &source->children);
157 
158  llist_init (&self->lookup);
159  llist_insertlist_next (&self->lookup, &source->lookup);
160 
161  self->line = source->line;
162  source->line = NULL;
163 
164  self->key = source->key;
165  self->key_size = source->key_size;
166  self->delim = source->delim;
167  self->vtable = source->vtable;
168 
169  return self;
170 }
171 
172 
173 LumieraConfigitem
174 lumiera_configitem_parse (LumieraConfigitem self, const char* line)
175 {
176  TRACE (configitem_dbg);
177 
178  lumiera_free (self->line);
179  self->line = lumiera_strndup (line, SIZE_MAX);
180 
182 
183  char* itr = self->line;
184 
185  /* skip leading whitespace */
186  while (*itr && isspace (*itr))
187  itr++;
188 
189  /* decide what this line represents */
190  if (!*itr || *itr == '#' )
191  {
192  /* this is an empty line or a a comment */
193  }
194  else if (*itr == '@' )
195  {
196  /* this is a directive */
197  self = parse_directive (self, itr);
198  }
199  else if (*itr == '[' )
200  {
201  /* this is a section */
202  self = parse_section (self, itr);
203  }
204  else
205  {
206  /* this is probably a configentry */
207  self = parse_configentry (self, itr);
208  }
209 
210  return self;
211 }
212 
213 
214 static LumieraConfigitem
215 parse_directive (LumieraConfigitem self, char* itr)
216 {
217  /* itr points now to @ */
218  self->key = itr;
219 
220  /* check whether there are illegal whitespace after @ */
221  itr++;
222  if (*itr && !isspace(*itr))
223  {
224  /* now look for the end of the directive and set the keysize */
225  self->key_size = strspn (itr, LUMIERA_CONFIG_KEY_CHARS);
226 
227  itr += self->key_size;
228 
229  /* we need a key with a length greater than zero and */
230  /* either end of line or whitespace after key */
231 
232  if ( self->key_size && ( !*itr || (*itr && isspace(*itr)) ))
233  {
234  /* look for given arguments */
235 
236  /* skip blanks */
237  while (*itr && isspace (*itr))
238  itr++;
239 
240  if (*itr)
241  {
242  /* there are arguments given, thus set delim */
243  self->delim = itr - 1;
244  }
245  else
246  {
247  /* no arguments were given */
248  self->delim = NULL;
249  }
250  }
251  else
252  {
253  /* malformed lines shall be treated like if they were comments */
254  self->key = NULL;
255  self->key_size = 0;
256 
257  LUMIERA_ERROR_SET (config, CONFIG_SYNTAX, self->line);
258  }
259  }
260  else
261  {
262  /* there occurred already an error right after the @! */
263  /* malformed lines shall be treated like if they were comments */
264  self->key = NULL;
265  self->key_size = 0;
266 
267  LUMIERA_ERROR_SET (config, CONFIG_SYNTAX, self->line);
268  }
269  return self;
270 }
271 
272 
273 static LumieraConfigitem
274 parse_section (LumieraConfigitem self, char* itr)
275 {
276  /* skip blanks before prefix */
277  itr++;
278  while (*itr && isspace(*itr))
279  itr++;
280 
281  /* itr points now to the begin of the key */
282  self->key = itr;
283 
284  /* now look for the end of the key and set the keysize */
285  self->key_size = strspn (itr, LUMIERA_CONFIG_KEY_CHARS);
286 
287  itr += self->key_size;
288 
289  /* if the line ends ends with prefix] delim points to ] */
290  /* and not the last (blank) character before the final square bracket */
291  if (self->key_size && *itr && *itr == ']')
292  {
293  self->delim = itr;
294  TODO("self->vtable = &lumiera_configsection_funcs;");
295  }
296  else if (self->key_size && *itr && isspace(*itr))
297  {
298  /* skip blanks until we reach the suffix or the final square bracket */
299  while (*itr && isspace(*itr))
300  itr++;
301 
302  if (*itr && *itr == ']')
303  {
304  /* final square bracket reached, so place delim one char before the */
305  /* actual position which must be a whitespace: no extra check necessary */
306  self->delim = itr - 1;
307  TODO("self->vtable = &lumiera_configsection_funcs;");
308  }
309  else if (*itr)
310  {
311  TODO("check wheter suffix is made of legal characters");
312 
313  /* delim points to the last whitespace before the actual position; */
314  /* no extra check needed */
315  self->delim = itr - 1;
316  TODO("self->vtable = &lumiera_configsection_funcs;");
317  }
318  else
319  {
320  /* malformed section line, treat this line like a comment */
321  self->key = NULL;
322  self->key_size = 0;
323 
324  LUMIERA_ERROR_SET (config, CONFIG_SYNTAX, self->line);
325  }
326  }
327  else
328  {
329  /* error: either *itr is false, points neither to a blank nor to a closed square */
330  /* bracket or the key_size is zero */
331 
332  /* treat this line like a comment */
333  self->key = NULL;
334  self->key_size = 0;
335 
336  LUMIERA_ERROR_SET (config, CONFIG_SYNTAX, self->line);
337  }
338 
339  return self;
340 }
341 
342 
343 static LumieraConfigitem
344 parse_configentry (LumieraConfigitem self, char* itr)
345 {
346  /* itr points now to the first not-whitespace-character */
347  self->key = itr;
348 
349  /* now look for the end of the key and set the keysize */
350  self->key_size = strspn (itr, LUMIERA_CONFIG_KEY_CHARS);
351 
352  /* skip blanks */
353  itr += self->key_size;
354  while (*itr && isspace (*itr))
355  itr++;
356 
357  if (self->key_size && *itr == '=')
358  {
359  /* this configentry assigns a value to a key */
360  self->delim = itr;
361  self->vtable = &lumiera_configentry_funcs;
362  }
363  else if (self->key_size && *itr == '<')
364  {
365  /* this configentry is a redirect */
366  self->delim = itr;
367  self->vtable = &lumiera_configentry_funcs;
368  }
369  else
370  {
371  /* this is not a valid configentry; treat this line like a comment */
372  self->key = NULL;
373  self->key_size = 0;
374 
375  LUMIERA_ERROR_SET (config, CONFIG_SYNTAX, self->line);
376  }
377 
378  return self;
379 }
380 
381 
382 /*
383 // Local Variables:
384 // mode: C
385 // c-file-style: "gnu"
386 // indent-tabs-mode: nil
387 // End:
388 */
Portable and safe wrappers around some C-Lib functions.
#define LUMIERA_ERROR_SET(flag, err, extra)
Helper macro to raise an error for the current thread.
Definition: error.h:91
Draft for a configuration system (2008).
Draft for a configuration system (2008).
Interface for a lumiera configuration system (draft).
This header is for including and configuring NoBug.
Round robin temporary buffers.
static void lumiera_free(void *mem)
Free previously allocated memory.
Definition: safeclib.h:82
void * lumiera_malloc(size_t size)
Allocate memory.
Definition: safeclib.c:122
LumieraConfigitem lumiera_config_lookup_remove(LumieraConfigLookup self, LumieraConfigitem item)
Remove a config item from a lookup structure.
Intrusive cyclic double linked list There is only one node type which contains a forward and a backwa...
char * lumiera_strndup(const char *str, size_t len)
Duplicate a C string.
Definition: safeclib.c:175
#define LLIST_WHILE_HEAD(list, head)
Consume a list from head.
Definition: llist.h:174
char * lumiera_tmpbuf_snprintf(size_t size, const char *fmt,...)
Construct a string in a tmpbuf.
Definition: tmpbuf.c:125