mirror of
https://github.com/git/git.git
synced 2026-02-13 11:21:10 +00:00
Merge pull request #42 from dscho/msys2-wrapper
git-wrapper: support MSys2
This commit is contained in:
@@ -12,6 +12,9 @@
|
||||
#include <shlwapi.h>
|
||||
#include <shellapi.h>
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
|
||||
static WCHAR msystem_bin[64];
|
||||
|
||||
static void print_error(LPCWSTR prefix, DWORD error_number)
|
||||
{
|
||||
@@ -36,12 +39,18 @@ static void print_error(LPCWSTR prefix, DWORD error_number)
|
||||
|
||||
static void setup_environment(LPWSTR exepath)
|
||||
{
|
||||
int len;
|
||||
WCHAR msystem[64];
|
||||
LPWSTR path2 = NULL;
|
||||
int len;
|
||||
|
||||
/* if not set, set TERM to msys */
|
||||
/* Set MSYSTEM */
|
||||
swprintf(msystem, sizeof(msystem),
|
||||
L"MINGW%d", (int) sizeof(void *) * 8);
|
||||
SetEnvironmentVariable(L"MSYSTEM", msystem);
|
||||
|
||||
/* if not set, set TERM to cygwin */
|
||||
if (!GetEnvironmentVariable(L"TERM", NULL, 0))
|
||||
SetEnvironmentVariable(L"TERM", L"msys");
|
||||
SetEnvironmentVariable(L"TERM", L"cygwin");
|
||||
|
||||
/* if not set, set PLINK_PROTOCOL to ssh */
|
||||
if (!GetEnvironmentVariable(L"PLINK_PROTOCOL", NULL, 0))
|
||||
@@ -79,12 +88,22 @@ static void setup_environment(LPWSTR exepath)
|
||||
len = sizeof(WCHAR) * (len + 2 * MAX_PATH);
|
||||
path2 = (LPWSTR)malloc(len);
|
||||
wcscpy(path2, exepath);
|
||||
PathAppend(path2, L"bin;");
|
||||
/* should do this only if it exists */
|
||||
wcscat(path2, exepath);
|
||||
PathAppend(path2, L"mingw\\bin;");
|
||||
GetEnvironmentVariable(L"PATH", &path2[wcslen(path2)],
|
||||
(len/sizeof(WCHAR))-wcslen(path2));
|
||||
PathAppend(path2, msystem_bin);
|
||||
if (_waccess(path2, 0) != -1) {
|
||||
/* We are in an MSys2-based setup */
|
||||
wcscat(path2, L";");
|
||||
wcscat(path2, exepath);
|
||||
PathAppend(path2, L"usr\\bin;");
|
||||
}
|
||||
else {
|
||||
/* Fall back to MSys1 paths */
|
||||
wcscpy(path2, exepath);
|
||||
PathAppend(path2, L"bin;");
|
||||
wcscat(path2, exepath);
|
||||
PathAppend(path2, L"mingw\\bin;");
|
||||
}
|
||||
GetEnvironmentVariable(L"PATH", path2 + wcslen(path2),
|
||||
(len / sizeof(WCHAR)) - wcslen(path2));
|
||||
SetEnvironmentVariable(L"PATH", path2);
|
||||
free(path2);
|
||||
|
||||
@@ -97,7 +116,7 @@ static void setup_environment(LPWSTR exepath)
|
||||
* untouched.
|
||||
*/
|
||||
static LPWSTR fixup_commandline(LPWSTR exepath, LPWSTR *exep, int *wait,
|
||||
LPWSTR builtin, int builtin_len)
|
||||
LPWSTR prefix_args, int prefix_args_len, int is_git_command)
|
||||
{
|
||||
int wargc = 0, gui = 0;
|
||||
LPWSTR cmd = NULL, cmdline = NULL;
|
||||
@@ -106,7 +125,7 @@ static LPWSTR fixup_commandline(LPWSTR exepath, LPWSTR *exep, int *wait,
|
||||
cmdline = GetCommandLine();
|
||||
wargv = CommandLineToArgvW(cmdline, &wargc);
|
||||
cmd = (LPWSTR)malloc(sizeof(WCHAR) *
|
||||
(wcslen(cmdline) + builtin_len + 1 + MAX_PATH));
|
||||
(wcslen(cmdline) + prefix_args_len + 1 + MAX_PATH));
|
||||
if (wargc > 1 && wcsicmp(L"gui", wargv[1]) == 0) {
|
||||
*wait = 0;
|
||||
if (wargc > 2 && wcsicmp(L"citool", wargv[2]) == 0) {
|
||||
@@ -127,9 +146,14 @@ static LPWSTR fixup_commandline(LPWSTR exepath, LPWSTR *exep, int *wait,
|
||||
*exep = NULL;
|
||||
}
|
||||
}
|
||||
else if (builtin)
|
||||
_swprintf(cmd, L"%s\\%s %.*s",
|
||||
exepath, L"git.exe", builtin_len, builtin);
|
||||
else if (prefix_args) {
|
||||
if (is_git_command)
|
||||
_swprintf(cmd, L"%s\\%s %.*s", exepath, L"git.exe",
|
||||
prefix_args_len, prefix_args);
|
||||
else
|
||||
_swprintf(cmd, L"%.*s", prefix_args_len, prefix_args);
|
||||
|
||||
}
|
||||
else
|
||||
wcscpy(cmd, L"git.exe");
|
||||
|
||||
@@ -149,13 +173,327 @@ static LPWSTR fixup_commandline(LPWSTR exepath, LPWSTR *exep, int *wait,
|
||||
return cmd;
|
||||
}
|
||||
|
||||
#ifdef MAGIC_RESOURCE
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#pragma pack(2)
|
||||
struct resource_directory
|
||||
{
|
||||
int8_t width;
|
||||
int8_t height;
|
||||
int8_t color_count;
|
||||
int8_t reserved;
|
||||
int16_t planes;
|
||||
int16_t bit_count;
|
||||
int32_t bytes_in_resource;
|
||||
int16_t id;
|
||||
};
|
||||
|
||||
struct header
|
||||
{
|
||||
int16_t reserved;
|
||||
int16_t type;
|
||||
int16_t count;
|
||||
};
|
||||
|
||||
struct icon_header
|
||||
{
|
||||
int8_t width;
|
||||
int8_t height;
|
||||
int8_t color_count;
|
||||
int8_t reserved;
|
||||
int16_t planes;
|
||||
int16_t bit_count;
|
||||
int32_t bytes_in_resource;
|
||||
int32_t image_offset;
|
||||
};
|
||||
|
||||
struct icon_image
|
||||
{
|
||||
BITMAPINFOHEADER header;
|
||||
RGBQUAD colors;
|
||||
int8_t xors[1];
|
||||
int8_t ands[1];
|
||||
};
|
||||
|
||||
struct icon
|
||||
{
|
||||
int count;
|
||||
struct header *header;
|
||||
struct resource_directory *items;
|
||||
struct icon_image **images;
|
||||
};
|
||||
|
||||
static int parse_ico_file(LPWSTR ico_path, struct icon *result)
|
||||
{
|
||||
struct header file_header;
|
||||
FILE *file = _wfopen(ico_path, L"rb");
|
||||
int i;
|
||||
|
||||
if (!file) {
|
||||
fwprintf(stderr, L"could not open icon file '%s'", ico_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fread(&file_header, sizeof(struct header), 1, file);
|
||||
result->count = file_header.count;
|
||||
|
||||
result->header = malloc(sizeof(struct header) + result->count
|
||||
* sizeof(struct resource_directory));
|
||||
result->header->reserved = 0;
|
||||
result->header->type = 1;
|
||||
result->header->count = result->count;
|
||||
result->items = (struct resource_directory *)(result->header + 1);
|
||||
struct icon_header *icon_headers = malloc(result->count
|
||||
* sizeof(struct icon_header));
|
||||
fread(icon_headers, result->count * sizeof(struct icon_header),
|
||||
1, file);
|
||||
result->images = malloc(result->count * sizeof(struct icon_image *));
|
||||
|
||||
for (i = 0; i < result->count; i++) {
|
||||
struct icon_image** image = result->images + i;
|
||||
struct icon_header* icon_header = icon_headers + i;
|
||||
struct resource_directory *item = result->items + i;
|
||||
|
||||
*image = malloc(icon_header->bytes_in_resource);
|
||||
fseek(file, icon_header->image_offset, SEEK_SET);
|
||||
fread(*image, icon_header->bytes_in_resource, 1, file);
|
||||
|
||||
memcpy(item, icon_header, sizeof(struct resource_directory));
|
||||
item->id = (int16_t)(i + 1);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wsuffixcmp(LPWSTR text, LPWSTR suffix)
|
||||
{
|
||||
int text_len = wcslen(text), suffix_len = wcslen(suffix);
|
||||
|
||||
if (text_len < suffix_len)
|
||||
return -1;
|
||||
|
||||
return wcscmp(text + (text_len - suffix_len), suffix);
|
||||
}
|
||||
|
||||
static int edit_resources(LPWSTR exe_path,
|
||||
LPWSTR ico_path, LPWSTR *commands, int command_count)
|
||||
{
|
||||
WORD language = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
|
||||
struct icon icon;
|
||||
HANDLE handle;
|
||||
int i;
|
||||
|
||||
if (command_count > 16) {
|
||||
fwprintf(stderr, L"Cannot handle more than 16 commands\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (wsuffixcmp(exe_path, L".exe")) {
|
||||
fwprintf(stderr, L"Not an .exe file: '%s'", exe_path);
|
||||
return -1;
|
||||
}
|
||||
if (_waccess(exe_path, 0) == -1) {
|
||||
fwprintf(stderr, L"File not found: '%s'", exe_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ico_path) {
|
||||
if (wsuffixcmp(ico_path, L".ico")) {
|
||||
fwprintf(stderr, L"Not an .ico file: '%s'", ico_path);
|
||||
return -1;
|
||||
}
|
||||
if (_waccess(ico_path, 0) == -1) {
|
||||
fwprintf(stderr, L"File not found: '%s'", ico_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (parse_ico_file(ico_path, &icon))
|
||||
return -1;
|
||||
}
|
||||
|
||||
handle = BeginUpdateResource(exe_path, FALSE);
|
||||
if (!handle) {
|
||||
fwprintf(stderr,
|
||||
L"Could not update resources of '%s'", exe_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ico_path) {
|
||||
int id = 1;
|
||||
UpdateResource(handle, RT_GROUP_ICON,
|
||||
L"MAINICON", language,
|
||||
icon.header, sizeof(struct header) + icon.count
|
||||
* sizeof(struct resource_directory));
|
||||
for (i = 0; i < icon.count; i++) {
|
||||
UpdateResource(handle, RT_ICON,
|
||||
MAKEINTRESOURCE(id++), language,
|
||||
icon.images[i],
|
||||
icon.items[i].bytes_in_resource);
|
||||
}
|
||||
}
|
||||
|
||||
if (command_count >= 0) {
|
||||
LPWSTR buffer, p;
|
||||
int alloc = 16; /* 16 words with string lengths, for sure... */
|
||||
|
||||
for (i = 0; i < command_count; i++) {
|
||||
int len = wcslen(commands[i]);
|
||||
if (len > 0xffff) {
|
||||
fwprintf(stderr, L"Too long command: %s\n",
|
||||
commands[i]);
|
||||
return -1;
|
||||
}
|
||||
alloc += len;
|
||||
}
|
||||
|
||||
p = buffer = calloc(alloc, sizeof(WCHAR));
|
||||
|
||||
for (i = 0; i < command_count; i++)
|
||||
p += swprintf(p, alloc - (p - buffer), L"%c%s",
|
||||
(WCHAR) wcslen(commands[i]), commands[i]);
|
||||
|
||||
UpdateResource(handle, RT_STRING, MAKEINTRESOURCE(1),
|
||||
language, buffer, sizeof(WCHAR) * alloc);
|
||||
}
|
||||
|
||||
if (EndUpdateResource(handle, FALSE))
|
||||
return 0;
|
||||
|
||||
fwprintf(stderr, L"Error %d updating resources\n",
|
||||
(int) GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int configure_via_resource(LPWSTR basename, LPWSTR exepath, LPWSTR exep,
|
||||
LPWSTR *prefix_args, int *prefix_args_len,
|
||||
int *is_git_command, int *start_in_home)
|
||||
{
|
||||
int id = 0, wargc;
|
||||
LPWSTR *wargv;
|
||||
|
||||
#define BUFSIZE 65536
|
||||
static WCHAR buf[BUFSIZE];
|
||||
int len;
|
||||
|
||||
if (!wcscmp(basename, L"edit-res.exe")) {
|
||||
LPWSTR cmdline = GetCommandLine();
|
||||
|
||||
wargv = CommandLineToArgvW(cmdline, &wargc);
|
||||
|
||||
if (wargv[1]) {
|
||||
if (wargc == 4 && !wcscmp(wargv[1], L"icon"))
|
||||
exit(edit_resources(wargv[2], wargv[3],
|
||||
NULL, -1));
|
||||
if (wargc > 1 && !wcscmp(wargv[1], L"command"))
|
||||
exit(edit_resources(wargv[2], NULL,
|
||||
wargv + 3, wargc - 3));
|
||||
}
|
||||
fwprintf(stderr,
|
||||
L"Usage: %s (icon | command) <exe> <args>...\n",
|
||||
basename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
SetEnvironmentVariable(L"EXEPATH", exepath);
|
||||
for (id = 0; ; id++) {
|
||||
len = LoadString(NULL, id, buf, BUFSIZE);
|
||||
|
||||
if (!len) {
|
||||
fwprintf(stderr, L"Need a valid command-line; "
|
||||
L"Copy %s to edit-res.exe and call\n"
|
||||
L"\n\tedit-res.exe command %s "
|
||||
L"\"<command-line>\"\n",
|
||||
basename, basename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (len >= BUFSIZE) {
|
||||
fwprintf(stderr,
|
||||
L"Could not read resource (too large)\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
buf[len] = L'\0';
|
||||
|
||||
for (;;) {
|
||||
LPWSTR atat = wcsstr(buf, L"@@"), atat2;
|
||||
WCHAR save;
|
||||
int env_len, delta;
|
||||
|
||||
if (!atat)
|
||||
break;
|
||||
|
||||
atat2 = wcsstr(atat + 2, L"@@");
|
||||
if (!atat2)
|
||||
break;
|
||||
|
||||
*atat2 = L'\0';
|
||||
env_len = GetEnvironmentVariable(atat + 2, NULL, 0);
|
||||
delta = env_len - 1 - (atat2 + 2 - atat);
|
||||
if (len + delta >= BUFSIZE) {
|
||||
fwprintf(stderr,
|
||||
L"Substituting '%s' results in too "
|
||||
L"large a command-line\n", atat + 2);
|
||||
exit(1);
|
||||
}
|
||||
if (delta)
|
||||
memmove(atat2 + 2 + delta, atat2 + 2,
|
||||
sizeof(WCHAR) * (len + 1
|
||||
- (atat2 + 2 - buf)));
|
||||
len += delta;
|
||||
save = atat[env_len - 1];
|
||||
GetEnvironmentVariable(atat + 2, atat, env_len);
|
||||
atat[env_len - 1] = save;
|
||||
}
|
||||
|
||||
/* parse first argument */
|
||||
wargv = CommandLineToArgvW(buf, &wargc);
|
||||
if (wargc < 1) {
|
||||
fwprintf(stderr, L"Invalid command-line: '%s'\n", buf);
|
||||
exit(1);
|
||||
}
|
||||
if (*wargv[0] == L'\\' ||
|
||||
(isalpha(*wargv[0]) && wargv[0][1] == L':'))
|
||||
wcscpy(exep, wargv[0]);
|
||||
else {
|
||||
wcscpy(exep, exepath);
|
||||
PathAppend(exep, wargv[0]);
|
||||
}
|
||||
|
||||
if (_waccess(exep, 0) != -1)
|
||||
break;
|
||||
fwprintf(stderr,
|
||||
L"Skipping command-line '%s'\n('%s' not found)\n",
|
||||
buf, exep);
|
||||
}
|
||||
|
||||
*prefix_args = buf;
|
||||
*prefix_args_len = wcslen(buf);
|
||||
|
||||
*is_git_command = 0;
|
||||
*start_in_home = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int r = 1, wait = 1, builtin_len = -1;
|
||||
int r = 1, wait = 1, prefix_args_len = -1, needs_env_setup = 1,
|
||||
is_git_command = 1, start_in_home = 0;
|
||||
WCHAR exepath[MAX_PATH], exe[MAX_PATH];
|
||||
LPWSTR cmd = NULL, exep = exe, builtin = NULL, basename;
|
||||
LPWSTR cmd = NULL, dir = NULL, exep = exe, prefix_args = NULL, basename;
|
||||
UINT codepage = 0;
|
||||
|
||||
/* Determine MSys2-based Git path. */
|
||||
swprintf(msystem_bin, sizeof(msystem_bin),
|
||||
L"mingw%d\\bin", (int) sizeof(void *) * 8);
|
||||
|
||||
/* get the installation location */
|
||||
GetModuleFileName(NULL, exepath, MAX_PATH);
|
||||
if (!PathRemoveFileSpec(exepath)) {
|
||||
@@ -163,12 +501,22 @@ int main(void)
|
||||
ExitProcess(1);
|
||||
}
|
||||
basename = exepath + wcslen(exepath) + 1;
|
||||
#ifdef MAGIC_RESOURCE
|
||||
if (configure_via_resource(basename, exepath, exep,
|
||||
&prefix_args, &prefix_args_len,
|
||||
&is_git_command, &start_in_home)) {
|
||||
/* do nothing */
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (!wcsncmp(basename, L"git-", 4)) {
|
||||
needs_env_setup = 0;
|
||||
|
||||
/* Call a builtin */
|
||||
builtin = basename + 4;
|
||||
builtin_len = wcslen(builtin);
|
||||
if (!wcscmp(builtin + builtin_len - 4, L".exe"))
|
||||
builtin_len -= 4;
|
||||
prefix_args = basename + 4;
|
||||
prefix_args_len = wcslen(prefix_args);
|
||||
if (!wcscmp(prefix_args + prefix_args_len - 4, L".exe"))
|
||||
prefix_args_len -= 4;
|
||||
|
||||
/* set the default exe module */
|
||||
wcscpy(exe, exepath);
|
||||
@@ -183,12 +531,27 @@ int main(void)
|
||||
|
||||
/* set the default exe module */
|
||||
wcscpy(exe, exepath);
|
||||
PathAppend(exe, L"bin\\git.exe");
|
||||
PathAppend(exe, msystem_bin);
|
||||
PathAppend(exe, L"git.exe");
|
||||
if (_waccess(exe, 0) == -1) {
|
||||
wcscpy(exe, exepath);
|
||||
PathAppend(exe, L"bin\\git.exe");
|
||||
}
|
||||
}
|
||||
|
||||
if (!builtin)
|
||||
if (needs_env_setup)
|
||||
setup_environment(exepath);
|
||||
cmd = fixup_commandline(exepath, &exep, &wait, builtin, builtin_len);
|
||||
cmd = fixup_commandline(exepath, &exep, &wait,
|
||||
prefix_args, prefix_args_len, is_git_command);
|
||||
|
||||
if (start_in_home) {
|
||||
int len = GetEnvironmentVariable(L"HOME", NULL, 0);
|
||||
|
||||
if (len) {
|
||||
dir = malloc(sizeof(WCHAR) * len);
|
||||
GetEnvironmentVariable(L"HOME", dir, len);
|
||||
}
|
||||
}
|
||||
|
||||
/* set the console to ANSI/GUI codepage */
|
||||
codepage = GetConsoleCP();
|
||||
@@ -209,7 +572,7 @@ int main(void)
|
||||
TRUE, /* handles inheritable? */
|
||||
CREATE_UNICODE_ENVIRONMENT,
|
||||
NULL, /* environment: use parent */
|
||||
NULL, /* starting directory: use parent */
|
||||
dir, /* starting directory: use parent */
|
||||
&si, &pi);
|
||||
if (br) {
|
||||
if (wait)
|
||||
|
||||
Reference in New Issue
Block a user