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 }