/***************************************************************************/
/***************************************************************************/
/*                                                                         */
/*   (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 <sys/time.h>
#include <ctype.h>
#include <Xm/Xm.h>
#include "xdir.h"
#include "xfer.h"
#include "str.h"

int init_local_copy();
int do_local_copy();
int abort_local_copy();

int init_put();
int do_put();
int abort_put();

int init_get();
int do_get();
int abort_get();

int init_remote_copy();
int do_remote_copy();
int abort_remote_copy();

long time_of_day();
double stop_timer();
char *entry_to_rel_path();
struct ref_st *create_reference_subtree();
void print_xfer_stats();
void xfer_done();
void xfer_abort();
void final_xfer_cleanup();
void start_timer();

int (*init_fn[2][2])() = {
	{ init_local_copy,  init_put         },
	{ init_get,         init_remote_copy }
};

int (*do_fn[2][2])() = {
	{ do_local_copy,    do_put         },
	{ do_get,           do_remote_copy }
};

int (*abort_fn[2][2])() = {
	{ abort_local_copy, abort_put         },
	{ abort_get,        abort_remote_copy }
};

static char *msg1 = "Unable to redisplay directory.\n\nDirectory window might be out of date.";

static struct timeval start_time;
static struct timeval stop_time;

extern struct xfer_ctrl_block xc;
extern int max_ftp_retries;
extern int diagnostics;
extern int beep_when_ops_done;
extern Display *display;


/*
 * cb_xfer_files - Work proc for transferring files.
 */
