// // WILDCARD.CPP // // Source file for ArchiveLib 1.0 // // Copyright (c) Greenleaf Software, Inc. 1994 // All Rights Reserved // // CONTENTS // // ALWildCardExpander::operator new() // ALWildCardExpander::ALWildCardExpander() // ALWildCardExpander::~ALWildCardExpander() // ALWildCardExpander::GetNextWildName() // ALWildCardExpander::GetNextFile() // // DESCRIPTION // // This file contains all the source code for the nifty class // ALWildCardExpander. The wild card expansion code is a state // drive routine, which keeps track of its entire state between // calls. So you can call it once to get a new file name, then // do some processing. When you call it again later, you will // get the next file name in the sequence. // // There is a little difference between the NT version and the DOS // version, because the function calls for get first/get next // are different. A minor difference is created by the fact // that under NT you can't specify attributes in a search, so when // you want to look for subdirectories, you have to search all files // and see if any of your matches turn out to be directories. // // The way the wild card class handles searching through subdirectories // is by keeping a link pointer to a subdirectory search. When it // is time to open up a subdirectory search, we create a new file // expander, and assign its pointer to our link pointer. As long as // the link is active, we keep searching there. When the link runs // out of files to return, we continue searching in our own directory. // // A lot of this code is easier to deal with because we use the // ALName class. That makes it easy to strip file names and // paths apart, and even easier to put them back together again. // // REVISION HISTORY // // May 26, 1994 1.0A : First release // // #include "arclib.h" #pragma hdrstop #include #ifdef __BORLANDC__ #include #endif #include "wildcard.h" // // void * ALWildCardExpander::operator new( size_t size ) // // ARGUMENTS: // // size : The number of bytes needed to create a new ALWildCardExpander // object. // // RETURNS // // A pointer to the newly allocated storage area, or 0 if no storage // was available. // // DESCRIPTION // // When using a DLL, it is easy to get into a dangerous situation when // creating objects whose ctor and dtor are both in the DLL. The problem // arises because when you create an object using new, the memory for // the object will be allocated from the EXE. However, when you destroy // the object using delete, the memory is freed inside the DLL. Since // the DLL doesn't really own that memory, bad things can happen. // // But, you say, won't the space just go back to the Windows heap regardless // of who tries to free it? Maybe, but maybe not. If the DLL is using // a subsegment allocation scheme, it might do some sort of local free // before returning the space to the windows heap. That is the point where // you could conceivably cook your heap. // // By providing our own version of operator new inside this class, we // ensure that all memory allocation for the class will be done from // inside the DLL, not the EXE calling the DLL. // // REVISION HISTORY // // May 26, 1994 1.0A : First release // #if defined( AL_BUILDING_DLL ) void AL_DLL_FAR * AL_PROTO ALWildCardExpander::operator new( size_t size ) { return ::new char[ size ]; } #endif // // ALWildCardExpander::ALWildCardExpander( const char *file_list, // int traverse_flag = 0, // ALCase name_case = AL_LOWER ) // // ARGUMENTS: // // file_list : A list of wild card file specifications, separated // by commas, semicolons, or spaces, maybe looking // something like this: "*.CPP, BOB.DAT, *.*" // // traverse_flag : A flag that indicates whether you want to traverse // all subdirectories under the current path. // // name_case : An indicator of whether you want all the returned // file names forced to a certain case. // // RETURNS // // No returns. // // DESCRIPTION // // The constructor for the expander has to set up a bunch of data members // that will all be used during the expansion process. The mCase // member is easy to understand. All of the objname objects that // we create are going to be force to a certain case by this // using this data member. miTraverseFlag is just our copy of the // input parameter. And the mState variable keeps track of what we // are doing in between function calls. We set it to GET_NEXT_WILD_NAME, // which means we will be doing that the first time we get called. // // mInputLine is where we keep a copy of the list of wild card file // specifications passed by the calling program. Each time we take // a new file name out of mInputLine, we remove it from the ALName // object, making mInputLine just a little shorter. // // The mResultFileName member is the storage area where we keep a copy // of the file name created by the expander. This is our local copy, // when it gets returned to the calling program they need to make // their own copy of it and leave ours alone. // // Every time we get asked to get a new file, the very first thing // we do is check to see if the mpNextExpander member is pointing // to a new expander object. If it is, we ask him to provide // the next file name, instead of giving it ourselves. When he // doesn't have any file names left to give, we destroy him and // set that pointer back to 0. Here in the constructor, the smart // thing to do is set him to 0 for starters. // // The final data member differs between NT and DOS. The structure // NT uses to expand directories is store in mFindFileHandle. The // DOS version is stored in mpFfblk. Both of these are presently // in an invalid state, but will get initialized when the user // calls the member function. // // REVISION HISTORY // // May 26, 1994 1.0A : First release // AL_PROTO ALWildCardExpander::ALWildCardExpander( const char AL_DLL_FAR *file_list, int traverse_flag /* = 0 */, ALCase name_case /* = AL_LOWER */ ) : mCase( name_case ), mResultFileName( "", name_case ) { mInputLine = file_list; mState = GET_NEXT_WILD_NAME; mpNextExpander = 0; miTraverseFlag = traverse_flag; #if defined( AL_WIN32S ) mFindFileHandle = INVALID_HANDLE_VALUE; #else mpFfblk = new find_t; #endif } // // ALWildCardExpander::~ALWildCardExpander() // // ARGUMENTS: // // None, destructors don't get any. // // RETURNS // // None, destructor. // // DESCRIPTION // // There are a couple of big deals we need to worry about in the // destructor an ALWildCardExpander. First, we have to worry about // any additional handlers we created to search subdirectories. If // this destructor is being called before our search is done, we // may have some of those expander objects just hanging around out // there. We take care of the by checking the mpNextExpander member. // If it isn't set to 0, we delete the dynamically created expander. // // Under NT we also have to worry about our mpFindFileHandle. Under // NT, the file expansion algorithm isn't just a get first/get next // deal. Instead, it is get first/get next/terminate. The termination // is done using the FindClose() call. If we still had a search in progress // we call that function. // // Under DOS, we just have to delete the dynamically created // mpFfblk structure. I wanted to make that a data member of this // class, instead of a pointer, but one of our compilers wasn't happy // about putting this C struct in a class, it complained about something. // So, to expedite, we made it a pointer. // // // REVISION HISTORY // // May 26, 1994 1.0A : First release // AL_PROTO ALWildCardExpander::~ALWildCardExpander() { if ( mpNextExpander ) delete mpNextExpander; #if defined( AL_WIN32S ) if ( mFindFileHandle != INVALID_HANDLE_VALUE ) FindClose( mFindFileHandle ); #else if ( mpFfblk ) delete mpFfblk; #endif } // PROTECTED MEMBER FUNCTION // // int ALWildCardExpander::GetNextWildName() // // ARGUMENTS: // // None. // // RETURNS // // 1 if it got a new file spec, 0 if it didn't. // // DESCRIPTION // // This function is called internally to get the next file spec out of // the input line. This is simply a matter of parsing past all the // delimiter characters. The resulting file spec is stored in // data member mFullWildName. That member will be the one used to // kick off the next wild card search. // // REVISION HISTORY // // May 26, 1994 1.0A : First release // int AL_PROTO ALWildCardExpander::GetNextWildName() { char wild_spec[ _MAX_PATH ]; int i = 0; char *p = mInputLine; for ( ; ; p++ ) { int c = *p; if ( c != ' ' && c != ',' && c != '\t' ) break; } for ( ; ; p++ ) { int c = *p; if ( c == ' ' || c == ',' || c == '\t' || c == '\0' ) break; wild_spec[ i++ ] = (char) c; if ( i >= ( _MAX_PATH - 2 ) ) return 0; } wild_spec[ i++ ] = '\0'; if ( i <= 1 ) return 0; mFullWildName = wild_spec; mInputLine = p; return 1; } // // char * ALWildCardExpander::GetNextFile() // // ARGUMENTS: // // None. // // RETURNS // // In the event that this routine is able to come up with a new // file name, it returns a character pointer to the name, which // is kept in member variable mResultFileName. If no new file // name could be cooked up, we return a 0, which means you are // done. // // DESCRIPTION // // There are two wild card expander routines. One for NT, and one // for DOS. They are both very similar in structure, but they weren't // quite close enough to combine into a single routine. However, the // both share a common structure, which is being described here. // // The ALWildCardExpander has what amounts to six different internal // states. They are: // // Searching subdirectories, using another object // // Extracting the next wild spec from the input line // // Expanding the wild card to get the first matching file // // Expanding the wild card to get the next matching file // // Looking for the first subdirectory // // Looking for the next subdirectory // // For the most part, we keep track of the state using the mState // variable. However, we keep track of whether we are searching // subdirectories by examining the pointer to the next expander. If // it is non-null, it means we are in that state. // // REVISION HISTORY // // May 26, 1994 1.0A : First release // #if defined( AL_WIN32S ) // // This is the NT version. It has to use FindFirstFile() and // FindNextFile() to get file names. Note that this is implemented // as a giant loop. This means we may go through several states // inside this routine until we finally come up with a filename. // char AL_DLL_FAR * AL_PROTO ALWildCardExpander::GetNextFile() { for ( ; ; ) { // // If the pointer to the next expander is set, it means we are working // on a subdirectory, so I have to let him do the work. If the subdirectory // search fails, I continue right back where I was when interrupted. // if ( mpNextExpander ) { char *p = mpNextExpander->GetNextFile(); if ( p ) return p; // Return the name if he found one delete mpNextExpander; // If not, he is toast mpNextExpander = 0; } switch ( mState ) { // // This is where I get the next wild spec from the input line. If // there aren't any more, I return 0, because we are done. If there // is one, I set up the member variable that will be used in the // rest of the search, and set up the state so that next I will get // get the first file name. // case GET_NEXT_WILD_NAME : if ( GetNextWildName() == 0 ) return 0; mWildPathOnly = mFullWildName; mWildPathOnly.StripFileName(); mWildNameOnly = mFullWildName; mWildNameOnly.StripPath(); mState = GET_FIRST_FILE_NAME; break; // // Once I have a wild spec, time to start getting file names. // FindFirstFile() does it for me. if there aren't any files, I // either go on to search directories, or go the the next wild // name in the input line. If there is a name, I return it to // the calling procedure. // case GET_FIRST_FILE_NAME : mFindFileHandle = FindFirstFile( mFullWildName, &mFindFileData ); if ( mFindFileHandle == INVALID_HANDLE_VALUE ) { if ( miTraverseFlag ) mState = GET_FIRST_DIRECTORY; else mState = GET_NEXT_WILD_NAME; break; } mState = GET_NEXT_FILE_NAME; if ( mFindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) break; mResultFileName = ALName( mWildPathOnly + mFindFileData.cFileName ); return mResultFileName; // // Time to get another file name with FindNextFile(). If there aren't // any more, I clean up, and either get the next name for the input // line or start searching subdirectories. If there was a name, I return // it to the calling procedure. // case GET_NEXT_FILE_NAME : if ( !FindNextFile( mFindFileHandle, &mFindFileData ) ) { FindClose( mFindFileHandle ); mFindFileHandle = INVALID_HANDLE_VALUE; if ( miTraverseFlag ) mState = GET_FIRST_DIRECTORY; else mState = GET_NEXT_WILD_NAME; break; } if ( mFindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) break; mResultFileName = ALName( mWildPathOnly + mFindFileData.cFileName ); return mResultFileName; // // The procedure to get the first subdirectory is an awful lot like that // we use to get the first file. If we find a valid subdirectory, we create // a new expander to deal with its wildcards. If we find a file, but // it isn't a subdirectory, we keep on searching. If we don't find // anything, we are going to go back and check out the next file spec // from the input line. // case GET_FIRST_DIRECTORY : mFindFileHandle = FindFirstFile( mWildPathOnly + "*.*", &mFindFileData ); if ( mFindFileHandle == INVALID_HANDLE_VALUE ) { mState = GET_NEXT_WILD_NAME; break; } mState = GET_NEXT_DIRECTORY; if ( mFindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { if ( strcmp( mFindFileData.cFileName, ".." ) == 0 ) break; if ( strcmp( mFindFileData.cFileName, "." ) == 0 ) break; mpNextExpander = new ALWildCardExpander( mWildPathOnly + mFindFileData.cFileName + "\\" + (char *) mWildNameOnly, 1, mCase ); } break; // // This works the same as the state where I get the first directory. // The only difference here is that if I run out of file names in the // directory, I have to call FindClose() to clean up after myself. // case GET_NEXT_DIRECTORY : if ( !FindNextFile( mFindFileHandle, &mFindFileData ) ) { FindClose( mFindFileHandle ); mFindFileHandle = INVALID_HANDLE_VALUE; mState = GET_NEXT_WILD_NAME; break; } if ( mFindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { if ( strcmp( mFindFileData.cFileName, ".." ) == 0 ) break; if ( strcmp( mFindFileData.cFileName, "." ) == 0 ) break; mpNextExpander = new ALWildCardExpander( mWildPathOnly + mFindFileData.cFileName + "\\" + (char *) mWildNameOnly, 1 ); } break; default : return 0; } } } #else // // This is the MS-DOS version of the file expander. In structure, // it is almost identical to the NT version. // char AL_DLL_FAR * AL_PROTO ALWildCardExpander::GetNextFile() { // // mpFfblk is the pointer to my structure used by _dos_findfirst() // and _dos_findnext(). If for some reason this is a null pointer, // I need to quit. The only reason this should be null is a memory // allocation failure. // if ( mpFfblk == 0 ) return 0; for ( ; ; ) { // // If the pointer to the next expander is non-zero, it means I am in // the middle of a subdirectory search. If that is the case, I call // the next expander to see if it can come up with a file name. if // it does, we return it. If it doesn't, it means it is done, and // I can delete it and try my luck with the next subdirectory. // if ( mpNextExpander ) { char *p = mpNextExpander->GetNextFile(); if ( p ) return p; delete mpNextExpander; mpNextExpander = 0; } switch ( mState ) { // // This is where I start, and this is where I end up after completely // processing one of the input wild specs. I get the next name from // the input line here. If there aren't any more names, I can return // 0, meaning the whole thing is done. // case GET_NEXT_WILD_NAME : if ( GetNextWildName() == 0 ) return 0; mWildPathOnly = mFullWildName; mWildPathOnly.StripFileName(); mWildNameOnly = mFullWildName; mWildNameOnly.StripPath(); mState = GET_FIRST_FILE_NAME; break; // // Once I have a file name, I start parsing using _dos_findfirst(). // If that doesn't return a name, I have struck out on my first swing. // if that is the case, I either move on to start searching subdirectories, // or go back and look for another name from the input line. On the // other hand, if I get a name, I return it to the caller. // case GET_FIRST_FILE_NAME : if ( _dos_findfirst( mFullWildName, 0, mpFfblk ) ) { if ( miTraverseFlag ) mState = GET_FIRST_DIRECTORY; else mState = GET_NEXT_WILD_NAME; break; } mState = GET_NEXT_FILE_NAME; mResultFileName = ALName( mWildPathOnly + mpFfblk->name ); return mResultFileName; // // This state is identical to GET_FIRST_FILE_NAME, except it has to // use _dos_findnext() instead of _dos_findfirst() // case GET_NEXT_FILE_NAME : if ( _dos_findnext( mpFfblk ) ) { if ( miTraverseFlag ) mState = GET_FIRST_DIRECTORY; else mState = GET_NEXT_WILD_NAME; break; } mResultFileName = mWildPathOnly + mpFfblk->name; return mResultFileName; // // After getting all of the file names that a wildspec expands into, // we can start searching subdirectories, if needed. Unlike with NT, // we can set our search up to look for directories only. that means // we don't have to check the status of the file returned from _dos_findxxxx(). // However, we always do have to check to make sure it isn't one of the // two bogus directory entries, "." or "..". // // If we score here, we create a new ALWildCardExpander, and put him to // work. If we strike out, time to go back and get our next input // file name. // case GET_FIRST_DIRECTORY : if ( _dos_findfirst( mWildPathOnly + "*.*", _A_SUBDIR, mpFfblk ) ) { mState = GET_NEXT_WILD_NAME; break; } mState = GET_NEXT_DIRECTORY; if ( mpFfblk->attrib & _A_SUBDIR ) { if ( strcmp( mpFfblk->name, ".." ) == 0 ) break; if ( strcmp( mpFfblk->name, "." ) == 0 ) break; mpNextExpander = new ALWildCardExpander( mWildPathOnly + mpFfblk->name + "\\" + (char *) mWildNameOnly, 1, mCase ); } break; // // This is just like GET_FIRST_DIRECTORY, except it gets to call // _dos_findnext() instead of _dos_findfirst(). // case GET_NEXT_DIRECTORY : if ( _dos_findnext( mpFfblk ) ) { mState = GET_NEXT_WILD_NAME; break; } if ( mpFfblk->attrib & _A_SUBDIR ) { if ( strcmp( mpFfblk->name, ".." ) == 0 ) break; if ( strcmp( mpFfblk->name, "." ) == 0 ) break; mpNextExpander = new ALWildCardExpander( mWildPathOnly + mpFfblk->name + "\\" + (char *) mWildNameOnly, 1 ); } break; default : return 0; } } #if defined( AL_MICROSOFT ) && ( AL_MICROSOFT < 800 ) return 0; //MSC 7.0 thinks I need this return path. No way to get here! #endif } #endif // #if defined( AL_WIN32S )... #else