jbsrc/lib/src/core/jb-variable.c (14586B) - raw
1 /*
2 * JB, the Jean-Yves Lefort's Build System
3 * Copyright (C) 2008 Jean-Yves Lefort <jylefort@brutele.be>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <string.h>
21 #include "jb-variable.h"
22 #include "jb-util.h"
23
24 static GHashTable *variable_types = NULL;
25 static GHashTable *variables_hash_table = NULL;
26
27 GSList *jb_variables = NULL;
28
29 GSList *jb_variable_groups = NULL;
30
31 JBVariableGroup *jb_variable_group_compiler_options = NULL;
32 JBVariableGroup *jb_variable_group_installation_options = NULL;
33 JBVariableGroup *jb_variable_group_external_programs = NULL;
34
35 JBVariableType *jb_variable_type_bool = NULL;
36 JBVariableType *jb_variable_type_string = NULL;
37 JBVariableType *jb_variable_type_mode = NULL;
38
39 static JBVariableType *
40 variable_type_new (const char *name,
41 GType gtype,
42 gboolean (*from_string) (const char *str, GValue *value, GError **err),
43 char * (*to_string) (const GValue *value))
44 {
45 JBVariableType *self;
46
47 self = g_new0(JBVariableType, 1);
48 self->name = g_strdup(name);
49 self->gtype = gtype;
50 self->from_string = from_string;
51 self->to_string = to_string;
52
53 return self;
54 }
55
56 static JBVariableType *
57 variable_type_add (const char *name,
58 GType gtype,
59 gboolean (*from_string) (const char *str, GValue *value, GError **err),
60 char * (*to_string) (const GValue *value))
61 {
62 JBVariableType *type;
63
64 type = variable_type_new(name, gtype, from_string, to_string);
65
66 g_hash_table_insert(variable_types, type->name, type);
67
68 return type;
69 }
70
71 static gboolean
72 bool_from_string (const char *str, GValue *value, GError **err)
73 {
74 if (! strcmp(str, "yes"))
75 g_value_set_boolean(value, TRUE);
76 else if (! strcmp(str, "no"))
77 g_value_set_boolean(value, FALSE);
78 else
79 {
80 g_set_error(err, 0, 0, "\"yes\" or \"no\" expected");
81 return FALSE;
82 }
83
84 return TRUE;
85 }
86
87 static char *
88 bool_to_string (const GValue *value)
89 {
90 if (g_value_get_boolean(value))
91 return g_strdup("yes");
92 else
93 return g_strdup("no");
94 }
95
96 static gboolean
97 string_from_string (const char *str, GValue *value, GError **err)
98 {
99 g_value_set_string(value, *str == '\0' ? NULL : str);
100
101 return TRUE;
102 }
103
104 static char *
105 string_to_string (const GValue *value)
106 {
107 char *str;
108
109 str = g_value_dup_string(value);
110
111 return str == NULL ? g_strdup("") : str;
112 }
113
114 static gboolean
115 mode_from_string (const char *str, GValue *value, GError **err)
116 {
117 guint32 mode;
118
119 if (! jb_parse_uint32(str, 8, &mode, err))
120 return FALSE;
121
122 if ((mode & ~07777) != 0)
123 {
124 g_set_error(err, 0, 0, "mode out of range");
125 return FALSE;
126 }
127
128 g_value_set_uint(value, mode);
129 return TRUE;
130 }
131
132 static char *
133 mode_to_string (const GValue *value)
134 {
135 unsigned int mode;
136
137 mode = g_value_get_uint(value);
138
139 return g_strdup_printf(JB_MODE_FORMAT, mode);
140 }
141
142 void
143 jb_variable_init (void)
144 {
145 variable_types = g_hash_table_new(g_str_hash, g_str_equal);
146 variables_hash_table = g_hash_table_new(g_str_hash, g_str_equal);
147
148 jb_variable_group_compiler_options = jb_variable_add_group("Compiler options");
149 jb_variable_group_installation_options = jb_variable_add_group("Installation options");
150 jb_variable_group_external_programs = jb_variable_add_group("External programs");
151
152 jb_variable_type_bool = variable_type_add("bool",
153 G_TYPE_BOOLEAN,
154 bool_from_string,
155 bool_to_string);
156 jb_variable_type_string = variable_type_add("string",
157 G_TYPE_STRING,
158 string_from_string,
159 string_to_string);
160 jb_variable_type_mode = variable_type_add("mode",
161 G_TYPE_UINT,
162 mode_from_string,
163 mode_to_string);
164 }
165
166 JBVariableGroup *
167 jb_variable_add_group (const char *name)
168 {
169 JBVariableGroup *group;
170
171 group = g_new0(JBVariableGroup, 1);
172 group->name = g_strdup(name);
173
174 jb_variable_groups = g_slist_append(jb_variable_groups, group);
175
176 return group;
177 }
178
179 JBVariableType *
180 jb_variable_get_type (const char *name)
181 {
182 g_return_val_if_fail(name != NULL, NULL);
183
184 return g_hash_table_lookup(variable_types, name);
185 }
186
187 static JBVariable *
188 variable_new (JBVariableType *type,
189 const char *name,
190 const char *description,
191 JBVariableGroup *group,
192 JBVariableFlags flags)
193 {
194 JBVariable *variable;
195
196 g_return_val_if_fail(type != NULL, NULL);
197 g_return_val_if_fail(name != NULL, NULL);
198
199 variable = g_new0(JBVariable, 1);
200 variable->type = type;
201 variable->name = g_strdup(name);
202 variable->description = g_strdup(description);
203 variable->group = group;
204 variable->flags = flags;
205
206 g_value_init(&variable->value, type->gtype);
207
208 return variable;
209 }
210
211 static void
212 add_variable (JBVariable *variable)
213 {
214 g_return_if_fail(variable != NULL);
215 g_return_if_fail(g_hash_table_lookup(variables_hash_table, variable->name) == NULL);
216
217 g_hash_table_insert(variables_hash_table, variable->name, variable);
218 jb_variables = g_slist_append(jb_variables, variable);
219 }
220
221 JBVariable *
222 jb_variable_get_variable (const char *name)
223 {
224 g_return_val_if_fail(name != NULL, NULL);
225
226 return g_hash_table_lookup(variables_hash_table, name);
227 }
228
229 JBVariable *
230 jb_variable_get_variable_or_error (const char *name)
231 {
232 JBVariable *variable;
233
234 g_return_val_if_fail(name != NULL, NULL);
235
236 variable = jb_variable_get_variable(name);
237 if (variable == NULL)
238 g_error("variable \"%s\" does not exist", name);
239
240 return variable;
241 }
242
243 JBVariable *
244 ensure_variable (JBVariableType *type, const char *name)
245 {
246 JBVariable *variable;
247
248 g_return_val_if_fail(name != NULL, NULL);
249
250 variable = jb_variable_get_variable(name);
251 if (variable == NULL)
252 {
253 variable = variable_new(type, name, NULL, NULL, 0);
254 add_variable(variable);
255 }
256
257 return variable;
258 }
259
260 JBVariable *
261 jb_variable_add (JBVariableType *type,
262 const char *name,
263 const char *description,
264 JBVariableGroup *group,
265 JBVariableFlags flags)
266 {
267 JBVariable *variable;
268
269 g_return_val_if_fail(type != NULL, NULL);
270 g_return_val_if_fail(name != NULL, NULL);
271
272 variable = variable_new(type, name, description, group, flags | JB_VARIABLE_USER_SETTABLE);
273
274 add_variable(variable);
275
276 return variable;
277 }
278
279 void
280 jb_variable_add_bool (const char *name,
281 const char *description,
282 JBVariableGroup *group,
283 JBVariableFlags flags,
284 gboolean default_value)
285 {
286 JBVariable *variable;
287
288 g_return_if_fail(name != NULL);
289
290 variable = jb_variable_add(jb_variable_type_bool, name, description, group, flags);
291
292 g_value_set_boolean(&variable->value, default_value);
293 }
294
295 void
296 jb_variable_add_string (const char *name,
297 const char *description,
298 JBVariableGroup *group,
299 JBVariableFlags flags,
300 const char *default_value)
301 {
302 JBVariable *variable;
303
304 g_return_if_fail(name != NULL);
305
306 variable = jb_variable_add(jb_variable_type_string, name, description, group, flags);
307
308 g_value_set_string(&variable->value, default_value);
309 }
310
311 void
312 jb_variable_add_mode (const char *name,
313 const char *description,
314 JBVariableGroup *group,
315 JBVariableFlags flags,
316 mode_t default_value)
317 {
318 JBVariable *variable;
319
320 g_return_if_fail(name != NULL);
321
322 variable = jb_variable_add(jb_variable_type_mode, name, description, group, flags);
323
324 g_value_set_uint(&variable->value, default_value);
325 }
326
327 gboolean
328 jb_variable_get_bool (const char *name)
329 {
330 JBVariable *variable;
331
332 g_return_val_if_fail(name != NULL, FALSE);
333
334 variable = jb_variable_get_variable_or_error(name);
335 g_return_val_if_fail(variable->type == jb_variable_type_bool, FALSE);
336
337 return g_value_get_boolean(&variable->value);
338 }
339
340 void
341 jb_variable_set_bool (const char *name, gboolean value)
342 {
343 JBVariable *variable;
344
345 g_return_if_fail(name != NULL);
346
347 variable = ensure_variable(jb_variable_type_bool, name);
348
349 g_value_set_boolean(&variable->value, value);
350 }
351
352 const char *
353 jb_variable_get_string (const char *name)
354 {
355 const char *value;
356
357 g_return_val_if_fail(name != NULL, FALSE);
358
359 value = jb_variable_get_string_or_null(name);
360
361 return value != NULL ? value : "";
362 }
363
364 const char *
365 jb_variable_get_string_or_null (const char *name)
366 {
367 JBVariable *variable;
368
369 g_return_val_if_fail(name != NULL, FALSE);
370
371 variable = jb_variable_get_variable_or_error(name);
372 g_return_val_if_fail(variable->type == jb_variable_type_string, FALSE);
373
374 return g_value_get_string(&variable->value);
375 }
376
377 void
378 jb_variable_set_string (const char *name, const char *value)
379 {
380 JBVariable *variable;
381
382 g_return_if_fail(name != NULL);
383
384 variable = ensure_variable(jb_variable_type_string, name);
385
386 g_value_set_string(&variable->value, value);
387 }
388
389 mode_t
390 jb_variable_get_mode (const char *name)
391 {
392 JBVariable *variable;
393
394 g_return_val_if_fail(name != NULL, FALSE);
395
396 variable = jb_variable_get_variable_or_error(name);
397 g_return_val_if_fail(variable->type == jb_variable_type_mode, FALSE);
398
399 return g_value_get_uint(&variable->value);
400 }
401
402 void
403 jb_variable_set_mode (const char *name, mode_t value)
404 {
405 JBVariable *variable;
406
407 g_return_if_fail(name != NULL);
408
409 variable = ensure_variable(jb_variable_type_mode, name);
410
411 g_value_set_uint(&variable->value, value);
412 }
413
414 void
415 jb_variable_set_package_flags (const char *name,
416 const char *cflags,
417 const char *cppflags,
418 const char *ldflags,
419 const char *libs)
420 {
421 char *cflags_var;
422 char *cppflags_var;
423 char *ldflags_var;
424 char *libs_var;
425
426 g_return_if_fail(name != NULL);
427
428 cflags_var = g_strdup_printf("%s-cflags", name);
429 jb_variable_set_string(cflags_var, cflags);
430 g_free(cflags_var);
431
432 cppflags_var = g_strdup_printf("%s-cppflags", name);
433 jb_variable_set_string(cppflags_var, cppflags);
434 g_free(cppflags_var);
435
436 ldflags_var = g_strdup_printf("%s-ldflags", name);
437 jb_variable_set_string(ldflags_var, ldflags);
438 g_free(ldflags_var);
439
440 libs_var = g_strdup_printf("%s-libs", name);
441 jb_variable_set_string(libs_var, libs);
442 g_free(libs_var);
443 }
444
445 gboolean
446 jb_variable_set_from_string (JBVariable *self, const char *value, GError **err)
447 {
448 g_return_val_if_fail(self != NULL, FALSE);
449 g_return_val_if_fail(value != NULL, FALSE);
450
451 return self->type->from_string(value, &self->value, err);
452 }
453
454 char *
455 jb_variable_to_string (JBVariable *self)
456 {
457 g_return_val_if_fail(self != NULL, NULL);
458
459 return self->type->to_string(&self->value);
460 }
461
462 static char *
463 get_expansion (const char *varname, GHashTable *extra_variables)
464 {
465 JBVariable *variable;
466
467 if (extra_variables != NULL)
468 {
469 const char *extra_value;
470
471 if (g_hash_table_lookup_extended(extra_variables, varname, NULL, (gpointer) &extra_value))
472 return g_strdup(extra_value != NULL ? extra_value : "");
473 }
474
475 variable = jb_variable_get_variable(varname);
476 if (variable != NULL)
477 return jb_variable_evaluate(variable);
478
479 jb_error("unknown variable \"%s\"", varname);
480 }
481
482 static char *
483 expand_real (const char *str, GHashTable *extra_variables)
484 {
485 GString *result;
486 const char *p;
487
488 g_return_val_if_fail(str != NULL, NULL);
489
490 result = g_string_new(NULL);
491
492 for (p = str; *p != '\0';)
493 {
494 char c = *p;
495
496 if (c == '$')
497 {
498 char *name;
499 char *value;
500 char *expanded_value;
501
502 if (p[1] == '$')
503 {
504 g_string_append_c(result, '$');
505 p += 2;
506 continue;
507 }
508
509 if (p[1] == '{')
510 {
511 char *end;
512
513 p += 2;
514
515 end = strchr(p, '}');
516 if (end == NULL)
517 jb_error("unterminated variable reference");
518
519 if (end == p + 1)
520 jb_error("empty variable reference");
521
522 name = g_strndup(p, end - p);
523 p = end + 1;
524 }
525 else
526 {
527 const char *end;
528
529 p++;
530
531 end = p + 1;
532 while (g_ascii_isalnum(*end) || *end == '-')
533 end++;
534
535 if (end == p)
536 jb_error("empty variable reference");
537
538 name = g_strndup(p, end - p);
539 p = end;
540 }
541
542 value = get_expansion(name, extra_variables);
543
544 /* do not pass the extra variables to the recursive expansion */
545 expanded_value = expand_real(value, NULL);
546 g_free(value);
547
548 g_string_append(result, expanded_value);
549 g_free(expanded_value);
550 }
551 else
552 {
553 g_string_append_c(result, c);
554 p++;
555 }
556 }
557
558 return g_string_free(result, FALSE);
559 }
560
561 char *
562 jb_variable_evaluate (JBVariable *variable)
563 {
564 static GSList *evaluation_stack = NULL;
565 char *str;
566 char *value;
567
568 g_return_val_if_fail(variable != NULL, NULL);
569
570 if (evaluation_stack != NULL && evaluation_stack->data == variable)
571 jb_error("variable \"%s\" references itself", variable->name);
572
573 if (g_slist_find(evaluation_stack, variable) != NULL)
574 jb_error("variable \"%s\" indirectly references itself", variable->name);
575
576 evaluation_stack = g_slist_prepend(evaluation_stack, variable);
577
578 str = jb_variable_to_string(variable);
579 value = expand_real(str, NULL);
580 g_free(str);
581
582 evaluation_stack = g_slist_delete_link(evaluation_stack, evaluation_stack);
583
584 return value;
585 }
586
587 char *
588 jb_variable_expand (const char *str, ...)
589 {
590 va_list args;
591 char *expanded;
592
593 va_start(args, str);
594 expanded = jb_variable_expandv(str, args);
595 va_end(args);
596
597 return expanded;
598 }
599
600 char *
601 jb_variable_expandv (const char *str, va_list args)
602 {
603 GHashTable *extra_variables;
604 const char *extra_name;
605 char *expanded;
606
607 g_return_val_if_fail(str != NULL, NULL);
608
609 extra_variables = g_hash_table_new(g_str_hash, g_str_equal);
610
611 while ((extra_name = va_arg(args, const char *)) != NULL)
612 {
613 const char *extra_value;
614
615 extra_value = va_arg(args, const char *);
616 /*
617 * For consistency, extra_value can be NULL, since variables can
618 * be NULL as well.
619 */
620
621 if (g_hash_table_lookup(extra_variables, extra_name) != NULL)
622 g_error("extra variable \"%s\" specified twice", extra_name);
623
624 g_hash_table_insert(extra_variables, (gpointer) extra_name, (gpointer) extra_value);
625 }
626
627 expanded = expand_real(str, extra_variables);
628 g_hash_table_destroy(extra_variables);
629
630 return expanded;
631 }
632
633 const char *
634 jb_variable_get_type_name (JBVariable *self)
635 {
636 g_return_val_if_fail(self != NULL, NULL);
637
638 return self->type->name;
639 }