home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / WIN_UTL2 / VULCAN.ZIP / VULSRC.ZIP / VCN.CPP < prev    next >
C/C++ Source or Header  |  1994-02-08  |  21KB  |  593 lines

  1. // VCN.CPP Part of VULCAN
  2. // Copyright (c) 1993 John Deurbrouck
  3. #include<windows.h>
  4. #include<stdlib.h>
  5. #include<time.h>
  6. #include<limits.h>
  7. #include<dos.h>
  8. #include<io.h>
  9. #include<fcntl.h>
  10. #include<share.h>
  11. #include<errno.h>
  12. #include<ctype.h>
  13. #include"vcn.hpp"
  14. #include"vulcan.hpp"
  15. #include"cancel.hpp"
  16. #include"browse.hpp"
  17.  
  18. struct queue{
  19.     vcn_info i;
  20.     queue _far *p; // points at lower-numbered entry
  21.     queue _far *n; // points at higher-numbered entry
  22. };
  23.  
  24. static long cluster_size=0L;
  25. static queue _far *head=NULL; // points at highest-numbered entry
  26. static queue _far *tail=NULL; // points at lowest-numbered entry
  27. static queue _far *cursor=NULL;
  28. static long total_disk_bytes=0L,highest=0L;
  29. static long lowest=LONG_MAX;
  30. static int queue_needs_writing=0;
  31. // ///////////////////////////////////////////////////////////
  32. // type and data for suballocator
  33. typedef struct malloc_chain_struct{
  34.     HGLOBAL hg;
  35.     struct malloc_chain_struct __far *next;
  36. }malloc_chain;
  37. malloc_chain __far *malloc_head=NULL;
  38. static int desired_malloc_size=16*1024;
  39. static int reset_static_pointers=0;
  40.  
  41.  
  42. static queue _far *vcnFind(long index);
  43. static void write_queue();
  44. static int read_vcn_file(void);
  45. static int read_vcn_filedata(long index,vcn_info __far * vcnp);
  46. static void __far *get_space(int bytes);
  47. static void free_all_space(void);
  48. static long target_drive_bytes_avail();
  49.  
  50. int vcnSetup(void){
  51.     int found_vulcan_vcn_file=0;
  52.     if(!cluster_size){
  53.         struct _diskfree_t df;
  54.         if(!_dos_getdiskfree(toupper((int)szTargetDirectory[0])-'A'+1,&df)){
  55.             cluster_size=
  56.                 (long)df.bytes_per_sector * (long)df.sectors_per_cluster;
  57.         }
  58.         if(!cluster_size)cluster_size=2048; // best guess
  59.     }
  60.     vcnDestroy(); // clear out existing stuff if any
  61.     // now get all the filenames and add to queue
  62.     vcn_info vi; // create temporary struct, set default values
  63.     // each file will have unique Index, StoredDate, StoredTime, StoredSize
  64.     vi.FileSize=0L;
  65.     vi.FileDate=vi.FileTime=vi.Attrib=0;
  66.     vi.version=vi.compressed=vi.signature=0;
  67.     vi.DispChar=' ';
  68.     vi.FullName=vi.FileName=NULL;
  69.     lstrcpy(pszTargetDirFile,"*.VCN"); // for directory listing
  70.     _find_t find_t_stuff;
  71.     int find_value=_dos_findfirst(szTargetDirectory,
  72.         _A_ARCH|_A_HIDDEN|_A_RDONLY|_A_SYSTEM,&find_t_stuff);
  73.     while(!find_value){
  74.         char *p=find_t_stuff.name;
  75.         while(isdigit(*p))p++;
  76.         if(find_t_stuff.attrib&(_A_SUBDIR|_A_VOLID))
  77.             ; // do nothing with directories and volume labels...
  78.         else if(*p=='.'){ // ok, it's an all-digit VCN file...data file!
  79.             vi.StoredTime=find_t_stuff.wr_time;
  80.             vi.StoredDate=find_t_stuff.wr_date;
  81.             vi.StoredSize=find_t_stuff.size;
  82.             vi.Index=atol(find_t_stuff.name);
  83.             if(!vcnPutInfo(vi))return 0;
  84.         }
  85.         else if(!lstrcmp(find_t_stuff.name,szTempFileName)){
  86.         // temp file, delete it
  87.             lstrcpy(pszTargetDirFile,szTempFileName);
  88.             if(_unlink(szTargetDirectory)){
  89.                 wsprintf(pacBigScratchBuffer,"Unable to delete %s. "
  90.                     "This prevents Vulcan from accepting new files.",
  91.                     (LPSTR)szTargetDirectory);
  92.                 MessageBox(CURR_WINDOW,pacBigScratchBuffer,
  93.                     szAppErrorTitle,MB_OK|MB_ICONSTOP);
  94.             }
  95.         }
  96.         else if(!lstrcmp(find_t_stuff.name,szVulcanVcn)){
  97.         // VULCAN.VCN, remember consumed disk space
  98.             total_disk_bytes+=vcnAdjustForClusterSize(find_t_stuff.size);
  99.             found_vulcan_vcn_file=1;
  100.         }
  101.         else{
  102.             lstrcpy(pszTargetDirFile,find_t_stuff.name);
  103.             wsprintf(pacBigScratchBuffer,"This non-Vulcan file:\n%s\n"
  104.                 "has Vulcan's file extension (VCN).\n\n"
  105.                 "Please delete, rename or move to avoid interfering with "
  106.                 "Vulcan's disk space computations.",
  107.                 (LPSTR)szTargetDirectory);
  108.             MessageBox(CURR_WINDOW,pacBigScratchBuffer,
  109.                 szAppWarningTitle,MB_OK|MB_ICONSTOP);
  110.         }
  111.         find_value=_dos_findnext(&find_t_stuff);
  112.     }
  113.     if(found_vulcan_vcn_file&&!read_vcn_file()){
  114.         MessageBox(CURR_WINDOW,"Not enough memory to read VULCAN.VCN",
  115.             szAppErrorTitle,MB_OK|MB_ICONSTOP);
  116.         return 0;
  117.     }
  118.     // now iterate through queue, getting full info on items not in VULCAN.VCN
  119.     {
  120.         queue __far *qfp=tail;
  121.         while(qfp!=NULL){
  122.             if(qfp->i.FullName==NULL){ // data not already present
  123.                 read_vcn_filedata(qfp->i.Index,&qfp->i); // suck data out
  124.             }
  125.             qfp=qfp->n;
  126.         }
  127.     }
  128.     // now go through queue, get rid of members we can't use
  129.     {
  130.         queue __far *qfp=tail;
  131.         while(qfp!=NULL){
  132.             if(qfp->i.FullName==NULL){ // data not present
  133.                 long BadIndex=qfp->i.Index;
  134.                 qfp=qfp->n;
  135.                 wsprintf(pszTargetDirFile,"%08ld.VCN",BadIndex);
  136.                 wsprintf(pacBigScratchBuffer,"File %s is not a valid "
  137.                 "VULCAN file. VULCAN can't use it.\n\nPress OK to delete "
  138.                 "it from disk, cancel to leave it there.",
  139.                 (LPSTR)szTargetDirectory);
  140.                 if(MessageBox(CURR_WINDOW,pacBigScratchBuffer,
  141.                 szAppErrorTitle,MB_OKCANCEL|MB_ICONSTOP)==IDOK){
  142.                     wsprintf(pszTargetDirFile,"%08ld.VCN",BadIndex);
  143.                     _dos_setfileattr(szTargetDirectory,_A_NORMAL);
  144.                     remove(szTargetDirectory);
  145.                     vcnDeleteItem(BadIndex,1);
  146.                 }
  147.                 else{
  148.                     vcnDeleteItem(BadIndex,0);
  149.                     return 0;
  150.                 }
  151.                 continue;
  152.             }
  153.             qfp=qfp->n;
  154.         }
  155.     }
  156.     return 1;
  157. }
  158. void vcnGetFilenumberRange(long &bottom,long &top){
  159.     bottom=lowest;
  160.     top=highest;
  161. }
  162. int vcnGetInfo(long index,vcn_info &vcn){
  163. // returns 0 for fail, 1 for success
  164.     queue __far *qp=vcnFind(index);
  165.     if(qp!=NULL){
  166.         vcn=qp->i;
  167.         return 1;
  168.     }
  169.     return 0;
  170. }
  171. int vcnGetRecordAfter(long index,vcn_info &vcn){
  172. // returns 0 for fail, 1 for success
  173.     queue __far *qp=vcnFind(index);
  174.     if(qp->n==NULL)return 0;
  175.     vcn=qp->n->i;
  176.     return 1;
  177. }
  178. int vcnGetRecordBefore(long index,vcn_info &vcn){
  179. // returns 0 for fail, 1 for success
  180.     queue __far *qp=vcnFind(index);
  181.     if(qp->p==NULL)return 0;
  182.     vcn=qp->p->i;
  183.     return 1;
  184. }
  185. int vcnPutInfo(vcn_info &vcn){
  186. // used to add given record number
  187. // chooses closest pointer (head, tail, cursor) to traverse to record
  188. // returns 0 for fail, 1 for success
  189.     char __far *fullnameptr=NULL;
  190.     char __far *filnam=NULL;
  191.     if(vcn.FullName!=NULL){ // get space to store filename
  192.         fullnameptr=(char __far *)get_space(lstrlen(vcn.FullName)+1);
  193.         if(fullnameptr==NULL)return 0;
  194.         lstrcpy(fullnameptr,vcn.FullName);
  195.         filnam=fullnameptr;
  196.         while(*filnam)filnam++; // point at null terminator
  197.         while(filnam>fullnameptr &&filnam[-1]!='\\' && filnam[-1]!=':')
  198.             filnam--; // point at start of filename
  199.     }
  200.     queue __far *qfp=vcnFind(vcn.Index);
  201.     if(qfp!=NULL){ // already exists, just save new data, shouldn't alter size
  202.         qfp->i=vcn;
  203.         qfp->i.FullName=fullnameptr;
  204.         qfp->i.FileName=filnam;
  205.         queue_needs_writing=1;
  206.         return 1;
  207.     }
  208.     { // ok, record didn't exist, so get a new structure
  209.         qfp=(queue __far *)get_space(sizeof(queue));
  210.         if(qfp==NULL)return 0;
  211.         qfp->i=vcn;
  212.         qfp->i.FullName=fullnameptr;
  213.         qfp->i.FileName=filnam;
  214.     }
  215.     // definitely going to add this record. fix up  highest, lowest,
  216.     // and total_disk_bytes
  217.     if(vcn.Index>highest)highest=vcn.Index;
  218.     if(vcn.Index<lowest)lowest=vcn.Index;
  219.     total_disk_bytes+=vcnAdjustForClusterSize(vcn.StoredSize);
  220.     queue_needs_writing=1;
  221.     // now insert into linked list
  222.     if(head==NULL){ // first entry, easy work
  223.         qfp->p=qfp->n=NULL;
  224.         head=tail=qfp;
  225.         return 1;
  226.     }
  227.     // ok, start at highest number and work back...
  228.     if(qfp->i.Index>head->i.Index){ // highest nbr, new head
  229.         head->n=qfp;
  230.         qfp->p=head;
  231.         qfp->n=NULL;
  232.         head=qfp;
  233.         return 1;
  234.     }
  235.     if(qfp->i.Index<tail->i.Index){ // lowest nbr, new tail
  236.         tail->p=qfp;
  237.         qfp->p=NULL;
  238.         qfp->n=tail;
  239.         tail=qfp;
  240.         return 1;
  241.     }
  242.     cursor=head;
  243.     while(cursor->i.Index>qfp->i.Index)cursor=cursor->p;
  244.     // cursor pointing at record preceding new one
  245.     qfp->n=cursor->n;
  246.     qfp->p=cursor;
  247.     cursor->n->p=qfp;
  248.     cursor->n=qfp;
  249.     return 1;
  250. }
  251. void vcnDeleteOldestItem(){
  252.     if(tail==NULL)return;
  253.     vcnDeleteItem(tail->i.Index);
  254. }
  255. void vcnDeleteItem(long index,int delete_file){
  256.     queue __far *qfp=vcnFind(index);
  257.     if(qfp==NULL)return;
  258.     queue_needs_writing=1;
  259.     // first delete the actual file
  260.     if(delete_file){
  261.         wsprintf(pszTargetDirFile,"%08d.VCN",qfp->i.Index);
  262.         _dos_setfileattr(szTargetDirectory,_A_NORMAL);
  263.         remove(szTargetDirectory);
  264.     }
  265.     // now adust total_disk_bytes
  266.     total_disk_bytes-=vcnAdjustForClusterSize(qfp->i.StoredSize);
  267.     // special handling for only item
  268.     if(qfp->n==NULL && qfp->p==NULL){
  269.         head=tail=NULL;
  270.         highest=0L;
  271.         lowest=LONG_MAX;
  272.         return;
  273.     }
  274.     // now remove entry from queue
  275.     queue __far * __far * pp;
  276.     queue __far * __far * nn;
  277.     nn=(qfp->n==NULL)?&head:&qfp->n->p;
  278.     pp=(qfp->p==NULL)?&tail:&qfp->p->n;
  279.     *nn=qfp->p;
  280.     *pp=qfp->n;
  281.     // fix up highest, lowest
  282.     highest=head->i.Index;
  283.     lowest=tail->i.Index;
  284.     return;
  285. }
  286. long vcnGetTargetDiskBytes(){
  287.     // two strategies, depending on UseLeaveMegsNumber
  288.     if(UseLeaveMegsNumber){
  289.         // strategy is to leave LeaveMegs megabytes of space on host drive
  290.         long target_space_left=LeaveMegs * 1024L * 1024L;
  291.         // here's how much empty space there *could* be
  292.         long potential_space=total_disk_bytes+target_drive_bytes_avail();
  293.         // not enough space to do what the user wants...
  294.         if(potential_space<=target_space_left)return 0L;
  295.         // ideal size leaves target_space_left bytes
  296.         return potential_space-target_space_left;
  297.     }
  298.     else{
  299.         // strategy is to limit total_file_bytes to MaxMegs megabytes
  300.         long target=MaxMegs * 1024L * 1024L;
  301.         // but that won't work if there's not enough disk space
  302.         long avail=total_disk_bytes+target_drive_bytes_avail();
  303.         // so return the smaller quantity
  304.         if(target>avail)target=avail;
  305.         return target;
  306.     }
  307. }
  308. long vcnGetActualDiskBytes(){
  309.     return total_disk_bytes;
  310. }
  311. int vcnAddBytesRequiresSomeDelete(long bytes){
  312.     // adjust for fudge factor, return 1 if would have to delete some
  313.     // return 0 if not
  314.     if((vcnAdjustForClusterSize(bytes+INFLATION)+total_disk_bytes)>
  315.     vcnGetTargetDiskBytes())
  316.         return 1;
  317.     return 0;
  318. }
  319. long vcnAdjustForClusterSize(long siz){
  320.     // even a one-byte file takes up a full cluster...
  321.     long remainder=siz%cluster_size;
  322.     if(remainder)siz+=cluster_size-remainder;
  323.     return siz;
  324. }
  325. void vcnForceToCorrectSize(long space){
  326.     // pares down achive to fit user's limits
  327.     // since space is either 0 or a file size (before copy),
  328.     //  assume space needs to be inflated by INFLATION bytes to account
  329.     //  for header
  330.     long fudge_bytes=vcnAdjustForClusterSize(space>0L?space+INFLATION:0L);
  331.     while(vcnGetTargetDiskBytes()<(fudge_bytes+vcnGetActualDiskBytes())){
  332.         if(head==NULL)break;
  333.         vcnDeleteOldestItem();
  334.     }
  335.     return;
  336. }
  337. void vcnSaveVcnFile(void){
  338.     write_queue(); // save file data in VULCAN.VCN
  339. }
  340. void vcnDestroy(void){
  341.     write_queue(); // save file data in VULCAN.VCN
  342.     head=tail=cursor=NULL;
  343.     total_disk_bytes=highest=0L;
  344.     lowest=LONG_MAX;
  345.     free_all_space();
  346.     return;
  347. }
  348. static queue _far *vcnFind(long index){
  349. // used to find given record number
  350. // chooses closest pointer (head, tail, cursor) to traverse to record
  351.     if(head==NULL)return NULL;
  352.     if(index<lowest||index>highest)return NULL;
  353.     long h_diff,t_diff,c_diff=LONG_MAX;
  354.     if(cursor!=NULL){
  355.         c_diff=cursor->i.Index-index;
  356.         if(c_diff<0L)c_diff*=-1L;
  357.     }
  358.     h_diff=head->i.Index-index;
  359.     if(h_diff<0L)h_diff*=-1L;
  360.     t_diff=tail->i.Index-index;
  361.     if(t_diff<0L)t_diff*=-1L;
  362.     long min_diff=h_diff<t_diff?h_diff:t_diff;
  363.     min_diff=min_diff<c_diff?min_diff:c_diff;
  364.     if(h_diff==min_diff)cursor=head;
  365.     else if(t_diff==min_diff)cursor=tail;
  366.     int went_up=0,went_down=0;
  367.     while(cursor->i.Index!=index){
  368.         if(cursor->i.Index<index){cursor=cursor->n;went_up=1;}
  369.         else if(cursor->i.Index>index){cursor=cursor->p;went_down=1;}
  370.         if(went_up&&went_down)return NULL;
  371.     }
  372.     return cursor;
  373. }
  374. static void write_queue(){
  375.     if(!queue_needs_writing||head==NULL)return;
  376.     lstrcpy(pszTargetDirFile,szVulcanVcn); // ok, this is output filename
  377.     _dos_setfileattr(szTargetDirectory,_A_NORMAL);
  378.     remove(szTargetDirectory);
  379.     int fh;
  380.     unsigned retval;
  381.     if(retval=_dos_creatnew(szTargetDirectory,_A_NORMAL,&fh)){
  382.         char *p;
  383.         switch(errno){
  384.         case EACCES: p="access denied";             break;
  385.         case EEXIST: p="file already exists";       break;
  386.         case EMFILE: p="no available file handles"; break;
  387.         case ENOENT: p="path doesn't exist";        break;
  388.         default:     p="unknown error";             break;
  389.         }
  390.         wsprintf(pacBigScratchBuffer,"Could not open %s for writing.\n\n"
  391.             "DOS error code %u(%Xh), %s",(LPSTR)szTargetDirectory,
  392.             retval,retval,(LPSTR)p);
  393.         MessageBox(CURR_WINDOW,pacBigScratchBuffer,
  394.             szAppErrorTitle,MB_OK|MB_ICONSTOP);
  395.         return;
  396.     }
  397.     cursor=head;
  398.     while(cursor!=NULL){
  399.         vcn_info vi=cursor->i;
  400.         if(!vcnWriteEntry(vi,fh)){
  401.             MessageBox(CURR_WINDOW,"Could not write to VULCAN.VCN",
  402.                 szAppErrorTitle,MB_OK|MB_ICONSTOP);
  403.             return;
  404.         }
  405.         cursor=cursor->p;
  406.     }
  407.     _dos_close(fh);
  408.     queue_needs_writing=0;
  409. }
  410. int vcnWriteEntry(vcn_info& vcn,int fhandle,int* hsize){
  411.     // returns 1 for success, 0 for error
  412.     if(vcn.FullName==NULL||lstrlen(vcn.FullName)>250)return 0;
  413.     vcn_info *p=(vcn_info *)pacBigScratchBuffer;
  414.     *p=vcn;                                // copy reproducable data to buffer
  415.     unsigned char *ucp=(unsigned char *)&p->FullName;
  416.     *ucp++=(unsigned char)(lstrlen(vcn.FullName)+1); // write length to buffer
  417.     lstrcpy((char __far *)ucp,vcn.FullName);           // write name to buffer
  418.     while(*ucp++);                                        // point past string
  419.     unsigned write_size=ucp-(unsigned char *)pacBigScratchBuffer;
  420.     if(hsize!=NULL)*hsize=(int)write_size;
  421.     unsigned actual_written;
  422.     if(_dos_write(fhandle,pacBigScratchBuffer,write_size,&actual_written)||
  423.     (actual_written!=write_size)){
  424.         return 0;
  425.     }
  426.     return 1;
  427. }
  428. static int read_vcn_file(void){
  429.     // reads through VULCAN.VCN, putting all the data into queue
  430.     lstrcpy(pszTargetDirFile,szVulcanVcn);
  431.     int fh;
  432.     if(_dos_open(szTargetDirectory,_O_RDONLY,&fh)){
  433.         MessageBox(CURR_WINDOW,"Could not open VULCAN.VCN for reading",
  434.             szAppErrorTitle,MB_OK|MB_ICONSTOP);
  435.         return 1;
  436.     }
  437.     vcn_info *p=(vcn_info *)pacBigScratchBuffer;
  438.     while(vcnReadEntry(p,pacBigScratchBuffer2,fh)){
  439.         queue _far *oldrec=vcnFind(p->Index);
  440.         if(oldrec==NULL)continue; // don't care, file's gone
  441.         if(lstrlen(pacBigScratchBuffer2)<4){  // no filename
  442.             _dos_close(fh);
  443.             return 1;
  444.         }
  445.         if(lstrlen(pacBigScratchBuffer2)>200){ // bogus filename
  446.             _dos_close(fh);
  447.             return 1;
  448.         }
  449.         p->FullName=(char __far *)get_space(lstrlen(pacBigScratchBuffer2)+1);
  450.         if(p->FullName==NULL){ // out of memory
  451.             _dos_close(fh);
  452.             return 0;
  453.         }
  454.         lstrcpy(p->FullName,pacBigScratchBuffer2);
  455.         char __far *filnam=p->FullName;
  456.         while(*filnam)filnam++; // point at null terminator
  457.         while(filnam>p->FullName &&
  458.         filnam[-1]!='\\' && filnam[-1]!=':')
  459.             filnam--; // point at start of filename
  460.         p->FileName=filnam;
  461.         oldrec->i.FileSize=p->FileSize; // save rest of data
  462.         oldrec->i.FileDate=p->FileDate;
  463.         oldrec->i.FileTime=p->FileTime;
  464.         oldrec->i.Attrib=p->Attrib;
  465.         oldrec->i.DispChar=p->DispChar;
  466.         oldrec->i.version=p->version;
  467.         oldrec->i.compressed=p->compressed;
  468.         oldrec->i.signature=p->signature;
  469.         oldrec->i.FileName=p->FileName;
  470.         oldrec->i.FullName=p->FullName;
  471.     }
  472.     _dos_close(fh);
  473.     return 1;
  474. }
  475. static int read_vcn_filedata(long index,vcn_info __far * vcnp){
  476.     // reads data from actual XXXXXXXX.VCN file
  477.     // if successful, returns 1 otherwise 0
  478.     wsprintf(pszTargetDirFile,"%08ld.VCN",index);
  479.     int fh;
  480.     if(_dos_open(szTargetDirectory,_O_RDONLY,&fh))return 0;
  481.     vcn_info *p=(vcn_info *)pacBigScratchBuffer;
  482.     if(!vcnReadEntry(p,pacBigScratchBuffer2,fh)){
  483.         _dos_close(fh);
  484.         return 0;
  485.     }
  486.     p->FullName=(char __far *)get_space(lstrlen(pacBigScratchBuffer2)+1);
  487.     if(p->FullName==NULL){
  488.         _dos_close(fh);
  489.         return 0;
  490.     }
  491.     lstrcpy(p->FullName,pacBigScratchBuffer2);
  492.     char __far *filnam=p->FullName;
  493.     while(*filnam)filnam++; // point at null terminator
  494.     while(filnam>p->FullName &&
  495.     filnam[-1]!='\\' && filnam[-1]!=':')
  496.         filnam--; // point at start of filename
  497.     p->FileName=filnam;
  498.     vcnp->FullName=p->FullName;
  499.     vcnp->FileName=filnam;
  500.     vcnp->FileSize=p->FileSize; // save rest of data
  501.     vcnp->FileDate=p->FileDate;
  502.     vcnp->FileTime=p->FileTime;
  503.     vcnp->Attrib=p->Attrib;
  504.     vcnp->DispChar=p->DispChar;
  505.     vcnp->version=p->version;
  506.     vcnp->compressed=p->compressed;
  507.     vcnp->signature=p->signature;
  508.     _dos_close(fh);
  509.     return 1;
  510. }
  511. int vcnReadEntry(vcn_info* vcnp,char* filename,int fhandle){
  512.     // reads from fhandle into vcnp, then reads filename
  513.     // returns 0 for EOF, corruption, bad data
  514.     // does not zero out bogus fields
  515.     // can overwrite vcnp, filename even on failure
  516.     unsigned actual_read;
  517.     unsigned read_count=(unsigned)((char *)&vcnp->FullName-(char *)vcnp);
  518.     // first read first elements of vcn_info
  519.     if(_dos_read(fhandle,vcnp,read_count,&actual_read)||
  520.     read_count!=actual_read||
  521.     vcnp->version!=1||
  522.     vcnp->signature!=SIGNATURE)return 0;
  523.     // now read length of string following
  524.     unsigned char len;
  525.     read_count=1;
  526.     if(_dos_read(fhandle,&len,read_count,&actual_read)||
  527.     read_count!=actual_read||len<4)
  528.         return 0;
  529.     // now read actual string
  530.     read_count=(unsigned)len;
  531.     filename[len]=0;
  532.     if(_dos_read(fhandle,filename,read_count,&actual_read)||
  533.     read_count!=actual_read||
  534.     lstrlen(filename)+1!=(int)read_count)
  535.         return 0;
  536.     return 1;
  537. }
  538. static void __far *get_space(int bytes){
  539.     static char __far *free_space=NULL;
  540.     static int bytes_left=0;
  541.     void __far *retval;
  542.     if(reset_static_pointers){
  543.         reset_static_pointers=0;
  544.         free_space=NULL;
  545.         bytes_left=0;
  546.     }
  547.     if(bytes<1)return NULL;
  548.     if(bytes<bytes_left){
  549.         retval=free_space;
  550.         bytes_left-=bytes;
  551.         free_space+=bytes;
  552.         return retval;
  553.     }
  554.     if((size_t)bytes>(desired_malloc_size+sizeof(malloc_chain)))
  555.         return NULL; // just can't satisfy some people
  556.     {                                   // OK, get some space
  557.         malloc_chain __far *newspace;
  558.         HGLOBAL newhandle=GlobalAlloc(GMEM_FIXED,desired_malloc_size);
  559.         if(newhandle==NULL)return NULL; // bail if get no allocation
  560.         newspace=(malloc_chain __far *)GlobalLock(newhandle);
  561.         if(newspace==NULL){             // bail if can't lock
  562.             GlobalFree(newhandle);
  563.             return NULL;
  564.         }
  565.         newspace->hg=newhandle;
  566.         newspace->next=malloc_head;
  567.         free_space=(char __far *)&newspace[1];
  568.         bytes_left=desired_malloc_size-sizeof(malloc_chain);
  569.         malloc_head=newspace;
  570.     }
  571.     retval=free_space;              // now we know we have the space; use it
  572.     bytes_left-=bytes;
  573.     free_space+=bytes;
  574.     return retval;
  575. }
  576. static void free_all_space(void){
  577.     while(malloc_head!=NULL){
  578.         HGLOBAL goner=malloc_head->hg;
  579.         malloc_head=malloc_head->next;
  580.         GlobalUnlock(goner);
  581.         GlobalFree(goner);
  582.     }
  583.     reset_static_pointers=1;
  584. }
  585. static long target_drive_bytes_avail(){
  586.     // returns bytes avail on drive named in szTargetDirectory
  587.     _diskfree_t diskspace;
  588.     if(_dos_getdiskfree(toupper(szTargetDirectory[0])-'A'+1,
  589.     &diskspace))
  590.         return 0L;
  591.     return (long)diskspace.avail_clusters*cluster_size;
  592. }
  593.