Main Page | Namespace List | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

classpath.c

Go to the documentation of this file.
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 

Generated on Fri Sep 30 18:49:01 2005 by  doxygen 1.4.4