#define _CRT_SECURE_NO_DEPRECATE #include "windows.h" #undef SetPort #include <string> #include <stdarg.h> #pragma warning(push) #pragma warning(disable : 4267 4244) #include <clientapi.h> #include <options.h> #include <i18napi.h> #pragma warning( pop ) #include "fs.h" #include "fs_filesys.h" #include "fs_client.h" #include "filewriter.h" const char* cUsageString1 = "Usage: "; const char* cUsageString2 = " [-c client -H host -p port -P pass -u user -h -s] sync [-[I|M|S|A] -a -v sync_options]"; static int sFileSpecCount = 0; static std::string sExtendedErrorMessage; static std::string sErrorMessage; static HANDLE sLogFile = INVALID_HANDLE_VALUE; bool sDoScriptedOutput = false; bool sClientInitialised = false; static ClientApi sClient; const char* cFileType[] = { "None", "Text", "Binary", "GZip", "None", "Directory", "SymLink", "Resource", "Special", "Missing", "CantTell", "Empty", "Unicode", "GunZip", "Utf16" }; EResult Sync(int argc, char **argv, const char* inToolName, SyncStatCallbackFunc inSyncStatCallback, FileStatCallbackFunc inFileStatCallback, BufferCallbackFunc inReadCallback, BufferCallbackFunc inWriteCallback, ClobberCallbackFunc inClobberCallback) { // Get rid of executable name --argc; ++argv; // Construct proper usage string std::string usage_string = std::string(cUsageString1) + inToolName + cUsageString2; // Now see if there are any global options present Error error; Options options; ErrorId usage; usage.code = E_FAILED; usage.fmt = usage_string.c_str(); options.Parse(argc, argv, "c:H:p:P:u:C:hs", OPT_ANY, usage, &error); if (error.Test()) { StrBuf msg; error.Fmt(&msg); sErrorMessage = std::string("Error parsing arguments.\n") + msg.Text() + "\n"; return FS_ERROR_ARGUMENT; } // Check for help request if (options['h'] || options['?']) { sErrorMessage = usage_string; return FS_HELP; } // Pick up the P4CHARSET environment variable, as this is (inconsistently) not done by p4api itself char* char_set_name = NULL; const int char_set_name_env_size = 256; char char_set_name_env[char_set_name_env_size]; if (::GetEnvironmentVariable("P4CHARSET", char_set_name_env, char_set_name_env_size) != 0) char_set_name = char_set_name_env; // Next, see if the config file has a P4CHARSET entry, as this is also ignored by p4api const StrPtr& config_file_path = sClient.GetConfig(); if (config_file_path != "noconfig") { HANDLE handle = ::CreateFile(config_file_path.Text(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (handle != INVALID_HANDLE_VALUE) { const int config_size = 4096; char config_buffer[config_size]; DWORD bytes_read = 0; if (::ReadFile(handle, config_buffer, config_size, &bytes_read, NULL) != 0) { config_buffer[bytes_read] = 0; char* line = ::strtok(config_buffer, "\r\n"); while (line) { char* split = ::strchr(line, '='); if (split) { char* start_var = line; char* end_var = split; while (start_var < end_var && *start_var == ' ') ++start_var; while (start_var < end_var && *(end_var - 1) == ' ') --end_var; *end_var = 0; char* start_val = split + 1; char* end_val = line + strlen(line); while (start_val < end_val && *start_val == ' ') ++start_val; while (start_val < end_val && *(end_val - 1) == ' ') --end_val; *end_val = 0; if (::strcmp(start_var, "P4CHARSET") == 0) char_set_name = start_val; } line = ::strtok(NULL, " \r\n"); } } ::CloseHandle(handle); } } // Get command line overrides of client, host, user, port, password and charset StrPtr* opt_value; if (opt_value = options['c']) sClient.SetClient(opt_value); if (opt_value = options['H']) sClient.SetHost(opt_value); if (opt_value = options['u']) sClient.SetUser(opt_value); if (opt_value = options['p']) sClient.SetPort(opt_value); if (opt_value = options['P']) sClient.SetPassword(opt_value); if (opt_value = options['C']) char_set_name = opt_value->Text(); if (options['s']) sDoScriptedOutput = true; // Apply the charset, if the P4CHARSET config option, environment variable or the -C commandline option was found if (char_set_name) { CharSetApi::CharSet char_set = CharSetApi::Lookup(char_set_name); if (char_set >= 0) sClient.SetTrans(char_set, 0, 0, 0); } // Verify that the "sync" is present if (argc == 0 || ::strcmp(*argv, "sync") != 0) { sErrorMessage = std::string(inToolName) + std::string(" supports only the p4 sync command.\n") + usage_string; return FS_ERROR_NO_SYNC; } // Ok, seen the "sync", now get rid of it. --argc; ++argv; if (!FileWriter::GetSetFileValidDataFunction()) { // We must be on Windows 2000, so set the default filewriter type to MT FileWriter::SetType(FileWriter::MT); } char** clean_args = new char*[argc]; int clean_arg_count = 0; bool type_set = false; for (int i = 0; i < argc; ++i) { if (strlen(argv[i]) == 2 && argv[i][0] == '-') { if (argv[i][1] == 'I' || argv[i][1] == 'M' || argv[i][1] == 'S' || argv[i][1] == 'A') { if (type_set) { sErrorMessage = std::string("Invalid arguments: cannot use multiple filewriter types.\n") + usage_string; return FS_ERROR_ARGUMENT; } type_set = true; } switch (argv[i][1]) { case 'I' : FileWriter::SetType(FileWriter::IMMEDIATE); break; case 'M' : FileWriter::SetType(FileWriter::MT); break; case 'S' : FileWriter::SetType(FileWriter::SYNC); break; case 'A' : FileWriter::SetType(FileWriter::ASYNC); break; case 'a' : FileWriter::UsePreAllocation(false); break; case 'v' : FileWriter::UseSetFileValid(false); break; case 'l' : sLogFile = ::CreateFile("p4fs.log", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); break; default : clean_args[clean_arg_count++] = argv[i]; break; } } else { clean_args[clean_arg_count++] = argv[i]; ++sFileSpecCount; } } FileWriter::SetReadCallback(inReadCallback); FileWriter::SetWriteCallback(inWriteCallback); // Client is now ready to initialise sClient.SetProtocol("tag", ""); sClient.Init(&error); if (error.Test()) { StrBuf msg; error.Fmt(&msg); sErrorMessage = std::string(msg.Text()); return FS_ERROR_INIT; } sClient.SetProg(inToolName); // Client is ready, now run the actual sync command, using any specified options FsClientUser client_user; client_user.SetSyncStatCallback(inSyncStatCallback); client_user.SetFileStatCallback(inFileStatCallback); client_user.SetClobberCallback(inClobberCallback); sClient.SetArgv(clean_arg_count, clean_args); sClientInitialised = true; sClient.Run("sync", &client_user); if (!inClobberCallback) { // If no clobber callback was registered, we're assuming we can close the connection now. sClientInitialised = false; sClient.Final(&error); } delete[] clean_args; if (sLogFile != INVALID_HANDLE_VALUE) ::CloseHandle(sLogFile); sExtendedErrorMessage = client_user.GetErrorMessage(); if (error.Test()) { StrBuf msg; error.Fmt(&msg); sErrorMessage = std::string(msg.Text()); sExtendedErrorMessage += sErrorMessage; return FS_ERROR_IO; } if (!sExtendedErrorMessage.empty()) return FS_ERROR_SYNC; return FS_OK; } EResult ForceSync(const std::vector<std::string>& inFiles, const char* inToolName, SyncStatCallbackFunc inSyncStatCallback, FileStatCallbackFunc inFileStatCallback, BufferCallbackFunc inReadCallback, BufferCallbackFunc inWriteCallback, ClobberCallbackFunc inClobberCallback) { if (!sClientInitialised) { sErrorMessage = "Internal error, ForceSync called without a successful Sync."; return FS_ERROR_RESYNC; } Error error; FsClientUser client_user; client_user.SetSyncStatCallback(inSyncStatCallback); client_user.SetFileStatCallback(inFileStatCallback); client_user.SetClobberCallback(inClobberCallback); sFileSpecCount = (int) inFiles.size(); int argc = (int) inFiles.size() + 1; char** argv = new char* [argc]; argv[0] = "-f"; // This time, the sync is for real for (unsigned int i = 0; i < inFiles.size(); ++i) argv[1 + i] = const_cast<char*>(inFiles[i].c_str()); sClient.SetArgv(argc, argv); sClient.Run("sync", &client_user); delete[] argv; sExtendedErrorMessage = client_user.GetErrorMessage(); if (error.Test()) { StrBuf msg; error.Fmt(&msg); sErrorMessage = std::string(msg.Text()); sExtendedErrorMessage += sErrorMessage; return FS_ERROR_IO; } if (!sExtendedErrorMessage.empty()) return FS_ERROR_SYNC; return FS_OK; } int GetFileSpecCount() { return sFileSpecCount; } const char* GetErrorMessage() { return sErrorMessage.c_str(); } const char* GetExtendedErrorMessage() { return sExtendedErrorMessage.c_str(); } bool DoScriptedOutput() { return sDoScriptedOutput; } void AbortSync() { FileWriter::AbortSync(); } void Finalize() { if (sClientInitialised) { Error error; sClientInitialised = false; sClient.Final(&error); FileWriter::Finalize(); } } int GetNewlinesDecoded() { return FsFileSys::GetNewlinesDecoded(); } void Print(const char* inFormat, ...) { char buffer[1024]; va_list args; va_start(args, inFormat); vsprintf(buffer, inFormat, args); va_end(args); if (sLogFile != INVALID_HANDLE_VALUE) { DWORD written; ::WriteFile(sLogFile, buffer, (DWORD) strlen(buffer), &written, NULL); } printf("%s", buffer); } void Log(const char* inFormat, ...) { char buffer[1024]; va_list args; va_start(args, inFormat); vsprintf(buffer, inFormat, args); va_end(args); if (sLogFile != INVALID_HANDLE_VALUE) { DWORD written; ::WriteFile(sLogFile, buffer, (DWORD) strlen(buffer), &written, NULL); } #ifdef _DEBUG printf("%s", buffer); #endif }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 6451 | Frank Compagner |
- Now fully support unicode & utf-16 files - Improved accuracy of P4fsV progress bar - Added logging to help in remote debugging |
||
#2 | 6420 | Frank Compagner |
A number of improvements: - p4fs now supports the global -s (scripted output) option. - p4fs and P4fsV now support the modtime client option. - P4CHARSET is now correctly handled (though no full Unicode support yet). - Increased the maximum command line length for p4fs to the Windows maximum 32768. - Improved error handling. - Fixed a crash when cancelling a sync using the async or multithreaded writers. - P4fsV progressbar now behaves well when passing more than one filespec - P4fsV will now offer the option to overwrite any locally changed (but not checked out) files when it finds these during a sync (cannot clobber ...). - Made the P4fsV error dialog resizeable. - P4fsV Windows layout fixed so it works properly with all Windows style setings. - Ooh, and prettier icons too. |
||
#1 | 6187 | Frank Compagner | Added p4fs project |