/*******************************\
* LCOM.C			*
* (C)1986-87, Patrick E. Hughes	*
\*******************************/

#include <exec/types.h>
#include <stdio.h>
#include <stat.h>
#include <fcntl.h>
#include <intuition/intuition.h>
#include <time.h>
#include "System.h"
#include "Defines.h"

/***********************\
* GLOBAL VARIABLE LIST	*
\***********************/

extern struct System System;

extern int Boards_Are_Active, Libraries_Are_Active;
extern int Bulletins_Are_Active, Mail_Is_Active;
extern int Whence_The_Logon;
extern struct User User;

static struct stat statstruct;

/********************/
int LCom()
{
int ch, status, command_accepted, ListNumber;
struct Library_Header *lh;
char string[81];

if(Libraries_Are_Active==0) { return(FAILURE); }

StatRollMessage("Libraries");
PutStr("Library Directories\r\n");

FOREVER
	{
	Check_Online_Status();
	PutStr("\r\nN>ew  S>ome  A>ll  L>ist  Q>uit\r\n");
	sprintf(string,"%s Library Choices? ",System.Name);
	PutStr(string);
	FOREVER
		{
		ch=ReadChar(120L);
		if(ch==TIMEOUT) { return(ch); }
		Check_Online_Status();
		switch(ch)
			{
			case 'a': /* ALL */
			case 'A':
				PutStr("ALL Library Directories\r\n\r\n");
				lh=System.Libraries_List;
				ListNumber=0;
				do{
				    if((User.Sec_Library>=lh->Download_Low &&
				      User.Sec_Library<=lh->Download_High) ||
				      (User.Sec_Library>=lh->Upload_Low &&
				      User.Sec_Library<=lh->Upload_High)){
				      	sprintf(string,"[%d] %s\r\n",
						++ListNumber,lh->Name);
					PutStr(string);
						status=Lib_Prompt(lh,ListNumber);
						if(status==TIMEOUT ||
						   status==NO_CARRIER)
							return(status);
						if(status=='@')
							{
							return(status);
							}
					}
					Check_Online_Status();
					lh=lh->Next_Library;
				}while(lh!=NULL);
				PutStr("Completed visiting ALL\r\n");
				command_accepted=SUCCESS;
				break;
			case 'n': /* NEW ONLY */
			case 'N':
				PutStr("NEW Library Directories\r\n\r\n");
				lh=System.Libraries_List;
				ListNumber=0;
				do{
				    if(User.Sec_Library>=lh->Download_Low &&
				      User.Sec_Library<=lh->Download_High &&
					   User.Time_Last_On<lh->Latest_Time){
					   sprintf(string,"\r\n[%d] %s\r\n",
						++ListNumber,lh->Name);
						PutStr(string);
						status=Lib_ReadNew(lh);
						if(status==TIMEOUT ||
						   status==NO_CARRIER)
							return(status);
						if(status=='@')
							{
							return(status);
							}
						status=Lib_Prompt(lh,ListNumber);
						if(status==TIMEOUT ||
						   status==NO_CARRIER)
							return(status);
						if(status=='@')
							{
							return(status);
							}
					}
					Check_Online_Status();
					lh=lh->Next_Library;
				}while(lh!=NULL);
				PutStr("\r\nCompleted visiting NEW\r\n");
				command_accepted=SUCCESS;
				break;
			case 's': /* selective */
			case 'S':
				PutStr("SOME Library Directories\r\n");
				lh=System.Libraries_List;
				ListNumber=0;
				do{
				    if((User.Sec_Library>=lh->Download_Low &&
				      User.Sec_Library<=lh->Download_High) ||
				      (User.Sec_Library>=lh->Upload_Low &&
				      User.Sec_Library<=lh->Upload_High)){
					   	sprintf(string,
							"\r\nVisit [%d] %s ?",
							++ListNumber,lh->Name);
						PutStr(string);
						ch=ReadChar(120L);
						if(ch==TIMEOUT)
							{
							return(ch);
							}
						if(toupper(ch)=='Y'){
							PutStr("Yes\r\n");
						status=Lib_Prompt(lh,ListNumber);
							if(status==TIMEOUT ||
							   status==NO_CARRIER)
							   {
							   return(status);
							   }
						}
						if(toupper(ch)=='Q')
							{
							PutStr("Quit\r\n");
							break;
							}
						if(ch=='@')
							{
							return('@');
							}
						PutStr("No\r\n");
					}
					Check_Online_Status();
					lh=lh->Next_Library;
				}while(lh!=NULL);
				PutStr("\r\nCompleted visiting SOME\r\n");
				command_accepted=SUCCESS;
				break;
			case 'L': /* LIST */
			case 'l':
				PutStr("LIST Library Directories\r\n\r\n");
				lh=System.Libraries_List;
				ListNumber=0;
				do{
				    if((User.Sec_Library>=lh->Download_Low &&
				      User.Sec_Library<=lh->Download_High) ||
				      (User.Sec_Library>=lh->Upload_Low &&
				      User.Sec_Library<=lh->Upload_High)){
						sprintf(string,
						"[%d] %s\r\n",
						++ListNumber,lh->Name);
						PutStr(string);
					}
					lh=lh->Next_Library;
				}while(lh!=NULL);
				PutStr("\r\nCompleted LIST\r\n");
				command_accepted=SUCCESS;
				break;
			case 'Q': /* outta here */
			case 'q':
				PutStr("QUIT to Main\r\n");
				return(SUCCESS);
				break;
			case '@': /* main */
				PutStr("@ Main Menu\r\n");
				return(SUCCESS);
				break;
			case '?':
			case '/':
				PutStr("Command Summary\r\n");
				strcpy(string,System.Location);
				strcat(string,"Libraries.Help");
				status=MenuSend(string);
				if(status==TIMEOUT || status==NO_CARRIER)
					return(status);
				command_accepted=SUCCESS;
				break;
			case '\000': /* FN-KEY HIT */
				command_accepted=SUCCESS;
				PutStr("\r\n");
				break;
			default:
				PurgeLine();
				command_accepted=FAILURE;
			}
		Check_Online_Status();
		if(command_accepted==FAILURE) { continue; }
		break;
		}
	}
}

