/* Filename:	restore.c
 * Author:		Mark R. Rinfret
 * Date:		08/02/87
 * Description:	Restore processing module for MRBackup
 *
 * History:		(most recent change first)
 *
 * 09/19/87 -MRR- Added NewHomeDir() which creates subdirectories as
 *                necessary when the initial backup path specifies a
 *                subdirectory.
 */


#include "MRBackup.h"

#ifdef DEBUG
extern char debugmsg[];
#endif

static char fullbackpath[256], fullhomepath[256];

static unsigned home_is_device;		/* true => home is "DH<N>:" */


/* Create a new directory on the home device.
 * Called with:
 *		name:		directory pathname
 * Returns:
 *		false => success
 *		true  => failure
 */
int
NewHomeDir(name)
	char   *name;
{
	char c;
	struct Lock *dirlock;
	int dirleng;
	int errnum;
	char dirname[256];
	int nameindx = 0, nameleng;

	*dirname = '\0';
	dirleng = 0;
	nameleng = strlen(name);

	/* Parse the pathname, one directory node at a time, creating
	 * directories as needed.
	 */

	while (nameindx < nameleng) {
		if (nameindx)				/* 2nd - nth pass? */
			dirname[dirleng++] = '/'; /* directory separator */
		while ((c = name[nameindx++]) && c != '/')
			dirname[dirleng++] = c;
		dirname[dirleng] = '\0';	/* terminate with null */
		if (dirlock = Lock(dirname,SHARED_LOCK)) /* subdir exists? */
			UnLock(dirlock);
		else {						/* create subdirectory */
			if ((dirlock = CreateDir(dirname))== NULL){
				if ((errnum = IoErr())== ERROR_DIRECTORY_NOT_EMPTY){
					sprintf(conmsg,
						"Directory %s already exists!\n",dirname);
					TypeAndSpeak(conmsg);
				}
				else {
					sprintf(conmsg,
						"ERROR %d: Unable to create directory %s\n",
						errnum,dirname);
					TypeAndSpeak(conmsg);
					return errnum;
				}
			}
			else
				UnLock(dirlock);
		}
	}								/* endwhile */
	return 0;
}
/* Restore files from floppy disk. */

int
Restore()
{
	int status = 0;

	Speak("And away we go!");

	if (!IsDir(backpath)) {
		TypeAndSpeak("Backup path must be a device or directory name!\n");
		return ERR_ABORT;
	}

	BreakPath(backpath, srcvol, srcpath);

	home_is_device = (homepath[strlen(homepath)-1] == ':');

	status = RestoreFile(srcpath);
	if (status == 0) {
		TypeAndSpeak("Your restoration project is completed, sire.\n");
	}
	else {
		sprintf(conmsg,"Restore terminated with status %d.\n",status);
		TypeAndSpeak(conmsg);
		TypeAndSpeak(
			"Perhaps you should check things out and try it again.\n");
	}
	return status;
}

/* Restore all the files in a directory.
 * Called with:
 *		lock:		lock on the directory
 *		fib:		pointer to file info block
 *		path:		directory pathname (without volume)
 * Returns:
 *		status (0 => success)
 */
int
RestoreDir(lock, fib, path)
	struct Lock *lock; struct FileInfoBlock *fib; char *path;
{
	struct Lock *dirlock = NULL, *filelock = NULL;
	char newpath[256];
	int status = 0;

	strcpy(temp, homepath);
	if (*path) {
		if (!home_is_device) strcat(temp, "/");
		strcat(temp, path);
	}
#ifdef DEBUG
	sprintf(debugmsg,"Checking for directory %s\n",temp);
	DebugWrite(debugmsg);
#endif
	if (!(dirlock = Lock(temp, SHARED_LOCK))) {
		if ((status = IoErr()) == ERROR_OBJECT_NOT_FOUND) {
#ifdef DEBUG
			sprintf(debugmsg,"Creating directory %s\n",temp);
			DebugWrite(debugmsg);
#endif
			if (status = NewHomeDir(temp))
				return status;
		}
		else {
			sprintf(conmsg,"RestoreDir cannot lock %s: %d\n",temp, status);
			TypeAndSpeak(conmsg);
			return status;
		}
	}
	if (dirlock) UnLock(dirlock);

	while (ExNext(lock,fib)) {
		strcpy(newpath, path);
		if (*newpath)
			strcat(newpath, "/");
		strcat(newpath, fib->fib_FileName);
		if (status = RestoreFile(newpath)) {

			/* filter out "permissable:" errors */

			if (status == ERROR_OBJECT_IN_USE ||
				status == ERROR_WRITE_PROTECTED)
				status = 0;
			else
				break;
		}
	}
done:
	return status;
}

