make/src/native/fixpath.c
changeset 47216 71c04702a3d5
parent 34101 a1db3b482137
child 53110 50677f43ac3d
child 57074 12615de8335e
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 #include <Windows.h>
       
    27 #include <io.h>
       
    28 #include <stdio.h>
       
    29 #include <string.h>
       
    30 #include <malloc.h>
       
    31 
       
    32 void report_error(char const * msg)
       
    33 {
       
    34   LPVOID lpMsgBuf;
       
    35   DWORD dw = GetLastError();
       
    36 
       
    37   FormatMessage(
       
    38       FORMAT_MESSAGE_ALLOCATE_BUFFER |
       
    39       FORMAT_MESSAGE_FROM_SYSTEM |
       
    40       FORMAT_MESSAGE_IGNORE_INSERTS,
       
    41       NULL,
       
    42       dw,
       
    43       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
       
    44       (LPTSTR) &lpMsgBuf,
       
    45       0,
       
    46       NULL);
       
    47 
       
    48   fprintf(stderr,
       
    49           "%s  Failed with error %d: %s\n",
       
    50           msg, dw, lpMsgBuf);
       
    51 
       
    52   LocalFree(lpMsgBuf);
       
    53 }
       
    54 
       
    55 /*
       
    56  * Test if pos points to /cygdrive/_/ where _ can
       
    57  * be any character.
       
    58  */
       
    59 int is_cygdrive_here(int pos, char const *in, int len)
       
    60 {
       
    61   // Length of /cygdrive/c/ is 12
       
    62   if (pos+12 > len) return 0;
       
    63   if (in[pos+11]=='/' &&
       
    64       in[pos+9]=='/' &&
       
    65       in[pos+8]=='e' &&
       
    66       in[pos+7]=='v' &&
       
    67       in[pos+6]=='i' &&
       
    68       in[pos+5]=='r' &&
       
    69       in[pos+4]=='d' &&
       
    70       in[pos+3]=='g' &&
       
    71       in[pos+2]=='y' &&
       
    72       in[pos+1]=='c' &&
       
    73       in[pos+0]=='/') {
       
    74     return 1;
       
    75   }
       
    76   return 0;
       
    77 }
       
    78 
       
    79 /*
       
    80  * Replace /cygdrive/_/ with _:/
       
    81  * Works in place since drive letter is always
       
    82  * shorter than /cygdrive/
       
    83  */
       
    84 char *replace_cygdrive_cygwin(char const *in)
       
    85 {
       
    86   size_t len = strlen(in);
       
    87   char *out = (char*) malloc(len+1);
       
    88   int i,j;
       
    89 
       
    90   if (len < 12) {
       
    91     memmove(out, in, len + 1);
       
    92     return out;
       
    93   }
       
    94 
       
    95   for (i = 0, j = 0; i<len;) {
       
    96     if (is_cygdrive_here(i, in, len)) {
       
    97       out[j++] = in[i+10];
       
    98       out[j++] = ':';
       
    99       i+=11;
       
   100     } else {
       
   101       out[j] = in[i];
       
   102       i++;
       
   103       j++;
       
   104     }
       
   105   }
       
   106   out[j] = '\0';
       
   107   return out;
       
   108 }
       
   109 
       
   110 void append(char **b, size_t *bl, size_t *u, char *add, size_t addlen)
       
   111 {
       
   112   while ((addlen+*u+1) > *bl) {
       
   113     *bl *= 2;
       
   114     *b = (char*) realloc(*b, *bl);
       
   115   }
       
   116   memcpy(*b+*u, add, addlen);
       
   117   *u += addlen;
       
   118 }
       
   119 
       
   120 /*
       
   121  * Creates a new string from in where the first occurrence of sub is
       
   122  * replaced by rep.
       
   123  */
       
   124 char *replace_substring(char *in, char *sub, char *rep)
       
   125 {
       
   126   int in_len = strlen(in);
       
   127   int sub_len = strlen(sub);
       
   128   int rep_len = strlen(rep);
       
   129   char *out = (char *) malloc(in_len - sub_len + rep_len + 1);
       
   130   char *p;
       
   131 
       
   132   if (!(p = strstr(in, sub))) {
       
   133     // If sub isn't a substring of in, just return in.
       
   134     return in;
       
   135   }
       
   136 
       
   137   // Copy characters from beginning of in to start of sub.
       
   138   strncpy(out, in, p - in);
       
   139   out[p - in] = '\0';
       
   140 
       
   141   sprintf(out + (p - in), "%s%s", rep, p + sub_len);
       
   142 
       
   143   return out;
       
   144 }
       
   145 
       
   146 char* msys_path_list; // @-separated list of paths prefix to look for
       
   147 char* msys_path_list_end; // Points to last \0 in msys_path_list.
       
   148 
       
   149 void setup_msys_path_list(char const * argument)
       
   150 {
       
   151   char* p;
       
   152   char* drive_letter_pos;
       
   153 
       
   154   msys_path_list = strdup(&argument[2]);
       
   155   msys_path_list_end = &msys_path_list[strlen(msys_path_list)];
       
   156 
       
   157   // Convert all at-sign (@) in path list to \0.
       
   158   // @ was chosen as separator to minimize risk of other tools messing around with it
       
   159   p = msys_path_list;
       
   160   do {
       
   161     if (p[1] == ':') {
       
   162       // msys has mangled our path list, restore it from c:/... to /c/...
       
   163       drive_letter_pos = p+1;
       
   164       *drive_letter_pos = *p;
       
   165       *p = '/';
       
   166     }
       
   167 
       
   168     // Look for an @ in the list
       
   169     p = strchr(p, '@');
       
   170     if (p != NULL) {
       
   171       *p = '\0';
       
   172       p++;
       
   173     }
       
   174   } while (p != NULL);
       
   175 }
       
   176 
       
   177 char *replace_cygdrive_msys(char const *in)
       
   178 {
       
   179   char* str;
       
   180   char* prefix;
       
   181   char* p;
       
   182 
       
   183   str = strdup(in);
       
   184 
       
   185   // For each prefix in the path list, search for it and replace /c/... with c:/...
       
   186   for (prefix = msys_path_list; prefix < msys_path_list_end && prefix != NULL; prefix += strlen(prefix)+1) {
       
   187     p=str;
       
   188     while ((p = strstr(p, prefix))) {
       
   189       char* drive_letter = p+1;
       
   190       *p = *drive_letter;
       
   191       *drive_letter = ':';
       
   192       p++;
       
   193     }
       
   194   }
       
   195 
       
   196   return str;
       
   197 }
       
   198 
       
   199 char*(*replace_cygdrive)(char const *in) = NULL;
       
   200 
       
   201 char *files_to_delete[1024];
       
   202 int num_files_to_delete = 0;
       
   203 
       
   204 char *fix_at_file(char const *in)
       
   205 {
       
   206   char *tmpdir;
       
   207   char name[2048];
       
   208   char *atname;
       
   209   char *buffer;
       
   210   size_t buflen=65536;
       
   211   size_t used=0;
       
   212   size_t len;
       
   213   int rc;
       
   214   FILE *atout;
       
   215   FILE *atin;
       
   216   char block[2048];
       
   217   size_t blocklen;
       
   218   char *fixed;
       
   219 
       
   220   atin = fopen(in+1, "r");
       
   221   if (atin == NULL) {
       
   222     fprintf(stderr, "Could not read at file %s\n", in+1);
       
   223     exit(-1);
       
   224   }
       
   225 
       
   226   tmpdir = getenv("TEMP");
       
   227   if (tmpdir == NULL) {
       
   228 #if _WIN64
       
   229     tmpdir = "c:/cygwin64/tmp";
       
   230 #else
       
   231     tmpdir = "c:/cygwin/tmp";
       
   232 #endif
       
   233   }
       
   234   _snprintf(name, sizeof(name), "%s\\atfile_XXXXXX", tmpdir);
       
   235 
       
   236   rc = _mktemp_s(name, strlen(name)+1);
       
   237   if (rc) {
       
   238     fprintf(stderr, "Could not create temporary file name for at file!\n");
       
   239     exit(-1);
       
   240   }
       
   241 
       
   242   atout = fopen(name, "w");
       
   243   if (atout == NULL) {
       
   244     fprintf(stderr, "Could not open temporary file for writing! %s\n", name);
       
   245     exit(-1);
       
   246   }
       
   247 
       
   248   buffer = (char*) malloc(buflen);
       
   249   while ((blocklen = fread(block, 1, sizeof(block), atin)) > 0) {
       
   250     append(&buffer, &buflen, &used, block, blocklen);
       
   251   }
       
   252   buffer[used] = 0;
       
   253   if (getenv("DEBUG_FIXPATH") != NULL) {
       
   254     fprintf(stderr, "fixpath input from @-file %s: %s\n", &in[1], buffer);
       
   255   }
       
   256   fixed = replace_cygdrive(buffer);
       
   257   if (getenv("DEBUG_FIXPATH") != NULL) {
       
   258     fprintf(stderr, "fixpath converted to @-file %s is: %s\n", name, fixed);
       
   259   }
       
   260   fwrite(fixed, strlen(fixed), 1, atout);
       
   261   fclose(atin);
       
   262   fclose(atout);
       
   263   free(fixed);
       
   264   free(buffer);
       
   265   files_to_delete[num_files_to_delete] = (char*) malloc(strlen(name)+1);
       
   266   strcpy(files_to_delete[num_files_to_delete], name);
       
   267   num_files_to_delete++;
       
   268   atname = (char*) malloc(strlen(name)+2);
       
   269   atname[0] = '@';
       
   270   strcpy(atname+1, name);
       
   271   return atname;
       
   272 }
       
   273 
       
   274 // given an argument, convert it to the windows command line safe quoted version
       
   275 // using rules from:
       
   276 // http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
       
   277 // caller is responsible for freeing both input and output.
       
   278 char * quote_arg(char const * in_arg) {
       
   279   char *quoted = NULL;
       
   280   char *current = quoted;
       
   281   int pass;
       
   282 
       
   283   if (strlen(in_arg) == 0) {
       
   284      // empty string? explicitly quote it.
       
   285      return _strdup("\"\"");
       
   286   }
       
   287 
       
   288   if (strpbrk(in_arg, " \t\n\v\r\\\"") == NULL) {
       
   289      return _strdup(in_arg);
       
   290   }
       
   291 
       
   292   // process the arg twice. Once to calculate the size and then to copy it.
       
   293   for (pass=1; pass<=2; pass++) {
       
   294     char const *arg = in_arg;
       
   295 
       
   296     // initial "
       
   297     if (pass == 2) {
       
   298       *current = '\"';
       
   299     }
       
   300     current++;
       
   301 
       
   302     // process string to be quoted until NUL
       
   303     do {
       
   304       int escapes = 0;
       
   305 
       
   306       while (*arg == '\\') {
       
   307         // count escapes.
       
   308         escapes++;
       
   309         arg++;
       
   310       }
       
   311 
       
   312       if (*arg == '\0') {
       
   313          // escape the escapes before final "
       
   314          escapes *= 2;
       
   315       } else if (*arg == '"') {
       
   316         // escape the escapes and the "
       
   317         escapes = escapes * 2 + 1;
       
   318       } else {
       
   319          // escapes aren't special, just echo them.
       
   320       }
       
   321 
       
   322       // emit some escapes
       
   323       while (escapes > 0) {
       
   324         if (pass == 2) {
       
   325           *current = '\\';
       
   326         }
       
   327         current++;
       
   328         escapes--;
       
   329       }
       
   330 
       
   331       // and the current char
       
   332       if (pass == 2) {
       
   333         *current = *arg;
       
   334       }
       
   335       current++;
       
   336     } while (*arg++ != '\0');
       
   337 
       
   338     // allocate the buffer
       
   339     if (pass == 1) {
       
   340       size_t alloc = (size_t) (current - quoted + (ptrdiff_t) 2);
       
   341       current = quoted = (char*) calloc(alloc, sizeof(char));
       
   342     }
       
   343   }
       
   344 
       
   345   // final " and \0
       
   346   *(current - 1) = '"';
       
   347   *current = '\0';
       
   348 
       
   349   return quoted;
       
   350 }
       
   351 
       
   352 int main(int argc, char const ** argv)
       
   353 {
       
   354     STARTUPINFO si;
       
   355     PROCESS_INFORMATION pi;
       
   356     unsigned short rc;
       
   357 
       
   358     char *line;
       
   359     char *current;
       
   360     int i, cmd;
       
   361     DWORD exitCode = 0;
       
   362     DWORD processFlags = 0;
       
   363     BOOL processInheritHandles = TRUE;
       
   364     BOOL waitForChild = TRUE;
       
   365 
       
   366     if (argc<2 || argv[1][0] != '-' || (argv[1][1] != 'c' && argv[1][1] != 'm')) {
       
   367         fprintf(stderr, "Usage: fixpath -c|m<path@path@...> [--detach] /cygdrive/c/WINDOWS/notepad.exe [/cygdrive/c/x/test.txt|@/cygdrive/c/x/atfile]\n");
       
   368         exit(0);
       
   369     }
       
   370 
       
   371     if (getenv("DEBUG_FIXPATH") != NULL) {
       
   372       char const * cmdline = GetCommandLine();
       
   373       fprintf(stderr, "fixpath input line >%s<\n", strstr(cmdline, argv[1]));
       
   374     }
       
   375 
       
   376     if (argv[1][1] == 'c' && argv[1][2] == '\0') {
       
   377       if (getenv("DEBUG_FIXPATH") != NULL) {
       
   378         fprintf(stderr, "fixpath using cygwin mode\n");
       
   379       }
       
   380       replace_cygdrive = replace_cygdrive_cygwin;
       
   381     } else if (argv[1][1] == 'm') {
       
   382       if (getenv("DEBUG_FIXPATH") != NULL) {
       
   383         fprintf(stderr, "fixpath using msys mode, with path list: %s\n", &argv[1][2]);
       
   384       }
       
   385       setup_msys_path_list(argv[1]);
       
   386       replace_cygdrive = replace_cygdrive_msys;
       
   387     } else {
       
   388       fprintf(stderr, "fixpath Unknown mode: %s\n", argv[1]);
       
   389       exit(-1);
       
   390     }
       
   391 
       
   392     if (argv[2][0] == '-') {
       
   393       if (strcmp(argv[2], "--detach") == 0) {
       
   394         if (getenv("DEBUG_FIXPATH") != NULL) {
       
   395           fprintf(stderr, "fixpath in detached mode\n");
       
   396         }
       
   397         processFlags |= DETACHED_PROCESS;
       
   398         processInheritHandles = FALSE;
       
   399         waitForChild = FALSE;
       
   400       } else {
       
   401         fprintf(stderr, "fixpath Unknown argument: %s\n", argv[2]);
       
   402         exit(-1);
       
   403       }
       
   404       i = 3;
       
   405     } else {
       
   406       i = 2;
       
   407     }
       
   408 
       
   409     // handle assignments
       
   410     while (i < argc) {
       
   411       char const * assignment = strchr(argv[i], '=');
       
   412       if (assignment != NULL && assignment != argv[i]) {
       
   413         size_t var_len = (size_t) (assignment - argv[i] + (ptrdiff_t) 1);
       
   414         char *var = (char *) calloc(var_len, sizeof(char));
       
   415         char *val = replace_cygdrive(assignment + 1);
       
   416         memmove(var, argv[i], var_len);
       
   417         var[var_len - 1] = '\0';
       
   418         strupr(var);
       
   419 
       
   420         if (getenv("DEBUG_FIXPATH") != NULL) {
       
   421           fprintf(stderr, "fixpath setting var >%s< to >%s<\n", var, val);
       
   422         }
       
   423 
       
   424         rc = SetEnvironmentVariable(var, val);
       
   425         if (!rc) {
       
   426           // Could not set var for some reason.  Try to report why.
       
   427           const int msg_len = 80 + var_len + strlen(val);
       
   428           char * msg = (char *) alloca(msg_len);
       
   429           _snprintf_s(msg, msg_len, _TRUNCATE, "Could not set environment variable [%s=%s]", var, val);
       
   430           report_error(msg);
       
   431           exit(1);
       
   432         }
       
   433         free(var);
       
   434         free(val);
       
   435       } else {
       
   436         // no more assignments;
       
   437         break;
       
   438       }
       
   439       i++;
       
   440     }
       
   441 
       
   442     // remember index of the command
       
   443     cmd = i;
       
   444 
       
   445     // handle command and it's args.
       
   446     while (i < argc) {
       
   447       char const *replaced = replace_cygdrive(argv[i]);
       
   448       if (replaced[0] == '@') {
       
   449         if (waitForChild == FALSE) {
       
   450           fprintf(stderr, "fixpath Cannot use @-files in detached mode: %s\n", replaced);
       
   451           exit(1);
       
   452         }
       
   453         // Found at-file! Fix it!
       
   454         replaced = fix_at_file(replaced);
       
   455       }
       
   456       argv[i] = quote_arg(replaced);
       
   457       i++;
       
   458     }
       
   459 
       
   460     // determine the length of the line
       
   461     line = NULL;
       
   462     // args
       
   463     for (i = cmd; i < argc; i++) {
       
   464       line += (ptrdiff_t) strlen(argv[i]);
       
   465     }
       
   466     // spaces and null
       
   467     line += (ptrdiff_t) (argc - cmd + 1);
       
   468     // allocate
       
   469     line = (char*) calloc(line - (char*) NULL, sizeof(char));
       
   470 
       
   471     // copy in args.
       
   472     current = line;
       
   473     for (i = cmd; i < argc; i++) {
       
   474       ptrdiff_t len = strlen(argv[i]);
       
   475       if (i != cmd) {
       
   476         *current++ = ' ';
       
   477       }
       
   478       memmove(current, argv[i], len);
       
   479       current += len;
       
   480     }
       
   481     *current = '\0';
       
   482 
       
   483     if (getenv("DEBUG_FIXPATH") != NULL) {
       
   484       fprintf(stderr, "fixpath converted line >%s<\n", line);
       
   485     }
       
   486 
       
   487     if (cmd == argc) {
       
   488        if (getenv("DEBUG_FIXPATH") != NULL) {
       
   489          fprintf(stderr, "fixpath no command provided!\n");
       
   490        }
       
   491        exit(0);
       
   492     }
       
   493 
       
   494     ZeroMemory(&si, sizeof(si));
       
   495     si.cb=sizeof(si);
       
   496     ZeroMemory(&pi, sizeof(pi));
       
   497 
       
   498     fflush(stderr);
       
   499     fflush(stdout);
       
   500 
       
   501     rc = CreateProcess(NULL,
       
   502                        line,
       
   503                        0,
       
   504                        0,
       
   505                        processInheritHandles,
       
   506                        processFlags,
       
   507                        NULL,
       
   508                        NULL,
       
   509                        &si,
       
   510                        &pi);
       
   511     if (!rc) {
       
   512       // Could not start process for some reason.  Try to report why:
       
   513       report_error("Could not start process!");
       
   514       exit(126);
       
   515     }
       
   516 
       
   517     if (waitForChild == TRUE) {
       
   518       WaitForSingleObject(pi.hProcess, INFINITE);
       
   519       GetExitCodeProcess(pi.hProcess, &exitCode);
       
   520 
       
   521       if (getenv("DEBUG_FIXPATH") != NULL) {
       
   522         for (i=0; i<num_files_to_delete; ++i) {
       
   523           fprintf(stderr, "fixpath Not deleting temporary file %s\n",
       
   524                   files_to_delete[i]);
       
   525         }
       
   526       } else {
       
   527         for (i=0; i<num_files_to_delete; ++i) {
       
   528           remove(files_to_delete[i]);
       
   529         }
       
   530       }
       
   531 
       
   532       if (exitCode != 0) {
       
   533         if (getenv("DEBUG_FIXPATH") != NULL) {
       
   534           fprintf(stderr, "fixpath exit code %d\n",
       
   535                   exitCode);
       
   536         }
       
   537       }
       
   538     } else {
       
   539       if (getenv("DEBUG_FIXPATH") != NULL) {
       
   540         fprintf(stderr, "fixpath Not waiting for child process");
       
   541       }
       
   542     }
       
   543 
       
   544     exit(exitCode);
       
   545 }