/* Data Library support */ #include "common.h" #include "help.h" #include "datalib.h" extern Global(CharPtr PNTR) DatalibTxt; /* defined in text.c */ enum { /* see setup.c, init[], /datalib */ LIST, /* 38 20 ! listwidth listheight */ RENAME_T, GROUP_T, CREATE_T, DELETE_T, DELETEGF_T, DELETEALL_T, SELECT_B, DESELECT_B, ESCAPE_B, CANCEL_B, HELP_B, SEPARATOR_I, FUNCTION_M, RENAME_I, GROUP_I, EDIT_I, CREATE_I, DELETE_I, DELETEALL_I, /*** COPY_I,***/ IMPORT_I, EXPORT_I, DUMMYEND_I } DatalibTxtIndex; /*--------------------*/ /* Low-level routines */ #define IOERR -1 #define WRITE_LOCK 1 Local(Int2) Fseek(DataLibPtr dl, long offset, int whence) { #if DEB if (offset==0 && whence==SEEK_SET) if (myMessage(MSG_OKC,"Fseek('%s',0,SEEK_SET)",dl->FileName) ==ANS_CANCEL) Abend(); #endif return (fseek(dl->Stream,offset,whence)==0) ? 0 : (dl->Flags|=WRITE_LOCK, myWarning("DL_SEEK",dl->FileName,offset), IOERR ); } Local(FilePtr) Ftell(DataLibPtr dl) { FilePtr fp; fp=ftell(dl->Stream); if (fp==-1L) { dl->Flags|=WRITE_LOCK; myWarning("DL_TELL",dl->FileName,strerror(errno)); } return fp; } Local(Int2) Fread(DataLibPtr dl, VoidPtr buf, size_t len) { if (len==0) return 0; return (fread(buf,len,1,dl->Stream)==1) ? 0 : (dl->Flags|=WRITE_LOCK, myWarning("DL_READ",dl->FileName,(int)len,Ftell(dl)), IOERR ); } Local(Int2) Fwrite(DataLibPtr dl, VoidPtr buf, size_t len) { if (dl->Flags&WRITE_LOCK) return IOERR; if (len==0) return 0; return (fwrite(buf,len,1,dl->Stream)==1) ? 0 : (dl->Flags|=WRITE_LOCK, myWarning("DL_WRITE",dl->FileName,(int)len,Ftell(dl)), IOERR ); } /* DataLib Header */ typedef struct { /* Header of archives */ FilePtr FreePtr; /* Pointer to the list of free Blocks */ FilePtr DirPtr; /* Pointer to the first directory entry */ FilePtr CurDirPtr; /* saved copies of DataLib fields */ FilePtr SelectedDirPtr; FilePtr ParentDirPtr; FilePtr FirstDirPtr; Int2 MinDepth; } Header; /* Read and Write header */ Local(void) ReadHeader(DataLibPtr dl, Header PNTR h) { rewind(dl->Stream); Fread(dl,h,sizeof(Header)); } Local(void) WriteHeader(DataLibPtr dl, Header PNTR h) { rewind(dl->Stream); Fwrite(dl,h,sizeof(Header)); fflush(dl->Stream); } /* Read len bytes into buffer buf starting from the current position */ Global(size_t) DataLibRead(DataLibPtr dl, CharPtr buf, size_t len) { FileLen l,w; for (l=0; len && dl->CurBlockPtr; ) { w=(FileLen)(dl->CurDesc.DataLen-dl->CurOffset); if (len>w) { if (Fread(dl,buf,(size_t)w)) return IOERR; buf+=(size_t)w; len-=w; l+=w; dl->PrevBlockPtr=dl->CurBlockPtr; /* 23.04.95. Make DataLibTell work after Read */ dl->CurBlockPtr=dl->CurDesc.Next; if (dl->CurBlockPtr) { if (Fseek(dl,dl->CurBlockPtr,SEEK_SET)) return IOERR; if (Fread(dl,&dl->CurDesc,sizeof(BlockDesc))) return IOERR; } dl->CurOffset=0; } else { if (Fread(dl,buf,(size_t)len)) return IOERR; l+=len; dl->CurOffset+=len; len=0; } } return l; } /* Writes len bytes from buffer buf starting from the current position. If both dl->CurBlockPtr and dl->PrevBlockPtr are zeroes new chain of Blocks is created and its head Block's FilePtr is assigned to FirstBlockPtr. This is the only way of creating new Blocks or reusing of the Blocks from the Free List. */ Local(FilePtr) FirstBlockPtr; /* Pointer to the first Block that was allocated or created by DataLibRead */ Global(size_t) DataLibWrite(DataLibPtr dl, CharPtr buf, size_t len) { FileLen l,w; Header h; FilePtr eof; if (!dl) return 0; FirstBlockPtr=0; /* Overwrite existent data */ for (l=0; len && dl->CurBlockPtr; ) { w=(FileLen)(dl->CurDesc.Len-dl->CurOffset); if (dl->CurDesc.Len>dl->CurDesc.DataLen) { /* Update DataLen field for the first Block (former free block) */ FilePtr cur; cur=Ftell(dl); dl->CurDesc.DataLen=dl->CurOffset+MIN(w,len); if (Fseek(dl,dl->CurBlockPtr,SEEK_SET)) return l; if (Fwrite(dl,&dl->CurDesc,sizeof(BlockDesc))) return l; if (Fseek(dl,cur,SEEK_SET)) return l; } if (len>w) { if (Fwrite(dl,buf,(size_t)w)) return l; buf+=(size_t)w; len-=w; l+=w; dl->PrevBlockPtr=dl->CurBlockPtr; dl->CurBlockPtr=dl->CurDesc.Next; if (dl->CurBlockPtr) { if (Fseek(dl,dl->CurBlockPtr,SEEK_SET)) return l; if (Fread(dl,&dl->CurDesc,sizeof(BlockDesc))) return l; if (Fseek(dl,0,SEEK_CUR)) return l; /* after fread before fwrite */ dl->CurOffset=0; } else dl->CurOffset+=w; } else { if (Fwrite(dl,buf,(size_t)len)) return l; l+=len; dl->CurOffset+=len; len=0; } } if (len) { /* Append to the End Of Partition. First, we'll use the Blocks from the Free List. Second, remaining data, if any will be appended to the EOF */ ReadHeader(dl,&h); if (h.FreePtr) { /* Use Blocks form Free List */ FirstBlockPtr=h.FreePtr; /* Link the previous Block with the current one */ if (dl->PrevBlockPtr) { if (Fseek(dl,dl->PrevBlockPtr,SEEK_SET)) return l; if (Fread(dl,&dl->CurDesc,sizeof(BlockDesc))) return l; /* Is the next free Block adjusent to the Previous? If so, merge them */ w=0; if (dl->PrevBlockPtr+sizeof(BlockDesc)+dl->CurDesc.Len==h.FreePtr) { BlockDesc de; if (Fseek(dl,h.FreePtr,SEEK_SET)) return l; if (Fread(dl,&de,sizeof(BlockDesc))) return l; h.FreePtr=de.Next; WriteHeader(dl,&h); w=(FileLen)(sizeof(BlockDesc)+de.Len); /* shrink by this value */ dl->CurDesc.Len+=w; dl->CurOffset=dl->CurDesc.DataLen; } else dl->CurDesc.Next=h.FreePtr; if (Fseek(dl,dl->PrevBlockPtr,SEEK_SET)) return l; if (Fwrite(dl,&dl->CurDesc,sizeof(BlockDesc))) return l; if (w) { /* write to the shrinked block */ dl->CurBlockPtr=dl->PrevBlockPtr; if (Fseek(dl,dl->CurOffset,SEEK_CUR)) return l; w=DataLibWrite(dl,buf,MIN(len,w)); /* CurDesc,Cur/PrevBlockPtr will not be changed */ buf+=(size_t)w; len-=w; l+=w; if (len && h.FreePtr) { dl->CurDesc.Next=h.FreePtr; if (Fseek(dl,dl->PrevBlockPtr,SEEK_SET)) return l; if (Fwrite(dl,&dl->CurDesc,sizeof(BlockDesc))) return l; } } } /* Travel Free List and write data into its Blocks */ /* The following if (len) statement was added to prevent CurBlockPtr from being set to zero in a case when successful merging has been done and merged block is not still full after w=DataLibWrite(dl,buf,MIN(len,w)); */ if (len) /*++ 11.07.94 ++*/ for (dl->CurBlockPtr=h.FreePtr; len && dl->CurBlockPtr; ) { dl->PrevBlockPtr=dl->CurBlockPtr; if (Fseek(dl,dl->CurBlockPtr,SEEK_SET)) return l; if (Fread(dl,&dl->CurDesc,sizeof(BlockDesc))) return l; w=(FileLen)(len>dl->CurDesc.Len ? (dl->CurBlockPtr=dl->CurDesc.Next,dl->CurDesc.Len) : len); dl->CurDesc.DataLen=w; if (Fseek(dl,dl->PrevBlockPtr,SEEK_SET)) return l; if (Fwrite(dl,&dl->CurDesc,sizeof(BlockDesc))) return l; /* update DataLen field */ if (Fwrite(dl,buf,(size_t)w)) return l; buf+=(size_t)w; len-=w; l+=w; dl->CurOffset=w; h.FreePtr=dl->CurDesc.Next; /* this must not be done if the loop is bypassed */ } /* Exclude the nodes just written from the Free List */ if (dl->CurDesc.Len-dl->CurDesc.DataLen>sizeof(BlockDesc)) { /* Split last written block */ BlockDesc b; b.Next=h.FreePtr; b.Len=(FileLen)(dl->CurDesc.Len-dl->CurDesc.DataLen-sizeof(BlockDesc)); h.FreePtr=(FilePtr)(dl->PrevBlockPtr+sizeof(BlockDesc)+dl->CurDesc.DataLen); if (Fseek(dl,h.FreePtr,SEEK_SET)) return l; if (Fwrite(dl,&b,sizeof(BlockDesc))) return l; dl->CurDesc.Len=dl->CurDesc.DataLen; dl->CurBlockPtr=0; /* The following dl->CurBlockPtr=0; statement was added to ensure CurBlockPtr is zero in a case of splitting last written block. In such a case len always is zero. */ dl->CurBlockPtr=0; /*++ 11.07.94 ++*/ } WriteHeader(dl,&h); dl->CurDesc.Next=0; if (Fseek(dl,dl->PrevBlockPtr,SEEK_SET)) return l; if (Fwrite(dl,&dl->CurDesc,sizeof(BlockDesc))) return l; if (Fseek(dl,(long)(dl->PrevBlockPtr+sizeof(BlockDesc)+dl->CurOffset),SEEK_SET)) return l; } /* Assert: dl->CurBlockPtr=0 xor len=0, dl->PrevBlockPtr is the last written Block, dl->CurOffset is correct offset in it, dl->CurDesc contains the last written Block descriptor */ if (len) { /* Free List is exhausted, append to EOF */ /* Assert: dl->PrevBlockPtr!=0 ==> its Len==DataLen */ /* Get file pointer to the place where to write */ if (Fseek(dl,0,SEEK_END)) return l; eof=Ftell(dl); if (dl->PrevBlockPtr) { if (Fseek(dl,dl->PrevBlockPtr,SEEK_SET)) return l; if (Fread(dl,&dl->CurDesc,sizeof(BlockDesc))) return l; if (dl->PrevBlockPtr+sizeof(BlockDesc)+dl->CurDesc.Len==eof) { dl->CurDesc.Len+=len; dl->CurDesc.DataLen+=len; if (Fseek(dl,dl->PrevBlockPtr,SEEK_SET)) return l; if (Fwrite(dl,&dl->CurDesc,sizeof(BlockDesc))) return l; } else { dl->CurDesc.Next=eof; if (Fseek(dl,dl->PrevBlockPtr,SEEK_SET)) return l; if (Fwrite(dl,&dl->CurDesc,sizeof(BlockDesc))) return l; if (Fseek(dl,0,SEEK_END)) return l; dl->PrevBlockPtr=eof; dl->CurOffset=0; dl->CurDesc.Next=0; dl->CurDesc.Len=dl->CurDesc.DataLen=len; if (Fwrite(dl,&dl->CurDesc,sizeof(BlockDesc))) return l; if (!FirstBlockPtr) FirstBlockPtr=eof; } } else { dl->PrevBlockPtr=eof; dl->CurOffset=0; dl->CurDesc.Next=0; dl->CurDesc.Len=dl->CurDesc.DataLen=len; if (Fwrite(dl,&dl->CurDesc,sizeof(BlockDesc))) return l; if (!FirstBlockPtr) FirstBlockPtr=eof; } if (Fseek(dl,0,SEEK_END)) return l; if (Fwrite(dl,buf,(size_t)len)) return l; l+=len; dl->CurOffset+=len; } } if (l) fflush(dl->Stream); return l; } /* Frees the chain of Blocks */ Local(void) FreeChain(DataLibPtr dl, FilePtr fp) { Header h; FilePtr wfp; FilePtr cp; FilePtr pp; BlockDesc fb, pb; #define cb pb ReadHeader(dl,&h); for (; fp; fp=wfp) { /* Read the description of the current Block to be freed and get it successor's File pointer */ if (Fseek(dl,fp,SEEK_SET)) return; if (Fread(dl,&fb,sizeof(BlockDesc))) return; wfp=fb.Next; /* Find the place in the Free List where to include */ for (pp=0, cp=h.FreePtr; cp && cpCurBlockPtr=pos; dl->CurOffset=0; dl->PrevBlockPtr=0; if (pos) { if (Fseek(dl,pos,SEEK_SET)) return 2; /* cannot fseek */ if (Fread(dl,&dl->CurDesc,sizeof(BlockDesc))) return 3; /* cannot read */ if (Fseek(dl,0,SEEK_CUR)) return 3; /* to allow subsequent write */ return 0; } else return 1; /* position is invalid */ } /* Tell/Seek */ Global(Int2) DataLibTell(DataLibPtr dl, DataLibPosPtr pos) { FilePtr fp; BlockDesc bl; if (dl->CurBlockPtr) { pos->BlockPtr=dl->CurBlockPtr; pos->Offset=dl->CurOffset; return 0; } if (dl->PrevBlockPtr) { pos->BlockPtr=dl->PrevBlockPtr; if (dl->CurOffset) pos->Offset=dl->CurOffset; else { fp=Ftell(dl); if (Fseek(dl,dl->PrevBlockPtr,SEEK_SET)) return -2; if (Fread(dl,&bl,sizeof(bl))) return -4; pos->Offset=bl.DataLen; if (Fseek(dl,fp,SEEK_SET)) return -3; } return 0; } pos->BlockPtr=0; return -1; } Global(Int2) DataLibSeek(DataLibPtr dl, DataLibPosPtr pos) { Int2 er; if (pos->BlockPtr) { er=Seek(dl,pos->BlockPtr); if (er) return er; if (Fseek(dl,pos->Offset,SEEK_CUR)) return -2; dl->CurOffset=pos->Offset; return 0; } return -1; } /*------------------------------*/ /* Middle-level Global routines */ Global(Boolean) DataLibIsClosed(DataLibPtr dl) { return dl->Stream==NULL; } /* Opens a Data Library */ Global(Int2) DataLibOpen(DataLibPtr dl, CharPtr fn) { Header h; WindoW w; if (!DataLibIsClosed(dl)) return 0; /* it's opened yet */ w=dl->Window; MemFill(dl,0,sizeof(*dl)); dl->Window=w; dl->Stream=fopen(fn,"r+b"); if (!dl->Stream) { /* file does not exists or is unreadable */ dl->Stream=fopen(fn,"wb"); if (dl->Stream) { MemFill(&h,0,sizeof(h)); if (Fwrite(dl,&h,sizeof(Header))) return 3; fclose(dl->Stream); dl->Stream=fopen(fn,"r+b"); if (!dl->Stream) return 2; /* unreadable */ } else return 1; /* cannot open for write */ } dl->FileName=_StringSave(fn); if (Fread(dl,&h,sizeof(Header))) return 2; dl->CurDirPtr=h.CurDirPtr; dl->SelectedDirPtr=h.SelectedDirPtr; dl->ParentDirPtr=h.ParentDirPtr; dl->FirstDirPtr=h.FirstDirPtr; dl->MinDepth=h.MinDepth; DataLibSeekDir(dl,dl->SelectedDirPtr ? dl->SelectedDirPtr : dl->CurDirPtr); return 0; /* OK */ } /* Closes a Data Library */ Global(void) DataLibClose(DataLibPtr dl) { Header h; FilePtr cp,pp; Int2 err; if (dl->Stream) { /* don't close just closed */ /* Truncate file if possible */ ReadHeader(dl,&h); if (h.FreePtr) { for (pp=0,cp=h.FreePtr; (err=Seek(dl,cp),!err && dl->CurDesc.Next); pp=cp, cp=dl->CurDesc.Next); if (err==0) { if (Fseek(dl,0,SEEK_END)) goto endtrunc; if (cp+sizeof(BlockDesc)+dl->CurDesc.Len==Ftell(dl)) { if (pp) { if (Seek(dl,pp)) goto endtrunc; dl->CurDesc.Next=0; if (Fseek(dl,pp,SEEK_SET)) goto endtrunc; if (Fwrite(dl,&dl->CurDesc,sizeof(BlockDesc))) goto endtrunc; } else { h.FreePtr=0; WriteHeader(dl,&h); } TruncateFile(dl->Stream,cp); } } } endtrunc:; /* Save current and selected dir entries ptrs */ ReadHeader(dl,&h); h.CurDirPtr=dl->CurDirPtr; h.SelectedDirPtr=dl->SelectedDirPtr; h.ParentDirPtr=dl->ParentDirPtr; h.FirstDirPtr=dl->FirstDirPtr; h.MinDepth=dl->MinDepth; WriteHeader(dl,&h); dl->FileName=_MemFree(dl->FileName); /* Close file */ fclose(dl->Stream); dl->Stream=NULL; } } /* Flush data to file */ Global(void) DataLibFlush(DataLibPtr dl) { CharPtr fn; DataLibPtr dlp; fn=_StringSave(dl->FileName); dlp=dl->Saved; DataLibClose(dl); DataLibOpen(dl,fn); dl->Saved=dlp; _MemFree(fn); } /* Directory entry */ typedef struct { FilePtr PrevPtr; /* To previous and next entries of the same level */ FilePtr NextPtr; FilePtr ParentPtr; /* To the parent entry */ FilePtr ChildPtr; /* To the first child entry */ FilePtr DataPtr; /* To the first Block of partition data */ FilePtr UserPtr; /* Used only by DataLibSetUserPtr/DataLibGetUserPtr */ Uchar NameLen; /* The name itself immediately follows this filed */ /* DON'T add any field here. NameLen MUST BE THE LAST field */ } DirEntry; typedef DirEntry PNTR DirEntryPtr; /* Reads a directory entry */ Local(Int2) ReadDir(DataLibPtr dl, FilePtr ptr, DirEntryPtr de) { dl->DirName[1]='\0'; if (!dl || Seek(dl,ptr)) return 1; /* cannot seek */ if (DataLibRead(dl,(CharPtr)de,sizeof(DirEntry))!=sizeof(DirEntry)) return 2; /* cannot read dir entry */ if (DataLibRead(dl,dl->DirName,de->NameLen)!=de->NameLen) return 3; /* cannot read dir name */ return 0; } /* Writes a directory entry */ Local(Int2) WriteDir(DataLibPtr dl, FilePtr ptr, DirEntryPtr de) { if (!dl || Seek(dl,ptr)) return 1; /* cannot seek */ de->NameLen=(Uchar)(StrLen(dl->DirName)+1); if (DataLibWrite(dl,(CharPtr)de,sizeof(DirEntry))!=sizeof(DirEntry)) return 2; /* cannot write */ if (DataLibWrite(dl,dl->DirName,de->NameLen)!=de->NameLen) return 2; /* cannot write */ fflush(dl->Stream); return 0; } /* Searches current directory for the entry with given name. Returns 0 and sets all the fields of *dl in success, returns non-zero otherwise. */ Global(Int2) DataLibFind(DataLibPtr dl, CharPtr name) { DirEntry de; Uchar namelen; namelen=(Uchar)(StrLen(name)+1); /* trailing zero stored in dir entry */ for (dl->CurDirPtr=dl->FirstDirPtr; dl->CurDirPtr; dl->CurDirPtr=de.NextPtr) { if (ReadDir(dl,dl->CurDirPtr,&de)) return 2; /* cannot read directory entry */ if (namelen!=de.NameLen) continue; if (!StrCmp(name,dl->DirName)) { /* found */ Seek(dl,de.DataPtr); return 0; } } return 1; /* not found */ } /* Like DataLibFind except the given name is allowed to be a substring of partition names. Returns 0 and sets all the fields of *dl in success, returns non-zero otherwise. */ Global(Int2) DataLibFindBySubstr(DataLibPtr dl, CharPtr name, Boolean from1st) { DirEntry de; if (from1st) dl->CurDirPtr=dl->FirstDirPtr; else { if (ReadDir(dl,dl->CurDirPtr,&de)) return 2; /* cannot read directory entry */ dl->CurDirPtr=de.NextPtr; } for ( ; dl->CurDirPtr; dl->CurDirPtr=de.NextPtr) { if (ReadDir(dl,dl->CurDirPtr,&de)) return 2; /* cannot read directory entry */ if (StrStr(dl->DirName,name)) { /* found */ Seek(dl,de.DataPtr); return 0; } } return 1; /* not found */ } /* Creates a new directory entry and first dummy data Block */ Global(Int2) DataLibCreate(DataLibPtr dl, CharPtr name) { Header h; DirEntry de,dw; DataLibPtr dlp; Char dummy='\0'; MemFill(&de,0,sizeof(DirEntry)); de.ParentPtr=dl->ParentDirPtr; de.NextPtr=dl->FirstDirPtr; de.NameLen=(Uchar)(StrLen(name)+1); /* +1 is for trailing zero */ if (de.NameLen>MAX_NAME_LEN) { de.NameLen=MAX_NAME_LEN; name[MAX_NAME_LEN-1]='\0'; } dl->CurBlockPtr=dl->PrevBlockPtr=0; if (DataLibWrite(dl,(CharPtr)&de,sizeof(DirEntry))!=sizeof(DirEntry)) return 1; /* cannot create dir entry */ dl->FirstDirPtr=dl->CurDirPtr=FirstBlockPtr; /********* if (dl->Saved && dl->Saved->CurDirPtr==0) dl->Saved->CurDirPtr=FirstBlockPtr; *********/ for (dlp=dl; dlp->Saved; dlp=dlp->Saved) if (dlp->Saved->CurDirPtr==0) dlp->Saved->CurDirPtr=FirstBlockPtr; if (DataLibWrite(dl,name,de.NameLen)!=de.NameLen) return 1; /* cannot create dir entry */ if (de.NextPtr) { if (ReadDir(dl,de.NextPtr,&dw)) return 2; /* cannot read prev dir */ dw.PrevPtr=dl->CurDirPtr; if (WriteDir(dl,de.NextPtr,&dw)) return 3; /* cannot write prev dir */ } if (dl->ParentDirPtr) { if (ReadDir(dl,dl->ParentDirPtr,&dw)) return 4; /* cannot read parent dir */ dw.ChildPtr=dl->CurDirPtr; if (WriteDir(dl,dl->ParentDirPtr,&dw)) return 5; /* cannot write parent dir */ } else { ReadHeader(dl,&h); h.DirPtr=dl->CurDirPtr; WriteHeader(dl,&h); } dl->CurBlockPtr=dl->PrevBlockPtr=0; if (*name!=SUBDIR_CHAR) { /* Create first empty data block */ if (DataLibWrite(dl,&dummy,1)!=1) return 6; /* cannot create data Block */ de.DataPtr=FirstBlockPtr; dl->CurOffset--; /* get rid of dummy char */ StrCpy(dl->DirName,name); WriteDir(dl,dl->CurDirPtr,&de); Seek(dl,de.DataPtr); } return 0; } /* Renames current directory */ Global(Int2) DataLibRename(DataLibPtr dl, CharPtr newname) { DirEntry de; if (dl->CurDirPtr) { if (ReadDir(dl,dl->CurDirPtr,&de)) return 2; StrCpy(dl->DirName,newname); return WriteDir(dl,dl->CurDirPtr,&de); } return 1; } /* Deletes current directory entry and frees data Blocks associated with it */ Global(Int2) DataLibDelete(DataLibPtr dl) { Header h; FilePtr cur; DirEntry de,dw; DataLibPtr dlp; if (!dl->CurDirPtr) return 0; /* nothing to delete */ if (ReadDir(dl,dl->CurDirPtr,&de)) return 1; /* cannot read dir */ if (dl->NameFirstChar==SUBDIR_CHAR && de.ChildPtr) return 8;/* subdir is not empty */ FreeChain(dl,de.DataPtr); if (de.NextPtr) { if (ReadDir(dl,de.NextPtr,&dw)) return 2; /* cannot read next dir */ dw.PrevPtr=de.PrevPtr; if (WriteDir(dl,de.NextPtr,&dw)) return 3; /* cannot write next dir */ } if (de.PrevPtr) { if (ReadDir(dl,de.PrevPtr,&dw)) return 4; /* cannot read prev dir */ dw.NextPtr=de.NextPtr; if (WriteDir(dl,de.PrevPtr,&dw)) return 5; /* cannot write prev dir */ } else { if (dl->ParentDirPtr) { if (ReadDir(dl,dl->ParentDirPtr,&dw)) return 6; /* cannot read parent */ dw.ChildPtr=de.NextPtr; if (WriteDir(dl,dl->ParentDirPtr,&dw)) return 7; /* cannot write parent */ } else { ReadHeader(dl,&h); h.DirPtr=de.NextPtr; WriteHeader(dl,&h); } dl->FirstDirPtr=de.NextPtr; } cur=dl->CurDirPtr; if (cur==dl->SelectedDirPtr) { dl->SelectedDirPtr=0; /********* if (dl->Saved && cur==dl->Saved->SelectedDirPtr) dl->Saved->SelectedDirPtr=0; *********/ } for (dlp=dl; dlp->Saved; dlp=dlp->Saved) if (cur==dlp->Saved->SelectedDirPtr) dlp->Saved->SelectedDirPtr=0; if (DataLibNext(dl,1)) DataLibPrev(dl,1); if (cur==dl->CurDirPtr) { dl->CurDirPtr=0; /********* if (dl->Saved && cur==dl->Saved->CurDirPtr) dl->Saved->CurDirPtr=0; *********/ } for (dlp=dl; dlp->Saved; dlp=dlp->Saved) if (cur==dlp->Saved->CurDirPtr) dlp->Saved->CurDirPtr=0; FreeChain(dl,cur); return 0; } /* Makes the n-th previous dir entry the current one */ Global(Int2) DataLibPrev(DataLibPtr dl, Int2 n) { FilePtr fp; DirEntry de; if (dl->CurDirPtr) { if (ReadDir(dl,dl->CurDirPtr,&de)) return 3; /* cannot read dir entry */ fp=dl->CurDirPtr; while (n--) do { if (!de.PrevPtr) return 2; /* no prev dir entry */ fp=de.PrevPtr; if (ReadDir(dl,fp,&de)) return 3; /* cannot read dir entry */ } while (dl->NameFirstChar==HIDDEN_CHAR); dl->CurDirPtr=fp; return 0; } else return 1; /* subdir is empty */ } /* Makes the n-th next dir entry the current one */ Global(Int2) DataLibNext(DataLibPtr dl, Int2 n) { FilePtr fp; DirEntry de; if (dl->CurDirPtr) { if (ReadDir(dl,dl->CurDirPtr,&de)) return 3; /* cannot read dir entry */ fp=dl->CurDirPtr; while (n--) do { if (!de.NextPtr) return 2; /* no next dir entry */ fp=de.NextPtr; if (ReadDir(dl,fp,&de)) return 3; /* cannot read dir entry */ } while (dl->NameFirstChar==HIDDEN_CHAR); dl->CurDirPtr=fp; return 0; } else return 1; /* subdir is empty */ } /* Makes the parent dir entry the current one */ Global(Int2) DataLibUp(DataLibPtr dl) { DirEntry de; Header h; if (dl->ParentDirPtr) { if (ReadDir(dl,dl->ParentDirPtr,&de)) return 2; /* cannot read parent */ dl->CurDirPtr=dl->ParentDirPtr; dl->ParentDirPtr=de.ParentPtr; if (de.ParentPtr) { if (ReadDir(dl,de.ParentPtr,&de)) return 3; /* cannot read parent dir entry */ dl->FirstDirPtr=de.ChildPtr; } else { ReadHeader(dl,&h); dl->FirstDirPtr=h.DirPtr; } ReadDir(dl,dl->CurDirPtr,&de); /* read into dl->DirName */ Seek(dl,de.DataPtr); /* prepare to read/write data */ dl->Depth--; return 0; } else return 1; /* there is no parent dir entry */ } /* Makes the 1-st child dir entry the current one */ Global(Int2) DataLibDown(DataLibPtr dl) { DirEntry de; FilePtr fp; if (dl->CurDirPtr) { if (ReadDir(dl,dl->CurDirPtr,&de)) return 2; /* cannot read dir entry */ /* There must not be here the check whether a Child exists. The application should be able to enter any subtree even if it is empty or is not subdirectory. High level callbacks of DataLibShow directed by the user must check anything by themselves. */ dl->ParentDirPtr=dl->CurDirPtr; fp=dl->FirstDirPtr=dl->CurDirPtr=de.ChildPtr; for (; fp && ReadDir(dl,fp,&de)==0 && dl->NameFirstChar==HIDDEN_CHAR; fp=de.NextPtr); if (fp) Seek(dl,de.DataPtr); dl->CurDirPtr=fp; dl->Depth++; return 0; } else return 1; /* cannot go down - subdir is empty */ } /* Makes given Dir entry the current one */ Global(Int2) DataLibSeekDir(DataLibPtr dl, FilePtr dp) { DirEntry de; FilePtr fp; Int2 depth; Header h; if (dp || dl->ParentDirPtr) { fp=dp ? (depth=-1,dp) : (depth=0,dl->ParentDirPtr); for (; fp; depth++,fp=de.ParentPtr) if (ReadDir(dl,fp,&de)) return 2; /* cannot read dir entry */ if (dp) { if (ReadDir(dl,dp,&de)) return 2; /* cannot read dir entry */ dl->ParentDirPtr=de.ParentPtr; if (de.ParentPtr) { if (ReadDir(dl,de.ParentPtr,&de)) return 3; /* cannot read parent dir entry */ dl->FirstDirPtr=de.ChildPtr; } else { ReadHeader(dl,&h); dl->FirstDirPtr=h.DirPtr; } ReadDir(dl,dp,&de); Seek(dl,de.DataPtr); /* prepare to read/write data */ } dl->CurDirPtr=dp; dl->Depth=depth; return 0; } else return 1; /* no last selected dir entry */ } /* Set/Get UserPtr field in Directory Entry */ Global(FilePtr) DataLibGetUserPtr(DataLibPtr dl, FilePtr dirptr) { DirEntry de; ReadDir(dl,dirptr,&de); DataLibSeekDir(dl,dl->CurDirPtr); return de.UserPtr; } Global(void) DataLibSetUserPtr(DataLibPtr dl, FilePtr dirptr, FilePtr datafp) { DirEntry de; ReadDir(dl,dirptr,&de); de.UserPtr=datafp; WriteDir(dl,dirptr,&de); DataLibSeekDir(dl,dl->CurDirPtr); } /* Updates SelectedDirPtr in the header */ Global(void) DataLibSetSelected(DataLibPtr dl) { Header h; DataLibPos dlp; if (DataLibIsClosed(dl)) return; DataLibTell(dl,&dlp); ReadHeader(dl,&h); h.SelectedDirPtr=dl->SelectedDirPtr; WriteHeader(dl,&h); DataLibSeek(dl,&dlp); } /*----------------------------------*/ /* Varivable-length records support */ /* Logical V-records are read/written by routines with the 'VRecord' string in their names. Each logical record consists of one or more physical records. Physical record consists of the descriptor and data. Descriptor has the type Uint2 and contains the number of bytes in data (without the len of the descriptor itself). If the value in the descriptor is UINT2_MAX then there is one more physical record follows (with possible zero value for the length). */ typedef Uint2 DescLen; /* MUST NOT BE CHANGED due to compatibility reasons */ /* This variabe contains number of bytes read by last DataLibReadVRecord call (excluded descriptor fields) */ Global(size_t) DataLibLastVRecLen; /* This variabe contains total number of bytes in descriptor fields read by last DataLibReadVRecord call */ Global(size_t) DataLibLastVRecDesc; /* returns total length of descriptor fields for Vrecord of the given length */ Global(size_t) DataLibGetDescLen(size_t VRecLen) { return sizeof(DescLen)*(VRecLen/UINT2_MAX+1); } /* Reads the entire VRecord from the currvent position (partition==NULL) or from the given partition otherwise. VRecord stands for Variable-size-Record. This is the same as DCB RECFM=V (Do you know what does it mean and where did it come from?) */ Global(CharPtr) DataLibReadVRecord(DataLibPtr dl, CharPtr partition) { CharPtr vp=NULL; Int2 rc; DescLen desc; DataLibLastVRecLen=DataLibLastVRecDesc=0; rc=partition ? DataLibFind(dl,partition) : 0; if (rc) vp=_MemNew(1); else do { if (DataLibRead(dl,(CharPtr)&desc,sizeof(desc))!=sizeof(desc)) break; DataLibLastVRecDesc+=sizeof(desc); vp=vp ? _MemMore(vp,DataLibLastVRecLen+desc) : _MemGet((size_t)desc,FALSE); if (!vp) break; DataLibLastVRecLen+=DataLibRead(dl,vp+DataLibLastVRecLen,(size_t)desc); } while (desc==UINT2_MAX); return vp; } /* Writes the VRecord starting from the current position (partition==NULL) or to a new partition otherwise */ Global(Int2) DataLibWriteVRecord(DataLibPtr dl, CharPtr partition, CharPtr buf, size_t len) { size_t desclen; DescLen desc; if (!dl) return 0; if (partition) { /* otherwise write to current */ if (DataLibFind(dl,partition)==0) DataLibDelete(dl); if (DataLibCreate(dl,partition)) return 1; } if (!buf) len=0; desclen=DataLibGetDescLen(len); while (desclen) { desc=(DescLen)MIN(len,UINT2_MAX); if (DataLibWrite(dl,(CharPtr)&desc,sizeof(desc))!=sizeof(desc)) return 2; if (DataLibWrite(dl,buf,(size_t)desc)!=(size_t)desc) return 2; len-=desc; buf+=desc; desclen-=sizeof(DescLen); } return 0; } /* Reads no more than n bytes from VRecord into the buffer pointed by buf and skips the rest of the VRecord. Returns the number of bytes actually read and sets DataLibLastVRecLen to the actual length of VRecored. */ Global(size_t) DataLibReadVRecordN(DataLibPtr dl, CharPtr buf, size_t maxlen) { CharPtr p; size_t al; p=DataLibReadVRecord(dl,NULL); al=MIN(maxlen,DataLibLastVRecLen); if (al>0) MemCopy(buf,p,al); _MemFree(p); return al; } /* Skips the entire VRecord */ Global(void) DataLibSkipVRecord(DataLibPtr dl) { _MemFree(DataLibReadVRecord(dl,NULL)); } /* Skips all the data up to the current partition's end */ Global(size_t) DataLibSkipPartition(DataLibPtr dl) { Char b[50]; size_t s,c; for (s=0; (c=DataLibRead(dl,b,sizeof(b)))==sizeof(b); s+=c); return s+c; } /* Skips n bytes */ Global(size_t) DataLibSkip(DataLibPtr dl, size_t n) { Char b[50]; size_t w=1; while (n>0 && w>0) { w=MIN(n,sizeof(b)); n-=(w=DataLibRead(dl,b,w)); } return n; } /* Deletes partitions (the list must be NULL terminated) */ #ifdef VAR_ARGS Global(void) DataLibDeletePartitions(dl, va_alist) DataLibPtr dl; va_dcl #else Global(void) DataLibDeletePartitions(DataLibPtr dl, ...) #endif { va_list lp; CharPtr p; #ifdef VAR_ARGS va_start(lp); #else va_start(lp,dl); #endif while (1) { p=va_arg(lp,CharPtr); if (p) { if (DataLibFind(dl,p)==0) DataLibDelete(dl); } else break; } va_end(lp); } Global(Int2) DataLibEnumEx=0; Global(Int2) DataLibEnumEvent; Local(CharPtr) DataLibIgnoreChars=NULL; Global(void) DataLibEnumIgnore(CharPtr s) { DataLibIgnoreChars=s; } /* Enumerates all the partitions in the current level (recursive==FALSE) or in the current level and all its sublevels (recursive==TRUE). For each enumerated partition makes it current one and calls the callback. Hidden entries are always excluded from enumeration. When recursive==TRUE subdirectory enties are also excluded. If the global variable DataLibEnumEx>0 the function behaves as described above with two exceptions: 1. The order of enumeration is reversed (from the first created partition to the last one). 2. callback is called when a group is entered (in which case DataLibEnumEvent is set to 1) and exited (DataLibEnumEvent is set to 2). Otherwise DataLibEnumEvent is set to 0. */ Global(Int2) DataLibEnum(DataLibPtr dl, DataLibEnumCallback callback, Boolean recursive) { FilePtr fp; DirEntry de; Int2 rc=0; if (DataLibEnumEx) { for (dl->CurDirPtr=fp=dl->FirstDirPtr; fp; dl->CurDirPtr=fp, fp=de.NextPtr) if (ReadDir(dl,fp,&de)) return 1;/* cannot read directory entry */ } else dl->CurDirPtr=dl->FirstDirPtr; for (; dl->CurDirPtr; dl->CurDirPtr=DataLibEnumEx ? de.PrevPtr : de.NextPtr) { if (ReadDir(dl,dl->CurDirPtr,&de)) return 1;/* cannot read directory entry */ if (dl->NameFirstChar==HIDDEN_CHAR) continue; if (StringChr(DataLibIgnoreChars,dl->DirName[1])) continue; if (recursive) if (dl->NameFirstChar==SUBDIR_CHAR) { if (DataLibEnumEx) { /* into a group */ DataLibEnumEvent=1; if (callback(dl)) { rc=-1; /* stop enumeration */ break; } } if (de.ChildPtr) { DataLibDown(dl); rc=DataLibEnum(dl,callback,recursive); DataLibUp(dl); if (rc) break; } if (DataLibEnumEx) { /* out of a group */ DataLibEnumEvent=2; if (callback(dl)) { rc=-1; /* stop enumeration */ break; } } continue; } if (de.DataPtr) if (Seek(dl,de.DataPtr)) { rc=2; /* cannot seek to data */ break; } DataLibEnumEvent=0; if (callback(dl)) { rc=-1; /* stop enumeration */ break; } } return rc; } /* Save and restore DataLib current position */ Global(DataLibPtr) DataLibSavePosPtr(DataLibPtr dl) { DataLibPtr dlsaved; dlsaved=_MemGet(sizeof(DataLib),FALSE); *dlsaved=*dl; return dlsaved; } Global(void) DataLibRestorePosPtr(DataLibPtr dl, DataLibPtr dlsaved) { DirEntry de; Header he; FilePtr first; DataLibPtr dls; FILE PNTR stream; CharPtr filename; if (dlsaved->ParentDirPtr) { ReadDir(dl,dlsaved->ParentDirPtr,&de); /* dl, not dlsaved!!! */ first=de.ChildPtr; } else { ReadHeader(dl,&he); first=he.DirPtr; } stream=dl->Stream; /* don't restore pointers */ filename=dl->FileName; dls=dl->Saved; *dl=*dlsaved; dl->Stream=stream; /* don't restore pointers */ dl->FileName=filename; dl->Saved=dls; dl->FirstDirPtr=first; _MemFree(dlsaved); if (dl->CurBlockPtr) Fseek(dl,(long)(dl->CurBlockPtr+sizeof(BlockDesc)+dl->CurOffset),SEEK_SET); } Global(void) DataLibSavePos(DataLibPtr dl) { DataLibPtr dlp; for (dlp=dl; dlp->Saved; dlp=dlp->Saved); dlp->Saved=DataLibSavePosPtr(dl); dlp->Saved->Saved=NULL; } Global(void) DataLibRestorePos(DataLibPtr dl) { DataLibPtr dlp; for (dlp=dl; dlp->Saved->Saved; dlp=dlp->Saved); DataLibRestorePosPtr(dl,dlp->Saved); dlp->Saved=NULL; } /*--------------------*/ /* High-level routine */ /* Data and functions local for DataLibShow */ enum indices { INDX_NOTIFY,INDX_CREATE,INDX_EDIT,INDX_DELETE,/***INDX_COPY,***/ INDX_RENAME,INDX_IMPORT,INDX_EXPORT,INDX_GROUP, INDX_ }; #define CHAR_SEP '-' #define CHAR_CREATE 'n' #define CHAR_EDIT 'u' #define CHAR_DELETE 'd' /***#define CHAR_COPY 'c'***/ #define CHAR_RENAME 'r' #define CHAR_IMPORT 'i' #define CHAR_EXPORT 'e' #define CHAR_GROUP 'g' #define CHAR_USERMENU 'm' /* add user menu; 2 params: text,menu */ #define CHAR_WINPOS 'w' /* honor window's position; 1 param: winid */ typedef struct { /* Data attached to DataLibShow's window */ WindoW parent; DataLibCallback Callback[INDX_]; Boolean Mask[INDX_]; LisT List; PrompT DepthPrompt; DataLibPtr DataLibAdr; CharPtr PNTR UserTxt; MenuDescPtr UserMenu; CharPtr SaveWinId; } WorkArea; Local(DataLibPtr) dl; /* DataLibPtr passed to DataLibShow */ Local(WorkArea PNTR) work; /* DataLibShow work area */ /* Inserts names of dir enrties into LisT and activates the current one */ Local(Int2) DataLibList(void) { DirEntry de; FilePtr p,last; Int2 n; LisT l; Char d[5]; l=work->List; Reset(l); Disable(l); last=0; /* last visible DirEntry */ if (!dl->CurDirPtr) dl->CurDirPtr=dl->FirstDirPtr; for (n=1, p=dl->FirstDirPtr; p; p=de.NextPtr) { if (ReadDir(dl,p,&de)) return 1; /* cannot read dir entry */ switch (*dl->DirName) { case HIDDEN_CHAR: if (p==dl->CurDirPtr) if (last) { dl->CurDirPtr=last; SetValue(l,dl->PrevCurrent=n-1); } else dl->CurDirPtr=de.NextPtr; continue; /* don't show a hidden entry */ case SUBDIR_CHAR: *dl->DirName='*'; break; } last=p; ListItem(l,dl->DirName); if (p==dl->CurDirPtr) SetValue(l,dl->PrevCurrent=n); n++; } ReadDir(dl,dl->CurDirPtr,&de); sprintf(d,"%.0i",(int)(dl->Depth-dl->MinDepth)); SetTitle(work->DepthPrompt,d); Enable(l); Select(l); return 0; } /*-----------------------------------------------------------*/ /* Description of menu and buttons in the DataLibShow window */ Local(void) EditProc(Int2); Local(Boolean) EditProc_Status(Int2); Local(void) CreateProc(Int2); Local(Boolean) CreateProc_Status(Int2); Local(void) RenameProc(Int2); Local(Boolean) RenameProc_Status(Int2); /*** Local(void) CopyProc(Int2); Local(Boolean) CopyProc_Status(Int2); ***/ Local(void) DeleteProc(Int2); Local(Boolean) DeleteProc_Status(Int2); Local(void) DeleteAllProc(Int2); Local(Boolean) DeleteAllProc_Status(Int2); Local(void) GroupProc(Int2); Local(Boolean) GroupProc_Status(Int2); Local(void) ImportProc(Int2); Local(Boolean) ImportProc_Status(Int2); Local(void) ExportProc(Int2); Local(Boolean) ExportProc_Status(Int2); #if _WIN #pragma argsused #endif Local(Boolean) True_Status(Int2 index) { return TRUE; } Local(MenuDesc) ShowMenu; Local(void) SelectProc(Int2); Local(Boolean) SelectProc_Status(Int2); Local(void) DeselectProc(Int2); Local(Boolean) DeselectProc_Status(Int2); Local(void) EscapeProc(Int2); Local(Boolean) EscapeProc_Status(Int2); Local(void) CancelProc(Int2); Local(Boolean) CancelProc_Status(Int2); Local(void) HelpProc(Int2); Local(MenuItem) Buttons[]={ {SELECT_B, SelectProc, SelectProc_Status}, {DESELECT_B, DeselectProc, DeselectProc_Status}, {ESCAPE_B, EscapeProc, EscapeProc_Status}, {CANCEL_B, CancelProc, CancelProc_Status}, {HELP_B, HelpProc, True_Status} }; Local(MenuDesc) ShowButtons={Buttons,DIM(Buttons)}; /*-----------------------*/ /* Callbacks for buttons */ #if _WIN #pragma argsused #endif Local(void) SelectProc(Int2 index) { DirEntry de; FilePtr newCur; if (Locked(&ShowMenu)) return; /* Motif: if the user pressed the Tab key to activate the list, then used the directional arrow keys to choose a system, and finally pressed the Enter key to make it the current one, then: BOTH ListActnProc and ButtonAction are called in order. This results to crash (SelectNotify for systems closes ArchivesLib; thus *dl is closed when SelectProc is called for the second time). It is not clear for me who is resposible for double call: X11/Motif/Vibrant. I'll report about this to dr.Kans. Meanwhile, let me fix this by myself... 28.07.95 */ if (DataLibIsClosed(dl)) return; Lock(&ShowMenu); if (GetValue(work->List)) { if (dl->NameFirstChar==SUBDIR_CHAR && dl->CurDirPtr) { /* CurDirPtr==0 if called from Deselect */ ReadDir(dl,dl->CurDirPtr,&de); DataLibDown(dl); DataLibList(); SetStatusOfItems(&ShowButtons); } else { Select(work->parent); if (work->SaveWinId) WriteWindowPos(work->SaveWinId,dl->Window); Remove(dl->Window); newCur=dl->CurDirPtr; if (dl->SelectedDirPtr) { ReadDir(dl,dl->CurDirPtr=dl->SelectedDirPtr,&de); Seek(dl,de.DataPtr); } /* deselect */ ((DataLibNotify)(work->Callback[INDX_NOTIFY]))(dl,NOTIFY_DESELECT); dl->SelectedDirPtr=dl->CurDirPtr=newCur; if (ReadDir(dl,newCur,&de)==0) /* newCur may be 0 */ Seek(dl,de.DataPtr); /* selection is made */ ((DataLibNotify)(work->Callback[INDX_NOTIFY]))(dl,NOTIFY_SELECT); return; /* don't Unlock because Remove has caused *work MemFree'ed */ } } Unlock(&ShowMenu); } #if _WIN #pragma argsused #endif Local(Boolean) SelectProc_Status(Int2 index) { return dl->CurDirPtr!=0; } /* Deselect */ Local(void) DeselectProc(Int2 index) { FilePtr curptr; /***** dl->SelectedDirPtr=0; CancelProc(index); *****/ curptr=dl->CurDirPtr; dl->CurDirPtr=0; SelectProc(index); dl->CurDirPtr=curptr; } #if _WIN #pragma argsused #endif Local(Boolean) DeselectProc_Status(Int2 index) { return dl->SelectedDirPtr!=0; } /* Go to the previous level */ #if _WIN #pragma argsused #endif Local(void) EscapeProc(Int2 index) { DirEntry de; if (Locked(&ShowMenu)) return; if (dl->ParentDirPtr) { ReadDir(dl,dl->ParentDirPtr,&de); if (dl->NameFirstChar==SUBDIR_CHAR) { DataLibUp(dl); DataLibList(); SetStatusOfItems(&ShowMenu); SetStatusOfItems(&ShowButtons); } } } #if _WIN #pragma argsused #endif Local(Boolean) EscapeProc_Status(Int2 index) { return dl->Depth>dl->MinDepth; } #if _WIN #pragma argsused #endif Local(void) CancelProc(Int2 index) { DirEntry de; if (Locked(&ShowMenu)) return; Lock(&ShowMenu); /* selection is canceled */ if (dl->SelectedDirPtr) { ReadDir(dl,dl->SelectedDirPtr,&de); Seek(dl,de.DataPtr); } else dl->DirName[1]='\0'; ((DataLibNotify)(work->Callback[INDX_NOTIFY]))(dl,NOTIFY_CANCEL); Unlock(&ShowMenu); Select(work->parent); if (work->SaveWinId) WriteWindowPos(work->SaveWinId,dl->Window); Remove(dl->Window); } #if _WIN #pragma argsused #endif Local(Boolean) CancelProc_Status(Int2 index) { return TRUE; } /*--------------------------*/ /* Callbacks for menu items */ Local(Int2) LockAndAsk(CharPtr Prompt, CharPtr Buf, Int2 Len) { Int2 res; Lock(&ShowMenu); /*** Disable(work->List);***/ res=Ask(Prompt,Buf,Len); /*** Enable(work->List);***/ Unlock(&ShowMenu); return res; } Local(Boolean) Always_Status(enum indices index) { return work->Mask[index]!=0; } Local(Boolean) CurrentExists_Status(enum indices index) { return Always_Status(index) && dl->CurDirPtr && dl->NameFirstChar!=HIDDEN_CHAR; } Local(Boolean) CurrentIsSystem_Status(enum indices index) { return Always_Status(index) && CurrentExists_Status(index) && dl->NameFirstChar==NAMEDIR_CHAR; } #if _WIN #pragma argsused #endif Local(void) RenameProc(Int2 index) { DirEntry de; if (dl->CurDirPtr) { ReadDir(dl,dl->CurDirPtr,&de); if (LockAndAsk(DatalibTxt[RENAME_T],dl->DirName+1,MAX_NAME_LEN-1)==0) { WriteDir(dl,dl->CurDirPtr,&de); if (work->Callback[INDX_RENAME]) work->Callback[INDX_RENAME](dl,NULL); DataLibList(); } } } #if _WIN #pragma argsused #endif Local(Boolean) RenameProc_Status(Int2 index) { return CurrentExists_Status(INDX_RENAME); } Local(Char) buf[MAX_NAME_LEN]; /* for Ask */ #if _WIN #pragma argsused #endif Local(void) GroupProc(Int2 index) { buf[1]='\0'; if (LockAndAsk(DatalibTxt[GROUP_T],buf+1,MAX_NAME_LEN-1)==0) { buf[0]=SUBDIR_CHAR; if (DataLibCreate(dl,buf)) return; DataLibList(); } } #if _WIN #pragma argsused #endif Local(Boolean) GroupProc_Status(Int2 index) { return Always_Status(INDX_GROUP); } /*----------------------------------------------------------*/ /* Callbacks for user-defined (externaly processed) actions */ /* Each callback consists of two parts. One is called */ /* immediately after selection of item and the second is */ /* by user-defined callback to notify that it has completed */ /* Create */ Local(void) CreatePost(DataLibPtr dl, Int2 res) { if (res) /* failed */ if (!DataLibFind(dl,buf)) DataLibDelete(dl); DataLibList(); SetStatusOfItems(&ShowButtons); Unlock(&ShowMenu); if (!res) { /* successful create => select */ Unlock(&ShowMenu); SelectProc(0); } } #if _WIN #pragma argsused #endif Local(void) CreateProc(Int2 index) { StrCpy(buf," "); if (LockAndAsk(DatalibTxt[CREATE_T],buf+1,MAX_NAME_LEN-1)) return; if (DataLibCreate(dl,buf)) return; Lock(&ShowMenu); if (work->Callback[INDX_CREATE](dl,CreatePost)) /* failed */ CreatePost(dl,1); } #if _WIN #pragma argsused #endif Local(Boolean) CreateProc_Status(Int2 index) { return Always_Status(INDX_CREATE); } /* Edit */ #if _WIN #pragma argsused #endif Local(void) EditPost(DataLibPtr dl, Int2 res) { SetStatusOfItems(&ShowButtons); Unlock(&ShowMenu); if (!res) { /* successful edit => select */ Unlock(&ShowMenu); SelectProc(0); } } #if _WIN #pragma argsused #endif Local(void) EditProc(Int2 index) { DirEntry de; if (dl->CurDirPtr) { if (ReadDir(dl,dl->CurDirPtr,&de)) return; if (dl->NameFirstChar==SUBDIR_CHAR) return; else { Seek(dl,de.DataPtr); Lock(&ShowMenu); if (work->Callback[INDX_EDIT](dl,EditPost)) /* failed */ EditPost(dl,1); } } } #if _WIN #pragma argsused #endif Local(Boolean) EditProc_Status(Int2 index) { return CurrentIsSystem_Status(INDX_EDIT); } #if _WIN #pragma argsused #endif Local(void) DeleteProc(Int2 index) { DirEntry de; if (dl->CurDirPtr) { if (ReadDir(dl,dl->CurDirPtr,&de)) return; sprintf(ParBuf,DatalibTxt[DELETE_T],dl->DirName+1); if (LockAndAsk(ParBuf,NULL,0)) return; if (dl->NameFirstChar==SUBDIR_CHAR) { if (DataLibDelete(dl)) LockAndAsk(DatalibTxt[DELETEGF_T],NULL,0); } else { if (work->Callback[INDX_DELETE](dl,NULL)) return; DataLibDelete(dl); } DataLibList(); } } #if _WIN #pragma argsused #endif Local(Boolean) DeleteProc_Status(Int2 index) { return CurrentExists_Status(INDX_DELETE); } /* Deletes current entry */ Local(Int2) DeleteCur(DataLibPtr dl) { if (dl->NameFirstChar!=NAMEDIR_CHAR) return 0; if (work->Callback[INDX_DELETE](dl,NULL)) return -1; DataLibDelete(dl); return 0; } #if _WIN #pragma argsused #endif Local(void) DeleteAllProc(Int2 index) { if (LockAndAsk(DatalibTxt[DELETEALL_T],NULL,0)) return; DataLibEnum(dl,DeleteCur,FALSE); DataLibList(); } #if _WIN #pragma argsused #endif Local(Boolean) DeleteAllProc_Status(Int2 index) { return CurrentExists_Status(INDX_DELETE); } /*** #if _WIN #pragma argsused #endif Local(void) CopyProc(Int2 index) { myMessage(MSG_OK,"Not implemented"); } #if _WIN #pragma argsused #endif Local(Boolean) CopyProc_Status(Int2 index) { return CurrentExists_Status(INDX_COPY); } ***/ #if _WIN #pragma argsused #endif Local(void) ImportPost(DataLibPtr dl, Int2 res) { SetStatusOfItems(&ShowButtons); Unlock(&ShowMenu); if (res==0) { DataLibList(); } } #if _WIN #pragma argsused #endif Local(void) ImportProc(Int2 index) { Lock(&ShowMenu); work->Callback[INDX_IMPORT](dl,ImportPost); } #if _WIN #pragma argsused #endif Local(Boolean) ImportProc_Status(Int2 index) { return Always_Status(INDX_IMPORT); } #if _WIN #pragma argsused #endif Local(void) ExportPost(DataLibPtr dl, Int2 res) { SetStatusOfItems(&ShowButtons); Unlock(&ShowMenu); } #if _WIN #pragma argsused #endif Local(void) ExportProc(Int2 index) { DirEntry de; if (ReadDir(dl,dl->CurDirPtr,&de)) return; Seek(dl,de.DataPtr); /* prepare to read/write data */ Lock(&ShowMenu); work->Callback[INDX_EXPORT](dl,ExportPost); } #if _WIN #pragma argsused #endif Local(Boolean) ExportProc_Status(Int2 index) { return CurrentExists_Status(INDX_EXPORT); } #if _WIN #pragma argsused #endif Local(void) HelpProc(Int2 index) { Help(NULL); } /*-----------------------*/ /* Callback for the List */ Local(void) ListActnProc(LisT List) { Int2 Current,Dist; Boolean select=dblClick; /* check it immediately */ Current=GetValue(List); Dist=dl->PrevCurrent-Current; if (Dist>0) DataLibPrev(dl,Dist); if (Dist<0) DataLibNext(dl,-Dist); dl->PrevCurrent=Current; SetStatusOfItems(&ShowMenu); SetStatusOfItems(&ShowButtons); if (work->UserMenu) SetStatusOfItems(work->UserMenu); if (select) /* Double click means 'Select' */ SelectProc(0); } /* Free extra window's data callback */ #if _WIN #pragma argsused #endif Local(void) FreeWorkArea(WindoW w, VoidPtr work) { _MemFree(work); ShowMenu.Menu=_MemFree(ShowMenu.Menu); } /* Common callback for menu items */ Local(void) MenuAction(IteM it) { if (work->UserMenu) Lock(work->UserMenu); DoMenuAction(&ShowMenu,it); if (work->UserMenu) { Unlock(work->UserMenu); SetStatusOfItems(work->UserMenu); } SetStatusOfItems(&ShowButtons); if (!ShowMenu.Lock) SetStatusOfItems(&ShowMenu); Select(work->List); } /* Common callback for Buttons */ Local(void) ButtonAction(ButtoN bu) { DoMenuAction(&ShowButtons,(IteM)bu); } /* Callback for user-supplied menu */ Local(void) UserMenuAction(IteM it) { Lock(work->UserMenu); Lock(&ShowMenu); Lock(&ShowButtons); DoMenuAction(work->UserMenu,it); Unlock(&ShowButtons); Unlock(&ShowMenu); Unlock(work->UserMenu); SetStatusOfItems(&ShowButtons); SetStatusOfItems(&ShowMenu); SetStatusOfItems(work->UserMenu); Select(work->List); } /* Shows Data Library's directory and allows the user to manipulate it. Notify is called when the user has either selected object or cancelled. Any other callback is optional; if set to NULL the action is not allowed (button is not created). */ #ifdef VAR_ARGS Global(Int2) DataLibShow(Dl, Title, Notify, Format, va_alist) DataLibPtr Dl; CharPtr Title; DataLibNotify Notify; CharPtr Format; va_dcl #else Global(Int2) DataLibShow(DataLibPtr Dl, CharPtr Title, DataLibNotify Notify, CharPtr Format, ...) #endif { WindoW w; RecT rg; GrouP gv,gg,gb; MenuItemPtr mip; enum indices i; Int2 spacing; Int2 left=-50,top=-50; int width,height,n; va_list valist; dl=Dl; /* Create work area associated with window */ work=_MemNew(sizeof(WorkArea)); work->parent=CurrentWindow(); work->Callback[INDX_NOTIFY]=(DataLibCallback)Notify; if (Format && *Format) { #ifdef VAR_ARGS va_start(valist); #else va_start(valist,Format); #endif mip=ShowMenu.Menu=_MemNew(ARRAYSIZE(MenuItem,StrLen(Format)+3)); mip->Title=FUNCTION_M; mip++; n=1; for (; *Format; Format++) { switch (*Format) { case CHAR_CREATE: i=INDX_CREATE; mip->Title=CREATE_I; mip->Action=CreateProc; mip->EnableDisable=CreateProc_Status; goto com; case CHAR_EDIT: i=INDX_EDIT; mip->Title=EDIT_I; mip->Action=EditProc; mip->EnableDisable=EditProc_Status; goto com; case CHAR_DELETE: i=INDX_DELETE; mip->Title=DELETE_I; mip->Action=DeleteProc; mip->EnableDisable=DeleteProc_Status; mip++; n++; mip->Title=DELETEALL_I; mip->Action=DeleteAllProc; mip->EnableDisable=DeleteAllProc_Status; goto com; /*** case CHAR_COPY: i=INDX_COPY; goto com;***/ case CHAR_IMPORT: i=INDX_IMPORT; mip->Title=IMPORT_I; mip->Action=ImportProc; mip->EnableDisable=ImportProc_Status; goto com; case CHAR_EXPORT: i=INDX_EXPORT; mip->Title=EXPORT_I; mip->Action=ExportProc; mip->EnableDisable=ExportProc_Status; goto com; case CHAR_RENAME: i=INDX_RENAME; mip->Title=RENAME_I; mip->Action=RenameProc; mip->EnableDisable=RenameProc_Status; goto com; com: work->Mask[i]=TRUE; work->Callback[i]=va_arg(valist,DataLibCallback); break; case CHAR_GROUP: work->Mask[INDX_GROUP]=TRUE; mip->Title=GROUP_I; mip->Action=GroupProc; mip->EnableDisable=GroupProc_Status; break; case CHAR_SEP: mip->Title=SEPARATOR_I; break; case CHAR_USERMENU: work->UserTxt=(CharPtr PNTR)va_arg(valist,DataLibCallback); work->UserMenu=(MenuDescPtr)va_arg(valist,DataLibCallback); continue; case CHAR_WINPOS: work->SaveWinId=(CharPtr)va_arg(valist,DataLibCallback); ReadWindowPos(work->SaveWinId,&left,&top); continue; default: continue; } mip++; n++; } mip->Title=DUMMYEND_I; n++; va_end(valist); ShowMenu.Num=(Char)n; } else ShowMenu.Num=0; /* Create window */ w=FixedWindow(left,top,-sysCharWidth,-sysLineHeight,Title,NULL); SetWindowExtra(w,work,FreeWorkArea); dl->Window=w; if (ShowMenu.Num) CreateMenu(w,DatalibTxt,&ShowMenu,MenuAction); /* Groups */ gv=HiddenGroup(w,0,2,NULL); /* Create a list of directoy entries */ work->DataLibAdr=dl; sscanf(DatalibTxt[LIST],"%i %i",&width,&height); work->List=SingleList(gv,width,height,ListActnProc); Dl->List=work->List; /* Group for buttons and Depth indicator */ gg=HiddenGroup(gv,2,0,NULL); /* Create standard group of buttons (Select/Cancel/Help) */ gb=HiddenGroup(gg,5,0,NULL); GetPosition(gv,&rg); spacing=Spacing(rg.right-rg.left,TRUE,DatalibTxt[SELECT_B], DatalibTxt[DESELECT_B], DatalibTxt[ESCAPE_B], DatalibTxt[CANCEL_B], DatalibTxt[HELP_B],NULL); SetGroupMargins(gb,spacing,5); SetGroupSpacing(gb,spacing,0); for (n=0; nDepthPrompt=StaticPrompt(gg,"99",vScrollBarWidth,rg.bottom-rg.top+1,NULL,'r'); /* Show the List */ DataLibList(); SetStatusOfItems(&ShowButtons); /* only now: DataLibList may set CurDirPtr that affects SelectProc_Status */ if (work->UserMenu) { CreateMenu(w,work->UserTxt,work->UserMenu,UserMenuAction); SetStatusOfItems(work->UserMenu); } Show(w); return 0; } #if DEB_DL /* DataLib low-level viewer. Used only in debug mode. */ #include "document.h" #include "archive.h" #define EXT_CAT "cat" #define EXT_DGM "dgm" #define EXT_SYS "con" Local(CharPtr) Ext[3]={EXT_CAT,EXT_DGM,EXT_SYS}; #define OUT "tmp.tmp" /* name of output file */ #define HL "04" /* set to 08 for very large files */ Char FileName[PATH_MAX+FILENAME_MAX+1]; static FILE PNTR ins; static FILE PNTR outs; static Int2 dDepth=2; static Int4 BlockTotal,BlockNotFull,Wasted; static Boolean Wait; static void CloseProc(WindoW w) { Hide(w); Wait=FALSE; } static void Pos(void); static void PrintData(CharPtr p, Int2 l); typedef struct _Interval { FilePtr From,To; struct _Interval PNTR Next; struct _Interval PNTR Pred; } Interval; typedef Interval PNTR IntervalPtr; static IntervalPtr Used; static void AddUsed(FilePtr adr, BlockDesc PNTR bd) { Interval i; IntervalPtr n; IntervalPtr p; IntervalPtr w; i.From=adr; i.To=adr+sizeof(BlockDesc)+bd->Len; i.Next=i.Pred=NULL; for (p=NULL,n=Used; n; p=n,n=n->Next) { if (MAX(n->From,i.From)<=MIN(n->To,i.To)-1) fprintf(outs,"\n\n===> More than one Block at %"HL"lx\n\n",i.From); if (i.To<=n->From) { w=_MemNew(sizeof(Interval)); *w=i; w->Next=n; w->Pred=p; n->Pred=w; if (p) p->Next=w; else Used=w; if (p && p->To==w->From) { p->To=w->To; p->Next=n; n->Pred=p; _MemFree(w); w=p; } if (w->To==n->From) { p=w->Pred; n->From=w->From; n->Pred=p; if (p) p->Next=n; else Used=n; _MemFree(w); } return; } } if (p) { if (p->To==i.From) p->To=i.To; else { w=_MemNew(sizeof(Interval)); *w=i; p->Next=w; w->Pred=p; } } else { Used=_MemNew(sizeof(Interval)); *Used=i; } } static Boolean PrintUnused(FilePtr from, FilePtr to) { size_t len; Int2 w; unsigned long Off; Char buf[16]; fprintf(outs," [%"HL"lx,%"HL"lx]\n",from,to); Fseek((DataLibPtr)&ins,from,SEEK_SET); dDepth+=2; for (len=to-from+1, Off=from; len; ) { w=MIN(len,16); if (Fread((DataLibPtr)&ins,buf,w)) { myMessage(MSG_OK,"Cannot fread %i bytes of data, Off=%08lx",(int)w,Off); } Pos(); fprintf(outs,"%"HL"lx ",Off); PrintData(buf,w); len-=w; Off+=w; } dDepth-=2; return TRUE; } static Boolean ReportUnused(Boolean prnt, Boolean fre) { IntervalPtr wp,used; Boolean res=FALSE; if (Used) { used=Used; if (prnt) fprintf(outs,"\n\nDangling intervals\n" "------------------\n\n"); if (used->From!=sizeof(Header)) { if (prnt) res=PrintUnused((long)sizeof(Header),used->From-1); } for (; used; used=wp) { wp=used->Next; if (prnt) if (wp) res=PrintUnused(used->To,wp->From-1); else { fseek(ins,0,SEEK_END); if (used->ToTo,Ftell((DataLibPtr)&ins)-1); } if (fre) _MemFree(used); } if (fre) Used=NULL; } return res; } static void Pos(void) { Int2 i; for (i=0; iFrom!=sizeof(Header)) { process(dl,(long)sizeof(Header),used->From-1); } for (; used; used=wp) { wp=used->Next; if (wp) { process(dl,used->To,wp->From-1); } else { fseek(ins,0,SEEK_END); if (used->ToTo,Ftell((DataLibPtr)&ins)-1); } } } } } #if _WIN #pragma argsused #endif Local(void) CorrectBlockDesc(DataLibPtr dl, FilePtr from, FilePtr to) { BlockDesc bd; printf("CorrectBlockDesc(%p,%x,%x)\n",ins,(int)from,(int)to); if (fseek(ins,from,SEEK_SET)) myMessage(MSG_OK,"Cannot seek BlockDesc(%x,%x)",(int)from,(int)to); else { bd.Next=bd.DataLen=0; bd.Len=to-from+1-sizeof(BlockDesc); if (fwrite(&bd,sizeof(bd),1,ins)!=1) myMessage(MSG_OK,"Cannot write BlockDesc(%x,%x)",(int)from,(int)to); printf("bd.Len=%x\n",(int)bd.Len); } } #if _WIN #pragma argsused #endif Local(void) FreeBlock(DataLibPtr dl, FilePtr from, FilePtr to) { FreeChain(dl,from); printf("FreeBlock (%p,%x,%x)\n",dl->Stream,(int)from,(int)to); } Local(void) FreeDangling(ButtoN b) { DataLibVar(dl); EnumDangling(CorrectBlockDesc,ins,NULL); fclose(ins); ins=NULL; if (DataLibOpen(&dl,FileName)) myMessage(MSG_OK,"Cannot open '%s'",FileName); else { EnumDangling(FreeBlock,dl.Stream,&dl); DataLibClose(&dl); } Disable(b); Wait=FALSE; } /* Update DataLib stuff */ /* */ /*----------------------*/ Global(void) Debug_DataLibShow(Int2 index) { WindoW w; GrouP g; DoC d; Header h; if (EnableUpdate) DeactivateArchives(); w=FixedWindow(-50,-50,-5,-5,"DataLib Viewer",CloseProc); if (EnableUpdate) { g=HiddenGroup(w,10,0,NULL); StaticPrompt(g," Offset:",0,dialogTextHeight,NULL,'r'); tOffset=DialogText(g,"",3,GetFileData); StaticPrompt(g," Length:",0,dialogTextHeight,NULL,'r'); tLength=DialogText(g,"",1,GetFileData); StaticPrompt(g," Data:",0,dialogTextHeight,NULL,'r'); tData=DialogText(g,"",10,CheckUserData); StaticPrompt(g," ",0,dialogTextHeight,NULL,'r'); /* placeholder */ bUpdate=PushButton(g,"Update",UpdateFileData); Disable(bUpdate); bDangling=PushButton(g,"Free dangling intervals",FreeDangling); Disable(bDangling); } Used=NULL; d=DocumentPanel(w,screenRect.right-4*sysCharWidth,screenRect.bottom*3/4); if (myGetInputFileName(FileName,sizeof(FileName),Ext[index-1])) { if ((ins=fopen(FileName,EnableUpdate ? "r+b" : "rb"),ins)) { if ((outs=fopen(OUT,"wt"),outs)) { if (Fread((DataLibPtr)&ins,&h,sizeof(Header))==0) { BlockTotal=BlockNotFull=Wasted=0; fprintf(outs,"Header\n------\n\n"); fprintf(outs," DirPtr=%"HL"lx\n",h.DirPtr); fprintf(outs," FreePtr=%"HL"lx\n",h.FreePtr); fprintf(outs," CurDirPtr=%"HL"lx\n",h.CurDirPtr); fprintf(outs," SelectedDirPtr=%"HL"lx\n",h.SelectedDirPtr); fprintf(outs," ParentDirPtr=%"HL"lx\n",h.ParentDirPtr); fprintf(outs," FirstDirPtr=%"HL"lx\n",h.FirstDirPtr); fprintf(outs," MinDepth=%"HL"lx\n",(Int4)h.MinDepth); if (h.DirPtr) { fprintf(outs,"\n\nAllocated blocks\n" "----------------\n\n"); PrintDir(h.DirPtr); } if (h.FreePtr) { fprintf(outs,"\n\nFree blocks\n" "-----------\n\n"); PrintFree(h.FreePtr); } fseek(ins,0,SEEK_END); fprintf(outs,"\n\n\nStatistics\n----------\n\n" "Blocks NotFull Wasted FileSize\n" "%6li %7li %6li %8li(%0lx)", BlockTotal,BlockNotFull,Wasted,Ftell((DataLibPtr)&ins),Ftell((DataLibPtr)&ins)); if (ReportUnused(TRUE,!EnableUpdate)) if (EnableUpdate) Enable(bDangling); SetTitle(w,FileName); } else myMessage(MSG_OK,"Cannot read the header",OUT); fclose(outs); Show(w); DisplayFancy(d,OUT,NULL,NULL,programFont,1); Wait=TRUE; while (Wait) myProcessAnyEvent(); if (EnableUpdate) ReportUnused(FALSE,TRUE); } else myMessage(MSG_OK,"Cannot open output file '%s'",OUT); if (ins) fclose(ins); } else myMessage(MSG_OK,"Cannot open input file '%s'",FileName); } Remove(w); if (EnableUpdate) ActivateArchives(); } #endif /* DEB_DL */