#include "cmd.h" extern TCHAR CurDrvDir[] ; extern TCHAR *SaveDir ; extern DWORD DosErr ; extern BOOL CtrlCSeen; PTCHAR SetWildCards( PTCHAR, BOOLEAN ); BOOLEAN IsFATDrive( PTCHAR ); VOID FreeStr( PTCHAR ); VOID SortFileList( PFS, PSORTDESC, ULONG); BOOLEAN FindFirstNt( PTCHAR, PWIN32_FIND_DATA, PHANDLE ); BOOLEAN FindNextNt ( PWIN32_FIND_DATA, HANDLE ); STATUS SetSearchPath ( PFS, PPATDSC, PTCHAR, ULONG); STATUS BuildFSFromPatterns ( IN PDRP pdpr, IN BOOLEAN fAddWild, OUT PFS * ppfs ) { struct cpyinfo * pcisFile; TCHAR szCurDir[MAX_PATH + 2]; TCHAR szFilePattern[MAX_PATH + 2]; PTCHAR pszPatternCur; PPATDSC ppatdscCur; PFS pfsFirst; PFS pfsCur; ULONG cbPath; BOOLEAN fFatDrive; ULONG i; PTCHAR pszT; // // determine FAT drive from original pattern. // Used in several places to control name format etc. // DosErr = 0; // // Run through each pattern making all sorts of FAT etc. specific // changes to it and creating the directory list for it. Then // combine groups of patterns into common directories and recurse // for each directory group. // *ppfs = pfsFirst = (PFS)gmkstr(sizeof(FS)); pfsFirst->pfsNext = NULL; pfsFirst->pszDir = NULL; pfsCur = pfsFirst; pfsCur->cpatdsc = 1; for(i = 1, ppatdscCur = &(pdpr->patdscFirst); i <= pdpr->cpatdsc; i++, ppatdscCur = ppatdscCur->ppatdscNext) { pszPatternCur = ppatdscCur->pszPattern; if (!(fFatDrive = IsFATDrive(pszPatternCur)) && DosErr) { // // Error in determining file system type so get out. // PutStdErr(DosErr, NOARGS); return( FAILURE ); } ppatdscCur->fIsFat = fFatDrive; // // Do any alterations that require wild cards for searching // such as change .xxx to *.xxx for FAT file system requests // // Note that if the return values is a different buffer then // the input the input will be freed when we are done with the // Dir command. // // // Note that though SetWildCards will allocate heap for the // modified pattern this will get freed when FreeStack is // called at the end of the Dir call. // // An out of memory is the only reason to fail and we would not // return from that but go through the abort call in gmstr // if (fAddWild) { pszT = SetWildCards(pszPatternCur, fFatDrive); FreeStr(pszPatternCur); pszPatternCur = pszT; } // // Convert the current pattern into a path and file part // // Save the current directory in SaveDir, change to new directory // and parse pattern into a copy information structure. This also // converts pszPatternCur into the current directory which also produces // a fully qualified name. // DosErr = 0; DEBUG((ICGRP, DILVL, "PrintPattern pattern `%ws'", pszPatternCur)); if ((pcisFile = SetFsSetSaveDir(pszPatternCur)) == (struct cpyinfo *) FAILURE) { // // DosErr is set in SetFs.. from GetLastError // // BUGBUG map to DIR error code // PutStdErr(DosErr, NOARGS); return( FAILURE ); } DEBUG((ICGRP, DILVL, "PrintPattern fullname `%ws'", pcisFile->fnptr)); // // CurDrvDir ends in '\' (old code also and a DOT but I do not // understand where this would come from I will leave it in for now. // Remove the final '\' from a copy of the current directory and // print that version out. // mystrcpy(szCurDir,CurDrvDir); // // SetFsSetSaveDir changes directories as a side effect. Since all // work will be in fully qualified paths we do not need this. Also // since we will change directories for each pattern that is examined // we will force the directory back to the original each time. // // This can not be done until after all use of the current directory // is made. // if (SaveDir) { mystrcpy(CurDrvDir,SaveDir); SaveDir = NULL; } DEBUG((ICGRP, DILVL, "PrintPattern Current Drive `%ws'", szCurDir)); cbPath = mystrlen(szCurDir); // // BUGBUG this is BS. it will not work for // dbcs. It is assuming character widths. // if (cbPath > 3) { if (fFatDrive && *penulc(szCurDir) == DOT) { szCurDir[cbPath-2] = NULLC; } else { szCurDir[cbPath-1] = NULLC; } } // // If no room for filename then return // if (cbPath >= MAX_PATH -1) { PutStdErr(ERROR_FILE_NOT_FOUND, NOARGS); return(FAILURE); } // // Add filename and possibly ext to szSearchPath // if no filename or ext, use "*" // // If pattern was just extension the SetWildCard had already // added * to front of extension. // if (*(pcisFile->fnptr) == NULLC) { mystrcpy(szFilePattern, TEXT("*")); } else { mystrcpy(szFilePattern, pcisFile->fnptr); } DEBUG((ICGRP, DILVL, "DIR:PrintPattern Pattern to search for `%ws'", szFilePattern)); // // Is name too long // if ((cbPath + mystrlen(szFilePattern) + 1) > MAX_PATH ) { PutStdErr(ERROR_BUFFER_OVERFLOW, NOARGS); return( FAILURE ); } else { // // If this is a FAT drive and there was a filename with // no extension then add '.*' (and there is room) // if (*pcisFile->fnptr && (!pcisFile->extptr || !*pcisFile->extptr) && ((mystrlen(szFilePattern) + 2) < MAX_PATH) && fFatDrive && fAddWild) { mystrcat(szFilePattern, TEXT(".*")) ; } } // // ppatdscCur->pszPattern will be freed at end of command when everything // else is freed. // ppatdscCur->pszPattern = (PTCHAR)gmkstr(_tcslen(szFilePattern)*sizeof(TCHAR) + sizeof(TCHAR)); mystrcpy(ppatdscCur->pszPattern, szFilePattern); ppatdscCur->pszDir = (PTCHAR)gmkstr(_tcslen(szCurDir)*sizeof(TCHAR) + sizeof(TCHAR)); mystrcpy(ppatdscCur->pszDir, szCurDir); if (pfsCur->pszDir) { // // changing directories so change directory grouping. // if (_tcsicmp(pfsCur->pszDir, ppatdscCur->pszDir)) { pfsCur->pfsNext = (PFS)gmkstr(sizeof(FS)); pfsCur = pfsCur->pfsNext; pfsCur->pszDir = (PTCHAR)gmkstr(_tcslen(ppatdscCur->pszDir)*sizeof(TCHAR) + sizeof(TCHAR)); mystrcpy(pfsCur->pszDir, ppatdscCur->pszDir); pfsCur->pfsNext = NULL; pfsCur->fIsFat = ppatdscCur->fIsFat; pfsCur->ppatdsc = ppatdscCur; pfsCur->cpatdsc = 1; } else { pfsCur->cpatdsc++; } } else { // // Have not filled in current fs descriptor yet. // pfsCur->pszDir = (PTCHAR)gmkstr(_tcslen(ppatdscCur->pszDir)*sizeof(TCHAR) + 2*sizeof(TCHAR)); mystrcpy(pfsCur->pszDir, ppatdscCur->pszDir); pfsCur->fIsFat = ppatdscCur->fIsFat; pfsCur->ppatdsc = ppatdscCur; } } // while for running through pattern list return( SUCCESS ); } STATUS DirWalkAndProcess( IN STATUS (* pfctProcessFiles) ( PSCREEN, PULONG, PLARGE_INTEGER, ULONG, ULONG, PFS ), IN STATUS (* pfctProcessDir) (PFS, PULONG), IN PSCREEN pscr, OUT PULONG pcffTotal, OUT PLARGE_INTEGER pcbFileTotal, IN PFS pfsFiles, IN PDRP pdpr, IN BOOLEAN fMustExist, IN BOOLEAN (*pfctPrintErr) (STATUS, PTCHAR) ) /*++ Routine Description: Arguments: Return Value: Return: --*/ { FS fsDirs; FS fsFilesNext; ULONG irgpffDirsCur; TCHAR szDirNew[MAX_PATH + 2]; PTCHAR pszDirName; BOOLEAN fFatDrive; BOOLEAN fRecurse; STATUS rc; STATUS (* pfctProcessFilesForGetFS) ( PSCREEN, ULONG, ULONG, PLARGE_INTEGER, PFS, PFF ); // // Turn the file name pattern into a list of files pointed to // by fsnode. The list of files will be qualified by rgfAttribs // which come from the attribute switch on the command line. // // After getting the file list then sort it and finally print // it out in the specified format. // // Last free the file list since we don't need it. // // Keep the directory since we have to use it to get the // file list. // // Check if there are more directories. // if (CtrlCSeen) { return( FAILURE ); } fFatDrive = pfsFiles->fIsFat; fRecurse = (BOOLEAN)(pdpr->rgfSwitchs & RECURSESWITCH); // // Even though there may be no function to process file list // still fetch to determine of any patterns qualify. In the case // of rmdir we are not to process of there is no pattern match // at the top level. // pfctProcessFilesForGetFS = NULL; if ((pdpr->rgfSwitchs & DELPROCESSEARLY)) { pfctProcessFilesForGetFS = EraseFile; } else if (!(pdpr->rgfSwitchs & (RECURSESWITCH | WIDEFORMATSWITCH | SORTDOWNFORMATSWITCH | SORTSWITCH))) pfctProcessFilesForGetFS = DisplayFile; rc = GetFS(pfsFiles, pdpr->rgfSwitchs, pdpr->rgfAttribs, pdpr->rgfAttribsOnOff, pdpr->dwTimeType, pscr, pfctPrintErr, pfctProcessFilesForGetFS ); if (pfctProcessFilesForGetFS) { *pcffTotal += pfsFiles->cffDisplayed; } if (rc != SUCCESS) { if ((rc != ERROR_FILE_NOT_FOUND) && (rc != ERROR_NO_MORE_FILES)) { // PutStdErr(rc, NOARGS); // pfctPrintErr(rc, NULL); return( rc ); } // // GetFS returns this only if not files were found // at all. // if (rc == ERROR_FILE_NOT_FOUND) { // // If we are not recursing then do not report error and // continue down tree. If not report error back to caller. // If are recursing and files must exist at top level then // removing all printing for cases where we do not // continue down tree. // if ((!fRecurse) || (fMustExist) ) { return( rc ); } } } // // Do not bother to sort if no function to process list // if (pfctProcessFiles) { SortFileList(pfsFiles, pdpr->rgsrtdsc, pdpr->dwTimeType); if ((rc == SUCCESS) || !fRecurse) { CHECKSTATUS(pfctProcessFiles(pscr, pcffTotal, pcbFileTotal, pdpr->rgfSwitchs, pdpr->dwTimeType, pfsFiles)); } } // // Free up buffer holding files since we no longer need these. // Move on to determine if we needed to go to another directory // FreeStr((PTCHAR)(pfsFiles->pff)); pfsFiles->pff = NULL; if (CtrlCSeen) { return( FAILURE ); } if (fRecurse) { fsDirs.pszDir = (PTCHAR)gmkstr(_tcslen(pfsFiles->pszDir)*sizeof(TCHAR) + sizeof(TCHAR)); mystrcpy(fsDirs.pszDir, pfsFiles->pszDir); fsDirs.ppatdsc = (PPATDSC)gmkstr( sizeof( PATDSC ) ); fsDirs.cpatdsc = 1; fsDirs.fIsFat = pfsFiles->fIsFat; fsDirs.pfsNext = NULL; if (fFatDrive) { fsDirs.ppatdsc->pszPattern = TEXT("*.*"); } else { fsDirs.ppatdsc->pszPattern = TEXT("*"); } fsDirs.ppatdsc->pszDir = (PTCHAR)gmkstr(_tcslen(fsDirs.pszDir)*sizeof(TCHAR) + sizeof(TCHAR)); mystrcpy(fsDirs.ppatdsc->pszDir, fsDirs.pszDir); fsDirs.fIsFat = fsDirs.fIsFat; fsDirs.ppatdsc->ppatdscNext = NULL; if (GetFS(&fsDirs, pdpr->rgfSwitchs, FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_DIRECTORY, pdpr->dwTimeType, pscr, NULL, NULL ) != SUCCESS) { // // No directory below // fsDirs.cff = 0; } // // Check for CtrlC again after calling GetFS because // GetFS may have returned failure because CtrlC was hit // inside the GetFS function call // if (CtrlCSeen) { return( FAILURE ); } // // Increment though the list of directories processing each one // for (irgpffDirsCur = 0;irgpffDirsCur < fsDirs.cff;irgpffDirsCur++) { // // Do not recurse on .. since that will send you up the tree // pszDirName = (PTCHAR)((fsDirs.prgpff[irgpffDirsCur])->data.cFileName); if (_tcscmp(TEXT(".."),pszDirName) && _tcscmp(TEXT("."),pszDirName)) { // // If name is too big then blow it all away // if ((_tcslen(pfsFiles->pszDir) + _tcslen(pszDirName) + 2) >= MAX_PATH) { return( ERROR_BUFFER_OVERFLOW ); } mystrcpy(szDirNew, pfsFiles->pszDir); // // check if it needs a trailing path char // if (*lastc(szDirNew) != BSLASH) { DEBUG((ICGRP, DILVL, "\t New Directory `%ws'",szDirNew)); mystrcat(szDirNew, TEXT("\\")); } mystrcat(szDirNew,pszDirName); DEBUG((ICGRP, DILVL, "\t New Directory `%ws'",szDirNew)); fsFilesNext.pszDir = (PTCHAR)gmkstr(_tcslen(szDirNew)*sizeof(TCHAR) + sizeof(TCHAR)); mystrcpy(fsFilesNext.pszDir, szDirNew); fsFilesNext.ppatdsc = pfsFiles->ppatdsc; fsFilesNext.cpatdsc = pfsFiles->cpatdsc; fsFilesNext.fIsFat = pfsFiles->fIsFat; fsFilesNext.pfsNext = NULL; rc = DirWalkAndProcess( pfctProcessFiles, pfctProcessDir, pscr, pcffTotal, pcbFileTotal, &fsFilesNext, pdpr, FALSE, pfctPrintErr); // // BUGBUG Should this error code be paid attention to // and what should it be? // // // BUGBUG merge the GetFs with the code above in a seperate // routine // if (pfctProcessDir) { rc = GetFS(&fsFilesNext, pdpr->rgfSwitchs, pdpr->rgfAttribs, pdpr->rgfAttribsOnOff, pdpr->dwTimeType, pscr, pfctPrintErr, NULL ); if (rc != SUCCESS) { if ((rc != ERROR_FILE_NOT_FOUND) && (rc != ERROR_NO_MORE_FILES)) { // PutStdErr(rc, NOARGS); return( rc ); } } pfctProcessDir(&fsFilesNext, pcffTotal); FreeStr((PTCHAR)(fsFilesNext.pff)); FreeStr((PTCHAR)(fsFilesNext.prgpff)); fsFilesNext.pff = NULL; fsFilesNext.prgpff = NULL; } FreeStr(fsFilesNext.pszDir); } } // // At bottom of directory tree, free buffer holding // list of directories. // FreeStr((PTCHAR)(fsDirs.pszDir)); FreeStr((PTCHAR)(fsDirs.pff)); FreeStr((PTCHAR)(fsDirs.prgpff)); } return(rc); } /*++ Routine Description: Arguments: Return Value: Return: --*/ STATUS GetFS( IN PFS pfsCur, IN ULONG rgfSwitchs, IN ULONG rgfAttribs, IN ULONG rgfAttribsOnOff, IN ULONG dwTimeType, IN PSCREEN pscr, IN BOOLEAN (*pfctPrintPatternErr) (STATUS, PTCHAR), IN STATUS (* pfctProcessFileEarly) ( PSCREEN, ULONG, ULONG, PLARGE_INTEGER, PFS, PFF ) ) { HANDLE hndFirst; PFF pffCur; ULONG cff = 0; ULONG cbfsLim; ULONG cbfsCur = 0; ULONG irgpffCur; ULONG cbT; PPATDSC ppatdscCur; ULONG i, rc; USHORT cbFileName, cbAlternateFileName; BOOLEAN bPrintedErr; TCHAR szSearchPath[MAX_PATH + 2]; UNREFERENCED_PARAMETER( rgfSwitchs ); DosErr = 0; pfsCur->pff = pffCur = (PFF)gmkstr( cbfsLim = CBFILEINC); pfsCur->cff = 0; pfsCur->cffDisplayed = 0; pfsCur->cbFileTotal.QuadPart = 0; bPrintedErr = FALSE; for(i = 1, ppatdscCur = pfsCur->ppatdsc; i <= pfsCur->cpatdsc; i++, ppatdscCur = ppatdscCur->ppatdscNext ) { // // Check immediately if a control-c was hit before // doing file I/O (which may take a long time on a slow link) // if (CtrlCSeen) { return(FAILURE); } if (mystrlen(pfsCur->pszDir) > (MAX_PATH + 2) ) { return( ERROR_BUFFER_OVERFLOW ); } mystrcpy(szSearchPath, pfsCur->pszDir); /* don't append '\' if we have a current dir is D:\ */ if (mystrlen(pfsCur->pszDir) != mystrlen(TEXT("D:\\")) || szSearchPath[1] != COLON) { if ( (mystrlen(szSearchPath) + mystrlen(TEXT("\\") ) ) > (MAX_PATH + 2) ) { return( ERROR_BUFFER_OVERFLOW ); } mystrcat(szSearchPath, TEXT("\\")); } //DbgPrint("GetFS: searching for %s in %s\n",ppatdscCur->pszPattern,szSearchPath); if ( (mystrlen(szSearchPath) + mystrlen(ppatdscCur->pszPattern) ) > (MAX_PATH + 2) ) { return( ERROR_BUFFER_OVERFLOW ); } mystrcat(szSearchPath, ppatdscCur->pszPattern); if (SetSearchPath(pfsCur, ppatdscCur, szSearchPath, MAX_PATH + 2) != SUCCESS) { return( ERROR_BUFFER_OVERFLOW ); } if (pfctProcessFileEarly) { // // Setting tabs to 0 forces single line output // if (pscr) SetTab(pscr, 0); if (!(rgfSwitchs & (BAREFORMATSWITCH|DELPROCESSEARLY))) { // // if it is not the bare format or del calling (no header, no tail) // then display which directory, volume etc. // CHECKSTATUS(DisplayFileListHeader(pscr, rgfSwitchs, pfsCur->pszDir )); } } // // Fetch all files since we may looking for a file with a attribute that // is not set (a non-directory etc. // if (!FindFirstNt(szSearchPath, &(pffCur->data), &hndFirst)) { if (DosErr) { if ((DosErr != ERROR_FILE_NOT_FOUND) && (DosErr != ERROR_NO_MORE_FILES)) { return( DosErr ); } // if (pfctPrintPatternErr) { bPrintedErr = TRUE; pfctPrintPatternErr(DosErr, szSearchPath); } // If doing multiple file list then keep loop till out of // patterns // if (pfsCur->cpatdsc > 1) { // if pfctPrintPatternErr is != NULL, we've been // called by del, so print an error. otherwise, we've // been called by dir. //if (pfctPrintPatternErr) { // PutStdErr(MSG_NOT_FOUND, ONEARG, szSearchPath); //} continue; } else { //if (pfctPrintPatternErr) { // PutStdErr(MSG_NOT_FOUND, ONEARG, szSearchPath); //} return( DosErr ); } } } else { do { // // Check immediately if a control-c was hit before // doing file I/O (which may take a long time on a slow link) // if (CtrlCSeen) { findclose( hndFirst ); return(FAILURE); } // // Before allowing this entry to be put in the list check it // for the proper attribs // // rgfAttribs is a bit mask of attribs we care to look at // rgfAttribsOnOff is the state these selected bits must be in // for them to be selected. // // IMPORTANT: both of these must be in the same bit order // // DEBUG((ICGRP, DILVL, " found %ws", pffCur->data.cFileName)) ; DEBUG((ICGRP, DILVL, " attribs %x", pffCur->data.dwFileAttributes)) ; if (!((pffCur->data.dwFileAttributes & rgfAttribs) == (rgfAttribs & rgfAttribsOnOff) )) { continue; } // // Compute the true size of the ff entry and don't forget the zero // and the DWORD alignment factor. // Note that pffCur->cb is a USHORT to save space. The // assumption is that MAX_PATH is quite a bit less then 32k // // To compute remove the size of the filename field since it is at MAX_PATH. // also take out the size of the alternative name field // then add back in the actual size of the field plus 1 byte termination // cbFileName = (USHORT)_tcslen((pffCur->data).cFileName); cbAlternateFileName = (USHORT)_tcslen((pffCur->data).cAlternateFileName); pffCur->cb = (USHORT)((sizeof(FF) - (MAX_PATH + 14*sizeof(TCHAR))) + cbFileName + sizeof(TCHAR) ); if (cbAlternateFileName) { // // cbAlternateFileName + 1 to account for zero termination // pffCur->cb += cbAlternateFileName + sizeof(TCHAR); pffCur->obAlternate = (USHORT)(cbFileName + sizeof(TCHAR)); memmove(&((pffCur->data).cFileName[cbFileName + sizeof(TCHAR)]), &((pffCur->data).cAlternateFileName), (cbAlternateFileName + 1) * sizeof(TCHAR)); } else { pffCur->obAlternate = 0; } // // Adjust count to align on DWORD boundaries for mips and risc // machines // pffCur->cb = (USHORT)(((pffCur->cb + sizeof(DWORD)) / sizeof(DWORD)) * sizeof(DWORD)); // // Here we print out the filenames early if a 'dir' // was executed and we don't need to sort them or print // them out in any wide format. // if (pfctProcessFileEarly) { rc = (pfctProcessFileEarly)(pscr, rgfSwitchs, dwTimeType, &pfsCur->cbFileTotal, pfsCur, pffCur ); if (rc == (FAILURE+1)) { findclose( hndFirst ); return FAILURE; } else if (rc == FAILURE) bPrintedErr = TRUE; } if (!pfctProcessFileEarly || (pffCur->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { cff++; // // Update the accounting information for file buffer info. // cbfsCur += pffCur->cb; (char*)(pffCur) += pffCur->cb; // // make sure we can handle the next entry. // if ((sizeof( FF ) + sizeof(DWORD) + cbfsCur) > cbfsLim) { // cbT = cbfsCur + CBFILEINC; // // BUGBUG change this inline to a #define later. // I do not want to make a change to the .h file this // close to beta. // the 64k value is here to avoid heavy heap // fragmentation // cbT = cbfsCur + (64 * 1024); DEBUG((ICGRP, DILVL, "\t size of new pff %d", cbT )); if ((pffCur = pfsCur->pff = (PFF)resize(pfsCur->pff, cbT)) == NULL) { DEBUG((ICGRP, DILVL, "\t Could not resize pff" )); return( MSG_NO_MEMORY ); } DEBUG((ICGRP, DILVL, "\t resized pffCur new value %lx", (ULONG)(pffCur))) ; // // recompute the currency of the ff pointer // pffCur = (PFF)((char*)(pfsCur->pff) + cbfsCur); // // reset the buffer size // cbfsLim = cbT; } } } while (FindNextNt(&(pffCur->data), hndFirst)); findclose( hndFirst ); // // BUGBUG may need to check for error on Find. here // } } // FOR // // DosErr is set in the FindNext code. // Check if we term. loop for something other then end of // file list. // if ((DosErr) && (DosErr != ERROR_NO_MORE_FILES)) { // // If not doing multiple file list then error // If multiple have failed but still have files from previous pattern // if (pfsCur->cpatdsc <= 1) { return( DosErr ); } } // // if no files then do not create pointers // if (cff || pfsCur->cffDisplayed) { if (!pfctProcessFileEarly) { pfsCur->prgpff = (PPFF)gmkstr( sizeof(PFF) * (cff)); pfsCur->cff = cff; pffCur = pfsCur->pff; for (irgpffCur = 0; irgpffCur < cff; irgpffCur++ ) { pfsCur->prgpff[irgpffCur] = pffCur; (char*)(pffCur) += pffCur->cb; } } return( SUCCESS); } if (!bPrintedErr) { DosErr = ERROR_FILE_NOT_FOUND; if (pfctPrintPatternErr) { pfctPrintPatternErr(DosErr, szSearchPath); } } return( DosErr ); } STATUS SetSearchPath ( IN PFS pfsCur, IN PPATDSC ppatdscCur, IN PTCHAR pszSearchPath, IN ULONG cSearchPath ) { if ((mystrlen(pfsCur->pszDir) + mystrlen(TEXT("\\")) + mystrlen(ppatdscCur->pszPattern) + 1) > cSearchPath) { return(ERROR_BUFFER_OVERFLOW); } mystrcpy(pszSearchPath, pfsCur->pszDir); /* don't append '\' if we have a current dir is D:\ */ if (mystrlen(pfsCur->pszDir) != mystrlen(TEXT("D:\\")) || pszSearchPath[1] != COLON) { mystrcat(pszSearchPath, TEXT("\\")); } mystrcat(pszSearchPath, ppatdscCur->pszPattern); return( SUCCESS ); }