1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: javadep.c,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 /* All Java Virtual Machine Specs are from
32 * "The Java Virtual Machine Specification", T. Lindholm, F. Yellin
44 #if defined(UNX) || defined(OS2)
46 #include <netinet/in.h> /* ntohl(), ntohs() */
49 #define access _access
50 #define vsnprintf _vsnprintf
53 #define PATH_MAX _MAX_PATH
54 #define ntohl(x) ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
55 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
57 #define ntohs(x) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
64 /* max. length of line in response file */
65 #define RES_FILE_BUF 65536
78 typedef struct file file_t
;
79 typedef unsigned char uint8
;
80 typedef unsigned short uint16
;
81 typedef unsigned int uint32
;
88 typedef struct utf8 utf8_t
;
90 /* The contents of the Constant_pool is described in JVMS p. 93
94 CONSTANT_Fieldref
= 9,
95 CONSTANT_Methodref
= 10,
96 CONSTANT_InterfaceMethodref
= 11,
102 CONSTANT_NameAndType
= 12,
106 enum { NGROW_INIT
= 10, NGROW
= 2 };
108 static char *pprogname
= "javadep";
109 static char csep
= ';';
110 #if defined (UNX) || defined(OS2)
112 static char cpathsep
= '/';
113 #elif defined (WNT) || defined(OS2)
114 static char cpathsep
= '\\';
116 static FILE *pfsout
= NULL
;
117 static char *pout_file
= NULL
;
121 uint8
read_uint8(const file_t
*pfile
);
122 uint16
read_uint16(const file_t
*pfile
);
123 uint32
read_uint32(const file_t
*pfile
);
124 void skip_bytes(const file_t
*pfile
, const size_t nnum
);
125 char *escape_slash(const char *pstr
);
126 int is_inner(const char *pstr
);
127 void print_dependencies(const struct growable
*pdep
,
128 const char* pclass_file
);
129 void process_class_file(const char *pfilenamem
,
130 const struct growable
*pfilt
);
131 char *utf8tolatin1(const utf8_t a_utf8
);
132 void *xmalloc(size_t size
);
133 void *xcalloc(size_t nmemb
, size_t size
);
134 void *xrealloc(void *ptr
, size_t size
);
135 void grow_if_needed (struct growable
*pgrow
);
136 int append_to_growable(struct growable
*, char *);
137 struct growable
*allocate_growable(void);
138 void free_growable(struct growable
*pgrowvoid
);
139 void create_filters(struct growable
*pfilt
, const struct growable
*pinc
);
141 void err_quit(const char *, ...);
142 void silent_quit(void);
144 /* poor man's getopt() */
145 int simple_getopt(char *pargv
[], const char *poptstring
);
152 read_uint8(const file_t
*pfile
)
154 /* read a byte from classfile */
157 nread
= fread(&ndata
, sizeof(uint8
), 1, pfile
->pfs
);
160 err_quit("%s: truncated class file", pfile
->pname
);
166 read_uint16(const file_t
*pfile
)
168 /* read a short from classfile and convert it to host format */
171 nread
= fread(&ndata
, sizeof(uint16
), 1, pfile
->pfs
);
174 err_quit("%s: truncated class file", pfile
->pname
);
176 ndata
= ntohs(ndata
);
181 read_uint32(const file_t
*pfile
)
183 /* read an int from classfile and convert it to host format */
186 nread
= fread(&ndata
, sizeof(uint32
), 1, pfile
->pfs
);
189 err_quit("%s: truncated class file", pfile
->pname
);
191 ndata
= ntohl(ndata
);
196 read_utf8(const file_t
*pfile
)
198 /* Read a java utf-8-string with uint16 length prependend
199 * from class file. Returns utf8 struct
200 * with fresh allocated datablock,
201 * caller is responsible for freeing.
202 * Data is still in network byteorder
210 a_utf8
.nlen
= read_uint16(pfile
);
211 if (a_utf8
.nlen
> 0) {
212 a_utf8
.pdata
= xmalloc(a_utf8
.nlen
*sizeof(char));
213 nread
= fread(a_utf8
.pdata
, a_utf8
.nlen
*sizeof(char), 1, pfile
->pfs
);
216 err_quit("%s: truncated class file", pfile
->pname
);
223 char *utf8tolatin1(const utf8_t a_utf8
)
225 /* function returns fresh allocated zero terminated string,
226 * caller is responsible for freeing
229 /* JVMS p. 101: the null byte is encoded using a two byte format,
230 * Java Virtual Machine Utf8 strings differ in this respect from
231 * standard UTF-8 strings
234 /* Multibyte data is in network byte order */
240 pstr
= pp
= xmalloc((a_utf8
.nlen
+1) * sizeof(char));
242 for ( p
= (char*)a_utf8
.pdata
;
243 p
< (char*)a_utf8
.pdata
+a_utf8
.nlen
;
246 err_quit("sorry, real UTF8 decoding not yet implemented\n");
258 skip_bytes(const file_t
*pfile
, const size_t nnumber
)
260 /* skip a nnumber of bytes in classfile */
261 if ( fseek(pfile
->pfs
, nnumber
, SEEK_CUR
) == -1 )
262 err_quit("%s: %s", pfile
->pname
, strerror(errno
));
266 add_to_dependencies(struct growable
*pdep
,
267 const struct growable
*pfilt
,
269 const char *pclass_file
)
271 /* create dependencies */
273 int nlen_filt
, nlen_str
, nlen_pdepstr
;
275 char path
[PATH_MAX
+1];
276 char cnp_class_file
[PATH_MAX
+1];
277 char cnp_str
[PATH_MAX
+1];
279 nlen_pdepstr
= strlen(pdepstr
);
280 pstr
= xmalloc((nlen_pdepstr
+6+1)*sizeof(char));
281 memcpy(pstr
, pdepstr
, nlen_pdepstr
+1);
282 strncat(pstr
, ".class", 6);
284 if ( pfilt
->ncur
== 0 ) { /* no filters */
285 if ( access(pstr
, F_OK
) == 0 ) {
286 append_to_growable(pdep
, strdup(pstr
));
289 nlen_str
= strlen(pstr
);
290 for ( i
= 0; i
< pfilt
->ncur
; i
++ ) {
291 nlen_filt
= strlen(pfilt
->parray
[i
]);
292 if ( nlen_filt
+ 1 + nlen_str
> PATH_MAX
)
293 err_quit("path to long");
294 memcpy(path
, pfilt
->parray
[i
], nlen_filt
);
295 path
[nlen_filt
] = '/';
296 memcpy( path
+nlen_filt
+1, pstr
, nlen_str
+1);
298 if ( access(path
, F_OK
) != 0 ) {
301 return; /* path doesn't represent a real file, don't bother */
304 /* get the canonical path */
305 #if defined (UNX) || defined(OS2)
306 if ( !(realpath(pclass_file
, cnp_class_file
)
307 && realpath(path
, cnp_str
) ) ) {
308 err_quit("can't get the canonical path");
311 if ( !(_fullpath(cnp_class_file
, pclass_file
, sizeof(cnp_class_file
))
312 && _fullpath(cnp_str
, path
, sizeof(cnp_str
)) ) ) {
313 err_quit("can't get the canonical path");
317 /* truncate so that only the package prefix remains */
318 ptrunc
= strrchr(cnp_str
, cpathsep
);
320 ptrunc
= strrchr(cnp_class_file
, cpathsep
);
323 if ( !strcmp(cnp_str
, cnp_class_file
) ) {
326 return; /* identical, don't bother with this one */
329 append_to_growable(pdep
, strdup(path
));
337 escape_slash(const char *pstr
)
339 /* returns a fresh allocated string with all cpathsep escaped exchanged
342 * caller is responsible for freeing
345 const char *pp
= pstr
;
348 int nlen_pnp
, nlen_pp
;
351 while ( (p
=strchr(pp
, cpathsep
)) != NULL
) {
356 nlen_pnp
= strlen(pstr
) + i
;
357 pnp
= pnew_str
= xmalloc((nlen_pnp
+1) * sizeof(char));
362 while ( (p
=strchr(pp
, cpathsep
)) != NULL
) {
363 memcpy(pnp
, pp
, p
-pp
);
370 nlen_pp
= strlen(pp
);
371 memcpy(pnp
, pp
, nlen_pp
+1);
378 print_dependencies(const struct growable
*pdep
, const char* pclass_file
)
383 pstr
= escape_slash(pclass_file
);
384 fprintf(pfsout
, "%s:", pstr
);
387 for( i
=0; i
<pdep
->ncur
; ++i
) {
388 fprintf(pfsout
, " \\\n");
389 pstr
=escape_slash(pdep
->parray
[i
]);
390 fprintf(pfsout
, "\t%s", pstr
);
394 fprintf(pfsout
,"\n\n");
399 is_inner(const char *pstr
)
401 /* return true if character '$' is found in classname */
404 * note that a '$' in a classname is not an exact indicator
405 * for an inner class. Java identifier may legally contain
406 * this chararcter, and so may classnames. In the context
407 * of javadep this doesn't matter since the makefile system
408 * can't cope with classfiles with '$'s in the filename
413 if ( strchr(pstr
, '$') != NULL
)
420 process_class_file(const char *pfilename
, const struct growable
*pfilt
)
422 /* read class file and extract object information
423 * java class files are in bigendian data format
428 uint16 nminor
, nmajor
;
435 struct growable
*pdepen
;
437 file
.pname
= (char*)pfilename
;
439 file
.pfs
= fopen(file
.pname
,"rb");
443 nmagic
= read_uint32(&file
);
445 if ( nmagic
!= 0xCAFEBABE ) {
447 err_quit("%s: invalid magic", file
.pname
);
450 nminor
= read_uint16(&file
);
451 nmajor
= read_uint16(&file
);
453 /* get number of entries in constant pool */
454 ncnt
= read_uint16(&file
);
457 printf("Magic: %p\n", (void*)nmagic
);
458 printf("Major %d, Minor %d\n", nmajor
, nminor
);
459 printf("Const_pool_count %d\n", ncnt
);
462 /* There can be ncount entries in the constant_pool table
463 * so at most ncount-1 of them can be of type CONSTANT_Class
464 * (at leat one CONSTANT_Utf8 entry must exist).
465 * Usually way less CONSTANT_Class entries exists, of course
468 pc_pool
= xcalloc(ncnt
,sizeof(utf8_t
));
469 pc_class
= xmalloc((ncnt
-1)*sizeof(uint16
));
471 /* pc_pool[0] is reserved to the java virtual machine and does
472 * not exist in the class file
477 for (i
= 1; i
< ncnt
; i
++) {
482 ntag
= read_uint8(&file
);
484 /* we are only interested in CONSTANT_Class entries and
485 * Utf8 string entries, because they might belong to
486 * CONSTANT_Class entries
490 nindex
= read_uint16(&file
);
491 pc_class
[nclass_cnt
++] = nindex
;
493 case CONSTANT_Fieldref
:
494 case CONSTANT_Methodref
:
495 case CONSTANT_InterfaceMethodref
:
496 skip_bytes(&file
, 4L);
498 case CONSTANT_String
:
499 skip_bytes(&file
, 2L);
501 case CONSTANT_Integer
:
503 skip_bytes(&file
, 4L);
506 case CONSTANT_Double
:
507 skip_bytes(&file
, 8L);
508 /* Long and Doubles take 2(!)
509 * entries in constant_pool_table
513 case CONSTANT_NameAndType
:
514 skip_bytes(&file
, 4L);
517 a_utf8
= read_utf8(&file
);
521 /* Unknown Constant_pool entry, this means we are
524 err_quit("corrupted class file\n");
532 pdepen
= allocate_growable();
534 for (i
= 0; i
< nclass_cnt
; i
++) {
535 char *pstr
, *ptmpstr
;
536 pstr
= ptmpstr
= utf8tolatin1(pc_pool
[pc_class
[i
]]);
537 /* we are not interested in inner classes */
538 if ( is_inner(pstr
) ) {
543 /* strip off evt. array indicators */
544 if ( *ptmpstr
== '[' ) {
545 while ( *ptmpstr
== '[' )
547 /* we only interested in obj. arrays, which are marked with 'L' */
548 if ( *ptmpstr
== 'L' ) {
550 pstr
= strdup(++ptmpstr
);
551 /* remove final ';' from object array name */
552 pstr
[strlen(pstr
)-1] = '\0';
561 add_to_dependencies(pdepen
, pfilt
, pstr
, file
.pname
);
566 print_dependencies(pdepen
, file
.pname
);
567 free_growable(pdepen
);
570 for (i
= 0; i
< ncnt
; i
++)
571 free(pc_pool
[i
].pdata
);
585 err_quit("out of memory");
592 xcalloc(size_t nmemb
, size_t size
)
596 ptr
= calloc(nmemb
, size
);
599 err_quit("out of memory");
605 xrealloc(void *ptr
, size_t size
)
607 ptr
= realloc(ptr
, size
);
610 err_quit("out of memory");
616 err_quit(const char* fmt
, ...)
618 /* No dependency file must be generated for any error condition,
619 * just print message and exit.
622 char buffer
[PATH_MAX
];
627 fprintf(stderr
, "%s: ", pprogname
);
628 vsnprintf(buffer
, sizeof(buffer
), fmt
, args
);
629 fputs(buffer
, stderr
);
635 if ( pfsout
&& pfsout
!= stdout
) {
645 /* In some cases we should just do a silent exit */
648 if ( pfsout
&& pfsout
!= stdout
) {
655 int append_to_growable(struct growable
*pgrow
, char *pstr
)
657 /* append an element pstr to pgrow,
658 * return new number of elements
660 grow_if_needed(pgrow
);
661 pgrow
->parray
[pgrow
->ncur
++] = pstr
;
666 grow_if_needed(struct growable
*pgrow
)
668 /* grow growable arrays */
670 if ( pgrow
->ncur
>= pgrow
->nmax
) {
671 pgrow
->parray
= xrealloc(pgrow
->parray
,
672 (NGROW
*pgrow
->nmax
)*sizeof(char*));
673 pgrow
->nmax
*= NGROW
;
678 struct growable
*allocate_growable(void)
680 /* allocate an growable array,
681 * initialize with NGROW_INIT elements
684 struct growable
*pgrow
;
686 pgrow
= xmalloc(sizeof(struct growable
));
687 pgrow
->parray
= xmalloc(NGROW_INIT
*sizeof(char *));
688 pgrow
->nmax
= NGROW_INIT
;
694 free_growable(struct growable
*pgrow
)
697 for( i
= 0; i
< pgrow
->ncur
; i
++ )
698 free(pgrow
->parray
[i
]);
704 create_filters(struct growable
*pfilt
, const struct growable
*pinc
)
707 int i
, nlen
, nlen_pstr
;
708 /* break up includes into filter list */
709 for ( i
= 0; i
< pinc
->ncur
; i
++ ) {
710 pp
= pinc
->parray
[i
];
712 while ( (p
= strchr(pp
, csep
)) != NULL
) {
714 pstr
= xmalloc((nlen
+1)*sizeof(char*));
715 memcpy(pstr
, pp
, nlen
);
717 append_to_growable(pfilt
, pstr
);
720 nlen_pstr
= strlen(pp
);
721 pstr
= xmalloc((nlen_pstr
+1)*sizeof(char*));
722 memcpy(pstr
, pp
, nlen_pstr
+1);
723 append_to_growable(pfilt
, pstr
);
732 "usage: %s [-i|-I includepath ... -s|-S seperator "
733 "-o|-O outpath -v|-V -h|-H] <file> ....\n",
737 /* my very simple minded implementation of getopt()
738 * it's to sad that getopt() is not available everywhere
739 * note: this is not a full POSIX conforming getopt()
741 int simple_getopt(char *pargv
[], const char *poptstring
)
743 char *parg
= pargv
[optind
];
745 /* skip all response file arguments */
747 while ( *parg
== '@' )
748 parg
= pargv
[++optind
];
750 if ( parg
[0] == '-' && parg
[1] != '\0' ) {
753 if ( (popt
= strchr(poptstring
, c
)) == NULL
) {
756 fprintf(stderr
, "Unknown option character `\\x%x'.\n", optopt
);
759 if ( *(++popt
) == ':') {
760 if ( parg
[2] != '\0' ) {
763 optarg
= pargv
[++optind
];
777 main(int argc
, char *argv
[])
780 struct growable
*presp
, *pincs
, *pfilters
;
784 presp
= allocate_growable();
786 /* FIXME: cleanup the option parsing */
787 /* search for response file, read it */
788 for ( i
= 1; i
< argc
; i
++ ) {
789 char *parg
= argv
[i
];
790 char buffer
[RES_FILE_BUF
];
792 if ( *parg
== '@' ) {
793 FILE *pfile
= fopen(++parg
, "r");
795 err_quit("%s: %s", parg
, strerror(errno
));
796 while ( !feof(pfile
) ) {
799 if ( fgets(buffer
, RES_FILE_BUF
, pfile
) ) {;
801 while ( (token
= strtok(p
, " \t\n")) != NULL
) {
803 append_to_growable(presp
, strdup(token
));
811 /* copy all arguments incl. response file in one array
812 * for parsing with getopt
814 nall_argc
= argc
+ presp
->ncur
;
815 pall_argv
= xmalloc((nall_argc
+1)*sizeof(char *));
816 memcpy(pall_argv
, argv
, argc
*sizeof(char *));
817 memcpy(pall_argv
+argc
, presp
->parray
, presp
->ncur
*sizeof(char *));
818 *(pall_argv
+argc
+presp
->ncur
) = '\0'; /* terminate */
821 pincs
= allocate_growable();
823 while( (c
= simple_getopt(pall_argv
, ":i:I:s:S:o:OhHvV")) != -1 ) {
827 append_to_growable(pincs
, strdup(optarg
));
847 if (isprint (optopt
))
849 "Unknown option `-%c'.\n", optopt
);
852 "Unknown option character `\\x%x'.\n",
858 fprintf(stderr
, "Missing parameter.\n");
869 pfilters
= allocate_growable();
870 create_filters(pfilters
, pincs
);
871 free_growable(pincs
);
875 pfsout
= fopen(pout_file
, "w");
877 err_quit("%s: %s", pout_file
, strerror(errno
));
882 /* the remaining arguments are either class file
883 * names or response files, ignore response file
884 * since they have already been included
886 for ( i
= optind
; i
< nall_argc
; i
++ ) {
887 char *parg
= pall_argv
[i
];
888 if ( *parg
!= '@' ) {
889 process_class_file(parg
, pfilters
);
890 if ( pfsout
!= stdout
) {
892 printf("Processed %s ...\n", parg
);
897 free_growable(pfilters
);
901 free_growable(presp
);