cb_xfer_files()
{
	long current_time;
	struct forest_node *tmp_node;
	char *rel_path;
	char *full_path;
	char msg[MAXPATHLEN+40];
	char *dir;
	int i;
	int retval;
	double ftime;

	/* Perform next step of file transfers */
	switch (xc.state) {
	case 0:   /* No xfer in progress */
		if (stop()) {
			xfer_abort(False);
			return True;
		}
		if ((xc.nstages == MAXSTAGES) || (xc.nstages && !xc.more_files)) {
			src_paths_of_xfer_node(xc.staged_file[0].node, &rel_path, NULL);
			set_xfermon_status(STATUS_WAIT);
			set_xfermon_name(rel_path);
			set_xfermon_progress((long)-1, (long)-1, (double)0.0);
			XSync(display, 0);
			XtFree(rel_path);
			current_time = time_of_day();
			if (current_time < xc.staged_file[0].time+STAGE_DELTA) {
				if (rest_stop(DELTA) == -6) {
					xfer_abort(False);
					return True;
				} else
					return False;
			}
			src_paths_of_xfer_node(xc.staged_file[0].node, NULL, &full_path);
			retval = stage_file(xc.src_dirwin->host, full_path);
			XtFree(full_path);
			switch (retval) {
			case -6:
				xfer_abort(False);
				return True;
			case -3:
				xfer_done(True, False);
				return True;
			case -1:
				src_paths_of_xfer_node(xc.staged_file[0].node, &rel_path, NULL);
				sprintf(msg, "Unable to initialize transfer of %s", rel_path);
				XtFree(rel_path);
				record_warning(msg);
				xc.error = True;
				for (i=1; i<xc.nstages; i++) {
					xc.staged_file[i-1].node = xc.staged_file[i].node;
					xc.staged_file[i-1].time = xc.staged_file[i].time;
				}
				xc.nstages--;
				break;
			case 0:
				xc.current_node = xc.staged_file[0].node;
				for (i=1; i<xc.nstages; i++) {
					xc.staged_file[i-1].node = xc.staged_file[i].node;
					xc.staged_file[i-1].time = xc.staged_file[i].time;
				}
				xc.nstages--;
				xc.state = 1;
				break;
			case 1:
				tmp_node = xc.staged_file[0].node;
				for (i=1; i<xc.nstages; i++) {
					xc.staged_file[i-1].node = xc.staged_file[i].node;
					xc.staged_file[i-1].time = xc.staged_file[i].time;
				}
				xc.staged_file[xc.nstages-1].node = tmp_node;
				xc.staged_file[xc.nstages-1].time = current_time;
			}
			return False;
		} else if (xc.more_files) {
			while (1) {
				if (xc.node[xc.level] == NULL) {
					xc.level--;
					if (xc.level < 0) {
						xc.more_files = False;
						return False;
					}
					xc.node[xc.level] = xc.node[xc.level]->next;
				} else if (xc.node[xc.level]->type == TYPE_DIR) {
					if (xc.node[xc.level]->status == UNABLE_TO_CREATE)
						xc.node[xc.level] = xc.node[xc.level]->next;
					else {
						xc.node[xc.level+1] = xc.node[xc.level]->first_child;
						xc.level++;
					}
				} else if (xc.node[xc.level]->type == TYPE_FILE) {
					if ((xc.src_host_server == SERVER_UNIX_UNITREE) ||
							(xc.src_host_server == SERVER_UNIX_NSL_UNITREE) ||
							(xc.src_host_server == SERVER_UNIX_HPSS)) {
						current_time = time_of_day();
						src_paths_of_xfer_node(xc.node[xc.level], &rel_path,
							&full_path);
						set_xfermon_status(STATUS_STAGE);
						set_xfermon_name(rel_path);
						set_xfermon_progress((long)-1, (long)-1, (double)0.0);
						XSync(display, 0);
						retval = stage_file(xc.src_dirwin->host, full_path);
						XtFree(rel_path);
						XtFree(full_path);
						switch (retval) {
						case -6:
							xfer_abort(False);
							return True;
						case -3:
							xfer_done(True, False);
							return True;
						case -1:
							src_paths_of_xfer_node(xc.node[xc.level], &rel_path,
								NULL);
							sprintf(msg, "Unable to initialize transfer of %s",
								rel_path);
							XtFree(rel_path);
							record_warning(msg);
							xc.error = True;
							xc.node[xc.level] = xc.node[xc.level]->next;
							return False;
						case 1:
							xc.staged_file[xc.nstages].node = xc.node[xc.level];
							xc.staged_file[xc.nstages].time = current_time;
							xc.nstages++;
							xc.node[xc.level] = xc.node[xc.level]->next;
							return False;
						}
					}
					xc.current_node = xc.node[xc.level];
					xc.node[xc.level] = xc.node[xc.level]->next;
					xc.state = 1;
					return False;
				} else
					fatal_error("Bug in cb_xfer_files()");
			}
		} else {
			xfer_done(False, False);
			if (xc.error)
				record_and_alert("At least one file transfer failed.",
					xc.snk_dirwin->w_shell);
			return True;
		}
	case 1:   /* Initialize file transfer */
		if (stop()) {
			xfer_abort(False);
			return True;
		}
		if (xc.snk_host_type == XREMOTE) {
			snk_paths_of_xfer_node(xc.current_node, NULL, &full_path);
			flush_cache_directory(xc.snk_dirwin->host, full_path);
			parse_path(xc.snk_host_system, full_path, &dir, NULL);
			XtFree(full_path);
			flush_cache_directory(xc.snk_dirwin->host, dir);
			XtFree(dir);
		}
		src_paths_of_xfer_node(xc.current_node, &rel_path, NULL);
		if (xc.mode == ASCII)
			set_xfermon_status(STATUS_XFER_ASCII);
		else
			set_xfermon_status(STATUS_XFER_BINARY);
		set_xfermon_name(rel_path);
		set_xfermon_progress((long)-1, (long)-1, (double)0.0);
		XSync(display, 0);
		XtFree(rel_path);
		retval = (*init_fn[xc.src_host_type][xc.snk_host_type])();
		switch (retval) {
		case -6:
			xfer_abort(False);
			return True;
		case -5:
			xfer_done(True, True);
			return True;
		case -4:
			xfer_done(False, True);
			return True;
		case -3:
			xfer_done(True, False);
			return True;
		case -1:
			src_paths_of_xfer_node(xc.current_node, &rel_path, NULL);
			sprintf(msg, "Unable to initialize transfer of %s", rel_path);
			record_warning(msg);
			XtFree(rel_path);
			xc.error = True;
			xc.state = 0;
			xc.nretries = 0;
			break;
		case 0:
			set_xfermon_progress(xc.file_len, xc.file_index, stop_timer());
			XSync(display, 0);
			start_timer();
			xc.state = 2;
		}
		return False;
	case 2:  /* File transfer in progress */
		if (stop()) {
			xfer_abort(True);
			return True;
		}
		retval = (*do_fn[xc.src_host_type][xc.snk_host_type])();
		switch (retval) {
		case -6:
			xfer_abort(False);
			return True;
		case -5:
			xfer_done(True, True);
			return True;
		case -4:
			xfer_done(False, True);
			return True;
		case -3:
			xfer_done(True, False);
			return True;
		case -7:    /* Unitree retry logic */
			if (xc.nretries++ < max_ftp_retries)  {
				xc.state = 1;
				src_paths_of_xfer_node(xc.current_node, &rel_path, NULL);
				sprintf(msg, "*** Transfer of %s failed.  Will retry.\n",
					rel_path);
				write_log(msg);
				XtFree(rel_path);
				return False;
			}
		case -1:
			src_paths_of_xfer_node(xc.current_node, &rel_path, NULL);
			sprintf(msg, "Unable to complete transfer of %s", rel_path);
			record_warning(msg);
			XtFree(rel_path);
			xc.error = True;
			xc.state = 0;
			xc.nretries = 0;
			return False;
		case 0:   /* Transfer complete */
			set_xfermon_progress(xc.file_len, xc.file_index, stop_timer());
			xc.file_count++;
			set_xfermon_file_count(xc.file_count, xc.file_total);
			XSync(display, 0);
			ftime = stop_timer();
			if (diagnostics >= NORMAL) {
				src_paths_of_xfer_node(xc.current_node, &rel_path, NULL);
				sprintf(msg, "*** Successfully transferred file:  %s\n",
					rel_path);
				write_log(msg);
				XtFree(rel_path);
				print_xfer_stats(ftime, xc.file_len);
			}
			xc.state = 0;
			xc.nretries = 0;
			return False;
		case 1:   /* More to transfer */
			set_xfermon_progress(xc.file_len, xc.file_index, stop_timer());
			return False;
		}
	}

	return True;   /* Make gcc compiler happy (shouldn't get here) */
}


