/****************************************************************************
 * NCSA Mosaic for the X Window System                                      *
 * Software Development Group                                               *
 * National Center for Supercomputing Applications                          *
 * University of Illinois at Urbana-Champaign                               *
 * 605 E. Springfield, Champaign IL 61820                                   *
 * mosaic@ncsa.uiuc.edu                                                     *
 *                                                                          *
 * Copyright (C) 1993, Board of Trustees of the University of Illinois      *
 *                                                                          *
 * NCSA Mosaic software, both binary and source (hereafter, Software) is    *
 * copyrighted by The Board of Trustees of the University of Illinois       *
 * (UI), and ownership remains with the UI.                                 *
 *                                                                          *
 * The UI grants you (hereafter, Licensee) a license to use the Software    *
 * for academic, research and internal business purposes only, without a    *
 * fee.  Licensee may distribute the binary and source code (if released)   *
 * to third parties provided that the copyright notice and this statement   *
 * appears on all copies and that no charge is associated with such         *
 * copies.                                                                  *
 *                                                                          *
 * Licensee may make derivative works.  However, if Licensee distributes    *
 * any derivative work based on or derived from the Software, then          *
 * Licensee will (1) notify NCSA regarding its distribution of the          *
 * derivative work, and (2) clearly notify users that such derivative       *
 * work is a modified version and not the original NCSA Mosaic              *
 * distributed by the UI.                                                   *
 *                                                                          *
 * Any Licensee wishing to make commercial use of the Software should       *
 * contact the UI, c/o NCSA, to negotiate an appropriate license for such   *
 * commercial use.  Commercial use includes (1) integration of all or       *
 * part of the source code into a product for sale or license by or on      *
 * behalf of Licensee to third parties, or (2) distribution of the binary   *
 * code or source code to third parties that need it to utilize a           *
 * commercial product sold or licensed by or on behalf of Licensee.         *
 *                                                                          *
 * UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR   *
 * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED          *
 * WARRANTY.  THE UI SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE    *
 * USERS OF THIS SOFTWARE.                                                  *
 *                                                                          *
 * By using or copying this Software, Licensee agrees to abide by the       *
 * copyright law and all other applicable laws of the U.S. including, but   *
 * not limited to, export control laws, and the terms of this license.      *
 * UI shall have the right to terminate this license immediately by         *
 * written notice upon Licensee's breach of, or non-compliance with, any    *
 * of its terms.  Licensee may be held legally responsible for any          *
 * copyright infringement that is caused or encouraged by Licensee's        *
 * failure to abide by the terms of this license.                           *
 *                                                                          *
 * Comments and questions are welcome and can be sent to                    *
 * mosaic-x@ncsa.uiuc.edu.                                                  *
 ****************************************************************************/

/* Copyright (C) 2005, 2006, 2007 - The VMS Mosaic Project */

#include "../config.h"
#include <stdio.h>
#include <stdlib.h>

#ifndef VMS
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <fcntl.h>
#endif

#include <errno.h>
#if defined(MULTINET) && defined(__DECC) && (__VMS_VER >= 70000000)
#define strdup  decc$strdup
#endif /* VMS V7.0 has strdup under a different name, do before string.h, GEC */
#include <string.h>

#ifndef VMS
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <pwd.h>
#endif

#ifdef RS6000
#include <sys/select.h>
#endif

#ifdef VMS
#include <ctype.h>
#include <descrip.h>
#include <file.h>
#include <lib$routines.h>
#include <lnmdef.h>
#include <starlet.h>
#if defined(__DECC) && (__VMS_VER >= 70000000)
#define _VMS_V6_SOURCE
#endif    /* avoid __UTC_STAT in VMS V7.0, GEC */
#include <stat.h>
#if defined(__DECC) && (__VMS_VER >= 70000000)
#undef _VMS_V6_SOURCE
#endif
#include <types.h>
#include <unixio.h>

#define $NEW_DESCRIPTOR(name) \
	struct dsc$descriptor_s name = { \
		0, DSC$K_DTYPE_T, DSC$K_CLASS_S, 0 \
	}
#endif

#include "system.h"