/* Restore one or more files according to the calling pathname.
 * The path argument does not contain the backup volume name.
 */
int
RestoreFile(path)
	char *path;
{
	struct FileInfoBlock *fib = NULL, *fib2 = NULL;
	UBYTE exists = FALSE, ignore = FALSE;
	struct Lock *lock = NULL;
	USHORT namelength;
	UBYTE savechar;
	int status = 0;

	if (status = CheckStop()) return status;

	if (!(fib = (struct FileInfoBlock *)
		AllocMem((long) sizeof (struct FileInfoBlock), 
					MEMF_PUBLIC | MEMF_CHIP))) {
		TypeAndSpeak("RestoreFile could not allocate FIB!\n");
		return ERROR_NO_FREE_STORE;
	}

	sprintf(fullbackpath, "%s:%s",srcvol,path);
	strcpy(fullhomepath, homepath);
	if (*path) {
		if (!home_is_device) strcat(fullhomepath, "/");
		strcat(fullhomepath, path);
	}

#ifdef DEBUG
		sprintf(conmsg,"fullbackpath = %s\n",fullbackpath);
		DebugWrite(conmsg);
		sprintf(conmsg,"fullhomepath = %s\n",fullhomepath);
		DebugWrite(conmsg);
#endif

	if (!(lock = Lock(fullbackpath, SHARED_LOCK))) {
		status = IoErr();
		sprintf(conmsg, "RestoreFile: can't lock %s: %d\n",
				fullbackpath, status);
		TypeAndSpeak(conmsg);
		goto done;
	}

	if (!Examine(lock, fib)) {
		status = IoErr();
		sprintf(conmsg, "RestoreFile can't examine %s: %d\n", 
				fullbackpath, status);
		TypeAndSpeak(conmsg);
		goto done;
	}

	if (fib->fib_DirEntryType > 0) {	/* path is a directory */
		status = RestoreDir(lock, fib, path);
		UnLock(lock);
		lock = NULL;
	}
	else {
		UnLock(lock);
		lock = NULL;
/*#define NOCOPY*/
#ifndef NOCOPY
		/* If this file exists, then check its modification date.  If
		 * it's newer than the backup, don't replace it.
		 */

		if ((lock = Lock(fullhomepath, SHARED_LOCK))) {

			if (!(fib2 = (struct FileInfoBlock *)
				AllocMem((long) sizeof (struct FileInfoBlock), 
							MEMF_PUBLIC | MEMF_CHIP))) {
				TypeAndSpeak("RestoreFile could not allocate FIB!\n");
				status = ERROR_NO_FREE_STORE;
				goto done;
			}
			Examine(lock, fib2);
			UnLock(lock);
			lock = NULL;
			if (CompareDS(&fib2->fib_Date, &fib->fib_Date) >= 0)
				ignore = TRUE;
		}
		if (ignore) {
			sprintf(conmsg,"Skipping %s.  Current version is newer.\n",
					path);
			TypeAndSpeak(conmsg);
		}
		else {
			if (!do_compress || !IsCompressed(fullhomepath)) {
copyfile:
				sprintf(conmsg,"Copying %s\n", fullbackpath);
				WriteConsole(conmsg);
				status = CopyFile(fullbackpath, fullhomepath);
			}
			else {
				/* truncate the destination pathname (remove ".z") */

				namelength = strlen(fullhomepath);
				fullhomepath[namelength-2] = '\0';
				sprintf(conmsg, "Decompressing %s\n", fullbackpath);
				WriteConsole(conmsg);
				if (status = decompress(fullbackpath, fullhomepath)) {
					sprintf(conmsg, 
						"Decompression of %s failed;  status is %d.\n",
						fullbackpath, status);
					TypeAndSpeak(conmsg);
					TypeAndSpeak("I will try to copy the file, instead.\n");
					/* restore ".z" to name */
					fullhomepath[namelength-2] = '.'; 
					goto copyfile;
				}
				CopyFileDate(fullbackpath, fullhomepath);
			}
		}
#endif
	}
done:
	if (lock) UnLock(lock);
	if (fib) FreeMem(fib, (long) sizeof(struct FileInfoBlock));
	if (fib2) FreeMem(fib2, (long) sizeof(struct FileInfoBlock));
	return status;
}
