// Larry Bush April 26, 2002 // // email : bushl2@rpi.edu // Lawrence_Bush@dps.state.ny.us // // Student ID : 660 220 742 // // File Name : traverse.cpp // Final Proj. : Grep for Windows // Class : Operating Systems // Instructor : Susan Bonner // Language : C++ // // // traverse.cpp: implementation of the traverse class. // ////////////////////////////////////////////////////////////////////// // Description of Program Files // // File Purpose // ---- ------- // // // // traverse.cpp Implementation of traverse member functions. // // #include #include #include "traverse.h" #include "buffer.h" //****************************************************************************************** //****************************************************************************************** //****************************************************************************************** //************* ************** //************* Travers Object ************** //************* Travers Object ************** //************* Travers Object ************** //************* ************** //****************************************************************************************** //****************************************************************************************** //****************************************************************************************** // Implementation of traverse member functions. //******************************************************* //******************************************************* //******** ********* //******** Travers Object Constructor ********* //******** ********* //******************************************************* //******************************************************* // Set the member variables. // These are essentially the initial arguments passed in by the user. traverse::traverse(char * Passed_In_currentDirectoryPath, char * Passed_In_word_to_find, char * Passed_In_replacement_word) { // Set object pointers. mv_currentDirectoryPath = Passed_In_currentDirectoryPath; mv_word_to_find = Passed_In_word_to_find; mv_replacement_word = Passed_In_replacement_word; stop = false; // should thread stop? 1=yes } //******************************************************* //******************************************************* //******** ********* //******** Travers Object Destructor ********* //******** ********* //******************************************************* //******************************************************* // There is nothing to destroy, because all of my handles // closed as soon as they are no longer needed, and all of // my objects are declared as objects, not pointers, and // they therefore distappear automatically when they go // out of scope. traverse::~traverse() { } //******************************************************* //******************************************************* //******** ********* //******** Thread Function ********* //******** ********* //******************************************************* //******************************************************* // This function sets up and calls the directory search helper. // It needs to set up new variables because dirSearchHelper only // passes the variables one recursion. They are then changed to // new variables because we want to pass back the original ones // unadulterated. DWORD WINAPI traverse::ThreadFunc (LPVOID param) { // This is a static thread function. // Because of this, we have to explicitly declare the "this", otherwise it will get lost. // Especailly when the function is pushed on and off the stack. traverse* pto = (traverse*)param; // prime the recursive functions with the member variables. // instantiate variables char word_to_find[256]; char replacement_word[256]; char currentDirectoryPath[32768]; // max pathname in windows+1 // copy correct word to them. strcpy(word_to_find, pto->mv_word_to_find); strcpy(replacement_word, pto->mv_replacement_word); strcpy(currentDirectoryPath, pto->mv_currentDirectoryPath); pto->DirSearchHelper( currentDirectoryPath, word_to_find, replacement_word ); // Check the stop flag in to see if we should continue. if (pto->stop != 1) { // This alerts the user that we are done. // It is only printed if the user did NOT preempt the search. // If the user did not preempt the search, the original cin<< // call is still waiting for the user. // This is necessary because if this is run from the dos prompt, // the user will want the output to stay on the screen so he can read it. cout<<"\nSearch Completed. Quit? y/n > "; } return 0; } //******************************************************* //******************************************************* //******** ********* //******** multithreaded_traverse ********* //******** ********* //******************************************************* //******************************************************* // This function is the thread helper (launcher) function. // It just calls the thread and waits for it. void traverse::multithreaded_traverse() { // thread stuff HANDLE hThread; DWORD threadId; // int i; // end thread stuff // thread stuff //******** call thread ************** hThread = (HANDLE)_beginthreadex(NULL,0, (PBEGINTHREADEX_THREADFUNC) ThreadFunc, (LPVOID) this, 0, (PBEGINTHREADEX_THREADID)&threadId ); //*************************************************** //********** error check thread ************* //*************************************************** if (hThread == INVALID_HANDLE_VALUE) { // If the tread cannot be created, cerr << "\nError : Could not start search thread.\n"; // close thread handle if (hThread != NULL) { if( (TRUE != CloseHandle(hThread)) ) {//Returns True on success //try again if( (TRUE != CloseHandle(hThread)) ) {//Returns True on sucess cerr<<"\nError: Cannot Close Handle - continuing anyway."<<"\n"; // Continue anyway. } } } } //**************************************************** //********** test if thread is ok ************* //********** and wait ************* //**************************************************** if (hThread) { // If the thread is ok, char user_input_character_array[50]; // This section allows the user to preempt the search. cout<<"Type y and return to stop search. > "; while(1) { cin>>user_input_character_array;// user input. // If 'y' then break from loop. if(strcmp(user_input_character_array, "y") == 0) { cout<< "Quitting...\n"; break; } if(strcmp(user_input_character_array, "n") == 0) { //exit(0); if (stop != 1) { cout<<"\nOK, I'll wait. Quit? y/n > "; } } // If not y, then re-loop. } stop = 1; // set stop flag DWORD errorWord; // Return Value holder. errorWord = WaitForSingleObject (hThread, INFINITE);// get mutex // Error check wait syscall if (errorWord == WAIT_FAILED) { cerr<<"ERROR: Wait Failed, waiting on invalid object.\n"; //exit(-1);// Probably should exit. } // close thread handle if (hThread != NULL) { if( (TRUE != CloseHandle(hThread)) ) {//Returns True on sucess //try again if( (TRUE != CloseHandle(hThread)) ) {//Returns True on sucess cerr<<"\nError: Cannot Close Handle - continuing anyway."<<"\n"; // Continue anyway. } } } } // end thread stuff } //******************************************************* //******************************************************* //******** ********* //******** Process the DirectoryOrFile ********* //******** ********* //******************************************************* //******************************************************* // Decide what to do. // Is it a dir - then recursively traverse it. // Is it a file - then search it. void traverse::DirectoryOrFile( WIN32_FIND_DATA findData, char * currentDirectoryPath, char * word_to_find, char * replacement_word ) { DWORD isDirectory; // Use flag to determine if we have a hold of a directory. isDirectory = findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; if(isDirectory) { // cout<<"\nIs a Dir. "; // Skip (don't traverse) this directory. if(strcmp(findData.cFileName,".")==0 ){ // cout<<"\nThis directory. \n"; } else { // Skip (don't traverse) parent directory. if(strcmp(findData.cFileName,"..")==0 ) { // cout<<"\nParent directory. \n"; } else { // Recursively traverst the directory: // cout<<" call self "; // Re-copy the variables so that we pass BACK the unadulterated path. char newcurrentDirectoryPath[2048];// this should be a new object so we don't pass it back by reference. strcpy(newcurrentDirectoryPath,currentDirectoryPath); strcat(newcurrentDirectoryPath,"\\" ); // Newly added (this is suposed to compensate for if the user does not put in a \ after his last dir. strcat(newcurrentDirectoryPath,findData.cFileName); // add sub-directory strcat(newcurrentDirectoryPath,"\\"); //green DirSearchHelper(newcurrentDirectoryPath,word_to_find, replacement_word); // pass forward the new directory path } } } else { // If file then make new copy of path plus the file name and call the buffer search. // --------------------------------------------------------------------------------- //Debug: cout<<"\nMatch found in file: " << findData.cFileName << "\nFile Path: " << currentDirectoryPath << "\n"; char fullFilePathBuffer[2028]; // New copy of char path // GetFullPathName(findData.cFileName, 256,fullFilePathBuffer); strcpy(fullFilePathBuffer, currentDirectoryPath); strcat(fullFilePathBuffer,"\\"); strcat(fullFilePathBuffer, findData.cFileName); // cout< buffy(fullFilePathBuffer, findData); // instantiate Buffer object "buffy" if(strcmp(replacement_word, "no_repacement") == 0) { // Then no replacement. (just search) buffy.grep_search_only(word_to_find); // run command execution loop } else { buffy.grep_with_replacement(word_to_find,replacement_word);} // Else with replacement. //delete buffy - note buffy gets deleted when she goes out of scope between files } } //******************************************************* //******************************************************* //******** ********* //******** DirSearchHelper (travers and find) ********* //******** ********* //******************************************************* //******************************************************* // This function searches one directory. // It calls DirectoryOrFile function for each item it finds in the directory. // That's it. This fn is recursively called again by DirectoryOrFile IF it finds a sub-directory. int traverse::DirSearchHelper(char * currentDirectoryPath, char * word_to_find, char * replacement_word) { // debug // cout <<"currentDirectoryPath \n "<< currentDirectoryPath<<"\n\n"; // Declarations WIN32_FIND_DATA findData; // This structure describes a file found by the FindFirstFile or FindNextFile function. char pathsearchname[255]; // Structure to store the file search path HANDLE searchhandle; // handle for use with FindFirstFile and FindNextFile BOOL retval; // FindNextFile regurn value. // search for directories // cout <<"search for directories \n\n"; strcpy(pathsearchname,currentDirectoryPath); strcat(pathsearchname,"*.*"); // Find the first file/dir in this dir searchhandle = FindFirstFile(pathsearchname,&findData); if ( (searchhandle == INVALID_HANDLE_VALUE) ) { if(GetLastError() != ERROR_NO_MORE_FILES) { // This shouldn't happen here anyway, but just a double check. // Tell the user that there is a problem. // i.e. Hey! Can't you type in a string of 50 inane characters without making a mistake? cerr<<"\n\n** Error, no such directory. **\n"; // FindFirstFile never opened the handle, so we don't need to close it. return 0; // Return from here. } } // If the searchhandle is good, // and if stop is not set, then ... if ( (searchhandle != INVALID_HANDLE_VALUE) && (!stop) ) { // Call the function to determine if this is a directory or file and take appropriate action. DirectoryOrFile( findData, currentDirectoryPath, word_to_find, replacement_word ); // prime the while loop retval = FindNextFile(searchhandle,&findData); // Loop through this directory. while ( (retval == TRUE) && (!stop) ) { // debug: cout<<"here 2"; // Call the function to determine if this is a directory or file and take appropriate action. DirectoryOrFile( findData, currentDirectoryPath, word_to_find, replacement_word ); // Get next file in this dir retval = FindNextFile(searchhandle,&findData); } //til no more sub-directories of files in this directory // FindNextFile Return Values - Nonzero indicates success. Zero indicates failure. // To get extended error information, call GetLastError. If no matching files can be found, // the GetLastError function returns ERROR_NO_MORE_FILES. if ( (searchhandle == INVALID_HANDLE_VALUE) ) { // If we preempt, the handle will be valid. if(GetLastError() != ERROR_NO_MORE_FILES) { // This shouldn't happen here anyway, but just a double check. // Tell the user that there is a problem. cerr<<"\n\n** Error, FindNextFile Failed. **\n"; return 0; // Return from here. } } } else { //( searchhandle == INVALID_HANDLE_VALUE ) // Stopping. Either the search handle was invalid (likely due to a mistyped path) // or the stop flag was set by the user. } // Close search handle: if (searchhandle != NULL) { if( ( ! FindClose(searchhandle)) ) {//Returns True on sucess //try again if( (! FindClose(searchhandle)) ) {//Returns True on sucess cerr<<"\nError: Cannot Close Handle - continuing anyway."<<"\n"; // Continue anyway. } } // debug: else {cout<<"closed searchhandls\n";} } return 0; }