/* (C) Tim Graves 20th April 1994
   This code is supplied AS IS. no warrantee either expressed or implied 
   is provided. This code may be freeley modified and modified as long as my
   origional authorship is acknowledged. 
   
   Tim Graves
    Sun Microsystems
     
     */
/* this module performs a backup of the files on the psion under path and stores it in backup, no input filtering is done (VAR_FILEFILT) is removed)*/

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "psion.h"
#include "psbackup.h"
static char vsn[] = "@(#) psbackup.c 2.11@(#)" ;
struct backupentry * backhead ;
int debugbackup = FALSE ;
int incremental = FALSE ;
extern int debugcall ;
int filecount, dircount ;
psrlist(path, fd)
char * path ;
FILE * fd ;
{
	if (debugcall >= BACKUPCALLDEBUG)
		printf("CALL: psrlist (path = %s)\n", path) ;
	if (findvar(VAR_VERBOSE) != NULL)
		printf("Generating a recursive listing of %s\n",path) ;
	/* force the incremental flag to be false as this is a listing */
	incremental = FALSE ;
	lowerpath(path) ;
	initbackuplist(path) ;
	backuplist(backhead,FALSE,"") ;
	printbackuplist(backhead, fd) ;
	closebackuplist(backhead) ;
}

psbackup(path,backupdir, incr)
char * path,* backupdir ;
{
	int filefilterwasset; 
	int ch ;
	time_t timestart, timeend ;
	if (debugcall >= BACKUPCALLDEBUG)
		printf("CALL: psbackup (path = %s, backupdir = %s, incr = %d)\n", path, backupdir, incr) ;	
		
	/* if backupnocheck is set dont ask the user to close all the files
	   This is potentialy dangerous as suncom can't get a file if it is
	   open */
	if (findvar(VAR_BACKUPNOCHECK) == NULL)
	{
		/* ask the user to ensure that all open files on the psion are closed */
		printf("BACKUP: Please ensure that all files on the psion are closed\n");
		printf("BACKUP: Press return to continue\n");
		ch = getchar();
	}
	/* is this incremental ? */
	incremental = incr ;
	/* reset the file/dir counts */
	filecount = 0 ;
	dircount = 0 ;
	/* get the start time */
	timestart = time(NULL) ;
	if (findvar(VAR_VERBOSE) != NULL)
	{
		printf("BACKUP: Starting %sbackup of %s to %s\n", incremental ? "incremental " : "", path,backupdir);
		printf("BACKUP: %s\n", ctime(&timestart)) ;
		printf("BACKUP: Stage 1, Building file list\n");
	}
	lowerpath(path) ;
	initbackuplist(path) ;
	/* was VAR_FILEFILT set ? if so unsetit */
	if (findvar(VAR_FILEFILT) != NULL)
	{
		filefilterwasset=TRUE ;
		delvar(VAR_FILEFILT) ;
	}
	/* build a list of files to backup using the excludlist*/
	backuplist(backhead,TRUE, backupdir) ;
	/* do the backup */
	if (findvar(VAR_VERBOSE) != NULL)
		printf("BACKUP: Stage 2, Getting files\n");
	dobackup(backupdir,backhead) ;
	/* free the accumulated data structures */
	closebackuplist(backhead) ;
	/* reset the VAR_FILEFILT if it was set */
	if (filefilterwasset)
		addvar(VAR_FILEFILT,"")  ;
	timeend = time(NULL) ;
	if (findvar(VAR_VERBOSE) != NULL)
	{
		printf("BACKUP: %s of %s to %s complete\n", incremental ? "Incremental backup" : "Backup", path, backupdir) ;
		printf("BACKUP: %s\n", ctime(&timeend)) ;
		printf("BACKUP: %d directories, %d files\n", dircount, filecount) ;
		printf("BACKUP: Time taken %d seconds\n", (int) timeend - timestart) ;
	}
		
}
backuplist(currnode,exclude, backupdir)
struct backupentry *currnode ;
int exclude ;
char * backupdir ;
{
	int i ;
	char tmp[100],  nodepath[120];
	FILE * fd ;
	int nodepathlen ;
	if (debugcall >= BACKUPCALLDEBUG)
		printf("CALL: backuplist (currnode->pname = %s, exclude = %d, backupdir = %s)\n", currnode->pname, exclude, backupdir) ;	
	/* are we using the excludelist ? if so is thie item on it ? */
	if (exclude)
	{
		if (currnode->exclude = findexclude(currnode->pname))
			return ;
	}
	/* if the current node is a file set the flg and and check
	   that the file mod has not changed, if so exclude it */
	if ((currnode->nodetype=backuppathtype(currnode->pname)) == BACKUPNODEFILE)
	{
		if (incremental)
			currnode->exclude = backupmodified(currnode->pname,backupdir) ;
		return ;
	}
	/* the current node is a directory, Lets work on it */
	/* build up the list command */
	strcpy(tmp, "list ");
	strcat(tmp,currnode->pname) ;
	/* remember we need a \ to make the search work */
	strcat(tmp,"\\");
	strcat(tmp," ");
	strcat(tmp,BACKUPLISTNAME) ;
	/* list the current node use cmdintr to do the work ! */
	cmdintr(tmp) ;
	/* we now have a list in BACKUPLISTNAME of all the items in this node lets get them !*/
	if ((fd = fopen(BACKUPLISTNAME,"r")) == NULL)
	{
		printf("ERROR: Error in opening backup temporary file quiting\n") ;
		psdc() ;
	}
	while (fgets(nodepath,120,fd) != NULL)
	/* for each item in the current node */
	{
		/* if the last char is newline, removeit, cmdintr
			doesnot like it */
		nodepathlen = strlen(nodepath) ;
		if (nodepath[nodepathlen -1] == '\n')
			nodepath[nodepathlen -1] = 0 ;
		if (debugbackup)
			printf("Now working on %s\n", nodepath) ;
		/* create a new node and install its name */
		lowerpath(nodepath) ;
		backupaddchild(currnode,nodepath) ;
	}
	fclose(fd) ;
	/* for each item in the current node */
	for (i = 0 ; i < currnode->count ; i ++)
	{
		/* recursively call backuplist to work on the new item */
		backuplist(currnode->children[i],exclude, backupdir) ;
	}
}
backuppathtype(path)
char * path ;
{
	int atts ;
	pssetpath(path) ;
	atts = psgetatts() ;
	if (atts & PSFILEDIR)
		return(BACKUPNODEDIR) ;
	else
		return(BACKUPNODEFILE );
}
struct backupentry * backupnode(path) 
char * path ;
{
	int i ;
	struct backupentry * ret ;
	if (debugcall >= BACKUPCALLDEBUG)
		printf("CALL: backupentry (path = %s)\n", path) ;
	/* create the ctructure */
	ret = (struct backupentry *) calloc (1,sizeof(struct backupentry)) ;
	/* fill in the name */
	strcpy(ret->pname,path) ;
	/* setup the next level stuff */
	ret->count = 0 ;
	/* ensure that we done know what type of node this is */
	ret->nodetype = BACKUPNODEUNKNOWN ;
	/* initialy thie node is not excluded */
	ret->exclude = FALSE ;
	if (debugbackup)
		printf("BACKUP: adding %s\n", path) ;
	return (ret) ;
}

