/***************************************************************************/
/***************************************************************************/
/*                                                                         */
/*   (c) 1995-1999.  The Regents of the University of California.  All     */
/*   rights reserved.                                                      */
/*                                                                         */
/*   This work was produced at the University of California, Lawrence      */
/*   Livermore National Laboratory (UC LLNL) under contract no.            */
/*   W-7405-ENG-48 (Contract 48) between the U.S. Department of Energy     */
/*   (DOE) and The Regents of the University of California (University)    */
/*   for the operation of UC LLNL.  Copyright is reserved to the           */
/*   University for purposes of controlled dissemination,                  */
/*   commercialization through formal licensing, or other disposition      */
/*   under terms of Contract 48; DOE policies, regulations and orders;     */
/*   and U.S. statutes.  The rights of the Federal Government are          */
/*   reserved under Contract 48 subject to the restrictions agreed upon    */
/*   by the DOE and University.                                            */
/*                                                                         */
/*                                                                         */
/*                              DISCLAIMER                                 */
/*                                                                         */
/*   This software was prepared as an account of work sponsored by an      */
/*   agency of the United States Government.  Neither the United States    */
/*   Government nor the University of California nor any of their          */
/*   employees, makes any warranty, express or implied, or assumes any     */
/*   liability or responsibility for the accuracy, completeness, or        */
/*   usefulness of any information, apparatus, product, or process         */
/*   disclosed, or represents that its specific commercial products,       */
/*   process, or service by trade name, trademark, manufacturer, or        */
/*   otherwise, does not necessarily constitute or imply its               */
/*   endorsement, recommendation, or favoring by the United States         */
/*   Government or the University of California. The views and opinions    */
/*   of the authors expressed herein do not necessarily state or reflect   */
/*   those of the United States Government or the University of            */
/*   California, and shall not be used for advertising or product          */
/*   endorsement purposes.                                                 */
/*                                                                         */
/*   Permission to use, copy, modify and distribute this software and its  */
/*   documentation for any non-commercial purpose, without fee, is         */
/*   hereby granted, provided that the above copyright notice and this     */
/*   permission notice appear in all copies of the software and            */
/*   supporting documentation, and that all UC LLNL identification in      */
/*   the user interface remain unchanged.  The title to copyright LLNL     */
/*   XDIR shall at all times remain with The Regents of the University     */
/*   of California and users agree to preserve same. Users seeking the     */
/*   right to make derivative works with LLNL XDIR for commercial          */
/*   purposes may obtain a license from the Lawrence Livermore National    */
/*   Laboratory's Technology Transfer Office, P.O. Box 808, L-795,         */
/*   Livermore, CA 94550.                                                  */
/*                                                                         */
/***************************************************************************/
/***************************************************************************/

#include <Xm/ToggleB.h>
#include "xdir.h"
#include "xfer.h"
#include "str.h"

struct xfer_ctrl_block xc;

static char *not_implemented_msg = "Copying entry(s) from a remote host to itself not implemented.\n\nConsider moving entry(s) instead (by holding down the Control\nkey when the mouse button is released over the drop site).";

char *xfer_question = "You are attempting to copy a directory, but\nthe general preference COPY MODE is set to\nNONRECURSIVE.\n\nCopy directories anyway?";

char *recursion_disabled_msg = "Recursion disabled.  Can't transfer directory %s";

char *merge_paths();
char *links_to_path();
char *entry_to_full_path();
char *entry_to_rel_path();
char **path_to_links();
void release_xfer_subforest();
int cb_examine_src_forest();

extern int inquire_on_copy;
extern struct st_host_info hinfo[];
extern XtAppContext app;
extern int rcopy_mode;
extern int xfer_mode;


/*
 * init_copy - Initialize file transfer of selected files in directory
 *             window "src_dirwin" to directory window "snk_dirwin".  If
 *             "einfo" is non-NULL then transfer the files into the
 *             directory represented by "einfo".
 */
void
init_copy(src_dirwin, snk_dirwin, einfo)
struct dirwin_st *src_dirwin;
struct dirwin_st *snk_dirwin;
struct entry_info *einfo;
{
	int src_host = src_dirwin->host;
	int snk_host = snk_dirwin->host;
	int operation = rcopy_mode?RCOPY:COPY;
	int asked_user = False;
	int retval;
	int indx;
	int i;
	int j;
	int nlinks;
	char *temp_snk_dir;
	char *snk_dir;
	struct forest_node **head;
	char *ptr;
	char *rel_path;
	char **links;
	char **link_ptr;
	struct forest_node *forest;
	struct forest_node *p;
	struct forest_node *parent;
	char msg[MAXPATHLEN+50];

