fixpath.c revision 1023:6005df19ef83
118334Speter/* 2169689Skan * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. 3169689Skan * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 418334Speter * 590075Sobrien * This code is free software; you can redistribute it and/or modify it 618334Speter * under the terms of the GNU General Public License version 2 only, as 790075Sobrien * published by the Free Software Foundation. Oracle designates this 890075Sobrien * particular file as subject to the "Classpath" exception as provided 990075Sobrien * by Oracle in the LICENSE file that accompanied this code. 1090075Sobrien * 1118334Speter * This code is distributed in the hope that it will be useful, but WITHOUT 1290075Sobrien * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1390075Sobrien * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1490075Sobrien * version 2 for more details (a copy is included in the LICENSE file that 1590075Sobrien * accompanied this code). 1618334Speter * 1718334Speter * You should have received a copy of the GNU General Public License version 1890075Sobrien * 2 along with this work; if not, write to the Free Software Foundation, 19169689Skan * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20169689Skan * 2118334Speter * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2218334Speter * or visit www.oracle.com if you need additional information or have any 2350397Sobrien * questions. 24132718Skan */ 25132718Skan 2650397Sobrien#include <Windows.h> 2718334Speter#include <io.h> 2818334Speter#include <stdio.h> 2990075Sobrien#include <string.h> 3018334Speter#include <malloc.h> 3150397Sobrien 3250397Sobrienvoid report_error(char const * msg) 3390075Sobrien{ 3496263Sobrien LPVOID lpMsgBuf; 3590075Sobrien DWORD dw = GetLastError(); 3690075Sobrien 37169689Skan FormatMessage( 38169689Skan FORMAT_MESSAGE_ALLOCATE_BUFFER | 39169689Skan FORMAT_MESSAGE_FROM_SYSTEM | 40169689Skan FORMAT_MESSAGE_IGNORE_INSERTS, 4118334Speter NULL, 42169689Skan dw, 43169689Skan MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 44169689Skan (LPTSTR) &lpMsgBuf, 45169689Skan 0, 4618334Speter NULL); 47117395Skan 4852284Sobrien fprintf(stderr, 49169689Skan "%s Failed with error %d: %s\n", 50169689Skan msg, dw, lpMsgBuf); 5152284Sobrien 5252284Sobrien LocalFree(lpMsgBuf); 5318334Speter} 54117395Skan 5552284Sobrien/* 56117395Skan * Test if pos points to /cygdrive/_/ where _ can 57132718Skan * be any character. 58117395Skan */ 59117395Skanint is_cygdrive_here(int pos, char const *in, int len) 6090075Sobrien{ 61169689Skan // Length of /cygdrive/c/ is 12 62169689Skan if (pos+12 > len) return 0; 6390075Sobrien if (in[pos+11]=='/' && 6490075Sobrien in[pos+9]=='/' && 6596263Sobrien in[pos+8]=='e' && 66169689Skan in[pos+7]=='v' && 67169689Skan in[pos+6]=='i' && 68169689Skan in[pos+5]=='r' && 6952284Sobrien in[pos+4]=='d' && 70132718Skan in[pos+3]=='g' && 71132718Skan in[pos+2]=='y' && 7290075Sobrien in[pos+1]=='c' && 7352284Sobrien in[pos+0]=='/') { 7490075Sobrien return 1; 75132718Skan } 7652284Sobrien return 0; 77169689Skan} 7852284Sobrien 79169689Skan/* 8052284Sobrien * Replace /cygdrive/_/ with _:/ 81169689Skan * Works in place since drive letter is always 82169689Skan * shorter than /cygdrive/ 83169689Skan */ 8452284Sobrienchar *replace_cygdrive_cygwin(char const *in) 85169689Skan{ 86169689Skan size_t len = strlen(in); 87169689Skan char *out = (char*) malloc(len+1); 88169689Skan int i,j; 89169689Skan 90169689Skan if (len < 12) { 91169689Skan memmove(out, in, len + 1); 92169689Skan return out; 93169689Skan } 9452284Sobrien 9552284Sobrien for (i = 0, j = 0; i<len;) { 9652284Sobrien if (is_cygdrive_here(i, in, len)) { 9790075Sobrien out[j++] = in[i+10]; 98132718Skan out[j++] = ':'; 9952284Sobrien i+=11; 10052284Sobrien } else { 101169689Skan out[j] = in[i]; 10252284Sobrien i++; 103169689Skan j++; 10452284Sobrien } 10552284Sobrien } 10652284Sobrien out[j] = '\0'; 10752284Sobrien return out; 10852284Sobrien} 10952284Sobrien 11052284Sobrienvoid append(char **b, size_t *bl, size_t *u, char *add, size_t addlen) 11152284Sobrien{ 11252284Sobrien while ((addlen+*u+1) > *bl) { 11352284Sobrien *bl *= 2; 11452284Sobrien *b = (char*) realloc(*b, *bl); 11552284Sobrien } 116169689Skan memcpy(*b+*u, add, addlen); 117169689Skan *u += addlen; 11852284Sobrien} 11952284Sobrien 12052284Sobrien/* 121169689Skan * Creates a new string from in where the first occurrence of sub is 12252284Sobrien * replaced by rep. 123169689Skan */ 12452284Sobrienchar *replace_substring(char *in, char *sub, char *rep) 125169689Skan{ 12652284Sobrien int in_len = strlen(in); 12790075Sobrien int sub_len = strlen(sub); 12890075Sobrien int rep_len = strlen(rep); 12990075Sobrien char *out = (char *) malloc(in_len - sub_len + rep_len + 1); 130169689Skan char *p; 13190075Sobrien 132169689Skan if (!(p = strstr(in, sub))) { 13352284Sobrien // If sub isn't a substring of in, just return in. 13452284Sobrien return in; 13590075Sobrien } 13690075Sobrien 137169689Skan // Copy characters from beginning of in to start of sub. 138169689Skan strncpy(out, in, p - in); 13990075Sobrien out[p - in] = '\0'; 140169689Skan 14190075Sobrien sprintf(out + (p - in), "%s%s", rep, p + sub_len); 14290075Sobrien 14390075Sobrien return out; 14490075Sobrien} 145169689Skan 14618334Speterchar* msys_path_list; // @-separated list of paths prefix to look for 14790075Sobrienchar* msys_path_list_end; // Points to last \0 in msys_path_list. 14890075Sobrien 14990075Sobrienvoid setup_msys_path_list(char const * argument) 15090075Sobrien{ 15118334Speter char* p; 152169689Skan char* drive_letter_pos; 153169689Skan 15490075Sobrien msys_path_list = strdup(&argument[2]); 155169689Skan msys_path_list_end = &msys_path_list[strlen(msys_path_list)]; 15690075Sobrien 15718334Speter // Convert all at-sign (@) in path list to \0. 15890075Sobrien // @ was chosen as separator to minimize risk of other tools messing around with it 159169689Skan p = msys_path_list; 16018334Speter do { 16190075Sobrien if (p[1] == ':') { 16252284Sobrien // msys has mangled our path list, restore it from c:/... to /c/... 163169689Skan drive_letter_pos = p+1; 164169689Skan *drive_letter_pos = *p; 16590075Sobrien *p = '/'; 16690075Sobrien } 167169689Skan 168169689Skan // Look for an @ in the list 16952284Sobrien p = strchr(p, '@'); 17090075Sobrien if (p != NULL) { 17118334Speter *p = '\0'; 172169689Skan p++; 173169689Skan } 17490075Sobrien } while (p != NULL); 175169689Skan} 17690075Sobrien 17718334Speterchar *replace_cygdrive_msys(char const *in) 17890075Sobrien{ 17990075Sobrien char* str; 18090075Sobrien char* prefix; 18190075Sobrien char* p; 18290075Sobrien 18352284Sobrien str = strdup(in); 184169689Skan 18518334Speter // For each prefix in the path list, search for it and replace /c/... with c:/... 186169689Skan for (prefix = msys_path_list; prefix < msys_path_list_end && prefix != NULL; prefix += strlen(prefix)+1) { 18752284Sobrien p=str; 188169689Skan while ((p = strstr(p, prefix))) { 189169689Skan char* drive_letter = p+1; 19052284Sobrien *p = *drive_letter; 19190075Sobrien *drive_letter = ':'; 19252284Sobrien p++; 193169689Skan } 19490075Sobrien } 195169689Skan 196169689Skan return str; 197169689Skan} 198169689Skan 199169689Skanchar*(*replace_cygdrive)(char const *in) = NULL; 20090075Sobrien 201169689Skanchar *files_to_delete[1024]; 202169689Skanint num_files_to_delete = 0; 20318334Speter 20418334Speterchar *fix_at_file(char const *in) 20590075Sobrien{ 20690075Sobrien char *tmpdir; 20790075Sobrien char name[2048]; 20890075Sobrien char *atname; 20990075Sobrien char *buffer; 210169689Skan size_t buflen=65536; 21152284Sobrien size_t used=0; 212169689Skan size_t len; 213169689Skan int rc; 21452284Sobrien FILE *atout; 215169689Skan FILE *atin; 216169689Skan char block[2048]; 217169689Skan size_t blocklen; 21890075Sobrien char *fixed; 21990075Sobrien 22090075Sobrien atin = fopen(in+1, "r"); 22190075Sobrien if (atin == NULL) { 22290075Sobrien fprintf(stderr, "Could not read at file %s\n", in+1); 22390075Sobrien exit(-1); 22490075Sobrien } 22590075Sobrien 22690075Sobrien tmpdir = getenv("TEMP"); 22790075Sobrien if (tmpdir == NULL) { 22890075Sobrien#if _WIN64 229169689Skan tmpdir = "c:/cygwin64/tmp"; 230169689Skan#else 231169689Skan tmpdir = "c:/cygwin/tmp"; 232169689Skan#endif 233169689Skan } 234169689Skan _snprintf(name, sizeof(name), "%s\\atfile_XXXXXX", tmpdir); 23590075Sobrien 23690075Sobrien rc = _mktemp_s(name, strlen(name)+1); 23790075Sobrien if (rc) { 23818334Speter fprintf(stderr, "Could not create temporary file name for at file!\n"); 23990075Sobrien exit(-1); 24090075Sobrien } 24190075Sobrien 24290075Sobrien atout = fopen(name, "w"); 243169689Skan if (atout == NULL) { 24490075Sobrien fprintf(stderr, "Could not open temporary file for writing! %s\n", name); 24590075Sobrien exit(-1); 24690075Sobrien } 24718334Speter 248117395Skan buffer = (char*) malloc(buflen); 249117395Skan while ((blocklen = fread(block, 1, sizeof(block), atin)) > 0) { 25090075Sobrien append(&buffer, &buflen, &used, block, blocklen); 251132718Skan } 252132718Skan buffer[used] = 0; 25352284Sobrien if (getenv("DEBUG_FIXPATH") != NULL) { 25496263Sobrien fprintf(stderr, "fixpath input from @-file %s: %s\n", &in[1], buffer); 255132718Skan } 25696263Sobrien fixed = replace_cygdrive(buffer); 25796263Sobrien if (getenv("DEBUG_FIXPATH") != NULL) { 258102780Skan fprintf(stderr, "fixpath converted to @-file %s is: %s\n", name, fixed); 259102780Skan } 260102780Skan fwrite(fixed, strlen(fixed), 1, atout); 261102780Skan fclose(atin); 262102780Skan fclose(atout); 263102780Skan free(fixed); 264102780Skan free(buffer); 265102780Skan files_to_delete[num_files_to_delete] = (char*) malloc(strlen(name)+1); 26696263Sobrien strcpy(files_to_delete[num_files_to_delete], name); 267132718Skan num_files_to_delete++; 26896263Sobrien atname = (char*) malloc(strlen(name)+2); 269169689Skan atname[0] = '@'; 270169689Skan strcpy(atname+1, name); 27196263Sobrien return atname; 27296263Sobrien} 27396263Sobrien 27496263Sobrien// given an argument, convert it to the windows command line safe quoted version 27596263Sobrien// using rules from: 276132718Skan// http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx 27796263Sobrien// caller is responsible for freeing both input and output. 27896263Sobrienchar * quote_arg(char const * in_arg) { 27996263Sobrien char *quoted = NULL; 280119256Skan char *current = quoted; 281119256Skan int pass; 282119256Skan 283119256Skan if (strlen(in_arg) == 0) { 28496263Sobrien // empty string? explicitly quote it. 285119256Skan return _strdup("\"\""); 286119256Skan } 287119256Skan 288119256Skan if (strpbrk(in_arg, " \t\n\v\r\\\"") == NULL) { 289119256Skan return _strdup(in_arg); 290119256Skan } 291169689Skan 292119256Skan // process the arg twice. Once to calculate the size and then to copy it. 293119256Skan for (pass=1; pass<=2; pass++) { 29496263Sobrien char const *arg = in_arg; 295119256Skan 296119256Skan // initial " 29796263Sobrien if (pass == 2) { 29896263Sobrien *current = '\"'; 29996263Sobrien } 30096263Sobrien current++; 30196263Sobrien 30296263Sobrien // process string to be quoted until NUL 30396263Sobrien do { 30496263Sobrien int escapes = 0; 30596263Sobrien 306169689Skan while (*arg == '\\') { 307169689Skan // count escapes. 308169689Skan escapes++; 309169689Skan arg++; 310169689Skan } 311169689Skan 312169689Skan if (*arg == '\0') { 313169689Skan // escape the escapes before final " 314169689Skan escapes *= 2; 315169689Skan } else if (*arg == '"') { 316169689Skan // escape the escapes and the " 317169689Skan escapes = escapes * 2 + 1; 318169689Skan } else { 319169689Skan // escapes aren't special, just echo them. 320169689Skan } 321169689Skan 322169689Skan // emit some escapes 323169689Skan while (escapes > 0) { 324169689Skan if (pass == 2) { 325169689Skan *current = '\\'; 326169689Skan } 327169689Skan current++; 328169689Skan escapes--; 329169689Skan } 330169689Skan 331169689Skan // and the current char 332169689Skan if (pass == 2) { 33390075Sobrien *current = *arg; 33490075Sobrien } 335169689Skan current++; 33690075Sobrien } while (*arg++ != '\0'); 33796263Sobrien 33890075Sobrien // allocate the buffer 33952284Sobrien if (pass == 1) { 34090075Sobrien size_t alloc = (size_t) (current - quoted + (ptrdiff_t) 2); 34152284Sobrien current = quoted = (char*) calloc(alloc, sizeof(char)); 342169689Skan } 34390075Sobrien } 344169689Skan 34590075Sobrien // final " and \0 34690075Sobrien *(current - 1) = '"'; 347169689Skan *current = '\0'; 34890075Sobrien 349169689Skan return quoted; 35090075Sobrien} 35190075Sobrien 352169689Skanint main(int argc, char const ** argv) 35352284Sobrien{ 35496263Sobrien STARTUPINFO si; 355169689Skan PROCESS_INFORMATION pi; 356102780Skan unsigned short rc; 357102780Skan 358102780Skan char *line; 359102780Skan char *current; 360102780Skan int i, cmd; 36196263Sobrien DWORD exitCode; 36296263Sobrien 36390075Sobrien if (argc<2 || argv[1][0] != '-' || (argv[1][1] != 'c' && argv[1][1] != 'm')) { 36496263Sobrien fprintf(stderr, "Usage: fixpath -c|m<path@path@...> /cygdrive/c/WINDOWS/notepad.exe [/cygdrive/c/x/test.txt|@/cygdrive/c/x/atfile]\n"); 36596263Sobrien exit(0); 366169689Skan } 36796263Sobrien 36896263Sobrien if (getenv("DEBUG_FIXPATH") != NULL) { 369169689Skan char const * cmdline = GetCommandLine(); 370169689Skan fprintf(stderr, "fixpath input line >%s<\n", strstr(cmdline, argv[1])); 371169689Skan } 372169689Skan 373169689Skan if (argv[1][1] == 'c' && argv[1][2] == '\0') { 37496263Sobrien if (getenv("DEBUG_FIXPATH") != NULL) { 37596263Sobrien fprintf(stderr, "fixpath using cygwin mode\n"); 376169689Skan } 377169689Skan replace_cygdrive = replace_cygdrive_cygwin; 378169689Skan } else if (argv[1][1] == 'm') { 379169689Skan if (getenv("DEBUG_FIXPATH") != NULL) { 380169689Skan fprintf(stderr, "fixpath using msys mode, with path list: %s\n", &argv[1][2]); 381169689Skan } 382169689Skan setup_msys_path_list(argv[1]); 383169689Skan replace_cygdrive = replace_cygdrive_msys; 384169689Skan } else { 385169689Skan fprintf(stderr, "fixpath Unknown mode: %s\n", argv[1]); 386169689Skan exit(-1); 387169689Skan } 388169689Skan 389169689Skan i = 2; 390169689Skan 391169689Skan // handle assignments 392169689Skan while (i < argc) { 393169689Skan char const * assignment = strchr(argv[i], '='); 394169689Skan if (assignment != NULL && assignment != argv[i]) { 395169689Skan size_t var_len = (size_t) (assignment - argv[i] + (ptrdiff_t) 1); 396169689Skan char *var = (char *) calloc(var_len, sizeof(char)); 397169689Skan char *val = replace_cygdrive(assignment + 1); 398169689Skan memmove(var, argv[i], var_len); 399169689Skan var[var_len - 1] = '\0'; 400169689Skan strupr(var); 401169689Skan 402169689Skan if (getenv("DEBUG_FIXPATH") != NULL) { 403169689Skan fprintf(stderr, "fixpath setting var >%s< to >%s<\n", var, val); 404169689Skan } 405169689Skan 406169689Skan rc = SetEnvironmentVariable(var, val); 407169689Skan if (!rc) { 408117395Skan // Could not set var for some reason. Try to report why. 409117395Skan const int msg_len = 80 + var_len + strlen(val); 410132718Skan char * msg = (char *) alloca(msg_len); 41196263Sobrien _snprintf_s(msg, msg_len, _TRUNCATE, "Could not set environment variable [%s=%s]", var, val); 412169689Skan report_error(msg); 41396263Sobrien exit(1); 414169689Skan } 41596263Sobrien free(var); 41696263Sobrien free(val); 41796263Sobrien } else { 41896263Sobrien // no more assignments; 419169689Skan break; 420169689Skan } 421169689Skan i++; 422169689Skan } 423169689Skan 424169689Skan // remember index of the command 425169689Skan cmd = i; 426169689Skan 427169689Skan // handle command and it's args. 42896263Sobrien while (i < argc) { 429169689Skan char const *replaced = replace_cygdrive(argv[i]); 430169689Skan if (replaced[0] == '@') { 431169689Skan // Found at-file! Fix it! 43296263Sobrien replaced = fix_at_file(replaced); 43396263Sobrien } 43496263Sobrien argv[i] = quote_arg(replaced); 43596263Sobrien i++; 436169689Skan } 437169689Skan 438169689Skan // determine the length of the line 439169689Skan line = NULL; 440169689Skan // args 44196263Sobrien for (i = cmd; i < argc; i++) { 442169689Skan line += (ptrdiff_t) strlen(argv[i]); 443169689Skan } 444169689Skan // spaces and null 445169689Skan line += (ptrdiff_t) (argc - cmd + 1); 446169689Skan // allocate 447169689Skan line = (char*) calloc(line - (char*) NULL, sizeof(char)); 448169689Skan 449169689Skan // copy in args. 450169689Skan current = line; 451169689Skan for (i = cmd; i < argc; i++) { 452169689Skan ptrdiff_t len = strlen(argv[i]); 45396263Sobrien if (i != cmd) { 45496263Sobrien *current++ = ' '; 455169689Skan } 456169689Skan memmove(current, argv[i], len); 457169689Skan current += len; 458169689Skan } 459169689Skan *current = '\0'; 460169689Skan 46196263Sobrien if (getenv("DEBUG_FIXPATH") != NULL) { 46252284Sobrien fprintf(stderr, "fixpath converted line >%s<\n", line); 463169689Skan } 464117395Skan 465132718Skan if (cmd == argc) { 466117395Skan if (getenv("DEBUG_FIXPATH") != NULL) { 467169689Skan fprintf(stderr, "fixpath no command provided!\n"); 468169689Skan } 469169689Skan exit(0); 470169689Skan } 471169689Skan 472169689Skan ZeroMemory(&si, sizeof(si)); 473169689Skan si.cb=sizeof(si); 474169689Skan ZeroMemory(&pi, sizeof(pi)); 475169689Skan 476117395Skan fflush(stderr); 477117395Skan fflush(stdout); 478117395Skan 479117395Skan rc = CreateProcess(NULL, 480117395Skan line, 481117395Skan 0, 48296263Sobrien 0, 48396263Sobrien TRUE, 484169689Skan 0, 48596263Sobrien NULL, 48696263Sobrien NULL, 48796263Sobrien &si, 48896263Sobrien &pi); 489169689Skan if (!rc) { 490169689Skan // Could not start process for some reason. Try to report why: 491169689Skan report_error("Could not start process!"); 49296263Sobrien exit(126); 493169689Skan } 49496263Sobrien 495169689Skan WaitForSingleObject(pi.hProcess, INFINITE); 496169689Skan GetExitCodeProcess(pi.hProcess, &exitCode); 497169689Skan 498169689Skan if (getenv("DEBUG_FIXPATH") != NULL) { 499169689Skan for (i=0; i<num_files_to_delete; ++i) { 500169689Skan fprintf(stderr, "fixpath Not deleting temporary file %s\n", 50196263Sobrien files_to_delete[i]); 50296263Sobrien } 503132718Skan } else { 50496263Sobrien for (i=0; i<num_files_to_delete; ++i) { 50596263Sobrien remove(files_to_delete[i]); 50696263Sobrien } 507132718Skan } 50896263Sobrien 509169689Skan if (exitCode != 0) { 51096263Sobrien if (getenv("DEBUG_FIXPATH") != NULL) { 511169689Skan fprintf(stderr, "fixpath exit code %d\n", 512169689Skan exitCode); 513169689Skan } 514169689Skan } 515169689Skan 51696263Sobrien exit(exitCode); 51796263Sobrien} 518169689Skan