diff --git a/README.md b/README.md new file mode 100644 index 0000000..d82d49e --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# rren + +![](https://img.shields.io/badge/written%20in-C%2B%2B-blue) + +A command-line batch renaming client for Windows, using TR1 regexes. + +Supports ECMA regexes, string find/replace, and propercasing using globbing file masks. Uses TR1 regexes (so it semi-predates C++11). + +Original project page: https://code.google.com/p/rren/ + + +## Download + +- [⬇️ rren.exe](dist-archive/rren.exe) *(24.50 KiB)* +- [⬇️ rren.cpp](dist-archive/rren.cpp) *(5.72 KiB)* diff --git a/dist-archive/rren.cpp b/dist-archive/rren.cpp new file mode 100644 index 0000000..f6c5395 --- /dev/null +++ b/dist-archive/rren.cpp @@ -0,0 +1,238 @@ +// 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; +} + diff --git a/dist-archive/rren.exe b/dist-archive/rren.exe new file mode 100644 index 0000000..9b647db Binary files /dev/null and b/dist-archive/rren.exe differ diff --git a/doc/rren.png b/doc/rren.png new file mode 100644 index 0000000..2861ab1 Binary files /dev/null and b/doc/rren.png differ