/* Use builtin strdup when appropriate -- code duplicated in tcp.h. PGE */
/* DEC C V5.2 string.h has for VMS V7.0, GEC */
#if defined(ultrix) || (defined(VMS) && (!defined(__GNUC__) || defined(vax)) && (!defined(__DECC) || (__VMS_VER < 70000000) || (__DECC_VER <= 50230003))) || defined(NeXT)
extern char *strdup(char *str);
#endif

#ifndef VMS
extern int sys_nerr;
#ifndef linux
extern char *sys_errlist[];
#endif
extern int errno;
#endif

#ifndef DISABLE_TRACE
extern int reportBugs;
int nutTrace = 0;
#endif

int sleep_interrupt = 0;

#ifdef STANDALONE	/* For testing */

#ifndef VMS /* Completely different tests needed under VMS. PGE */
char *userPath = {"/bin:/usr/bin:/sbin:/usr/sbin"};

int main(int argc, char **argv)
{
	int retValue;
	char *cmd, *fnam, *lpr;
	char buf[BUFSIZ];

	if (argc == 3) {
		if (my_sleep(atoi(argv[1]), atoi(argv[2]))) {
			printf("Interrupted\n");
		} else {
			printf("Not Interrupted\n");
		}
		exit(0);
	}
	lpr = (char *)malloc(50);
	strcpy(lpr, "dsfs/usr/ucb/lpr");

	fnam = (char *)malloc(50);
	strcpy(fnam, "/sdlfjsdusr5/spowers/.cshrc");

	cmd = (char *)malloc((strlen(lpr) + strlen(fnam) + 24));
	sprintf(cmd, "%s %s", lpr, fnam);

	printf("Executing '%s'.\n", cmd);

	if ((retValue = my_system(cmd, buf, BUFSIZ)) != SYS_SUCCESS)
		printf("-----\nError Code [%d]\n-----\n", retValue);

	if (buf && *buf) {
		printf("------\n%s-----\n", buf);
	} else {
		printf("------\nNo output.\n------\n");
	}
}

#else	/* VMS */

/* Copied from mo-www.c PGE */
#if defined(ultrix) || defined(VMS) || defined(NeXT) || defined(M4310) || defined(vax)
char *strdup(char *str) {
  char *dup;

  if (!str || !(dup = (char *)malloc(strlen(str) + 1)))
    return NULL;

  return(strcpy(dup, str));
}
#endif

int main(int argc, char **argv)
{
   int    retValue, argc2;
   char **argv2;
   char  *home_dir;
   char   buffer[256];

   retValue = get_home(&home_dir);
   printf("Home = \"%s\"\n  retValue = %d\n\n", home_dir, retValue);
   free(home_dir);

   if (file_exists("sys$login:login.com")) {
      printf("The file sys$login:login.com exists\n");
   } else {
      printf("The file sys$login:login.com does not exist\n");
   }
   if (file_exists("sys$login:ipserngipnfb.sadfghth")) {
      printf("The file sys$login:ipserngipnfb.sadfghth exists\n\n");
   } else {
      printf("The file sys$login:ipserngipnfb.sadfghth does not exist\n\n");
   }
   buffer[0] = '\0';
   retValue = my_system("kdshlkw", buffer, 256);
   printf("my_system kdshlkw\n");
   printf("  gave \"%s\"\n  retValue = %d\n\n", buffer, retValue);

   buffer[0] = '\0';
   retValue = my_system("dir *.*;0", buffer, 256);
   printf("my_system dir *.*;0\n");
   printf("  gave \"%s\"\n  retValue = %d\n\n", buffer, retValue);

   buffer[0] = '\0';
   retValue = my_copy("argjhsrghjajh", "oijfdgath", buffer, 256, TRUE);
   printf("my_copy argjhsrghjajh to oijfdgath\n");
   printf("  gave \"%s\"\n  retValue = %d\n\n", buffer, retValue);

   buffer[0] = '\0';
   retValue = my_copy("sys$login:login.com", "sys$login:login.copy",
                      buffer, 256, TRUE);
   printf("my_copy sys$login:login.com to sys$login:login.copy\n");
   printf("  gave \"%s\"\n  retValue = %d\n\n", buffer, retValue);

   printf("Starting 5 second sleep\n");
   retValue = my_sleep(5, TRUE);
   printf("Ending 5 second sleep\n  retValue = %d\n\n", retValue);

   printf("errno == 1 => \"%s\"\n\n", my_strerror(1));

   argv2 = buildArgv("abc def", &argc2);
   printf("\"abc def\" => argc %d, argv [0] \"%s\", argv [1] \"%s\"\n\n",
          argc2, argv2[0], argv2[1]);
   free(*argv);
   free(argv);

   buffer[0] = '\0';
   retValue = my_move("sys$login:login.copy", "sys$login:login.move",
                      buffer, 256, TRUE);
   printf("my_move sys$login:login.copy to sys$login:login.move\n");
   printf("  gave %s\n  retValue = %d\n\n", buffer, retValue);

   retValue = my_system("diff sys$login:login.com sys$login:login.move",
			buffer, 256);
   retValue = my_system("delete/log/noconf sys$login:login.move;*",
			buffer, 256);
   buffer[0] = '\0';
   retValue = my_move("sys$manager:sylogin.com", "sys$login:sylogin.move",
                      buffer, 256, TRUE);
   printf("my_move sys$manager:sylogin.com to sys$login:sylogin.move\n");
   printf("  gave %s\n  retValue = %d\n\n", buffer, retValue);
   retValue = my_system("diff sys$manager:sylogin.com sys$login:sylogin.move",
			buffer, 256);
   retValue = my_system("dir/full sys$login:sylogin.move", buffer, 256);
   retValue = my_system("delete/log/noconf sys$login:sylogin.move;*",
			buffer, 256);
}
#endif /* VMS */