initbackuplist(path)
char * path ;
{
	/* create the first entry */
	if (debugcall >= BACKUPCALLDEBUG)
		printf("CALL: initbackuplist ()\n") ;
	backhead = backupnode(path) ;
}
backupaddchild(currnode,nodepath) 
char * nodepath ;
struct backupentry * currnode ;
{
	if (debugcall >= BACKUPCALLDEBUG)
		printf("CALL: backupaddchild (currnode->pname, nodepath = %s)\n", currnode->pname, nodepath) ;
	/* have we exceeded the max no of children a node can have ? */
	if (currnode->count >=MAXBACKUPNODECHILDREN )
	{
		printf("BACKUP: Error in adding (%s) to node (%s), no space in node, this path will not be backed up\n", nodepath, currnode->pname) ;
		return ;
	}
	currnode->children[currnode->count] = backupnode(nodepath) ;
	currnode->count ++ ;
}
closebackuplist (currnode)
struct backupentry * currnode ;
{
	if (debugcall >= BACKUPCALLDEBUG)
		printf("CALL: closebackuplist (currnode->pname = %s)\n", currnode->pname) ;
	/* for each entry in the current node kill the children */
	for (;currnode->count > 0 ; currnode->count --)
		closebackuplist(currnode->children[currnode->count-1]) ;
	/* no children left, free ourselves */
	free (currnode) ;
}
printbackuplist(currnode, fd)
struct backupentry * currnode ;
FILE * fd ;
{
	int i ;
	if (debugcall >= BACKUPCALLDEBUG)
		printf("CALL: printbackuplist (currnode->pname = %s)\n", currnode->pname) ;

	/* print the curent node name and then do eack of the cuildren */
	if ( fd == NULL)
		printf("%s\n",currnode->pname) ;
	else
		fprintf(fd, "%s\n", currnode->pname) ;
		
	/* for each entry in the current print the children */
	for (i = 0;i < currnode->count  ; i ++)
		printbackuplist(currnode->children[i], fd) ;
	/* no children left return */
	return ;
}
dobackup(path,currnode)
char * path ;
struct backupentry * currnode ;
{
	int i ;
	char * tmp ;
	char targetpath [1024] ;
	char cmd[2048] ;
	if (debugcall >= BACKUPCALLDEBUG)
		printf("CALL: dobackup (path = %s, currnode->pname = %s)\n",path, currnode->pname) ;
	/* check that the current node is not excluded */
	if (currnode->exclude)
		return ;

