00001 /*! 00002 * @file classpath.c 00003 * 00004 * @brief Extract @b CLASSPATH runtime variables from the environment 00005 * and/or the command line or other appropriate sources. 00006 * 00007 * The HEAP_INIT() function must have been called before using 00008 * these functions so the environment variables can be stored 00009 * into it and not depend on the argument or environment pointers 00010 * to always be unchanged. 00011 * 00012 * The tmparea_init() function must have been called before these 00013 * functions so the internal @b CLASSPATH can be set up properly. 00014 * 00015 * 00016 * @section Control 00017 * 00018 * \$URL: https://svn.apache.org/path/name/classpath.c $ \$Id: classpath.c 0 09/28/2005 dlydick $ 00019 * 00020 * Copyright 2005 The Apache Software Foundation 00021 * or its licensors, as applicable. 00022 * 00023 * Licensed under the Apache License, Version 2.0 ("the License"); 00024 * you may not use this file except in compliance with the License. 00025 * You may obtain a copy of the License at 00026 * 00027 * http://www.apache.org/licenses/LICENSE-2.0 00028 * 00029 * Unless required by applicable law or agreed to in writing, 00030 * software distributed under the License is distributed on an 00031 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 00032 * either express or implied. 00033 * 00034 * See the License for the specific language governing permissions 00035 * and limitations under the License. 00036 * 00037 * @version \$LastChangedRevision: 0 $ 00038 * 00039 * @date \$LastChangedDate: 09/28/2005 $ 00040 * 00041 * @author \$LastChangedBy: dlydick $ 00042 * Original code contributed by Daniel Lydick on 09/28/2005. 00043 * 00044 * @section Reference 00045 * 00046 */ 00047 00048 #include "arch.h" 00049 ARCH_COPYRIGHT_APACHE(classpath, c, "$URL: https://svn.apache.org/path/name/classpath.c $ $Id: classpath.c 0 09/28/2005 dlydick $"); 00050 00051 00052 #include <string.h> 00053 #include <stdlib.h> 00054 #include <sys/stat.h> 00055 00056 #include "jvmcfg.h" 00057 #include "cfmacros.h" 00058 #include "classfile.h" 00059 #include "classpath.h" 00060 #include "exit.h" 00061 #include "heap.h" 00062 #include "linkage.h" 00063 #include "jvm.h" 00064 #include "nts.h" 00065 #include "utf.h" 00066 #include "util.h" 00067 00068 /*! 00069 * 00070 * @brief Initialize @b CLASSPATH search area of the JVM model. 00071 * 00072 * Break @b CLASSPATH apart into its constituent paths. Run once 00073 * during startup to parse @b CLASSPATH. Heap management must be 00074 * started before calling this function via HEAP_INIT(). The 00075 * command line must also have been scanned via argv_init(). 00076 * 00077 * The tmparea_init() function must have been called before these 00078 * functions so the internal @b CLASSPATH can be set up properly. 00079 * 00080 * 00081 * @param argc Number of arguments on command line 00082 * 00083 * @param argv Argument vector from the command line 00084 * 00085 * @param envp Environment pointer from command line environ 00086 * 00087 * 00088 * @returns @link #rvoid rvoid@endlink 00089 * 00090 * 00091 * @todo Add proper searching for @c @b rt.jar file 00092 * and @c @b Xbootclasspath . 00093 * For the moment, they are defined in 00094 * @link config.h config.h@endlink as the 00095 * @link #CONFIG_HACKED_RTJARFILE CONFIG_HACKED_RTJARFILE@endlink 00096 * and 00097 @link #CONFIG_HACKED_BOOTCLASSPATH CONFIG_HACKED_BOOTCLASSPATH@endlink 00098 * pre-processor symbols and are commented in 00099 * @link jvm/src/jvmcfg.h jvmcfg.h@endlink after this 00100 * fashion. 00101 * 00102 */ 00103 static rchar **classpath_list = CHEAT_AND_USE_NULL_TO_INITIALIZE; 00104 static rint classpath_list_len = 0; 00105 00106 rvoid classpath_init() 00107 { 00108 /* Initialize only once */ 00109 if (rnull != classpath_list) 00110 { 00111 return; 00112 } 00113 00114 rint i; 00115 rint pathcount; 00116 00117 /* 00118 * Prepare to concatenate the temp directory area with actual 00119 * @b CLASSPATH, followed by potential 00120 * CONFIG_HACKED_xxx definitions. 00121 */ 00122 rchar *tmpclasspath; /* @b CLASSPATH dlm */ 00123 rint tmpcplen = strlen(tmparea_get()) + sizeof(rchar) + 00124 strlen(pjvm->classpath) + sizeof(rchar); 00125 00126 /* 00127 * Compensate for CONFIG_HACKED_xxx definitions, handy development 00128 * hooks for when @b CLASSPATH is in question. 00129 */ 00130 #ifdef CONFIG_HACKED_BOOTCLASSPATH 00131 /* @b CLASSPATH dlm*/ 00132 tmpcplen += strlen(CONFIG_HACKED_BOOTCLASSPATH) + sizeof(rchar); 00133 #endif 00134 #ifdef CONFIG_HACKED_RTJARFILE 00135 00136 /* For $JAVA_HOME/$CONFIG_HACKED_RTJARFILE */ 00137 tmpcplen += strlen(pjvm->java_home); 00138 tmpcplen += sizeof(rchar); 00139 tmpcplen += strlen(CONFIG_HACKED_RTJARFILE) + sizeof(rchar); 00140 #endif 00141 tmpcplen += sizeof(rchar);/* NUL byte, possibly 1 more than needed*/ 00142 00143 /* 00144 * Allocate space for classpath image with temp area and 00145 * possible CONFIG_HACKED_xxx adjustments 00146 */ 00147 tmpclasspath = HEAP_GET_DATA(tmpcplen, rfalse); 00148 00149 /* 00150 * Generate concatenation of 00151 * 00152 * pjvm->TMPAREA:classpath: 00153 @link #CONFIG_HACKED_BOOTCLASSPATH CONFIG_HACKED_BOOTCLASSPATH@endlink: 00154 * @link #CONFIG_HACKED_RTJARFILE CONFIG_HACKED_RTJARFILE@endlink 00155 */ 00156 strcpy(tmpclasspath, tmparea_get()); 00157 i = strlen(tmpclasspath); 00158 tmpclasspath[i] = CLASSPATH_ITEM_DELIMITER_CHAR; 00159 tmpclasspath[i + 1] = '\0'; 00160 00161 strcat(tmpclasspath, pjvm->classpath); 00162 00163 #ifdef CONFIG_HACKED_BOOTCLASSPATH 00164 i = strlen(tmpclasspath); 00165 tmpclasspath[i] = CLASSPATH_ITEM_DELIMITER_CHAR; 00166 tmpclasspath[i + 1] = '\0'; 00167 strcat(tmpclasspath, CONFIG_HACKED_BOOTCLASSPATH); 00168 #endif 00169 #ifdef CONFIG_HACKED_RTJARFILE 00170 i = strlen(tmpclasspath); 00171 tmpclasspath[i] = CLASSPATH_ITEM_DELIMITER_CHAR; 00172 tmpclasspath[i + 1] = '\0'; 00173 strcat(tmpclasspath, pjvm->java_home); 00174 00175 i = strlen(tmpclasspath); 00176 tmpclasspath[i] = CLASSPATH_ITEM_DELIMITER_CHAR; 00177 tmpclasspath[i + 1] = '\0'; 00178 strcat(tmpclasspath, CONFIG_HACKED_RTJARFILE); 00179 #endif 00180 HEAP_FREE_DATA(pjvm->classpath); /* May or may not be on heap */ 00181 pjvm->classpath = tmpclasspath; /* Keep for duration of pgm run */ 00182 00183 00184 /* WARNING! NON-STANDARD TERMINATION CONDITION <= VERSUS < */ 00185 for (i = 0, pathcount = 0; i <= strlen(tmpclasspath); i++) 00186 { 00187 if ((CLASSPATH_ITEM_DELIMITER_CHAR == tmpclasspath[i]) || 00188 (i == strlen(tmpclasspath))) 00189 { 00190 pathcount++; 00191 } 00192 } 00193 00194 00195 /* Allocate space for list of @b CLASSPATH entries */ 00196 classpath_list = HEAP_GET_DATA(pathcount * sizeof(rchar *), rtrue); 00197 00198 rchar *nextpath; 00199 rint thislen; 00200 classpath_list_len = 0; 00201 00202 /* WARNING! NON-STANDARD TERMINATION CONDITION <= VERSUS < */ 00203 for (i = 0, nextpath = tmpclasspath; 00204 i <= strlen(tmpclasspath); 00205 i++) 00206 { 00207 00208 /* If found item delimiter OR END OF STRING (SEE ABOVE) */ 00209 if ((CLASSPATH_ITEM_DELIMITER_CHAR == tmpclasspath[i]) || 00210 (i == strlen(tmpclasspath))) 00211 { 00212 /* calculate length of this @b CLASSPATH entry */ 00213 thislen = (&tmpclasspath[i]) - nextpath; 00214 00215 /* 00216 * Ignore double-delimiter cases. 00217 * It does not hurt any thing for @b classpath_list 00218 * to be longer than the pointers slots allocated 00219 * in it because @b classpath_list_len limits the 00220 * range of usage to those validly allocated. 00221 */ 00222 if (0 == thislen) 00223 { 00224 /* Pretend it was valid */ 00225 nextpath = &tmpclasspath[i + 1]; 00226 continue; 00227 } 00228 00229 /* 00230 * Allocate enough space for item, plus final '\0'. 00231 * Since we are scanning for a delimiter or EOS, 00232 * the current length calculation includes the "1 + x" 00233 * for the '\0'. The string[x] location is set to '\0'. 00234 */ 00235 classpath_list[classpath_list_len] = 00236 HEAP_GET_DATA(thislen + sizeof(rchar), rfalse); 00237 00238 /* Store current @b CLASSPATH item, including final '\0' */ 00239 memcpy(classpath_list[classpath_list_len], 00240 nextpath, 00241 thislen); 00242 classpath_list[classpath_list_len][thislen] = '\0'; 00243 classpath_list_len++; 00244 00245 /* Start looking at next @b CLASSPATH item */ 00246 nextpath = &tmpclasspath[i + 1]; 00247 00248 } /* if tmpclasspath[i] */ 00249 00250 } /* for i */ 00251 00252 /* Declare this module initialized */ 00253 jvm_classpath_initialized = rtrue; 00254 00255 return; 00256 00257 } /* END of classpath_init() */ 00258 00259 00260 /*! 00261 * @brief Determine whether or not a @b CLASSPATH entry is a JAR file 00262 * instead of being a directory name. 00263 * 00264 * A JAR file will be named @c @b /path/name/filename.jar, while a 00265 * file name in a directory will be named 00266 * @c @b /path/name/ClassName.class . 00267 * 00268 * 00269 * @param pclasspath String from @b CLASSPATH list 00270 * 00271 * @returns @link #rtrue rtrue@endlink if string ends with 00272 * @c @b .jar, @link #rfalse rfalse@endlink otherwise. 00273 * 00274 */ 00275 rboolean classpath_isjar(rchar *pclasspath) 00276 { 00277 rint len, jarlen; 00278 00279 /* Lengths of test string and of JAR extension (w/ name.ext dlm)*/ 00280 len = strlen(pclasspath); 00281 jarlen = strlen(JVMCFG_EXTENSION_DELIMITER_STRING) + 00282 strlen(CLASSFILE_EXTENSION_JAR); 00283 00284 /* For VERY short @b CLASSPATH entries, it cannot be a JAR file */ 00285 if (jarlen >= len) 00286 { 00287 return(rfalse); 00288 } 00289 00290 /* Check if name.ext delimiter present in test string */ 00291 if (JVMCFG_EXTENSION_DELIMITER_CHAR != pclasspath[len - jarlen]) 00292 { 00293 return(rfalse); 00294 } 00295 00296 /* Now go test JAR extension since delimiter is present */ 00297 jarlen--; 00298 if (0 == strncmp(&pclasspath[len - jarlen], 00299 CLASSFILE_EXTENSION_JAR, 00300 jarlen)) 00301 { 00302 return(rtrue); 00303 } 00304 else 00305 { 00306 return(rfalse); 00307 } 00308 00309 } /* END of classpath_isjar() */ 00310 00311 00312 /*! 00313 * @brief Convert class name format external to internal form. 00314 00315 * The external format is @c @b class.name.format , while the 00316 * internal format is @c @b class/name/format . 00317 * Result is unchanged if it is already in internal format. 00318 * When finished with result, call HEAP_FREE_DATA(). 00319 * 00320 * 00321 * @param clsname Null-terminated string containing class name. 00322 * 00323 * 00324 * @returns Null terminated string in internal format. 00325 * Call HEAP_FREE_DATA() when finished with buffer. 00326 * 00327 */ 00328 00329 rchar *classpath_external2internal_classname(rchar *clsname) 00330 { 00331 rint len = strlen(clsname); 00332 rchar *rc = HEAP_GET_DATA(1 + len, rfalse); /* 1 + for NUL byte */ 00333 00334 memcpy(rc, clsname, 1 + len); /* 1 + for NUL byte */ 00335 00336 return(classpath_external2internal_classname_inplace(rc)); 00337 00338 } /* END of classpath_external2internal_classname() */ 00339 00340 00341 /* 00342 * In-place version of classpath_externam2internal_classname(): 00343 * Takes an existing buffer and performs the conversion on it 00344 * @e without heap allocation. Return the input buffer. 00345 * 00346 * 00347 * @param[in,out] inoutbfr Existing buffer containing text to be 00348 * translated, also receive output 00349 * 00350 * 00351 * @returns buffer address @b inoutbfr 00352 * 00353 */ 00354 rchar *classpath_external2internal_classname_inplace(rchar *inoutbfr) 00355 { 00356 rint i; 00357 int len = strlen(inoutbfr); 00358 00359 for (i = 0; i < len; i++) 00360 { 00361 /* 00362 * Substitute internal/external delimiter character 00363 * in @b inoutbfr where needed. 00364 */ 00365 if (CLASSNAME_EXTERNAL_DELIMITER_CHAR == inoutbfr[i]) 00366 { 00367 inoutbfr[i] = CLASSNAME_INTERNAL_DELIMITER_CHAR; 00368 } 00369 } 00370 00371 return(inoutbfr); 00372 00373 } /* END of classpath_external2internal_classname_inplace() */ 00374 00375 00376 /*! 00377 * @brief Search @b CLASSPATH for a given class name using a prchar. 00378 * 00379 * Return heap pointer to a buffer containing its location. 00380 * If not found, return @link #rnull rnull@endlink. 00381 * If a class by this name is stored in more than one location, only 00382 * the first location is returned. When done with result, call 00383 * HEAP_FREE_DATA(result) to return buffer to heap area. 00384 * 00385 * All CLASSNAME_EXTERNAL_DELIMITER (ASCII period) characters 00386 * found in the input class name will be unconditionally replaced 00387 * with CLASSNAME_INTERNAL_DELIMITER (ASCII slash) characters. 00388 * Therefore, the class file extension CLASSFILE_EXTENSION_DEFAULT 00389 * may not be appended to the class name. This constraint permits 00390 * both internal and external class names to use the same function 00391 * to search for classes. 00392 * 00393 * 00394 * @param clsname Name of class, without @c @b .class 00395 * extension, as either @c @b some.class.name 00396 * or @c @b some/class/name , that is, 00397 * the internal form of the class name. The string 00398 * may or may not contain class formatting of the 00399 * form @c @b [[[Lsome/class/name; 00400 * 00401 * 00402 * @returns Heap pointer into @b CLASSPATH of directory or JAR file 00403 * containing class (for a regular .class file). 00404 * For a JAR file, report the name of the .jar file 00405 * as for a .class file, but also call classpath_isjar() 00406 * to distinguish between them. Thus the usage is, 00407 * Return @link #rnull rnull@endlink if no match. 00408 * 00409 * @todo VM Spec section 5.3.1: Throw 'NoClassDeffoundError' 00410 * if no match. 00411 * 00412 * Notice that @b clsname must be specified with package 00413 * designations using INTERNAL (slash) delimiter form of 00414 * the path. This is what is natively found in the class 00415 * files. Of course, no package name means the simple 00416 * default package, that is, an unpackaged class having 00417 * no <b><code>package some.package.name</code></b> statement 00418 * in source. 00419 * 00420 * @verbatim 00421 rchar *p = classpath_get_from_prchar( 00422 "some/package/name/SomeClassName"); 00423 00424 if (rnull != p) 00425 { 00426 00427 if (rtrue == classpath_isjar(p)) 00428 { 00429 ** Extract class from JAR file ** 00430 } 00431 else 00432 { 00433 ** Read class file directly ** 00434 } 00435 } 00436 @endverbatim 00437 * 00438 */ 00439 00440 rchar *classpath_get_from_prchar(rchar *clsname) 00441 { 00442 rint i; 00443 struct stat statbfr; 00444 rchar *name; 00445 int baselen; 00446 00447 rchar *class_location = HEAP_GET_DATA(JVMCFG_PATH_MAX, rfalse); 00448 00449 if (rtrue == nts_prchar_isclassformatted(clsname)) 00450 { 00451 /* 00452 * Convert @c @b [[[Lpath/name/ClassName; into 00453 * @c @b path/name/ClassName 00454 */ 00455 jvm_array_dim arraydims = nts_get_prchar_arraydims(clsname); 00456 name = &clsname[1 + arraydims]; 00457 00458 /* Calc position of end-of-class delimiter */ 00459 rchar *pdlm = strchr(name, BASETYPE_CHAR_L_TERM); 00460 baselen = strlen(name); 00461 00462 /* Should @e always be @link #rtrue rtrue@endlink */ 00463 if (rnull != pdlm) 00464 { 00465 baselen = pdlm - name; 00466 } 00467 } 00468 else 00469 { 00470 /* 00471 * If this class name string contained no formatting, 00472 * fake the adjustment above to a formatted class name. 00473 * Notice that without formatting, there cannot be any 00474 * array dimensions. 00475 */ 00476 name = clsname; 00477 baselen = strlen(name); 00478 } 00479 00480 rchar *jarscript = HEAP_GET_DATA(JVMCFG_SCRIPT_MAX, rfalse); 00481 00482 /* 00483 * Search through each entry in @b CLASSPATH for a file by 00484 * the proper name. 00485 */ 00486 00487 for (i = 0; i < classpath_list_len; i++) 00488 { 00489 int clen; 00490 00491 /* Test for JAR files in @b CLASSPATH */ 00492 if (rtrue == classpath_isjar(classpath_list[i])) 00493 { 00494 /* Convert input parm to internal form, append suffix */ 00495 strcpy(class_location, name); 00496 clen = strlen(class_location); 00497 (rvoid) classpath_external2internal_classname_inplace( 00498 class_location); 00499 class_location[clen] = JVMCFG_EXTENSION_DELIMITER_CHAR; 00500 class_location[clen + 1] = '\0'; 00501 strcat(class_location, CLASSFILE_EXTENSION_DEFAULT); 00502 00503 /*! 00504 * @internal Build up JAR command using internal class name 00505 * with suffix. Make @e sure all files are writeable 00506 * for final <b><code>rm -rf</code></b>. 00507 */ 00508 sprintfLocal(jarscript, 00509 JVMCFG_JARFILE_DATA_EXTRACT_SCRIPT, 00510 tmparea_get(), 00511 pjvm->java_home, 00512 pjvm->java_home, 00513 JVMCFG_PATHNAME_DELIMITER_CHAR, 00514 classpath_list[i], 00515 class_location); 00516 00517 int rc = system(jarscript); 00518 00519 if (0 != rc) 00520 { 00521 sysErrMsg("classpath_get_from_prchar", 00522 "Cannot extract '%s' from JAR file %s", 00523 class_location, 00524 classpath_list[i]); 00525 exit_jvm(EXIT_CLASSPATH_JAR); 00526 /*NOTREACHED*/ 00527 } 00528 00529 /* Location of extracted file */ 00530 sprintfLocal(jarscript, 00531 "%s%c%s", 00532 tmparea_get(), 00533 JVMCFG_PATHNAME_DELIMITER_CHAR, 00534 class_location); 00535 00536 rc = stat(jarscript, &statbfr); 00537 00538 /* 00539 * If file was extracted, report result 00540 * in heap-allocated bfr 00541 */ 00542 if (0 == rc) 00543 { 00544 HEAP_FREE_DATA(class_location); 00545 00546 return(jarscript); 00547 } 00548 00549 HEAP_FREE_DATA(jarscript); 00550 } 00551 else 00552 { 00553 /* Convert input parm to internal form */ 00554 sprintfLocal(class_location, 00555 "%s%c\0", 00556 classpath_list[i], 00557 JVMCFG_PATHNAME_DELIMITER_CHAR); 00558 00559 clen = strlen(class_location); 00560 00561 strcat(class_location, name); 00562 00563 /* 00564 * Convert input parm to internal format and append 00565 * class suffix, but convert @e only the @b name part 00566 * just appended, and not if if it is a JAR file. 00567 */ 00568 if (rfalse == classpath_isjar(class_location)) 00569 { 00570 (rvoid) classpath_external2internal_classname_inplace( 00571 &class_location[clen]); 00572 00573 00574 class_location[clen + baselen] = 00575 JVMCFG_EXTENSION_DELIMITER_CHAR; 00576 class_location[clen + baselen + 1] = '\0'; 00577 00578 strcat(class_location, CLASSFILE_EXTENSION_DEFAULT); 00579 } 00580 00581 /* Test for existence of valid class file */ 00582 int rc = stat(class_location, &statbfr); 00583 00584 /* If match found, report result in heap-allocated bfr */ 00585 if (0 == rc) 00586 { 00587 return(class_location); 00588 } 00589 } 00590 } /* for i */ 00591 00592 /* Class not found in @b CLASSPATH */ 00593 HEAP_FREE_DATA(class_location); 00594 return((rchar *) rnull); 00595 00596 } /* END of classpath_get_from_prchar() */ 00597 00598 00599 /*! 00600 * @brief Search @b CLASSPATH for a given class name using 00601 * a CONSTANT_Utf8_info. 00602 * 00603 * Invoke @link #classpath_get_from_prchar() 00604 classpath_get_from_prchar@endlink after converting @b clsname 00605 * from CONSTANT_Utf8_info to prchar. 00606 * 00607 * For more information, see @link #classpath_get_from_prchar() 00608 classpath_get_from_prchar@endlink. 00609 * 00610 * 00611 * @param clsname Name of class, without @c @b .class 00612 * extension, as either @c @b some.class.name 00613 * or @c @b some/class/name , that is, 00614 * the internal form of the class name. The string 00615 * may or may not contain class formatting of the 00616 * form @c @b [[[Lsome/class/name; 00617 * 00618 * 00619 * @returns Heap pointer into @b CLASSPATH of directory or JAR file 00620 * containing class (for a regular .class file). 00621 * 00622 */ 00623 00624 rchar *classpath_get_from_cp_entry_utf(cp_info_dup *clsname) 00625 { 00626 rchar *prchar_clsname = utf_utf2prchar(PTR_THIS_CP_Utf8(clsname)); 00627 00628 rchar *rc = classpath_get_from_prchar(prchar_clsname); 00629 00630 HEAP_FREE_DATA(prchar_clsname); 00631 00632 return(rc); 00633 00634 } /* END of classpath_get_from_cp_entry_utf() */ 00635 00636 00637 /*! 00638 * @brief Shut down the @b CLASSPATH search area of the JVM model after 00639 * JVM execution. 00640 * 00641 * @b Parameters: @link #rvoid rvoid@endlink 00642 * 00643 * 00644 * @returns @link #rvoid rvoid@endlink 00645 * 00646 */ 00647 rvoid classpath_shutdown() 00648 { 00649 rint i; 00650 00651 for (i = 0; i < classpath_list_len; i++) 00652 { 00653 HEAP_FREE_DATA(classpath_list[i]); 00654 } 00655 HEAP_FREE_DATA(classpath_list); 00656 00657 classpath_list = (rchar **) rnull; 00658 classpath_list_len = 0; 00659 00660 /* Declare this module uninitialized */ 00661 jvm_classpath_initialized = rfalse; 00662 00663 return; 00664 00665 } /* END of classpath_shutdown() */ 00666 00667 00668 /* EOF */ 00669