tools/javadeps.c

Go to the documentation of this file.
00001 /*
00002 RPM and it's source code are covered under two separate licenses. 
00003 
00004 The entire code base may be distributed under the terms of the GNU General
00005 Public License (GPL), which appears immediately below.  Alternatively,
00006 all of the source code in the lib subdirectory of the RPM source code
00007 distribution as well as any code derived from that code may instead be
00008 distributed under the GNU Library General Public License (LGPL), at the
00009 choice of the distributor. The complete text of the LGPL appears
00010 at the bottom of this file.
00011 
00012 This alternatively is allowed to enable applications to be linked against
00013 the RPM library (commonly called librpm) without forcing such applications
00014 to be distributed under the GPL. 
00015 
00016 Any questions regarding the licensing of RPM should be addressed to
00017 [email protected] and [email protected].
00018 */
00019 
00020 /* 
00021    Simple progam for pullng all the referenced java classes out of a
00022    class file.  Java files are supposed to be platform independent, so
00023    this program should work on all platforms.  This code is based on 
00024    the information found in:
00025 
00026        "Java Virtual Machine" by Jon Meyer & Troy Downing.
00027           O'Reilly & Associates, INC. (First Edition, March 1997)
00028           ISBN: 1-56592-194-1
00029 
00030    Jonathan Ross, Ken Estes
00031    Mail.com
00032  */
00033 
00034 
00035 /* 
00036    Remember that: 
00037 
00038    JAR consists of a zip archive, as defined by PKWARE, containing
00039    a manifest file and potentially signature files, as defined in
00040    the Manifest and Signature specification.  So we use infozip's 
00041    'unzip -p' found at http://www.cdrom.com/pub/infozip/.
00042 
00043    Additional documentation, about this fact, at:
00044 
00045    http://java.sun.com/products/jdk/1.1/docs/guide/jar/jarGuide.html
00046    http://java.sun.com/products/jdk/1.2/docs/guide/jar/jarGuide.html
00047    
00048    http://java.sun.com/products/jdk/1.1/docs/guide/jar/manifest.html
00049    http://java.sun.com/products/jdk/1.2/docs/guide/jar/manifest.html
00050    
00051 */
00052 
00053 #include "system.h"
00054 
00055 /*
00056   these includes are for my use, rpm will use #include "system.h"*
00057 */
00058 
00059 /*
00060 #include <stdio.h>
00061 #include <stdlib.h>
00062 #include <string.h>
00063 #include <ctype.h>
00064 #include <netinet/in.h>
00065 */
00066 
00067 #include <stdarg.h>
00068 #include "debug.h"
00069 
00070 /*---------typedefs---------*/
00071 
00072 
00073 /* The symbol table is a waste of memory.. 
00074    but it's easy to code! */
00075 
00076 typedef struct {
00077   short poolSize;
00078   char **stringList;
00079   short *classRef;
00080   short *typeRef;
00081 } symbolTable_t;
00082 
00083 
00084 /*---------Global Variables, in all caps---------*/
00085 
00086 /*name of this program*/
00087 char *PROGRAM_NAME=0;
00088 
00089 /*name of the current class file*/
00090 char *FILE_NAME=0;
00091 
00092 /*the name of the last class file seen*/
00093 char *CLASS_NAME=0;
00094 
00095 /*The string to put before each line, 
00096   this is a formatted version of CLASS_NAME */
00097 char *OUTPUT_PREFIX=0;
00098 
00099 /*arguments chosen*/
00100 int ARG_PROVIDES=0;
00101 int ARG_REQUIRES=0;
00102 int ARG_RPMFORMAT=0;
00103 int ARG_DEPSFORMAT=0;
00104 int ARG_KEYWORDS=0;
00105 int ARG_STARPROV=0;
00106 
00107 /*keywords found in class file*/
00108 char *KEYWORD_VERSION=0;
00109 char *KEYWORD_REVISION=0;
00110 char *KEYWORD_EPOCH=0;
00111 
00112 /* 
00113 
00114    Quantify says over 80 percent of the time of the program is spent
00115    in printf (and the functions it calls) AND I verified that only
00116    about a quarter of the classes found in the dependency analysis are
00117    unique. After the change no function used more then 26% of the over
00118    all time.
00119 
00120    I implement a simple mechanism to remove most duplicate
00121    dependencies.  The print_table is a table of string, with
00122    duplicates, which are to be printed.  Just before the program
00123    exists it is sorted and all unique entries are printed.  If it
00124    fills up during then it is flushed using the same technique as
00125    above.
00126 
00127    The functions which manipulate the table are:
00128         void print_table_flush(void)
00129         void print_table_add(char *str)
00130 
00131 */
00132 
00133 
00134 #define MAX_PRINT_TABLE 10000
00135 char *PRINT_TABLE[MAX_PRINT_TABLE];
00136 int SIZE_PRINT_TABLE;
00137 
00138 /*--------- declare all functions ---------*/
00139 
00140 void usage (void);
00141 void outofmemory(void);
00142 void die(char *format, ...);
00143 size_t my_fread(void *ptr, size_t size, size_t nitems, FILE *stream);
00144 void check_range(short entryNum, short value, short poolSize);
00145 char *is_lower_equal (char *string, char *pattern);
00146 int findJavaMagic (FILE *fileHandle);
00147 int my_strcmp (const void *a, const void *b);
00148 void print_table_flush(void);
00149 void print_table_add(char *str);
00150 char *formatClassName(char *pSomeString, char terminator, char print_star);
00151 void dumpRefType(char *pSomeString);
00152 void genSymbolTable (FILE *fileHandle, symbolTable_t *symbolTable);
00153 void freeSymbolTable (symbolTable_t *symbolTable);
00154 char *findClassName (FILE *fileHandle, symbolTable_t *symbolTable);
00155 void dumpProvides(char *className);
00156 void dumpRequires(symbolTable_t *symbolTable);
00157 void processJavaFile (FILE *fileHandle);
00158 
00159 /*--------- functions ---------*/
00160 
00161 void
00162 usage (void)
00163 {
00164   printf("NAME:\n\tjavadeps - Examine Java class files and\n"
00165          "\t\t\treturn information about their dependencies.\n\n");
00166   printf("USAGE:\n");
00167   printf("\t javadeps { --provides | --requires } \n"
00168          "\t\t   [--rpmformat] [--depsformat] [--keywords] \n"
00169          "\t\t     [--] classfile-name ... \n\n"
00170          "\t javadeps [--help]\n\n");
00171   printf("\n\n");
00172   printf("DESCRIPTION:\n\n");
00173   printf("List the dependencies or the fully qualified class names, of the \n"
00174          "classfiles listed on the command line. \n\n");
00175   printf("OPTIONS:\n\n");
00176   printf("--requires   For each class files listed in the arguments,\n"
00177          " -r          print the list of class files that would be\n"
00178          "             required to run these java programs.  This does not \n"
00179          "             include anyting instantiated by reflection.\n\n");
00180   printf("--provides   For each class files listed in the arguments, \n"
00181          " -p          Print the fully qualified java classes,\n"
00182          "             that they provide.\n\n");
00183   printf("--rpmformat  Format the output to match that used by RPM's \n"
00184          " -F          (Red Hat Package Manager) dependency analysis  \n"
00185          "             database. The default is not --rpmformat.\n\n");
00186   printf("--depsformat print the name of the class which  \n"
00187          " -d          This is mostly used in conjunctions with --requires \n"
00188          "             to list the class file dependencies in a format "
00189          "             similar to traditional Makefile dependencies. The "
00190          "             default is not --depsformat.\n\n");
00191   printf("--keywords   Make use of any keywords embeded in the classfile.\n"
00192          " -k          The default is not --keyword.\n\n");
00193   printf("--starprov   Add the star notation provides to the provides list.\n"
00194          " -s          The default is not --starprov.  This is only for use\n"
00195          "             with (Sun) jhtml dependencies, and since jhtml is \n"
00196          "             deprecated so is this option.\n\n");
00197   printf("--help       Display this page and exit.\n\n");
00198   printf("--           This stops the processing of arguments, making it \n"
00199          "             easier for users to have filenames like '--keywords',\n"
00200          "             without the command line parser getting confused.\n\n");
00201   printf("\n\n");
00202   printf("If any of the class file names in the argument list is '-' then\n"
00203          "<stdin> will be read instead of reading from a file.  The\n"
00204          "contents of <stdin> should be the contents of a class file and \n"
00205          "not a list of class files to read.  It is assumed that when run \n"
00206          "with '-', this program is in a pipeline preceeded by the \n"
00207          "command 'unzip -p filename.jar' so that <stdin> may contain\n"
00208          "the contents of several classfiles concatenated together.\n");
00209   printf("\n\n");
00210   printf("If --keywords is specified then the following strings are \n"
00211          "searched for (case insensitive) in the class file string table\n"
00212          "and, if a string is found with a prefix matching the keyword then \n"
00213          "the dependencies are changed accordingly.  There may be multiple \n"
00214          "string tables entries prefixed with RPM_Provides and RPM_Requires. \n"
00215          "This would indicate that the dependency is the union\n"
00216          "of all entries.\n"
00217          "\n\n"
00218          "Keyword List:\n\n"
00219          "'$Revision: '     This RCS/CVS compatible keyword is assumed to \n"
00220          "                  contain the version number of the class file \n"
00221          "                  it is found in.  Care should be taken with this\n" 
00222          "                  option as RPM's notion of which version is later\n"
00223          "                  may not corrispond with your own, especially\n"
00224          "                  if you use branches. This keyword\n"
00225          "                  only effects the output of --provides and only\n"
00226          "                  when RPM_Version is not defined.\n\n"
00227          "'RPM_Version: '   This is an alternative method of specifing the\n"
00228          "                  version number of the class.  It will override\n"
00229          "                  $Revision if set.   This keyword only effects\n"
00230          "                  the output of --provides \n\n"
00231          "'RPM_Epoch: '     This string contains the epoch to use with the \n"
00232          "                  version number stored in Revision.  If not \n"
00233          "                  specified, the epoch is assumed to be zero.\n"
00234          "                  This keyword only effects the output of\n "
00235          "                  --provides and only when $Revision number is\n"
00236          "                  used.\n\n"
00237          "'RPM_Provides: '  This string lists additional capabilites\n"
00238          "                  provided by the java class.  The string should\n"
00239          "                  be  a white space ([\\t\\v\\n\\r\\f\\ ])\n"
00240          "                  separated list of dependency strings.  Each\n"
00241          "                  dependency string must be of the same format as\n"
00242          "                  would be valid in the Requires or Provides line\n"
00243          "                  of the specfile. This keyword only effects the\n"
00244          "                  output of --provides.\n\n"
00245          "'RPM_Requires: '  This string lists additional requirements of\n"
00246          "                  the java class.  The string should be a white \n"
00247          "                  space ([\\t   \v\\n\\r\\f\\ ]) separated list of \n"
00248          "                  dependency strings.  Each dependency string must\n"
00249          "                  be of the same format as would be valid in the \n"
00250          "                  Requires or Provides line of the specfile.  This\n"
00251          "                  keyword only effects the output of --requires.\n "
00252          "                  \n\n"
00253          "Note that there is no means of setting the release number.  This\n"
00254          "is necessary because release numbers are incremented when the\n"
00255          "source does not change but the package needs to be rebuilt.  So\n"
00256          "relase numbers can not be stored in the source.  The release is\n"
00257          "assumed to be zero. \n\n"
00258          "");
00259   printf("EXAMPLES (Java Keywords): \n\n"
00260          "\t public static final String REVISION = \"$Revision: 2.12 $\";\n"
00261          "\t public static final String EPOCH = \"4\";\n"
00262          "\t public static final String REQUIRES = \"RPM_Requires: "
00263          "java(gnu.regexp.RE) java(com.ibm.site.util.Options)>=1.5\";\n"
00264          "");
00265   printf("\n\n");
00266   printf("EXAMPLES (Arguments): \n\n"
00267          "\tjavadeps --requires -- filename.class\n\n"
00268          "\tjavadeps --provides -- filename.class\n\n"
00269          "\tjavadeps --help\n\n"
00270          "\n"
00271          "\tjavadeps --requires --rpmformat --keywords -- filename.class\n\n"
00272          "\tjavadeps --requires --depsformat -- filename.class\n\n"
00273          "\tjavadeps --requires -- filename1.class filename2.class\n\n"
00274          "\tcat filename2.class | javadeps --requires -- filename1.class -\n\n"
00275          "\tunzip -p filename.jar | javadeps --requires -- - \n\n"
00276          "");
00277   printf("This program is distributed with RPM the Redhat Package \n"
00278          "Managment system.  Further information about RPM can be found at \n"
00279          "\thttp://www.rpm.org/\n\n");
00280   printf("\n\n");
00281   exit(-1);
00282 }
00283 
00284 
00285 void outofmemory(void) {
00286 
00287   /* Its doubtful we could do a printf if there is really a memory
00288     issue but at least halt the program */
00289 
00290   fprintf(stderr, "Could not allocate memory\n");
00291   exit(-1);
00292 }
00293 
00294 
00295 void die(char *format, ...) {
00296   /* Most errors are fatal.
00297      This function throws a fatal error and 
00298      accepts arguments like printf does*/
00299 
00300   char  *newformat = NULL, *newmsg = NULL;
00301   va_list ap;
00302 
00303   if ( 
00304       !(newformat = (char*) malloc(1024)) || 
00305       !(newmsg = (char*) malloc(1024)) 
00306       )
00307     outofmemory();
00308 
00309   /* Rewrite format line, to include additional information.  The
00310      format line we chose depends on how much information is availible
00311      at the time of the error.  Display the maximum ammount of
00312      information. */
00313 
00314   /* notice the FILE_NAME is useless for jar files.  We would want to
00315      print the name of the classfile which caused the error.  That
00316      is hard since we only know that name when we are done parsing
00317      the file, and most errors will occur before that.*/
00318 
00319   if ( (!FILE_NAME) ) {
00320 
00321     sprintf (newformat, "\n%s: %s",
00322              PROGRAM_NAME, format);
00323 
00324   } else if ( (FILE_NAME) && (!CLASS_NAME) ) {
00325     
00326     sprintf (newformat, "\n%s: Java classfile: %s, %s",
00327              PROGRAM_NAME, FILE_NAME, format);
00328     
00329   } else if (CLASS_NAME) {
00330     sprintf (newformat, "\n%s: Java classfile: %s, classname: %s, %s",
00331              PROGRAM_NAME, FILE_NAME, CLASS_NAME, format);
00332   }
00333     
00334   va_start(ap, format);
00335   vsprintf (newmsg, newformat, ap);  
00336   va_end(ap);
00337   
00338   /* print error to where it needs to go:
00339          stdout, stderr, or syslog
00340   */
00341 
00342   fprintf(stderr, newmsg);
00343   
00344   free(newformat);
00345   free(newmsg);
00346   
00347   exit(-1);
00348 }
00349 
00350 
00351 /* wrap fread for safety.  It is a fatal error to get an unexpected
00352    EOF inside a class file. */
00353 
00354 size_t my_fread(void *ptr, size_t size, size_t nitems, FILE *stream) {
00355   size_t rc=0;
00356   /*these variables are helpful in the debugger*/
00357   int eof=0;
00358   int error=0;
00359 
00360 
00361   rc = fread(ptr, size, nitems, stream);
00362   if ( (size!=0) && (rc == 0) ) {
00363     eof = feof(stream);
00364     error = ferror(stream);
00365     die("Error reading from file, or unexpected EOF\n");
00366   }
00367   return rc;
00368 }
00369 
00370 
00371 void check_range(short entryNum, short value, short poolSize) {
00372 
00373   if (value > poolSize) {
00374     die("Symbol Table Entry Number: %d, Value: %d, "
00375         "is out of range of the constant pool. Pool Size: %d\n",
00376         entryNum, value, poolSize);
00377   }
00378   return ;
00379 }
00380  
00381 
00382 
00383 /* If lower case conversion of string is equal to pattern return a
00384    pointer into string, just after the match.  If the string does not
00385    patch the pattern the null pointer is returned.  This does not
00386    change string. 
00387 
00388    This is similar to strcasecmp, but I expect my patterns to be a
00389    prefix of my strings. */
00390 
00391 char 
00392 *is_lower_equal (char *string, char *pattern) 
00393 {
00394   
00395   while ( (tolower(*string) == *pattern) && 
00396           *string && *pattern )  {
00397     string++; 
00398     pattern++;
00399   }
00400 
00401   if ( *pattern == 0 ) {
00402     return string;
00403   } 
00404 
00405   return NULL;
00406 }
00407 
00408 
00409 /*  
00410    Read fileHandle until we find the next instance of the Java
00411    Classfile magic number indicating a java file or find EOF or
00412    fileread error. Since we are reading from stdin which may contain
00413    the concatination of many class files we can not be sure that the
00414    magic number will be the first few bytes.
00415 
00416    Return 1 on success 0 on failure.  */
00417 
00418 #define mod4(num) ( (num) & 3 )
00419 
00420 
00421 int findJavaMagic (FILE *fileHandle)
00422 {
00423   int offset=0;
00424   int foundMagic = 0;
00425   size_t rc;
00426 
00427   /* what were looking for */
00428   unsigned char magicInt[4] = {0xCA, 0xFE, 0xBA, 0xBE};
00429   /*the hex reads in decimal: 202 254 186 190 */
00430 
00431   /* a circular buffer indicating the last few bytes we read */
00432   unsigned char buffer[4] = {0};
00433 
00434   foundMagic = 0;
00435   while( !foundMagic ) {
00436 
00437       rc = fread(&buffer[offset], 1, 1, fileHandle);
00438       if ( !rc ) {
00439 
00440         /* Either this was not a java file or we were given a jar file
00441            and have already found the last java file in it.*/
00442 
00443         if ( feof(fileHandle) ) {
00444           return 0;
00445         }
00446         
00447         if ( ferror(fileHandle) ) {
00448           die ("Error reading character from file.\n");
00449         };
00450 
00451       }
00452       
00453       /* offset points to the most recent char we read so offest+1
00454          points to the oldest char we saved. */
00455 
00456       foundMagic = (
00457                     (magicInt[0] == buffer[mod4(offset+1)]) && 
00458                     (magicInt[1] == buffer[mod4(offset+2)]) && 
00459                     (magicInt[2] == buffer[mod4(offset+3)]) && 
00460                     (magicInt[3] == buffer[mod4(offset+0)]) && 
00461                     1
00462                     );  
00463 
00464       offset = mod4(offset+1);
00465       
00466     } /*end while*/
00467 
00468   return foundMagic;
00469 }
00470 
00471 #undef mod4
00472 
00473 
00474 int
00475 my_strcmp (const void *a, const void *b) {
00476 char **a1; char **b1;
00477 int ret;
00478 
00479 a1 = (char **)a;
00480 b1 = (char **)b;
00481 ret = strcmp(*a1,*b1);
00482   return ret;
00483 }
00484 
00485 /* print the unique strings found in PRINT_TABLE and clear it out */
00486 
00487 void 
00488 print_table_flush(void) {
00489   int i;
00490   char *last_string;
00491 
00492   if (!SIZE_PRINT_TABLE) {
00493     return ;
00494   }
00495 
00496   /* The qsort line gives a warning on some unicies who insist that
00497      strcmp takes arguments of type pointers to void not the
00498      traditional pointers to char. */
00499 
00500   qsort( (void *) PRINT_TABLE, (size_t) SIZE_PRINT_TABLE, 
00501          sizeof(char *), &my_strcmp);
00502 
00503   printf("%s\n",PRINT_TABLE[0]);
00504   last_string = PRINT_TABLE[0];
00505   PRINT_TABLE[0] = NULL;
00506   
00507   for (i = 1; i < SIZE_PRINT_TABLE; i++) {
00508     if ( strcmp(last_string, PRINT_TABLE[i]) ){
00509       printf("%s\n",PRINT_TABLE[i]);
00510       free(last_string);
00511       last_string = PRINT_TABLE[i];
00512     } else {
00513       free(PRINT_TABLE[i]);
00514     }
00515     PRINT_TABLE[i] = NULL;
00516   }
00517   
00518   free(last_string);
00519   SIZE_PRINT_TABLE = 0;
00520   return ; 
00521 }
00522 
00523 
00524 /* add an element to PRINT_TABLE for later printing to stdout.  We do
00525    not make a copy of the string so each string must be unique,
00526    (different calls must pass pointers to different areas of memory)
00527    and the string must not be freed anywhere else in the code and the
00528    string must be from memory which can be freed.*/
00529 
00530 void 
00531 print_table_add(char *str) {
00532 
00533   if (SIZE_PRINT_TABLE == MAX_PRINT_TABLE) {
00534     print_table_flush();
00535   }
00536   
00537   if (OUTPUT_PREFIX) {
00538     char *new_str;
00539 
00540     new_str= (char*) malloc(strlen(OUTPUT_PREFIX)+strlen(str)+1);
00541     if (!new_str){
00542       outofmemory();
00543     }
00544 
00545     new_str[0]='\0';
00546     strcat(new_str,OUTPUT_PREFIX);
00547     strcat(new_str,str);
00548     free(str);
00549     str=new_str;
00550   }
00551 
00552   PRINT_TABLE[SIZE_PRINT_TABLE] = str;
00553   SIZE_PRINT_TABLE++;
00554   return ;
00555 }
00556 
00557 
00558 static void 
00559 print_list(char *in_string) {
00560 
00561   /* This function is no longer needed due to fixes in RPM's
00562      processing of dependencies.  Keep the code until I get a chance
00563      to use RPM3.0 personally */
00564 
00565   if (in_string) {
00566     printf("%s\n", in_string);
00567   }
00568 
00569 /* 
00570    Old function did:
00571 
00572    Given a list separated by whitespace, put each element in the print
00573    table with an added "\n" */
00574 
00575  /*
00576   char *WhiteSpace_Set = "\t\v\n\r\f ";
00577   char *newEnd, *out_string;
00578   int copy_len;
00579 
00580   in_string += strspn(in_string, WhiteSpace_Set); 
00581 
00582   while (*in_string) {
00583     newEnd = strpbrk(in_string, WhiteSpace_Set);
00584     
00585     if  (newEnd) {
00586       copy_len = newEnd-in_string;
00587     } else {
00588       if (*in_string) {
00589         copy_len = strlen(in_string);
00590       } else {
00591         copy_len = 0;
00592       }
00593     }
00594     
00595     out_string = malloc(copy_len+10);
00596     if ( !out_string ) {
00597       outofmemory();
00598     }
00599     out_string[0]= '\0';
00600 
00601     if (copy_len) {
00602       strncat(out_string, in_string, copy_len);
00603       in_string+=copy_len;
00604       strcat(out_string, "\n");
00605       print_table_add(out_string);
00606     }
00607 
00608     in_string += strspn(in_string+copy_len, WhiteSpace_Set);
00609   }
00610 
00611  */
00612  return ;
00613 }
00614 
00615 
00616 /*  Print a properly formatted java class name, and returns the length
00617     of the class string .  Do not print \n here as we may wish to
00618     append the version number IFF we are printing the name of this classfile
00619     
00620     We also provide the class with the leaf node replaced with '*'.
00621     This would not be necessary if we only had to worry about java
00622     Class files.  However our parsing of jhtml files depends on this
00623     information.  This is deprecated since jhtml is deprecated and
00624     this method allows for very inaccurate dependencies.
00625     
00626     Also users may wish to refer to dependencies using star notation
00627     (though this would be less accurate then explicit dependencies
00628     since any leaf class will satify a star dependency).  */
00629 
00630 char
00631 *formatClassName(char *in_string, char terminator, 
00632                  char print_star)
00633 { 
00634   char *leaf_class=0, *out_string=0;
00635   char *ClassName_Break_Set=0;
00636 
00637 
00638   out_string = (char*) malloc(strlen(in_string) + 10);
00639   if ( !out_string ) {
00640     outofmemory();
00641   }
00642   out_string[0]= '\0';
00643   
00644   /*these characters end the current parse of the string in function
00645     formatClassName.*/
00646   
00647   ClassName_Break_Set = (char*) malloc(3);
00648   if ( !ClassName_Break_Set ) {
00649     outofmemory();
00650   }
00651   ClassName_Break_Set[0] = '/';
00652   /*terminator can be '\0' this must go after '/'*/
00653   ClassName_Break_Set[1] = terminator;
00654   ClassName_Break_Set[2] = '\0';
00655   
00656   if(ARG_RPMFORMAT) {
00657     strcat(out_string, "java(");
00658   }
00659   if (print_star) {
00660     /* print the path to the leaf class but do not print the leaf
00661        class, stop at the last '/' we fix this back below*/
00662     leaf_class = strrchr(in_string, '/');
00663     if (leaf_class) {
00664       leaf_class[0] = terminator;
00665     }
00666   }
00667   
00668   while (*in_string != terminator) {
00669     char *newEnd=0;
00670     int copy_len;
00671 
00672     /* handle the break_set */
00673 
00674     if (in_string[0] == '\0' ) {
00675       die("Classname does not terminate with: '%c', '%s'\n", 
00676           terminator, in_string);
00677     } else {
00678       if (in_string[0] == '/' ) {
00679         /* convert '/' to '.' */
00680         strcat(out_string, ".");
00681         in_string++;
00682       }
00683     }
00684 
00685     newEnd = strpbrk(in_string, ClassName_Break_Set);
00686 
00687     if  (newEnd) {
00688       copy_len = newEnd-in_string;
00689     } else {
00690       if (terminator == '\0') {
00691         copy_len = strlen(in_string);
00692       } else {
00693         copy_len = 0;
00694       }
00695     }
00696     
00697     /* handle upto but not including the break_set*/
00698     if (copy_len) {
00699       strncat(out_string, in_string, copy_len);
00700       in_string+=copy_len;
00701     }
00702 
00703   } /*end while*/
00704   
00705   if (leaf_class) {
00706     /* print the star and fix the leaf class*/
00707     strcat(out_string, ".*"); 
00708     leaf_class[0] = '/';
00709   }
00710   if(ARG_RPMFORMAT) {
00711     strcat(out_string, ")");
00712   }
00713 
00714   free(ClassName_Break_Set);
00715   return out_string;
00716 }
00717 
00718 
00719 /* Parse out just the class names from a java type and moves the string
00720    pointer to the end of the string. */
00721 
00722 void
00723 dumpRefType(char *string)
00724 {
00725   /* All class types start with a 'L' and and end with a ';'. We want
00726      everyting in between.  There might be more then one per string
00727      like (params for a method call) */
00728 
00729   string = strchr(string, 'L');
00730   while (string) {
00731     string++;
00732     print_table_add(formatClassName(string, ';', 0));
00733     string = strchr(string, ';');
00734     string = strchr(string, 'L');
00735   }
00736   
00737   return ;
00738 }
00739 
00740 
00741 /* Print out the classes referenced in the symbol table */
00742 
00743 void
00744 dumpRequires(symbolTable_t *symbolTable) {
00745   int tem;
00746   int ref = 0;
00747 
00748   for(tem=1; tem < symbolTable->poolSize; tem++ ) {
00749 
00750     /* dump all the classes in the const table. */
00751     ref = symbolTable->classRef[tem];
00752     if(ref) {
00753       char *string = symbolTable->stringList[ref];
00754       if( !*string ) {
00755         die("class num: %d, referenced string num: %d, "
00756             "which is null.\n",
00757             tem, ref);
00758       }
00759       if ( string[0] == '[' ) {
00760         /*
00761           This is an array. We need to ingore 
00762           strings like:
00763                '[B'
00764                '[[B'
00765                '[[I'
00766                
00767           This hack leaves blank lines in the output 
00768           when a string not containing a class is
00769           sent to dumpRefType.
00770         */
00771         string = strchr(string, 'L');
00772         if (string) {
00773           dumpRefType(string);
00774         }
00775       } else {
00776         print_table_add(formatClassName(string, '\0', 0));
00777       }
00778     }
00779     
00780     /* dump all the references */
00781     ref = symbolTable->typeRef[tem];
00782     if (ref) {
00783       char *string = symbolTable->stringList[ref];
00784       if ( !*string ) {
00785         die("type num: %d, referenced string num: %d, "
00786             "which is null.\n",
00787             tem, ref);
00788       }
00789       /* this is a java type... parse out the class names */
00790       dumpRefType(string);
00791     } 
00792     
00793   } /*end for*/
00794   
00795   return ;
00796 }
00797 
00798 
00799 /* Read a java class files symbol table into memory. 
00800 */
00801 
00802 
00803 void genSymbolTable (FILE *fileHandle, symbolTable_t *symbolTable)
00804 {
00805   char ignore[10];
00806   int i=0;
00807   
00808 
00809   /* We are called just after fileHandle saw the magic number, seek a
00810      few bytes in to find the poolsize */
00811 
00812   my_fread(&ignore, 4, 1, fileHandle);
00813 
00814   my_fread(&(symbolTable->poolSize), 2, 1, fileHandle);
00815   symbolTable->poolSize=ntohs(symbolTable->poolSize);
00816 
00817   /* new the tables */
00818 
00819   symbolTable->stringList = (char**) calloc(symbolTable->poolSize, 
00820                                             sizeof(char*));
00821   if(!symbolTable->stringList){
00822     outofmemory();
00823   }
00824   
00825   symbolTable->classRef = (short*) calloc(symbolTable->poolSize,
00826                                           sizeof(short*));
00827   if(!symbolTable->classRef){
00828     outofmemory();
00829   }
00830   
00831   symbolTable->typeRef = (short*) calloc(symbolTable->poolSize,
00832                                          sizeof(short*));
00833   if(!symbolTable->typeRef){
00834     outofmemory();
00835   }
00836   
00837   /* zero 'em all out. */
00838   for(i=0; i < symbolTable->poolSize; i++) {
00839     symbolTable->stringList[i] = NULL;
00840     symbolTable->classRef[i] = 0;
00841     symbolTable->typeRef[i] = 0;
00842   }
00843   
00844   
00845   /* for the number of entries
00846        (it starts at 1)  
00847   */
00848 
00849   for(i=1; i < symbolTable->poolSize; i++) {
00850     unsigned short type = 0;
00851     unsigned short value = 0;
00852     unsigned char tag = 0;
00853 
00854       /* read the type of this entry  */
00855 
00856       my_fread(&tag, 1, 1, fileHandle);
00857       switch(tag) {
00858         case 1: /* master string pool. */
00859           {
00860             /* record all these strings */
00861                 char *someString;
00862                 unsigned short length = 0;
00863 
00864                 /* I am not sure if these strings must be null
00865                    terminated.  I termiante them to be safe. */
00866 
00867                 my_fread(&length, 2, 1, fileHandle);
00868                 length=ntohs(length);
00869 
00870                 someString = (char*) malloc(length+1);
00871                 if(!someString){
00872                   outofmemory();
00873                 }
00874                 my_fread(someString, length, 1, fileHandle);
00875                 someString[length]=0;
00876                 symbolTable->stringList[i] = someString;
00877                 
00878                 if (ARG_KEYWORDS) {
00879                   char *ptr=0;
00880 
00881                   /* Each keyword can appear multiple times.  Don't
00882                     bother with datastructures to store these strings,
00883                     if we need to print it print it now.  */
00884 
00885                   /* it would be better if instead of printing the
00886                      strings "raw" I turn the space separated list
00887                      into a "\n" separated list*/
00888                   
00889                   if (ARG_REQUIRES) {
00890                     ptr = is_lower_equal(someString, "rpm_requires: ");
00891                     if(ptr){
00892                       print_list(ptr);
00893                     }
00894                   }
00895                   if (ARG_PROVIDES) {
00896                     ptr = is_lower_equal(someString, "rpm_provides: ");
00897                     if(ptr){
00898                       print_list(ptr);
00899                     }
00900                   }
00901                   /* I wish there was a good way to handle this
00902                   ptr = is_lower_equal(someString, "rpm_conflicts: ");
00903                   */
00904                   ptr = is_lower_equal(someString, "$revision: ");
00905                   if(ptr){
00906                     KEYWORD_REVISION=ptr;
00907                     /* terminate the string before " $" */
00908                     ptr = strchr(KEYWORD_REVISION, ' ');
00909                     if (ptr) {
00910                       *ptr = 0;
00911                     }
00912                   }
00913                   ptr = is_lower_equal(someString, "rpm_version: ");
00914                   if(ptr){
00915                     KEYWORD_VERSION=ptr;
00916                     /* terminate the string at first whitespace */
00917                     ptr = strchr(KEYWORD_VERSION, ' ');
00918                     if (ptr) {
00919                       *ptr = 0;
00920                     }
00921                   }
00922                   ptr = is_lower_equal(someString, "rpm_epoch: ");
00923                   if(ptr){
00924                     KEYWORD_EPOCH=ptr;
00925                     /* terminate the string at first whitespace */
00926                     ptr = strchr(KEYWORD_EPOCH, ' ');
00927                     if (ptr) {
00928                       *ptr = 0;
00929                     }
00930                   }
00931                 }
00932                 break;
00933           }
00934       case 2:   /* unknow type!! */
00935           die("Unknown type in constant table. "
00936               "Entry: %d. \n", i);
00937           break;
00938         case 3: /* int */
00939           my_fread(&ignore, 4, 1, fileHandle);
00940           break;
00941         case 4: /* float */
00942           my_fread(&ignore, 4, 1, fileHandle);
00943           break;
00944         case 5: /* long (counts as 2) */
00945           my_fread(&ignore, 8, 1, fileHandle);
00946           i++;
00947           break;
00948         case 6: /* double (counts as 2) */
00949           my_fread(&ignore, 8, 1, fileHandle);
00950           i++;
00951           break;
00952         case 7: /* Class */
00953           my_fread(&value, 2, 1, fileHandle);
00954           symbolTable->classRef[i]=ntohs(value);
00955           /* record which const it's referencing */
00956           check_range(i, symbolTable->classRef[i], symbolTable->poolSize);
00957           break;
00958         case 8: /* String */
00959           my_fread(&ignore, 2, 1, fileHandle);
00960           break;
00961         case 9: /* field reference */
00962           my_fread(&ignore, 4, 1, fileHandle);
00963           break;
00964         case 10: /* method reference */
00965           my_fread(&ignore, 4, 1, fileHandle);
00966           break;
00967         case 11: /* interface method reference */
00968           my_fread(&ignore, 4, 1, fileHandle);
00969           break;
00970         case 12: /* constant name/type */
00971           my_fread(&ignore, 2, 1, fileHandle);
00972           my_fread(&type, 2, 1, fileHandle);
00973           symbolTable->typeRef[i]=ntohs(type);
00974           /* record the name, and the type it's referencing. */
00975           check_range(i, symbolTable->typeRef[i], symbolTable->poolSize);
00976           break;
00977         default:
00978           die("Unknown tag type: %d.\n",
00979               "Entry: %d. \n", tag, i);
00980           break;
00981       }
00982   }
00983 
00984   return ;  
00985 }
00986  
00987 
00988 /*  Find the proper name of the current Java Class file and return a
00989     formatted of the name. 
00990 */
00991 
00992 char*
00993 findClassName (FILE *fileHandle, symbolTable_t *symbolTable) {
00994   char ignore[10];
00995   unsigned short type = 0;
00996   unsigned short class = 0;
00997   char *className;
00998 
00999   /* seek a little past the end of the table */
01000   
01001   my_fread(&ignore, 2, 1, fileHandle);
01002   
01003   /* read the name of this classfile */
01004   
01005   my_fread(&type, 2, 1, fileHandle);
01006   type=ntohs(type);
01007   class = symbolTable->classRef[type];
01008   if( !class ||
01009       !symbolTable->stringList[class] ) {
01010       die("Couln't find class: %d, provided by file.\n", class);
01011   }
01012 
01013   className=symbolTable->stringList[class];
01014   
01015   return className;
01016 
01017 }
01018 
01019 
01020 /*  Print the name of the class that we have found and whos symbol
01021    table is loaded into memory.
01022 
01023    The name may need to have the version number printed after it, if
01024    there are keywords in the symbol table.  
01025 
01026    Additionally the class name may need to be printed twice, once with
01027    the regular name of the class and once with the leaf class replaced
01028    with a star.
01029 
01030 */
01031 
01032 void
01033 dumpProvides(char *className) {
01034   char *out_string;
01035 #ifdef  UNUSED
01036   char *formattedClassName;
01037   char *newline;
01038 #endif
01039 
01040   /* Provide the star version of this class for jhtml
01041      dependencies. This option is deprecated since jhtml is
01042      deprecated. */
01043   
01044   if (ARG_STARPROV) {
01045     print_table_add(formatClassName(className, '\0', 1));
01046   }
01047 
01048   /* If we have information about the version number of this class
01049      then add it to the formatted name and print this as well. */
01050 
01051   out_string = formatClassName(className, '\0', 0);
01052 
01053   {
01054     int len = 10;
01055 
01056     if (out_string) {
01057       len += strlen(out_string);
01058     }
01059     if (KEYWORD_EPOCH) {
01060       len += strlen(KEYWORD_EPOCH);
01061     }
01062     if (KEYWORD_VERSION) {
01063       len += strlen(KEYWORD_VERSION);
01064     }
01065     if (KEYWORD_REVISION) {
01066       len += strlen(KEYWORD_REVISION);
01067     }
01068     
01069     out_string = realloc(out_string, len );
01070     if (!out_string){
01071       outofmemory();
01072     }
01073   
01074   }
01075 
01076   if( KEYWORD_VERSION || KEYWORD_REVISION ){
01077     /* It is easier to remove the extra new line here in one place
01078        then to try and add a newline every where that formatClassName
01079        is called */
01080     char *newline;
01081 
01082     /* I am not using rpm 3.0 yet so I need both the dependencies with
01083        and without the version numbers, when I upgrade I will remove
01084        this block (with copy_string) and change the "=" to " = " ten
01085        lines down.*/
01086     {
01087       char *copy_string;
01088       copy_string = (char*) malloc(strlen(out_string)+1);
01089       if (!copy_string){
01090         outofmemory();
01091       }
01092       copy_string = strcpy(copy_string, out_string);
01093       print_table_add(copy_string);
01094     }
01095 
01096     newline = strrchr(out_string, '\n');
01097     if (newline) {
01098       newline[0] = '\0';
01099     }
01100     strcat(out_string, " = ");
01101     if(KEYWORD_EPOCH){
01102       strcat(out_string, KEYWORD_EPOCH);
01103       strcat(out_string, ":");
01104     }
01105     if(KEYWORD_VERSION){
01106       strcat(out_string, KEYWORD_VERSION);
01107     } else {
01108       strcat(out_string, KEYWORD_REVISION);
01109     }
01110     strcat(out_string, "\n");
01111   }
01112 
01113   print_table_add(out_string);
01114   out_string=NULL;
01115 
01116   return ;  
01117 }
01118 
01119 
01120 
01121 
01122 
01123 void freeSymbolTable (symbolTable_t *symbolTable)
01124 {  
01125   int i=0;
01126 
01127   for(i=1; i < symbolTable->poolSize; i++) {
01128     if( symbolTable->stringList[i] ) {
01129       free(symbolTable->stringList[i]);
01130       symbolTable->stringList[i] = 0;
01131     }
01132   }
01133   
01134   free(symbolTable->stringList);
01135   symbolTable->stringList=0;
01136   
01137   free(symbolTable->classRef);
01138   symbolTable->classRef=0;
01139   
01140   free(symbolTable->typeRef);
01141   symbolTable->typeRef=0;
01142   
01143   return ;
01144 }
01145 
01146 
01147 /* Process each file.
01148    This procedure must be called directly after finding 
01149    the magic number.
01150 */
01151 
01152 void processJavaFile (FILE *fileHandle) {
01153   symbolTable_t symbolTable= {0};
01154   char *format_class_name;
01155 
01156   genSymbolTable(fileHandle, &symbolTable);
01157   CLASS_NAME=findClassName(fileHandle, &symbolTable);
01158   
01159   if(ARG_DEPSFORMAT) {
01160     char *prefix_seperator = ": ";
01161 
01162     format_class_name = formatClassName(CLASS_NAME, '\0', 0);
01163 
01164     OUTPUT_PREFIX = (char*) malloc(strlen(format_class_name)+
01165                                    strlen(prefix_seperator)+1);
01166     if (!OUTPUT_PREFIX){
01167       outofmemory();
01168     }
01169     OUTPUT_PREFIX = strcpy(OUTPUT_PREFIX, format_class_name);
01170     strcat(OUTPUT_PREFIX, prefix_seperator);
01171   }
01172 
01173   if(ARG_PROVIDES) {
01174     dumpProvides(CLASS_NAME);
01175   }
01176   if(ARG_REQUIRES) {
01177     dumpRequires(&symbolTable);
01178   }
01179 
01180   free(OUTPUT_PREFIX);
01181   OUTPUT_PREFIX = 0;
01182   freeSymbolTable(&symbolTable);
01183 
01184   return ;
01185 
01186 }
01187 
01188 
01189 int
01190 main(int argc, char **argv)
01191 {
01192   FILE *fileHandle;
01193   int i = 0;
01194   int rc = 0;
01195   int foundMagic=0;
01196 
01197   PROGRAM_NAME=argv[0];
01198   
01199   if(argv[1] == NULL) {
01200     usage();
01201   }
01202   
01203   /* parse arguments which are not filenames*/
01204   
01205   for (i = 1; argv[i] != NULL; i++) {
01206     
01207     if (0) {
01208       /* 
01209          First entry a dummy to get the 
01210          other entries to align correctly
01211       */
01212       ;
01213     } else if ( !strcmp("-p",argv[i]) || !strcmp("--provides",argv[i]) ) {
01214       ARG_PROVIDES = 1;
01215     } else if ( !strcmp("-r",argv[i]) || !strcmp("--requires",argv[i]) ) {
01216       ARG_REQUIRES = 1;
01217     } else if ( !strcmp("-h",argv[i]) || !strcmp("--help",argv[i]) ||
01218                 !strcmp("-?",argv[i]) ) {
01219                 
01220       usage();
01221     } else if ( !strcmp("-F",argv[i]) || !strcmp("--rpmformat",argv[i]) ) {
01222       ARG_RPMFORMAT=1;
01223     } else if ( !strcmp("-d",argv[i]) || !strcmp("--depsformat",argv[i]) ) {
01224       ARG_DEPSFORMAT=1;
01225     } else if ( !strcmp("-k",argv[i]) || !strcmp("--keywords",argv[i]) ) {
01226       ARG_KEYWORDS=1;
01227     } else if ( !strcmp("-s",argv[i]) || !strcmp("--starprov",argv[i]) ) {
01228       ARG_STARPROV=1;
01229     } else if ( !strcmp("--",argv[i]) ) {
01230       i++;
01231       break;      
01232     } else {
01233       /* we do not recognize the argument, must be a filename*/
01234       break;
01235     }
01236   } /*end for arguments which are not filenames*/
01237   
01238   /* check arguments for consistancy */
01239 
01240   if ( !ARG_PROVIDES && !ARG_REQUIRES ) {
01241     die ("Must specify either --provides or --requires.\n");
01242   }
01243   
01244   if ( ARG_PROVIDES && ARG_REQUIRES ) {
01245     die ("Can not specify both --provides and --requires.\n");
01246   }
01247   
01248   if ( ARG_REQUIRES && ARG_STARPROV) {
01249     die ("Can not specify both --requires and --starpov.\n");
01250   }
01251   
01252   if(argv[i] == NULL) {
01253     die ("Must specify Java class files.\n");
01254   }
01255   
01256   /* parse arguments which are filenames.  */
01257 
01258   for ( /*null initializer*/; argv[i] != NULL; i++) {
01259     
01260     /*open the correct file and process it*/
01261     
01262     if ( !strcmp("-", argv[i]) ) {
01263       /* use stdin, might be a jar file */
01264       fileHandle = stdin;
01265       FILE_NAME = "<stdin>";
01266 
01267       foundMagic = findJavaMagic(fileHandle);      
01268       while (foundMagic) {      
01269         processJavaFile(fileHandle);
01270         foundMagic = findJavaMagic(fileHandle);
01271       } 
01272     } else {
01273       /* Open a disk file*/
01274       fileHandle = fopen(argv[i], "r");
01275       if( fileHandle == 0 ) {
01276         die ("Could not open file: %s.\n", argv[i]);
01277       }
01278       fileHandle = fileHandle;
01279       FILE_NAME = argv[i];      
01280 
01281       foundMagic = findJavaMagic(fileHandle);      
01282       if (foundMagic) { 
01283         processJavaFile(fileHandle);
01284       }
01285     }
01286 
01287     rc = fclose(fileHandle);
01288     if( rc ) {
01289       die ("Could not close file: %s.\n", FILE_NAME);
01290     }
01291     CLASS_NAME=0;    
01292   } /*end parsing arguments which are filenames*/
01293   
01294   print_table_flush();
01295   return 0;
01296 }

Generated on Fri Oct 12 08:44:55 2007 for rpm by  doxygen 1.5.2