diff --git a/src/dos/drive_cache.cpp b/src/dos/drive_cache.cpp new file mode 100644 index 00000000..d6a71aec --- /dev/null +++ b/src/dos/drive_cache.cpp @@ -0,0 +1,462 @@ + +/* + * Copyright (C) 2002-2003 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "drives.h" +#include "dos_inc.h" +#include "dirent.h" +#include "support.h" + +// STL stuff +#include +#include +#include + + +bool SortByName(DOS_Drive_Cache::CFileInfo* const &a, DOS_Drive_Cache::CFileInfo* const &b) +{ + return strcmp(a->shortname,b->shortname)<0; +}; + +bool SortByNameRev(DOS_Drive_Cache::CFileInfo* const &a, DOS_Drive_Cache::CFileInfo* const &b) +{ + return strcmp(a->shortname,b->shortname)>0; +}; + +bool SortByDirName(DOS_Drive_Cache::CFileInfo* const &a, DOS_Drive_Cache::CFileInfo* const &b) +{ + // Directories first... + if (a->isDir!=b->isDir) return (a->isDir>b->isDir); + return strcmp(a->shortname,b->shortname)<0; +}; + +bool SortByDirNameRev(DOS_Drive_Cache::CFileInfo* const &a, DOS_Drive_Cache::CFileInfo* const &b) +{ + // Directories first... + if (a->isDir!=b->isDir) return (a->isDir>b->isDir); + return strcmp(a->shortname,b->shortname)>0; +}; + +DOS_Drive_Cache::DOS_Drive_Cache(void) +{ + dirBase = new CFileInfo; + dirSearch = 0; + save_dir = 0; + SetDirSort(DIRALPHABETICAL); +}; + +DOS_Drive_Cache::DOS_Drive_Cache(const char* path) +{ + dirBase = new CFileInfo; + dirSearch = 0; + save_dir = 0; + SetDirSort(DIRALPHABETICAL); + SetBaseDir(path); +}; + +DOS_Drive_Cache::~DOS_Drive_Cache(void) +{ + delete dirBase; dirBase = 0; +}; + +void DOS_Drive_Cache::SetBaseDir(const char* baseDir) +{ + strcpy(dirBase->fullname,baseDir); + if (OpenDir(baseDir)) { + struct dirent result; + struct stat status; + ReadDir(&result,&status); + }; +}; + +void DOS_Drive_Cache::ExpandName(char* path) +{ + strcpy(path,GetExpandName(path)); +}; + +char* DOS_Drive_Cache::GetExpandName(const char* path) +{ + static char work [CROSS_LEN]; + char dir [CROSS_LEN]; + + work[0] = 0; + strcpy (dir,path); + + char* pos = strrchr(path,CROSS_FILESPLIT); + + if (pos) dir[pos-path+1] = 0; + CFileInfo* dirInfo = FindDirInfo(dir, work); + + if (pos) { + // Last Entry = File + strcpy(dir,pos+1); + if (strchr(dir,'~')) GetLongName(dirInfo, dir); + strcat(work,dir); + } + return work; +}; + +void DOS_Drive_Cache::AddEntry(const char* path) +{ + // Get Last part... + char file [CROSS_LEN]; + char expand [CROSS_LEN]; + + CFileInfo* dir = FindDirInfo(path,expand); + char* pos = strrchr(path,CROSS_FILESPLIT); + + if (pos) { + strcpy(file,pos+1); + CreateEntry(dir,file); + // Sort Lists - filelist has to be alphabetically sorted + std::sort(dir->fileList.begin(), dir->fileList.end(), SortByName); + // Output list - user defined + switch (sortDirType) { + case ALPHABETICAL : std::sort(dir->outputList.begin(), dir->outputList.end(), SortByName); break; + case DIRALPHABETICAL : std::sort(dir->outputList.begin(), dir->outputList.end(), SortByDirName); break; + case ALPHABETICALREV : std::sort(dir->outputList.begin(), dir->outputList.end(), SortByNameRev); break; + case DIRALPHABETICALREV : std::sort(dir->outputList.begin(), dir->outputList.end(), SortByDirNameRev); break; + }; + } +}; + +void DOS_Drive_Cache::CacheOut(const char* path, bool ignoreLastDir) +{ + char expand[CROSS_LEN] = { 0 }; + CFileInfo* dir; + + if (ignoreLastDir) { + char tmp[CROSS_LEN] = { 0 }; + Bit32s len = strrchr(path,CROSS_FILESPLIT) - path; + if (len>0) { + strncpy(tmp,path,len); + tmp[len] = 0; + } else { + strcpy(tmp,path); + } + dir = FindDirInfo(tmp,expand); + } else { + dir = FindDirInfo(path,expand); + } +// LOG_DEBUG("DIR: Caching out %s : dir %s",expand,dir->fullname); + // delete file objects... + for(Bit32u i=0; ifileList.size(); i++) delete dir->fileList[i]; + // clear lists + dir->fileList.clear(); + dir->longNameList.clear(); + dir->outputList.clear(); + dir->shortNr = 0; + save_dir = 0; + nextEntry = 0; +}; + +bool DOS_Drive_Cache::IsCachedIn(CFileInfo* curDir) +{ + return (curDir->fileList.size()>0); +}; + +Bit16u DOS_Drive_Cache::CreateShortNameID(CFileInfo* curDir, const char* name) +{ + Bit16s foundNr = 0; + Bit16s low = 0; + Bit16s high = curDir->longNameList.size()-1; + Bit16s mid, res; + + CFileInfo* test; + + while (low<=high) { + mid = (low+high)/2; + test = curDir->longNameList[mid]; + res = strncmp(name,curDir->longNameList[mid]->shortname,curDir->longNameList[mid]->compareCount); + if (res>0) low = mid+1; else + if (res<0) high = mid-1; + else { + // any more same x chars in next entries ? + do { + foundNr = curDir->longNameList[mid]->shortNr; + mid++; + } while(midlongNameList.size() && (strncmp(name,curDir->longNameList[mid]->shortname,curDir->longNameList[mid]->compareCount)==0)); + break; + }; + } + return foundNr+1; +}; + +Bit16s DOS_Drive_Cache::GetLongName(CFileInfo* curDir, char* shortName) +{ + // Search long name and return array number of element + Bit16s low = 0; + Bit16s high = curDir->fileList.size()-1; + Bit16s mid,res; + while (low<=high) { + mid = (low+high)/2; + CFileInfo* test = curDir->fileList[mid]; + res = strcmp(shortName,curDir->fileList[mid]->shortname); + if (res>0) low = mid+1; else + if (res<0) high = mid-1; else + { // Found +// strcpy(shortName,curDir->fileList[mid]->fullname); + strcpy(shortName,curDir->fileList[mid]->orgname); + return mid; + }; + } + // not available + return -1; +}; + +Bit16s DOS_Drive_Cache::RemoveSpaces(char* str) +// Removes all spaces +// return length of string upto first '.' +{ + char* curpos = str; + char* chkpos = str; + Bit16s len = -1; + while (*chkpos!=0) { + if (*chkpos=='.') { if (len<0) len = curpos - str; }; + if (*chkpos==' ') chkpos++; else *curpos++ = *chkpos++; + } + *curpos = 0; + if (len<0) len = curpos - str; + return len; +}; + +void DOS_Drive_Cache::CreateShortName(CFileInfo* curDir, CFileInfo* info) +{ + Bit16s len = 0; + Bit16s lenExt = 0; + char tmpName[CROSS_LEN]; + + // Remove Spaces + strcpy(tmpName,info->fullname); + len = RemoveSpaces(tmpName); + + // Get Length of filename + char* pos = strchr(tmpName,'.'); + if (pos) { + len = (Bit16u)(pos - tmpName); + // Get Length of extension + lenExt = strlen(pos)-1; + } else + len = strlen(tmpName); + + // Should shortname version be created ? + bool createShort = (len>8) || (lenExt>3); +// if (!createShort) { +// char buffer[CROSS_LEN]; +// strcpy(buffer,tmpName); +// createShort = (GetLongName(curDir,buffer)>=0); +// } + + if (createShort) { + // Create number + char buffer[8]; + info->shortNr = CreateShortNameID(curDir,tmpName); + sprintf(buffer,"%d",info->shortNr); + // Copy first letters + Bit16u tocopy; + if (len+strlen(buffer)>8) tocopy = 8 - strlen(buffer) - 1; + else tocopy = len; + strncpy(info->shortname,tmpName,tocopy); + info->shortname[tocopy] = 0; + // Copy number + strcat(info->shortname,"~"); + strcat(info->shortname,buffer); + // Create compare Count + info->compareCount = tocopy; + // Add (and cut) Extension, if available + if (pos) { + // Step to last extension... + pos = strrchr(tmpName, '.'); + // add extension + strncat(info->shortname,pos,4); + info->shortname[DOS_NAMELENGTH] = 0; + }; + // Put it in longname list... + curDir->longNameList.push_back(info); + std::sort(curDir->longNameList.begin(), curDir->longNameList.end(), SortByName); + } else { + strcpy(info->shortname,tmpName); + } +}; + +DOS_Drive_Cache::CFileInfo* DOS_Drive_Cache::FindDirInfo(const char* path, char* expandedPath) +{ + // statics + static char split[2] = { CROSS_FILESPLIT,0 }; + + char dir [CROSS_LEN]; + char work [CROSS_LEN]; + char* start = (char*)path; + char* pos; + CFileInfo* curDir = dirBase; + + if (save_dir && (strcmp(path,save_path)==0)) { + strcpy(expandedPath,save_expanded); + return save_dir; + }; + +// LOG_DEBUG("DIR: Find %s",path); + + // Remove base dir path + start += strlen(dirBase->fullname); + strcpy(expandedPath,dirBase->fullname); + + do { +// bool errorcheck = false; + pos = strchr(start,CROSS_FILESPLIT); + if (pos) { strncpy(dir,start,pos-start); dir[pos-start] = 0; /*errorcheck = true;*/ } + else { strcpy(dir,start); }; + + // Path found + Bit16s nextDir = GetLongName(curDir,dir); + strcat(expandedPath,dir); + + // Error check +// if ((errorcheck) && (nextDir<0)) { +// LOG_DEBUG("DIR: Error: %s not found.",expandedPath); +// }; + + // Follow Directory + if ((nextDir>=0) && curDir->fileList[nextDir]->isDir) { + curDir = curDir->fileList[nextDir]; + strcpy (curDir->fullname,dir); + strcpy (work,path); + // Cut Directory, if its only part of whole path + if (pos) work[(Bit32u)pos-(Bit32u)path] = 0; + if (!IsCachedIn(curDir)) { + if (OpenDir(curDir,work)) { + struct dirent result; + struct stat status; + ReadDir(&result,&status); + }; + } + }; + if (pos) { + strcat(expandedPath,split); + start = pos+1; + } + } while (pos); + + // Save last result for faster access next time + strcpy(save_path,path); + strcpy(save_expanded,expandedPath); + save_dir = curDir; + + return curDir; +}; + +bool DOS_Drive_Cache::OpenDir(const char* path) +{ + char expand[CROSS_LEN] = {0}; + CFileInfo* dir = FindDirInfo(path,expand); + return OpenDir(dir,expand); +}; + +bool DOS_Drive_Cache::OpenDir(CFileInfo* dir, char* expand) +{ + dirSearch = dir; + // Add "/" + char end[2]={CROSS_FILESPLIT,0}; + if (expand[strlen(expand)-1]!=CROSS_FILESPLIT) strcat(expand,end); + // open dir + if (dirSearch) { + // open dir + DIR* dirp = opendir(expand); + if (dirp) { + dirFirstTime = true; + closedir(dirp); + strcpy(dirPath,expand); + return true; + } + }; + return false; +}; + +void DOS_Drive_Cache::CreateEntry(CFileInfo* dir, const char* name) +{ + struct stat status; + CFileInfo* info = new CFileInfo; + strcpy(info->fullname,name); + strcpy(info->orgname ,name); + // always upcase + upcase(info->fullname); + info->shortNr = 0; + // Read and copy file stats + char buffer[CROSS_LEN]; + strcpy(buffer,dirPath); + strcat(buffer,info->fullname); + stat (buffer,&status); + memcpy(&info->status,&status,sizeof(status)); + info->isDir = (S_ISDIR(status.st_mode)>0); + // Check for long filenames... + CreateShortName(dir, info); + // Put file in lists + dir->fileList.push_back(info); + dir->outputList.push_back(info); +}; + +bool DOS_Drive_Cache::ReadDir(struct dirent* result, struct stat *status) +{ + if (dirFirstTime) { + if (!IsCachedIn(dirSearch)) { + // Try to open directory + DIR* dirp = opendir(dirPath); + if (!dirp) { +// LOG_DEBUG("DIR: Error Caching in %s",dirPath); + return false; + } else { +// LOG_DEBUG("DIR: Caching in %s",dirPath); + }; + // Read complete directory + struct dirent* tmpres; + while (tmpres = readdir(dirp)) { + CreateEntry(dirSearch,tmpres->d_name); + } + // close dir + closedir(dirp); + // Sort Lists - filelist has to be alphabetically sorted + std::sort(dirSearch->fileList.begin(), dirSearch->fileList.end(), SortByName); + // Output list - user defined + switch (sortDirType) { + case ALPHABETICAL : std::sort(dirSearch->outputList.begin(), dirSearch->outputList.end(), SortByName); break; + case DIRALPHABETICAL : std::sort(dirSearch->outputList.begin(), dirSearch->outputList.end(), SortByDirName); break; + case ALPHABETICALREV : std::sort(dirSearch->outputList.begin(), dirSearch->outputList.end(), SortByNameRev); break; + case DIRALPHABETICALREV : std::sort(dirSearch->outputList.begin(), dirSearch->outputList.end(), SortByDirNameRev); break; + }; + } else { + dirFirstTime=false; + } + // Reset it.. + nextEntry = 0; + }; + return SetResult(dirSearch, result, status, nextEntry); +}; + +bool DOS_Drive_Cache::SetResult(CFileInfo* dir, struct dirent* result, struct stat* status, Bit16u entryNr) +{ + if (entryNr>=dir->outputList.size()) return false; + CFileInfo* info = dir->outputList[entryNr]; + // copy filename, short version + strcpy(result->d_name,info->shortname); + // copy file status + memcpy(status,&info->status,sizeof(info->status)); + // Set to next Entry + nextEntry = entryNr+1; + return true; +}; + diff --git a/src/dos/drive_local.cpp b/src/dos/drive_local.cpp index a12c504b..c32e9e64 100644 --- a/src/dos/drive_local.cpp +++ b/src/dos/drive_local.cpp @@ -49,8 +49,9 @@ bool localDrive::FileCreate(DOS_File * * file,char * name,Bit16u attributes) { strcpy(newname,basedir); strcat(newname,name); CROSS_FILENAME(newname); - FILE * hand=fopen(newname,"wb+"); + FILE * hand=fopen(dirCache.GetExpandName(newname),"wb+"); if (!hand) return false; + dirCache.AddEntry(newname); /* Make the 16 bit device information */ *file=new localFile(hand,0x202); return true; @@ -59,9 +60,9 @@ bool localDrive::FileCreate(DOS_File * * file,char * name,Bit16u attributes) { bool localDrive::FileOpen(DOS_File * * file,char * name,Bit32u flags) { char * type; switch (flags) { - case OPEN_READ:type="rb";break; - case OPEN_WRITE:type="rb+";break; - case OPEN_READWRITE:type="rb+";break; + case OPEN_READ:type="rb"; break; + case OPEN_WRITE:type="rb+"; break; + case OPEN_READWRITE:type="rb+"; break; default: //TODO FIX IT type="rb+"; @@ -72,10 +73,13 @@ bool localDrive::FileOpen(DOS_File * * file,char * name,Bit32u flags) { strcpy(newname,basedir); strcat(newname,name); CROSS_FILENAME(newname); + dirCache.ExpandName(newname); + FILE * hand=fopen(newname,type); Bit32u err=errno; if (!hand) return false; *file=new localFile(hand,0x202); +// (*file)->SetFileName(newname); return true; }; @@ -84,54 +88,59 @@ bool localDrive::FileUnlink(char * name) { strcpy(newname,basedir); strcat(newname,name); CROSS_FILENAME(newname); - if (!unlink(newname)) return true; + if (!unlink(dirCache.GetExpandName(newname))) { + dirCache.CacheOut(newname); + return true; + }; return false; }; bool localDrive::FindFirst(char * _dir,DOS_DTA & dta) { - if (srch_opendir) closedir(srch_opendir); + strcpy(srch_dir,basedir); strcat(srch_dir,_dir); CROSS_FILENAME(srch_dir); char end[2]={CROSS_FILESPLIT,0}; if (srch_dir[strlen(srch_dir)-1]!=CROSS_FILESPLIT) strcat(srch_dir,end); - if((srch_opendir=opendir(srch_dir))==NULL) return false; + + if (!dirCache.OpenDir(srch_dir)) return false; return FindNext(dta); } + bool localDrive::FindNext(DOS_DTA & dta) { - struct dirent * dir_ent; + + struct dirent dir_ent; struct stat stat_block; - char full_name[CROSS_LEN]; + Bit8u srch_attr;char srch_pattern[DOS_NAMELENGTH_ASCII]; Bit8u find_attr; - if(!srch_opendir) return false; - dta.GetSearchParams(srch_attr,srch_pattern); - again: - if((dir_ent=readdir(srch_opendir))==NULL) { - closedir(srch_opendir); - srch_opendir=NULL; - return false; - } - if(!WildFileCmp(dir_ent->d_name,srch_pattern)) goto again; - strcpy(full_name,srch_dir); - strcat(full_name,dir_ent->d_name); - if(stat(full_name,&stat_block)!=0){ - goto again; - } + +again: + if (!dirCache.ReadDir(&dir_ent,&stat_block)) return false; + + if(!WildFileCmp(dir_ent.d_name,srch_pattern)) goto again; + if(S_ISDIR(stat_block.st_mode)) find_attr=DOS_ATTR_DIRECTORY; else find_attr=DOS_ATTR_ARCHIVE; if (~srch_attr & find_attr & (DOS_ATTR_DIRECTORY | DOS_ATTR_HIDDEN | DOS_ATTR_SYSTEM)) goto again; + + if(S_ISDIR(stat_block.st_mode)) find_attr=DOS_ATTR_DIRECTORY; + else find_attr=DOS_ATTR_ARCHIVE; + if (~srch_attr & find_attr & (DOS_ATTR_DIRECTORY | DOS_ATTR_HIDDEN | DOS_ATTR_SYSTEM)) goto again; + /*file is okay, setup everything to be copied in DTA Block */ char find_name[DOS_NAMELENGTH_ASCII];Bit16u find_date,find_time;Bit32u find_size; - if(strlen(dir_ent->d_name)d_name); + + if(strlen(dir_ent.d_name)0) && (newdir[len-1]!='\\')) { @@ -209,7 +222,8 @@ bool localDrive::Rename(char * oldname,char * newname) { strcpy(newnew,basedir); strcat(newnew,newname); CROSS_FILENAME(newnew); - int temp=rename(newold,newnew); + int temp=rename(dirCache.GetExpandName(newold),dirCache.GetExpandName(newnew)); + if (temp==0) dirCache.CacheOut(newnew); return (temp==0); }; @@ -229,6 +243,7 @@ bool localDrive::FileExists(const char* name) { strcpy(newname,basedir); strcat(newname,name); CROSS_FILENAME(newname); + dirCache.ExpandName(newname); FILE* Temp=fopen(newname,"rb"); if(Temp==NULL) return false; fclose(Temp); @@ -240,6 +255,7 @@ bool localDrive::FileStat(const char* name, FileStat_Block * const stat_block) { strcpy(newname,basedir); strcat(newname,name); CROSS_FILENAME(newname); + dirCache.ExpandName(newname); struct stat temp_stat; if(stat(newname,&temp_stat)!=0) return false; /* Convert the stat to a FileStat */ @@ -268,6 +284,8 @@ localDrive::localDrive(const char * startdir,Bit16u _bytes_sector,Bit8u _sectors allocation.total_clusters=_total_clusters; allocation.free_clusters=_free_clusters; allocation.mediaid=_mediaid; + + dirCache.SetBaseDir(basedir); } @@ -312,6 +330,7 @@ bool localFile::Seek(Bit32u * pos,Bit32u type) { } bool localFile::Close() { + fclose(fhandle); return true; } @@ -319,7 +338,7 @@ bool localFile::Close() { Bit16u localFile::GetInformation(void) { return info; } - + localFile::localFile(FILE * handle,Bit16u devinfo) { fhandle=handle; diff --git a/src/dos/drives.h b/src/dos/drives.h index 90c8fce6..893d64dc 100644 --- a/src/dos/drives.h +++ b/src/dos/drives.h @@ -21,12 +21,77 @@ #include #include +#include #include "dos_system.h" #include "cross.h" bool WildFileCmp(const char * file, const char * wild); +class DOS_Drive_Cache { +public: + DOS_Drive_Cache (void); + DOS_Drive_Cache (const char* path); + ~DOS_Drive_Cache (void); + + typedef enum TDirSort { NOSORT, ALPHABETICAL, DIRALPHABETICAL, ALPHABETICALREV, DIRALPHABETICALREV }; + + void SetBaseDir (const char* path); + void SetDirSort (TDirSort sort) { sortDirType = sort; }; + bool OpenDir (const char* path); + bool ReadDir (struct dirent* result,struct stat* status); + + void ExpandName (char* path); + char* GetExpandName (const char* path); + + void CacheOut (const char* path, bool ignoreLastDir = false); + void AddEntry (const char* path); + + class CFileInfo { + public: + ~CFileInfo(void) { + for (Bit32u i=0; i fileList; + std::vector longNameList; + std::vector outputList; + }; + +private: + + Bit16s GetLongName (CFileInfo* info, char* shortname); + void CreateShortName (CFileInfo* dir, CFileInfo* info); + Bit16u CreateShortNameID (CFileInfo* dir, const char* name); + bool SetResult (CFileInfo* dir, struct dirent* result, struct stat* status, Bit16u entryNr); + bool IsCachedIn (CFileInfo* dir); + CFileInfo* FindDirInfo (const char* path, char* expandedPath); + Bit16s RemoveSpaces (char* str); + bool OpenDir (CFileInfo* dir, char* path); + void CreateEntry (CFileInfo* dir, const char* name); + + Bit32u nextEntry; + CFileInfo* dirBase; + CFileInfo* dirSearch; + char dirPath [CROSS_LEN]; + bool dirFirstTime; + TDirSort sortDirType; + CFileInfo* save_dir; + char save_path [CROSS_LEN]; + char save_expanded [CROSS_LEN]; + +}; + class localDrive : public DOS_Drive { public: localDrive(const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid); @@ -55,6 +120,7 @@ private: Bit16u free_clusters; Bit8u mediaid; } allocation; + DOS_Drive_Cache dirCache; }; struct VFILE_Block;