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 }