#include "xcp.h"

extern int prefs, errno;

extern void inform(), add_file_to_dir(), list_remove_file(), working();
extern XmString make_correct();
extern file_list_struct *map_name_to_file();
extern void remove_item_dir(), remove_item(), list_remove_directory();

Boolean remove_dir(), move_dir_sub();
int copy_dir(), move_dir();
Boolean copy_file(), move_file();
void get_filename(), get_path();

Boolean remove_dir(path, name)
char *path, *name;
{
  DIR *directory;
  struct dirent *current;
  struct stat buf;
  char current_path[MAXPATHLEN+1], orig_path[MAXPATHLEN+1];
  file_list_struct *temp;

  chdir(path);
  getwd(orig_path);
  chdir(name);
  if ((directory=opendir((char *)getwd(current_path))) == NULL)
    perror("opendir");
  while(current=readdir(directory)) 
    if (current->d_ino)
      if ((strcmp(current->d_name, ".")) && (strcmp(current->d_name, ".."))) {
	lstat(current->d_name, &buf);
	if ((buf.st_mode & S_IFMT) == S_IFDIR){
	  if (remove_dir(current_path, current->d_name))
	    remove_item_dir(current_path);
	}
	else
	  if (unlink(current->d_name))
	    if (temp=map_name_to_file(current_path, current->d_name)){
	      remove_item(current_path, temp);	
	      working();
	    }
      }
  closedir(directory);
  chdir(orig_path);
  if (rmdir(name))
    return(False);
  else
    return(True);
}

Boolean copy_file(s_file, d_file)
char *s_file, *d_file;
/* absolute paths PLEASE  */
{
  int source, destination;
  char string_buf[MAXPATHLEN + 40], tmp[MAXPATHLEN];
  char file_buffer[BUFFERSIZE];
  int c_read;
  file_list_struct *item;
  struct stat buf;

#ifdef DEBUG
  printf("source - %s\ndestination - %s\n",s_file, d_file);
#endif
  if ((source = open(s_file, O_RDONLY)) == -1){
    sprintf(string_buf, "Can't read from file : %s\n", s_file);
    inform(string_buf);
    return(False);
  }
  stat(s_file,&buf);
  if ((destination = open(d_file, O_CREAT|O_WRONLY|O_EXCL, buf.st_mode)) == -1){
    if ((errno == EEXIST) && (prefs & OVERWRITE))
      if ((destination = open (d_file, O_CREAT|O_WRONLY|O_TRUNC, buf.st_mode)) == -1){
	sprintf(string_buf, "Can't write to file : %s\n", d_file);
	inform(string_buf);
	close(source);
	return(False);
      }
  }    
  while ((c_read = read(source, file_buffer, BUFFERSIZE)) > 0)  /* restarts on signals */
    if (write(destination, file_buffer, c_read) == -1){
      sprintf(string_buf,"filesystem problems - skipping file : %s\n", d_file);
      inform(string_buf);
      close (source);
      close (destination);
      return(False);
    }
  working();
  if (c_read){
    close(source);
    close(destination);
    return(False);
  }
  else{
    if ((item = (file_list_struct *)malloc(sizeof(file_list_struct))) == NULL){
      inform("memory problems\n");
      close(source);
      close(destination);
      return(False);
    }	
    item->next_file=NULL;
    get_filename(d_file, tmp);
    strcpy(item->filename, tmp);
    item->fullname = (char *)malloc(sizeof(char) * (strlen(d_file) +1));
    strcpy(item->fullname, d_file);
    item->link_info = IS_FILE;
    lstat(d_file, &buf);
    memcpy(&item->stats, &buf, sizeof(struct stat));
    item->name_str = make_correct(item, 0);
    item->fullname_str = make_correct(item, 1);
    get_path(d_file, tmp);
    add_file_to_dir(tmp,item);
  }
  close(source);
  close(destination);
  return(True);
}