#else

#ifndef VMS
extern char *userPath;
#endif

#endif


#ifndef VMS
/*
 * Written by: Scott Powers
 *
 * findProgram takes a program name and a path and searches it until:
 *   a) The program name is found, at which time the full path is returned.
 *   b) The end of the search path comes about, at which time NULL is returned.
 *
 */
static char *findProgram(char *pname, char *spath)
{
	char *start = spath;
	char *ptr, *endptr;
	char tryit[BUFSIZ];
	struct stat buf;

	if (!spath || !*spath || !pname || !*pname)
		return(NULL);

	while (start && *start) {
		ptr = start;
		endptr = strchr(start, ':');
		if (endptr) {
			start = endptr + 1;
			*endptr = '\0';
		} else {
			start = NULL;
		}
		sprintf(tryit, "%s/%s", ptr, pname);
		if (!stat(tryit, &buf))
			return(strdup(tryit));
	}
	return(NULL);
}
#endif


/*
 * Written by: Scott Powers and Brad Viviano
 *
 * Takes a string command, executes the command and returns the output of
 *   the command (if any) in retBuf (passed in and pre-allocated).
 *
 * Returns one of the following:
 *   SYS_SUCCESS - The command executed without incident.  Note, this does not
 *     necessarily mean the command was successful... e.g. some systems have
 *     a shell script for "lpr".  In this case, the shell that runs "lpr" will
 *     execute fine, but the command "lp" which "lpr" calls may still fail.
 *   SYS_NO_COMMAND - There was no command provided.
 *   SYS_FORK_FAIL - The fork failed.
 *   SYS_PROGRAM_FAILED - The exec could not start the program.
 *   SYS_NO_RETBUF - There was no retBuf allocated.
 *   SYS_FCNTL_FAILED - The set of NON_BLOCK on the parent end of the pipe
 *     failed.
 */
int my_system(char *cmd, char *retBuf, int bufsize)
{
#ifndef VMS  /* PGE */
	char **sys_argv = NULL;
	int sys_argc;
	pid_t pid;
	int status, statusloc;
	int fds[2];
	char buf[BUFSIZ];
	char *path = NULL;

	if (!retBuf)
		return(SYS_NO_RETBUF);

	*retBuf = '\0';

	if (!cmd || !*cmd)
		return(SYS_NO_COMMAND);

	pipe(fds);
	if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1) {
#ifndef DISABLE_TRACE
		if (nutTrace)
			perror("fcntl-nonblock");
#endif
		return(SYS_FCNTL_FAILED);
	}
	if ((pid = fork()) == -1) {
		return(SYS_FORK_FAIL);
	} else if (pid == 0) {
		/* In child -- so don't worry about frees */
		sys_argv = buildArgv(cmd, &sys_argc);
		dup2(fds[1], 1);
		dup2(fds[1], 2);
		if (sys_argv) {
			if (sys_argv[0] && sys_argv[0][0] &&
			    sys_argv[0][0] == '/') {
				path = strdup(sys_argv[0]);
			} else {
				path = findProgram(sys_argv[0], userPath);
			}
			execv(path, sys_argv);
#ifndef DISABLE_TRACE
			if (nutTrace) {
				fprintf(stderr, "Exec of %s failed!\n", cmd);
				perror("exec");
			}
#endif
		} else {
#ifndef DISABLE_TRACE
			if (nutTrace)
				fprintf(stderr,
					"Could not build argv for [%s].\n",cmd);
#endif
		}
		exit(1);  /* child */
	} else {
		int n;

		/* In parent */
		status = wait(&statusloc);
		n = read(fds[0], retBuf, bufsize - 1);
		if (n > 0) {
			retBuf[n] = '\0';
		} else {
			*retBuf = '\0';
		}
		close(fds[0]);
		close(fds[1]);

		if (*retBuf)
			return(SYS_PROGRAM_FAILED);

		return(SYS_SUCCESS);
	}