	/* Start operation */
	if (!start_op(True))
		return;

	/* Clear error flag */
	raise_okflag();

	/* Is operation implemented */
	if ((src_host != LOCAL) && (src_host == snk_host)) {
		warn(not_implemented_msg, snk_dirwin->w_shell);
		end_op();
		return;
	}

	/* Ask the user if okay to copy */
	if (inquire_on_copy) {
		if (!verify_selection(src_dirwin, snk_dirwin,
				"Do you really want to copy these entries?")) {
			end_op();
			return;
		}
	}

	/* Make operation interruptable */
	show_stop_button(snk_dirwin);

    /* This might take some time */
    use_busy_cursor();

	/* Make sure remote connections are still good */
	if (src_host != LOCAL) {
		retval = check_connection(src_host, snk_dirwin);
		if (retval < 0) {
			switch (retval) {
			case -6:
				record_abort("Copy Files");
				break;
			case -1:
				record_and_alert("Unable to transfer file(s).",
					snk_dirwin->w_shell);
			}
			restore_prev_cursor();
			hide_stop_button();
			end_op();
			return;
		}
	}
	if (snk_host != LOCAL) {
		retval = check_connection(snk_host, snk_dirwin);
		if (retval < 0) {
			switch (retval) {
			case -6:
				record_abort("Copy Files");
				break;
			case -1:
				record_and_alert("Unable to transfer file(s).",
					snk_dirwin->w_shell);
			}
			restore_prev_cursor();
			hide_stop_button();
			end_op();
			return;
		}
	}

	/* Determine true sink directory path */
	if (einfo)
		temp_snk_dir = entry_to_full_path(einfo);
	else 
		temp_snk_dir = XtNewString(snk_dirwin->dirname);
    retval = determine_true_path(snk_dirwin->host, temp_snk_dir, True, False,
		False, &snk_dir);
	XtFree(temp_snk_dir);
	if (retval < 0) {
		switch (retval) {
		case -1:
	        record_and_alert("Unable to transfer file(s).",snk_dirwin->w_shell);
			break;
		case -3:
			restore_lost_connection(snk_dirwin->host, snk_dirwin);
			break;
		case -6:
			record_abort("Copy Files");
		}
		restore_prev_cursor();
		hide_stop_button();
		end_op();
		return;
	}

    /* Form list of selected entries */
	forest = NULL;
	for (i=src_dirwin->nentries-1; i>=0; i--) {
		if (src_dirwin->entries[i].state != SELECTED)
			continue;
		if (src_dirwin->entries[i].type == DIRECTORY_TYPE) {
			if ((operation == COPY) && !asked_user) {
				beep();
				if (question_dialog(xfer_question, snk_dirwin->w_shell))
					operation = RCOPY;
				asked_user = True;
			}
			if (operation == COPY) {
				rel_path = entry_to_rel_path(&src_dirwin->entries[i]);
				sprintf(msg, recursion_disabled_msg, rel_path);
				XtFree(rel_path);
				record_warning(msg);
				continue;
			}
		}
		rel_path = entry_to_rel_path(&src_dirwin->entries[i]);
		links = path_to_links(hinfo[src_dirwin->host].system, rel_path);
		XtFree(rel_path);
		if (hinfo[src_dirwin->host].system == SYS_VMS) {
			nlinks = 0;
			link_ptr = links;
			while (*link_ptr++)
				nlinks++;
			if (src_dirwin->entries[i].type == UNKNOWN_TYPE) {
				if (strstr(links[nlinks-1], ".dir"))
					src_dirwin->entries[i].type = DIRECTORY_TYPE;
				else
					src_dirwin->entries[i].type = FILE_TYPE;
			}
			for (j=0; j<nlinks; j++)   /* Remove ".dir;n" */
				if ((ptr = strstr(links[j], ".dir")))
					*ptr = '\0';
		}
		indx = 0;
		p = forest;
		head = &forest;
		parent = NULL;
		while (links[indx])
			if (p && !strcmp(p->entry, links[indx])) {
				if (p->position == AT_START_NODE)
					break;
				if (!links[indx+1]) {
					p->position = AT_START_NODE;
					release_xfer_subforest(p->first_child);
					p->first_child = NULL;
					break;
				}
				parent = p;
				head = &p->first_child;
				p = p->first_child;
				indx++;
			} else if (p && p->next)
				p = p->next;
			else {
				p = XtNew(struct forest_node);
				p->entry = XtNewString(links[indx]);
				p->first_child = NULL;
				p->parent = parent;
				p->status = NOT_PROCESSED;
				p->next = *head;
				if (links[indx+1]) {
					p->position = ABOVE_START_NODE;
					p->type = TYPE_DIR;
				} else {
					p->position = AT_START_NODE;
					switch (src_dirwin->entries[i].type) {
					case UNKNOWN_TYPE:
					case LINK_TYPE:
					case SOCKET_TYPE:
						p->type = TYPE_UNKNOWN;
						break;
					case DIRECTORY_TYPE:
						p->type = TYPE_DIR;
						break;
					case FILE_TYPE:
					case EXECUTABLE_TYPE:
						p->type = TYPE_FILE;
					}
				}
				parent = p;
				*head = p;
				head = &p->first_child;
				p = p->first_child;
				indx++;
			}
		release_path_links(links);
	}

