bool DOS_Rename(char const * const oldname,char const * const newname) { Bit8u driveold;char fullold[DOS_PATHLENGTH]; Bit8u drivenew;char fullnew[DOS_PATHLENGTH]; if (!DOS_MakeName(oldname,fullold,&driveold)) return false; if (!DOS_MakeName(newname,fullnew,&drivenew)) return false; /* No tricks with devices */ if ( (DOS_FindDevice(oldname) != DOS_DEVICES) || (DOS_FindDevice(newname) != DOS_DEVICES) ) { DOS_SetError(DOSERR_FILE_NOT_FOUND); return false; } /* Must be on the same drive */ if(driveold != drivenew) { DOS_SetError(DOSERR_NOT_SAME_DEVICE); return false; } /*Test if target exists => no access */ Bit16u attr; if(Drives[drivenew]->GetFileAttr(fullnew,&attr)) { DOS_SetError(DOSERR_ACCESS_DENIED); return false; } /* Source must exist, check for path ? */ if (!Drives[driveold]->GetFileAttr( fullold, &attr ) ) { DOS_SetError(DOSERR_FILE_NOT_FOUND); return false; } if (Drives[drivenew]->Rename(fullold,fullnew)) return true; /* If it still fails. which error should we give ? PATH NOT FOUND or EACCESS */ LOG(LOG_FILES,LOG_NORMAL)("Rename fails for %s to %s, no proper errorcode returned.",oldname,newname); DOS_SetError(DOSERR_FILE_NOT_FOUND); return false; }
bool DOS_FCBOpen(Bit16u seg,Bit16u offset) { DOS_FCB fcb(seg,offset); char shortname[DOS_FCBNAME];Bit16u handle; fcb.GetName(shortname); /* First check if the name is correct */ Bit8u drive; char fullname[DOS_PATHLENGTH]; if (!DOS_MakeName(shortname,fullname,&drive)) return false; /* Check, if file is already opened */ for (Bit8u i=0;i<DOS_FILES;i++) { DOS_PSP psp(dos.psp()); if (Files[i] && Files[i]->IsOpen() && Files[i]->IsName(fullname)) { handle = psp.FindEntryByHandle(i); if (handle==0xFF) { // This shouldnt happen LOG(LOG_FILES,LOG_ERROR)("DOS: File %s is opened but has no psp entry.",shortname); return false; } fcb.FileOpen((Bit8u)handle); return true; } } if (!DOS_OpenFile(shortname,OPEN_READWRITE,&handle)) return false; fcb.FileOpen((Bit8u)handle); return true; }
bool DOS_FCBRenameFile(Bit16u seg, Bit16u offset) { DOS_FCB fcbold(seg,offset); DOS_FCB fcbnew(seg,offset+16); if(!fcbold.Valid()) return false; char oldname[DOS_FCBNAME]; char newname[DOS_FCBNAME]; fcbold.GetName(oldname); fcbnew.GetName(newname); /* Check, if sourcefile is still open. This was possible in DOS, but modern oses don't like this */ Bit8u drive; char fullname[DOS_PATHLENGTH]; if (!DOS_MakeName(oldname,fullname,&drive)) return false; DOS_PSP psp(dos.psp()); for (Bit8u i=0; i<DOS_FILES; i++) { if (Files[i] && Files[i]->IsOpen() && Files[i]->IsName(fullname)) { Bit16u handle = psp.FindEntryByHandle(i); //(more than once maybe) if (handle == 0xFF) { DOS_CloseFile(i,true); } else { DOS_CloseFile(handle); } } } /* Rename the file */ return DOS_Rename(oldname,newname); }
bool DOS_RemoveDir(char const * const dir) { /* We need to do the test before the removal as can not rely on * the host to forbid removal of the current directory. * We never change directory. Everything happens in the drives. */ Bit8u drive; char fulldir[DOS_PATHLENGTH]; if (!DOS_MakeName(dir,fulldir,&drive)) return false; /* Check if exists */ if(!Drives[drive]->TestDir(fulldir)) { DOS_SetError(DOSERR_PATH_NOT_FOUND); return false; } /* See if it's current directory */ char currdir[DOS_PATHLENGTH]= { 0 }; DOS_GetCurrentDir(drive + 1 ,currdir); if(strcmp(currdir,fulldir) == 0) { DOS_SetError(DOSERR_REMOVE_CURRENT_DIRECTORY); return false; } if(Drives[drive]->RemoveDir(fulldir)) return true; /* Failed. We know it exists and it's not the current dir */ /* Assume non empty */ DOS_SetError(DOSERR_ACCESS_DENIED); return false; }
bool DOS_MakeDir(char const* const dir) { Bit8u drive; const char *testdir = dir; if (*testdir && testdir[1] == ':') { drive = (*testdir | 0x20)-'a'; if (drive >= DOS_DRIVES || !Drives[drive]) { DOS_SetError(DOSERR_INVALID_DRIVE); return false; } testdir += 2; } size_t len = strlen(testdir); char fulldir[DOS_PATHLENGTH]; if (!len || !DOS_MakeName(dir, fulldir, &drive) || (*fulldir && testdir[len-1] == '\\')) { DOS_SetError(DOSERR_PATH_NOT_FOUND); return false; } if (Drives[drive]->MakeDir(fulldir)) return true; if (Drives[drive]->TestDir(fulldir)) // Determine reason for failing DOS_SetError(DOSERR_ACCESS_DENIED); else DOS_SetError(DOSERR_PATH_NOT_FOUND); return false; }
bool DOS_ChangeDir(char const* const dir) { Bit8u drive; const char *testdir = dir; if (*testdir && testdir[1] == ':') { drive = (*dir | 0x20)-'a'; if (drive >= DOS_DRIVES || !Drives[drive]) { DOS_SetError(DOSERR_INVALID_DRIVE); return false; } testdir += 2; } size_t len = strlen(testdir); char fulldir[DOS_PATHLENGTH]; if (!len || !DOS_MakeName(dir, fulldir, &drive) || (*fulldir && testdir[len-1] == '\\')) { } else if (Drives[drive]->TestDir(fulldir)) { for (int i = 0; fulldir[i]; i++) // Names in MS-DOS are allways uppercase fulldir[i] = toupper(fulldir[i]); strcpy(Drives[drive]->curdir, fulldir); return true; } DOS_SetError(DOSERR_PATH_NOT_FOUND); return false; }
Bit8u DOS_FindDevice(char const * name) { /* should only check for the names before the dot and spacepadded */ char fullname[DOS_PATHLENGTH];Bit8u drive; // if(!name || !(*name)) return DOS_DEVICES; //important, but makename does it if (!DOS_MakeName(name,fullname,&drive)) return DOS_DEVICES; char* name_part = strrchr(fullname,'\\'); if(name_part) { *name_part++ = 0; //Check validity of leading directory. if(!Drives[drive]->TestDir(fullname)) return DOS_DEVICES; } else name_part = fullname; char* dot = strrchr(name_part,'.'); if(dot) *dot = 0; //no ext checking static char com[5] = { 'C','O','M','1',0 }; static char lpt[5] = { 'L','P','T','1',0 }; // AUX is alias for COM1 and PRN for LPT1 // A bit of a hack. (but less then before). // no need for casecmp as makename returns uppercase if (strcmp(name_part, "AUX") == 0) name_part = com; if (strcmp(name_part, "PRN") == 0) name_part = lpt; /* loop through devices */ for(Bit8u index = 0;index < DOS_DEVICES;index++) { if (Devices[index]) { if (WildFileCmp(name_part,Devices[index]->name)) return index; } } return DOS_DEVICES; }
bool DOS_FileExists(char const* const name) { char fullname[DOS_PATHLENGTH]; Bit8u drive; if (!DOS_MakeName(name, fullname, &drive)) return false; return Drives[drive]->FileExists(fullname); }
void* DOS_CreateOpenFile(char const* const name) { char fullname[DOS_PATHLENGTH]; Bit8u drive; if (!DOS_MakeName(name, fullname, &drive)) return NULL; return Drives[drive]->CreateOpenFile(fullname); }
Bit32u DOS_GetCompressedFileSize(char const* const name) { char fullname[DOS_PATHLENGTH]; Bit8u drive; if (!DOS_MakeName(name, fullname, &drive)) return false; return Drives[drive]->GetCompressedSize(fullname); }
bool DOS_GetSFNPath(char const * const path,char * SFNPath,bool LFN) { char dir_current[DOS_PATHLENGTH + 1], pdir[LFN_NAMELENGTH], *p; Bit8u drive;char fulldir[DOS_PATHLENGTH],LFNPath[CROSS_LEN]; char name[DOS_NAMELENGTH_ASCII], lname[LFN_NAMELENGTH]; int w=0; DOS_DTA dta(dos.dta()); Bit32u size;Bit16u date;Bit16u time;Bit8u attr; if (!DOS_MakeName(path,fulldir,&drive)) return false; sprintf(SFNPath,"%c:\\",drive+'A'); strcpy(LFNPath,SFNPath); strcpy(dir_current,Drives[drive]->curdir); Drives[drive]->curdir,""; p = fulldir; if (*p==0) return true; for (char *s = strchr(p,'\\'); s != NULL; s = strchr(p,'\\')) { *s = 0; if (SFNPath[strlen(SFNPath)-1]=='\\') sprintf(pdir,"\"%s%s\"",SFNPath,p); else sprintf(pdir,"\"%s\\%s\"",SFNPath,p); if (!strrchr(p,'*') && !strrchr(p,'?')) { *s = '\\'; p = s + 1; if (DOS_FindFirst(pdir,0xffff & DOS_ATTR_DIRECTORY & ~DOS_ATTR_VOLUME,false)) { dta.GetResult(name,lname,size,date,time,attr); strcat(SFNPath,name); strcat(LFNPath,lname); Drives[drive]->curdir,SFNPath+3; strcat(SFNPath,"\\"); strcat(LFNPath,"\\"); } else { return false;} } else { strcat(SFNPath,p); strcat(LFNPath,p); strcat(SFNPath,"\\"); strcat(LFNPath,"\\"); *s = '\\'; p = s + 1; break; } } if (p != 0) { sprintf(pdir,"\"%s%s\"",SFNPath,p); if (!strrchr(p,'*')&&!strrchr(p,'?')&&DOS_FindFirst(pdir,0xffff & ~DOS_ATTR_VOLUME,false)) { dta.GetResult(name,lname,size,date,time,attr); strcat(SFNPath,name); strcat(LFNPath,lname); } else { strcat(SFNPath,p); strcat(LFNPath,p); } } Drives[drive]->curdir,dir_current; if (LFN) strcpy(SFNPath,LFNPath); return true; }
bool DOS_Canonicalize(char const* const name, char* const big) { // TODO Add Better support for devices and shit but will it be needed i doubt it :) if (!DOS_MakeName(name, &big[3], (Bit8u *)big)) return false; big[0] += 'A'; big[1] = ':'; big[2] = '\\'; return true; }
bool DOS_GetFileAttr(char const * const name,Bit16u * attr) { char fullname[DOS_PATHLENGTH];Bit8u drive; if (!DOS_MakeName(name,fullname,&drive)) return false; if (Drives[drive]->GetFileAttr(fullname,attr)) { return true; } else { DOS_SetError(DOSERR_FILE_NOT_FOUND); return false; } }
bool DOS_UnlinkFile(char const * const name) { char fullname[DOS_PATHLENGTH];Bit8u drive; if (!DOS_MakeName(name,fullname,&drive)) return false; if(Drives[drive]->FileUnlink(fullname)){ return true; } else { DOS_SetError(DOSERR_FILE_NOT_FOUND); return false; } }
bool DOS_GetFileAttrEx(char const* const name, struct stat *status, Bit8u hdrive) { char fullname[DOS_PATHLENGTH]; Bit8u drive; bool usehdrive=hdrive>=0&&hdrive<DOS_FILES; if (usehdrive) strcpy(fullname,name); else if (!DOS_MakeName(name, fullname, &drive)) return false; return Drives[usehdrive?hdrive:drive]->GetFileAttrEx(fullname, status); }
bool DOS_Canonicalize(char const * const name,char * const big) { //TODO Add Better support for devices and shit but will it be needed i doubt it :) Bit8u drive; char fullname[DOS_PATHLENGTH]; if (!DOS_MakeName(name,fullname,&drive)) return false; big[0]=drive+'A'; big[1]=':'; big[2]='\\'; strcpy(&big[3],fullname); return true; }
bool DOS_SetFileAttr(char const* const name, Bit16u /*attr*/) // this function does not change the file attributs // it just does some tests if file is available // returns false when using on cdrom (stonekeep) { char fullname[DOS_PATHLENGTH]; Bit8u drive; if (!DOS_MakeName(name, fullname, &drive)) return false; Bit16u attrTemp; return Drives[drive]->GetFileAttr(fullname, &attrTemp); }
bool DOS_ChangeDir(char const * const dir) { Bit8u drive;char fulldir[DOS_PATHLENGTH]; if (!DOS_MakeName(dir,fulldir,&drive)) return false; if (Drives[drive]->TestDir(fulldir)) { strcpy(Drives[drive]->curdir,fulldir); return true; } else { DOS_SetError(DOSERR_PATH_NOT_FOUND); } return false; }
static bool PathExists(char const * const name) { const char* leading = strrchr(name,'\\'); if(!leading) return true; char temp[CROSS_LEN]; strcpy(temp,name); char * lead = strrchr(temp,'\\'); if (lead == temp) return true; *lead = 0; Bit8u drive;char fulldir[DOS_PATHLENGTH]; if (!DOS_MakeName(temp,fulldir,&drive)) return false; if(!Drives[drive]->TestDir(fulldir)) return false; return true; }
static bool PathExists(char const* const name) { char temp[MAX_PATH_LEN]; char* lead = strrchr(strcpy(temp, name), '\\'); if (!lead || lead == temp) return true; *lead = 0; Bit8u drive; char fulldir[DOS_PATHLENGTH]; if (!DOS_MakeName(temp, fulldir, &drive) || !Drives[drive]->TestDir(fulldir)) return false; return true; }
bool DOS_Rename(char const * const oldname, char const * const newname) { Bit8u driveold; char fullold[DOS_PATHLENGTH]; if (!DOS_MakeName(oldname, fullold, &driveold)) { DOS_SetError(DOSERR_FILE_NOT_FOUND); return false; } Bit8u drivenew; char fullnew[DOS_PATHLENGTH]; if (!DOS_MakeName(newname, fullnew, &drivenew)) return false; if ((DOS_FindDevice(oldname) != DOS_DEVICES) || (DOS_FindDevice(newname) != DOS_DEVICES)) // No tricks with devices { DOS_SetError(DOSERR_FILE_NOT_FOUND); return false; } if (driveold != drivenew) // Must be on the same drive { DOS_SetError(DOSERR_NOT_SAME_DEVICE); return false; } Bit16u attr; if (Drives[drivenew]->GetFileAttr(fullnew, &attr)) // Test if target exists => no access { DOS_SetError(DOSERR_ACCESS_DENIED); return false; } if (!Drives[driveold]->GetFileAttr(fullold, &attr )) // Source must exist, check for path ? { DOS_SetError(DOSERR_FILE_NOT_FOUND); return false; } if (Drives[drivenew]->Rename(fullold, fullnew)) return true; DOS_SetError(DOSERR_FILE_NOT_FOUND); // If it still fails. which error should we give ? PATH NOT FOUND or EACCESS return false; }
bool DOS_SetFileAttr(char const * const name,Bit16u /*attr*/) // this function does not change the file attributs // it just does some tests if file is available // returns false when using on cdrom (stonekeep) { Bit16u attrTemp; char fullname[DOS_PATHLENGTH];Bit8u drive; if (!DOS_MakeName(name,fullname,&drive)) return false; if (strncmp(Drives[drive]->GetInfo(),"CDRom ",6)==0 || strncmp(Drives[drive]->GetInfo(),"isoDrive ",9)==0) { DOS_SetError(DOSERR_ACCESS_DENIED); return false; } return Drives[drive]->GetFileAttr(fullname,&attrTemp); }
bool DOS_UnlinkFile(char const * const name) { char fullname[DOS_PATHLENGTH];Bit8u drive; // An existing device returns an access denied error if (DOS_FindDevice(name) != DOS_DEVICES) { DOS_SetError(DOSERR_ACCESS_DENIED); return false; } if (!DOS_MakeName(name,fullname,&drive)) return false; if(Drives[drive]->FileUnlink(fullname)){ return true; } else { DOS_SetError(DOSERR_FILE_NOT_FOUND); return false; } }
bool DOS_CreateFile(char const * name,Bit16u attributes,Bit16u * entry,bool fcb) { // Creation of a device is the same as opening it // Tc201 installer if (DOS_FindDevice(name) != DOS_DEVICES) return DOS_OpenFile(name, OPEN_READ, entry, fcb); LOG(LOG_FILES,LOG_NORMAL)("file create attributes %X file %s",attributes,name); char fullname[DOS_PATHLENGTH]; Bit8u drive; DOS_PSP psp(dos.psp()); if (!DOS_MakeName(name,fullname,&drive)) return false; /* Check for a free file handle */ Bit8u handle=DOS_FILES; Bit8u i; for (i=0; i<DOS_FILES; i++) { if (!Files[i]) { handle=i; break; } } if (handle==DOS_FILES) { DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES); return false; } /* We have a position in the main table now find one in the psp table */ *entry = fcb?handle:psp.FindFreeFileEntry(); if (*entry==0xff) { DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES); return false; } /* Don't allow directories to be created */ if (attributes&DOS_ATTR_DIRECTORY) { DOS_SetError(DOSERR_ACCESS_DENIED); return false; } bool foundit=Drives[drive]->FileCreate(&Files[handle],fullname,attributes); if (foundit) { Files[handle]->SetDrive(drive); Files[handle]->AddRef(); if (!fcb) psp.SetFileHandle(*entry,handle); return true; } else { if(!PathExists(name)) DOS_SetError(DOSERR_PATH_NOT_FOUND); else DOS_SetError(DOSERR_FILE_NOT_FOUND); return false; } }
bool DOS_MakeDir(char const * const dir) { Bit8u drive;char fulldir[DOS_PATHLENGTH]; size_t len = strlen(dir); if(!len || dir[len-1] == '\\') { DOS_SetError(DOSERR_PATH_NOT_FOUND); return false; } if (!DOS_MakeName(dir,fulldir,&drive)) return false; if(Drives[drive]->MakeDir(fulldir)) return true; /* Determine reason for failing */ if(Drives[drive]->TestDir(fulldir)) DOS_SetError(DOSERR_ACCESS_DENIED); else DOS_SetError(DOSERR_PATH_NOT_FOUND); return false; }
bool DOS_FindFirst(char * search,Bit16u attr,bool fcb_findfirst) { LOG(LOG_FILES,LOG_NORMAL)("file search attributes %X name %s",attr,search); DOS_DTA dta(dos.dta()); Bit8u drive; char fullsearch[DOS_PATHLENGTH]; char dir[DOS_PATHLENGTH]; char pattern[DOS_PATHLENGTH]; size_t len = strlen(search); if(len && search[len - 1] == '\\' && !( (len > 2) && (search[len - 2] == ':') && (attr == DOS_ATTR_VOLUME) )) { //Dark Forces installer, but c:\ is allright for volume labels(exclusively set) DOS_SetError(DOSERR_NO_MORE_FILES); return false; } if (!DOS_MakeName(search,fullsearch,&drive)) return false; //Check for devices. FindDevice checks for leading subdir as well bool device = (DOS_FindDevice(search) != DOS_DEVICES); /* Split the search in dir and pattern */ char * find_last; find_last=strrchr(fullsearch,'\\'); if (!find_last) { /*No dir */ strcpy(pattern,fullsearch); dir[0]=0; } else { *find_last=0; strcpy(pattern,find_last+1); strcpy(dir,fullsearch); } dta.SetupSearch(drive,(Bit8u)attr,pattern); if(device) { find_last = strrchr(pattern,'.'); if(find_last) *find_last = 0; //TODO use current date and time dta.SetResult(pattern,0,0,0,DOS_ATTR_DEVICE); LOG(LOG_DOSMISC,LOG_WARN)("finding device %s",pattern); return true; } if (Drives[drive]->FindFirst(dir,dta,fcb_findfirst)) return true; return false; }
bool DOS_CreateFile(char const* name, Bit16u attributes, Bit16u* entry) { if (DOS_FindDevice(name) != DOS_DEVICES) // Creating a device is the same as opening it return DOS_OpenFile(name, OPEN_READWRITE, entry); char fullname[DOS_PATHLENGTH]; Bit8u drive; if (!DOS_MakeName(name, fullname, &drive)) return false; Bit8u handle; for (handle = 0; handle < DOS_FILES; handle++) // Check for a free file handle if (!Files[handle]) break; if (handle == DOS_FILES) { DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES); return false; } DOS_PSP psp(dos.psp()); // We have a position in the main table, now find one in the psp table *entry = psp.FindFreeFileEntry(); if (*entry == 0xff) { DOS_SetError(DOSERR_TOO_MANY_OPEN_FILES); return false; } if (attributes&DOS_ATTR_DIRECTORY) // Don't allow directories to be created { DOS_SetError(DOSERR_ACCESS_DENIED); return false; } if (Drives[drive]->FileCreate(&Files[handle], fullname, attributes)) { Files[handle]->SetDrive(drive); Files[handle]->AddRef(); psp.SetFileHandle(*entry, handle); return true; } DOS_SetError(PathExists(name) ? DOSERR_FILE_ALREADY_EXISTS : DOSERR_PATH_NOT_FOUND); return false; }
Bit8u DOS_FindDevice(char const * name) { // should only check for the names before the dot and spacepadded char fullname[DOS_PATHLENGTH]; Bit8u drive; // if(!name || !(*name)) return DOS_DEVICES; //important, but makename does it if (!DOS_MakeName(name, fullname, &drive)) return DOS_DEVICES; char* name_part = strrchr(fullname,'\\'); if (name_part) { *name_part++ = 0; // Check validity of leading directory. if (!Drives[drive]->TestDir(fullname)) return DOS_DEVICES; } else name_part = fullname; char* dot = strrchr(name_part, '.'); if (dot) *dot = 0; // no ext checking static char com[] = "COM1"; static char lpt[] = "LPT1"; // AUX is alias for COM1 and PRN for LPT1 // A bit of a hack. (but less then before). if (!stricmp(name_part, "AUX")) name_part = com; else if (!stricmp(name_part, "PRN")) name_part = lpt; // loop through devices for (Bit8u index = 0; index < DOS_DEVICES; index++) if (Devices[index]) if (WildFileCmp(name_part, Devices[index]->name)) return index; return DOS_DEVICES; }
bool DOS_RemoveDir(char const * const dir) { /* We need to do the test before the removal as can not rely on * the host to forbid removal of the current directory. * We never change directory. Everything happens in the drives. */ Bit8u drive; const char *testdir = dir; if (*testdir && testdir[1] == ':') { drive = (*testdir | 0x20)-'a'; if (drive >= DOS_DRIVES || !Drives[drive]) { DOS_SetError(DOSERR_INVALID_DRIVE); return false; } testdir += 2; } size_t len = strlen(testdir); char fulldir[DOS_PATHLENGTH]; if (!len || !DOS_MakeName(dir, fulldir, &drive) || (*fulldir && testdir[len-1] == '\\')) { DOS_SetError(DOSERR_PATH_NOT_FOUND); return false; } if (!Drives[drive]->TestDir(fulldir)) // Check if exists { DOS_SetError(DOSERR_PATH_NOT_FOUND); return false; } if (strcmp(Drives[drive]->curdir, fulldir) == 0) // Test if it's current directory { DOS_SetError(DOSERR_REMOVE_CURRENT_DIRECTORY); return false; } if (Drives[drive]->RemoveDir(fulldir)) return true; DOS_SetError(DOSERR_ACCESS_DENIED); // Failed. We know it exists and it's not the current dir assume non empty return false; }
bool DOS_FindFirst(char * search, Bit16u attr, bool fcb_findfirst) { DOS_DTA dta(dos.dta()); size_t len = strlen(search); if (len && search[len - 1] == '\\' && !((len > 2) && (search[len - 2] == ':') && (attr == DOS_ATTR_VOLUME))) { DOS_SetError(DOSERR_NO_MORE_FILES); // Dark Forces installer, but c:\ is allright for volume labels(exclusively set) return false; } char fullsearch[DOS_PATHLENGTH]; Bit8u drive; if (!DOS_MakeName(search, fullsearch, &drive)) return false; char dir[DOS_PATHLENGTH]; // Split the search in dir and pattern char pattern[DOS_PATHLENGTH]; char * find_last = strrchr(fullsearch, '\\'); if (!find_last) // No dir { strcpy(pattern, fullsearch); dir[0] = 0; } else { *find_last = 0; strcpy(pattern, find_last+1); strcpy(dir, fullsearch); } dta.SetupSearch(drive, (Bit8u)attr, pattern); if (bool device = (DOS_FindDevice(search) != DOS_DEVICES)) // Check for devices. FindDevice checks for leading subdir as well { if (!(attr & DOS_ATTR_DEVICE)) return false; if (find_last = strrchr(pattern, '.')) *find_last = 0; dta.SetResult(pattern, 0, 0, 0, DOS_ATTR_DEVICE); // TODO use current date and time return true; } return Drives[drive]->FindFirst(dir, dta, fcb_findfirst); }