/*********************/
static int Lib_Prompt(lh,LibraryNumber)
	struct Library_Header *lh;
	int LibraryNumber;
{
int ch, status, command_accepted;
char string[133];

FOREVER
	{
	Check_Online_Status();
PutStr("\r\nN>ew L>ist C>atalog U>pload D>ownload Q>uit @>MainMenu\r\n");
	if(User.Sec_Status==255)
		{
		PutStr("R>emovals\r\n");
		}
	sprintf(string,"[%d] %s, Directory Options? ",LibraryNumber,lh->Name);
	PutStr(string);
	FOREVER
		{
		ch=ReadChar(120L);
		if(ch==TIMEOUT || ch==NO_CARRIER) { return(ch); }
		Check_Online_Status();
		switch(ch)
			{
			case 'n': /* NEW READING */
			case 'N':
				if(User.Sec_Library<lh->Download_Low ||
				   User.Sec_Library>lh->Download_High){
					command_accepted=FAILURE;
					break;
				}
				status=Lib_ReadNew(lh);
				if(status=='@') { return(status); }
				command_accepted=SUCCESS;
				break;
			case 'r': /* REMOVAL */
			case 'R':
				if(User.Sec_Status<150){
					command_accepted=FAILURE;
					break;
				}
				status=Lib_Delete_Frontend(lh);
				if(status=='@') { return(status); }
				command_accepted=SUCCESS;
				break;
			case 'c': /* CATALOG */
			case 'C':
				if(User.Sec_Library<lh->Download_Low ||
				   User.Sec_Library>lh->Download_High){
					command_accepted=FAILURE;
					break;
				}
				status=Lib_Catalog(lh);
				if(status=='@') { return(status); }
				command_accepted=SUCCESS;
				break;
			case 'l': /* LIST */
			case 'L':
				if(User.Sec_Library<lh->Download_Low ||
				   User.Sec_Library>lh->Download_High){
					command_accepted=FAILURE;
					break;
				}
				status=Lib_Read_Frontend(lh);
				if(status=='@') { return(status); }
				command_accepted=SUCCESS;
				break;
			case 'u': /* UPLOAD */
			case 'U':
				if(User.Sec_Library<lh->Upload_Low ||
				   User.Sec_Library>lh->Upload_High){
					command_accepted=FAILURE;
					break;
				}
				status=Lib_Upload(lh);
				if(status==SUCCESS)
					{
					Append_Stat(STAT_UPLOAD);
					++User.Uploads;
					}
				if(status=='@') { return(status); }
				command_accepted=SUCCESS;
				break;
			case 'd': /* DOWNLOAD */
			case 'D':
			case 'i':
			case 'I':
				if(User.Sec_Library<lh->Download_Low ||
				   User.Sec_Library>lh->Download_High){
					command_accepted=FAILURE;
					break;
				}
				status=Lib_Immediate(lh);
				if(status=='@') { return(status); }
				command_accepted=SUCCESS;
				break;
			case 'Q': /* exit */
			case 'q':
				PutStr("Quit to next\r\n");
				return(SUCCESS);
				break;
			case '@': /* main */
				PutStr("@ Main Menu\r\n");
				return('@');
				break;
			case '?':
			case '/':
				PutStr("Command Summary\r\n");
				strcpy(string,System.Location);
				strcat(string,"LibPrompt.Help");
				status=MenuSend(string);
				command_accepted=SUCCESS;
				break;
			case '\000': /* FN-KEY HIT */
				command_accepted=SUCCESS;
				PutStr("\r\n");
				break;
			default:
				PurgeLine();
				command_accepted=FAILURE;
				break;
			}
		if(command_accepted==TIMEOUT) { return(TIMEOUT); }
		if(command_accepted==NO_CARRIER) { return(NO_CARRIER); }
		if(command_accepted==FAILURE) { continue; }
		break;
		}
	}
}

