/****************************************************************************
 * 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 <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>


#include "system.h"


extern int sys_nerr;
extern char *sys_errlist[];
extern int errno;



int my_system(char *cmd, char *retBuf, int bufsize);
char *my_strerror(int errornum);
char **buildArgv(char *cmd, int *new_argc);



#undef STANDALONE
#ifdef STANDALONE

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

int retValue;
char *cmd, *fnam, *lpr;
char buf[BUFSIZ];

	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");
	}
}

#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) {

char **sys_argv=NULL;
int sys_argc;
pid_t pid;
int status,statusloc;
int fds[2];
char buf[BUFSIZ];

	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)) {
		perror("fcntl-nonblock");
		return(SYS_FCNTL_FAILED);
	}

	if ((pid=fork())==(-1)) {
		return(SYS_FORK_FAIL);
	}
	else if (pid==0) {
		/*in child*/
		sys_argv=buildArgv(cmd, &sys_argc);
		dup2(fds[1],1);
		dup2(fds[1],2);
		if (sys_argv!=NULL) {
			if (sys_argv[0][0]!='/') {
				printf("%s _has_ to be the full path to the executable.\n",sys_argv[0]);
				exit(1);
			}
			execv(sys_argv[0],sys_argv);
			printf("Exec of %s failed!\n",cmd);
			perror("exec");
		}
		else {
			printf("Could not build argv for [%s].\n",cmd);
		}
		exit(1);
	}
	else {
		/*in parent*/
		status=wait(&statusloc);
		read(fds[0],retBuf,bufsize-1);
		close(fds[0]);
		close(fds[1]);

		if (*retBuf) {
			return(SYS_PROGRAM_FAILED);
		}
/*
		status=(status>>8)&0x000000FF;
#if defined(_BSD) || defined(__alpha)
		if (statusloc) {
			strcat(retBuf,"An Error has occurred.\n");
			return(SYS_PROGRAM_FAILED);
		}
#else
		if (status) {
			strcat(retBuf,"An Error has occurred.\n");
			return(SYS_PROGRAM_FAILED);
		}
#endif
*/
		return(SYS_SUCCESS);
	}
}


/*
 * 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;
char *rename_error=NULL, *copy_error=NULL;
char buf[BUFSIZ];
struct stat *dest_stat;

	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);
	}
	if (!retBuf) {
		return(SYS_NO_RETBUF);
	}
	*retBuf='\0';

	if (!overwrite) {
		if (stat(dest,dest_stat)) {
			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 ((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);
	}

	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) {

        if (errornum<sys_nerr) {
                return(sys_errlist[errornum]);
	}

        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);
}
