# 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 #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.6.8.4 $\";\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   va_start(ap, format);
    00320
    00321   if ( (!FILE_NAME) ) {
    00322
    00323     sprintf (newformat, "\n%s: %s",
    00324              PROGRAM_NAME, format);
    00325
    00326   } else if ( (FILE_NAME) && (!CLASS_NAME) ) {
    00327
    00328     sprintf (newformat, "\n%s: Java classfile: %s, %s",
    00329              PROGRAM_NAME, FILE_NAME, format);
    00330
    00331   } else if (CLASS_NAME) {
    00332     sprintf (newformat, "\n%s: Java classfile: %s, classname: %s, %s",
    00333              PROGRAM_NAME, FILE_NAME, CLASS_NAME, format);
    00334   }
    00335
    00336   vsprintf (newmsg, newformat, 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 }