// rren #define RREN_VERSION L"1.1" // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #ifdef _WIN32 #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0500 #endif #include #else #define EXIT_SUCCESS 0 #define EXIT_FAILURE 1 #endif #include #include #include #include #include using namespace std; struct twostr { wstring first; wstring second; }; enum RREN_MODES { RREN_MODE_NONE, RREN_MODE_REGEX, RREN_MODE_REPLACE, RREN_MODE_PCASE }; #define BUFF 255 typedef vector V2STR; V2STR vFiles; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #define PRINT_ARGS wprintf( \ L"rren v" RREN_VERSION L"\n" \ L"Usage: rren mode filemask \n" \ L" -e ECMA Regex rren -e filemask \"find\" \"replace\"\n" \ L" -r Find/Replace rren -r filemask \"find\" \"replace\"\n" \ L" -p Proper case rren -p filemask\n") #define PRINT_INVALID_ARGS \ wprintf(L"Invalid number of arguments.\n\n"); \ PRINT_ARGS; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Applies a find/replace ECMAScript regex over all twostr::first in vFiles bool do_regex(wchar_t* wcFind, wchar_t* wcReplace) { // Initialise variables tr1::wregex rFind; wstring sReplace = wcReplace; try {rFind.assign(wcFind);} catch (...) { wprintf(L"Invalid search regex.\n"); return false; } // Loop over vFiles for (V2STR::iterator vi = vFiles.begin(); vi != vFiles.end(); vi++) { vi->second = tr1::regex_replace(vi->first, rFind, sReplace); } return true; } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Applies a find/replace string search over all twostr::first in vFiles bool do_replace(wchar_t* wcFind, wchar_t* wcReplace) { // Initialise variables if (std::wstring(wcReplace).find(wcFind) != wstring::npos) { wprintf(L"Recursive find/replace.\n"); return false; } size_t pos = wstring::npos; size_t flen = wcslen(wcFind); // Loop over vFiles for (V2STR::iterator vi = vFiles.begin(); vi != vFiles.end(); vi++) { vi->second.assign(vi->first); for(;;) { pos = vi->second.find(wcFind); if (pos == wstring::npos) break; vi->second.replace(pos, flen, wcReplace); } } return true; } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Returns the proper-case version of a wstring wstring proper_case(wstring wsInputString) { wstring wBuff(wsInputString); size_t pos = wstring::npos; // All lowercase for (;;) { if (++pos > wBuff.size()) break; wBuff[pos] = towlower(wBuff[pos]); } // First character becomes uppercase if (wBuff.size() > 0) wBuff[0] = towupper(wBuff[0]); // Everything following a space becomes uppercase pos = 0; for (;;) { pos = wBuff.find(L" ", pos+1); if (pos == wstring::npos) break; if (wBuff.size() >= (pos+1)) { wBuff[pos+1] = towupper(wBuff[pos+1]); } else break; } return wBuff; } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Modifies the case of all twostr::first in vFiles bool do_pcase() { for (V2STR::iterator vi = vFiles.begin(); vi != vFiles.end(); vi++) { vi->second = proper_case(vi->first); } return true; } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Program entry point int wmain(int argc, wchar_t* argv[]) { // Variables WIN32_FIND_DATA wfd; HANDLE hFind = 0; wchar_t wCwd[BUFF] = {0}; wstring sSearch; twostr tsNext; int iMode = RREN_MODE_NONE; // Validate command-line arguments if (argc < 3) { PRINT_ARGS; return EXIT_FAILURE; } else if (_wcsnicmp(argv[1], L"-e", 2)==0) { if (argc != 5) { PRINT_INVALID_ARGS; return EXIT_FAILURE; } else iMode = RREN_MODE_REGEX; } else if (_wcsnicmp(argv[1], L"-r", 2)==0) { if (argc != 5) { PRINT_INVALID_ARGS; return EXIT_FAILURE; } else iMode = RREN_MODE_REPLACE; } else if (_wcsnicmp(argv[1], L"-p", 2)==0) { if (argc != 3) { PRINT_INVALID_ARGS; return EXIT_FAILURE; } else iMode = RREN_MODE_PCASE; } else { wprintf(L"Invalid mode.\n"); PRINT_ARGS; return EXIT_FAILURE; } // Create search mask from working directory GetCurrentDirectory(BUFF-1, wCwd); if (!wcslen(wCwd)) return EXIT_FAILURE; sSearch.assign(wCwd); sSearch.append(L"\\"); sSearch.append(argv[2]); // Get all file matches hFind = FindFirstFileW(sSearch.c_str(), &wfd); if (!hFind) { wprintf(L"No files.\n"); return EXIT_FAILURE; } vFiles.clear(); tsNext.first.clear(); tsNext.second.clear(); do { if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { tsNext.first.assign(wfd.cFileName); vFiles.push_back(tsNext); } } while (FindNextFile(hFind, &wfd)); if (vFiles.empty()) { wprintf(L"No files.\n"); return EXIT_FAILURE; } // Apply transform based on argument switch (iMode) { case RREN_MODE_REGEX: if (!do_regex(argv[3], argv[4])) return EXIT_FAILURE; break; case RREN_MODE_REPLACE: if (!do_replace(argv[3], argv[4])) return EXIT_FAILURE; break; case RREN_MODE_PCASE: if (!do_pcase()) return EXIT_FAILURE; break; default: wprintf(L"Invalid mode.\n"); PRINT_ARGS; return EXIT_FAILURE; } // Display all results for (V2STR::iterator vi = vFiles.begin(); vi != vFiles.end(); vi++) { wprintf(L"%s\n--> %s\n", vi->first.c_str(), vi->second.c_str()); } // Prompt for apply wprintf(L"\nApply changes? (y/n) "); wint_t gwc = getwchar(); if (gwc == 'Y' || gwc == 'y') { wprintf(L"Applying changes... "); int count = vFiles.size(), done = 0; for (V2STR::iterator vi = vFiles.begin(); vi != vFiles.end(); vi++) { if (MoveFile(vi->first.c_str(), vi->second.c_str()) == TRUE) done++; } wprintf(L"done (%d / %d).\n", done, count); } else { wprintf(L"No changes were made.\n"); } // Clean up return EXIT_SUCCESS; }