int copy_dir(s_dir, d_dir)
     char *s_dir, *d_dir;
     /* copies s_dir to d_dir recursively (absolute pathnames please!!) */
{  
  DIR *directory;
  struct dirent *current;
  struct stat buf;
  char string_buf[MAXPATHLEN + 50], s_buf[MAXPATHLEN], d_buf[MAXPATHLEN], tmp[MAXPATHLEN];
  int file_c=True, temp;
  file_list_struct *item;
  
  stat(s_dir, &buf);
  if ((directory=opendir(s_dir)) == NULL){
    sprintf(string_buf, "can't open directory : %s\n", s_dir);
    inform(string_buf);
    return(NONE);
  }
  if ((!(mkdir(d_dir, buf.st_mode))) || (errno == EEXIST)){
    if (errno != EEXIST) {
      if ((item = (file_list_struct *)malloc(sizeof(file_list_struct))) == NULL){
	inform("memory problems\n");
	closedir(directory);
	return(False);
      }	
      item->next_file=NULL;
      get_filename(d_dir, tmp);
      strcpy(item->filename, tmp);
      item->fullname = (char *)malloc(sizeof(char) * (strlen(d_dir) +1));
      strcpy(item->fullname, d_dir);
      item->link_info = IS_DIRECTORY;
      lstat(d_dir, &buf);
      memcpy(&item->stats, &buf, sizeof(struct stat));
      item->name_str = make_correct(item, 0);
      item->fullname_str = make_correct(item, 1);
      get_path(d_dir, tmp);
      add_file_to_dir(tmp,item);
    }
    file_c=COMPLETE;
    while(current=readdir(directory)) 
      if (current->d_ino)
	if ((strcmp(current->d_name, ".")) && (strcmp(current->d_name, ".."))) {
	  sprintf(d_buf, "%s/%s", d_dir, current->d_name);
	  sprintf(s_buf, "%s/%s", s_dir, current->d_name);
	  stat(s_buf, &buf);
	  if ((buf.st_mode & S_IFMT) == S_IFDIR){
	    if ((temp=copy_dir(s_buf, d_buf)==NONE) || (temp==PART))
	      file_c=PART;
	  }
	  else
	    if (!copy_file(s_buf, d_buf))
	      file_c=COMPLETE;
	}
    closedir(directory);
    return(file_c);
  }
  else{
    sprintf(string_buf, "Can't create directory : %s\n", d_dir);
    inform(string_buf);
    closedir(directory);
    return(NONE);
  }
}


