home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1994 #1
/
monster.zip
/
monster
/
WIN_UTL2
/
VULCAN.ZIP
/
VULSRC.ZIP
/
VCN.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1994-02-08
|
21KB
|
593 lines
// VCN.CPP Part of VULCAN
// Copyright (c) 1993 John Deurbrouck
#include<windows.h>
#include<stdlib.h>
#include<time.h>
#include<limits.h>
#include<dos.h>
#include<io.h>
#include<fcntl.h>
#include<share.h>
#include<errno.h>
#include<ctype.h>
#include"vcn.hpp"
#include"vulcan.hpp"
#include"cancel.hpp"
#include"browse.hpp"
struct queue{
vcn_info i;
queue _far *p; // points at lower-numbered entry
queue _far *n; // points at higher-numbered entry
};
static long cluster_size=0L;
static queue _far *head=NULL; // points at highest-numbered entry
static queue _far *tail=NULL; // points at lowest-numbered entry
static queue _far *cursor=NULL;
static long total_disk_bytes=0L,highest=0L;
static long lowest=LONG_MAX;
static int queue_needs_writing=0;
// ///////////////////////////////////////////////////////////
// type and data for suballocator
typedef struct malloc_chain_struct{
HGLOBAL hg;
struct malloc_chain_struct __far *next;
}malloc_chain;
malloc_chain __far *malloc_head=NULL;
static int desired_malloc_size=16*1024;
static int reset_static_pointers=0;
static queue _far *vcnFind(long index);
static void write_queue();
static int read_vcn_file(void);
static int read_vcn_filedata(long index,vcn_info __far * vcnp);
static void __far *get_space(int bytes);
static void free_all_space(void);
static long target_drive_bytes_avail();
int vcnSetup(void){
int found_vulcan_vcn_file=0;
if(!cluster_size){
struct _diskfree_t df;
if(!_dos_getdiskfree(toupper((int)szTargetDirectory[0])-'A'+1,&df)){
cluster_size=
(long)df.bytes_per_sector * (long)df.sectors_per_cluster;
}
if(!cluster_size)cluster_size=2048; // best guess
}
vcnDestroy(); // clear out existing stuff if any
// now get all the filenames and add to queue
vcn_info vi; // create temporary struct, set default values
// each file will have unique Index, StoredDate, StoredTime, StoredSize
vi.FileSize=0L;
vi.FileDate=vi.FileTime=vi.Attrib=0;
vi.version=vi.compressed=vi.signature=0;
vi.DispChar=' ';
vi.FullName=vi.FileName=NULL;
lstrcpy(pszTargetDirFile,"*.VCN"); // for directory listing
_find_t find_t_stuff;
int find_value=_dos_findfirst(szTargetDirectory,
_A_ARCH|_A_HIDDEN|_A_RDONLY|_A_SYSTEM,&find_t_stuff);
while(!find_value){
char *p=find_t_stuff.name;
while(isdigit(*p))p++;
if(find_t_stuff.attrib&(_A_SUBDIR|_A_VOLID))
; // do nothing with directories and volume labels...
else if(*p=='.'){ // ok, it's an all-digit VCN file...data file!
vi.StoredTime=find_t_stuff.wr_time;
vi.StoredDate=find_t_stuff.wr_date;
vi.StoredSize=find_t_stuff.size;
vi.Index=atol(find_t_stuff.name);
if(!vcnPutInfo(vi))return 0;
}
else if(!lstrcmp(find_t_stuff.name,szTempFileName)){
// temp file, delete it
lstrcpy(pszTargetDirFile,szTempFileName);
if(_unlink(szTargetDirectory)){
wsprintf(pacBigScratchBuffer,"Unable to delete %s. "
"This prevents Vulcan from accepting new files.",
(LPSTR)szTargetDirectory);
MessageBox(CURR_WINDOW,pacBigScratchBuffer,
szAppErrorTitle,MB_OK|MB_ICONSTOP);
}
}
else if(!lstrcmp(find_t_stuff.name,szVulcanVcn)){
// VULCAN.VCN, remember consumed disk space
total_disk_bytes+=vcnAdjustForClusterSize(find_t_stuff.size);
found_vulcan_vcn_file=1;
}
else{
lstrcpy(pszTargetDirFile,find_t_stuff.name);
wsprintf(pacBigScratchBuffer,"This non-Vulcan file:\n%s\n"
"has Vulcan's file extension (VCN).\n\n"
"Please delete, rename or move to avoid interfering with "
"Vulcan's disk space computations.",
(LPSTR)szTargetDirectory);
MessageBox(CURR_WINDOW,pacBigScratchBuffer,
szAppWarningTitle,MB_OK|MB_ICONSTOP);
}
find_value=_dos_findnext(&find_t_stuff);
}
if(found_vulcan_vcn_file&&!read_vcn_file()){
MessageBox(CURR_WINDOW,"Not enough memory to read VULCAN.VCN",
szAppErrorTitle,MB_OK|MB_ICONSTOP);
return 0;
}
// now iterate through queue, getting full info on items not in VULCAN.VCN
{
queue __far *qfp=tail;
while(qfp!=NULL){
if(qfp->i.FullName==NULL){ // data not already present
read_vcn_filedata(qfp->i.Index,&qfp->i); // suck data out
}
qfp=qfp->n;
}
}
// now go through queue, get rid of members we can't use
{
queue __far *qfp=tail;
while(qfp!=NULL){
if(qfp->i.FullName==NULL){ // data not present
long BadIndex=qfp->i.Index;
qfp=qfp->n;
wsprintf(pszTargetDirFile,"%08ld.VCN",BadIndex);
wsprintf(pacBigScratchBuffer,"File %s is not a valid "
"VULCAN file. VULCAN can't use it.\n\nPress OK to delete "
"it from disk, cancel to leave it there.",
(LPSTR)szTargetDirectory);
if(MessageBox(CURR_WINDOW,pacBigScratchBuffer,
szAppErrorTitle,MB_OKCANCEL|MB_ICONSTOP)==IDOK){
wsprintf(pszTargetDirFile,"%08ld.VCN",BadIndex);
_dos_setfileattr(szTargetDirectory,_A_NORMAL);
remove(szTargetDirectory);
vcnDeleteItem(BadIndex,1);
}
else{
vcnDeleteItem(BadIndex,0);
return 0;
}
continue;
}
qfp=qfp->n;
}
}
return 1;
}
void vcnGetFilenumberRange(long &bottom,long &top){
bottom=lowest;
top=highest;
}
int vcnGetInfo(long index,vcn_info &vcn){
// returns 0 for fail, 1 for success
queue __far *qp=vcnFind(index);
if(qp!=NULL){
vcn=qp->i;
return 1;
}
return 0;
}
int vcnGetRecordAfter(long index,vcn_info &vcn){
// returns 0 for fail, 1 for success
queue __far *qp=vcnFind(index);
if(qp->n==NULL)return 0;
vcn=qp->n->i;
return 1;
}
int vcnGetRecordBefore(long index,vcn_info &vcn){
// returns 0 for fail, 1 for success
queue __far *qp=vcnFind(index);
if(qp->p==NULL)return 0;
vcn=qp->p->i;
return 1;
}
int vcnPutInfo(vcn_info &vcn){
// used to add given record number
// chooses closest pointer (head, tail, cursor) to traverse to record
// returns 0 for fail, 1 for success
char __far *fullnameptr=NULL;
char __far *filnam=NULL;
if(vcn.FullName!=NULL){ // get space to store filename
fullnameptr=(char __far *)get_space(lstrlen(vcn.FullName)+1);
if(fullnameptr==NULL)return 0;
lstrcpy(fullnameptr,vcn.FullName);
filnam=fullnameptr;
while(*filnam)filnam++; // point at null terminator
while(filnam>fullnameptr &&filnam[-1]!='\\' && filnam[-1]!=':')
filnam--; // point at start of filename
}
queue __far *qfp=vcnFind(vcn.Index);
if(qfp!=NULL){ // already exists, just save new data, shouldn't alter size
qfp->i=vcn;
qfp->i.FullName=fullnameptr;
qfp->i.FileName=filnam;
queue_needs_writing=1;
return 1;
}
{ // ok, record didn't exist, so get a new structure
qfp=(queue __far *)get_space(sizeof(queue));
if(qfp==NULL)return 0;
qfp->i=vcn;
qfp->i.FullName=fullnameptr;
qfp->i.FileName=filnam;
}
// definitely going to add this record. fix up highest, lowest,
// and total_disk_bytes
if(vcn.Index>highest)highest=vcn.Index;
if(vcn.Index<lowest)lowest=vcn.Index;
total_disk_bytes+=vcnAdjustForClusterSize(vcn.StoredSize);
queue_needs_writing=1;
// now insert into linked list
if(head==NULL){ // first entry, easy work
qfp->p=qfp->n=NULL;
head=tail=qfp;
return 1;
}
// ok, start at highest number and work back...
if(qfp->i.Index>head->i.Index){ // highest nbr, new head
head->n=qfp;
qfp->p=head;
qfp->n=NULL;
head=qfp;
return 1;
}
if(qfp->i.Index<tail->i.Index){ // lowest nbr, new tail
tail->p=qfp;
qfp->p=NULL;
qfp->n=tail;
tail=qfp;
return 1;
}
cursor=head;
while(cursor->i.Index>qfp->i.Index)cursor=cursor->p;
// cursor pointing at record preceding new one
qfp->n=cursor->n;
qfp->p=cursor;
cursor->n->p=qfp;
cursor->n=qfp;
return 1;
}
void vcnDeleteOldestItem(){
if(tail==NULL)return;
vcnDeleteItem(tail->i.Index);
}
void vcnDeleteItem(long index,int delete_file){
queue __far *qfp=vcnFind(index);
if(qfp==NULL)return;
queue_needs_writing=1;
// first delete the actual file
if(delete_file){
wsprintf(pszTargetDirFile,"%08d.VCN",qfp->i.Index);
_dos_setfileattr(szTargetDirectory,_A_NORMAL);
remove(szTargetDirectory);
}
// now adust total_disk_bytes
total_disk_bytes-=vcnAdjustForClusterSize(qfp->i.StoredSize);
// special handling for only item
if(qfp->n==NULL && qfp->p==NULL){
head=tail=NULL;
highest=0L;
lowest=LONG_MAX;
return;
}
// now remove entry from queue
queue __far * __far * pp;
queue __far * __far * nn;
nn=(qfp->n==NULL)?&head:&qfp->n->p;
pp=(qfp->p==NULL)?&tail:&qfp->p->n;
*nn=qfp->p;
*pp=qfp->n;
// fix up highest, lowest
highest=head->i.Index;
lowest=tail->i.Index;
return;
}
long vcnGetTargetDiskBytes(){
// two strategies, depending on UseLeaveMegsNumber
if(UseLeaveMegsNumber){
// strategy is to leave LeaveMegs megabytes of space on host drive
long target_space_left=LeaveMegs * 1024L * 1024L;
// here's how much empty space there *could* be
long potential_space=total_disk_bytes+target_drive_bytes_avail();
// not enough space to do what the user wants...
if(potential_space<=target_space_left)return 0L;
// ideal size leaves target_space_left bytes
return potential_space-target_space_left;
}
else{
// strategy is to limit total_file_bytes to MaxMegs megabytes
long target=MaxMegs * 1024L * 1024L;
// but that won't work if there's not enough disk space
long avail=total_disk_bytes+target_drive_bytes_avail();
// so return the smaller quantity
if(target>avail)target=avail;
return target;
}
}
long vcnGetActualDiskBytes(){
return total_disk_bytes;
}
int vcnAddBytesRequiresSomeDelete(long bytes){
// adjust for fudge factor, return 1 if would have to delete some
// return 0 if not
if((vcnAdjustForClusterSize(bytes+INFLATION)+total_disk_bytes)>
vcnGetTargetDiskBytes())
return 1;
return 0;
}
long vcnAdjustForClusterSize(long siz){
// even a one-byte file takes up a full cluster...
long remainder=siz%cluster_size;
if(remainder)siz+=cluster_size-remainder;
return siz;
}
void vcnForceToCorrectSize(long space){
// pares down achive to fit user's limits
// since space is either 0 or a file size (before copy),
// assume space needs to be inflated by INFLATION bytes to account
// for header
long fudge_bytes=vcnAdjustForClusterSize(space>0L?space+INFLATION:0L);
while(vcnGetTargetDiskBytes()<(fudge_bytes+vcnGetActualDiskBytes())){
if(head==NULL)break;
vcnDeleteOldestItem();
}
return;
}
void vcnSaveVcnFile(void){
write_queue(); // save file data in VULCAN.VCN
}
void vcnDestroy(void){
write_queue(); // save file data in VULCAN.VCN
head=tail=cursor=NULL;
total_disk_bytes=highest=0L;
lowest=LONG_MAX;
free_all_space();
return;
}
static queue _far *vcnFind(long index){
// used to find given record number
// chooses closest pointer (head, tail, cursor) to traverse to record
if(head==NULL)return NULL;
if(index<lowest||index>highest)return NULL;
long h_diff,t_diff,c_diff=LONG_MAX;
if(cursor!=NULL){
c_diff=cursor->i.Index-index;
if(c_diff<0L)c_diff*=-1L;
}
h_diff=head->i.Index-index;
if(h_diff<0L)h_diff*=-1L;
t_diff=tail->i.Index-index;
if(t_diff<0L)t_diff*=-1L;
long min_diff=h_diff<t_diff?h_diff:t_diff;
min_diff=min_diff<c_diff?min_diff:c_diff;
if(h_diff==min_diff)cursor=head;
else if(t_diff==min_diff)cursor=tail;
int went_up=0,went_down=0;
while(cursor->i.Index!=index){
if(cursor->i.Index<index){cursor=cursor->n;went_up=1;}
else if(cursor->i.Index>index){cursor=cursor->p;went_down=1;}
if(went_up&&went_down)return NULL;
}
return cursor;
}
static void write_queue(){
if(!queue_needs_writing||head==NULL)return;
lstrcpy(pszTargetDirFile,szVulcanVcn); // ok, this is output filename
_dos_setfileattr(szTargetDirectory,_A_NORMAL);
remove(szTargetDirectory);
int fh;
unsigned retval;
if(retval=_dos_creatnew(szTargetDirectory,_A_NORMAL,&fh)){
char *p;
switch(errno){
case EACCES: p="access denied"; break;
case EEXIST: p="file already exists"; break;
case EMFILE: p="no available file handles"; break;
case ENOENT: p="path doesn't exist"; break;
default: p="unknown error"; break;
}
wsprintf(pacBigScratchBuffer,"Could not open %s for writing.\n\n"
"DOS error code %u(%Xh), %s",(LPSTR)szTargetDirectory,
retval,retval,(LPSTR)p);
MessageBox(CURR_WINDOW,pacBigScratchBuffer,
szAppErrorTitle,MB_OK|MB_ICONSTOP);
return;
}
cursor=head;
while(cursor!=NULL){
vcn_info vi=cursor->i;
if(!vcnWriteEntry(vi,fh)){
MessageBox(CURR_WINDOW,"Could not write to VULCAN.VCN",
szAppErrorTitle,MB_OK|MB_ICONSTOP);
return;
}
cursor=cursor->p;
}
_dos_close(fh);
queue_needs_writing=0;
}
int vcnWriteEntry(vcn_info& vcn,int fhandle,int* hsize){
// returns 1 for success, 0 for error
if(vcn.FullName==NULL||lstrlen(vcn.FullName)>250)return 0;
vcn_info *p=(vcn_info *)pacBigScratchBuffer;
*p=vcn; // copy reproducable data to buffer
unsigned char *ucp=(unsigned char *)&p->FullName;
*ucp++=(unsigned char)(lstrlen(vcn.FullName)+1); // write length to buffer
lstrcpy((char __far *)ucp,vcn.FullName); // write name to buffer
while(*ucp++); // point past string
unsigned write_size=ucp-(unsigned char *)pacBigScratchBuffer;
if(hsize!=NULL)*hsize=(int)write_size;
unsigned actual_written;
if(_dos_write(fhandle,pacBigScratchBuffer,write_size,&actual_written)||
(actual_written!=write_size)){
return 0;
}
return 1;
}
static int read_vcn_file(void){
// reads through VULCAN.VCN, putting all the data into queue
lstrcpy(pszTargetDirFile,szVulcanVcn);
int fh;
if(_dos_open(szTargetDirectory,_O_RDONLY,&fh)){
MessageBox(CURR_WINDOW,"Could not open VULCAN.VCN for reading",
szAppErrorTitle,MB_OK|MB_ICONSTOP);
return 1;
}
vcn_info *p=(vcn_info *)pacBigScratchBuffer;
while(vcnReadEntry(p,pacBigScratchBuffer2,fh)){
queue _far *oldrec=vcnFind(p->Index);
if(oldrec==NULL)continue; // don't care, file's gone
if(lstrlen(pacBigScratchBuffer2)<4){ // no filename
_dos_close(fh);
return 1;
}
if(lstrlen(pacBigScratchBuffer2)>200){ // bogus filename
_dos_close(fh);
return 1;
}
p->FullName=(char __far *)get_space(lstrlen(pacBigScratchBuffer2)+1);
if(p->FullName==NULL){ // out of memory
_dos_close(fh);
return 0;
}
lstrcpy(p->FullName,pacBigScratchBuffer2);
char __far *filnam=p->FullName;
while(*filnam)filnam++; // point at null terminator
while(filnam>p->FullName &&
filnam[-1]!='\\' && filnam[-1]!=':')
filnam--; // point at start of filename
p->FileName=filnam;
oldrec->i.FileSize=p->FileSize; // save rest of data
oldrec->i.FileDate=p->FileDate;
oldrec->i.FileTime=p->FileTime;
oldrec->i.Attrib=p->Attrib;
oldrec->i.DispChar=p->DispChar;
oldrec->i.version=p->version;
oldrec->i.compressed=p->compressed;
oldrec->i.signature=p->signature;
oldrec->i.FileName=p->FileName;
oldrec->i.FullName=p->FullName;
}
_dos_close(fh);
return 1;
}
static int read_vcn_filedata(long index,vcn_info __far * vcnp){
// reads data from actual XXXXXXXX.VCN file
// if successful, returns 1 otherwise 0
wsprintf(pszTargetDirFile,"%08ld.VCN",index);
int fh;
if(_dos_open(szTargetDirectory,_O_RDONLY,&fh))return 0;
vcn_info *p=(vcn_info *)pacBigScratchBuffer;
if(!vcnReadEntry(p,pacBigScratchBuffer2,fh)){
_dos_close(fh);
return 0;
}
p->FullName=(char __far *)get_space(lstrlen(pacBigScratchBuffer2)+1);
if(p->FullName==NULL){
_dos_close(fh);
return 0;
}
lstrcpy(p->FullName,pacBigScratchBuffer2);
char __far *filnam=p->FullName;
while(*filnam)filnam++; // point at null terminator
while(filnam>p->FullName &&
filnam[-1]!='\\' && filnam[-1]!=':')
filnam--; // point at start of filename
p->FileName=filnam;
vcnp->FullName=p->FullName;
vcnp->FileName=filnam;
vcnp->FileSize=p->FileSize; // save rest of data
vcnp->FileDate=p->FileDate;
vcnp->FileTime=p->FileTime;
vcnp->Attrib=p->Attrib;
vcnp->DispChar=p->DispChar;
vcnp->version=p->version;
vcnp->compressed=p->compressed;
vcnp->signature=p->signature;
_dos_close(fh);
return 1;
}
int vcnReadEntry(vcn_info* vcnp,char* filename,int fhandle){
// reads from fhandle into vcnp, then reads filename
// returns 0 for EOF, corruption, bad data
// does not zero out bogus fields
// can overwrite vcnp, filename even on failure
unsigned actual_read;
unsigned read_count=(unsigned)((char *)&vcnp->FullName-(char *)vcnp);
// first read first elements of vcn_info
if(_dos_read(fhandle,vcnp,read_count,&actual_read)||
read_count!=actual_read||
vcnp->version!=1||
vcnp->signature!=SIGNATURE)return 0;
// now read length of string following
unsigned char len;
read_count=1;
if(_dos_read(fhandle,&len,read_count,&actual_read)||
read_count!=actual_read||len<4)
return 0;
// now read actual string
read_count=(unsigned)len;
filename[len]=0;
if(_dos_read(fhandle,filename,read_count,&actual_read)||
read_count!=actual_read||
lstrlen(filename)+1!=(int)read_count)
return 0;
return 1;
}
static void __far *get_space(int bytes){
static char __far *free_space=NULL;
static int bytes_left=0;
void __far *retval;
if(reset_static_pointers){
reset_static_pointers=0;
free_space=NULL;
bytes_left=0;
}
if(bytes<1)return NULL;
if(bytes<bytes_left){
retval=free_space;
bytes_left-=bytes;
free_space+=bytes;
return retval;
}
if((size_t)bytes>(desired_malloc_size+sizeof(malloc_chain)))
return NULL; // just can't satisfy some people
{ // OK, get some space
malloc_chain __far *newspace;
HGLOBAL newhandle=GlobalAlloc(GMEM_FIXED,desired_malloc_size);
if(newhandle==NULL)return NULL; // bail if get no allocation
newspace=(malloc_chain __far *)GlobalLock(newhandle);
if(newspace==NULL){ // bail if can't lock
GlobalFree(newhandle);
return NULL;
}
newspace->hg=newhandle;
newspace->next=malloc_head;
free_space=(char __far *)&newspace[1];
bytes_left=desired_malloc_size-sizeof(malloc_chain);
malloc_head=newspace;
}
retval=free_space; // now we know we have the space; use it
bytes_left-=bytes;
free_space+=bytes;
return retval;
}
static void free_all_space(void){
while(malloc_head!=NULL){
HGLOBAL goner=malloc_head->hg;
malloc_head=malloc_head->next;
GlobalUnlock(goner);
GlobalFree(goner);
}
reset_static_pointers=1;
}
static long target_drive_bytes_avail(){
// returns bytes avail on drive named in szTargetDirectory
_diskfree_t diskspace;
if(_dos_getdiskfree(toupper(szTargetDirectory[0])-'A'+1,
&diskspace))
return 0L;
return (long)diskspace.avail_clusters*cluster_size;
}