	/* Set up transfer control block */
	xc.operation = operation;
	xc.mode = xfer_mode;
	xc.level = 0;
	xc.state = 0;
	xc.nretries = 0;
	xc.error = False;
	xc.asked_user = asked_user;
	xc.src_dirwin = src_dirwin;
	xc.snk_dirwin = snk_dirwin;
	xc.src_host_system = hinfo[src_dirwin->host].system;
	xc.src_host_server = hinfo[src_dirwin->host].server;
	xc.snk_host_system = hinfo[snk_dirwin->host].system;
	xc.src_dirname = XtNewString(src_dirwin->dirname);
	xc.snk_dirname = snk_dir;
	xc.forest = forest;
	xc.s0ptr = forest;
	xc.s0head = &xc.forest;
	xc.node[0] = forest;
	xc.nstages = 0;
	xc.more_files = True;
	if (src_dirwin->host == LOCAL)
		xc.src_host_type = XLOCAL;
	else
		xc.src_host_type = XREMOTE;
	if (snk_dirwin->host == LOCAL)
		xc.snk_host_type = XLOCAL;
	else
		xc.snk_host_type = XREMOTE;

	/* Transfer files */
	show_xfermon(snk_dirwin);
	set_xfermon_status(STATUS_PASS1);
	set_xfermon_name_label("Directory:");
	set_xfermon_progress((long)-1, (long)-1, (double)0.0);
	set_xfermon_file_count(0, -1);
	XtAppAddWorkProc(app, (XtWorkProc)cb_examine_src_forest, NULL);
}


/*
 * test_for_dir - Returns 0 if full path is a directory, -1 if path is not a
 *                directory, -3 if connection is lost, and -6 if stop button
 *                pushed.
 */
test_for_dir(host, path)
int host;
char *path;
{
	if (host == LOCAL) {
		if (local_cd(path, False) == 0)
			return 0;
	} else
		switch (remote_cd(host, path, False)) {
		case -3:
			return -3;
		case -6:
			return -6;
		case 0:
			return 0;
		}
	return -1;
}


/*
 * rel_path_of_xfer_node - Given an xfer node, return the corresponding
 *                         entry's relative path in native format.
 *                         "which" can be either SOURCE or SINK.
 */
char *
rel_path_of_xfer_node(system, node, which)
int system;
struct forest_node *node;
int which;
{
	struct forest_node *ptr;
	char **links;
	char *rel_path;
	int i;
	int nlinks;

	/* Determine number of links */
	nlinks = 0;
	ptr = node;
	while (ptr) {
		nlinks++;
		if ((which == SINK) && (ptr->position == AT_START_NODE))
			break;
		ptr = ptr->parent;
	}
	links = (char **)XtMalloc(sizeof(char *)*(nlinks+1));
	ptr = node;
	for (i=nlinks-1; i>=0; i--) {
		if (system == SYS_VMS) {
			switch (ptr->type) {
			case TYPE_DIR:
				links[i] = XtMalloc(strlen(ptr->entry)+5);
				strcpy(links[i], ptr->entry);
				strcat(links[i], ".dir");
				break;
			case TYPE_FILE:
				links[i] = XtNewString(ptr->entry);
				break;
			case TYPE_UNKNOWN:
				fatal_error("Bug in rel_path_of_xfer_node()");
			}
		} else
			links[i] = XtNewString(ptr->entry);
		ptr = ptr->parent;
	}
	links[nlinks] = NULL;		
	rel_path = links_to_path(system, links, nlinks);
	release_path_links(links);
	return rel_path;
}