Boolean move_file(s_file, d_file)
     char *s_file, *d_file;
     /* absolute paths PLEASE  */
{
  char tmp[MAXPATHLEN], d_temp[MAXPATHLEN], string_buf[MAXPATHLEN + 30];
  int len, cant;
  file_list_struct *item;
  struct stat buf;

#ifdef DEBUG
  printf("source - %s\ndestination - %s\n",s_file, d_file);
#endif
  working();
  if ((cant = ((lstat(d_file, &buf) == -1) && (errno == ENOENT))) ||
      (prefs & OVERWRITE)) {
    if (rename(s_file, d_file) == 0){
      if ((item = (file_list_struct *)malloc(sizeof(file_list_struct))) == NULL){
	inform("memory problems\n");
	return(False);
      }	
      item->next_file=NULL;
      get_filename(d_file, tmp);
      strcpy(item->filename, tmp);
      item->fullname = (char *)malloc(sizeof(char) * (strlen(d_file) +1));
      strcpy(item->fullname, d_file);
      item->link_info = IS_FILE;
      lstat(d_file, &buf);
      memcpy(&item->stats, &buf, sizeof(struct stat));
      item->name_str = make_correct(item, 0);
      item->fullname_str = make_correct(item, 1);
      get_path(d_file, tmp);
      add_file_to_dir(tmp,item);
      list_remove_file(s_file);
      return(True);
    }
    else
      if (cant){
	lstat(s_file, &buf);
	if ((buf.st_mode & S_IFMT) == S_IFLNK){
	  len = readlink(s_file, d_temp, MAXPATHLEN);
	  d_temp[len] = '\0';
	  if ((symlink(d_temp, d_file)) == 0)
	    if (!unlink(s_file)){
	      if ((item = (file_list_struct *)malloc(sizeof(file_list_struct))) == NULL){
		inform("memory problems\n");
		return(False);
	      }
	      item->next_file=NULL;
	      get_filename(d_file, tmp);
	      strcpy(item->filename, tmp);
	      item->fullname = (char *)malloc(sizeof(char) * (strlen(d_file) +1));
	      strcpy(item->fullname, d_file);
	      item->link_info = IS_LINK | IS_FILE;
	      lstat(d_file, &buf);
	      memcpy(&item->stats, &buf, sizeof(struct stat));
	      item->name_str = make_correct(item, 0);
	      item->fullname_str = make_correct(item, 1);
	      get_path(d_file, tmp);
	      add_file_to_dir(tmp, item);
	      if (unlink (s_file))
		list_remove_file(s_file);
	      else{
		sprintf(string_buf, "Can't exactly move %s - part has been copied\n", s_file);
		strcat(string_buf, "It is reccomended that you press Sync\n");
		inform(string_buf);
	      }
	      return(True);
	    }
	    else{
	      unlink(d_temp);
	      inform("can't move file\n");
	      return(False);
	    }	  
	  else{
	    inform("having trouble creating the new symlink\n");
	    return(False);
	  }
	}
	else
	  if (copy_file(s_file, d_file))
	    if (!unlink(s_file)){
	      if ((item = (file_list_struct *)malloc(sizeof(file_list_struct))) == NULL){
		inform("memory problems\n");
		return(False);
	      }	
	      item->next_file=NULL;
	      get_filename(d_file, tmp);
	      strcpy(item->filename, tmp);
	      item->fullname = (char *)malloc(sizeof(char) * (strlen(d_file) +1));
	      strcpy(item->fullname, d_file);
	      item->link_info = IS_FILE;
	      lstat(d_file, &buf);
	      memcpy(&item->stats, &buf, sizeof(struct stat));
	      item->name_str = make_correct(item, 0);
	      item->fullname_str = make_correct(item, 1);
	      get_path(d_file, tmp);
	      list_remove_file(s_file);
	      return(True);
	    }
	    else{
	      unlink(d_file);
	      inform("can't move file\n");
	      return(False);
	    }
      }
  }
}	

int move_dir(s_dir, d_dir)
char *s_dir, *d_dir;
{
  char tmp[MAXPATHLEN], d_temp[MAXPATHLEN], string_buf[MAXPATHLEN + 20];
  int  cant, len, a, buf_fail;
  file_list_struct *item;
  struct stat buf;
  
  if ((prefs & OVERWRITE) || 
      (cant = ((lstat(d_dir, &buf)) == -1) && (errno == ENOENT))){
    if (rename(s_dir, d_dir) == 0){
      if ((item = (file_list_struct *)malloc(sizeof(file_list_struct))) == NULL){
	inform("memory problems\n");
	return(False);
      }	
      item->next_file=NULL;
      get_filename(d_dir, tmp);
      strcpy(item->filename, tmp);
      item->fullname = (char *)malloc(sizeof(char) * (strlen(d_dir) +1));
      strcpy(item->fullname, d_dir);
      item->link_info = IS_DIRECTORY;
      lstat(d_dir, &buf);
      memcpy(&item->stats, &buf, sizeof(struct stat));
      item->name_str = make_correct(item, 0);
      item->fullname_str = make_correct(item, 1);
      get_path(d_dir, tmp);
      add_file_to_dir(tmp,item);
      list_remove_directory(s_dir);
      list_remove_file(s_dir);
      working();
      return(COMPLETE);
    }
    else{
      lstat(s_dir, &buf);
      if (cant)
	if ((buf.st_mode & S_IFMT) == S_IFLNK){
	  len = readlink(s_dir, d_temp, MAXPATHLEN);
	  d_temp[len] = '\0';
	  if ((symlink(d_temp, d_dir)) == 0)
	    if (!rmdir(s_dir)){
	      item->next_file=NULL;
		get_filename(d_dir, tmp);
	      strcpy(item->filename, tmp);
	      item->fullname = (char *)malloc(sizeof(char) * (strlen(d_dir) +1));
	      strcpy(item->fullname, d_dir);
	      item->link_info = IS_LINK | IS_DIRECTORY;
	      lstat(d_dir, &buf);
		memcpy(&item->stats, &buf, sizeof(struct stat));
	      item->name_str = make_correct(item, 0);
	      item->fullname_str = make_correct(item, 1);
		get_path(d_dir, tmp);
	      add_file_to_dir(tmp,item);
	      list_remove_file(s_dir);
	      return(COMPLETE);
	    }
	    else{
	      unlink(d_temp);
	      inform("can't move directory\n");
	      return(NONE);
	    }  
	  else{
	    inform("having trouble creating the new symlink\n");
	    return(NONE);
	  }
	}
	else
	  if (a = move_dir_sub(s_dir, d_dir))
	    if (a==COMPLETE){
	      if (!rmdir(s_dir)){
		list_remove_file(s_dir);
		return(COMPLETE);
	      }
	      else{
		sprintf(string_buf, "can't erase %s\n", s_dir);
		inform(string_buf);
	      }
	    }
	    else
	      return(PART);
	  else{
	    rmdir(d_dir);
	    inform("can't move file\n");
	    return(NONE);
	  }
    }
  }
}	