/*
 * extract_file_len - Extracts the file length usually returned in a "150"
 *                    ftp reply.  This routine looks for a pattern of the
 *                    form "(xxx bytes)", "(xxx)", or "(xxxk) near the end
 *                    of the reply, where the "xxx" represent digits.  If
 *                    this pattern is found, the file length is returned,
 *                    otherwise -1 is returned.  "reply" is the "150"
 *                    ftp reply to be scanned.
 */
long
extract_file_len(reply)
char *reply;
{
    static char *pat1 = " bytes)";
    int lenpat1 = strlen(pat1);
	static char *pat2 = ")";
	int lenpat2 = strlen(pat2);
	static char *pat3 = "k)";
	int lenpat3 = strlen(pat3);
	char *ptr;
	long len;

    if ((ptr = strrchr(reply, '(')) && isdigit(*++ptr)) {
        len = 0;
        while (*ptr && isdigit(*ptr))
            len = (len*10)+(*ptr++-'0');
        if ((int)(strlen(reply)-strlen(ptr)) >= lenpat1)
            if (strncmp(ptr, pat1, lenpat1) == 0)
                return len;
        if ((int)(strlen(reply)-strlen(ptr)) >= lenpat2)
            if (strncmp(ptr, pat2, lenpat2) == 0)
                return len;
        if ((int)(strlen(reply)-strlen(ptr)) >= lenpat3)
            if (strncmp(ptr, pat3, lenpat3) == 0)
                return len*1024;
    }

	return -1;
}


/*
 * start_timer - Starts the microsecond timer.  Needs to be called at
 *               start of interval to be timed.  Call stop_timer() at
 *               end of interval.
 *
 */
void
start_timer()
{
    gettimeofday(&start_time, NULL);
}


/*
 * stop_timer - Returns the number of microseconds that have elapsed
 *              since start_timer() was last called.  Undefined results
 *              if start_timer() is not called first.
 */
double
stop_timer()
{
    double fstart_time;
    double fstop_time;

    gettimeofday(&stop_time, NULL);
    fstart_time = (double)start_time.tv_sec+(double)start_time.tv_usec/1000000.;
    fstop_time = (double)stop_time.tv_sec+(double)stop_time.tv_usec/1000000.;
    return fstop_time-fstart_time;
}


/*
 * print_xfer_stats - Prints statistics about a file transfer.  "ftime"
 *                    is the number of seconds the transfer took.  "nbytes"
 *                    is the number of bytes transferred.  Set "nbytes"
 *                    to -1 if file length not known.
 */
void
print_xfer_stats(ftime, nbytes)
double ftime;
long nbytes;
{
	char msg[120];

	write_log("      ");
	if (nbytes == -1)
		write_log("?");
	else {
		sprintf(msg, "%ld", nbytes);
		write_log(msg);
	}
	sprintf(msg, " bytes transferred in %3.1f seconds", ftime);
	write_log(msg);
	if (nbytes != -1) {
		if (ftime <= 0)
			write_log(" (Very Fast)");
		else {
			sprintf(msg, " (%3.1f Kbytes/s)", (double)nbytes/(ftime*1000.));
			write_log(msg);
		}
	}
	write_log("\n");
}


/*
 * time_of_day - Returns time of day in seconds.
 */
long
time_of_day()
{
	struct timeval now;

    gettimeofday(&now, NULL);
	return (long)(now.tv_sec+(now.tv_usec+500000)/1000000);
}


