Main Page   Modules   Compound List   File List   Compound Members   File Members   Related Pages  

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

Generated at Mon May 21 08:53:41 2001 for rpm by doxygen1.2.6 written by Dimitri van Heesch, © 1997-2001