// 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 = [NSString stringWithFormat:@"\"%@\"", [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 = [NSString stringWithFormat:@"\"%@\"", [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; } -(NSString *)clientName { NSString *cName = nil; if (_p4Path == nil) { [self checkSettings]; } if (_p4Path == nil) { return nil; } NSString *filePath = [XCodeHelper pathToCurrentDocument]; if (filePath) { NSString* parentFolder = [filePath stringByDeletingLastPathComponent]; auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Command = std::string(" change -o"); auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder]; NSString *p4Result = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; NSArray *seperatedString = [p4Result componentsSeparatedByString:@"\n"]; for (NSString *aString in seperatedString) { if (aString.length) { NSRange rangeOfPound = [aString rangeOfString:@"%#"]; // skip over the lines that start with # if (rangeOfPound.location == 0) { continue; } NSArray *sepLine = [aString componentsSeparatedByString:@":"]; if (sepLine.count == 2) { if ([sepLine[0] isEqualToString:@"Client"]) { cName = [sepLine[1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; break; } } } } } return cName; } -(NSArray *)p4Opened { NSMutableArray *fileList = [NSMutableArray array]; if (_p4Path == nil) { [self checkSettings]; } if (_p4Path == nil) { return nil; } NSString *clientWorkSpace = [self clientName]; NSString* p4Result = nil; NSString *filePath = [XCodeHelper pathToCurrentDocument]; if (filePath) { NSString* parentFolder = [filePath stringByDeletingLastPathComponent]; auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Command = std::string(" fstat -Olhp -Rco -e default //"); p4Command += [clientWorkSpace cStringUsingEncoding:NSASCIIStringEncoding]; p4Command += std::string("/..."); auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder]; p4Result = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; NSArray *seperatedList = [p4Result componentsSeparatedByString:@"\n"]; for (NSString *aLine in seperatedList) { if (aLine.length) { NSRange rangeOfPathsym = [aLine rangeOfString:@"... path "]; if (rangeOfPathsym.location == 0) { NSString *thePath = [aLine substringFromIndex:rangeOfPathsym.length]; [fileList addObject:thePath]; } else { continue; } } } } return fileList; } - (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 = [NSString stringWithFormat:@"\"%@\"", [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 = [NSString stringWithFormat:@"\"%@\"", [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 = [NSString stringWithFormat:@"\"%@\"", [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 = [NSString stringWithFormat:@"\"%@\"", [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 = [NSString stringWithFormat:@"\"%@\"", [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 = [NSString stringWithFormat:@"\"%@\"", [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*)issueP4SyncForFile:(NSString*)fileToBeSync forceSync:(BOOL)forceSync { if(fileToBeSync == nil) { return @"ERROR: p4 add operation requires a valid file path to work"; } NSString* parentFolder = [fileToBeSync stringByDeletingLastPathComponent]; NSString* fileName = [NSString stringWithFormat:@"\"%@\"", [fileToBeSync lastPathComponent]]; auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Command = forceSync?std::string(" sync -f "):std::string(" sync "); 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 = [NSString stringWithFormat:@"\"%@\"", [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 = [NSString stringWithFormat:@"\"%@\"", [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 |