/*
 * xfer_done - Clean up after terminating during a file transfer pass.
 *             Set "lost_src_host" to True if connection to source is
 *             lost.  Set "lost_snk_host" to True if connection to sink
 *             host is lost.
 */
void
xfer_done(lost_src_host, lost_snk_host)
int lost_src_host;
int lost_snk_host;
{
	struct dirwin_st *dirwin;
	struct entry_info *einfo;
	int indx;
	char *anchor_rel_path;
	struct ref_st *ref_tree;
	int retval;

	/* Hide file transfer dialog */
	hide_xfermon();

	/* Try to restore lost connections and update sink directory list */
	if (lost_src_host)
		restore_lost_connection(xc.src_dirwin->host, xc.snk_dirwin);
	if (lost_snk_host)
		restore_lost_connection(xc.snk_dirwin->host, xc.snk_dirwin);
	else if (xc.operation != VIEW) {     /* Update sink directory */
		if (is_dir_displayed(xc.snk_dirwin->host, xc.snk_dirname, &dirwin,
				&einfo)) {
			if (einfo) {
				if (dirwin != xc.snk_dirwin)  /* Sanity check */
					fatal_error("Bug in cb_xfer_files()");
				indx = xc.snk_dirwin->first_visible_row;
				anchor_rel_path =
					entry_to_rel_path(&xc.snk_dirwin->entries[indx]);
				ref_tree = create_reference_subtree(einfo);
				collapse_subtree(&einfo, False);
				retval = re_expand_subtree(&einfo, ref_tree,
					xc.snk_dirwin->cache_mode, False);
				release_reference_subtree(ref_tree);
				switch (retval) {
				case -6:
					record_abort("Copy Files");
					dirwin_out_of_date_alert(xc.snk_dirwin);
					goto fin;
				case -1:
					record_and_alert(msg1, xc.snk_dirwin->w_shell);
					XtFree(anchor_rel_path);
					goto fin;
				case -3:
					restore_lost_connection(xc.snk_dirwin->host, xc.snk_dirwin);
					XtFree(anchor_rel_path);
					lost_snk_host = True;
					goto fin;
				}
				calc_dirwin_geometry(xc.snk_dirwin);
				set_first_visible_entry(xc.snk_dirwin, anchor_rel_path);
				XtFree(anchor_rel_path);
			} else {
				switch (display_dir(xc.snk_dirwin->host, dirwin, xc.snk_dirname,
					True, False, xc.snk_dirwin->cache_mode, False)) {
				case -6:
					record_abort("Copy Files");
					dirwin_out_of_date_alert(xc.snk_dirwin);
					break;
				case -1:
					record_and_alert(msg1, xc.snk_dirwin->w_shell);
					break;
				case -3:
					restore_lost_connection(xc.snk_dirwin->host, xc.snk_dirwin);
					lost_snk_host = True;
				}
			}
		}
	}
	
fin	:
	
	/* Finish cleaning up */
	final_xfer_cleanup();
}


/*
 * xfer_abort - Abort active file transfer and then clean up.  Call
 *              this function instead of xfer_done() if stop button
 *              has been pressed.  Set urgent to True if in the middle
 *              of a file transfer.
 */
void
xfer_abort(urgent)
int urgent;
{
	struct dirwin_st *dirwin;
	struct entry_info *einfo;

	/* Hide file transfer dialog */
	hide_xfermon();

	/* Send abort message(s) to remote host(s) involved in transfer */
	if (urgent)
		(*abort_fn[xc.src_host_type][xc.snk_host_type])();
	
	/* Clean up */
	record_abort("Transferring Files");

	/* Will sink directory window be out of date? */
	if (xc.operation != VIEW) {
		if (is_dir_displayed(xc.snk_dirwin->host, xc.snk_dirname, &dirwin,
				&einfo))
			dirwin_out_of_date_alert(xc.snk_dirwin);
	}

	/* Final cleanup */
	final_xfer_cleanup();
}


/*
 * final_xfer_cleanup - Some cleanup in common between xfer_done() and
 *                      xfer_abort().
 */
void
final_xfer_cleanup()
{
	/* Miscellaneous cleanup */
	release_xfer_forest();
	XtFree(xc.src_dirname);
	XtFree(xc.snk_dirname);
	clear_selected_entries();
	update_dir_controls();
	if (beep_when_ops_done)
		beep();
	restore_prev_cursor();

	/* Signal end of operation */
	hide_stop_button();
	end_op();
}



syntax highlighted by Code2HTML, v. 0.9.1