/****************************************************************************
 * 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.                                                  *
 ****************************************************************************/
#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(__DECC) || (__VMS_VER < 70000000) || (__DECC_VER <= 50230003))) || defined(NeXT)
extern char *strdup(char *str);
#endif

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


#ifndef DISABLE_TRACE
int nutTrace=0;
#endif


int sleep_interrupt=0;


int my_system(char *cmd, char *retBuf, int bufsize);
char *my_strerror(int errornum);
char **buildArgv(char *cmd, int *new_argc);
char *findProgram(char *pname,char *spath);
int my_sleep(int length, int interrupt);


/*#define STANDALONE /* */
#undef STANDALONE /* */


#ifdef STANDALONE

#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
/* 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)
    return NULL;

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

  dup = strcpy(dup, str);

  return dup;
}
#endif

int main(int argc, char **argv) {

   int    retValue;
   char  *home_dir;
   char   buffer [256];
   char **argv2;
   int    argc2;

   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


extern char *userPath;


#endif


/*
 * Written by: Scott Powers and Brad Viviano
 *
 * Takes a string command, executes the command, 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!=NULL) {
			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);
   }

   {
      $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)
         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
 *
 * 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.
 *
 */
char *findProgram(char *pname,char *spath) {

#ifndef VMS  /* Always return NULL on VMS. PGE */

char *start=NULL,*ptr=NULL,*endptr=NULL;
char tryit[BUFSIZ];
struct stat buf;

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

	start=spath;
	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));
		}
	}

#endif /* VMS, PGE */

	return(NULL);
}


/*
 * 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, n_src=1, n_dest=1, fd_src, fd_dest, ret;
char *rename_error=NULL, *copy_error=NULL;
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 accross partitions*/
		rename_error=strdup(my_strerror(errno));
		if (!rename_error) {
			strcpy(retBuf,"There was no enough memory allocate.\n");
			return(SYS_NO_MEMORY);
		}

#if 0
		if ((fd_src=open(src,O_RDONLY))==(-1)) {
			copy_error=strdup(my_strerror(errno));
			if (!copy_error) {
				free(rename_error);
				strcpy(retBuf,"There was no enough memory allocate.\n");
				return(SYS_NO_MEMORY);
			}

			if (strlen(rename_error)>bufsize) {
				fprintf(stderr,"%s\n",rename_error);
			}
			else {
				sprintf(retBuf,"Rename([%s] to [%s]) error:\n     %s\n\n",src,dest,rename_error);
			}
			free(rename_error);

			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) {
				free(rename_error);
				strcpy(retBuf,"There was no enough memory allocate.\n");
				return(SYS_NO_MEMORY);
			}

			if (strlen(rename_error)>bufsize) {
				fprintf(stderr,"%s\n",rename_error);
			}
			else {
				sprintf(retBuf,"Rename([%s] to [%s]) error:\n     %s\n\n",src,dest,rename_error);
			}
			free(rename_error);

			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 file 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);
#endif

		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]);
	}
#else
        return(strerror(errornum));
#endif

        return(NULL);
}


/*
 * 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=NULL;
char *buf=NULL,*tmp=NULL;
int i=0;

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

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

	tmp=buf;

	new_argv=(char **)calloc(1,sizeof(char *));
	if (!new_argv) {
		free(buf);
		*new_argc=0;
		return(NULL);
	}

	new_argv[0]=NULL;

	while (*tmp) {
		if (!isspace(*tmp)) { /*found the begining of a word*/
			new_argv[i]=tmp;
			for (; *tmp && !isspace(*tmp); tmp++);
			if (*tmp) {
				*tmp='\0';
				tmp++;
			}
			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 of 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;
int num;

#ifndef VMS  /* PGE */
struct timeval timeout;
#else
unsigned char interval [8];   /* 64 bit VMS time */
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, n_src=1, n_dest=1, fd_src, fd_dest;
  char *copy_error=NULL;
  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);
  
#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 */

  return(SYS_SUCCESS);
} 


/*
 * 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  /* PGE */
  char *home = NULL;
  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 = malloc(sizeof(char) * (strlen(pwdent->pw_dir) + 1));

	  if(!home) {
	    *ret=NULL;
	    return(SYS_NO_MEMORY);
	  }

	  strcpy(home, pwdent->pw_dir);
*/
	  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, PGE */
  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;
  char               logical_table_data [] = "LNM$FILE_DEV";
  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)
  {
    printf ("Status = %d", 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);
}