Boolean move_dir_sub(s_dir, d_dir)
char *s_dir, *d_dir;
{
  DIR *directory;
  struct dirent *current;
  struct stat buf;
  char string_buf[MAXPATHLEN + 50], s_buf[MAXPATHLEN], d_buf[MAXPATHLEN], tmp[MAXNAMLEN];
  int temp, file_c;
  file_list_struct *item;
  
  working();
  if ((directory=opendir(s_dir)) == NULL){
    sprintf(string_buf, "can't open directory : %s\n", s_dir);
    inform(string_buf);
    return(NONE);
  }
  if ((prefs & OVERWRITE) || 
      ((lstat(d_dir, &buf) == -1) && (errno == ENOENT))){
    if ((lstat(s_dir, &buf)) == -1)
      buf.st_mode = 022;   /*  just use the default for the user, none for others */
    if ((!(mkdir(d_dir, buf.st_mode))) || (errno == EEXIST)){
      if (errno != EEXIST) {
	if ((item = (file_list_struct *)malloc(sizeof(file_list_struct))) == NULL){
	  inform("memory problems\n");
	  closedir(directory);
	  return(False);
	}	
	item->next_file=NULL;
	get_filename(d_dir, tmp);
	strcpy(item->filename, tmp);
	item->fullname = (char *)malloc(sizeof(char) * (strlen(d_dir) +1));
	strcpy(item->fullname, d_dir);
	item->link_info = IS_DIRECTORY;
	lstat(d_dir, &buf);
	memcpy(&item->stats, &buf, sizeof(struct stat));
	item->name_str = make_correct(item, 0);
	item->fullname_str = make_correct(item, 1);
	get_path(d_dir, tmp);
	add_file_to_dir(tmp,item);
      }
      file_c=COMPLETE;
      while(current=readdir(directory)) 
	if (current->d_ino)
	  if ((strcmp(current->d_name, ".")) && (strcmp(current->d_name, ".."))) {
	    sprintf(d_buf, "%s/%s", d_dir, current->d_name);
	    sprintf(s_buf, "%s/%s", s_dir, current->d_name);
	    lstat(s_buf, &buf);
	    if ((buf.st_mode & S_IFMT) == S_IFDIR){
	      if ((temp=move_dir(s_buf, d_buf)==NONE) || (temp==PART))
		file_c=PART;
	    }
	    else
	      if (!move_file(s_buf, d_buf))
		file_c=COMPLETE;
	  }
      closedir(directory);
      return(file_c);
    }
    else{
      sprintf(string_buf, "Can't create directory : %s\n", d_dir);
      inform(string_buf);
      closedir(directory);
      return(NONE);
    }
  }
}



void get_filename(name, result)
char *name, *result;
{
  strcpy(result, (strrchr(name, '/') + (sizeof(char))));
}

void get_path(name, result)
char *name, *result;
{
  int len;
  len = (strrchr(name,'/') - name);
  strncpy(result, name, len);
  result[len] = '\0';
}
