Sync with rpm-4_0 branch.
[platform/upstream/rpm.git] / tools / javadeps.c
1 /*
2 RPM and it's source code are covered under two separate licenses. 
3
4 The entire code base may be distributed under the terms of the GNU General
5 Public License (GPL), which appears immediately below.  Alternatively,
6 all of the source code in the lib subdirectory of the RPM source code
7 distribution as well as any code derived from that code may instead be
8 distributed under the GNU Library General Public License (LGPL), at the
9 choice of the distributor. The complete text of the LGPL appears
10 at the bottom of this file.
11
12 This alternatively is allowed to enable applications to be linked against
13 the RPM library (commonly called librpm) without forcing such applications
14 to be distributed under the GPL. 
15
16 Any questions regarding the licensing of RPM should be addressed to
17 marc@redhat.com and ewt@redhat.com.
18 */
19
20 /* 
21    Simple progam for pullng all the referenced java classes out of a
22    class file.  Java files are supposed to be platform independent, so
23    this program should work on all platforms.  This code is based on 
24    the information found in:
25
26        "Java Virtual Machine" by Jon Meyer & Troy Downing.
27           O'Reilly & Associates, INC. (First Edition, March 1997)
28           ISBN: 1-56592-194-1
29
30    Jonathan Ross, Ken Estes
31    Mail.com
32  */
33
34
35 /* 
36    Remember that: 
37
38    JAR consists of a zip archive, as defined by PKWARE, containing
39    a manifest file and potentially signature files, as defined in
40    the Manifest and Signature specification.  So we use infozip's 
41    'unzip -p' found at http://www.cdrom.com/pub/infozip/.
42
43    Additional documentation, about this fact, at:
44
45    http://java.sun.com/products/jdk/1.1/docs/guide/jar/jarGuide.html
46    http://java.sun.com/products/jdk/1.2/docs/guide/jar/jarGuide.html
47    
48    http://java.sun.com/products/jdk/1.1/docs/guide/jar/manifest.html
49    http://java.sun.com/products/jdk/1.2/docs/guide/jar/manifest.html
50    
51 */
52
53 #include "system.h"
54
55 /*
56   these includes are for my use, rpm will use #include "system.h"*
57 */
58
59 /*
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <ctype.h>
64 */
65
66 #include <stdarg.h>
67 #include "debug.h"
68
69 /*---------typedefs---------*/
70
71
72 /* The symbol table is a waste of memory.. 
73    but it's easy to code! */
74
75 typedef struct {
76   short poolSize;
77   char **stringList;
78   short *classRef;
79   short *typeRef;
80 } symbolTable_t;
81
82
83 /*---------Global Variables, in all caps---------*/
84
85 /*name of this program*/
86 char *PROGRAM_NAME=0;
87
88 /*name of the current class file*/
89 char *FILE_NAME=0;
90
91 /*the name of the last class file seen*/
92 char *CLASS_NAME=0;
93
94 /*arguments chosen*/
95 int ARG_PROVIDES=0;
96 int ARG_REQUIRES=0;
97 int ARG_RPMFORMAT=0;
98 int ARG_KEYWORDS=0;
99 int ARG_STARPROV=0;
100
101 /*keywords found in class file*/
102 char *KEYWORD_VERSION=0;
103 char *KEYWORD_REVISION=0;
104 char *KEYWORD_EPOCH=0;
105
106 /* 
107
108    Quantify says over 80 percent of the time of the program is spent
109    in printf (and the functions it calls) AND I verified that only
110    about a quarter of the classes found in the dependency analysis are
111    unique. After the change no function used more then 26% of the over
112    all time.
113
114    I implement a simple mechanism to remove most duplicate dependencies.
115    The print_table is a table of string which are to be printed.  Just
116    before the program exists it is sorted and all unique entries are
117    printed.  If it fills up during then it is flushed using the same
118    technique as above. 
119
120    The functions which manipulate the table are:
121         void print_table_flush(void)
122         void print_table_add(char *str)
123
124 */
125
126
127 #define MAX_PRINT_TABLE 10000
128 char *PRINT_TABLE[MAX_PRINT_TABLE];
129 int SIZE_PRINT_TABLE;
130
131 /*--------- declare all functions ---------*/
132
133 void usage (void);
134 void outofmemory(void);
135 void die(char *format, ...);
136 size_t my_fread(void *ptr, size_t size, size_t nitems, FILE *stream);
137 void check_range(short value, short poolSize);
138 char *is_lower_equal (char *string, char *pattern);
139 int findJavaMagic (FILE *fileHandle);
140 int my_strcmp (const void *a, const void *b);
141 void print_table_flush(void);
142 void print_table_add(char *str);
143 char *formatClassName(char *pSomeString, char terminator, char print_star);
144 void dumpRefType(char *pSomeString);
145 void dumpRequires(symbolTable_t *symbolTable);
146 void genSymbolTable (FILE *fileHandle, symbolTable_t *symbolTable);
147 void findClassName  (FILE *fileHandle, symbolTable_t *symbolTable);
148 void freeSymbolTable (symbolTable_t *symbolTable);
149 void processJavaFile (FILE *fileHandle);
150
151 /*--------- functions ---------*/
152
153 void
154 usage (void)
155 {
156   printf("NAME:\n\tjavadeps - Examine Java class files and\n"
157          "\t\t\treturn information about their dependencies.\n\n");
158   printf("USAGE:\n");
159   printf("\t javadeps { --provides | --requires } \n"
160          "\t\t   [--rpmformat] [--keywords] \n"
161          "\t\t     [--] classfile-name ... \n\n"
162          "\t javadeps [--help]\n\n");
163   printf("\n\n");
164   printf("DESCRIPTION:\n\n");
165   printf("List the dependencies or the fully qualified class names, of the \n"
166          "classfiles listed on the command line. \n\n");
167   printf("OPTIONS:\n\n");
168   printf("--requires  For each class files listed in the arguments,\n"
169          " -r         print the list of class files that would be\n"
170          "            required to run these java programs.  This does not \n"
171          "            include anyting instantiated by reflection.\n\n");
172   printf("--provides  For each class files listed in the arguments, \n"
173          " -p         Print the fully qualified java classes,\n"
174          "            that they provide.\n\n");
175   printf("--rpmformat format the output to match that used by RPM's \n"
176          " -F         (Red Hat Package Manager) dependency analysis  \n"
177          "            database. The default is not --rpmformat.\n\n");
178   printf("--keywords  Make use of any keywords embeded in the classfile.\n"
179          " -k         The default is not --keyword.\n\n");
180   printf("--starprov  Add the star notation provides to the provides list.\n"
181          " -s         The default is not --starprov.  This is only for use\n"
182          "            with (Sun) jhtml dependencies, and since jhtml is \n"
183          "            deprecated so is this option.\n\n");
184   printf("--help      Display this page and exit.\n\n");
185   printf("--          This stops the processing of arguments, making it \n"
186          "            easier for users to have filenames like '--keywords',\n"
187          "            without the command line parser getting confused.\n\n");
188   printf("\n\n");
189   printf("If any of the class file names in the argument list is '-' then\n"
190          "<stdin> will be read instead of reading from a file.  The\n"
191          "contents of <stdin> should be the contents of a class file and \n"
192          "not a list of class files to read.  It is assumed that when run \n"
193          "with '-', this program is in a pipeline preceeded by the \n"
194          "command 'unzip -p filename.jar' so that <stdin> may contain\n"
195          "the contents of several classfiles concatenated together.\n");
196   printf("\n\n");
197   printf("If --keywords is specified then the following strings are \n"
198          "searched for (case insensitive) in the class file string table\n"
199          "and, if a string is found with a prefix matching the keyword then \n"
200          "the dependencies are changed accordingly.  There may be multiple \n"
201          "string tables entries prefixed with RPM_Provides and RPM_Requires. \n"
202          "This would indicate that the dependency is the union\n"
203          "of all entries.\n"
204          "\n\n"
205          "Keyword List:\n\n"
206          "'$Revision: '     This RCS/CVS compatible keyword is assumed to \n"
207          "                  contain the version number of the class file \n"
208          "                  it is found in.  Care should be taken with this\n" 
209          "                  option as RPM's notion of which version is later\n"
210          "                  may not corrispond with your own, especially\n"
211          "                  if you use branches. This keyword\n"
212          "                  only effects the output of --provides and only\n"
213          "                  when RPM_Version is not defined.\n\n"
214          "'RPM_Version: '   This is an alternative method of specifing the\n"
215          "                  version number of the class.  It will override\n"
216          "                  $Revision if set.   This keyword only effects\n"
217          "                  the output of --provides \n\n"
218          "'RPM_Epoch: '     This string contains the epoch to use with the \n"
219          "                  version number stored in Revision.  If not \n"
220          "                  specified, the epoch is assumed to be zero.\n"
221          "                  This keyword only effects the output of\n "
222          "                  --provides and only when $Revision number is\n"
223          "                  used.\n\n"
224          "'RPM_Provides: '  This string lists additional capabilites\n"
225          "                  provided by the java class.  The string should\n"
226          "                  be  a white space ([\\t\\v\\n\\r\\f\\ ])\n"
227          "                  separated list of dependency strings.  Each\n"
228          "                  dependency string must be of the same format as\n"
229          "                  would be valid in the Requires or Provides line\n"
230          "                  of the specfile. This keyword only effects the\n"
231          "                  output of --provides.\n\n"
232          "'RPM_Requires: '  This string lists additional requirements of\n"
233          "                  the java class.  The string should be a white \n"
234          "                  space ([\\t\v\\n\\r\\f\\ ]) separated list of \n"
235          "                  dependency strings.  Each dependency string must\n"
236          "                  be of the same format as would be valid in the \n"
237          "                  Requires or Provides line of the specfile.  This\n"
238          "                  keyword only effects the output of --requires.\n "
239          "                  \n\n"
240          "Note that there is no means of setting the release number.  This\n"
241          "is necessary because release numbers are incremented when the\n"
242          "source does not change but the package needs to be rebuilt.  So\n"
243          "relase numbers can not be stored in the source.  The release is\n"
244          "assumed to be zero. \n\n"
245          "");
246   printf("EXAMPLES (Java Keywords): \n\n"
247          "\t public static final String REVISION = \"$Revision: 2.8 $\";\n"
248          "\t public static final String EPOCH = \"4\";\n"
249          "\t public static final String REQUIRES = \"RPM_Requires: "
250          "java(gnu.regexp.RE) java(com.ibm.site.util.Options)>=1.5\";\n"
251          "");
252   printf("\n\n");
253   printf("EXAMPLES (Arguments): \n\n"
254          "\tjavadeps --requires -- filename.class\n\n"
255          "\tjavadeps --provides -- filename.class\n\n"
256          "\tjavadeps --help\n\n"
257          "\n"
258          "\tjavadeps --requires --rpmformat --keywords -- filename.class\n\n"
259          "\tjavadeps --requires -- filename1.class filename2.class\n\n"
260          "\tcat filename2.class | javadeps --requires -- filename1.class -\n\n"
261          "\tunzip -p filename.jar | javadeps --requires -- - \n\n"
262          "");
263   printf("This program is distributed with RPM the Redhat Package \n"
264          "Managment system.  Further information about RPM can be found at \n"
265          "\thttp://www.rpm.org/\n\n");
266   printf("\n\n");
267   exit(-1);
268 }
269
270
271 void outofmemory(void) {
272
273   /* Its doubtful we could do a printf if there is really a memory
274     issue but at least halt the program */
275
276   fprintf(stderr, "Could not allocate memory");
277   exit(-1);
278 }
279
280
281 void die(char *format, ...) {
282   /* Most errors are fatal.
283      This function throws a fatal error and 
284      accepts arguments like printf does*/
285
286   char  *newformat = NULL, *newmsg = NULL;
287   va_list ap;
288
289   if ( !(newformat = malloc(1024)) || !(newmsg = malloc(1024)) )
290     outofmemory();
291
292   /* Rewrite format line, to include additional information.  The
293      format line we chose depends on how much information is availible
294      at the time of the error.  Display the maximum ammount of
295      information. */
296
297   /* notice the FILE_NAME is useless for jar files.  We would want to
298      print the name of the classfile which caused the error.  That
299      is hard since we only know that name when we are done parsing
300      the file, and most errors will occur before that.*/
301
302   va_start(ap, format);
303   
304   if ( (!FILE_NAME) ) {
305
306     sprintf (newformat, "\n%s: %s",
307              PROGRAM_NAME, format);
308
309   } else if ( (FILE_NAME) && (!CLASS_NAME) ) {
310     
311     sprintf (newformat, "\n%s: Java classfile: %s, %s",
312              PROGRAM_NAME, FILE_NAME, format);
313     
314   } else if (CLASS_NAME) {
315     sprintf (newformat, "\n%s: Java classfile: %s, classname: %s, %s",
316              PROGRAM_NAME, FILE_NAME, CLASS_NAME, format);
317   }
318     
319   vsprintf (newmsg, newformat, ap);  
320   
321   /* print error to where it needs to go:
322          stdout, stderr, or syslog
323   */
324
325   fprintf(stderr, newmsg);
326   
327   free(newformat);
328   free(newmsg);
329   
330   exit(-1);
331 }
332
333
334 /* wrap fread for safety.  It is a fatal error to get an unexpected
335    EOF inside a class file. */
336
337 size_t my_fread(void *ptr, size_t size, size_t nitems, FILE *stream) {
338   size_t rc=0;
339   /*these variables are helpful in the debugger*/
340   int eof=0;
341   int error=0;
342
343
344   rc = fread(ptr, size, nitems, stream);
345   if ( (size!=0) && (rc == 0) ) {
346     eof = feof(stream);
347     error = ferror(stream);
348     die("Error reading from file, or unexpected EOF\n");
349   }
350   return rc;
351 }
352
353
354 void check_range(short value, short poolSize) {
355
356   if (value > poolSize) {
357     die("Value: %d, is out of range of the constant pool\n",
358         value);
359   }
360   return ;
361 }
362  
363
364
365 /* If lower case conversion of string is equal to pattern return a
366    pointer into string, just after the match.  If the string does not
367    patch the pattern the null pointer is returned.  This does not
368    change string. 
369
370    This is similar to strcasecmp, but I expect my patterns to be a
371    prefix of my strings. */
372
373 char 
374 *is_lower_equal (char *string, char *pattern) 
375 {
376   
377   while ( (tolower(*string) == *pattern) && 
378           *string && *pattern )  {
379     string++; 
380     pattern++;
381   }
382
383   if ( *pattern == 0 ) {
384     return string;
385   } 
386
387   return NULL;
388 }
389
390
391 /*  
392    Read fileHandle until we find the next instance of the Java
393    Classfile magic number indicating a java file or find EOF or
394    fileread error. Since we are reading from stdin which may contain
395    the concatination of many class files we can not be sure that the
396    magic number will be the first few bytes.
397
398    Return 1 on success 0 on failure.  */
399
400 #define mod4(num) ( (num) & 3 )
401
402
403 int findJavaMagic (FILE *fileHandle)
404 {
405   int offset=0;
406   int foundMagic = 0;
407   size_t rc;
408
409   /* what were looking for */
410   unsigned char magicInt[4] = {0xCA, 0xFE, 0xBA, 0xBE};
411   /*the hex reads in decimal: 202 254 186 190 */
412
413   /* a circular buffer indicating the last few bytes we read */
414   unsigned char buffer[4] = {0};
415
416   foundMagic = 0;
417   while( !foundMagic ) {
418
419       rc = fread(&buffer[offset], 1, 1, fileHandle);
420       if ( !rc ) {
421
422         /* Either this was not a java file or we were given a jar file
423            and have already found the last java file in it.*/
424
425         if ( feof(fileHandle) ) {
426           return 0;
427         }
428         
429         if ( ferror(fileHandle) ) {
430           die ("Error reading character from file.\n");
431         };
432
433       }
434       
435       /* offset points to the most recent char we read so offest+1
436          points to the oldest char we saved. */
437
438       foundMagic = (
439                     (magicInt[0] == buffer[mod4(offset+1)]) && 
440                     (magicInt[1] == buffer[mod4(offset+2)]) && 
441                     (magicInt[2] == buffer[mod4(offset+3)]) && 
442                     (magicInt[3] == buffer[mod4(offset+0)]) && 
443                     1
444                     );  
445
446       offset = mod4(offset+1);
447       
448     } /*end while*/
449
450   return foundMagic;
451 }
452
453 #undef mod4
454
455
456 int
457 my_strcmp (const void *a, const void *b) {
458 char **a1; char **b1;
459 int ret;
460
461 a1 = (char **)a;
462 b1 = (char **)b;
463 ret = strcmp(*a1,*b1);
464   return ret;
465 }
466
467 /* print the unique strings found in PRINT_TABLE and clear it out */
468
469 void 
470 print_table_flush(void) {
471   int i;
472   char *last_string;
473
474   if (!SIZE_PRINT_TABLE) {
475     return ;
476   }
477
478   /* The qsort line gives a warning on some unicies who insist that
479      strcmp takes arguments of type pointers to void not the
480      traditional pointers to char. */
481
482   qsort( (void *) PRINT_TABLE, (size_t) SIZE_PRINT_TABLE, 
483          sizeof(char *), &my_strcmp);
484
485   printf("%s",PRINT_TABLE[0]);
486   last_string = PRINT_TABLE[0];
487   PRINT_TABLE[0] = NULL;
488   
489   for (i = 1; i < SIZE_PRINT_TABLE; i++) {
490     if ( strcmp(last_string, PRINT_TABLE[i]) ){
491       printf("%s",PRINT_TABLE[i]);
492       free(last_string);
493       last_string = PRINT_TABLE[i];
494     } else {
495       free(PRINT_TABLE[i]);
496     }
497     PRINT_TABLE[i] = NULL;
498   }
499   
500   free(last_string);
501   SIZE_PRINT_TABLE = 0;
502   return ; 
503 }
504
505
506 /* add an element to PRINT_TABLE for later printing to stdout.  We do
507    not make a copy of the string so each string must be unique,
508    (different calls must pass pointers to different areas of memory)
509    and the string must not be freed anywhere else in the code and the
510    string must be from memory which can be freed.*/
511
512 void 
513 print_table_add(char *str) {
514
515   if (SIZE_PRINT_TABLE == MAX_PRINT_TABLE) {
516     print_table_flush();
517   }
518   
519   PRINT_TABLE[SIZE_PRINT_TABLE] = str;
520   SIZE_PRINT_TABLE++;
521   return ;
522 }
523
524
525 void 
526 print_list(char *in_string) {
527
528   /* This function is no longer needed due to fixes in RPM's
529      processing of dependencies.  Keep the code until I get a chance
530      to use RPM3.0 personally */
531
532   if (in_string) {
533     printf("%s\n", in_string);
534   }
535
536 /* 
537    Old function did:
538
539    Given a list separated by whitespace, put each element in the print
540    table with an added "\n" */
541
542  /*
543   char *WhiteSpace_Set = "\t\v\n\r\f ";
544   char *newEnd, *out_string;
545   int copy_len;
546
547   in_string += strspn(in_string, WhiteSpace_Set); 
548
549   while (*in_string) {
550     newEnd = strpbrk(in_string, WhiteSpace_Set);
551     
552     if  (newEnd) {
553       copy_len = newEnd-in_string;
554     } else {
555       if (*in_string) {
556         copy_len = strlen(in_string);
557       } else {
558         copy_len = 0;
559       }
560     }
561     
562     out_string = malloc(copy_len+10);
563     if ( !out_string ) {
564       outofmemory();
565     }
566     out_string[0]= '\0';
567
568     if (copy_len) {
569       strncat(out_string, in_string, copy_len);
570       in_string+=copy_len;
571       strcat(out_string, "\n");
572       print_table_add(out_string);
573     }
574
575     in_string += strspn(in_string+copy_len, WhiteSpace_Set);
576   }
577
578  */
579  return ;
580 }
581
582
583 /*  Print a properly formatted java class name, and returns the length
584     of the class string .  Do not print \n here as we may wish to
585     append the version number IFF we are printing the name of this classfile
586     
587     We also provide the class with the leaf node replaced with '*'.
588     This would not be necessary if we only had to worry about java
589     Class files.  However our parsing of jhtml files depends on this
590     information.  This is deprecated since jhtml is deprecated and
591     this method allows for very inaccurate dependencies.
592     
593     Also users may wish to refer to dependencies using star notation
594     (though this would be less accurate then explicit dependencies
595     since any leaf class will satify a star dependency).  */
596
597 char
598 *formatClassName(char *in_string, char terminator, 
599                  char print_star)
600
601   char *leaf_class=0, *out_string=0;
602   char *ClassName_Break_Set=0;
603
604
605   out_string = malloc(strlen(in_string) + 10);
606   if ( !out_string ) {
607     outofmemory();
608   }
609   out_string[0]= '\0';
610   
611   /*these characters end the current parse of the string in function
612     formatClassName.*/
613   
614   ClassName_Break_Set = malloc(3);
615   if ( !ClassName_Break_Set ) {
616     outofmemory();
617   }
618   ClassName_Break_Set[0] = '/';
619   /*terminator can be '\0' this must go after '/'*/
620   ClassName_Break_Set[1] = terminator;
621   ClassName_Break_Set[2] = '\0';
622   
623   if(ARG_RPMFORMAT) {
624     strcat(out_string, "java(");
625   }
626   if (print_star) {
627     /* print the path to the leaf class but do not print the leaf
628        class, stop at the last '/' we fix this back below*/
629     leaf_class = strrchr(in_string, '/');
630     if (leaf_class) {
631       leaf_class[0] = terminator;
632     }
633   }
634   
635   while (*in_string != terminator) {
636     char *newEnd=0;
637     int copy_len;
638
639     /* handle the break_set */
640
641     if (in_string[0] == '\0' ) {
642       die("Classname does not terminate with: '%c', '%s'\n", 
643           terminator, in_string);
644     } else {
645       if (in_string[0] == '/' ) {
646         /* convert '/' to '.' */
647         strcat(out_string, ".");
648         in_string++;
649       }
650     }
651
652     newEnd = strpbrk(in_string, ClassName_Break_Set);
653
654     if  (newEnd) {
655       copy_len = newEnd-in_string;
656     } else {
657       if (terminator == '\0') {
658         copy_len = strlen(in_string);
659       } else {
660         copy_len = 0;
661       }
662     }
663     
664     /* handle upto but not including the break_set*/
665     if (copy_len) {
666       strncat(out_string, in_string, copy_len);
667       in_string+=copy_len;
668     }
669
670   } /*end while*/
671   
672   if (leaf_class) {
673     /* print the star and fix the leaf class*/
674     strcat(out_string, ".*"); 
675     leaf_class[0] = '/';
676   }
677   if(ARG_RPMFORMAT) {
678     strcat(out_string, ")");
679   }
680
681   strcat(out_string, "\n");
682   free(ClassName_Break_Set);
683   return out_string;
684 }
685
686
687 /* Parse out just the class names from a java type and moves the string
688    pointer to the end of the string. */
689
690 void
691 dumpRefType(char *string)
692 {
693   /* All class types start with a 'L' and and end with a ';'. We want
694      everyting in between.  There might be more then one per string
695      like (params for a method call) */
696
697   string = strchr(string, 'L');
698   while (string) {
699     string++;
700     print_table_add(formatClassName(string, ';', 0));
701     string = strchr(string, ';');
702     string = strchr(string, 'L');
703   }
704   
705   return ;
706 }
707
708
709 /* Print out the classes referenced in the symbol table */
710
711 void
712 dumpRequires(symbolTable_t *symbolTable) {
713   int tem;
714   int ref = 0;
715
716   for(tem=1; tem < symbolTable->poolSize; tem++ ) {
717
718     /* dump all the classes in the const table. */
719     ref = symbolTable->classRef[tem];
720     if(ref) {
721       char *string = symbolTable->stringList[ref];
722       if( !*string ) {
723         die("class num: %d, referenced string num: %d, "
724             "which is null.\n",
725             tem, ref);
726       }
727       if ( string[0] == '[' ) {
728         /*
729           This is an array. We need to ingore 
730           strings like:
731                '[B'
732                '[[B'
733                '[[I'
734                
735           This hack leaves blank lines in the output 
736           when a string not containing a class is
737           sent to dumpRefType.
738         */
739         string = strchr(string, 'L');
740         if (string) {
741           dumpRefType(string);
742         }
743       } else {
744         print_table_add(formatClassName(string, '\0', 0));
745       }
746     }
747     
748     /* dump all the references */
749     ref = symbolTable->typeRef[tem];
750     if (ref) {
751       char *string = symbolTable->stringList[ref];
752       if ( !*string ) {
753         die("type num: %d, referenced string num: %d, "
754             "which is null.\n",
755             tem, ref);
756       }
757       /* this is a java type... parse out the class names */
758       dumpRefType(string);
759     } 
760     
761   } /*end for*/
762   
763   return ;
764 }
765
766
767 /* Read a java class files symbol table into memory. 
768                 - also  -
769    Find the proper name of the current Java Class file.
770    Print it regardless of: --provides | --requires
771 */
772
773
774 void genSymbolTable (FILE *fileHandle, symbolTable_t *symbolTable)
775 {
776   char ignore[10];
777   int i=0;
778   
779
780   /* We are called just after fileHandle saw the magic number, seek a
781      few bytes in to find the poolsize */
782
783   my_fread(&ignore, 4, 1, fileHandle);
784
785   my_fread(&(symbolTable->poolSize), 2, 1, fileHandle);
786
787   /* new the tables */
788
789   symbolTable->stringList = (char**) calloc(symbolTable->poolSize, 
790                                             sizeof(char*));
791   if(!symbolTable->stringList){
792     outofmemory();
793   }
794   
795   symbolTable->classRef = (short*) calloc(symbolTable->poolSize,
796                                           sizeof(short*));
797   if(!symbolTable->classRef){
798     outofmemory();
799   }
800   
801   symbolTable->typeRef = (short*) calloc(symbolTable->poolSize,
802                                          sizeof(short*));
803   if(!symbolTable->typeRef){
804     outofmemory();
805   }
806   
807   /* zero 'em all out. */
808   for(i=0; i < symbolTable->poolSize; i++) {
809     symbolTable->stringList[i] = NULL;
810     symbolTable->classRef[i] = 0;
811     symbolTable->typeRef[i] = 0;
812   }
813   
814   
815   /* for the number of entries
816        (it starts at 1)  
817   */
818
819   for(i=1; i < symbolTable->poolSize; i++) {
820     unsigned short type = 0;
821     unsigned short value = 0;
822     unsigned char tag = 0;
823
824       /* read the type of this entry  */
825
826       my_fread(&tag, 1, 1, fileHandle);
827       switch(tag) {
828         case 1: /* master string pool. */
829           {
830             /* record all these strings */
831                 char *someString;
832                 unsigned short length = 0;
833
834                 /* I am not sure if these strings must be null
835                    terminated.  I termiante them to be safe. */
836
837                 my_fread(&length, 2, 1, fileHandle);
838                 someString = (char*) malloc(length+1);
839                 if(!someString){
840                   outofmemory();
841                 }
842                 my_fread(someString, length, 1, fileHandle);
843                 someString[length]=0;
844                 symbolTable->stringList[i] = someString;
845                 
846                 if (ARG_KEYWORDS) {
847                   char *ptr=0;
848
849                   /* Each keyword can appear multiple times.  Don't
850                     bother with datastructures to store these strings,
851                     if we need to print it print it now.  */
852
853                   /* it would be better if instead of printing the
854                      strings "raw" I turn the space separated list
855                      into a "\n" separated list*/
856                   
857                   if (ARG_REQUIRES) {
858                     ptr = is_lower_equal(someString, "rpm_requires: ");
859                     if(ptr){
860                       print_list(ptr);
861                     }
862                   }
863                   if (ARG_PROVIDES) {
864                     ptr = is_lower_equal(someString, "rpm_provides: ");
865                     if(ptr){
866                       print_list(ptr);
867                     }
868                   }
869                   /* I wish there was a good way to handle this
870                   ptr = is_lower_equal(someString, "rpm_conflicts: ");
871                   */
872                   ptr = is_lower_equal(someString, "$revision: ");
873                   if(ptr){
874                     KEYWORD_REVISION=ptr;
875                     /* terminate the string before " $" */
876                     ptr = strchr(KEYWORD_REVISION, ' ');
877                     if (ptr) {
878                       *ptr = 0;
879                     }
880                   }
881                   ptr = is_lower_equal(someString, "rpm_version: ");
882                   if(ptr){
883                     KEYWORD_VERSION=ptr;
884                     /* terminate the string at first whitespace */
885                     ptr = strchr(KEYWORD_VERSION, ' ');
886                     if (ptr) {
887                       *ptr = 0;
888                     }
889                   }
890                   ptr = is_lower_equal(someString, "rpm_epoch: ");
891                   if(ptr){
892                     KEYWORD_EPOCH=ptr;
893                     /* terminate the string at first whitespace */
894                     ptr = strchr(KEYWORD_EPOCH, ' ');
895                     if (ptr) {
896                       *ptr = 0;
897                     }
898                   }
899                 }
900                 break;
901           }
902       case 2:   /* unknow type!! */
903           die("Unknown type in constant table. "
904               "Entry: %d. \n", i);
905           break;
906         case 3: /* int */
907           my_fread(&ignore, 4, 1, fileHandle);
908           break;
909         case 4: /* float */
910           my_fread(&ignore, 4, 1, fileHandle);
911           break;
912         case 5: /* long (counts as 2) */
913           my_fread(&ignore, 8, 1, fileHandle);
914           i++;
915           break;
916         case 6: /* double (counts as 2) */
917           my_fread(&ignore, 8, 1, fileHandle);
918           i++;
919           break;
920         case 7: /* Class */
921           my_fread(&value, 2, 1, fileHandle);
922           /* record which const it's referencing */
923           check_range(value, symbolTable->poolSize);
924           symbolTable->classRef[i]=value;
925           break;
926         case 8: /* String */
927           my_fread(&ignore, 2, 1, fileHandle);
928           break;
929         case 9: /* field reference */
930           my_fread(&ignore, 4, 1, fileHandle);
931           break;
932         case 10: /* method reference */
933           my_fread(&ignore, 4, 1, fileHandle);
934           break;
935         case 11: /* interface method reference */
936           my_fread(&ignore, 4, 1, fileHandle);
937           break;
938         case 12: /* constant name/type */
939           my_fread(&ignore, 2, 1, fileHandle);
940           my_fread(&type, 2, 1, fileHandle);
941           /* record the name, and the type it's referencing. */
942           check_range(type, symbolTable->poolSize);
943           symbolTable->typeRef[i]=type;
944           break;
945         default:
946           die("Unknown tag type: %d.\n",
947               "Entry: %d. \n", tag, i);
948           break;
949       }
950   }
951
952   return ;  
953 }
954  
955
956 /* 
957    Find the proper name of the current Java Class file.
958    Print it regardless of: --provides | --requires
959 */
960
961 void 
962 findClassName (FILE *fileHandle, symbolTable_t *symbolTable) {
963   char ignore[10];
964   unsigned short type = 0;
965   unsigned short class = 0;
966   char *out_string;
967   char *newline;
968
969   /* seek a little past the end of the table */
970   
971   my_fread(&ignore, 2, 1, fileHandle);
972   
973   /* read the name of this classfile */
974   
975   my_fread(&type, 2, 1, fileHandle);
976   class = symbolTable->classRef[type];
977   if( !class ||
978       !symbolTable->stringList[class] ) {
979       die("Couln't find class: %d, provided by file.\n", class);
980   }
981   CLASS_NAME=symbolTable->stringList[class];
982
983   out_string = formatClassName(symbolTable->stringList[class], '\0', 0);
984
985   {
986     int len = 10;
987
988     if (out_string) {
989       len += strlen(out_string);
990     }
991     if (KEYWORD_EPOCH) {
992       len += strlen(KEYWORD_EPOCH);
993     }
994     if (KEYWORD_VERSION) {
995       len += strlen(KEYWORD_VERSION);
996     }
997     if (KEYWORD_REVISION) {
998       len += strlen(KEYWORD_REVISION);
999     }
1000     
1001     out_string = realloc(out_string, len );
1002   }
1003
1004   if (!out_string){
1005     outofmemory();
1006   }
1007   
1008   if( KEYWORD_VERSION || KEYWORD_REVISION ){
1009     /* It is easier to remove the extra new line here in one place
1010        then to try and add a newline every where that formatClassName
1011        is called */
1012     char *newline;
1013
1014     /* I am not using rpm 3.0 yet so I need both the dependencies with
1015        and without the version numbers, when I upgrade I will remove
1016        this block (with copy_string) and change the "=" to " = " ten
1017        lines down.*/
1018     {
1019       char *copy_string;
1020       copy_string = (char*) malloc(strlen(out_string));
1021       if (!copy_string){
1022         outofmemory();
1023       }
1024       copy_string = strcpy(copy_string, out_string);
1025       print_table_add(copy_string);
1026     }
1027
1028     newline = strrchr(out_string, '\n');
1029     if (newline) {
1030       newline[0] = '\0';
1031     }
1032     strcat(out_string, " = ");
1033     if(KEYWORD_EPOCH){
1034       strcat(out_string, KEYWORD_EPOCH);
1035       strcat(out_string, ":");
1036     }
1037     if(KEYWORD_VERSION){
1038       strcat(out_string, KEYWORD_VERSION);
1039     } else {
1040       strcat(out_string, KEYWORD_REVISION);
1041     }
1042     strcat(out_string, "\n");
1043   }
1044   
1045   print_table_add(out_string);
1046   out_string=NULL;
1047
1048   /* Provide the star version of this class for jhtml
1049      dependencies. This option is deprecated since jhtml is
1050      deprecated. */
1051   
1052   if (ARG_STARPROV) {
1053     out_string = formatClassName(symbolTable->stringList[class], '\0', 1);
1054     print_table_add(out_string);
1055   }
1056   
1057   return ;  
1058 }
1059
1060
1061
1062
1063
1064 void freeSymbolTable (symbolTable_t *symbolTable)
1065 {  
1066   int i=0;
1067
1068   for(i=1; i < symbolTable->poolSize; i++) {
1069     if( symbolTable->stringList[i] ) {
1070       free(symbolTable->stringList[i]);
1071       symbolTable->stringList[i] = 0;
1072     }
1073   }
1074   
1075   free(symbolTable->stringList);
1076   symbolTable->stringList=0;
1077   
1078   free(symbolTable->classRef);
1079   symbolTable->classRef=0;
1080   
1081   free(symbolTable->typeRef);
1082   symbolTable->typeRef=0;
1083   
1084   free(symbolTable);
1085   symbolTable=0;
1086
1087   return ;
1088 }
1089
1090
1091 /* process each file, 
1092    must be called directly after finding 
1093    the magic number.
1094 */
1095
1096 void processJavaFile (FILE *fileHandle) {
1097   symbolTable_t symbolTable= {0};
1098   
1099   genSymbolTable(fileHandle, &symbolTable);
1100   findClassName(fileHandle, &symbolTable);
1101   
1102   if(ARG_REQUIRES) {
1103     dumpRequires(&symbolTable);
1104   }
1105
1106   freeSymbolTable(&symbolTable);
1107
1108   return ;
1109
1110 }
1111
1112
1113 int
1114 main(int argc, char **argv)
1115 {
1116   FILE *fileHandle;
1117   int i = 0;
1118   int rc = 0;
1119   int foundMagic=0;
1120
1121   PROGRAM_NAME=argv[0];
1122   
1123   if(argv[1] == NULL) {
1124     usage();
1125   }
1126   
1127   /* parse arguments which are not filenames*/
1128   
1129   for (i = 1; argv[i] != NULL; i++) {
1130     
1131     if (0) {
1132       /* 
1133          First entry a dummy to get the 
1134          other entries to align correctly
1135       */
1136       ;
1137     } else if ( !strcmp("-p",argv[i]) || !strcmp("--provides",argv[i]) ) {
1138       ARG_PROVIDES = 1;
1139     } else if ( !strcmp("-r",argv[i]) || !strcmp("--requires",argv[i]) ) {
1140       ARG_REQUIRES = 1;
1141     } else if ( !strcmp("-h",argv[i]) || !strcmp("--help",argv[i]) ||
1142                 !strcmp("-?",argv[i]) ) {
1143                 
1144       usage();
1145     } else if ( !strcmp("-F",argv[i]) || !strcmp("--rpmformat",argv[i]) ) {
1146       ARG_RPMFORMAT=1;
1147     } else if ( !strcmp("-k",argv[i]) || !strcmp("--keywords",argv[i]) ) {
1148       ARG_KEYWORDS=1;
1149     } else if ( !strcmp("-s",argv[i]) || !strcmp("--starprov",argv[i]) ) {
1150       ARG_STARPROV=1;
1151     } else if ( !strcmp("--",argv[i]) ) {
1152       i++;
1153       break;      
1154     } else {
1155       /* we do not recognize the argument, must be a filename*/
1156       break;
1157     }
1158   } /*end for arguments which are not filenames*/
1159   
1160   /* check arguments for consistancy */
1161
1162   if ( !ARG_PROVIDES && !ARG_REQUIRES ) {
1163     die ("Must specify either --provides or --requires.\n");
1164   }
1165   
1166   if ( ARG_PROVIDES && ARG_REQUIRES ) {
1167     die ("Can not specify both --provides and --requires.\n");
1168   }
1169   
1170   if ( ARG_REQUIRES && ARG_STARPROV) {
1171     die ("Can not specify both --requires and --starpov.\n");
1172   }
1173   
1174   if(argv[i] == NULL) {
1175     die ("Must specify Java class files.\n");
1176   }
1177   
1178   /* parse arguments which are filenames.  */
1179
1180   for ( /*null initializer*/; argv[i] != NULL; i++) {
1181     
1182     /*open the correct file and process it*/
1183     
1184     if ( !strcmp("-", argv[i]) ) {
1185       /* use stdin, might be a jar file */
1186       fileHandle = stdin;
1187       FILE_NAME = "<stdin>";
1188
1189       foundMagic = findJavaMagic(fileHandle);      
1190       while (foundMagic) {      
1191         processJavaFile(fileHandle);
1192         foundMagic = findJavaMagic(fileHandle);
1193       } 
1194     } else {
1195       /* Open a disk file*/
1196       fileHandle = fopen(argv[i], "r");
1197       if( fileHandle == 0 ) {
1198         die ("Could not open file: %s.\n", argv[i]);
1199       }
1200       fileHandle = fileHandle;
1201       FILE_NAME = argv[i];      
1202
1203       foundMagic = findJavaMagic(fileHandle);      
1204       if (foundMagic) { 
1205         processJavaFile(fileHandle);
1206       }
1207     }
1208
1209     rc = fclose(fileHandle);
1210     if( rc ) {
1211       die ("Could not close file: %s.\n", FILE_NAME);
1212     }
1213     CLASS_NAME=0;    
1214   } /*end parsing arguments which are filenames*/
1215   
1216   print_table_flush();
1217   return 0;
1218 }