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

#ifndef P_tmpdir
#define P_tmpdir  "/usr/tmp/"
#endif

#define MAXTRIES  10

int init_local_copy();
int do_local_copy();

int init_get();
int do_get();

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

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

char **path_to_links();
double stop_timer();
char *temp_file_name();
int cb_view_files();
char *merge_paths();
void delete_file_and_its_dir();

extern int xfer_mode;
extern struct st_host_info hinfo[];
extern struct xfer_ctrl_block xc;
extern XtAppContext app;
extern Display *display;
extern int diagnostics;


/*
 * init_view - Initialize file transfer of selected files in directory
 *             window "dirwin" to temporary directory on local host
 *             for viewing.  "list" is an array list containing the
 *             relative paths of the files to view.
 */
void
init_view(dirwin, list)
struct dirwin_st *dirwin;
struct sl_struct *list;
{
	int retval;
	int i;
	int indx;
	struct forest_node *forest;
	struct forest_node **head;
	struct forest_node *p;
	struct forest_node *parent;
	char **links;
	char **link_ptr;
	char *ptr;
	char *rel_path;
	char msg[MAXPATHLEN+30];

	/* Clear error flag */
	raise_okflag();

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

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

	/* Make sure connection is still good */
	if (dirwin->host != LOCAL) {
		retval = check_connection(dirwin->host, dirwin);
		if (retval < 0) {
			restore_prev_cursor();
			switch (retval) {
			case -6:
				record_abort("View Files");
				break;
			case -1:
				record_and_alert("Unable to view file(s).", dirwin->w_shell);
			}
			hide_stop_button();
			end_op();
			return;
		}
	}

	/* Set transfer mode */
	if ((dirwin->host != LOCAL)
			&& ((retval = ftp_type(dirwin->host, xfer_mode)) < 0)) {
		restore_prev_cursor();
		switch (retval) {
		case -1:
			record_and_alert("Unable to view file(s).", dirwin->w_shell);
			break;
		case -3:
			restore_lost_connection(dirwin->host, dirwin);
			break;
		case -6:
			record_abort("View Files");
		}
		hide_stop_button();
		end_op();
		return;
	}

    /* Form list of selected items to transfer */
	forest = NULL;
	for (i=list->nentries-1; i>=0; i--) {
		links = path_to_links(hinfo[dirwin->host].system, list->entries[i]);
		if (hinfo[dirwin->host].system == SYS_VMS) {   /* Remove ".dir;n" */
			link_ptr = links;
			while (*link_ptr) {
				if ((ptr = strstr(*link_ptr, ".dir")))
					*ptr = '\0';
				link_ptr++;
			}
		}
		indx = 0;
		p = forest;
		head = &forest;
		parent = NULL;
		while (links[indx])
			if (p && !strcmp(p->entry, links[indx])) {
				if (p->position == AT_START_NODE) {
					if (links[indx+1]) {
						src_paths_of_xfer_node(p, &rel_path, NULL);
						sprintf(msg, "Can't view directory %s", rel_path);
						XtFree(rel_path);
						record_warning(msg);
						xc.error = True;
						p->position = ABOVE_START_NODE;
						p->type = TYPE_DIR;
					} else
						break;   /* Duplicate entry.  Can this happen? */
				} else if (!links[indx+1]) {
					sprintf(msg, "Can't view directory %s", list->entries[i]);
					record_warning(msg);
					xc.error = True;
					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->type = TYPE_FILE;
					p->position = AT_START_NODE;
				}
				parent = p;
				*head = p;
				head = &p->first_child;
				p = p->first_child;
				indx++;
			}
		release_path_links(links);
	}

	/* Set up transfer control block */
	xc.operation = VIEW;
	xc.mode = xfer_mode;
	xc.level = 0;
	xc.state = 0;
	xc.nretries = 0;
	xc.error = False;
	xc.src_dirwin = dirwin;
	xc.src_host_system = hinfo[dirwin->host].system;
	xc.src_host_server = hinfo[dirwin->host].server;
	xc.snk_host_system = SYS_UNIX;
	xc.src_dirname = XtNewString(dirwin->dirname);
	xc.snk_dirname = XtNewString("");
	xc.forest = forest;
	xc.node[0] = forest;
	xc.nstages = 0;
	xc.file_count = 0;
	xc.file_total = nfiles_in_xfer_forest();
	xc.more_files = True;
	if (dirwin->host == LOCAL)
		xc.src_host_type = XLOCAL;
	else
		xc.src_host_type = XREMOTE;

