fixpath.c revision 1023:6005df19ef83
150465Smarcel/* 250465Smarcel * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. 350465Smarcel * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 450465Smarcel * 550465Smarcel * This code is free software; you can redistribute it and/or modify it 650465Smarcel * under the terms of the GNU General Public License version 2 only, as 750465Smarcel * published by the Free Software Foundation. Oracle designates this 850465Smarcel * particular file as subject to the "Classpath" exception as provided 950465Smarcel * by Oracle in the LICENSE file that accompanied this code. 1050465Smarcel * 1150465Smarcel * This code is distributed in the hope that it will be useful, but WITHOUT 1250465Smarcel * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1350465Smarcel * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1450465Smarcel * version 2 for more details (a copy is included in the LICENSE file that 1565067Smarcel * accompanied this code). 1650465Smarcel * 1750465Smarcel * You should have received a copy of the GNU General Public License version 1850465Smarcel * 2 along with this work; if not, write to the Free Software Foundation, 1950465Smarcel * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2050465Smarcel * 2150465Smarcel * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2250465Smarcel * or visit www.oracle.com if you need additional information or have any 2350465Smarcel * questions. 2450465Smarcel */ 2550465Smarcel 2650465Smarcel#include <Windows.h> 2750465Smarcel#include <io.h> 2850465Smarcel#include <stdio.h> 29116173Sobrien#include <string.h> 30116173Sobrien#include <malloc.h> 31116173Sobrien 32235063Snetchildvoid report_error(char const * msg) 33235063Snetchild{ 34235063Snetchild LPVOID lpMsgBuf; 3550465Smarcel DWORD dw = GetLastError(); 3650465Smarcel 37235063Snetchild FormatMessage( 3850465Smarcel FORMAT_MESSAGE_ALLOCATE_BUFFER | 3950465Smarcel FORMAT_MESSAGE_FROM_SYSTEM | 4050465Smarcel FORMAT_MESSAGE_IGNORE_INSERTS, 4150465Smarcel NULL, 42191896Sjamie dw, 4350465Smarcel MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 4487275Srwatson (LPTSTR) &lpMsgBuf, 4587275Srwatson 0, 46191896Sjamie NULL); 4750465Smarcel 48140214Sobrien fprintf(stderr, 49140214Sobrien "%s Failed with error %d: %s\n", 50140214Sobrien msg, dw, lpMsgBuf); 5164907Smarcel 52133816Stjr LocalFree(lpMsgBuf); 53235063Snetchild} 5464907Smarcel 55246085Sjhb/* 5650465Smarcel * Test if pos points to /cygdrive/_/ where _ can 57235063Snetchild * be any character. 58235063Snetchild */ 59235063Snetchildint is_cygdrive_here(int pos, char const *in, int len) 60235063Snetchild{ 61235063Snetchild // Length of /cygdrive/c/ is 12 62235063Snetchild if (pos+12 > len) return 0; 63235063Snetchild if (in[pos+11]=='/' && 64235063Snetchild in[pos+9]=='/' && 65235063Snetchild in[pos+8]=='e' && 66235063Snetchild in[pos+7]=='v' && 67235063Snetchild in[pos+6]=='i' && 68235063Snetchild in[pos+5]=='r' && 69235063Snetchild in[pos+4]=='d' && 70235063Snetchild in[pos+3]=='g' && 71235063Snetchild in[pos+2]=='y' && 72235063Snetchild in[pos+1]=='c' && 73235063Snetchild in[pos+0]=='/') { 74235063Snetchild return 1; 75235063Snetchild } 76235063Snetchild return 0; 77235063Snetchild} 78235063Snetchild 79235063Snetchild/* 80235063Snetchild * Replace /cygdrive/_/ with _:/ 81235063Snetchild * Works in place since drive letter is always 82235063Snetchild * shorter than /cygdrive/ 83235063Snetchild */ 84235063Snetchildchar *replace_cygdrive_cygwin(char const *in) 85235063Snetchild{ 86235063Snetchild size_t len = strlen(in); 87235063Snetchild char *out = (char*) malloc(len+1); 88235063Snetchild int i,j; 89235063Snetchild 90235063Snetchild if (len < 12) { 91235063Snetchild memmove(out, in, len + 1); 92235063Snetchild return out; 93235063Snetchild } 94235063Snetchild 95235063Snetchild for (i = 0, j = 0; i<len;) { 96235063Snetchild if (is_cygdrive_here(i, in, len)) { 97235063Snetchild out[j++] = in[i+10]; 98235063Snetchild out[j++] = ':'; 99235063Snetchild i+=11; 100235063Snetchild } else { 101235063Snetchild out[j] = in[i]; 102235063Snetchild i++; 103235063Snetchild j++; 104235063Snetchild } 105235063Snetchild } 106235063Snetchild out[j] = '\0'; 107235063Snetchild return out; 108235063Snetchild} 109235063Snetchild 110235063Snetchildvoid append(char **b, size_t *bl, size_t *u, char *add, size_t addlen) 111235063Snetchild{ 112235063Snetchild while ((addlen+*u+1) > *bl) { 113235063Snetchild *bl *= 2; 114235063Snetchild *b = (char*) realloc(*b, *bl); 115235063Snetchild } 116235063Snetchild memcpy(*b+*u, add, addlen); 117235063Snetchild *u += addlen; 118235063Snetchild} 119235063Snetchild 120235063Snetchild/* 121235063Snetchild * Creates a new string from in where the first occurrence of sub is 122235063Snetchild * replaced by rep. 123235063Snetchild */ 12450465Smarcelchar *replace_substring(char *in, char *sub, char *rep) 12550465Smarcel{ 12650465Smarcel int in_len = strlen(in); 12750465Smarcel int sub_len = strlen(sub); 128191972Sdchagin int rep_len = strlen(rep); 12950465Smarcel char *out = (char *) malloc(in_len - sub_len + rep_len + 1); 13050465Smarcel char *p; 131192895Sjamie 132192895Sjamie if (!(p = strstr(in, sub))) { 133192895Sjamie // If sub isn't a substring of in, just return in. 134192895Sjamie return in; 135192895Sjamie } 136192895Sjamie 137192895Sjamie // Copy characters from beginning of in to start of sub. 138191896Sjamie strncpy(out, in, p - in); 139191896Sjamie out[p - in] = '\0'; 140227309Sed 14150465Smarcel sprintf(out + (p - in), "%s%s", rep, p + sub_len); 14250465Smarcel 143219668Snetchild return out; 144219668Snetchild} 145219668Snetchild 146219668Snetchildchar* msys_path_list; // @-separated list of paths prefix to look for 14750465Smarcelchar* msys_path_list_end; // Points to last \0 in msys_path_list. 14862573Sphk 14950465Smarcelvoid setup_msys_path_list(char const * argument) 15050465Smarcel{ 15150465Smarcel char* p; 15250465Smarcel char* drive_letter_pos; 153235063Snetchild 154235063Snetchild msys_path_list = strdup(&argument[2]); 155112206Sjhb msys_path_list_end = &msys_path_list[strlen(msys_path_list)]; 15650465Smarcel 157235063Snetchild // Convert all at-sign (@) in path list to \0. 158235063Snetchild // @ was chosen as separator to minimize risk of other tools messing around with it 159235063Snetchild p = msys_path_list; 160235063Snetchild do { 16150465Smarcel if (p[1] == ':') { 162235063Snetchild // msys has mangled our path list, restore it from c:/... to /c/... 163112206Sjhb drive_letter_pos = p+1; 164235063Snetchild *drive_letter_pos = *p; 165235063Snetchild *p = '/'; 16650465Smarcel } 16750465Smarcel 16850465Smarcel // Look for an @ in the list 16950465Smarcel p = strchr(p, '@'); 170191792Sjamie if (p != NULL) { 17150465Smarcel *p = '\0'; 17250465Smarcel p++; 17350465Smarcel } 17450465Smarcel } while (p != NULL); 17562573Sphk} 17650465Smarcel 17750465Smarcelchar *replace_cygdrive_msys(char const *in) 17850465Smarcel{ 17950465Smarcel char* str; 180235063Snetchild char* prefix; 181235063Snetchild char* p; 182112206Sjhb 18350465Smarcel str = strdup(in); 184235063Snetchild 185235063Snetchild // For each prefix in the path list, search for it and replace /c/... with c:/... 186235063Snetchild for (prefix = msys_path_list; prefix < msys_path_list_end && prefix != NULL; prefix += strlen(prefix)+1) { 187235063Snetchild p=str; 18850465Smarcel while ((p = strstr(p, prefix))) { 189235063Snetchild char* drive_letter = p+1; 190112206Sjhb *p = *drive_letter; 191235063Snetchild *drive_letter = ':'; 192235063Snetchild p++; 19350465Smarcel } 19450465Smarcel } 19550465Smarcel 19650465Smarcel return str; 197191792Sjamie} 19850465Smarcel 19950465Smarcelchar*(*replace_cygdrive)(char const *in) = NULL; 20050465Smarcel 20150465Smarcelchar *files_to_delete[1024]; 20262573Sphkint num_files_to_delete = 0; 20350465Smarcel 20450465Smarcelchar *fix_at_file(char const *in) 20550465Smarcel{ 20650465Smarcel char *tmpdir; 207235063Snetchild char name[2048]; 208235063Snetchild char *atname; 209112206Sjhb char *buffer; 21050465Smarcel size_t buflen=65536; 211235063Snetchild size_t used=0; 212235063Snetchild size_t len; 213235063Snetchild int rc; 214235063Snetchild FILE *atout; 21550465Smarcel FILE *atin; 216235063Snetchild char block[2048]; 217112206Sjhb size_t blocklen; 218235063Snetchild char *fixed; 219235063Snetchild 22050465Smarcel atin = fopen(in+1, "r"); 22150465Smarcel if (atin == NULL) { 22250465Smarcel fprintf(stderr, "Could not read at file %s\n", in+1); 22350465Smarcel exit(-1); 224191792Sjamie } 22550465Smarcel 22650465Smarcel tmpdir = getenv("TEMP"); 22750465Smarcel if (tmpdir == NULL) { 22887275Srwatson#if _WIN64 229191972Sdchagin tmpdir = "c:/cygwin64/tmp"; 230191972Sdchagin#else 231191972Sdchagin tmpdir = "c:/cygwin/tmp"; 232191972Sdchagin#endif 233191972Sdchagin } 234191972Sdchagin _snprintf(name, sizeof(name), "%s\\atfile_XXXXXX", tmpdir); 235191972Sdchagin 236191972Sdchagin rc = _mktemp_s(name, strlen(name)+1); 237235063Snetchild if (rc) { 238235063Snetchild fprintf(stderr, "Could not create temporary file name for at file!\n"); 239191972Sdchagin exit(-1); 240191972Sdchagin } 241191972Sdchagin 242235063Snetchild atout = fopen(name, "w"); 243235063Snetchild if (atout == NULL) { 244191972Sdchagin fprintf(stderr, "Could not open temporary file for writing! %s\n", name); 245235063Snetchild exit(-1); 246191972Sdchagin } 247191972Sdchagin 248235063Snetchild buffer = (char*) malloc(buflen); 249235063Snetchild while ((blocklen = fread(block, 1, sizeof(block), atin)) > 0) { 250191972Sdchagin append(&buffer, &buflen, &used, block, blocklen); 251235063Snetchild } 252191972Sdchagin buffer[used] = 0; 253191972Sdchagin if (getenv("DEBUG_FIXPATH") != NULL) { 254235063Snetchild fprintf(stderr, "fixpath input from @-file %s: %s\n", &in[1], buffer); 255235063Snetchild } 256191972Sdchagin fixed = replace_cygdrive(buffer); 257235063Snetchild if (getenv("DEBUG_FIXPATH") != NULL) { 258191972Sdchagin fprintf(stderr, "fixpath converted to @-file %s is: %s\n", name, fixed); 259191972Sdchagin } 260235063Snetchild fwrite(fixed, strlen(fixed), 1, atout); 261235063Snetchild fclose(atin); 262191972Sdchagin fclose(atout); 263235063Snetchild free(fixed); 264191972Sdchagin free(buffer); 265191972Sdchagin files_to_delete[num_files_to_delete] = (char*) malloc(strlen(name)+1); 266235063Snetchild strcpy(files_to_delete[num_files_to_delete], name); 267235063Snetchild num_files_to_delete++; 268191972Sdchagin atname = (char*) malloc(strlen(name)+2); 269191972Sdchagin atname[0] = '@'; 270191972Sdchagin strcpy(atname+1, name); 271191972Sdchagin return atname; 272192895Sjamie} 273192895Sjamie 27487275Srwatson// given an argument, convert it to the windows command line safe quoted version 275191896Sjamie// using rules from: 276192895Sjamie// http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx 27750465Smarcel// caller is responsible for freeing both input and output. 278191896Sjamiechar * quote_arg(char const * in_arg) { 279191896Sjamie char *quoted = NULL; 28050465Smarcel char *current = quoted; 281235063Snetchild int pass; 282235063Snetchild 283192895Sjamie if (strlen(in_arg) == 0) { 284192895Sjamie // empty string? explicitly quote it. 285192895Sjamie return _strdup("\"\""); 286192895Sjamie } 287192895Sjamie 288192895Sjamie if (strpbrk(in_arg, " \t\n\v\r\\\"") == NULL) { 289192895Sjamie return _strdup(in_arg); 290192895Sjamie } 291192895Sjamie 292192895Sjamie // process the arg twice. Once to calculate the size and then to copy it. 293191896Sjamie for (pass=1; pass<=2; pass++) { 294192895Sjamie char const *arg = in_arg; 295192895Sjamie 296235063Snetchild // initial " 297235063Snetchild if (pass == 2) { 298191896Sjamie *current = '\"'; 299191896Sjamie } 300191896Sjamie current++; 301191896Sjamie 302192895Sjamie // process string to be quoted until NUL 303192895Sjamie do { 304191896Sjamie int escapes = 0; 305191896Sjamie 306191896Sjamie while (*arg == '\\') { 307191896Sjamie // count escapes. 308192895Sjamie escapes++; 309191896Sjamie arg++; 310191896Sjamie } 311191896Sjamie 312235063Snetchild if (*arg == '\0') { 313235063Snetchild // escape the escapes before final " 314191896Sjamie escapes *= 2; 315191896Sjamie } else if (*arg == '"') { 316192895Sjamie // escape the escapes and the " 317192895Sjamie escapes = escapes * 2 + 1; 318191896Sjamie } else { 319191896Sjamie // escapes aren't special, just echo them. 320191896Sjamie } 321191896Sjamie 322191896Sjamie // emit some escapes 323192895Sjamie while (escapes > 0) { 324191896Sjamie if (pass == 2) { 325192895Sjamie *current = '\\'; 326192895Sjamie } 327191896Sjamie current++; 328191896Sjamie escapes--; 329191896Sjamie } 330192895Sjamie 331192895Sjamie // and the current char 332191896Sjamie if (pass == 2) { 333192895Sjamie *current = *arg; 334192895Sjamie } 335192895Sjamie current++; 336192895Sjamie } while (*arg++ != '\0'); 337191896Sjamie 338192895Sjamie // allocate the buffer 339191896Sjamie if (pass == 1) { 340192895Sjamie size_t alloc = (size_t) (current - quoted + (ptrdiff_t) 2); 341192895Sjamie current = quoted = (char*) calloc(alloc, sizeof(char)); 342191896Sjamie } 343191896Sjamie } 344192895Sjamie 345192895Sjamie // final " and \0 346235063Snetchild *(current - 1) = '"'; 347235063Snetchild *current = '\0'; 348191896Sjamie 349191896Sjamie return quoted; 350191896Sjamie} 351191896Sjamie 352191896Sjamieint main(int argc, char const ** argv) 353191896Sjamie{ 354191896Sjamie STARTUPINFO si; 355191896Sjamie PROCESS_INFORMATION pi; 356191896Sjamie unsigned short rc; 357191896Sjamie 358191896Sjamie char *line; 359235063Snetchild char *current; 360191896Sjamie int i, cmd; 361235063Snetchild DWORD exitCode; 362235063Snetchild 363235063Snetchild if (argc<2 || argv[1][0] != '-' || (argv[1][1] != 'c' && argv[1][1] != 'm')) { 364235063Snetchild fprintf(stderr, "Usage: fixpath -c|m<path@path@...> /cygdrive/c/WINDOWS/notepad.exe [/cygdrive/c/x/test.txt|@/cygdrive/c/x/atfile]\n"); 365235063Snetchild exit(0); 366235063Snetchild } 367235063Snetchild 368235063Snetchild if (getenv("DEBUG_FIXPATH") != NULL) { 369191896Sjamie char const * cmdline = GetCommandLine(); 370235063Snetchild fprintf(stderr, "fixpath input line >%s<\n", strstr(cmdline, argv[1])); 371191896Sjamie } 372191896Sjamie 373195870Sjamie if (argv[1][1] == 'c' && argv[1][2] == '\0') { 374191896Sjamie if (getenv("DEBUG_FIXPATH") != NULL) { 375235063Snetchild fprintf(stderr, "fixpath using cygwin mode\n"); 376235063Snetchild } 377235063Snetchild replace_cygdrive = replace_cygdrive_cygwin; 378235063Snetchild } else if (argv[1][1] == 'm') { 379191896Sjamie if (getenv("DEBUG_FIXPATH") != NULL) { 380191896Sjamie fprintf(stderr, "fixpath using msys mode, with path list: %s\n", &argv[1][2]); 381191896Sjamie } 382191896Sjamie setup_msys_path_list(argv[1]); 383191896Sjamie replace_cygdrive = replace_cygdrive_msys; 384191896Sjamie } else { 385191896Sjamie fprintf(stderr, "fixpath Unknown mode: %s\n", argv[1]); 386195870Sjamie exit(-1); 387191896Sjamie } 388235063Snetchild 389235063Snetchild i = 2; 390191896Sjamie 391195870Sjamie // handle assignments 392235063Snetchild while (i < argc) { 393235063Snetchild char const * assignment = strchr(argv[i], '='); 394235063Snetchild if (assignment != NULL && assignment != argv[i]) { 395235063Snetchild size_t var_len = (size_t) (assignment - argv[i] + (ptrdiff_t) 1); 396195870Sjamie char *var = (char *) calloc(var_len, sizeof(char)); 397235063Snetchild char *val = replace_cygdrive(assignment + 1); 398235063Snetchild memmove(var, argv[i], var_len); 399195870Sjamie var[var_len - 1] = '\0'; 400235063Snetchild strupr(var); 401235063Snetchild 402235063Snetchild if (getenv("DEBUG_FIXPATH") != NULL) { 403195870Sjamie fprintf(stderr, "fixpath setting var >%s< to >%s<\n", var, val); 404235063Snetchild } 405195870Sjamie 406191896Sjamie rc = SetEnvironmentVariable(var, val); 407235063Snetchild if (!rc) { 408235063Snetchild // Could not set var for some reason. Try to report why. 409235063Snetchild const int msg_len = 80 + var_len + strlen(val); 410235063Snetchild char * msg = (char *) alloca(msg_len); 411191896Sjamie _snprintf_s(msg, msg_len, _TRUNCATE, "Could not set environment variable [%s=%s]", var, val); 412235063Snetchild report_error(msg); 413235063Snetchild exit(1); 414191896Sjamie } 415235063Snetchild free(var); 416235063Snetchild free(val); 417235063Snetchild } else { 418191896Sjamie // no more assignments; 419235063Snetchild break; 420191896Sjamie } 421191896Sjamie i++; 422235063Snetchild } 423235063Snetchild 424191896Sjamie // remember index of the command 425191896Sjamie cmd = i; 426191896Sjamie 427191896Sjamie // handle command and it's args. 428235063Snetchild while (i < argc) { 429235063Snetchild char const *replaced = replace_cygdrive(argv[i]); 430235063Snetchild if (replaced[0] == '@') { 431235063Snetchild // Found at-file! Fix it! 432191896Sjamie replaced = fix_at_file(replaced); 433235063Snetchild } 434235063Snetchild argv[i] = quote_arg(replaced); 435191896Sjamie i++; 436235063Snetchild } 437235063Snetchild 438235063Snetchild // determine the length of the line 439191896Sjamie line = NULL; 440235063Snetchild // args 441191896Sjamie for (i = cmd; i < argc; i++) { 442191896Sjamie line += (ptrdiff_t) strlen(argv[i]); 443235063Snetchild } 444235063Snetchild // spaces and null 445191896Sjamie line += (ptrdiff_t) (argc - cmd + 1); 446191896Sjamie // allocate 447192895Sjamie line = (char*) calloc(line - (char*) NULL, sizeof(char)); 448192895Sjamie 449192895Sjamie // copy in args. 450235063Snetchild current = line; 451192895Sjamie for (i = cmd; i < argc; i++) { 452192895Sjamie ptrdiff_t len = strlen(argv[i]); 453191896Sjamie if (i != cmd) { 454191896Sjamie *current++ = ' '; 455191896Sjamie } 456235063Snetchild memmove(current, argv[i], len); 457235063Snetchild current += len; 458235063Snetchild } 459235063Snetchild *current = '\0'; 460235063Snetchild 461235063Snetchild if (getenv("DEBUG_FIXPATH") != NULL) { 462235063Snetchild fprintf(stderr, "fixpath converted line >%s<\n", line); 463191896Sjamie } 464191896Sjamie 465191896Sjamie if (cmd == argc) { 466191896Sjamie if (getenv("DEBUG_FIXPATH") != NULL) { 467191896Sjamie fprintf(stderr, "fixpath no command provided!\n"); 468191896Sjamie } 469191896Sjamie exit(0); 470191896Sjamie } 471191896Sjamie 472195870Sjamie ZeroMemory(&si, sizeof(si)); 473191896Sjamie si.cb=sizeof(si); 474235063Snetchild ZeroMemory(&pi, sizeof(pi)); 475235063Snetchild 476191896Sjamie fflush(stderr); 477195870Sjamie fflush(stdout); 478235063Snetchild 479235063Snetchild rc = CreateProcess(NULL, 480195870Sjamie line, 481195870Sjamie 0, 482191896Sjamie 0, 483235063Snetchild TRUE, 484235063Snetchild 0, 485191896Sjamie NULL, 486191896Sjamie NULL, 487191896Sjamie &si, 488195870Sjamie &pi); 489191896Sjamie if (!rc) { 490235063Snetchild // Could not start process for some reason. Try to report why: 491235063Snetchild report_error("Could not start process!"); 492191896Sjamie exit(126); 493191896Sjamie } 494191896Sjamie 495195870Sjamie WaitForSingleObject(pi.hProcess, INFINITE); 496191896Sjamie GetExitCodeProcess(pi.hProcess, &exitCode); 497191896Sjamie 498235063Snetchild if (getenv("DEBUG_FIXPATH") != NULL) { 499235063Snetchild for (i=0; i<num_files_to_delete; ++i) { 500195870Sjamie fprintf(stderr, "fixpath Not deleting temporary file %s\n", 501195870Sjamie files_to_delete[i]); 502195870Sjamie } 503195870Sjamie } else { 504195870Sjamie for (i=0; i<num_files_to_delete; ++i) { 505195870Sjamie remove(files_to_delete[i]); 506195870Sjamie } 507195870Sjamie } 508195870Sjamie 509191896Sjamie if (exitCode != 0) { 510191896Sjamie if (getenv("DEBUG_FIXPATH") != NULL) { 511191896Sjamie fprintf(stderr, "fixpath exit code %d\n", 512195870Sjamie exitCode); 513195870Sjamie } 514112206Sjhb } 515195870Sjamie 516191896Sjamie exit(exitCode); 517112206Sjhb} 518191896Sjamie