
/* usage: join_xattr srcdir dstdir
 *        join_xattr --files-from file srcdir dstdir
 * 
 * this "undoes" split_xattr, setting xattrs in srcdir
 * based on the xattr container appearing files in dstdir.
 * dstdir should definitely exist prior to invocation.
 * 
 * with the --files-from flag, the only files in srcdir that
 * are examined are those listed in file.
 *
 * Returns -1 if errors detected, and 0 otherwise.
 *
 */

#include "util.h"
#include "xattr_util.h"



static int return_value = 0;
static int source_name_len = 0;
static char *destination_name = 0;

static char dblname[MAXLEN];

void process_xattrs(const char *itemname, const struct stat *itemstat, 
                    const char *dirname, const char *basename)
{
   int has_d, has_x;
   struct stat dblstat;


   if (snprintf(dblname, MAXLEN, "%s%s/%s%s", 
      destination_name, 
      dirname + source_name_len, 
      DBL_PREFIX,
      basename) >= MAXLEN) overflow();

    has_d = ( !lstat(dblname, &dblstat) && S_ISREG(dblstat.st_mode) );
    has_x = has_xattr(itemname) || has_locks(itemstat);

    if (has_d || has_x) {
       char *dn = (has_d ? dblname : 0);

       if (join_xattr(itemname, itemstat, dn)) {

          fprintf(stderr, "join_xattr: error processing %s\n", itemname);
          return_value = -1;

       }

   }
}

void dirwalk(const char *dirname, const struct stat *dirstat, int walk_state)
{
   char itemname[MAXLEN];
   DIR *dirlist;
   struct dirent *diritem; 
   struct stat itemstat;
   int walk_state1;


   dirlist = opendir(dirname);

   if (!dirlist) {
      fprintf(stderr, "join_xattr: internal error -- opendir failed\n");
      return_value = -1;
      return;
   }

   while ( (diritem = readdir(dirlist)) ) {

      if (strcmp(diritem->d_name, ".") == 0 
          || strcmp(diritem->d_name, "..") == 0)
         continue;

      if (snprintf(itemname, MAXLEN, "%s/%s", 
          dirname, diritem->d_name) >= MAXLEN) overflow();

      walk_state1 = walk_state;

      if (walk_state1 == 0) {
	    walk_state1 = lookup_name(itemname + source_name_len + 1);
	    if (walk_state1 == -1) continue; /* pruning */
      }

      if (lstat(itemname, &itemstat)) {
         fprintf(stderr, "join_xattr: lstat failed on %s\n", itemname);
         return_value = -1;
         continue;
      }


      if (!(S_ISDIR(itemstat.st_mode) || S_ISREG_OR_LNK(itemstat.st_mode)))
         continue;

      if (walk_state1 == 1 && S_ISREG_OR_LNK(itemstat.st_mode)) {
         process_xattrs(itemname, &itemstat, dirname, diritem->d_name);
      }

      if (S_ISDIR(itemstat.st_mode)) {
	 dirwalk(itemname, &itemstat, walk_state1);
      }

   }

   closedir(dirlist);

   if (walk_state == 1)
      process_xattrs(dirname, dirstat, dirname, ".");

}

void usage()
{
   fprintf(stderr, "usage: join_xattr [ --files-from file ] srcdir dstdir\n");
}


int main(int argc, char **argv)
{
   char *fname, *srcname, *dstname;
   struct stat srcstat, dststat;
   int srcname_len, dstname_len;
   int walk_state;

   fname = 0;
   
   
   if (argc == 5) {
      if (strcmp(argv[1], "--files-from") != 0) {
         usage();
         return -1;
      }
      fname = argv[2];
      srcname = argv[3];
      dstname = argv[4];
   }
   else if (argc == 3) {
      srcname = argv[1];
      dstname = argv[2];
   }
   else {
      usage();
      return -1;
   }

   if (fname) {
      collect_names(fname);
      walk_state = 0;
   }
   else {
      walk_state = 1;
   }

   srcname_len = strlen(srcname);
   if (srcname[srcname_len-1] == '/') {
      srcname[srcname_len-1] = '\0';
      srcname_len--;
   }

   dstname_len = strlen(dstname);
   if (dstname[dstname_len-1] == '/')
      dstname[dstname_len-1] = '\0';

   if (lstat(srcname, &srcstat) || !S_ISDIR(srcstat.st_mode)) {
      usage();
      return -1;
   }

   if (lstat(dstname, &dststat) || !S_ISDIR(dststat.st_mode)) {
      usage();
      return -1;
   }

   source_name_len = srcname_len;

   destination_name = dstname;

   dirwalk(srcname, &srcstat, walk_state);

   return return_value;

}