/********************/
static int Lib_ReadNew(lh)
	struct Library_Header *lh;
{
struct Library_Data *ld;
int number;

PutStr("New Files\r\n");

if(lh->Highest_File==0)
	{
	PutStr("No files in this directory\r\n");
	return(SUCCESS);
	}

if(lh->Latest_Time<=User.Time_Last_On)
	{
	PutStr("Nothing new today\r\n");
	return(SUCCESS);
	}

for(number=0;number<lh->Highest_File;++number)
	{
	ld=lh->Start_of_Data;
	ld+=number;
	if(ld->Time>User.Time_Last_On) { break; }
	}

return(Lib_Read(lh,number+1,lh->Highest_Post));
}

/********************/
static int Lib_Read(lh,from,to)
	struct Library_Header *lh;
	int from,to;
{
struct Library_Data *ld;
int ch, number, status, fd, count;
long seekstat, lslot, lsize;
char filename[133], string[81], *timestr;

--from;
--to;

for(number=from;number<=to && number<lh->Highest_File;++number)
	{
	Check_Online_Status();
	ld=lh->Start_of_Data;
	ld+=number;

	/* SEND THE MESSAGE ITSELF */
	status=Send_Message(lh,ld,number+1);
	if(status==TIMEOUT || status==NO_CARRIER) { close(fd); return(status); }

	/* BETWEEN MESSAGE PROMPT */
	status=Between_Lib_Prompt(lh,ld,number+1);
	if(status==TIMEOUT || status==NO_CARRIER) { return(status); }
	if(status==0) { break; } /* QUIT */
	if(status==1) { continue; } /* CONTINUE */
	if(status==2) { --number; continue; } /* AGAIN */
	if(status==3 && number>0) { number-=2; continue; } /* BACKWARD */
	if(status==3 && number==0){ --number; continue; } /* defaults AGAIN */
	if(status=='@') { return('@'); }
	}
}

