mail-notification

Fork of Jean-Yves Lefort's mail-notification, a tray icon to notify of new mail
git clone https://code.djc.id.au/git/mail-notification/

jbsrc/lib/src/core/jb-action.c (19679B) - 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 <sys/types.h>
     22 #include <sys/stat.h>
     23 #include <fcntl.h>
     24 #include <unistd.h>
     25 #include <errno.h>
     26 #include "jb-group.h"
     27 #include "jb-resource.h"
     28 #include "jb-tests.h"
     29 #include "jb-feature.h"
     30 #include "jb-config.h"
     31 #include "jb-action.h"
     32 
     33 static const char *standard_distfiles[] = {
     34   "jb",
     35   "jbsrc/jb.c",
     36   "jbsrc/lib/COPYING",
     37   "jbsrc/lib/README",
     38   "jbsrc/tools/config.guess",
     39   "jbsrc/tools/config.sub",
     40   JB_PACKAGE_SOURCES,
     41   JB_SOURCES
     42 };
     43 
     44 static const char *optional_distfiles[] = {
     45   "AUTHORS",
     46   "COPYING",
     47   "COPYING-DOCS",
     48   "INSTALL",
     49   "NEWS",
     50   "README",
     51   "TODO",
     52   "TRANSLATING",
     53   "po/POTFILES.in",
     54   "po/POTFILES.skip"
     55 };
     56 
     57 static const char *distcleanfiles[] = {
     58   JB_CONFIG_FILE,
     59   "build/jb",
     60   "build/jb.c",
     61   "build/jbsrc/jb",
     62   "build/jbsrc/jb.c"
     63 };
     64 
     65 static const char *maintainercleanfiles[] = {
     66   "jb",
     67   "jbsrc/lib",
     68   "jbsrc/tools/config.guess",
     69   "jbsrc/tools/config.sub"
     70 };
     71 
     72 static GSList *
     73 get_groups (GSList *group_names)
     74 {
     75   GSList *l;
     76   GSList *groups = NULL;
     77 
     78   if (group_names == NULL)
     79     groups = g_slist_copy(jb_groups);
     80   else
     81     JB_LIST_FOREACH(l, group_names)
     82       {
     83 	const char *name = l->data;
     84 	JBGroup *group;
     85 
     86 	group = jb_group_get(name);
     87 	if (group == NULL)
     88 	  jb_error("unknown group \"%s\"", name);
     89 
     90 	groups = g_slist_append(groups, group);
     91       }
     92 
     93   return groups;
     94 }
     95 
     96 static void
     97 perform_action (GSList *groups, void (*perform) (JBResource *res))
     98 {
     99   GSList *la;
    100 
    101   JB_LIST_FOREACH(la, groups)
    102     {
    103       JBGroup *group = la->data;
    104       GSList *lb;
    105 
    106       JB_LIST_FOREACH(lb, group->resources)
    107 	{
    108 	  JBResource *res = lb->data;
    109 
    110 	  perform(res);
    111 	}
    112     }
    113 }
    114 
    115 static void
    116 core_configure (void)
    117 {
    118   jb_check_cc_dependency_style();
    119 }
    120 
    121 static void
    122 report_variable_group (const JBVariableGroup *group, int varname_len)
    123 {
    124   GSList *l;
    125 
    126   jb_message("");
    127   jb_message("  %s:", group->name);
    128   jb_message("");
    129 
    130   JB_LIST_FOREACH(l, jb_variables)
    131     {
    132       JBVariable *variable = l->data;
    133 
    134       if (variable->group == group && (variable->flags & JB_VARIABLE_NO_REPORT) == 0)
    135 	{
    136 	  char *varname;
    137 	  char *value;
    138 
    139 	  varname = g_strdup_printf("%s:", variable->name);
    140 	  value = jb_variable_to_string(variable);
    141 
    142 	  jb_message("    %-*s %s", varname_len, varname, value);
    143 
    144 	  g_free(varname);
    145 	  g_free(value);
    146 	}
    147     }
    148 }
    149 
    150 static int
    151 get_longest_displayable_variable_name_len (void)
    152 {
    153   GSList *l;
    154   int longest = 0;
    155 
    156   JB_LIST_FOREACH(l, jb_variables)
    157     {
    158       JBVariable *variable = l->data;
    159       int len;
    160 
    161       if (variable->group == NULL)
    162 	continue;
    163 
    164       len = strlen(variable->name) + 1;	/* +1 for the semicolon */
    165       if (len > longest)
    166 	longest = len;
    167     }
    168 
    169   return longest;
    170 }
    171 
    172 static void
    173 print_configure_report (void)
    174 {
    175   int varname_len;
    176   GSList *l;
    177 
    178   jb_message("");
    179   jb_message_expand("$human-package $version was configured successfully.", NULL);
    180   jb_message("The following variables are in effect:");
    181 
    182   varname_len = get_longest_displayable_variable_name_len();
    183 
    184   JB_LIST_FOREACH(l, jb_variable_groups)
    185     report_variable_group(l->data, varname_len);
    186 
    187   jb_message("");
    188   jb_message_expand("Type \"./jb build\" to build $human-package $version.", NULL);
    189 }
    190 
    191 static void
    192 show_variable_group_help (const JBVariableGroup *group)
    193 {
    194   GSList *l;
    195 
    196   jb_message("");
    197   jb_message("  %s:", group->name);
    198 
    199   JB_LIST_FOREACH(l, jb_variables)
    200     {
    201       JBVariable *variable = l->data;
    202       char *value;
    203 
    204       if (variable->group != group)
    205 	continue;
    206 
    207       jb_message("");
    208       jb_message("    variable:      %s (%s)", variable->name, jb_variable_get_type_name(variable));
    209       jb_message("    description:   %s", variable->description);
    210 
    211       value = jb_variable_to_string(variable);
    212       jb_message("    default value: %s", value);
    213       g_free(value);
    214     }
    215 }
    216 
    217 void
    218 jb_action_help (void)
    219 {
    220   GSList *l;
    221 
    222   jb_message("Usage:");
    223   jb_message("");
    224   jb_message("  ./jb help");
    225   jb_message("  ./jb configure [VARIABLE=VALUE...]");
    226   jb_message("  ./jb build [GROUP...]");
    227   jb_message("  ./jb install [GROUP...]");
    228   jb_message("  ./jb makedist");
    229   jb_message("  ./jb clean [GROUP...]");
    230   jb_message("  ./jb distclean [GROUP...]");
    231   jb_message("  ./jb maintainerclean [GROUP...]");
    232   jb_message("");
    233   jb_message("Variables:");
    234 
    235   JB_LIST_FOREACH(l, jb_variable_groups)
    236     show_variable_group_help(l->data);
    237 }
    238 
    239 static void
    240 configure_real (void)
    241 {
    242   jb_set_log_file("build/configure.log");
    243 
    244   jb_feature_configure();
    245   core_configure();
    246   jb_package_configure();
    247 
    248   /* remove our temporary test files */
    249   unlink("build/test");
    250   unlink("build/test.c");
    251   unlink("build/test.o");
    252   unlink("build/test.o.deps");
    253 
    254   jb_config_save();
    255 }
    256 
    257 void
    258 jb_action_configure (void)
    259 {
    260   configure_real();
    261 
    262   print_configure_report();
    263 }
    264 
    265 static void
    266 ensure_configure (void)
    267 {
    268   if (g_file_test(JB_CONFIG_FILE, G_FILE_TEST_EXISTS))
    269     jb_config_load();
    270   else
    271     {
    272       jb_message_expand("configuring $human-package $version with default values since \"./jb configure\" was not run...", NULL);
    273       configure_real();
    274     }
    275 }
    276 
    277 static void
    278 build_real (GSList *groups)
    279 {
    280   jb_set_log_file("build/build.log");
    281 
    282   perform_action(groups, jb_resource_pre_build);
    283   perform_action(groups, jb_resource_build);
    284 }
    285 
    286 void
    287 jb_action_build (GSList *group_names)
    288 {
    289   GSList *groups;
    290 
    291   ensure_configure();
    292   jb_package_add_resources();
    293 
    294   groups = get_groups(group_names);
    295 
    296   build_real(groups);
    297 
    298   jb_message("");
    299   jb_message_expand("$human-package $version was built successfully.", NULL);
    300   jb_message_expand("Type \"sudo ./jb install\" to install $human-package $version.", NULL);
    301 
    302   g_slist_free(groups);
    303 }
    304 
    305 static void
    306 install_real (GSList *groups)
    307 {
    308   jb_set_log_file("build/install.log");
    309 
    310   perform_action(groups, jb_resource_install);
    311 }
    312 
    313 void
    314 jb_action_install (GSList *group_names)
    315 {
    316   GSList *groups;
    317 
    318   ensure_configure();
    319   jb_package_add_resources();
    320 
    321   groups = get_groups(group_names);
    322 
    323   build_real(groups);
    324   install_real(groups);
    325 
    326   jb_message("");
    327   jb_message_expand("$human-package $version was installed successfully.", NULL);
    328 
    329   g_slist_free(groups);
    330 }
    331 
    332 static void
    333 add_standard_distfiles_to_dist (void)
    334 {
    335   int i;
    336 
    337   for (i = 0; i < G_N_ELEMENTS(standard_distfiles); i++)
    338     jb_action_add_to_dist_string_list(standard_distfiles[i]);
    339 }
    340 
    341 static void
    342 add_optional_distfiles_to_dist (void)
    343 {
    344   int i;
    345 
    346   for (i = 0; i < G_N_ELEMENTS(optional_distfiles); i++)
    347     {
    348       const char *file = optional_distfiles[i];
    349 
    350       if (g_file_test(file, G_FILE_TEST_EXISTS))
    351 	jb_action_add_to_dist(file);
    352     }
    353 }
    354 
    355 static void
    356 makedist_real (GSList *groups)
    357 {
    358   jb_set_log_file("build/makedist.log");
    359 
    360   /* just in case */
    361   jb_rmtree(jb_action_get_distdir());
    362 
    363   add_standard_distfiles_to_dist();
    364   add_optional_distfiles_to_dist();
    365 
    366   perform_action(groups, jb_resource_makedist);
    367 }
    368 
    369 void
    370 jb_action_makedist (void)
    371 {
    372   GSList *groups;
    373   char *tarball;
    374 
    375   ensure_configure();
    376   jb_package_add_resources();
    377 
    378   groups = get_groups(NULL);
    379 
    380   build_real(groups);
    381   makedist_real(groups);
    382 
    383   tarball = jb_variable_expand("${package}-$version.tar.bz2", NULL);
    384 
    385   jb_message("creating build/%s", tarball);
    386 
    387   jb_action_exec("cd build && tar -chof - ${package}-$version | bzip2 -9 -c >$tarball",
    388 		 "tarball", tarball,
    389 		 NULL);
    390 
    391   jb_rmtree(jb_action_get_distdir());
    392 
    393   jb_message("");
    394   jb_message("build/%s was created successfully.", tarball);
    395 
    396   g_slist_free(groups);
    397   g_free(tarball);
    398 }
    399 
    400 static void
    401 clean_real (GSList *groups)
    402 {
    403   jb_set_log_file("build/clean.log");
    404 
    405   perform_action(groups, jb_resource_clean);
    406 }
    407 
    408 void
    409 jb_action_clean (GSList *group_names)
    410 {
    411   GSList *groups;
    412 
    413   ensure_configure();
    414   jb_package_add_resources();
    415 
    416   groups = get_groups(group_names);
    417 
    418   clean_real(groups);
    419 
    420   jb_message("");
    421   jb_message_expand("$human-package $version was cleaned successfully.", NULL);
    422 
    423   g_slist_free(groups);
    424 }
    425 
    426 static void
    427 distclean_real (GSList *groups)
    428 {
    429   jb_set_log_file("build/distclean.log");
    430 
    431   perform_action(groups, jb_resource_distclean);
    432 
    433   jb_action_rm_array((char **) distcleanfiles, G_N_ELEMENTS(distcleanfiles));
    434 }
    435 
    436 void
    437 jb_action_distclean (GSList *group_names)
    438 {
    439   GSList *groups;
    440 
    441   ensure_configure();
    442   jb_package_add_resources();
    443 
    444   groups = get_groups(group_names);
    445 
    446   clean_real(groups);
    447   distclean_real(groups);
    448 
    449   jb_message("");
    450   jb_message_expand("$human-package $version was dist-cleaned successfully.", NULL);
    451 
    452   g_slist_free(groups);
    453 }
    454 
    455 static void
    456 maintainerclean_real (GSList *groups)
    457 {
    458   jb_set_log_file("build/maintainerclean.log");
    459 
    460   perform_action(groups, jb_resource_maintainerclean);
    461 
    462   jb_action_rm_array((char **) maintainercleanfiles, G_N_ELEMENTS(maintainercleanfiles));
    463 }
    464 
    465 void
    466 jb_action_maintainerclean (GSList *group_names)
    467 {
    468   GSList *groups;
    469 
    470   ensure_configure();
    471   jb_package_add_resources();
    472 
    473   groups = get_groups(group_names);
    474 
    475   clean_real(groups);
    476   distclean_real(groups);
    477   maintainerclean_real(groups);
    478 
    479   jb_message("");
    480   jb_message_expand("$human-package $version was maintainer-cleaned successfully.", NULL);
    481 
    482   g_slist_free(groups);
    483 }
    484 
    485 void
    486 jb_action_exec (const char *str, ...)
    487 {
    488   va_list args;
    489   char *command;
    490   gboolean can_fail = FALSE;
    491   char *standard_output;
    492   char *standard_error;
    493 
    494   g_return_if_fail(str != NULL);
    495 
    496   va_start(args, str);
    497   command = jb_variable_expandv(str, args);
    498   va_end(args);
    499 
    500   if (command[0] == '-')
    501     {
    502       char *tmp;
    503 
    504       can_fail = TRUE;
    505 
    506       tmp = g_strdup(command + 1);
    507       g_free(command);
    508       command = tmp;
    509     }
    510 
    511   if (! jb_exec(&standard_output, &standard_error, "%s", command) && ! can_fail)
    512     {
    513       g_printerr("%s\n", command);
    514 
    515       if (*standard_output != '\0' && *standard_error != '\0')
    516 	{
    517 	  g_printerr("standard output:\n");
    518 	  g_printerr("%s\n", standard_output);
    519 
    520 	  g_printerr("standard error:\n");
    521 	  g_printerr("%s\n", standard_error);
    522 	}
    523       else if (*standard_output != '\0')
    524 	g_printerr("%s\n", standard_output);
    525       else if (*standard_error != '\0')
    526 	g_printerr("%s\n", standard_error);
    527 
    528       jb_error("command failed");
    529     }
    530 
    531   g_free(command);
    532 }
    533 
    534 /*
    535  * Doing this internally is much faster than using an external program
    536  * (install or cp), and also more portable.
    537  */
    538 static gboolean
    539 install_file_real (const char *srcfile,
    540 		   const char *dstfile,
    541 		   const char *owner,
    542 		   const char *group,
    543 		   mode_t mode,
    544 		   GError **err)
    545 {
    546   int in;
    547   int out;
    548 
    549   in = open(srcfile, O_RDONLY);
    550   if (in < 0)
    551     {
    552       g_set_error(err, 0, 0, "cannot open %s for reading: %s", srcfile, g_strerror(errno));
    553       return FALSE;
    554     }
    555 
    556   out = open(dstfile, O_CREAT | O_WRONLY | O_TRUNC, mode);
    557   if (out < 0)
    558     {
    559       /*
    560        * Unlink the file and try again, in case the file could not be
    561        * opened because it already existed and was not writable. Do
    562        * this unconditionally without testing EPERM, since it might
    563        * not be portable.
    564        */
    565       unlink(dstfile);
    566 
    567       out = open(dstfile, O_CREAT | O_WRONLY | O_TRUNC, mode);
    568       if (out < 0)
    569 	{
    570 	  g_set_error(err, 0, 0, "cannot open %s for writing: %s", dstfile, g_strerror(errno));
    571 	  goto error;
    572 	}
    573     }
    574 
    575   while (TRUE)
    576     {
    577       char buf[4096];
    578       ssize_t bytes_read;
    579       ssize_t bytes_written;
    580 
    581       bytes_read = read(in, buf, sizeof(buf));
    582       if (bytes_read < 0)
    583 	{
    584 	  g_set_error(err, 0, 0, "cannot read from %s: %s", srcfile, g_strerror(errno));
    585 	  goto error;
    586 	}
    587       if (bytes_read == 0)
    588 	break;
    589 
    590       bytes_written = write(out, buf, bytes_read);
    591       if (bytes_written < 0)
    592 	{
    593 	  g_set_error(err, 0, 0, "cannot write to %s: %s", dstfile, g_strerror(errno));
    594 	  goto error;
    595 	}
    596       if (bytes_written != bytes_read)
    597 	{
    598 	  g_set_error(err, 0, 0, "cannot write to %s", dstfile);
    599 	  goto error;
    600 	}
    601     }
    602 
    603   /*
    604    * The Linux manpage of fchmod() mentions that "as a security
    605    * measure, depending on the file system, the set-user-ID and
    606    * set-group-ID execution bits may be turned off if a file is
    607    * written", so set the ownership and permissions after writing the
    608    * file.
    609    */
    610 
    611   if (owner != NULL || group != NULL)
    612     {
    613       GError *tmp_err = NULL;
    614 
    615       if (! jb_fchown_by_name(out, owner, group, &tmp_err))
    616 	{
    617 	  if (owner != NULL && group != NULL)
    618 	    g_set_error(err, 0, 0, "cannot chown %s to %s:%s: %s", dstfile, owner, group, tmp_err->message);
    619 	  else if (owner != NULL)
    620 	    g_set_error(err, 0, 0, "cannot chown %s to owner %s: %s", dstfile, owner, tmp_err->message);
    621 	  else
    622 	    g_set_error(err, 0, 0, "cannot chown %s to group %s: %s", dstfile, group, tmp_err->message);
    623 
    624 	  g_error_free(tmp_err);
    625 	  goto error;
    626 	}
    627     }
    628 
    629   /*
    630    * Set the permissions after chowning the file, since the chown can
    631    * clear the setuid/setgid bits.
    632    */
    633 
    634   if (fchmod(out, mode) < 0)
    635     {
    636       g_set_error(err, 0, 0, "cannot chmod %s to " JB_MODE_FORMAT ": %s", dstfile, (unsigned int) mode, g_strerror(errno));
    637       goto error;
    638     }
    639 
    640   if (close(out) < 0)
    641     {
    642       g_set_error(err, 0, 0, "cannot close %s: %s", dstfile, g_strerror(errno));
    643       goto error;
    644     }
    645 
    646   close(in);
    647 
    648   return TRUE;
    649 
    650  error:
    651   close(in);
    652   close(out);
    653 
    654   return FALSE;
    655 }
    656 
    657 void
    658 jb_action_install_file (const char *srcfile,
    659 			const char *dstdir,
    660 			const char *owner,
    661 			const char *group,
    662 			mode_t mode)
    663 {
    664   char *srcfile_basename;
    665   char *dstfile;
    666 
    667   g_return_if_fail(srcfile != NULL);
    668   g_return_if_fail(dstdir != NULL);
    669 
    670   srcfile_basename = g_path_get_basename(srcfile);
    671   dstfile = g_strdup_printf("%s/%s", dstdir, srcfile_basename);
    672   g_free(srcfile_basename);
    673 
    674   jb_action_install_to_file(srcfile, dstfile, owner, group, mode);
    675   g_free(dstfile);
    676 }
    677 
    678 void
    679 jb_action_install_to_file (const char *srcfile,
    680 			   const char *dstfile,
    681 			   const char *owner,
    682 			   const char *group,
    683 			   mode_t mode)
    684 {
    685   char *real_dstfile;
    686   GError *err = NULL;
    687 
    688   g_return_if_fail(srcfile != NULL);
    689   g_return_if_fail(dstfile != NULL);
    690 
    691   real_dstfile = jb_variable_expand("$destdir$dstfile",
    692 				    "dstfile", dstfile,
    693 				    NULL);
    694 
    695   jb_message("installing %s", real_dstfile);
    696 
    697   jb_mkdir_of_file(real_dstfile);
    698 
    699   if (! install_file_real(srcfile, real_dstfile, owner, group, mode, &err))
    700     jb_error("%s", err->message);
    701 
    702   g_free(real_dstfile);
    703 }
    704 
    705 void
    706 jb_action_install_data (const char *srcfile, const char *dstdir)
    707 {
    708   g_return_if_fail(srcfile != NULL);
    709   g_return_if_fail(dstdir != NULL);
    710 
    711   jb_action_install_file(srcfile,
    712 			 dstdir,
    713 			 jb_variable_get_string_or_null("data-owner"),
    714 			 jb_variable_get_string_or_null("data-group"),
    715 			 jb_variable_get_mode("data-mode"));
    716 }
    717 
    718 void
    719 jb_action_install_data_to_file (const char *srcfile, const char *dstfile)
    720 {
    721   g_return_if_fail(srcfile != NULL);
    722   g_return_if_fail(dstfile != NULL);
    723 
    724   jb_action_install_to_file(srcfile,
    725 			    dstfile,
    726 			    jb_variable_get_string_or_null("data-owner"),
    727 			    jb_variable_get_string_or_null("data-group"),
    728 			    jb_variable_get_mode("data-mode"));
    729 }
    730 
    731 void
    732 jb_action_install_data_list (GSList *srcfiles, const char *dstdir)
    733 {
    734   GSList *l;
    735 
    736   g_return_if_fail(dstdir != NULL);
    737 
    738   JB_LIST_FOREACH(l, srcfiles)
    739     {
    740       const char *srcfile = l->data;
    741 
    742       jb_action_install_data(srcfile, dstdir);
    743     }
    744 }
    745 
    746 void
    747 jb_action_install_program (const char *srcfile, const char *dstdir)
    748 {
    749   g_return_if_fail(srcfile != NULL);
    750   g_return_if_fail(dstdir != NULL);
    751 
    752   jb_action_install_file(srcfile,
    753 			 dstdir,
    754 			 jb_variable_get_string_or_null("program-owner"),
    755 			 jb_variable_get_string_or_null("program-group"),
    756 			 jb_variable_get_mode("program-mode"));
    757 }
    758 
    759 void
    760 jb_action_install_library (const char *srcfile, const char *dstdir)
    761 {
    762   g_return_if_fail(srcfile != NULL);
    763   g_return_if_fail(dstdir != NULL);
    764 
    765   jb_action_install_file(srcfile,
    766 			 dstdir,
    767 			 jb_variable_get_string_or_null("library-owner"),
    768 			 jb_variable_get_string_or_null("library-group"),
    769 			 jb_variable_get_mode("library-mode"));
    770 }
    771 
    772 void
    773 jb_action_rm (const char *file)
    774 {
    775   g_return_if_fail(file != NULL);
    776 
    777   if (g_file_test(file, G_FILE_TEST_EXISTS))
    778     {
    779       jb_message("removing %s", file);
    780       unlink(file);
    781     }
    782 
    783   /* if the parent directories were created by JB, also remove them */
    784 
    785   if (g_str_has_prefix(file, "jbsrc/tools/"))
    786     jb_action_rmdir("jbsrc/tools");
    787   else if (g_str_has_prefix(file, "build/"))
    788     {
    789       GSList *parentdirs = NULL;
    790       char *dir;
    791 
    792       dir = g_path_get_dirname(file);
    793 
    794       while (TRUE)
    795 	{
    796 	  parentdirs = g_slist_append(parentdirs, dir);
    797 
    798 	  if (! strcmp(dir, "build"))
    799 	    break;
    800 
    801 	  dir = g_path_get_dirname(dir);
    802 	  g_assert(strcmp(dir, "/"));
    803 	}
    804 
    805       jb_action_rmdir_list(parentdirs);
    806 
    807       jb_g_slist_free_deep(parentdirs);
    808     }
    809 }
    810 
    811 void
    812 jb_action_rm_array (char **files, int len)
    813 {
    814   int i;
    815 
    816   for (i = 0; i < len; i++)
    817     jb_action_rm(files[i]);
    818 }
    819 
    820 void
    821 jb_action_rm_list (GSList *files)
    822 {
    823   GSList *l;
    824 
    825   JB_LIST_FOREACH(l, files)
    826     jb_action_rm(l->data);
    827 }
    828 
    829 static gboolean
    830 dir_is_empty (const char *dir)
    831 {
    832   GDir *gdir;
    833   gboolean is_empty;
    834 
    835   gdir = g_dir_open(dir, 0, NULL);
    836   if (gdir == NULL)
    837     return TRUE;
    838 
    839   is_empty = g_dir_read_name(gdir) == NULL;
    840 
    841   g_dir_close(gdir);
    842 
    843   return is_empty;
    844 }
    845 
    846 void
    847 jb_action_rmdir (const char *dir)
    848 {
    849   g_return_if_fail(dir != NULL);
    850 
    851   if (g_file_test(dir, G_FILE_TEST_EXISTS) && dir_is_empty(dir))
    852     {
    853       jb_message("removing directory %s", dir);
    854       rmdir(dir);
    855     }
    856 }
    857 
    858 void
    859 jb_action_rmdir_list (GSList *dirs)
    860 {
    861   GSList *l;
    862 
    863   JB_LIST_FOREACH(l, dirs)
    864     jb_action_rmdir(l->data);
    865 }
    866 
    867 static gboolean
    868 add_to_hash_set (JBStringHashSet **hash_set, const char *value)
    869 {
    870   if (*hash_set == NULL)
    871     *hash_set = jb_string_hash_set_new();
    872 
    873   if (! jb_string_hash_set_add(*hash_set, value))
    874     return FALSE;
    875 
    876   return TRUE;
    877 }
    878 
    879 void
    880 jb_action_add_to_dist (const char *file)
    881 {
    882   static JBStringHashSet *distfiles = NULL;
    883   char *dir;
    884   char *distdir;
    885 
    886   g_return_if_fail(file != NULL);
    887   g_return_if_fail(*file != '\0');
    888 
    889   if (! add_to_hash_set(&distfiles, file))
    890     return;
    891 
    892   jb_message("adding %s to dist", file);
    893 
    894   dir = g_path_get_dirname(file);
    895   distdir = g_strdup_printf("%s/%s", jb_action_get_distdir(), dir);
    896   g_free(dir);
    897 
    898   jb_mkdir(distdir);
    899 
    900   jb_action_exec("cp -p $file $distdir",
    901 		 "file", file,
    902 		 "distdir", distdir,
    903 		 NULL);
    904 
    905   g_free(distdir);
    906 }
    907 
    908 void
    909 jb_action_add_to_dist_list (GSList *files)
    910 {
    911   GSList *l;
    912 
    913   JB_LIST_FOREACH(l, files)
    914     {
    915       const char *file = l->data;
    916       jb_action_add_to_dist(file);
    917     }
    918 }
    919 
    920 void
    921 jb_action_add_to_dist_string_list (const char *files)
    922 {
    923   int i;
    924   char **array;
    925 
    926   g_return_if_fail(files != NULL);
    927 
    928   array = g_strsplit(files, " ", 0);
    929 
    930   for (i = 0; array[i] != NULL; i++)
    931     {
    932       const char *file = array[i];
    933 
    934       /* the files array can contain extra spaces */
    935       if (*file != '\0')
    936 	jb_action_add_to_dist(array[i]);
    937     }
    938 
    939   g_strfreev(array);
    940 }
    941 
    942 const char *
    943 jb_action_get_distdir (void)
    944 {
    945   static char *distdir = NULL;
    946 
    947   if (distdir == NULL)
    948     distdir = jb_variable_expand("build/${package}-$version", NULL);
    949 
    950   return distdir;
    951 }