1 /*
   2  * Copyright (c) 1998, 2008, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 /*
  26  * Gamma (Hotspot internal engineering test) launcher based on 1.6.0-b28 JDK,
  27  * search "GAMMA" for gamma specific changes.
  28  *
  29  * GAMMA: gamma launcher is much simpler than regular java launcher in that
  30  *        JVM is either statically linked in or it is installed in the
  31  *        same directory where the launcher exists, so we don't have to
  32  *        worry about choosing the right JVM based on command line flag, jar
  33  *        file and/or ergonomics. Intead of removing unused logic from source
  34  *        they are commented out with #ifndef GAMMA, hopefully it'll be easier
  35  *        to maintain this file in sync with regular JDK launcher.
  36  */
  37 
  38 /*
  39  * Shared source for 'java' command line tool.
  40  *
  41  * If JAVA_ARGS is defined, then acts as a launcher for applications. For
  42  * instance, the JDK command line tools such as javac and javadoc (see
  43  * makefiles for more details) are built with this program.  Any arguments
  44  * prefixed with '-J' will be passed directly to the 'java' command.
  45  */
  46 
  47 #ifdef GAMMA
  48 #  ifdef JAVA_ARGS
  49 #    error Do NOT define JAVA_ARGS when building gamma launcher
  50 #  endif
  51 #  if !defined(LINK_INTO_AOUT) && !defined(LINK_INTO_LIBJVM)
  52 #    error Either LINK_INTO_AOUT or LINK_INTO_LIBJVM must be defined
  53 #  endif
  54 #endif
  55 
  56 /*
  57  * One job of the launcher is to remove command line options which the
  58  * vm does not understand and will not process.  These options include
  59  * options which select which style of vm is run (e.g. -client and
  60  * -server) as well as options which select the data model to use.
  61  * Additionally, for tools which invoke an underlying vm "-J-foo"
  62  * options are turned into "-foo" options to the vm.  This option
  63  * filtering is handled in a number of places in the launcher, some of
  64  * it in machine-dependent code.  In this file, the function
  65  * CheckJVMType removes vm style options and TranslateDashJArgs
  66  * removes "-J" prefixes.  On unix platforms, the
  67  * CreateExecutionEnvironment function from the unix java_md.c file
  68  * processes and removes -d<n> options.  However, in case
  69  * CreateExecutionEnvironment does not need to exec because
  70  * LD_LIBRARY_PATH is set acceptably and the data model does not need
  71  * to be changed, ParseArguments will screen out the redundant -d<n>
  72  * options and prevent them from being passed to the vm; this is done
  73  * by using the machine-dependent call
  74  * RemovableMachineDependentOption.
  75  */
  76 
  77 #include <stdio.h>
  78 #include <stdlib.h>
  79 #include <string.h>
  80 
  81 #include <jni.h>
  82 #include "java.h"
  83 
  84 #ifndef GAMMA
  85 #include "manifest_info.h"
  86 #include "version_comp.h"
  87 #endif
  88 
  89 #ifndef FULL_VERSION
  90 #define FULL_VERSION JDK_MAJOR_VERSION "." JDK_MINOR_VERSION
  91 #endif
  92 
  93 /*
  94  * The following environment variable is used to influence the behavior
  95  * of the jre exec'd through the SelectVersion routine.  The command line
  96  * options which specify the version are not passed to the exec'd version,
  97  * because that jre may be an older version which wouldn't recognize them.
  98  * This environment variable is known to this (and later) version and serves
  99  * to suppress the version selection code.  This is not only for efficiency,
 100  * but also for correctness, since any command line options have been
 101  * removed which would cause any value found in the manifest to be used.
 102  * This would be incorrect because the command line options are defined
 103  * to take precedence.
 104  *
 105  * The value associated with this environment variable is the MainClass
 106  * name from within the executable jar file (if any). This is strictly a
 107  * performance enhancement to avoid re-reading the jar file manifest.
 108  *
 109  * A NOTE TO DEVELOPERS: For performance reasons it is important that
 110  * the program image remain relatively small until after SelectVersion
 111  * CreateExecutionEnvironment have finished their possibly recursive
 112  * processing. Watch everything, but resist all temptations to use Java
 113  * interfaces.
 114  */
 115 #define ENV_ENTRY "_JAVA_VERSION_SET"
 116 
 117 static jboolean printVersion = JNI_FALSE; /* print and exit */
 118 static jboolean showVersion = JNI_FALSE;  /* print but continue */
 119 static char *progname;
 120 jboolean _launcher_debug = JNI_FALSE;
 121 
 122 /*
 123  * List of VM options to be specified when the VM is created.
 124  */
 125 static JavaVMOption *options;
 126 static int numOptions, maxOptions;
 127 
 128 /*
 129  * Prototypes for functions internal to launcher.
 130  */
 131 static void AddOption(char *str, void *info);
 132 static void SetClassPath(char *s);
 133 static void SelectVersion(int argc, char **argv, char **main_class);
 134 static jboolean ParseArguments(int *pargc, char ***pargv, char **pjarfile,
 135                                char **pclassname, int *pret);
 136 static jboolean InitializeJVM(JavaVM **pvm, JNIEnv **penv,
 137                               InvocationFunctions *ifn);
 138 static jstring NewPlatformString(JNIEnv *env, char *s);
 139 static jobjectArray NewPlatformStringArray(JNIEnv *env, char **strv, int strc);
 140 static jclass LoadClass(JNIEnv *env, char *name);
 141 static jstring GetMainClassName(JNIEnv *env, char *jarname);
 142 static void SetJavaCommandLineProp(char* classname, char* jarfile, int argc, char** argv);
 143 #ifdef GAMMA
 144 static void SetJavaLauncherProp(void);
 145 #endif
 146 
 147 #ifdef JAVA_ARGS
 148 static void TranslateDashJArgs(int *pargc, char ***pargv);
 149 static jboolean AddApplicationOptions(void);
 150 #endif
 151 
 152 static void PrintJavaVersion(JNIEnv *env);
 153 static void PrintUsage(void);
 154 static jint PrintXUsage(void);
 155 
 156 static void SetPaths(int argc, char **argv);
 157 
 158 /* Maximum supported entries from jvm.cfg. */
 159 #define INIT_MAX_KNOWN_VMS      10
 160 /* Values for vmdesc.flag */
 161 #define VM_UNKNOWN              -1
 162 #define VM_KNOWN                 0
 163 #define VM_ALIASED_TO            1
 164 #define VM_WARN                  2
 165 #define VM_ERROR                 3
 166 #define VM_IF_SERVER_CLASS       4
 167 #define VM_IGNORE                5
 168 struct vmdesc {
 169     char *name;
 170     int flag;
 171     char *alias;
 172     char *server_class;
 173 };
 174 static struct vmdesc *knownVMs = NULL;
 175 static int knownVMsCount = 0;
 176 static int knownVMsLimit = 0;
 177 
 178 static void GrowKnownVMs();
 179 static int  KnownVMIndex(const char* name);
 180 static void FreeKnownVMs();
 181 
 182 jboolean ServerClassMachine();
 183 
 184 /* flag which if set suppresses error messages from the launcher */
 185 static int noExitErrorMessage = 0;
 186 
 187 /*
 188  * Entry point.
 189  */
 190 int
 191 main(int argc, char ** argv)
 192 {
 193     JavaVM *vm = 0;
 194     JNIEnv *env = 0;
 195     char *jarfile = 0;
 196     char *classname = 0;
 197     char *s = 0;
 198     char *main_class = NULL;
 199     jstring mainClassName;
 200     jclass mainClass;
 201     jmethodID mainID;
 202     jobjectArray mainArgs;
 203     int ret;
 204     InvocationFunctions ifn;
 205     jlong start, end;
 206     char jrepath[MAXPATHLEN], jvmpath[MAXPATHLEN];
 207     char ** original_argv = argv;
 208 
 209     /*
 210      * Error message to print or display; by default the message will
 211      * only be displayed in a window.
 212      */
 213     char * message = "Fatal exception occurred.  Program will exit.";
 214     jboolean messageDest = JNI_FALSE;
 215 
 216     if (getenv("_JAVA_LAUNCHER_DEBUG") != 0) {
 217         _launcher_debug = JNI_TRUE;
 218         printf("----_JAVA_LAUNCHER_DEBUG----\n");
 219     }
 220 
 221 #ifndef GAMMA
 222     /*
 223      * Make sure the specified version of the JRE is running.
 224      *
 225      * There are three things to note about the SelectVersion() routine:
 226      *  1) If the version running isn't correct, this routine doesn't
 227      *     return (either the correct version has been exec'd or an error
 228      *     was issued).
 229      *  2) Argc and Argv in this scope are *not* altered by this routine.
 230      *     It is the responsibility of subsequent code to ignore the
 231      *     arguments handled by this routine.
 232      *  3) As a side-effect, the variable "main_class" is guaranteed to
 233      *     be set (if it should ever be set).  This isn't exactly the
 234      *     poster child for structured programming, but it is a small
 235      *     price to pay for not processing a jar file operand twice.
 236      *     (Note: This side effect has been disabled.  See comment on
 237      *     bugid 5030265 below.)
 238      */
 239     SelectVersion(argc, argv, &main_class);
 240 #endif /* ifndef GAMMA */
 241 
 242     /* copy original argv */
 243     {
 244       int i;
 245       original_argv = (char**)MemAlloc(sizeof(char*)*(argc+1));
 246       for(i = 0; i < argc+1; i++)
 247         original_argv[i] = argv[i];
 248     }
 249 
 250     CreateExecutionEnvironment(&argc, &argv,
 251                                jrepath, sizeof(jrepath),
 252                                jvmpath, sizeof(jvmpath),
 253                                original_argv);
 254     ifn.CreateJavaVM = 0;
 255     ifn.GetDefaultJavaVMInitArgs = 0;
 256 
 257     if (_launcher_debug)
 258       start = CounterGet();
 259     if (!LoadJavaVM(jvmpath, &ifn)) {
 260       exit(6);
 261     }
 262     if (_launcher_debug) {
 263       end   = CounterGet();
 264       printf("%ld micro seconds to LoadJavaVM\n",
 265              (long)(jint)Counter2Micros(end-start));
 266     }
 267 
 268 #ifdef JAVA_ARGS  /* javac, jar and friends. */
 269     progname = "java";
 270 #else             /* java, oldjava, javaw and friends */
 271 #ifdef PROGNAME
 272     progname = PROGNAME;
 273 #else
 274     progname = *argv;
 275     if ((s = strrchr(progname, FILE_SEPARATOR)) != 0) {
 276         progname = s + 1;
 277     }
 278 #endif /* PROGNAME */
 279 #endif /* JAVA_ARGS */
 280     ++argv;
 281     --argc;
 282 
 283 #ifdef JAVA_ARGS
 284     /* Preprocess wrapper arguments */
 285     TranslateDashJArgs(&argc, &argv);
 286     if (!AddApplicationOptions()) {
 287         exit(1);
 288     }
 289 #endif
 290 
 291     /* Set default CLASSPATH */
 292     if ((s = getenv("CLASSPATH")) == 0) {
 293         s = ".";
 294     }
 295 #ifndef JAVA_ARGS
 296     SetClassPath(s);
 297 #endif
 298 
 299     /*
 300      *  Parse command line options; if the return value of
 301      *  ParseArguments is false, the program should exit.
 302      */
 303     if (!ParseArguments(&argc, &argv, &jarfile, &classname, &ret)) {
 304       exit(ret);
 305     }
 306 
 307     /* Override class path if -jar flag was specified */
 308     if (jarfile != 0) {
 309         SetClassPath(jarfile);
 310     }
 311 
 312     /* set the -Dsun.java.command pseudo property */
 313     SetJavaCommandLineProp(classname, jarfile, argc, argv);
 314 
 315 #ifdef GAMMA
 316     /* Set the -Dsun.java.launcher pseudo property */
 317     SetJavaLauncherProp();
 318 #endif
 319 
 320     /*
 321      * Done with all command line processing and potential re-execs so
 322      * clean up the environment.
 323      */
 324     (void)UnsetEnv(ENV_ENTRY);
 325 
 326     /* Initialize the virtual machine */
 327 
 328     if (_launcher_debug)
 329         start = CounterGet();
 330     if (!InitializeJVM(&vm, &env, &ifn)) {
 331         ReportErrorMessage("Could not create the Java virtual machine.",
 332                            JNI_TRUE);
 333         exit(1);
 334     }
 335 
 336     if (printVersion || showVersion) {
 337         PrintJavaVersion(env);
 338         if ((*env)->ExceptionOccurred(env)) {
 339             ReportExceptionDescription(env);
 340             goto leave;
 341         }
 342         if (printVersion) {
 343             ret = 0;
 344             message = NULL;
 345             goto leave;
 346         }
 347         if (showVersion) {
 348             fprintf(stderr, "\n");
 349         }
 350     }
 351 
 352     /* If the user specified neither a class name nor a JAR file */
 353     if (jarfile == 0 && classname == 0) {
 354         PrintUsage();
 355         message = NULL;
 356         goto leave;
 357     }
 358 
 359 #ifndef GAMMA
 360     FreeKnownVMs();  /* after last possible PrintUsage() */
 361 #endif
 362 
 363     if (_launcher_debug) {
 364         end   = CounterGet();
 365         printf("%ld micro seconds to InitializeJVM\n",
 366                (long)(jint)Counter2Micros(end-start));
 367     }
 368 
 369     /* At this stage, argc/argv have the applications' arguments */
 370     if (_launcher_debug) {
 371         int i = 0;
 372         printf("Main-Class is '%s'\n", classname ? classname : "");
 373         printf("Apps' argc is %d\n", argc);
 374         for (; i < argc; i++) {
 375             printf("    argv[%2d] = '%s'\n", i, argv[i]);
 376         }
 377     }
 378 
 379     ret = 1;
 380 
 381     /*
 382      * Get the application's main class.
 383      *
 384      * See bugid 5030265.  The Main-Class name has already been parsed
 385      * from the manifest, but not parsed properly for UTF-8 support.
 386      * Hence the code here ignores the value previously extracted and
 387      * uses the pre-existing code to reextract the value.  This is
 388      * possibly an end of release cycle expedient.  However, it has
 389      * also been discovered that passing some character sets through
 390      * the environment has "strange" behavior on some variants of
 391      * Windows.  Hence, maybe the manifest parsing code local to the
 392      * launcher should never be enhanced.
 393      *
 394      * Hence, future work should either:
 395      *     1)   Correct the local parsing code and verify that the
 396      *          Main-Class attribute gets properly passed through
 397      *          all environments,
 398      *     2)   Remove the vestages of maintaining main_class through
 399      *          the environment (and remove these comments).
 400      */
 401     if (jarfile != 0) {
 402         mainClassName = GetMainClassName(env, jarfile);
 403         if ((*env)->ExceptionOccurred(env)) {
 404             ReportExceptionDescription(env);
 405             goto leave;
 406         }
 407         if (mainClassName == NULL) {
 408           const char * format = "Failed to load Main-Class manifest "
 409                                 "attribute from\n%s";
 410           message = (char*)MemAlloc((strlen(format) + strlen(jarfile)) *
 411                                     sizeof(char));
 412           sprintf(message, format, jarfile);
 413           messageDest = JNI_TRUE;
 414           goto leave;
 415         }
 416         classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0);
 417         if (classname == NULL) {
 418             ReportExceptionDescription(env);
 419             goto leave;
 420         }
 421         mainClass = LoadClass(env, classname);
 422         if(mainClass == NULL) { /* exception occurred */
 423             ReportExceptionDescription(env);
 424             message = "Could not find the main class.  Program will exit.";
 425             goto leave;
 426         }
 427         (*env)->ReleaseStringUTFChars(env, mainClassName, classname);
 428     } else {
 429       mainClassName = NewPlatformString(env, classname);
 430       if (mainClassName == NULL) {
 431         const char * format = "Failed to load Main Class: %s";
 432         message = (char *)MemAlloc((strlen(format) + strlen(classname)) *
 433                                    sizeof(char) );
 434         sprintf(message, format, classname);
 435         messageDest = JNI_TRUE;
 436         goto leave;
 437       }
 438       classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0);
 439       if (classname == NULL) {
 440         ReportExceptionDescription(env);
 441         goto leave;
 442       }
 443       mainClass = LoadClass(env, classname);
 444       if(mainClass == NULL) { /* exception occurred */
 445         ReportExceptionDescription(env);
 446         message = "Could not find the main class. Program will exit.";
 447         goto leave;
 448       }
 449       (*env)->ReleaseStringUTFChars(env, mainClassName, classname);
 450     }
 451 
 452     /* Get the application's main method */
 453     mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
 454                                        "([Ljava/lang/String;)V");
 455     if (mainID == NULL) {
 456         if ((*env)->ExceptionOccurred(env)) {
 457             ReportExceptionDescription(env);
 458         } else {
 459           message = "No main method found in specified class.";
 460           messageDest = JNI_TRUE;
 461         }
 462         goto leave;
 463     }
 464 
 465     {    /* Make sure the main method is public */
 466         jint mods;
 467         jmethodID mid;
 468         jobject obj = (*env)->ToReflectedMethod(env, mainClass,
 469                                                 mainID, JNI_TRUE);
 470 
 471         if( obj == NULL) { /* exception occurred */
 472             ReportExceptionDescription(env);
 473             goto leave;
 474         }
 475 
 476         mid =
 477           (*env)->GetMethodID(env,
 478                               (*env)->GetObjectClass(env, obj),
 479                               "getModifiers", "()I");
 480         if ((*env)->ExceptionOccurred(env)) {
 481             ReportExceptionDescription(env);
 482             goto leave;
 483         }
 484 
 485         mods = (*env)->CallIntMethod(env, obj, mid);
 486         if ((mods & 1) == 0) { /* if (!Modifier.isPublic(mods)) ... */
 487             message = "Main method not public.";
 488             messageDest = JNI_TRUE;
 489             goto leave;
 490         }
 491     }
 492 
 493     /* Build argument array */
 494     mainArgs = NewPlatformStringArray(env, argv, argc);
 495     if (mainArgs == NULL) {
 496         ReportExceptionDescription(env);
 497         goto leave;
 498     }
 499 
 500     /* Invoke main method. */
 501     (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
 502 
 503     /*
 504      * The launcher's exit code (in the absence of calls to
 505      * System.exit) will be non-zero if main threw an exception.
 506      */
 507     ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
 508 
 509     /*
 510      * Detach the main thread so that it appears to have ended when
 511      * the application's main method exits.  This will invoke the
 512      * uncaught exception handler machinery if main threw an
 513      * exception.  An uncaught exception handler cannot change the
 514      * launcher's return code except by calling System.exit.
 515      */
 516     if ((*vm)->DetachCurrentThread(vm) != 0) {
 517         message = "Could not detach main thread.";
 518         messageDest = JNI_TRUE;
 519         ret = 1;
 520         goto leave;
 521     }
 522 
 523     message = NULL;
 524 
 525  leave:
 526     /*
 527      * Wait for all non-daemon threads to end, then destroy the VM.
 528      * This will actually create a trivial new Java waiter thread
 529      * named "DestroyJavaVM", but this will be seen as a different
 530      * thread from the one that executed main, even though they are
 531      * the same C thread.  This allows mainThread.join() and
 532      * mainThread.isAlive() to work as expected.
 533      */
 534     (*vm)->DestroyJavaVM(vm);
 535 
 536     if(message != NULL && !noExitErrorMessage)
 537       ReportErrorMessage(message, messageDest);
 538     return ret;
 539 }
 540 
 541 
 542 #ifndef GAMMA
 543 /*
 544  * Checks the command line options to find which JVM type was
 545  * specified.  If no command line option was given for the JVM type,
 546  * the default type is used.  The environment variable
 547  * JDK_ALTERNATE_VM and the command line option -XXaltjvm= are also
 548  * checked as ways of specifying which JVM type to invoke.
 549  */
 550 char *
 551 CheckJvmType(int *pargc, char ***argv, jboolean speculative) {
 552     int i, argi;
 553     int argc;
 554     char **newArgv;
 555     int newArgvIdx = 0;
 556     int isVMType;
 557     int jvmidx = -1;
 558     char *jvmtype = getenv("JDK_ALTERNATE_VM");
 559 
 560     argc = *pargc;
 561 
 562     /* To make things simpler we always copy the argv array */
 563     newArgv = MemAlloc((argc + 1) * sizeof(char *));
 564 
 565     /* The program name is always present */
 566     newArgv[newArgvIdx++] = (*argv)[0];
 567 
 568     for (argi = 1; argi < argc; argi++) {
 569         char *arg = (*argv)[argi];
 570         isVMType = 0;
 571 
 572 #ifdef JAVA_ARGS
 573         if (arg[0] != '-') {
 574             newArgv[newArgvIdx++] = arg;
 575             continue;
 576         }
 577 #else
 578         if (strcmp(arg, "-classpath") == 0 ||
 579             strcmp(arg, "-cp") == 0) {
 580             newArgv[newArgvIdx++] = arg;
 581             argi++;
 582             if (argi < argc) {
 583                 newArgv[newArgvIdx++] = (*argv)[argi];
 584             }
 585             continue;
 586         }
 587         if (arg[0] != '-') break;
 588 #endif
 589 
 590         /* Did the user pass an explicit VM type? */
 591         i = KnownVMIndex(arg);
 592         if (i >= 0) {
 593             jvmtype = knownVMs[jvmidx = i].name + 1; /* skip the - */
 594             isVMType = 1;
 595             *pargc = *pargc - 1;
 596         }
 597 
 598         /* Did the user specify an "alternate" VM? */
 599         else if (strncmp(arg, "-XXaltjvm=", 10) == 0 || strncmp(arg, "-J-XXaltjvm=", 12) == 0) {
 600             isVMType = 1;
 601             jvmtype = arg+((arg[1]=='X')? 10 : 12);
 602             jvmidx = -1;
 603         }
 604 
 605         if (!isVMType) {
 606             newArgv[newArgvIdx++] = arg;
 607         }
 608     }
 609 
 610     /*
 611      * Finish copying the arguments if we aborted the above loop.
 612      * NOTE that if we aborted via "break" then we did NOT copy the
 613      * last argument above, and in addition argi will be less than
 614      * argc.
 615      */
 616     while (argi < argc) {
 617         newArgv[newArgvIdx++] = (*argv)[argi];
 618         argi++;
 619     }
 620 
 621     /* argv is null-terminated */
 622     newArgv[newArgvIdx] = 0;
 623 
 624     /* Copy back argv */
 625     *argv = newArgv;
 626     *pargc = newArgvIdx;
 627 
 628     /* use the default VM type if not specified (no alias processing) */
 629     if (jvmtype == NULL) {
 630       char* result = knownVMs[0].name+1;
 631       /* Use a different VM type if we are on a server class machine? */
 632       if ((knownVMs[0].flag == VM_IF_SERVER_CLASS) &&
 633           (ServerClassMachine() == JNI_TRUE)) {
 634         result = knownVMs[0].server_class+1;
 635       }
 636       if (_launcher_debug) {
 637         printf("Default VM: %s\n", result);
 638       }
 639       return result;
 640     }
 641 
 642     /* if using an alternate VM, no alias processing */
 643     if (jvmidx < 0)
 644       return jvmtype;
 645 
 646     /* Resolve aliases first */
 647     {
 648       int loopCount = 0;
 649       while (knownVMs[jvmidx].flag == VM_ALIASED_TO) {
 650         int nextIdx = KnownVMIndex(knownVMs[jvmidx].alias);
 651 
 652         if (loopCount > knownVMsCount) {
 653           if (!speculative) {
 654             ReportErrorMessage("Error: Corrupt jvm.cfg file; cycle in alias list.",
 655                                JNI_TRUE);
 656             exit(1);
 657           } else {
 658             return "ERROR";
 659             /* break; */
 660           }
 661         }
 662 
 663         if (nextIdx < 0) {
 664           if (!speculative) {
 665             ReportErrorMessage2("Error: Unable to resolve VM alias %s",
 666                                 knownVMs[jvmidx].alias, JNI_TRUE);
 667             exit(1);
 668           } else {
 669             return "ERROR";
 670           }
 671         }
 672         jvmidx = nextIdx;
 673         jvmtype = knownVMs[jvmidx].name+1;
 674         loopCount++;
 675       }
 676     }
 677 
 678     switch (knownVMs[jvmidx].flag) {
 679     case VM_WARN:
 680         if (!speculative) {
 681             fprintf(stderr, "Warning: %s VM not supported; %s VM will be used\n",
 682                     jvmtype, knownVMs[0].name + 1);
 683         }
 684         /* fall through */
 685     case VM_IGNORE:
 686         jvmtype = knownVMs[jvmidx=0].name + 1;
 687         /* fall through */
 688     case VM_KNOWN:
 689         break;
 690     case VM_ERROR:
 691         if (!speculative) {
 692             ReportErrorMessage2("Error: %s VM not supported", jvmtype, JNI_TRUE);
 693             exit(1);
 694         } else {
 695             return "ERROR";
 696         }
 697     }
 698 
 699     return jvmtype;
 700 }
 701 #endif /* ifndef GAMMA */
 702 
 703 /*
 704  * Adds a new VM option with the given given name and value.
 705  */
 706 static void
 707 AddOption(char *str, void *info)
 708 {
 709     /*
 710      * Expand options array if needed to accommodate at least one more
 711      * VM option.
 712      */
 713     if (numOptions >= maxOptions) {
 714         if (options == 0) {
 715             maxOptions = 4;
 716             options = MemAlloc(maxOptions * sizeof(JavaVMOption));
 717         } else {
 718             JavaVMOption *tmp;
 719             maxOptions *= 2;
 720             tmp = MemAlloc(maxOptions * sizeof(JavaVMOption));
 721             memcpy(tmp, options, numOptions * sizeof(JavaVMOption));
 722             free(options);
 723             options = tmp;
 724         }
 725     }
 726     options[numOptions].optionString = str;
 727     options[numOptions++].extraInfo = info;
 728 }
 729 
 730 static void
 731 SetClassPath(char *s)
 732 {
 733     char *def = MemAlloc(strlen(s) + 40);
 734     sprintf(def, "-Djava.class.path=%s", s);
 735     AddOption(def, NULL);
 736 }
 737 
 738 #ifndef GAMMA
 739 /*
 740  * The SelectVersion() routine ensures that an appropriate version of
 741  * the JRE is running.  The specification for the appropriate version
 742  * is obtained from either the manifest of a jar file (preferred) or
 743  * from command line options.
 744  */
 745 static void
 746 SelectVersion(int argc, char **argv, char **main_class)
 747 {
 748     char    *arg;
 749     char    **new_argv;
 750     char    **new_argp;
 751     char    *operand;
 752     char    *version = NULL;
 753     char    *jre = NULL;
 754     int     jarflag = 0;
 755     int     restrict_search = -1;               /* -1 implies not known */
 756     manifest_info info;
 757     char    env_entry[MAXNAMELEN + 24] = ENV_ENTRY "=";
 758     char    *env_in;
 759     int     res;
 760 
 761     /*
 762      * If the version has already been selected, set *main_class
 763      * with the value passed through the environment (if any) and
 764      * simply return.
 765      */
 766     if ((env_in = getenv(ENV_ENTRY)) != NULL) {
 767         if (*env_in != '\0')
 768             *main_class = strdup(env_in);
 769         return;
 770     }
 771 
 772     /*
 773      * Scan through the arguments for options relevant to multiple JRE
 774      * support.  For reference, the command line syntax is defined as:
 775      *
 776      * SYNOPSIS
 777      *      java [options] class [argument...]
 778      *
 779      *      java [options] -jar file.jar [argument...]
 780      *
 781      * As the scan is performed, make a copy of the argument list with
 782      * the version specification options (new to 1.5) removed, so that
 783      * a version less than 1.5 can be exec'd.
 784      */
 785     new_argv = MemAlloc((argc + 1) * sizeof(char*));
 786     new_argv[0] = argv[0];
 787     new_argp = &new_argv[1];
 788     argc--;
 789     argv++;
 790     while ((arg = *argv) != 0 && *arg == '-') {
 791         if (strncmp(arg, "-version:", 9) == 0) {
 792             version = arg + 9;
 793         } else if (strcmp(arg, "-jre-restrict-search") == 0) {
 794             restrict_search = 1;
 795         } else if (strcmp(arg, "-no-jre-restrict-search") == 0) {
 796             restrict_search = 0;
 797         } else {
 798             if (strcmp(arg, "-jar") == 0)
 799                 jarflag = 1;
 800             /* deal with "unfortunate" classpath syntax */
 801             if ((strcmp(arg, "-classpath") == 0 || strcmp(arg, "-cp") == 0) &&
 802               (argc >= 2)) {
 803                 *new_argp++ = arg;
 804                 argc--;
 805                 argv++;
 806                 arg = *argv;
 807             }
 808             *new_argp++ = arg;
 809         }
 810         argc--;
 811         argv++;
 812     }
 813     if (argc <= 0) {    /* No operand? Possibly legit with -[full]version */
 814         operand = NULL;
 815     } else {
 816         argc--;
 817         *new_argp++ = operand = *argv++;
 818     }
 819     while (argc-- > 0)  /* Copy over [argument...] */
 820         *new_argp++ = *argv++;
 821     *new_argp = NULL;
 822 
 823     /*
 824      * If there is a jar file, read the manifest. If the jarfile can't be
 825      * read, the manifest can't be read from the jar file, or the manifest
 826      * is corrupt, issue the appropriate error messages and exit.
 827      *
 828      * Even if there isn't a jar file, construct a manifest_info structure
 829      * containing the command line information.  It's a convenient way to carry
 830      * this data around.
 831      */
 832     if (jarflag && operand) {
 833         if ((res = parse_manifest(operand, &info)) != 0) {
 834             if (res == -1)
 835                 ReportErrorMessage2("Unable to access jarfile %s",
 836                   operand, JNI_TRUE);
 837             else
 838                 ReportErrorMessage2("Invalid or corrupt jarfile %s",
 839                   operand, JNI_TRUE);
 840             exit(1);
 841         }
 842     } else {
 843         info.manifest_version = NULL;
 844         info.main_class = NULL;
 845         info.jre_version = NULL;
 846         info.jre_restrict_search = 0;
 847     }
 848 
 849     /*
 850      * The JRE-Version and JRE-Restrict-Search values (if any) from the
 851      * manifest are overwritten by any specified on the command line.
 852      */
 853     if (version != NULL)
 854         info.jre_version = version;
 855     if (restrict_search != -1)
 856         info.jre_restrict_search = restrict_search;
 857 
 858     /*
 859      * "Valid" returns (other than unrecoverable errors) follow.  Set
 860      * main_class as a side-effect of this routine.
 861      */
 862     if (info.main_class != NULL)
 863         *main_class = strdup(info.main_class);
 864 
 865     /*
 866      * If no version selection information is found either on the command
 867      * line or in the manifest, simply return.
 868      */
 869     if (info.jre_version == NULL) {
 870         free_manifest();
 871         free(new_argv);
 872         return;
 873     }
 874 
 875     /*
 876      * Check for correct syntax of the version specification (JSR 56).
 877      */
 878     if (!valid_version_string(info.jre_version)) {
 879         ReportErrorMessage2("Syntax error in version specification \"%s\"",
 880           info.jre_version, JNI_TRUE);
 881         exit(1);
 882     }
 883 
 884     /*
 885      * Find the appropriate JVM on the system. Just to be as forgiving as
 886      * possible, if the standard algorithms don't locate an appropriate
 887      * jre, check to see if the one running will satisfy the requirements.
 888      * This can happen on systems which haven't been set-up for multiple
 889      * JRE support.
 890      */
 891     jre = LocateJRE(&info);
 892     if (_launcher_debug)
 893         printf("JRE-Version = %s, JRE-Restrict-Search = %s Selected = %s\n",
 894           (info.jre_version?info.jre_version:"null"),
 895           (info.jre_restrict_search?"true":"false"), (jre?jre:"null"));
 896     if (jre == NULL) {
 897         if (acceptable_release(FULL_VERSION, info.jre_version)) {
 898             free_manifest();
 899             free(new_argv);
 900             return;
 901         } else {
 902             ReportErrorMessage2(
 903               "Unable to locate JRE meeting specification \"%s\"",
 904               info.jre_version, JNI_TRUE);
 905             exit(1);
 906         }
 907     }
 908 
 909     /*
 910      * If I'm not the chosen one, exec the chosen one.  Returning from
 911      * ExecJRE indicates that I am indeed the chosen one.
 912      *
 913      * The private environment variable _JAVA_VERSION_SET is used to
 914      * prevent the chosen one from re-reading the manifest file and
 915      * using the values found within to override the (potential) command
 916      * line flags stripped from argv (because the target may not
 917      * understand them).  Passing the MainClass value is an optimization
 918      * to avoid locating, expanding and parsing the manifest extra
 919      * times.
 920      */
 921     if (info.main_class != NULL)
 922         (void)strcat(env_entry, info.main_class);
 923     (void)putenv(env_entry);
 924     ExecJRE(jre, new_argv);
 925     free_manifest();
 926     free(new_argv);
 927     return;
 928 }
 929 #endif /* ifndef GAMMA */
 930 
 931 /*
 932  * Parses command line arguments.  Returns JNI_FALSE if launcher
 933  * should exit without starting vm (e.g. certain version and usage
 934  * options); returns JNI_TRUE if vm needs to be started to process
 935  * given options.  *pret (the launcher process return value) is set to
 936  * 0 for a normal exit.
 937  */
 938 static jboolean
 939 ParseArguments(int *pargc, char ***pargv, char **pjarfile,
 940                        char **pclassname, int *pret)
 941 {
 942     int argc = *pargc;
 943     char **argv = *pargv;
 944     jboolean jarflag = JNI_FALSE;
 945     char *arg;
 946 
 947     *pret = 1;
 948     while ((arg = *argv) != 0 && *arg == '-') {
 949         argv++; --argc;
 950         if (strcmp(arg, "-classpath") == 0 || strcmp(arg, "-cp") == 0) {
 951             if (argc < 1) {
 952                 ReportErrorMessage2("%s requires class path specification",
 953                                     arg, JNI_TRUE);
 954                 PrintUsage();
 955                 return JNI_FALSE;
 956             }
 957             SetClassPath(*argv);
 958             argv++; --argc;
 959         } else if (strcmp(arg, "-jar") == 0) {
 960             jarflag = JNI_TRUE;
 961         } else if (strcmp(arg, "-help") == 0 ||
 962                    strcmp(arg, "-h") == 0 ||
 963                    strcmp(arg, "-?") == 0) {
 964             PrintUsage();
 965             *pret = 0;
 966             return JNI_FALSE;
 967         } else if (strcmp(arg, "-version") == 0) {
 968             printVersion = JNI_TRUE;
 969             return JNI_TRUE;
 970         } else if (strcmp(arg, "-showversion") == 0) {
 971             showVersion = JNI_TRUE;
 972         } else if (strcmp(arg, "-X") == 0) {
 973             *pret = PrintXUsage();
 974             return JNI_FALSE;
 975 /*
 976  * The following case provide backward compatibility with old-style
 977  * command line options.
 978  */
 979         } else if (strcmp(arg, "-fullversion") == 0) {
 980             fprintf(stderr, "%s full version \"%s\"\n", progname,
 981                     FULL_VERSION);
 982             *pret = 0;
 983             return JNI_FALSE;
 984         } else if (strcmp(arg, "-verbosegc") == 0) {
 985             AddOption("-verbose:gc", NULL);
 986         } else if (strcmp(arg, "-t") == 0) {
 987             AddOption("-Xt", NULL);
 988         } else if (strcmp(arg, "-tm") == 0) {
 989             AddOption("-Xtm", NULL);
 990         } else if (strcmp(arg, "-debug") == 0) {
 991             AddOption("-Xdebug", NULL);
 992         } else if (strcmp(arg, "-noclassgc") == 0) {
 993             AddOption("-Xnoclassgc", NULL);
 994         } else if (strcmp(arg, "-Xfuture") == 0) {
 995             AddOption("-Xverify:all", NULL);
 996         } else if (strcmp(arg, "-verify") == 0) {
 997             AddOption("-Xverify:all", NULL);
 998         } else if (strcmp(arg, "-verifyremote") == 0) {
 999             AddOption("-Xverify:remote", NULL);
1000         } else if (strcmp(arg, "-noverify") == 0) {
1001             AddOption("-Xverify:none", NULL);
1002         } else if (strcmp(arg, "-XXsuppressExitMessage") == 0) {
1003             noExitErrorMessage = 1;
1004         } else if (strncmp(arg, "-prof", 5) == 0) {
1005             char *p = arg + 5;
1006             char *tmp = MemAlloc(strlen(arg) + 50);
1007             if (*p) {
1008                 sprintf(tmp, "-Xrunhprof:cpu=old,file=%s", p + 1);
1009             } else {
1010                 sprintf(tmp, "-Xrunhprof:cpu=old,file=java.prof");
1011             }
1012             AddOption(tmp, NULL);
1013         } else if (strncmp(arg, "-ss", 3) == 0 ||
1014                    strncmp(arg, "-oss", 4) == 0 ||
1015                    strncmp(arg, "-ms", 3) == 0 ||
1016                    strncmp(arg, "-mx", 3) == 0) {
1017             char *tmp = MemAlloc(strlen(arg) + 6);
1018             sprintf(tmp, "-X%s", arg + 1); /* skip '-' */
1019             AddOption(tmp, NULL);
1020         } else if (strcmp(arg, "-checksource") == 0 ||
1021                    strcmp(arg, "-cs") == 0 ||
1022                    strcmp(arg, "-noasyncgc") == 0) {
1023             /* No longer supported */
1024             fprintf(stderr,
1025                     "Warning: %s option is no longer supported.\n",
1026                     arg);
1027         } else if (strncmp(arg, "-version:", 9) == 0 ||
1028                    strcmp(arg, "-no-jre-restrict-search") == 0 ||
1029                    strcmp(arg, "-jre-restrict-search") == 0) {
1030             ; /* Ignore machine independent options already handled */
1031         } else if (RemovableMachineDependentOption(arg) ) {
1032             ; /* Do not pass option to vm. */
1033         }
1034         else {
1035             AddOption(arg, NULL);
1036         }
1037     }
1038 
1039     if (--argc >= 0) {
1040         if (jarflag) {
1041             *pjarfile = *argv++;
1042             *pclassname = 0;
1043         } else {
1044             *pjarfile = 0;
1045             *pclassname = *argv++;
1046         }
1047         *pargc = argc;
1048         *pargv = argv;
1049     }
1050 
1051     return JNI_TRUE;
1052 }
1053 
1054 /*
1055  * Initializes the Java Virtual Machine. Also frees options array when
1056  * finished.
1057  */
1058 static jboolean
1059 InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn)
1060 {
1061     JavaVMInitArgs args;
1062     jint r;
1063 
1064     memset(&args, 0, sizeof(args));
1065     args.version  = JNI_VERSION_1_2;
1066     args.nOptions = numOptions;
1067     args.options  = options;
1068     args.ignoreUnrecognized = JNI_FALSE;
1069 
1070     if (_launcher_debug) {
1071         int i = 0;
1072         printf("JavaVM args:\n    ");
1073         printf("version 0x%08lx, ", (long)args.version);
1074         printf("ignoreUnrecognized is %s, ",
1075                args.ignoreUnrecognized ? "JNI_TRUE" : "JNI_FALSE");
1076         printf("nOptions is %ld\n", (long)args.nOptions);
1077         for (i = 0; i < numOptions; i++)
1078             printf("    option[%2d] = '%s'\n",
1079                    i, args.options[i].optionString);
1080     }
1081 
1082     r = ifn->CreateJavaVM(pvm, (void **)penv, &args);
1083     free(options);
1084     return r == JNI_OK;
1085 }
1086 
1087 
1088 #define NULL_CHECK0(e) if ((e) == 0) return 0
1089 #define NULL_CHECK(e) if ((e) == 0) return
1090 
1091 /*
1092  * Returns a pointer to a block of at least 'size' bytes of memory.
1093  * Prints error message and exits if the memory could not be allocated.
1094  */
1095 void *
1096 MemAlloc(size_t size)
1097 {
1098     void *p = malloc(size);
1099     if (p == 0) {
1100         perror("malloc");
1101         exit(1);
1102     }
1103     return p;
1104 }
1105 
1106 static jstring platformEncoding = NULL;
1107 static jstring getPlatformEncoding(JNIEnv *env) {
1108     if (platformEncoding == NULL) {
1109         jstring propname = (*env)->NewStringUTF(env, "sun.jnu.encoding");
1110         if (propname) {
1111             jclass cls;
1112             jmethodID mid;
1113             NULL_CHECK0 (cls = FindBootStrapClass(env, "java/lang/System"));
1114             NULL_CHECK0 (mid = (*env)->GetStaticMethodID(
1115                                    env, cls,
1116                                    "getProperty",
1117                                    "(Ljava/lang/String;)Ljava/lang/String;"));
1118             platformEncoding = (*env)->CallStaticObjectMethod (
1119                                     env, cls, mid, propname);
1120         }
1121     }
1122     return platformEncoding;
1123 }
1124 
1125 static jboolean isEncodingSupported(JNIEnv *env, jstring enc) {
1126     jclass cls;
1127     jmethodID mid;
1128     NULL_CHECK0 (cls = FindBootStrapClass(env, "java/nio/charset/Charset"));
1129     NULL_CHECK0 (mid = (*env)->GetStaticMethodID(
1130                            env, cls,
1131                            "isSupported",
1132                            "(Ljava/lang/String;)Z"));
1133     return (jboolean)(*env)->CallStaticObjectMethod (env, cls, mid, enc);
1134 }
1135 
1136 /*
1137  * Returns a new Java string object for the specified platform string.
1138  */
1139 static jstring
1140 NewPlatformString(JNIEnv *env, char *s)
1141 {
1142     int len = (int)strlen(s);
1143     jclass cls;
1144     jmethodID mid;
1145     jbyteArray ary;
1146     jstring enc;
1147 
1148     if (s == NULL)
1149         return 0;
1150     enc = getPlatformEncoding(env);
1151 
1152     ary = (*env)->NewByteArray(env, len);
1153     if (ary != 0) {
1154         jstring str = 0;
1155         (*env)->SetByteArrayRegion(env, ary, 0, len, (jbyte *)s);
1156         if (!(*env)->ExceptionOccurred(env)) {
1157 #ifdef GAMMA
1158             /* We support running JVM with older JDK, so here we have to deal */
1159             /* with the case that sun.jnu.encoding is undefined (enc == NULL) */
1160             if (enc != NULL && isEncodingSupported(env, enc) == JNI_TRUE) {
1161 #else
1162             if (isEncodingSupported(env, enc) == JNI_TRUE) {
1163 #endif
1164                 NULL_CHECK0(cls = FindBootStrapClass(env, "java/lang/String"));
1165                 NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "<init>",
1166                                           "([BLjava/lang/String;)V"));
1167                 str = (*env)->NewObject(env, cls, mid, ary, enc);
1168             } else {
1169                 /*If the encoding specified in sun.jnu.encoding is not
1170                   endorsed by "Charset.isSupported" we have to fall back
1171                   to use String(byte[]) explicitly here without specifying
1172                   the encoding name, in which the StringCoding class will
1173                   pickup the iso-8859-1 as the fallback converter for us.
1174                 */
1175                 NULL_CHECK0(cls = FindBootStrapClass(env, "java/lang/String"));
1176                 NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "<init>",
1177                                           "([B)V"));
1178                 str = (*env)->NewObject(env, cls, mid, ary);
1179             }
1180             (*env)->DeleteLocalRef(env, ary);
1181             return str;
1182         }
1183     }
1184     return 0;
1185 }
1186 
1187 /*
1188  * Returns a new array of Java string objects for the specified
1189  * array of platform strings.
1190  */
1191 static jobjectArray
1192 NewPlatformStringArray(JNIEnv *env, char **strv, int strc)
1193 {
1194     jarray cls;
1195     jarray ary;
1196     int i;
1197 
1198     NULL_CHECK0(cls = FindBootStrapClass(env, "java/lang/String"));
1199     NULL_CHECK0(ary = (*env)->NewObjectArray(env, strc, cls, 0));
1200     for (i = 0; i < strc; i++) {
1201         jstring str = NewPlatformString(env, *strv++);
1202         NULL_CHECK0(str);
1203         (*env)->SetObjectArrayElement(env, ary, i, str);
1204         (*env)->DeleteLocalRef(env, str);
1205     }
1206     return ary;
1207 }
1208 
1209 /*
1210  * Loads a class, convert the '.' to '/'.
1211  */
1212 static jclass
1213 LoadClass(JNIEnv *env, char *name)
1214 {
1215     char *buf = MemAlloc(strlen(name) + 1);
1216     char *s = buf, *t = name, c;
1217     jclass cls;
1218     jlong start, end;
1219 
1220     if (_launcher_debug)
1221         start = CounterGet();
1222 
1223     do {
1224         c = *t++;
1225         *s++ = (c == '.') ? '/' : c;
1226     } while (c != '\0');
1227     // use the application class loader for the main-class
1228     cls = (*env)->FindClass(env, buf);
1229     free(buf);
1230 
1231     if (_launcher_debug) {
1232         end   = CounterGet();
1233         printf("%ld micro seconds to load main class\n",
1234                (long)(jint)Counter2Micros(end-start));
1235         printf("----_JAVA_LAUNCHER_DEBUG----\n");
1236     }
1237 
1238     return cls;
1239 }
1240 
1241 
1242 /*
1243  * Returns the main class name for the specified jar file.
1244  */
1245 static jstring
1246 GetMainClassName(JNIEnv *env, char *jarname)
1247 {
1248 #define MAIN_CLASS "Main-Class"
1249     jclass cls;
1250     jmethodID mid;
1251     jobject jar, man, attr;
1252     jstring str, result = 0;
1253 
1254     NULL_CHECK0(cls = FindBootStrapClass(env, "java/util/jar/JarFile"));
1255     NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "<init>",
1256                                           "(Ljava/lang/String;)V"));
1257     NULL_CHECK0(str = NewPlatformString(env, jarname));
1258     NULL_CHECK0(jar = (*env)->NewObject(env, cls, mid, str));
1259     NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "getManifest",
1260                                           "()Ljava/util/jar/Manifest;"));
1261     man = (*env)->CallObjectMethod(env, jar, mid);
1262     if (man != 0) {
1263         NULL_CHECK0(mid = (*env)->GetMethodID(env,
1264                                     (*env)->GetObjectClass(env, man),
1265                                     "getMainAttributes",
1266                                     "()Ljava/util/jar/Attributes;"));
1267         attr = (*env)->CallObjectMethod(env, man, mid);
1268         if (attr != 0) {
1269             NULL_CHECK0(mid = (*env)->GetMethodID(env,
1270                                     (*env)->GetObjectClass(env, attr),
1271                                     "getValue",
1272                                     "(Ljava/lang/String;)Ljava/lang/String;"));
1273             NULL_CHECK0(str = NewPlatformString(env, MAIN_CLASS));
1274             result = (*env)->CallObjectMethod(env, attr, mid, str);
1275         }
1276     }
1277     return result;
1278 }
1279 
1280 #ifdef JAVA_ARGS
1281 static char *java_args[] = JAVA_ARGS;
1282 static char *app_classpath[] = APP_CLASSPATH;
1283 
1284 /*
1285  * For tools convert 'javac -J-ms32m' to 'java -ms32m ...'
1286  */
1287 static void
1288 TranslateDashJArgs(int *pargc, char ***pargv)
1289 {
1290     const int NUM_ARGS = (sizeof(java_args) / sizeof(char *));
1291     int argc = *pargc;
1292     char **argv = *pargv;
1293     int nargc = argc + NUM_ARGS;
1294     char **nargv = MemAlloc((nargc + 1) * sizeof(char *));
1295     int i;
1296 
1297     *pargc = nargc;
1298     *pargv = nargv;
1299 
1300     /* Copy the VM arguments (i.e. prefixed with -J) */
1301     for (i = 0; i < NUM_ARGS; i++) {
1302         char *arg = java_args[i];
1303         if (arg[0] == '-' && arg[1] == 'J') {
1304             *nargv++ = arg + 2;
1305         }
1306     }
1307 
1308     for (i = 0; i < argc; i++) {
1309         char *arg = argv[i];
1310         if (arg[0] == '-' && arg[1] == 'J') {
1311             if (arg[2] == '\0') {
1312                 ReportErrorMessage("Error: the -J option should not be "
1313                                    "followed by a space.", JNI_TRUE);
1314                 exit(1);
1315             }
1316             *nargv++ = arg + 2;
1317         }
1318     }
1319 
1320     /* Copy the rest of the arguments */
1321     for (i = 0; i < NUM_ARGS; i++) {
1322         char *arg = java_args[i];
1323         if (arg[0] != '-' || arg[1] != 'J') {
1324             *nargv++ = arg;
1325         }
1326     }
1327     for (i = 0; i < argc; i++) {
1328         char *arg = argv[i];
1329         if (arg[0] != '-' || arg[1] != 'J') {
1330             *nargv++ = arg;
1331         }
1332     }
1333     *nargv = 0;
1334 }
1335 
1336 /*
1337  * For our tools, we try to add 3 VM options:
1338  *      -Denv.class.path=<envcp>
1339  *      -Dapplication.home=<apphome>
1340  *      -Djava.class.path=<appcp>
1341  * <envcp>   is the user's setting of CLASSPATH -- for instance the user
1342  *           tells javac where to find binary classes through this environment
1343  *           variable.  Notice that users will be able to compile against our
1344  *           tools classes (sun.tools.javac.Main) only if they explicitly add
1345  *           tools.jar to CLASSPATH.
1346  * <apphome> is the directory where the application is installed.
1347  * <appcp>   is the classpath to where our apps' classfiles are.
1348  */
1349 static jboolean
1350 AddApplicationOptions()
1351 {
1352     const int NUM_APP_CLASSPATH = (sizeof(app_classpath) / sizeof(char *));
1353     char *s, *envcp, *appcp, *apphome;
1354     char home[MAXPATHLEN]; /* application home */
1355     char separator[] = { PATH_SEPARATOR, '\0' };
1356     int size, i;
1357     int strlenHome;
1358 
1359     s = getenv("CLASSPATH");
1360     if (s) {
1361         /* 40 for -Denv.class.path= */
1362         envcp = (char *)MemAlloc(strlen(s) + 40);
1363         sprintf(envcp, "-Denv.class.path=%s", s);
1364         AddOption(envcp, NULL);
1365     }
1366 
1367     if (!GetApplicationHome(home, sizeof(home))) {
1368         ReportErrorMessage("Can't determine application home", JNI_TRUE);
1369         return JNI_FALSE;
1370     }
1371 
1372     /* 40 for '-Dapplication.home=' */
1373     apphome = (char *)MemAlloc(strlen(home) + 40);
1374     sprintf(apphome, "-Dapplication.home=%s", home);
1375     AddOption(apphome, NULL);
1376 
1377     /* How big is the application's classpath? */
1378     size = 40;                                 /* 40: "-Djava.class.path=" */
1379     strlenHome = (int)strlen(home);
1380     for (i = 0; i < NUM_APP_CLASSPATH; i++) {
1381         size += strlenHome + (int)strlen(app_classpath[i]) + 1; /* 1: separator */
1382     }
1383     appcp = (char *)MemAlloc(size + 1);
1384     strcpy(appcp, "-Djava.class.path=");
1385     for (i = 0; i < NUM_APP_CLASSPATH; i++) {
1386         strcat(appcp, home);                    /* c:\program files\myapp */
1387         strcat(appcp, app_classpath[i]);        /* \lib\myapp.jar         */
1388         strcat(appcp, separator);               /* ;                      */
1389     }
1390     appcp[strlen(appcp)-1] = '\0';  /* remove trailing path separator */
1391     AddOption(appcp, NULL);
1392     return JNI_TRUE;
1393 }
1394 #endif
1395 
1396 /*
1397  * inject the -Dsun.java.command pseudo property into the args structure
1398  * this pseudo property is used in the HotSpot VM to expose the
1399  * Java class name and arguments to the main method to the VM. The
1400  * HotSpot VM uses this pseudo property to store the Java class name
1401  * (or jar file name) and the arguments to the class's main method
1402  * to the instrumentation memory region. The sun.java.command pseudo
1403  * property is not exported by HotSpot to the Java layer.
1404  */
1405 void
1406 SetJavaCommandLineProp(char *classname, char *jarfile,
1407                        int argc, char **argv)
1408 {
1409 
1410     int i = 0;
1411     size_t len = 0;
1412     char* javaCommand = NULL;
1413     char* dashDstr = "-Dsun.java.command=";
1414 
1415     if (classname == NULL && jarfile == NULL) {
1416         /* unexpected, one of these should be set. just return without
1417          * setting the property
1418          */
1419         return;
1420     }
1421 
1422     /* if the class name is not set, then use the jarfile name */
1423     if (classname == NULL) {
1424         classname = jarfile;
1425     }
1426 
1427     /* determine the amount of memory to allocate assuming
1428      * the individual components will be space separated
1429      */
1430     len = strlen(classname);
1431     for (i = 0; i < argc; i++) {
1432         len += strlen(argv[i]) + 1;
1433     }
1434 
1435     /* allocate the memory */
1436     javaCommand = (char*) MemAlloc(len + strlen(dashDstr) + 1);
1437 
1438     /* build the -D string */
1439     *javaCommand = '\0';
1440     strcat(javaCommand, dashDstr);
1441     strcat(javaCommand, classname);
1442 
1443     for (i = 0; i < argc; i++) {
1444         /* the components of the string are space separated. In
1445          * the case of embedded white space, the relationship of
1446          * the white space separated components to their true
1447          * positional arguments will be ambiguous. This issue may
1448          * be addressed in a future release.
1449          */
1450         strcat(javaCommand, " ");
1451         strcat(javaCommand, argv[i]);
1452     }
1453 
1454     AddOption(javaCommand, NULL);
1455 }
1456 
1457 /*
1458  * JVM wants to know launcher type, so tell it.
1459  */
1460 #ifdef GAMMA
1461 void SetJavaLauncherProp() {
1462   AddOption("-Dsun.java.launcher=" LAUNCHER_TYPE, NULL);
1463 }
1464 #endif
1465 
1466 /*
1467  * Prints the version information from the java.version and other properties.
1468  */
1469 static void
1470 PrintJavaVersion(JNIEnv *env)
1471 {
1472     jclass ver;
1473     jmethodID print;
1474 
1475     NULL_CHECK(ver = FindBootStrapClass(env, "sun/misc/Version"));
1476     NULL_CHECK(print = (*env)->GetStaticMethodID(env, ver, "print", "()V"));
1477 
1478     (*env)->CallStaticVoidMethod(env, ver, print);
1479 }
1480 
1481 /*
1482  * Prints default usage message.
1483  */
1484 static void
1485 PrintUsage(void)
1486 {
1487     int i;
1488 
1489     fprintf(stdout,
1490         "Usage: %s [-options] class [args...]\n"
1491         "           (to execute a class)\n"
1492         "   or  %s [-options] -jar jarfile [args...]\n"
1493         "           (to execute a jar file)\n"
1494         "\n"
1495         "where options include:\n",
1496         progname,
1497         progname);
1498 
1499 #ifndef GAMMA
1500     PrintMachineDependentOptions();
1501 
1502     if ((knownVMs[0].flag == VM_KNOWN) ||
1503         (knownVMs[0].flag == VM_IF_SERVER_CLASS)) {
1504       fprintf(stdout, "    %s\t  to select the \"%s\" VM\n",
1505               knownVMs[0].name, knownVMs[0].name+1);
1506     }
1507     for (i=1; i<knownVMsCount; i++) {
1508         if (knownVMs[i].flag == VM_KNOWN)
1509             fprintf(stdout, "    %s\t  to select the \"%s\" VM\n",
1510                     knownVMs[i].name, knownVMs[i].name+1);
1511     }
1512     for (i=1; i<knownVMsCount; i++) {
1513         if (knownVMs[i].flag == VM_ALIASED_TO)
1514             fprintf(stdout, "    %s\t  is a synonym for "
1515                     "the \"%s\" VM  [deprecated]\n",
1516                     knownVMs[i].name, knownVMs[i].alias+1);
1517     }
1518 
1519     /* The first known VM is the default */
1520     {
1521       const char* defaultVM   = knownVMs[0].name+1;
1522       const char* punctuation = ".";
1523       const char* reason      = "";
1524       if ((knownVMs[0].flag == VM_IF_SERVER_CLASS) &&
1525           (ServerClassMachine() == JNI_TRUE)) {
1526         defaultVM = knownVMs[0].server_class+1;
1527         punctuation = ", ";
1528         reason = "because you are running on a server-class machine.\n";
1529       }
1530       fprintf(stdout, "                  The default VM is %s%s\n",
1531               defaultVM, punctuation);
1532       fprintf(stdout, "                  %s\n",
1533               reason);
1534     }
1535 #endif /* ifndef GAMMA */
1536 
1537     fprintf(stdout,
1538 "    -cp <class search path of directories and zip/jar files>\n"
1539 "    -classpath <class search path of directories and zip/jar files>\n"
1540 "                  A %c separated list of directories, JAR archives,\n"
1541 "                  and ZIP archives to search for class files.\n"
1542 "    -D<name>=<value>\n"
1543 "                  set a system property\n"
1544 "    -verbose[:class|gc|jni]\n"
1545 "                  enable verbose output\n"
1546 "    -version      print product version and exit\n"
1547 "    -version:<value>\n"
1548 "                  require the specified version to run\n"
1549 "    -showversion  print product version and continue\n"
1550 "    -jre-restrict-search | -jre-no-restrict-search\n"
1551 "                  include/exclude user private JREs in the version search\n"
1552 "    -? -help      print this help message\n"
1553 "    -X            print help on non-standard options\n"
1554 "    -ea[:<packagename>...|:<classname>]\n"
1555 "    -enableassertions[:<packagename>...|:<classname>]\n"
1556 "                  enable assertions\n"
1557 "    -da[:<packagename>...|:<classname>]\n"
1558 "    -disableassertions[:<packagename>...|:<classname>]\n"
1559 "                  disable assertions\n"
1560 "    -esa | -enablesystemassertions\n"
1561 "                  enable system assertions\n"
1562 "    -dsa | -disablesystemassertions\n"
1563 "                  disable system assertions\n"
1564 "    -agentlib:<libname>[=<options>]\n"
1565 "                  load native agent library <libname>, e.g. -agentlib:hprof\n"
1566 "                    see also, -agentlib:jdwp=help and -agentlib:hprof=help\n"
1567 "    -agentpath:<pathname>[=<options>]\n"
1568 "                  load native agent library by full pathname\n"
1569 "    -javaagent:<jarpath>[=<options>]\n"
1570 "                  load Java programming language agent, see java.lang.instrument\n"
1571 
1572             ,PATH_SEPARATOR);
1573 }
1574 
1575 /*
1576  * Print usage message for -X options.
1577  */
1578 static jint
1579 PrintXUsage(void)
1580 {
1581     char path[MAXPATHLEN];
1582     char buf[128];
1583     size_t n;
1584     FILE *fp;
1585 
1586     GetXUsagePath(path, sizeof(path));
1587     fp = fopen(path, "r");
1588     if (fp == 0) {
1589         fprintf(stderr, "Can't open %s\n", path);
1590         return 1;
1591     }
1592     while ((n = fread(buf, 1, sizeof(buf), fp)) != 0) {
1593         fwrite(buf, 1, n, stdout);
1594     }
1595     fclose(fp);
1596     return 0;
1597 }
1598 
1599 #ifndef GAMMA
1600 
1601 /*
1602  * Read the jvm.cfg file and fill the knownJVMs[] array.
1603  *
1604  * The functionality of the jvm.cfg file is subject to change without
1605  * notice and the mechanism will be removed in the future.
1606  *
1607  * The lexical structure of the jvm.cfg file is as follows:
1608  *
1609  *     jvmcfg         :=  { vmLine }
1610  *     vmLine         :=  knownLine
1611  *                    |   aliasLine
1612  *                    |   warnLine
1613  *                    |   ignoreLine
1614  *                    |   errorLine
1615  *                    |   predicateLine
1616  *                    |   commentLine
1617  *     knownLine      :=  flag  "KNOWN"                  EOL
1618  *     warnLine       :=  flag  "WARN"                   EOL
1619  *     ignoreLine     :=  flag  "IGNORE"                 EOL
1620  *     errorLine      :=  flag  "ERROR"                  EOL
1621  *     aliasLine      :=  flag  "ALIASED_TO"       flag  EOL
1622  *     predicateLine  :=  flag  "IF_SERVER_CLASS"  flag  EOL
1623  *     commentLine    :=  "#" text                       EOL
1624  *     flag           :=  "-" identifier
1625  *
1626  * The semantics are that when someone specifies a flag on the command line:
1627  * - if the flag appears on a knownLine, then the identifier is used as
1628  *   the name of the directory holding the JVM library (the name of the JVM).
1629  * - if the flag appears as the first flag on an aliasLine, the identifier
1630  *   of the second flag is used as the name of the JVM.
1631  * - if the flag appears on a warnLine, the identifier is used as the
1632  *   name of the JVM, but a warning is generated.
1633  * - if the flag appears on an ignoreLine, the identifier is recognized as the
1634  *   name of a JVM, but the identifier is ignored and the default vm used
1635  * - if the flag appears on an errorLine, an error is generated.
1636  * - if the flag appears as the first flag on a predicateLine, and
1637  *   the machine on which you are running passes the predicate indicated,
1638  *   then the identifier of the second flag is used as the name of the JVM,
1639  *   otherwise the identifier of the first flag is used as the name of the JVM.
1640  * If no flag is given on the command line, the first vmLine of the jvm.cfg
1641  * file determines the name of the JVM.
1642  * PredicateLines are only interpreted on first vmLine of a jvm.cfg file,
1643  * since they only make sense if someone hasn't specified the name of the
1644  * JVM on the command line.
1645  *
1646  * The intent of the jvm.cfg file is to allow several JVM libraries to
1647  * be installed in different subdirectories of a single JRE installation,
1648  * for space-savings and convenience in testing.
1649  * The intent is explicitly not to provide a full aliasing or predicate
1650  * mechanism.
1651  */
1652 jint
1653 ReadKnownVMs(const char *jrepath, char * arch, jboolean speculative)
1654 {
1655     FILE *jvmCfg;
1656     char jvmCfgName[MAXPATHLEN+20];
1657     char line[MAXPATHLEN+20];
1658     int cnt = 0;
1659     int lineno = 0;
1660     jlong start, end;
1661     int vmType;
1662     char *tmpPtr;
1663     char *altVMName;
1664     char *serverClassVMName;
1665     static char *whiteSpace = " \t";
1666     if (_launcher_debug) {
1667         start = CounterGet();
1668     }
1669 
1670     strcpy(jvmCfgName, jrepath);
1671     strcat(jvmCfgName, FILESEP "lib" FILESEP);
1672     strcat(jvmCfgName, arch);
1673     strcat(jvmCfgName, FILESEP "jvm.cfg");
1674 
1675     jvmCfg = fopen(jvmCfgName, "r");
1676     if (jvmCfg == NULL) {
1677       if (!speculative) {
1678         ReportErrorMessage2("Error: could not open `%s'", jvmCfgName,
1679                             JNI_TRUE);
1680         exit(1);
1681       } else {
1682         return -1;
1683       }
1684     }
1685     while (fgets(line, sizeof(line), jvmCfg) != NULL) {
1686         vmType = VM_UNKNOWN;
1687         lineno++;
1688         if (line[0] == '#')
1689             continue;
1690         if (line[0] != '-') {
1691             fprintf(stderr, "Warning: no leading - on line %d of `%s'\n",
1692                     lineno, jvmCfgName);
1693         }
1694         if (cnt >= knownVMsLimit) {
1695             GrowKnownVMs(cnt);
1696         }
1697         line[strlen(line)-1] = '\0'; /* remove trailing newline */
1698         tmpPtr = line + strcspn(line, whiteSpace);
1699         if (*tmpPtr == 0) {
1700             fprintf(stderr, "Warning: missing VM type on line %d of `%s'\n",
1701                     lineno, jvmCfgName);
1702         } else {
1703             /* Null-terminate this string for strdup below */
1704             *tmpPtr++ = 0;
1705             tmpPtr += strspn(tmpPtr, whiteSpace);
1706             if (*tmpPtr == 0) {
1707                 fprintf(stderr, "Warning: missing VM type on line %d of `%s'\n",
1708                         lineno, jvmCfgName);
1709             } else {
1710                 if (!strncmp(tmpPtr, "KNOWN", strlen("KNOWN"))) {
1711                     vmType = VM_KNOWN;
1712                 } else if (!strncmp(tmpPtr, "ALIASED_TO", strlen("ALIASED_TO"))) {
1713                     tmpPtr += strcspn(tmpPtr, whiteSpace);
1714                     if (*tmpPtr != 0) {
1715                         tmpPtr += strspn(tmpPtr, whiteSpace);
1716                     }
1717                     if (*tmpPtr == 0) {
1718                         fprintf(stderr, "Warning: missing VM alias on line %d of `%s'\n",
1719                                 lineno, jvmCfgName);
1720                     } else {
1721                         /* Null terminate altVMName */
1722                         altVMName = tmpPtr;
1723                         tmpPtr += strcspn(tmpPtr, whiteSpace);
1724                         *tmpPtr = 0;
1725                         vmType = VM_ALIASED_TO;
1726                     }
1727                 } else if (!strncmp(tmpPtr, "WARN", strlen("WARN"))) {
1728                     vmType = VM_WARN;
1729                 } else if (!strncmp(tmpPtr, "IGNORE", strlen("IGNORE"))) {
1730                     vmType = VM_IGNORE;
1731                 } else if (!strncmp(tmpPtr, "ERROR", strlen("ERROR"))) {
1732                     vmType = VM_ERROR;
1733                 } else if (!strncmp(tmpPtr,
1734                                     "IF_SERVER_CLASS",
1735                                     strlen("IF_SERVER_CLASS"))) {
1736                     tmpPtr += strcspn(tmpPtr, whiteSpace);
1737                     if (*tmpPtr != 0) {
1738                         tmpPtr += strspn(tmpPtr, whiteSpace);
1739                     }
1740                     if (*tmpPtr == 0) {
1741                         fprintf(stderr, "Warning: missing server class VM on line %d of `%s'\n",
1742                                 lineno, jvmCfgName);
1743                     } else {
1744                         /* Null terminate server class VM name */
1745                         serverClassVMName = tmpPtr;
1746                         tmpPtr += strcspn(tmpPtr, whiteSpace);
1747                         *tmpPtr = 0;
1748                         vmType = VM_IF_SERVER_CLASS;
1749                     }
1750                 } else {
1751                     fprintf(stderr, "Warning: unknown VM type on line %d of `%s'\n",
1752                             lineno, &jvmCfgName[0]);
1753                     vmType = VM_KNOWN;
1754                 }
1755             }
1756         }
1757 
1758         if (_launcher_debug)
1759             printf("jvm.cfg[%d] = ->%s<-\n", cnt, line);
1760         if (vmType != VM_UNKNOWN) {
1761             knownVMs[cnt].name = strdup(line);
1762             knownVMs[cnt].flag = vmType;
1763             switch (vmType) {
1764             default:
1765                 break;
1766             case VM_ALIASED_TO:
1767                 knownVMs[cnt].alias = strdup(altVMName);
1768                 if (_launcher_debug) {
1769                     printf("    name: %s  vmType: %s  alias: %s\n",
1770                            knownVMs[cnt].name, "VM_ALIASED_TO", knownVMs[cnt].alias);
1771                 }
1772                 break;
1773             case VM_IF_SERVER_CLASS:
1774                 knownVMs[cnt].server_class = strdup(serverClassVMName);
1775                 if (_launcher_debug) {
1776                     printf("    name: %s  vmType: %s  server_class: %s\n",
1777                            knownVMs[cnt].name, "VM_IF_SERVER_CLASS", knownVMs[cnt].server_class);
1778                 }
1779                 break;
1780             }
1781             cnt++;
1782         }
1783     }
1784     fclose(jvmCfg);
1785     knownVMsCount = cnt;
1786 
1787     if (_launcher_debug) {
1788         end   = CounterGet();
1789         printf("%ld micro seconds to parse jvm.cfg\n",
1790                (long)(jint)Counter2Micros(end-start));
1791     }
1792 
1793     return cnt;
1794 }
1795 
1796 
1797 static void
1798 GrowKnownVMs(int minimum)
1799 {
1800     struct vmdesc* newKnownVMs;
1801     int newMax;
1802 
1803     newMax = (knownVMsLimit == 0 ? INIT_MAX_KNOWN_VMS : (2 * knownVMsLimit));
1804     if (newMax <= minimum) {
1805         newMax = minimum;
1806     }
1807     newKnownVMs = (struct vmdesc*) MemAlloc(newMax * sizeof(struct vmdesc));
1808     if (knownVMs != NULL) {
1809         memcpy(newKnownVMs, knownVMs, knownVMsLimit * sizeof(struct vmdesc));
1810     }
1811     free(knownVMs);
1812     knownVMs = newKnownVMs;
1813     knownVMsLimit = newMax;
1814 }
1815 
1816 
1817 /* Returns index of VM or -1 if not found */
1818 static int
1819 KnownVMIndex(const char* name)
1820 {
1821     int i;
1822     if (strncmp(name, "-J", 2) == 0) name += 2;
1823     for (i = 0; i < knownVMsCount; i++) {
1824         if (!strcmp(name, knownVMs[i].name)) {
1825             return i;
1826         }
1827     }
1828     return -1;
1829 }
1830 
1831 static void
1832 FreeKnownVMs()
1833 {
1834     int i;
1835     for (i = 0; i < knownVMsCount; i++) {
1836         free(knownVMs[i].name);
1837         knownVMs[i].name = NULL;
1838     }
1839     free(knownVMs);
1840 }
1841 
1842 #endif /* ifndef GAMMA */