	/* Transfer file */
	show_xfermon(dirwin);
	set_xfermon_status(STATUS_PASS1);
	set_xfermon_name_label("File Name:");
	set_xfermon_progress((long)-1, (long)-1, (double)0.0);
	set_xfermon_file_count(0, xc.file_total);
	XSync(display, 0);
	XtAppAddWorkProc(app, (XtWorkProc)cb_view_files, NULL);
}


/*
 * cb_view_files - Work proc for transferring and viewing files.
 */
cb_view_files()
{
	long current_time;
	struct forest_node *tmp_node;
	char *rel_path;
	char *full_path;
	char msg[MAXPATHLEN+40];
	int i;
	int retval;
	int retval1;
	int retval2;
	double ftime;
	char *temp_ptr;
	char *snk_path;
	char *src_path;
	char *q;

	/* 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) {
					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_view_files()");
			}
		} else {
			xfer_done(False, False);
			if (xc.error)
				record_and_alert("At least one file cannot be viewed.",
					xc.src_dirwin->w_shell);
			return True;
		}
	case 1:   /* Initialize file transfer */
		if (stop()) {
			xfer_done(False, False);
			return True;
		}
		if (xc.src_host_type == XLOCAL) {
			src_paths_of_xfer_node(xc.current_node, NULL, &src_path);
			if (!need_to_decompress(src_path)) {
				if (view_file(xc.src_dirwin, src_path, True) == -6) {
					xfer_abort(True);
					XtFree(src_path);
					return True;
				}
				XtFree(src_path);
				xc.state = 0;
				xc.nretries = 0;
				return False;
			}
			XtFree(src_path);
		}
		if ((temp_ptr = temp_file_name()) == NULL) {
			src_paths_of_xfer_node(xc.current_node, &rel_path, NULL);
			sprintf(msg, "Can't create temp directory for %s.", rel_path);
			record_warning("Unable to create temporary directory for viewer");
			XtFree(rel_path);
			xc.error = True;
			xc.state = 0;
			xc.nretries = 0;
			return False;
		}
		XtFree(xc.snk_dirname);
		xc.snk_dirname = temp_ptr;
		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);
		retval1 = local_mkdir(xc.snk_dirname, 0700);
		if (retval1 == 0)
			retval2 = (*init_fn[xc.src_host_type])();
		if ((retval1 == 0) && (retval2 == -6)) {
			snk_paths_of_xfer_node(xc.current_node, NULL, &snk_path);
			xfer_abort(True);
			delete_file_and_its_dir(snk_path);
			XtFree(snk_path);
			return True;
		} else if ((retval1 == -1) || (retval2 == -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);
			snk_paths_of_xfer_node(xc.current_node, NULL, &snk_path);
			delete_file_and_its_dir(snk_path);
			XtFree(snk_path);
			xc.error = True;
			xc.state = 0;
			xc.nretries = 0;
			return False;
		} else if ((retval1 == 0) && (retval2 == -3)) {
			snk_paths_of_xfer_node(xc.current_node, NULL, &snk_path);
			xfer_done(True, False);
			delete_file_and_its_dir(snk_path);
			XtFree(snk_path);
			return True;
		} else {
			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()) {
			snk_paths_of_xfer_node(xc.current_node, NULL, &snk_path);
			xfer_abort(True);
			delete_file_and_its_dir(snk_path);
			XtFree(snk_path);
			return True;
		}
		switch ((*do_fn[xc.src_host_type])()) {
		case -6:
			snk_paths_of_xfer_node(xc.current_node, NULL, &snk_path);
			xfer_abort(True);
			delete_file_and_its_dir(snk_path);
			XtFree(snk_path);
			return True;
		case -3:
			snk_paths_of_xfer_node(xc.current_node, NULL, &snk_path);
			xfer_done(True, False);
			delete_file_and_its_dir(snk_path);
			XtFree(snk_path);
			return True;
		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);
			snk_paths_of_xfer_node(xc.current_node, NULL, &snk_path);
			delete_file_and_its_dir(snk_path);
			XtFree(snk_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);
			}
			snk_paths_of_xfer_node(xc.current_node, NULL, &snk_path);
			if (xc.src_host_system == SYS_VMS) {
				if ((q = strrchr(snk_path, ';')))
					*q = '\0';
			}
			if (view_file(xc.src_dirwin, snk_path, False) == -6) {
				xfer_abort(True);
				delete_file_and_its_dir(snk_path);
				XtFree(snk_path);
				return True;
			}
			XtFree(snk_path);
			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) */
}