/***********************/
static int Send_Message(lh,ld,number)
	struct Library_Header *lh;
	struct Library_Data *ld;
	int number;
{
int count, ch, more;
char status, *ptr, *timestr, output[133], string[81];

/* SEND OUT HEADER INFO */
sprintf(string,"\r\nFile Number: [%hu] of [%hu]\r\n",number,lh->Highest_File);
PutStr(string);
sprintf(string,"      File Name: %s\r\n",ld->Filename);
PutStr(string);
sprintf(string,"         Origin: %s [%hu]\r\n",ld->Personname,ld->Person_Code);
PutStr(string);
timestr=ctime(&ld->Time);
timestr[strlen(timestr)-1]='\0';
sprintf(string," Time Validated: %s\r\n",timestr);
PutStr(string);
if(strlen(ld->Description)>0)
	{
	sprintf(string,"%s\r\n",ld->Description);
	PutStr(string);
	}
else
	{
	PutStr("No description available\r\n");
	}

SendMark1:
if(Whence_The_Logon>=REMOTE_LOGON)
	{
	status=CheckCarrier();
	if(status==FALSE)	return(NO_CARRIER);
	}

Check_Online_Status();
return(SUCCESS);
}

/**********************/
static int Flush_Keys(lh)
	struct Library_Header *lh;
{
char string[133];
int fd, status;

strcpy(string,lh->Location);
strcat(string,"Library.Keys");
fd=open(string,O_WRONLY);
if(fd==(-1))
	{
	PutStr("File ");
	PutStr(string);
	PutStr(" can not be opened, possible corrupt data\r\n");
	return(FAILURE);
	}
status=write(fd,lh->Start_of_Data,lh->Maximum*sizeof(struct Library_Data));
if(status!=lh->Maximum*sizeof(struct Library_Data))
	{
	PutStr("File ");
	PutStr(string);
	PutStr(" can not be written, possible corrupt data\r\n");
	close(fd);
	return(FAILURE);
	}
status=close(fd);
if(status==(-1))
	{
	PutStr("File ");
	PutStr(string);
	PutStr(" can not be closed, possible corrupt data\r\n");
	return(FAILURE);
	}

return(SUCCESS);
}

/****************************/
static int Lib_Read_Frontend(lh)
	struct Library_Header *lh;
{
USHORT from, to, status;
char string[81], holder[6];

PutStr("List Files\r\n");

if(lh->Highest_File==0)
	{
	PutStr("There are no files in this directory\r\n");
	return(SUCCESS);
	}

Read_Front1:
Check_Online_Status();
sprintf(string,"\r\nList Files FROM [1,%hu]? ",lh->Highest_File);
PutStr(string);
status=LineInput("",string,5,120L);
if(status==TIMEOUT) { return(status); }
from=atoi(string);
if(from==0) { return(SUCCESS); }
if(from>lh->Highest_File)
	{
    sprintf(string,"Too high. %hu is the highest File\r\n",lh->Highest_File);
	PutStr(string);
	goto Read_Front1;
	}

Read_Front2:
Check_Online_Status();
sprintf(string,"  List Files TO [%hu,%hu]? ",from,lh->Highest_File);
PutStr(string);
status=LineInput("",string,5,120L);
if(status==TIMEOUT) { return(status); }
to=atoi(string);
if(to==0) { goto Read_Front1; }
if(to>lh->Highest_File)
	{
    sprintf(string,"Too high. %hu is the highest file\r\n",lh->Highest_File);
	PutStr(string);
	goto Read_Front2;
	}
if(to<from)
	{
	sprintf(string,"Too low. %hu is the lowest\r\n",from);
	PutStr(string);
	goto Read_Front2;
	}

return(Lib_Read(lh,from,to));
}

/****************************/
static int Lib_Immediate(lh)
	struct Library_Header *lh;
{
int from, status;
char string[81], holder[6];

PutStr("Immediate List (Download)\r\n");

if(lh->Highest_File==0)
	{
	PutStr("There are no files in this directory\r\n");
	return(SUCCESS);
	}

Immediate_Front1:
Check_Online_Status();
PutStr("\r\nNumber or blank line\r\n");
sprintf(string,"Immediate/Download Which [1,%hu]? ",lh->Highest_File);
PutStr(string);
status=LineInput("",string,5,120L);
if(status==TIMEOUT) { return(status); }
from=atoi(string);
if(from==0) { return(SUCCESS); }
if(from>lh->Highest_File)
	{
    sprintf(string,"Too high. %hu is the highest file\r\n",lh->Highest_File);
	PutStr(string);
	goto Immediate_Front1;
	}
if(from<1)
	{
    sprintf(string,"Too low. 1 is the lowest\r\n");
	PutStr(string);
	goto Immediate_Front1;
	}

return(Lib_Read(lh,from,from));
}

