// Created by Jaime O. Rios #import "P4Functionality.h" #import "XCodeHelper.h" #import <string> #import <algorithm> // http://stackoverflow.com/questions/478898/how-to-execute-a-command-and-get-output-of-command-within-c static std::string exec(const char* cmd) { auto result = std::string(""); auto pipe = std::shared_ptr<FILE>(popen(cmd, "r"), pclose); if (pipe.get()) { const auto buffLength = 256; char buffer[buffLength]; #if defined(_DEBUG) auto error = 0; auto errNo = 0; #endif while(!feof(pipe.get())) { auto stringBuf = fgets(buffer, buffLength, pipe.get()); if( stringBuf != nullptr) { result += buffer; } #if defined(_DEBUG) else if (result.length() == 0) { error = ferror(pipe.get()); if(error != 0) { errNo = errno; (void)error; (void)errNo; perror("Error "); } } #endif } } return result; } static void remove_newline(std::string& str) { auto newline = std::string("\n"); auto found = str.find_last_not_of(newline); if (found!=std::string::npos) { str.erase(found+1); } else { str.clear(); } } @implementation P4Functionality - (instancetype)init { self = [super init]; if (self) { [[NSUserDefaults standardUserDefaults] registerDefaults:@{@"P4PATH":@"/usr/local/bin/p4", @"P4VCPATH":@"/usr/local/bin/p4vc"}]; _userHomePathSettings = @".bash_login"; [self updateP4Status]; [self updateP4VCStatus]; _P4CONFIG_Set = NO; _p4ClientName = nil; } return self; } - (void)updateP4Status { _p4Path = [[NSUserDefaults standardUserDefaults] stringForKey:@"P4PATH"];//[self getPathToCommandLineApp:(const char*)"which p4"]; #if _DEBUG && (0) // added to diagnose path issue if(_p4Path==nil) { // I would like to see what the environment has to say NSString* environmentVars = [self getPathToCommandLineApp:(const char*)"env"]; (void)environmentVars; // this line suppresses the unused variable warning int x = 0; ++x; // for breakpoints } #endif _p4Installed = _p4Path != nil ? YES : NO; } - (void)updateP4VCStatus { _p4vcPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"P4VCPATH"];//[self getPathToCommandLineApp:(const char*)"which p4vc"]; _p4vcInstalled = _p4vcPath != nil ? YES : NO; } - (NSString*)getPathToCommandLineApp:(const char*)string { NSString* commandPath = nil; auto command = std::string(string); auto returnVal = exec(command.c_str()); if (returnVal.length()) { remove_newline(returnVal); commandPath = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; } return commandPath; } /* As you see, this function is just a proxy for the real function */ - (void)checkSettings { const BOOL routineResult = [self p4ClientSet]; (void)routineResult; // used for debug mode } /** Precondition(s): - _p4Path and _userHomePathSettings must be non-nil and valid Postcondition(s): - Returns p4 command return string - Returns empty std::string on error */ - (std::string)execP4CommandForProjectAtPath:(std::string)p4Command workingPath:(const char*)workingPath { if (_p4Path == nil || _userHomePathSettings == nil) { return std::string(""); } auto userSettingsFile = [_userHomePathSettings cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Path = [_p4Path cStringUsingEncoding:NSASCIIStringEncoding]; auto command = std::string(""); command = "source ~/"; command += userSettingsFile; command += "; "; command += "cd \""; command += workingPath; command += "\"; "; command += p4Path; command += p4Command; return exec(command.c_str()); } - (BOOL)p4ClientSet { if (_p4Path == nil) { [self updateP4Status]; } if (_p4vcPath == nil) { [self updateP4VCStatus]; } BOOL commandSuccess = NO; if(_p4Path != nil) { auto projectURL = [XCodeHelper currentProjectURL]; if(projectURL != nil) { auto projectPath = [[projectURL path] cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Command = std::string(" set | grep P4CLIENT"); auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:projectPath]; if (returnVal.length()) { _P4CONFIG_Set = YES; _p4ClientName = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; commandSuccess = YES; } } } if(commandSuccess == NO) { _P4CONFIG_Set = NO; auto noClientName = std::string("No client name defined"); _p4ClientName = [NSString stringWithCString:noClientName.c_str() encoding:NSASCIIStringEncoding]; } return commandSuccess; } - (NSString*)p4EditCurrentFile { NSString* checkoutSuccess = nil; if (_p4Path == nil || _p4ClientName == nil) { [self checkSettings]; } if (_p4Path != nil && _p4ClientName != nil) { NSString *filePath = [XCodeHelper pathToCurrentDocument]; if (filePath) { NSString* fileName = [filePath lastPathComponent]; NSString* parentFolder = [filePath stringByDeletingLastPathComponent]; auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Command = std::string(" edit "); p4Command += [fileName cStringUsingEncoding:NSASCIIStringEncoding]; auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder]; checkoutSuccess = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; } } return checkoutSuccess; } - (NSString*)p4Revert { NSString* revertFileSuccess = nil; if (_p4Path == nil || _p4ClientName == nil) { [self checkSettings]; } if (_p4Path != nil && _p4ClientName != nil) { NSString *filePath = [XCodeHelper pathToCurrentDocument]; if (filePath) { NSString* parentFolder = [filePath stringByDeletingLastPathComponent]; NSString* fileName = [filePath lastPathComponent]; auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Command = std::string(" revert "); p4Command += [fileName cStringUsingEncoding:NSASCIIStringEncoding]; auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder]; revertFileSuccess = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; } } return revertFileSuccess; } - (NSString*)p4Info { if (_p4Path == nil) { [self checkSettings]; } if (_p4Path == nil) { return nil; } NSString* p4InfoResult = nil; NSString *filePath = [XCodeHelper pathToCurrentDocument]; if (filePath) { NSString* parentFolder = [filePath stringByDeletingLastPathComponent]; auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Command = std::string(" info"); auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder]; p4InfoResult = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; } return p4InfoResult; } - (void)p4vcRevGraph { if (_p4vcPath == nil || _p4ClientName == nil) { [self checkSettings]; } if (_p4vcPath != nil && _p4ClientName != nil) { NSString *filePath = [XCodeHelper pathToCurrentDocument]; if (filePath) { std::string command(""); std::string returnVal(""); NSString* parentFolder = [filePath stringByDeletingLastPathComponent]; NSString* fileName = [filePath lastPathComponent]; command = "source ~/"; command += [_userHomePathSettings cStringUsingEncoding:NSASCIIStringEncoding]; command += "; cd "; command += [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; command += "; "; command += [_p4vcPath cStringUsingEncoding:NSASCIIStringEncoding]; command += " revgraph "; command += [fileName cStringUsingEncoding:NSASCIIStringEncoding]; returnVal = exec(command.c_str()); NSLog(@"Return value: %s\n", returnVal.c_str()); } } } - (void)p4vcTimeLapse { if (_p4vcPath == nil || _p4ClientName == nil) { [self checkSettings]; } if (_p4vcPath != nil && _p4ClientName != nil) { NSString *filePath = [XCodeHelper pathToCurrentDocument]; if (filePath) { std::string command(""); std::string returnVal(""); NSString* parentFolder = [filePath stringByDeletingLastPathComponent]; NSString* fileName = [filePath lastPathComponent]; command = "source ~/"; command += [_userHomePathSettings cStringUsingEncoding:NSASCIIStringEncoding]; command += "; cd "; command += [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; command += "; "; command += [_p4vcPath cStringUsingEncoding:NSASCIIStringEncoding]; command += " timelapse "; command += [fileName cStringUsingEncoding:NSASCIIStringEncoding]; returnVal = exec(command.c_str()); NSLog(@"Return value: %s\n", returnVal.c_str()); } } } #pragma mark - - (NSString*)issueP4EditForFile:(NSString*)fileToBeCheckedOut { if(fileToBeCheckedOut == nil) { return @"ERROR: p4 edit operation requires a valid file path to work"; } NSString* fileName = [fileToBeCheckedOut lastPathComponent]; NSString* parentFolder = [fileToBeCheckedOut stringByDeletingLastPathComponent]; auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Command = std::string(" edit "); p4Command += [fileName cStringUsingEncoding:NSASCIIStringEncoding]; auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder]; NSString* result = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; return result; } - (NSString* _Nonnull)issueP4SubmitForFile:(NSString* _Nonnull)fileToBeSubmitted withDescription:(NSString * _Nonnull)submitDescription { NSString* fileName = [fileToBeSubmitted lastPathComponent]; NSString* parentFolder = [fileToBeSubmitted stringByDeletingLastPathComponent]; auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Command = std::string(" submit -d \""); p4Command += [submitDescription cStringUsingEncoding:NSASCIIStringEncoding]; p4Command += std::string("\" "); p4Command += [fileName cStringUsingEncoding:NSASCIIStringEncoding]; auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder]; NSString* result = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; return result; } - (NSString*)issueP4AddForFile:(NSString*)fileToBeAdded { if(fileToBeAdded == nil) { return @"ERROR: p4 add operation requires a valid file path to work"; } NSString* parentFolder = [fileToBeAdded stringByDeletingLastPathComponent]; NSString* fileName = [fileToBeAdded lastPathComponent]; auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Command = std::string(" add "); p4Command += [fileName cStringUsingEncoding:NSASCIIStringEncoding]; auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder]; NSString* result = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; return result; } - (NSString*)issueP4RevertForFile:(NSString*)fileToBeReverted { if(fileToBeReverted == nil) { return @"ERROR: p4 add operation requires a valid file path to work"; } NSString* parentFolder = [fileToBeReverted stringByDeletingLastPathComponent]; NSString* fileName = [fileToBeReverted lastPathComponent]; auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Command = std::string(" revert "); p4Command += [fileName cStringUsingEncoding:NSASCIIStringEncoding]; auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder]; NSString* result = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; return result; } - (NSString*)issueP4InfoForFolder:(NSString*)folderPath { if(folderPath == nil) { return @"ERROR: p4 info operation requires a valid folder path to work"; } auto workingFolder = [folderPath cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Command = std::string(" info"); auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder]; NSString* result = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; return result; } - (NSString*)displayP4VCRevGraphForFileAtPath:(NSString*)filePath { if(filePath == nil) { return nil; } NSString* parentFolder = [filePath stringByDeletingLastPathComponent]; NSString* fileName = [filePath lastPathComponent]; auto command = std::string("source ~/"); command += [_userHomePathSettings cStringUsingEncoding:NSASCIIStringEncoding]; command += "; cd "; command += [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; command += "; "; command += [_p4vcPath cStringUsingEncoding:NSASCIIStringEncoding]; command += " revgraph "; command += [fileName cStringUsingEncoding:NSASCIIStringEncoding]; auto returnVal = exec(command.c_str()); NSString* messageString = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; return messageString; } - (NSString*)displayP4VCTimeLapseForFileAtPath:(NSString*)filePath { if(filePath == nil) { return nil; } NSString* parentFolder = [filePath stringByDeletingLastPathComponent]; NSString* fileName = [filePath lastPathComponent]; auto command = std::string("source ~/"); command += [_userHomePathSettings cStringUsingEncoding:NSASCIIStringEncoding]; command += "; cd "; command += [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; command += "; "; command += [_p4vcPath cStringUsingEncoding:NSASCIIStringEncoding]; command += " timelapse "; command += [fileName cStringUsingEncoding:NSASCIIStringEncoding]; auto returnVal = exec(command.c_str()); NSString* messageString = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; return messageString; } @end
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#5 | 20284 | robc_apple |
Added smarter Menu Maint. , if you have a file checked out it and you a editing that file the menu for edit will not be enalbed and revert and submit will be enabled, if you don't have the file checked out the edit will be enabled but revert and submit won't. |
||
#4 | 20283 | robc_apple | Added p4 Sync and p4 -f Sync. | ||
#3 | 20282 | robc_apple | more space issues when calling p4 | ||
#2 | 20281 | robc_apple | Fixed case when space was in file name. | ||
#1 | 20279 | robc_apple | Inital Add |