#else /* VMS -- PGE */
   unsigned int status;
   unsigned int completion_status;

   if (!retBuf)
      return(SYS_NO_RETBUF);

   *retBuf = '\0';

   if (!cmd || !*cmd)
      return(SYS_NO_COMMAND);

#ifndef DISABLE_TRACE
   if (nutTrace)
      fprintf(stderr, "MY_SYSTEM executing command = %s\n", cmd);
#endif

   {
      $NEW_DESCRIPTOR(cmd_desc);
      $NEW_DESCRIPTOR(retBuf_desc);
      unsigned short int string_end;

      cmd_desc.dsc$w_length = strlen(cmd);
      cmd_desc.dsc$a_pointer = cmd;
      retBuf_desc.dsc$w_length = bufsize - 1;
      retBuf_desc.dsc$a_pointer = retBuf;

      status = lib$spawn(&cmd_desc, 0, 0, 0, 0, 0, &completion_status);
      if (status != 1) {
#ifndef DISABLE_TRACE
	 if (nutTrace || reportBugs)
	    fprintf(stderr, "Spawn failed with status = %d\n", status);
#endif
         return(SYS_FORK_FAIL);
      }
      if ((completion_status & 1) != 1) {
         status = sys$getmsg(completion_status, &string_end, &retBuf_desc,
		             15, 0);
         retBuf[string_end] = '\0';
      }
   }

   return(SYS_SUCCESS);
#endif
}


/*
 * Written by: Scott Powers
 *
 * This will effectively get rid of the problem with using "/bin/mv" as it
 * first tries to use "rename".  If that fails, it stores the error report
 * and then tries to actually copy the file.  If that fails, it stores the
 * error report.  If the copy fails then we return an error as well as copying
 * both error reports into "retBuf" so they can be displayed to the user.
 *
 * If "overwrite" is true, the destination file will automatically be
 * overwritten.  If it is false and the file exists, my_move will return
 * SYS_FILE_EXISTS.  It is up to the programmer to tell the user this.
 *
 * Return Values:
 *   SYS_NO_SRC_FILE -- There was no source filename specified.
 *   SYS_NO_DEST_FILE -- There was no destination filename specified.
 *   SYS_NO_RETBUF -- There was no retBuf specified (not allocated).
 *   SYS_DEST_EXISTS -- Overwrite was off and the destination exists.
 *   SYS_NO_MEMORY -- No memory to allocate with.
 *   SYS_SRC_OPEN_FAIL -- Open failed on the source file.
 *   SYS_DEST_OPEN_FAIL -- Open failed on the destination file.
 *   SYS_READ_FAIL -- The read call failed.
 *   SYS_WRITE_FAIL -- The write call failed.
 *   SYS_SUCCESS -- Success.
 */