/****************************/
static int Lib_Delete_Frontend(lh)
	struct Library_Header *lh;
{
int from, to, status;
char string[81], holder[6];

PutStr("Remove Files\r\n");

if(lh->Highest_File==0)
	{
	PutStr("There are no files in this directory\r\n");
	return(SUCCESS);
	}

Delete_Front1:
Check_Online_Status();
sprintf(string,"\r\nRemove FROM [1,%hu]? ",lh->Highest_File);
PutStr(string);
status=LineInput("",string,5,120L);
if(status==TIMEOUT) { return(status); }
from=atoi(string);
if(from==0) { return(SUCCESS); }
if(from>lh->Highest_File)
	{
    sprintf(string,"Too high. %hu is the highest file\r\n",lh->Highest_File);
	PutStr(string);
	goto Delete_Front1;
	}
Delete_Front2:
Check_Online_Status();
sprintf(string,"  Remove TO [%hu,%hu]? ",from,lh->Highest_File);
PutStr(string);
status=LineInput("",string,5,120L);
if(status==TIMEOUT) { return(status); }
to=atoi(string);
if(to==0) { goto Delete_Front1; }
if(to>lh->Highest_File)
	{
    sprintf(string,"Too high. %hu is the highest file\r\n",lh->Highest_File);
	PutStr(string);
	goto Delete_Front2;
	}
if(to<from)
	{
	sprintf(string,"Too low. %hu is the lowest\r\n",from);
	PutStr(string);
	goto Delete_Front2;
	}

return(Lib_Delete(lh,from,to));
}

/********************/
static int Between_Lib_Prompt(lh,ld,number)
	struct Library_Header *lh;
	struct Library_Data *ld;
	int number;
{
int ch, status, command_accepted;
char str[133];

FOREVER
	{
	Check_Online_Status();
PutStr("\r\nA>gain N>ext L>ast S>tatistics D>ownload Q>uit @>MainMenu\r\n");
	if(User.Sec_Status==255)
		{
		PutStr("R>emove\r\n");
		}
	PutStr("File Option? ");
	FOREVER
		{
		ch=ReadChar(120L);
		if(ch==TIMEOUT) { return(ch); }
		if(Whence_The_Logon>=REMOTE_LOGON)
			{
			status=CheckCarrier();
			if(status==FALSE)	return(NO_CARRIER);
			}
		switch(ch)
			{
			case 'A': /* AGAIN */
			case 'a':
				PutStr("\r\n");
				return(2); /* 2 := read again */
				break;
			case 'C': /* CONTINUE */
			case 'c':
			case 'N': /* NEXT */
			case 'n':
			case '\r':
				PutStr("\r\n");
				return(1); /* 1 := onward */
				break;
			case 'L': /* LAST */
			case 'l':
				PutStr("\r\n");
				return(3); /* 3 := backwards */
				break;
			case 'S': /* STATISTICS */
			case 's':
				strcpy(str,lh->Location);
				strcat(str,ld->Filename);
				PutStr("Statistics\r\n");
				report(str);
				PutStr("\r\n");
				command_accepted=SUCCESS;
				break;
			case 'D': /* DOWNLOAD */
			case 'd':
				strcpy(str,lh->Location);
				strcat(str,ld->Filename);
				PutStr("Download\r\n");
				if(Whence_The_Logon<=LOCAL_LOGON){
					PutStr("Not Allowed Locally\r\n");
				}
				else{
					status=Xmodem_Send(str);
					if(status==SUCCESS){
						Append_Stat(STAT_DOWNLOAD);
						++User.Downloads;
					}
				}
				return(1);
				break;
			case 'R': /* REMOVE */
			case 'r':
				if(User.Sec_Status<150){
					command_accepted=FAILURE;
					break;
				}
				PutStr("Remove\r\n");
				Lib_Delete(lh,number,number);
				if(number>lh->Highest_File) { return(0); }
				return(2);
				break;
			case 'Q': /* QUIT */
			case 'q':
				PutStr("Quit\r\n");
				return(0); /* 0 := stop */
				break;
			case '@':
				PutStr("@ Main Menu\r\n");
				return('@');
				break;
			case '?': /* HELP */
			case '/':
				PutStr("Command summary\r\n");
				strcpy(str,System.Location);
				strcat(str,"BetweenLib.Help");
				MenuSend(str);
				command_accepted=SUCCESS;
				break;
			case '\000': /* FN-KEY HIT */
				command_accepted=SUCCESS;
				PutStr("\r\n");
				break;
			default:
				PurgeLine();
				command_accepted=FAILURE;
				break;
			}
		if(command_accepted==FAILURE) { continue; }
		break;
		}
	}
}