	/* if the current entry is a file get it and return */
	if (currnode->nodetype == BACKUPNODEFILE)
	{
		/* get it */
		if (debugbackup)
			printf("BACKUP: Getting %s\n", currnode->pname) ;
		/* build up the command to do the get */
		strcpy (cmd, "get ") ;
		strcat (cmd, currnode->pname) ;
		/* its now safe to call the path munger as we have a copy of the
		   path in cmd */
		backupbuildpath(currnode->pname, path, targetpath) ;
		strcat(cmd, " ") ;
		strcat(cmd,targetpath) ;
		if (debugbackup)
			printf("backup, doing %s\n",cmd) ;
		cmdintr(cmd) ;
		/* increment the filecount */
		filecount ++ ;
		return ;
	}
	else
	{
		/* the current entry is a directory, make it in the filesystem and then look at all its children */
		/* make the directory */
		if (debugbackup)
			printf("BACKUP: Making dir %s\n",currnode->pname) ;
		/* build up the command to do the mkdir */
		strcpy (cmd, "mkdir -p  ") ;
		backupbuildpath(currnode->pname, path, targetpath) ;
		strcat(cmd,targetpath) ;
		if (debugbackup)
			printf("backup, doing system(%s)\n", cmd) ;
		system(cmd) ;
		/* for each entry in the current get  the children */
		for (i = 0;i < currnode->count  ; i ++)
			dobackup(path,currnode->children[i]) ;
		/* no children left increment the dircount and return */
		dircount ++ ;
		return ;
	}
}

backupbuildpath(path, root, target)
char * path, *root, *target ;
{
	int i ;
	/* path is the path to munge (remove drive:, change all \ to /) */
	/* root is the unix directory at the top of this tree */
	/* target is the place to put the resultant string */
	/* is there a ?: at the start ? if so remove it */
	if (path[1] == ':')
		path += 2 ;
	
	/* change the \ to / */
	for (i = 0 ; i < (int) strlen(path) ; i ++ )
		if (path[i] == '\\') 
		{
			path[i] = '/' ;
		}
	/* build the output name */
	strcpy(target, root) ;
	strcat(target,path) ;
}
long psgetmodtime() ;
backupmodified(psname, backupdir)
char * psname,  *backupdir ;
{
	char sunpath [1024] ;
	char tmp [100] ;
	long pstime , suntime ;
	struct stat sunstat ;
	int ret ;
	if (debugcall >= BACKUPCALLDEBUG)
		printf("CALL: backupmodified (psname = %s, backupdir = %s)\n",psname, backupdir) ;

	/* has the file on the psion been changes more recently 
	   than the backup ? */
	
	pssetpath(psname) ;
	pstime = psgetmodtime() ;
	
	/* create a temporary copy of the psname so we can work on it */
	strcpy(tmp, psname) ;
	/* get the sunname for this */
	backupbuildpath(tmp, backupdir, sunpath) ;
	
	/* get the mod time of the sun file if it exists */
	ret = stat(sunpath, &sunstat) ;
	if (ret != 0)
		return(FALSE) ;
		
#ifndef SVR4
	if (debugbackup)
		printf("ret = %d suntime = %ld pstime = %ld\n", ret, sunstat.st_mtime, pstime) ;
	if ((pstime + CLOCKDIFF) < sunstat.st_mtime)
		return(TRUE) ;
	else
		return(FALSE) ; 
#else
	if (debugbackup)
		printf("ret = %d suntime = %ld pstime = %ld\n", ret, sunstat.st_mtim.tv_sec, pstime) ;

	if ((pstime + CLOCKDIFF) < sunstat.st_mtim.tv_sec)
		return(TRUE) ;
	else
		return(FALSE) ; 
#endif
}
