jbsrc/lib/src/core/jb-util.c (20674B) - 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 <stdio.h> 21 #include <string.h> 22 #include <stdlib.h> 23 #include <unistd.h> 24 #include <sys/types.h> 25 #include <sys/stat.h> 26 #include <sys/wait.h> 27 #include <pwd.h> 28 #include <grp.h> 29 #include <errno.h> 30 #include <glob.h> 31 #include "jb-util.h" 32 #include "jb-variable.h" 33 #include "jb-main.h" 34 35 static char *log_file = NULL; 36 37 static gboolean printing_action = FALSE; 38 39 void 40 jb_set_log_file (const char *filename) 41 { 42 g_return_if_fail(filename != NULL); 43 44 g_free(log_file); 45 log_file = g_strdup(filename); 46 } 47 48 void 49 jb_log (const char *format, ...) 50 { 51 static gboolean logging = FALSE; 52 static GIOChannel *log_channel = NULL; 53 static char *current_log_file = NULL; 54 char *message; 55 char *with_nl; 56 GError *err = NULL; 57 gsize bytes_written; 58 59 g_return_if_fail(format != NULL); 60 g_return_if_fail(log_file != NULL); 61 62 if (logging) 63 return; 64 65 logging = TRUE; 66 67 if (log_channel != NULL) 68 { 69 if (strcmp(current_log_file, log_file)) 70 { 71 if (g_io_channel_shutdown(log_channel, TRUE, &err) != G_IO_STATUS_NORMAL) 72 jb_error("unable to write to %s: %s", current_log_file, err->message); 73 74 g_io_channel_unref(log_channel); 75 log_channel = NULL; 76 77 g_free(current_log_file); 78 } 79 } 80 81 if (log_channel == NULL) 82 { 83 current_log_file = g_strdup(log_file); 84 85 log_channel = g_io_channel_new_file(log_file, "w", &err); 86 if (log_channel == NULL) 87 jb_error("unable to open %s for writing: %s", log_file, err->message); 88 } 89 90 JB_STRDUP_VPRINTF(message, format); 91 92 with_nl = g_strdup_printf("%s\n", message); 93 g_free(message); 94 95 if (g_io_channel_write_chars(log_channel, with_nl, -1, &bytes_written, &err) != G_IO_STATUS_NORMAL 96 || g_io_channel_flush(log_channel, &err) != G_IO_STATUS_NORMAL) 97 jb_error("unable to write to %s: %s", log_file, err->message); 98 99 g_free(with_nl); 100 101 logging = FALSE; 102 } 103 104 void 105 jb_message (const char *format, ...) 106 { 107 char *message; 108 109 g_return_if_fail(format != NULL); 110 g_return_if_fail(! printing_action); 111 112 JB_STRDUP_VPRINTF(message, format); 113 114 g_print("%s\n", message); 115 jb_log("%s", message); 116 117 g_free(message); 118 } 119 120 void 121 jb_message_expand (const char *str, ...) 122 { 123 va_list args; 124 char *message; 125 126 g_return_if_fail(str != NULL); 127 128 va_start(args, str); 129 message = jb_variable_expandv(str, args); 130 va_end(args); 131 132 jb_message("%s", message); 133 g_free(message); 134 } 135 136 void 137 jb_message_action (const char *format, ...) 138 { 139 char *message; 140 141 g_return_if_fail(format != NULL); 142 g_return_if_fail(! printing_action); 143 144 JB_STRDUP_VPRINTF(message, format); 145 146 g_print("%s...", message); 147 jb_log("%s", message); 148 149 g_free(message); 150 151 printing_action = TRUE; 152 } 153 154 void 155 jb_message_checking (const char *format, ...) 156 { 157 char *message; 158 159 g_return_if_fail(format != NULL); 160 g_return_if_fail(! printing_action); 161 162 JB_STRDUP_VPRINTF(message, format); 163 jb_message_action("checking %s", message); 164 g_free(message); 165 } 166 167 void 168 jb_message_result_bool (gboolean result) 169 { 170 g_return_if_fail(printing_action); 171 172 jb_message_result_string(result ? "yes" : "no"); 173 } 174 175 void 176 jb_message_result_string (const char *result) 177 { 178 g_return_if_fail(result != NULL); 179 g_return_if_fail(printing_action); 180 181 g_print(" %s\n", result); 182 jb_log("result: %s", result); 183 jb_log(""); 184 185 printing_action = FALSE; 186 } 187 188 static void 189 finish_printing_action (void) 190 { 191 if (printing_action) 192 { 193 g_print("\n"); 194 printing_action = FALSE; 195 } 196 } 197 198 void 199 jb_message_result_string_format (const char *format, ...) 200 { 201 char *message; 202 203 g_return_if_fail(format != NULL); 204 g_return_if_fail(printing_action); 205 206 JB_STRDUP_VPRINTF(message, format); 207 jb_message_result_string(message); 208 g_free(message); 209 } 210 211 static void 212 print_warning_or_error (const char *prefix, const char *format, va_list args) 213 { 214 char *message; 215 char **lines; 216 int i; 217 218 /* 219 * We allow to interrupt an action print, in case the caller is a 220 * library function which does not know that an action is in 221 * progress. 222 */ 223 finish_printing_action(); 224 225 message = g_strdup_vprintf(format, args); 226 lines = g_strsplit(message, "\n", 0); 227 g_free(message); 228 229 for (i = 0; lines[i] != NULL; i++) 230 { 231 const char *line = lines[i]; 232 233 g_printerr("%s: %s\n", prefix, line); 234 jb_log("%s: %s", prefix, line); 235 } 236 237 g_strfreev(lines); 238 } 239 240 void 241 jb_warning (const char *format, ...) 242 { 243 va_list args; 244 245 g_return_if_fail(format != NULL); 246 247 va_start(args, format); 248 print_warning_or_error("WARNING", format, args); 249 va_end(args); 250 } 251 252 void 253 jb_warning_expand (const char *str, ...) 254 { 255 va_list args; 256 char *message; 257 258 g_return_if_fail(str != NULL); 259 260 va_start(args, str); 261 message = jb_variable_expandv(str, args); 262 va_end(args); 263 264 jb_warning("%s", message); 265 g_free(message); 266 } 267 268 void 269 jb_error (const char *format, ...) 270 { 271 va_list args; 272 273 g_assert(format != NULL); 274 275 va_start(args, format); 276 print_warning_or_error("ERROR", format, args); 277 va_end(args); 278 279 exit(1); 280 } 281 282 void 283 jb_error_expand (const char *str, ...) 284 { 285 va_list args; 286 char *message; 287 288 g_assert(str != NULL); 289 290 va_start(args, str); 291 message = jb_variable_expandv(str, args); 292 va_end(args); 293 294 jb_error("%s", message); 295 g_free(message); 296 } 297 298 void 299 jb_g_slist_free_deep (GSList *list) 300 { 301 jb_g_slist_free_deep_custom(list, (GFunc) g_free, NULL); 302 } 303 304 void 305 jb_g_slist_free_deep_custom (GSList *list, 306 GFunc element_free_func, 307 gpointer user_data) 308 { 309 g_slist_foreach(list, element_free_func, user_data); 310 g_slist_free(list); 311 } 312 313 char * 314 jb_strdelimit (const char *str, const char *delimiters, char new_delimiter) 315 { 316 char *result; 317 318 g_return_val_if_fail(str != NULL, NULL); 319 g_return_val_if_fail(delimiters != NULL, NULL); 320 321 result = g_strdup(str); 322 g_strdelimit(result, delimiters, new_delimiter); 323 324 return result; 325 } 326 327 char * 328 jb_strip_newline (const char *str) 329 { 330 int len; 331 332 g_return_val_if_fail(str != NULL, NULL); 333 334 len = strlen(str); 335 if (len > 0 && str[len - 1] == '\n') 336 return g_strndup(str, len - 1); 337 else 338 return g_strdup(str); 339 } 340 341 char * 342 jb_c_quote (const char *str) 343 { 344 GString *result; 345 const char *p; 346 347 g_return_val_if_fail(str != NULL, NULL); 348 349 result = g_string_new("\""); 350 351 for (p = str; *p != '\0'; p++) 352 { 353 char c = *p; 354 355 switch (c) 356 { 357 case '\\': 358 g_string_append(result, "\\\\"); 359 break; 360 361 case '"': 362 g_string_append(result, "\\\""); 363 break; 364 365 case '\r': 366 g_string_append(result, "\\r"); 367 break; 368 369 case '\n': 370 g_string_append(result, "\\n"); 371 break; 372 373 case '\t': 374 g_string_append(result, "\\t"); 375 break; 376 377 default: 378 g_string_append_c(result, c); 379 break; 380 } 381 } 382 383 g_string_append_c(result, '"'); 384 385 return g_string_free(result, FALSE); 386 } 387 388 char * 389 jb_strip_extension (const char *filename) 390 { 391 char *p; 392 393 p = strrchr(filename, '.'); 394 if (p != NULL) 395 return g_strndup(filename, p - filename); 396 else 397 return g_strdup(filename); 398 } 399 400 char * 401 jb_strip_chars (const char *str, const char *chars) 402 { 403 const char *p; 404 GString *result; 405 406 g_return_val_if_fail(str != NULL, NULL); 407 g_return_val_if_fail(chars != NULL, NULL); 408 409 result = g_string_new(NULL); 410 411 for (p = str; *p != '\0'; p++) 412 { 413 char c = *p; 414 415 if (strchr(chars, c) == NULL) 416 g_string_append_c(result, c); 417 } 418 419 return g_string_free(result, FALSE); 420 } 421 422 char * 423 jb_utf8_escape (const char *str) 424 { 425 GString *escaped; 426 427 g_return_val_if_fail(str != NULL, NULL); 428 429 escaped = g_string_new(NULL); 430 431 while (*str != '\0') 432 { 433 gunichar c; 434 435 c = g_utf8_get_char_validated(str, -1); 436 if (c != (gunichar) -2 && c != (gunichar) -1) 437 { 438 g_string_append_unichar(escaped, c); 439 str = g_utf8_next_char(str); 440 } 441 else 442 { 443 g_string_append_printf(escaped, "\\x%02x", (unsigned int) (unsigned char) *str); 444 str++; 445 } 446 } 447 448 return g_string_free(escaped, FALSE); 449 } 450 451 gboolean 452 jb_parse_uint32 (const char *str, int base, guint32 *value, GError **err) 453 { 454 guint64 v; 455 456 g_return_val_if_fail(str != NULL, FALSE); 457 458 if (! jb_parse_uint64(str, base, &v, err)) 459 return FALSE; 460 461 if (v > G_MAXUINT32) 462 { 463 g_set_error(err, 0, 0, "number out of range"); 464 return FALSE; 465 } 466 467 *value = v; 468 return TRUE; 469 } 470 471 gboolean 472 jb_parse_uint64 (const char *str, int base, guint64 *value, GError **err) 473 { 474 guint64 v; 475 char *end; 476 477 g_return_val_if_fail(str != NULL, FALSE); 478 479 v = g_ascii_strtoull(str, &end, base); 480 481 if (*end != '\0') 482 { 483 g_set_error(err, 0, 0, "invalid number"); 484 return FALSE; 485 } 486 487 if (v == G_MAXUINT64 && errno == ERANGE) 488 { 489 g_set_error(err, 0, 0, "number out of range"); 490 return FALSE; 491 } 492 493 *value = v; 494 return TRUE; 495 } 496 497 gboolean 498 jb_write_file (const char *filename, const char *contents, GError **err) 499 { 500 GIOChannel *channel; 501 gsize bytes_written; 502 gboolean status = FALSE; 503 504 g_return_val_if_fail(filename != NULL, FALSE); 505 g_return_val_if_fail(contents != NULL, FALSE); 506 507 channel = g_io_channel_new_file(filename, "w", err); 508 if (channel == NULL) 509 return FALSE; 510 511 if (g_io_channel_write_chars(channel, contents, -1, &bytes_written, err) == G_IO_STATUS_NORMAL) 512 { 513 if (g_io_channel_shutdown(channel, TRUE, err) == G_IO_STATUS_NORMAL) 514 status = TRUE; 515 } 516 else 517 g_io_channel_shutdown(channel, FALSE, NULL); 518 519 g_io_channel_unref(channel); 520 521 return status; 522 } 523 524 void 525 jb_write_file_or_exit (const char *filename, const char *contents) 526 { 527 GError *err = NULL; 528 529 if (! jb_write_file(filename, contents, &err)) 530 jb_error("cannot write %s: %s", filename, err->message); 531 } 532 533 char * 534 jb_read_file (const char *filename, GError **err) 535 { 536 GIOChannel *channel; 537 char *contents = NULL; 538 gsize length; 539 540 g_return_val_if_fail(filename != NULL, FALSE); 541 542 channel = g_io_channel_new_file(filename, "r", err); 543 if (channel == NULL) 544 return NULL; 545 546 g_io_channel_read_to_end(channel, &contents, &length, err); 547 548 g_io_channel_shutdown(channel, FALSE, NULL); 549 g_io_channel_unref(channel); 550 551 return contents; 552 } 553 554 char * 555 jb_read_file_or_exit (const char *filename) 556 { 557 GError *err = NULL; 558 char *contents; 559 560 contents = jb_read_file(filename, &err); 561 if (contents == NULL) 562 jb_error("cannot read %s: %s", filename, err->message); 563 564 return contents; 565 } 566 567 GSList * 568 jb_match_files (const char *pattern) 569 { 570 glob_t glob_result; 571 572 g_return_val_if_fail(pattern != NULL, NULL); 573 574 if (glob(pattern, 0, NULL, &glob_result) == 0) 575 { 576 int i; 577 GSList *files = NULL; 578 579 for (i = 0; i < glob_result.gl_pathc; i++) 580 files = g_slist_append(files, g_strdup(glob_result.gl_pathv[i])); 581 582 globfree(&glob_result); 583 584 return files; 585 } 586 else 587 return NULL; 588 } 589 590 void 591 jb_chdir (const char *path) 592 { 593 g_return_if_fail(path != NULL); 594 595 if (chdir(path) < 0) 596 jb_error("cannot change directory to %s: %s", path, g_strerror(errno)); 597 } 598 599 void 600 jb_mkdir (const char *pathname) 601 { 602 g_return_if_fail(pathname != NULL); 603 604 if (g_mkdir_with_parents(pathname, 0755) < 0) 605 jb_error("cannot create directory %s: %s", pathname, g_strerror(errno)); 606 } 607 608 void 609 jb_mkdir_of_file (const char *filename) 610 { 611 char *dir; 612 613 g_return_if_fail(filename != NULL); 614 615 dir = g_path_get_dirname(filename); 616 jb_mkdir(dir); 617 g_free(dir); 618 } 619 620 void 621 jb_rename (const char *oldpath, const char *newpath) 622 { 623 g_return_if_fail(oldpath != NULL); 624 g_return_if_fail(newpath != NULL); 625 626 if (rename(oldpath, newpath) < 0) 627 jb_error("cannot rename %s to %s: %s", oldpath, newpath, g_strerror(errno)); 628 } 629 630 void 631 jb_chmod (const char *path, mode_t mode) 632 { 633 g_return_if_fail(path != NULL); 634 635 if (chmod(path, mode) < 0) 636 jb_error("cannot chmod %s to " JB_MODE_FORMAT ": %s", path, (unsigned int) mode, g_strerror(errno)); 637 } 638 639 gboolean 640 jb_fchown_by_name (int fd, 641 const char *owner, 642 const char *group, 643 GError **err) 644 { 645 uid_t uid = -1; 646 gid_t gid = -1; 647 648 g_return_val_if_fail(fd >= 0, FALSE); 649 g_return_val_if_fail(owner != NULL || group != NULL, FALSE); 650 651 if (owner != NULL) 652 { 653 struct passwd *info; 654 655 info = getpwnam(owner); 656 if (info == NULL) 657 { 658 g_set_error(err, 0, 0, "unknown user \"%s\"", owner); 659 return FALSE; 660 } 661 662 uid = info->pw_uid; 663 } 664 665 if (group != NULL) 666 { 667 struct group *info; 668 669 info = getgrnam(group); 670 if (group == NULL) 671 { 672 g_set_error(err, 0, 0, "unknown group \"%s\"", group); 673 return FALSE; 674 } 675 676 gid = info->gr_gid; 677 } 678 679 if (fchown(fd, uid, gid) < 0) 680 { 681 g_set_error(err, 0, 0, "%s", g_strerror(errno)); 682 return FALSE; 683 } 684 685 return TRUE; 686 } 687 688 void 689 jb_rmtree (const char *dir) 690 { 691 /* 692 * Be paranoid and refuse abberant inputs. These tests are not 693 * assertions to make sure that they won't be compiled out. 694 */ 695 if (dir == NULL) 696 g_error("dir is NULL"); 697 if (g_path_is_absolute(dir)) 698 g_error("refusing to rmtree an absolute path"); 699 700 jb_exec(NULL, NULL, "rm -rf %s", dir); 701 } 702 703 static char * 704 subst_real (const char *str, GHashTable *variables) 705 { 706 GString *result; 707 const char *p; 708 709 result = g_string_new(NULL); 710 711 for (p = str; *p != '\0';) 712 { 713 char c = *p; 714 715 if (c == '@') 716 { 717 const char *start; 718 char *end; 719 720 start = p + 1; 721 end = strpbrk(start, "@\n"); 722 723 if (end != NULL && *end == '@') 724 { 725 char *name; 726 const char *value; 727 728 name = g_strndup(start, end - start); 729 value = g_hash_table_lookup(variables, name); 730 g_free(name); 731 732 if (value != NULL) 733 { 734 g_string_append(result, value); 735 p = end + 1; 736 continue; 737 } 738 } 739 } 740 741 g_string_append_c(result, c); 742 p++; 743 } 744 745 return g_string_free(result, FALSE); 746 } 747 748 void 749 jb_subst (const char *infile, 750 const char *outfile, 751 GHashTable *variables) 752 { 753 char *contents; 754 char *result; 755 char *tmp_outfile; 756 757 g_return_if_fail(infile != NULL); 758 g_return_if_fail(outfile != NULL); 759 g_return_if_fail(variables != NULL); 760 761 contents = jb_read_file_or_exit(infile); 762 result = subst_real(contents, variables); 763 g_free(contents); 764 765 tmp_outfile = g_strdup_printf("%s.tmp", outfile); 766 jb_write_file_or_exit(tmp_outfile, result); 767 g_free(result); 768 769 jb_rename(tmp_outfile, outfile); 770 g_free(tmp_outfile); 771 } 772 773 static char * 774 convert_process_output (const char *output) 775 { 776 char *utf8; 777 char *no_nl; 778 779 if (g_utf8_validate(output, -1, NULL)) 780 utf8 = g_strdup(output); 781 else 782 { 783 utf8 = g_locale_to_utf8(output, -1, NULL, NULL, NULL); 784 if (utf8 == NULL) 785 utf8 = jb_utf8_escape(output); 786 } 787 788 no_nl = jb_strip_newline(utf8); 789 g_free(utf8); 790 791 return no_nl; 792 } 793 794 /* 795 * Returns TRUE if the command exited with status 0. 796 * 797 * The trailing newline of @standard_output and @standard_error will 798 * be stripped. 799 * 800 * @standard_output and @standard_error will be set even if FALSE is 801 * returned, since a command can produce an output even if it exits 802 * with a non-zero status. 803 */ 804 gboolean 805 jb_exec (char **standard_output, 806 char **standard_error, 807 const char *format, 808 ...) 809 { 810 char *command; 811 int command_status; 812 gboolean status = FALSE; 813 char *_stdout; 814 char *_stderr; 815 char *converted_stdout = NULL; 816 char *converted_stderr = NULL; 817 char *shell_argv[4]; 818 GError *err = NULL; 819 820 g_return_val_if_fail(format != NULL, FALSE); 821 822 JB_STRDUP_VPRINTF(command, format); 823 824 shell_argv[0] = "/bin/sh"; 825 shell_argv[1] = "-c"; 826 shell_argv[2] = command; 827 shell_argv[3] = NULL; 828 829 if (g_spawn_sync(NULL, 830 shell_argv, 831 NULL, 832 G_SPAWN_SEARCH_PATH, 833 NULL, 834 NULL, 835 &_stdout, 836 &_stderr, 837 &command_status, 838 &err)) 839 { 840 converted_stdout = convert_process_output(_stdout); 841 g_free(_stdout); 842 843 converted_stderr = convert_process_output(_stderr); 844 g_free(_stderr); 845 846 if (WIFEXITED(command_status)) 847 { 848 int exit_status; 849 850 exit_status = WEXITSTATUS(command_status); 851 if (exit_status == 0) 852 { 853 jb_log("command \"%s\" succeeded", command); 854 status = TRUE; 855 } 856 else 857 jb_log("command \"%s\" failed with status %i", command, exit_status); 858 859 if (*converted_stdout != '\0') 860 { 861 jb_log("standard output:"); 862 jb_log("%s", converted_stdout); 863 jb_log(JB_SEPARATOR); 864 } 865 if (*converted_stderr != '\0') 866 { 867 jb_log("standard error output:"); 868 jb_log("%s", converted_stderr); 869 jb_log(JB_SEPARATOR); 870 } 871 } 872 else 873 jb_log("command exited abnormally"); 874 } 875 else 876 /* fatal error: it should not happend since we exec the shell */ 877 jb_error("cannot execute command \"%s\": %s", command, err->message); 878 879 g_free(command); 880 881 if (standard_output) 882 *standard_output = converted_stdout; 883 else 884 g_free(converted_stdout); 885 886 if (standard_error) 887 *standard_error = converted_stderr; 888 else 889 g_free(converted_stderr); 890 891 return status; 892 } 893 894 gboolean 895 jb_exec_expand (char **standard_output, 896 char **standard_error, 897 const char *str, 898 ...) 899 { 900 va_list args; 901 char *command; 902 gboolean result; 903 904 g_return_val_if_fail(str != NULL, FALSE); 905 906 va_start(args, str); 907 command = jb_variable_expandv(str, args); 908 va_end(args); 909 910 result = jb_exec(standard_output, standard_error, "%s", command); 911 g_free(command); 912 913 return result; 914 } 915 916 JBStringHashSet * 917 jb_string_hash_set_new (void) 918 { 919 return g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); 920 } 921 922 gboolean 923 jb_string_hash_set_add (JBStringHashSet *set, const char *value) 924 { 925 g_return_val_if_fail(set != NULL, FALSE); 926 g_return_val_if_fail(value != NULL, FALSE); 927 928 if (g_hash_table_lookup(set, value) != NULL) 929 return FALSE; 930 931 g_hash_table_insert(set, g_strdup(value), GINT_TO_POINTER(TRUE)); 932 return TRUE; 933 } 934 935 gboolean 936 jb_string_hash_set_contains (JBStringHashSet *set, const char *value) 937 { 938 g_return_val_if_fail(set != NULL, FALSE); 939 g_return_val_if_fail(value != NULL, FALSE); 940 941 return g_hash_table_lookup_extended(set, value, NULL, NULL); 942 } 943 944 gboolean 945 jb_is_uptodate (const char *dst, const char *src) 946 { 947 struct stat dst_sb; 948 struct stat src_sb; 949 950 g_return_val_if_fail(dst != NULL, FALSE); 951 g_return_val_if_fail(src != NULL, FALSE); 952 953 if (stat(dst, &dst_sb) < 0) 954 return FALSE; 955 956 if (stat(src, &src_sb) < 0) 957 g_error("%s (dependency of %s) does not exist", src, dst); 958 959 return dst_sb.st_mtime >= src_sb.st_mtime; 960 } 961 962 gboolean 963 jb_is_uptodate_list (const char *dst, GSList *src_list) 964 { 965 struct stat dst_sb; 966 GSList *l; 967 968 g_return_val_if_fail(dst != NULL, FALSE); 969 g_return_val_if_fail(src_list != NULL, FALSE); 970 971 if (stat(dst, &dst_sb) < 0) 972 return FALSE; 973 974 JB_LIST_FOREACH(l, src_list) 975 { 976 const char *src = l->data; 977 struct stat src_sb; 978 979 if (stat(src, &src_sb) < 0) 980 g_error("%s (dependency of %s) does not exist", src, dst); 981 982 if (dst_sb.st_mtime < src_sb.st_mtime) 983 return FALSE; 984 } 985 986 return TRUE; 987 } 988 989 gboolean 990 jb_is_uptodate_list_list (GSList *dst_list, GSList *src_list) 991 { 992 GArray *dst_mtimes; 993 GSList *l; 994 gboolean result = TRUE; 995 const char *filename; 996 struct stat sb; 997 998 dst_mtimes = g_array_new(FALSE, FALSE, sizeof(time_t)); 999 1000 JB_LIST_FOREACH(l, dst_list) 1001 { 1002 filename = l->data; 1003 1004 if (stat(filename, &sb) < 0) 1005 { 1006 result = FALSE; 1007 goto end; 1008 } 1009 1010 g_array_append_val(dst_mtimes, sb.st_mtime); 1011 } 1012 1013 JB_LIST_FOREACH(l, src_list) 1014 { 1015 int i; 1016 1017 filename = l->data; 1018 1019 if (stat(filename, &sb) < 0) 1020 g_error("%s (dependency of %s) does not exist", filename, jb_string_list_join(dst_list, " ")); 1021 1022 for (i = 0; i < dst_mtimes->len; i++) 1023 { 1024 time_t dst_mtime = g_array_index(dst_mtimes, time_t, i); 1025 1026 if (dst_mtime < sb.st_mtime) 1027 { 1028 result = FALSE; 1029 goto end; 1030 } 1031 } 1032 } 1033 1034 end: 1035 g_array_free(dst_mtimes, TRUE); 1036 return result; 1037 } 1038 1039 char * 1040 jb_string_list_join (GSList *list, const char *separator) 1041 { 1042 GString *result; 1043 GSList *l; 1044 1045 g_return_val_if_fail(separator != NULL, NULL); 1046 1047 result = g_string_new(NULL); 1048 1049 JB_LIST_FOREACH(l, list) 1050 { 1051 const char *str = l->data; 1052 1053 g_string_append(result, str); 1054 1055 if (l->next != NULL) 1056 g_string_append(result, separator); 1057 } 1058 1059 return g_string_free(result, FALSE); 1060 }