/********************/
static int Lib_Delete(lh,from,to)
	struct Library_Header *lh;
	int from,to;
{
struct Library_Data *dh, *dhsource, *dhreplace;
int count, offset;

/* CONVERT TO REAL DATA POSITIONS */
--from;
--to;

/* SET THE NEW HIGH MESSAGE */
lh->Highest_File-=((to-from)+1);

/* MOVE THE HIGHER NUMBERED MESSAGES DOWN TO FILL THE DELETED GAP */
for(offset=0,count=to+1;count<lh->Maximum;++count,++offset)
	{
	dhreplace=lh->Start_of_Data;
	dhsource=lh->Start_of_Data;
	dhsource+=count;
	dhreplace+=(from+offset);
	*dhreplace=*dhsource;
	}

/* FINISH BY SETTING FILES ABOVE THE HIGHEST TO INVALID */
for(count=lh->Highest_File;count<lh->Maximum;++count)
	{
	dh=lh->Start_of_Data;
	dh+=count;
	dh->Time=0L;
	dh->Access=0;

	}

PutStr("NOTE: the file itself remains in the library directory\r\n");
return(Flush_Keys(lh));
}

/********************/
static int Lib_Upload(lh)
	struct Library_Header *lh;
{
static struct Library_Data *data, the_struct;
int x, status, msg_length;
char final[133];

/* VERIFY FREE SPACE */
if(lh->Highest_File==lh->Maximum)
	{
	PutStr("This directory is full, no room for another file\r\n");
	return(FAILURE);
	}

data=&the_struct;

PutStr("Upload\r\n\r\n");

UploadMark1:
Check_Online_Status();

PutStr("A blank line will quit\r\n");
PutStr("Enter File's Name: ");
status=LineInput("",data->Filename,30,120L);
if(status==TIMEOUT || status==NO_CARRIER) { return(status); }

if(strlen(data->Filename)==0)
	{
	PutStr("Canceled upload\r\n");
	return(FAILURE);
	}
if(status==1 && toupper(data->Filename[0])=='Q')
	{
	PutStr("Canceled upload\r\n");
	return(FAILURE);
	}

/* CHECK FOR SPURIOUS DEVICE:DIRECTORY/ :/ SYMBOLS AND REJECT */
for(x=0;x<strlen(data->Filename);++x)
	{
	if(data->Filename[x]==':' || data->Filename[x]=='/' || data->Filename[x]=='*')
		{
		PutStr("You may not include the special symbols\r\n");
		PutStr(" : /  * in the file's name\r\n");
		goto UploadMark1;
		}
	}

status=Lib_Crossref(lh,data->Filename);
if(status==1) /* filename already exists in the internal catalog */
	{
	PutStr("File already exists, you must choose another name\r\n");
	goto UploadMark1;
	}

PutStr("Accepted File Name\r\n\r\n");

PutStr("Enter a Description [79 chars available]:\r\n");
status=LineInput("",data->Description,79,120L);
if(status==TIMEOUT || status==NO_CARRIER) { return(status); }

if(User.Sec_Status<150)
	{
	strcpy(data->Personname,User.Name);
	}
else
	{
	PutStr("Name to use: ");
	status=LineInput(User.Name,data->Personname,30,120L);
	if(status==TIMEOUT || status==NO_CARRIER) { return(status); }
	}

data->Person_Code=User.Slot_Number;
strcpy(final,lh->Location);
strcat(final,data->Filename);

if(Whence_The_Logon>=REMOTE_LOGON)
	{
	PutStr("Entering Xmodem, send the file now\r\n\r\n");
	status=Xmodem_Receive(final);
	}
else
	{
	PutStr("Local upload, protocols bypassed\r\n");
	status=SUCCESS;
	}

if(status==TIMEOUT || status==NO_CARRIER) { unlink(final); return(status); }
if(status==FAILURE) { unlink(final); return(FAILURE); }

if((Store_File(lh,data))==FAILURE)
	{
	unlink(final);
	return(FAILURE);
	}

return(SUCCESS);
}