/*
 * src_paths_of_xfer_node - Given an xfer node, return the corresponding
 *                          entry's source relative path and source full
 *                          path in native format.  "rel_path" and
 *                          "full_path" may be NULL, if that value is not
 *                          to be returned.  Use XtFree() to release the
 *                          returned memory.
 */
void
src_paths_of_xfer_node(node, rel_path, full_path)
struct forest_node *node;
char **rel_path;
char **full_path;
{
	char *tmp_rel_path;
	char *tmp_full_path;

	/* Create paths */
	tmp_rel_path = rel_path_of_xfer_node(xc.src_host_system, node, SOURCE);
	tmp_full_path = merge_paths(xc.src_host_system, xc.src_dirname,
		tmp_rel_path);

	/* Return relative path? */
	if (rel_path)
		*rel_path = tmp_rel_path;
	else
		XtFree(tmp_rel_path);

	/* Return full path? */
	if (full_path)
		*full_path = tmp_full_path;
	else
		XtFree(tmp_full_path);
}


/*
 * snk_paths_of_xfer_node - Given an xfer node, return the corresponding
 *                          entry's sink relative path and sink full
 *                          path in native format.  "rel_path" and
 *                          "full_path" may be NULL, if that value is not
 *                          to be returned.  Use XtFree() to release the
 *                          returned memory.
 */
void
snk_paths_of_xfer_node(node, rel_path, full_path)
struct forest_node *node;
char **rel_path;
char **full_path;
{
	char *tmp_rel_path;
	char *tmp_full_path;

	/* Create paths */
	tmp_rel_path = rel_path_of_xfer_node(xc.snk_host_system, node, SINK);
	tmp_full_path = merge_paths(xc.snk_host_system, xc.snk_dirname,
		tmp_rel_path);

	/* Return relative path? */
	if (rel_path)
		*rel_path = tmp_rel_path;
	else
		XtFree(tmp_rel_path);

	/* Return full path? */
	if (full_path)
		*full_path = tmp_full_path;
	else
		XtFree(tmp_full_path);
}


/*
 * print_xfer_forest
 */
void
print_xfer_forest()
{
	struct forest_node *node[MAXLEVELS];
	int level = 0;
	int i;
	
	node[0] = xc.forest;
	while (1) {
		if (node[level] == NULL) {
			level--;
			if (level < 0)
				return;
			node[level] = node[level]->next;
		} else if (node[level]->type == TYPE_DIR) {
			for (i=0; i<3*level; i++)
				printf(" ");
			if (node[level]->position == ABOVE_START_NODE)
				printf("P %s\n", node[level]->entry);
			else
				printf("D %s\n", node[level]->entry);
			node[level+1] = node[level]->first_child;
			level++;
		} else if (node[level]->type == TYPE_UNKNOWN) {
			for (i=0; i<3*level; i++)
				printf(" ");
			printf("U %s\n", node[level]->entry);
			node[level] = node[level]->next;
		} else {
			for (i=0; i<3*level; i++)
				printf(" ");
			printf("F %s\n", node[level]->entry);
			node[level] = node[level]->next;
		}
	}
}


/*
 * release_xfer_forest - Release memory used by transfer forest.
 */
void
release_xfer_forest()
{
	release_xfer_subforest(xc.forest);
}


/*
 * release_xfer_subforest - Release memory used by forest starting at "node".
 */
void
release_xfer_subforest(node)
struct forest_node *node;
{
	struct forest_node *ptr1;
	struct forest_node *ptr2;

	/* If forest is empty, nothing to do */
	if (!node)
		return;

	/* March through siblings */
	ptr1 = node;
	while (ptr1) {
		release_xfer_subforest(ptr1->first_child);
		XtFree(ptr1->entry);
		ptr2 = ptr1;
		ptr1 = ptr1->next;
		XtFree((char *)ptr2);
	}
}


/*
 * nfiles_in_xfer_forest - Returns number of file in the transfer forest.
 */
nfiles_in_xfer_forest()
{
	struct forest_node *node[MAXLEVELS];
	int level = 0;
	int count = 0;
	
	node[0] = xc.forest;
	while (1) {
		if (node[level] == NULL) {
			level--;
			if (level < 0)
				return count;
			node[level] = node[level]->next;
		} else if (node[level]->type == TYPE_DIR) {
			node[level+1] = node[level]->first_child;
			level++;
		} else if (node[level]->type == TYPE_FILE) {
			count++;
			node[level] = node[level]->next;
		} else
			fatal_error("Bug in nfiles_in_xfer_forest()");
	}
}



syntax highlighted by Code2HTML, v. 0.9.1