|
/** |
|
* Helper utility for php from cygwin |
|
* for using with phpStorm |
|
* |
|
* PHPStorm expects an file called php.exe and phpStrom will provide windows pathnames |
|
* thus this program will convert windows pathnames to unix paths and pass them to |
|
* php.exe |
|
* |
|
* compile with |
|
* |
|
* i686-mingw-gcc --std=c99 -Wall php-winpath-helper.c -o /bin/winpath/php.exe -DPHP_EXECUTABLE=[windows path to cygwin php.exe] |
|
**/ |
|
|
|
#include <stdio.h> |
|
#include <windows.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <ctype.h> |
|
#include <stdbool.h> |
|
#include <malloc.h> |
|
#include <process.h> |
|
#include <assert.h> |
|
#include <errno.h> |
|
|
|
#define PATH_SEPARATOR ";" |
|
#define BUFFER_SIZE 1024*1000 |
|
#define CYGWIN_PREFIX "/cygdrive/" |
|
#define MAX_CMD 10000 |
|
|
|
/** |
|
* Checks if parameter looks like an (absolute) path |
|
**/ |
|
bool |
|
is_path(const char* testString) { |
|
if(strlen(testString) < 2) |
|
return false; |
|
|
|
if(!isalpha((int)testString[0])) |
|
return false; |
|
|
|
if(testString[1] != ':') |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
|
|
bool is_cygwin_path(const char* str) { |
|
return strncmp(str, CYGWIN_PREFIX, strlen(CYGWIN_PREFIX)) == 0; |
|
} |
|
|
|
|
|
/** |
|
* Convert absolute windows path name to cygwin path |
|
**/ |
|
void |
|
convert_path(char* path, char *newPath) { |
|
assert(is_path(path)); |
|
strcpy(newPath, CYGWIN_PREFIX); |
|
|
|
char drive[2]; |
|
drive[0] = path[0]; |
|
drive[1] = '\0'; |
|
strcat(newPath, drive); |
|
strcat(newPath, path+2); |
|
|
|
for(int i=0; i<strlen(newPath); i++) |
|
if(newPath[i] == '\\') |
|
newPath[i] = '/'; |
|
} |
|
|
|
|
|
/** |
|
* Convert absolute cygwin path name to windows path |
|
**/ |
|
void |
|
convert_path_to_win(char* path, char *newPath) { |
|
assert(is_cygwin_path(path)); |
|
assert(strlen(path)>strlen(CYGWIN_PREFIX)+2); |
|
strcpy(newPath+1, path+strlen(CYGWIN_PREFIX)); |
|
newPath[0]=newPath[1]; |
|
newPath[1]=':'; |
|
for(; *newPath; newPath++) |
|
if(*newPath == '/') |
|
*newPath = '\\'; |
|
} |
|
|
|
|
|
/** |
|
* convert list of paths (path1;path2) to cygwin paths |
|
**/ |
|
void |
|
adjust_path_list(char *oldPathList, char *newPathList) { |
|
strcpy(newPathList, ""); |
|
|
|
char *part = strtok(oldPathList, PATH_SEPARATOR); |
|
while(part!=NULL) { |
|
strcat(newPathList, PATH_SEPARATOR); |
|
if(is_path(part)) { |
|
char newPath[MAX_PATH]; |
|
convert_path(part, newPath); |
|
strcat(newPathList, newPath); |
|
} |
|
strtok(NULL, PATH_SEPARATOR); |
|
} |
|
} |
|
|
|
|
|
|
|
unsigned run_piped(char *cmd, char *envStr, char *buffer) { |
|
HANDLE child_input_read; |
|
HANDLE child_input_write; |
|
HANDLE child_output_read; |
|
HANDLE child_output_write; |
|
DWORD bytes_read=0, processExitCode; |
|
PROCESS_INFORMATION process_info; |
|
STARTUPINFO startup_info; |
|
SECURITY_ATTRIBUTES security_attributes; |
|
|
|
// Set the security attributes for the pipe handles created |
|
security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); |
|
security_attributes.bInheritHandle = TRUE; |
|
security_attributes.lpSecurityDescriptor = NULL; |
|
CreatePipe(&child_output_read, &child_output_write, &security_attributes, 0); |
|
CreatePipe(&child_input_read, &child_input_write, &security_attributes, 0); |
|
|
|
// Create the child process |
|
ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION)); |
|
ZeroMemory(&startup_info, sizeof(STARTUPINFO)); |
|
startup_info.cb = sizeof(STARTUPINFO); |
|
startup_info.hStdInput = child_input_read; |
|
startup_info.hStdOutput = child_output_write; |
|
startup_info.hStdError = child_output_write; |
|
startup_info.dwFlags |= STARTF_USESTDHANDLES; |
|
CreateProcess(NULL, cmd , NULL, NULL, TRUE, 0, envStr, NULL, &startup_info, &process_info); |
|
|
|
DWORD numCharsPending, curBytesRead=0, clientRunning=true; |
|
while(clientRunning){ |
|
Sleep(1); |
|
|
|
numCharsPending=0; |
|
// Look if we have data, but dont read it (ReadFile would block) |
|
while(numCharsPending == 0 && clientRunning) { |
|
GetExitCodeProcess(process_info.hProcess, &processExitCode); |
|
if(processExitCode != STILL_ACTIVE) { |
|
clientRunning=false; |
|
break; |
|
} |
|
|
|
PeekNamedPipe(child_output_read, NULL, 0, NULL, &numCharsPending, NULL); |
|
|
|
if (GetLastError() == ERROR_BROKEN_PIPE) |
|
clientRunning=false; |
|
} |
|
|
|
if(bytes_read >= BUFFER_SIZE){ |
|
perror("buffer size exceeded"); |
|
exit(-1); |
|
} |
|
|
|
if(numCharsPending>0){ |
|
ReadFile( child_output_read, buffer, BUFFER_SIZE-bytes_read-1, &curBytesRead, NULL); |
|
buffer+=curBytesRead; |
|
bytes_read+=curBytesRead; |
|
} |
|
|
|
} |
|
buffer[0]=0; |
|
|
|
return processExitCode; |
|
} |
|
|
|
|
|
|
|
char * |
|
convertOutputPaths(char *buffer) { |
|
char *newBuffer=malloc(BUFFER_SIZE); |
|
char *newBufferStart = newBuffer; |
|
char *nextPath=newBuffer; |
|
while(*buffer) { |
|
if(*buffer==' ' || *buffer=='(' || *buffer=='\n') { |
|
*newBuffer=0; |
|
if(is_cygwin_path(nextPath)) { |
|
char new_path[MAX_PATH]; |
|
convert_path_to_win(nextPath, new_path); |
|
strcpy(nextPath,new_path); |
|
newBuffer = nextPath+strlen(new_path); |
|
} |
|
nextPath=newBuffer+1; |
|
} |
|
*newBuffer++=*buffer++; |
|
} |
|
strcpy(buffer, newBufferStart); |
|
return buffer; |
|
} |
|
|
|
|
|
int |
|
main(int argc, char* argv[]) { |
|
|
|
// convert all arguments |
|
for(int i=1; i<argc; i++) |
|
if(is_path(argv[i])) { |
|
char *buf = malloc(MAX_PATH); |
|
convert_path(argv[i], buf); |
|
free(argv[i]); |
|
argv[i] = buf; |
|
} |
|
|
|
|
|
// adjust environment |
|
const char* env_file_vars[] = { |
|
|
|
"IDE_PHPUNIT_CUSTOM_LOADER", |
|
"IDE_PHPUNIT_PHPUNIT_PHAR" |
|
}; |
|
|
|
char environmentBlock[BUFFER_SIZE]; // null terminated block of null terminated strings var=value for for createProcess() |
|
char *envStr = environmentBlock; |
|
for(int i=0; i<(sizeof(env_file_vars)/sizeof(char*)); i++) { |
|
const char *variable_name = env_file_vars[i]; |
|
char *value = getenv(env_file_vars[i]); |
|
if(value != NULL) |
|
if(is_path(value)) { |
|
char *new_path = malloc(MAX_PATH); |
|
convert_path(value, new_path); |
|
//setenv(variable_name, new_path, 1); |
|
sprintf(envStr, "%s=%s", variable_name, new_path); |
|
envStr+=strlen(envStr)+1; |
|
} |
|
} |
|
|
|
char *oldPhpInclude = getenv("IDE_PHPUNIT_PHPUNIT_INCLUDE"); |
|
if(oldPhpInclude != NULL) { |
|
char newPhpInclude[MAX_PATH]; |
|
adjust_path_list(oldPhpInclude, newPhpInclude); |
|
sprintf(envStr, "%s=%s", "IDE_PHPUNIT_PHPUNIT_INCLUDE", newPhpInclude); |
|
envStr+=strlen(envStr)+1; |
|
} |
|
envStr[0]=0; // Terminte block; |
|
|
|
argv[0] = PHP_EXECUTABLE; |
|
|
|
char cmd[MAX_CMD]; |
|
cmd[0]=0; |
|
for(int i=0; i<argc; i++) { |
|
strcat(cmd, "\""); |
|
strcat(cmd, argv[i]); |
|
strcat(cmd, "\""); |
|
if(i<argc-1) strcat(cmd, " "); |
|
} |
|
printf("Command %s\n",cmd); |
|
|
|
char *buffer = malloc(BUFFER_SIZE); |
|
int rc = run_piped(cmd, environmentBlock, buffer); |
|
|
|
printf("%s", convertOutputPaths(buffer)); |
|
//printf("%s", buffer); |
|
return rc; |
|
} |
|
|
I wrote a couple of scripts to solve similar problem around calling cygwin's git.exe version from PHPStorm , it basically rewrite arguments which contain an absolute windows path like c:.... e:.... etc in the mixed mode c:/../.... with cygpath
git-wrapper.bat
We need a batch script to be placed in phpstorm git configuration pane
git-wrapper.sh
This is the real script which rewrite arguments passin through git.exe