/********************/
static int Store_File(lh,ld)
	struct Library_Header *lh;
	struct Library_Data *ld;
{
struct Library_Data *dd;

ld->Time=time(NULL);
lh->Latest_Time=ld->Time;
ld->Access=0;
dd=lh->Start_of_Data;
dd+=lh->Highest_File;
*dd=*ld;

++lh->Highest_File;

return(Flush_Keys(lh,ld));
}

/********************/
static int Lib_Catalog(lh)
	struct Library_Header *lh;
{
struct Library_Data *dh;
int status, more, ListNumber;
char ch, str[81];

PutStr("Catalog\r\n\r\n");

for(ListNumber=0,more=0;ListNumber<lh->Highest_File;++ListNumber)
	{
	dh=lh->Start_of_Data;
	dh+=ListNumber;
	sprintf(str,"[%d] %s\r\n",ListNumber+1,dh->Filename);
	PutStr(str);
	++more;
	if(more>=22)
		{
		more=0;
		PutStr("More..");
		ch=ReadChar(120L);
		if(ch==TIMEOUT || ch==NO_CARRIER) { return(ch); }
		if(ch=='Q' || ch=='q' || ch=='N' || ch=='n')
			{
			PutStr("\b\b\b\b\b\b      \b\b\b\b\b\b\r\n");
			return(SUCCESS);
			}
		PutStr("\b\b\b\b\b\b      \b\b\b\b\b\b");
		}
	/* CHECK FOR INPUT && CARRIER */
	Check_Online_Status();
	if(CheckInput())
		{
		status=ReadChar(120L);
		switch(status)
			{
			case '\023': /* ^S Pause */
			case 'S':
			case 's':
			case 'P':
			case 'p':
			case ' ':
				PurgeLine();
				PutStr("Any key..");
				status=ReadChar(120L);
			    if(status==TIMEOUT||status==NO_CARRIER){return(status);}
			PutStr("\b\b\b\b\b\b\b\b\b         \b\b\b\b\b\b\b\b\b");
				break;
			case '\033': /* ESC Abort */
			case '\003': /* ^C */
			case '\020': /* ^P */
			case '\031': /* ^Y */
			case 'Q':
			case 'q':
			case 'X':
			case 'x':
				PutStr("\r\nAbort\r\n");
				goto CatMark1;
				break;
			default:
				PurgeLine();
				break;
			}
		}
	}

CatMark1:

PutStr("\r\nCompleted\r\n");
return(SUCCESS);
}

/********************/
static int Lib_Crossref(lh,testname)
	struct Library_Header *lh;
	char testname[];
{
struct Library_Data *dh;
int status, ListNumber;

for(ListNumber=0;ListNumber<lh->Highest_File;++ListNumber)
	{
	dh=lh->Start_of_Data;
	dh+=ListNumber;
	if(jive(dh->Filename,testname)==SUCCESS) { return(1); }

	/* CHECK FOR CARRIER */
	Check_Online_Status();
	}

return(0);
}