int my_move(char *src, char *dest, char *retBuf, int bufsize, int overwrite)
{
	int status, fd_src, fd_dest, ret;
	int n_src = 1;
	int n_dest = 1;
	char *rename_error, *copy_error;
	struct stat dest_stat;

        if (!retBuf)
		return(SYS_NO_RETBUF);
	if (!src || !*src) {
		strcpy(retBuf, "There was no source file specified.\n");
		return(SYS_NO_SRC_FILE);
	}
	if (!dest || !*dest) {
		strcpy(retBuf, "There was no destination file specified.\n");
		return(SYS_NO_DEST_FILE);
	}

	*retBuf = '\0';

	if (!overwrite) {
#if defined(MULTINET) && defined(__alpha)
                if (decc$stat(dest, &dest_stat)) {
#else
		if (stat(dest, &dest_stat)) {
#endif /* Alpha DEC C couldn't find it otherwise ?????, GEC */
			sprintf(retBuf,
				"Stat [%s] error:\n     File already exists.\n",
				dest);
			return(SYS_DEST_EXISTS);
		}
	}
	if ((status = rename(src, dest)) == (-1)) {
		/* Manual copy -- prolly across partitions */
		rename_error = strdup(my_strerror(errno));
		if (!rename_error) {
			strcpy(retBuf,"There was no enough memory allocate.\n");
			return(SYS_NO_MEMORY);
		}
		if ((ret = my_copy(src, dest, retBuf, bufsize, overwrite)) ==
		    SYS_SUCCESS) {
			/* Now get rid of previous file */
#ifndef VMS
			unlink(src);
#else
			remove(src);
#endif /* VMS, GEC */
		}
		return(ret);
	}

	/* Now get rid of previous file */
#ifndef VMS
	unlink(src);
#endif /* VMS, no such routine on VMS nor is it needed, GEC */

	return(SYS_SUCCESS);
}


/*
 * Written by: Scott Powers
 *
 * Some systems do not have a "strerror" function.  This covers all the bases.
 */
char *my_strerror(int errornum)
{
#ifndef VMS   /* PGE, old VMS versions do not support sys_errlist */
        if (errornum < sys_nerr)
                return(sys_errlist[errornum]);
        return(NULL);
#else
        return(strerror(errornum));
#endif
}


/*
 * Written by: Brad Viviano and Scott Powers
 *
 * Takes a 1d string and turns it into a 2d array of strings.
 *
 * Watch out for the frees!  You must free(*argv) and then free(argv)!  NOTHING
 *   ELSE!!  Do _NOT_ free the individual args of argv.
 */
char **buildArgv(char *cmd, int *new_argc)
{
	char **new_argv;
	char *buf, *tmp;
	int i = 0;

	if (!cmd && !*cmd) {
		*new_argc = 0;
		return(NULL);
	}

	for (tmp = cmd; isspace(*tmp); tmp++)
		;
	if (!(buf = strdup(tmp))) {
		*new_argc = 0;
		return(NULL);
	}
	tmp = buf;

	new_argv = (char **)calloc(1, sizeof(char *));
	if (!new_argv) {
		free(buf);
		*new_argc = 0;
		return(NULL);
	}
	while (*tmp) {
		if (!isspace(*tmp)) {  /* Found the beginning of a word */
			new_argv[i] = tmp;
			for (; *tmp && !isspace(*tmp); tmp++)
				;
			if (*tmp)
				*tmp++ = '\0';
			i++;
			new_argv = (char **)realloc(new_argv,
				                    (i + 1) * sizeof(char *));
			new_argv[i] = NULL;
		} else {
			tmp++;
		}
	}
	*new_argc = i;

	return(new_argv);
}


/*
 * Written by: Scott Powers
 *
 * Takes an integer which is the number of seconds to sleep and an integer
 *   which is a boolean for whether to interrupt the sleep or not.
 *
 * This function sleeps for X seconds.  It is interruptable.
 *
 * Returns a 1 if interrupted (and allowed to interrupt) or 0 when done
 *   sleeping.
 *
 * Note that this is not _really_ _truly_ _exact_ as it is does perform some
 *   condition checking inbetween each 100 milliseconds.  But...it's pretty
 *   darn close.
 */
int my_sleep(int length, int interrupt)
{
	int count = 0;
#ifndef VMS  /* PGE */
	struct timeval timeout;
#else
	unsigned char interval [8];   /* 64 bit VMS time */
	static char string_interval_data [] = "0 00:00:00.10";   /* 100msec. */
	$NEW_DESCRIPTOR(string_interval);

        string_interval.dsc$w_length = strlen(string_interval_data);
        string_interval.dsc$a_pointer = string_interval_data;

	/* Convert string time into binary format. */
        sys$bintim(&string_interval, &interval);
#endif

	sleep_interrupt = 0;

	length *= 1000000;

	while (count < length) {
#ifndef VMS  /* PGE */
		timeout.tv_sec = 0;
		timeout.tv_usec = 100000;
		select(0, NULL, NULL, NULL, &timeout);
#else
                /* Sleep for 100 msec. */
                sys$schdwk(0, 0, &interval, 0);
                sys$hiber();
#endif
		count += 100000;
		if (interrupt && sleep_interrupt)
			return(1);
	}
	return(0);
}


/*
 * Written by: Tommy Reilly
 *
 * Simple function that stat's a file to see if it exists. 
 *
 * Simple returns 1 or 0. 
 */
int file_exists(char *name)
{
  struct stat buf;

  if (!name)
      return(0);

#if defined(MULTINET) && defined(__alpha)
  if (!decc$stat(name, &buf))
#else
  if (!stat(name, &buf))
#endif
      return(1);

  return(0);
}

/*
 * Written by: Tommy Reilly (with major code snarfing from Scott Powers)
 *
 * This is essentially Scott's my_move function re-written without rename
 * and without erasing the source file. 
 *
 * If "overwrite" is true, the destination file will automatically be
 * overwritten.  If it is false and the file exists, my_move will return
 * SYS_FILE_EXISTS.  It is up to the programmer to tell the user this.
 *
 * Return Values:
 *   SYS_NO_SRC_FILE -- There was no source filename specified.
 *   SYS_NO_DEST_FILE -- There was no destination filename specified.
 *   SYS_DEST_EXISTS -- Overwrite was off and the destination exists.
 *   SYS_NO_MEMORY -- No memory to allocate with.
 *   SYS_SRC_OPEN_FAIL -- Open failed on the source file.
 *   SYS_INTERNAL_FAIL -- Error occured that user doesn't want to know about.
 *   SYS_SUCCESS -- Success.
 */
int my_copy(char *src, char *dest, char *retBuf, int bufsize, int overwrite)
{
  int status, fd_src, fd_dest;
  int n_src = 1;
  int n_dest = 1;
  char *copy_error;
  char buf[BUFSIZ];
  struct stat dest_stat;
  
  if (!retBuf)
      return(SYS_NO_RETBUF);

  if (!src || !*src) {
      strcpy(retBuf, "There was no source file specified.\n");
      return(SYS_NO_SRC_FILE);
  }
  if (!dest || !*dest) {
      strcpy(retBuf, "There was no destination file specified.\n");
      return(SYS_NO_DEST_FILE);
  }
  
  *retBuf = '\0';
  
  if (!overwrite) {
#if defined(MULTINET) && defined(__alpha)
      if (decc$stat(dest, &dest_stat)) {
#else
      if (stat(dest, &dest_stat)) {
#endif
	  sprintf(retBuf, "Stat [%s] error:\n    File already exists.\n", dest);
	  return(SYS_DEST_EXISTS);
      }
  }
  
#ifndef VMS  /* Must copy file header as well as contents. PGE */
  if ((fd_src = open(src, O_RDONLY)) == -1) {
      copy_error = strdup(my_strerror(errno));

      if (!copy_error) {
	  strcpy(retBuf, "There was not enough memory allocate.\n");
	  return(SYS_NO_MEMORY);
      }
      
      if (strlen(copy_error) > (bufsize - strlen(retBuf))) {
	  fprintf(stderr, "%s\n", copy_error);
      } else {
	  sprintf(retBuf, "%sCopy([%s] to [%s]) error:\n     %s\n\n",
		  retBuf, src, dest, copy_error);
      }
      free(copy_error);
      
      return(SYS_SRC_OPEN_FAIL);
  }
  if ((fd_dest = open(dest, O_WRONLY | O_CREAT, 0644)) == -1) {
      copy_error = strdup(my_strerror(errno));

      if (!copy_error) {
	  strcpy(retBuf, "There was not enough memory allocate.\n");
	  return(SYS_NO_MEMORY);
      }
      if (strlen(copy_error) > (bufsize - strlen(retBuf))) {
	  fprintf(stderr, "%s\n", copy_error);
      } else { 
	  sprintf(retBuf, "%sCopy([%s] to [%s]) error:\n     %s\n\n",
		  retBuf, src, dest, copy_error);
      }
      free(copy_error);
      
      close(fd_src);
      
      return(SYS_DEST_OPEN_FAIL);
  }
  
  /* Both files open and ready */
  while (n_src > 0) {
      n_src = read(fd_src, buf, BUFSIZ - 1);
      if (n_src > 0) {
	  n_dest = write(fd_dest, buf, n_src);
	  if (n_dest > 0) 
	      continue;
	  close(fd_src);
	  close(fd_dest);
	  sprintf(retBuf,
		  "Write([%s]) error:\n     %s\n\n", dest, my_strerror(errno));
	  return(SYS_WRITE_FAIL);
      }
      if (!n_src) 
	  continue;

      close(fd_src);
      close(fd_dest);
      sprintf(retBuf, "Read([%s]) error:\n     %s\n\n", src,my_strerror(errno));
      return(SYS_READ_FAIL);
  }
  close(fd_src);
  close(fd_dest);
  
  return(SYS_SUCCESS);

#else   /* VMS, PGE */
  /*
  ** Unix does copy in code for security reasons.  The dest could include
  ** "filename; command1; command2" and the commands would be executed.
  ** These concerns do not apply to VMS, because each line can only contain
  ** one command.  Also, the inline copy is much more difficult, due to the
  ** need to maintain VMS file types.
  */
  sprintf(buf, "copy$$/noconf/nolog %s %s", src, dest);
  status = my_system(buf, retBuf, bufsize);

  /* Purge previous versions back to number specified by overwrite, GEC */
  if (overwrite >= 1) {
      sprintf(buf, "purge$$/noconf/nolog/keep=%d %s", overwrite, dest);
      my_system(buf, retBuf, bufsize);
  }

  return status;
#endif  /* VMS, PGE */
} 


/*
 * Written by: Tommy Reilly (originally by Scott Powers I think)
 *
 * This is a function that finds the users home directory. 
 *
 * Return Values:
 *   SYS_NO_MEMORY -- No memory to allocate with.
 *   SYS_INTERNAL_FAIL -- Not an error that user would want to know about.
 *   SYS_SUCCESS -- Success.
 */
int get_home(char **ret)
{
#ifndef VMS
  char *home;
  struct passwd *pwdent;
  
  if (!(home = getenv("HOME"))) {
      if (!(pwdent = getpwuid(getuid()))) {
	  fprintf(stderr, "Could not find home dir.\n");
	  *ret = NULL;
	  return(SYS_INTERNAL_FAIL);
      } else {
	  home = strdup(pwdent->pw_dir);
      }
  } else {
	home = strdup(home);
  }
  if (home) {
      *ret = home;  /* He better free it */
  } else {
      *ret = NULL;
      return(SYS_INTERNAL_FAIL);
  }

#else  /* VMS */
  typedef struct {
    unsigned short int  length;
    unsigned short int  code;
    char               *buffer;
    unsigned short int *return_length;
  } ITEMLIST;

  int                status;
  ITEMLIST           item_list[2];
  char               translated_home_data[256];
  unsigned short int translated_home_length;
  static char        logical_table_data[] = "LNM$FILE_DEV";
  static char        home_data[] = "SYS$LOGIN";
  $NEW_DESCRIPTOR(logical_table);
  $NEW_DESCRIPTOR(home_logical);

  /* Setup values to pass into sys$trnlnm */
  logical_table.dsc$w_length = strlen(logical_table_data);
  logical_table.dsc$a_pointer = logical_table_data;
  home_logical.dsc$w_length = strlen(home_data);
  home_logical.dsc$a_pointer = home_data;

  /* Setup values to return from sys$trnlnm */
  item_list[0].code = LNM$_STRING;
  item_list[0].length = 256;
  item_list[0].buffer = translated_home_data;
  item_list[0].return_length = &translated_home_length;
  item_list[1].code = 0;
  item_list[1].length = 0;

  /* Translate SYS$LOGIN logical to DISK:[DIRECTORY] */
  status = sys$trnlnm (0, &logical_table, &home_logical, 0, item_list);
  if (status != 1) {
    fprintf(stderr, "sys$trnlnm status = %d\n", status);
    return(SYS_INTERNAL_FAIL);
  }

  /* Convert to null terminated C style string. */
  translated_home_data[translated_home_length] = '\0';

  *ret = strdup(translated_home_data);   /* He better free it */
  if (!*ret)
    return(SYS_NO_MEMORY);
#endif /* VMS, PGE */

  return(SYS_SUCCESS);
}