/*
 * temp_file_name - Create a unique name for a temporary file in the
 *                  directory defined as P_tmpdir in <stdio.h>.  If the
 *                  environment variable TMPDIR is defined, it is used
 *                  as the directory.  If successful, this function returns
 *                  a pointer to the name, which can be released by calling
 *                  XtFree(); if unsuccessful, NULL is returned.  The name
 *                  will begin with the prefix "xdir".
 */
char *
temp_file_name()
{
	char *dir_path = NULL;
	char *file_path;
	char *temp;
	int pid;
	char pid_string[5];
	char suffix[5];
	int i;
	int j;

	/* Get directory path */
	if ((temp = getenv("TMPDIR")))
		dir_path = XtNewString(temp);
	else
		dir_path = XtNewString(P_tmpdir);

	/* Verify that directory is accessible */
	if (access(dir_path, 0) < 0) {
		XtFree(dir_path);
		return NULL;
	}

	/* Convert PID into string */
	pid = getpid();
	sprintf(pid_string, "%04d", pid%10000);

	/* Try several times to create a unique name */
	for (i=0; i<MAXTRIES; i++) {
		for (j=0; j<4; j++)
			suffix[j] = ((rand()&0xff)%26)+'A';
		suffix[4] = '\0';
		file_path = XtMalloc(strlen(dir_path)+14);
		strcpy(file_path, dir_path);
		if (file_path[strlen(file_path)-1] != '/')
			strcat(file_path, "/");
		strcat(file_path, "xdir");
		strcat(file_path, pid_string);
		strcat(file_path, suffix);
		if (access(file_path, 0) < 0) {
			if (errno == ENOENT) {
				XtFree(dir_path);
				return file_path;
			}
		}
		XtFree(file_path);
	}

	XtFree(dir_path);
	return NULL;
}


/*
 * delete_file_and_its_dir - Delete "file" and the "dir" that contains it.
 *                           No errors are reported if the file and/or
 *                           directory do not exist, or if unable to
 *                           delete either.
 */
void
delete_file_and_its_dir(file)
char *file;
{
	char *dir;

	parse_path(SYS_UNIX, file, &dir, NULL);
	delete_dir_and_its_files(dir);
	XtFree(dir);
}


/*
 * delete_dir_and_its_files - Delete the directory specified by "dir"
 *                            and any files it might contain.  "dir"
 *                            must be a full path specification.  Returns
 *                            0 for success and -1 for failure.
 */
delete_dir_and_its_files(dir)
char *dir;
{
	struct sl_struct *dlist;
	char *temp;
	char *temp_dir;
	int i;
	int retval = 0;

	/* Important safety check.  Delete only temp directories */
	if ((temp = getenv("TMPDIR")))
		temp_dir = XtNewString(temp);
	else
		temp_dir = XtNewString(P_tmpdir);
	if (strlen(temp_dir) > strlen(dir))
		fatal_error("Bug in delete_dir_and_its_files()");
	temp = XtNewString(dir);
	temp[strlen(temp_dir)] = '\0';
	if (strcmp(temp, temp_dir))
		fatal_error("Bug in delete_dir_and_its_files()");
	XtFree(temp);
	XtFree(temp_dir);

	/* Delete all entries in the directory */
	if (get_dirlist(LOCAL, dir, TABULAR, True, False, False, &dlist) == 0) {
		for (i=0; i<dlist->nentries; i++) {
			temp = merge_paths(SYS_UNIX, dir, dlist->entries[i]);
			if (unlink(temp) < 0)
				retval = -1;
			XtFree(temp);
		}
		release_array_list(dlist);
	} else
		retval = -1;

	/* Now remove its directory */
	if (rmdir(dir) < 0)
		retval = -1;

	/* Get out of here */
	return retval;
}



syntax highlighted by Code2HTML, v. 0.9.1