// Created by Jaime O. Rios #import "P4Functionality.h" #import "P4XcodeHelper.h" #import #import // 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 = popen(cmd, "r"); if (pipe) { const auto buffLength = 256; char buffer[buffLength]; #if defined(_DEBUG) auto error = 0; auto errNo = 0; #endif while(!feof(pipe)) { auto stringBuf = fgets(buffer, buffLength, pipe); if( stringBuf != nullptr) { result += buffer; } #if defined(_DEBUG) else if (result.length() == 0) { error = ferror(pipe); if(error != 0) { errNo = errno; (void)error; (void)errNo; perror("Error "); } } #endif } pclose(pipe); } 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) { _userHomePathSettings = @".bash_login"; [self updateP4Status]; [self updateP4VCStatus]; _P4CONFIG_Set = NO; _p4ClientName = nil; } return self; } - (void)updateP4Status { _p4Path = [self getPathToCommandLineApp:(const char*)"which p4"]; _p4Installed = _p4Path != nil ? YES : NO; } - (void)updateP4VCStatus { _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; } - (void)checkSettings { [self p4ClientSet]; } /** 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 = [P4XcodeHelper 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) { IDESourceCodeDocument *currentSourceCodeDocument = [P4XcodeHelper currentSourceCodeDocument]; NSString *filePath = [[currentSourceCodeDocument fileURL] path]; 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*)p4Add { NSString* addFileSuccess = nil; if (_p4Path == nil || _p4ClientName == nil) { [self checkSettings]; } if (_p4Path != nil && _p4ClientName != nil) { IDESourceCodeDocument *currentSourceCodeDocument = [P4XcodeHelper currentSourceCodeDocument]; NSString *filePath = [[currentSourceCodeDocument fileURL] path]; if (filePath) { NSString* parentFolder = [filePath stringByDeletingLastPathComponent]; NSString* fileName = [filePath lastPathComponent]; auto workingFolder = [parentFolder cStringUsingEncoding:NSASCIIStringEncoding]; auto p4Command = std::string(" add "); p4Command += [fileName cStringUsingEncoding:NSASCIIStringEncoding]; auto returnVal = [self execP4CommandForProjectAtPath:p4Command workingPath:workingFolder]; addFileSuccess = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; } } return addFileSuccess; } - (NSString*)p4Info { if (_p4Path == nil) { [self checkSettings]; } if (_p4Path == nil) { return nil; } IDESourceCodeDocument *currentSourceCodeDocument = [P4XcodeHelper currentSourceCodeDocument]; if(currentSourceCodeDocument == nil) { return nil; } NSString* p4InfoResult = nil; NSString *filePath = [[currentSourceCodeDocument fileURL] path]; 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) { IDESourceCodeDocument *currentSourceCodeDocument = [P4XcodeHelper currentSourceCodeDocument]; NSString *filePath = [[currentSourceCodeDocument fileURL] path]; 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) { IDESourceCodeDocument *currentSourceCodeDocument = [P4XcodeHelper currentSourceCodeDocument]; NSString *filePath = [[currentSourceCodeDocument fileURL] path]; 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* editRes = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; return editRes; } - (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* addFileRes = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; return addFileRes; } - (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* p4InfoResult = [NSString stringWithCString:returnVal.c_str() encoding:NSASCIIStringEncoding]; return p4InfoResult; } - (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