1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
29 /* All Java Virtual Machine Specs are from
30 * "The Java Virtual Machine Specification", T. Lindholm, F. Yellin
44 #include <netinet/in.h> /* ntohl(), ntohs() */
47 #define access _access
48 #define vsnprintf _vsnprintf
51 #define PATH_MAX _MAX_PATH
52 #define ntohl(x) ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
53 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
55 #define ntohs(x) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
58 /* max. length of line in response file */
59 #define RES_FILE_BUF 65536
72 typedef struct file file_t
;
73 typedef unsigned char uint8
;
74 typedef unsigned short uint16
;
75 typedef unsigned int uint32
;
82 typedef struct utf8 utf8_t
;
84 /* The contents of the Constant_pool is described in JVMS p. 93
88 CONSTANT_Fieldref
= 9,
89 CONSTANT_Methodref
= 10,
90 CONSTANT_InterfaceMethodref
= 11,
96 CONSTANT_NameAndType
= 12,
100 enum { NGROW_INIT
= 10, NGROW
= 2 };
102 static char *pprogname
= "javadep";
103 static char csep
= ';';
106 static char cpathsep
= '/';
108 static char cpathsep
= '\\';
110 static FILE *pfsout
= NULL
;
111 static char *pout_file
= NULL
;
115 uint8
read_uint8(const file_t
*pfile
);
116 uint16
read_uint16(const file_t
*pfile
);
117 uint32
read_uint32(const file_t
*pfile
);
118 void skip_bytes(const file_t
*pfile
, const long nnum
);
119 char *escape_slash(const char *pstr
);
120 int is_inner(const char *pstr
);
121 void print_dependencies(const struct growable
*pdep
,
122 const char* pclass_file
);
123 void process_class_file(const char *pfilenamem
,
124 const struct growable
*pfilt
);
125 char *utf8tolatin1(const utf8_t a_utf8
);
126 void *xmalloc(size_t size
);
127 void *xcalloc(size_t nmemb
, size_t size
);
128 void *xrealloc(void *ptr
, size_t size
);
129 void grow_if_needed (struct growable
*pgrow
);
130 int append_to_growable(struct growable
*, char *);
131 struct growable
*allocate_growable(void);
132 void free_growable(struct growable
*pgrowvoid
);
133 void create_filters(struct growable
*pfilt
, const struct growable
*pinc
);
135 void err_quit(const char *, ...);
136 void silent_quit(void);
139 /* poor man's getopt() */
140 int simple_getopt(char *pargv
[], const char *poptstring
);
148 read_uint8(const file_t
*pfile
)
150 /* read a byte from classfile */
153 nread
= fread(&ndata
, sizeof(uint8
), 1, pfile
->pfs
);
156 err_quit("%s: truncated class file", pfile
->pname
);
162 read_uint16(const file_t
*pfile
)
164 /* read a short from classfile and convert it to host format */
167 nread
= fread(&ndata
, sizeof(uint16
), 1, pfile
->pfs
);
170 err_quit("%s: truncated class file", pfile
->pname
);
172 ndata
= ntohs(ndata
);
177 read_uint32(const file_t
*pfile
)
179 /* read an int from classfile and convert it to host format */
182 nread
= fread(&ndata
, sizeof(uint32
), 1, pfile
->pfs
);
185 err_quit("%s: truncated class file", pfile
->pname
);
187 ndata
= ntohl(ndata
);
192 read_utf8(const file_t
*pfile
)
194 /* Read a java utf-8-string with uint16 length prependend
195 * from class file. Returns utf8 struct
196 * with fresh allocated datablock,
197 * caller is responsible for freeing.
198 * Data is still in network byteorder
206 a_utf8
.nlen
= read_uint16(pfile
);
207 if (a_utf8
.nlen
> 0) {
208 a_utf8
.pdata
= xmalloc(a_utf8
.nlen
*sizeof(char));
209 nread
= fread(a_utf8
.pdata
, a_utf8
.nlen
*sizeof(char), 1, pfile
->pfs
);
212 err_quit("%s: truncated class file", pfile
->pname
);
219 char *utf8tolatin1(const utf8_t a_utf8
)
221 /* function returns fresh allocated zero terminated string,
222 * caller is responsible for freeing
225 /* JVMS p. 101: the null byte is encoded using a two byte format,
226 * Java Virtual Machine Utf8 strings differ in this respect from
227 * standard UTF-8 strings
230 /* Multibyte data is in network byte order */
236 pstr
= pp
= xmalloc((a_utf8
.nlen
+1) * sizeof(char));
238 for ( p
= (char*)a_utf8
.pdata
;
239 p
< (char*)a_utf8
.pdata
+a_utf8
.nlen
;
242 err_quit("sorry, real UTF8 decoding not yet implemented\n");
254 skip_bytes(const file_t
*pfile
, const long nnumber
)
256 /* skip a nnumber of bytes in classfile */
257 if ( fseek(pfile
->pfs
, nnumber
, SEEK_CUR
) == -1 )
258 err_quit("%s: %s", pfile
->pname
, strerror(errno
));
262 add_to_dependencies(struct growable
*pdep
,
263 const struct growable
*pfilt
,
265 const char *pclass_file
)
267 /* create dependencies */
269 size_t nlen_filt
, nlen_str
, nlen_pdepstr
;
271 char path
[PATH_MAX
+1];
272 char cnp_class_file
[PATH_MAX
+1];
273 char cnp_str
[PATH_MAX
+1];
275 nlen_pdepstr
= strlen(pdepstr
);
276 pstr
= xmalloc((nlen_pdepstr
+6+1)*sizeof(char));
277 memcpy(pstr
, pdepstr
, nlen_pdepstr
+1);
278 strncat(pstr
, ".class", 6);
280 if ( pfilt
->ncur
== 0 ) { /* no filters */
281 if ( access(pstr
, F_OK
) == 0 ) {
282 append_to_growable(pdep
, strdup(pstr
));
285 nlen_str
= strlen(pstr
);
286 for ( i
= 0; i
< pfilt
->ncur
; i
++ ) {
287 nlen_filt
= strlen(pfilt
->parray
[i
]);
288 if ( nlen_filt
+ 1 + nlen_str
> PATH_MAX
)
289 err_quit("path to long");
290 memcpy(path
, pfilt
->parray
[i
], nlen_filt
);
291 path
[nlen_filt
] = '/';
292 memcpy( path
+nlen_filt
+1, pstr
, nlen_str
+1);
294 if ( access(path
, F_OK
) != 0 ) {
297 return; /* path doesn't represent a real file, don't bother */
300 /* get the canonical path */
302 if ( !(realpath(pclass_file
, cnp_class_file
)
303 && realpath(path
, cnp_str
) ) ) {
304 err_quit("can't get the canonical path");
307 if ( !(_fullpath(cnp_class_file
, pclass_file
, sizeof(cnp_class_file
))
308 && _fullpath(cnp_str
, path
, sizeof(cnp_str
)) ) ) {
309 err_quit("can't get the canonical path");
313 /* truncate so that only the package prefix remains */
314 ptrunc
= strrchr(cnp_str
, cpathsep
);
316 ptrunc
= strrchr(cnp_class_file
, cpathsep
);
319 if ( !strcmp(cnp_str
, cnp_class_file
) ) {
322 return; /* identical, don't bother with this one */
325 append_to_growable(pdep
, strdup(path
));
333 escape_slash(const char *pstr
)
335 /* returns a fresh allocated string with all cpathsep escaped exchanged
338 * caller is responsible for freeing
341 const char *pp
= pstr
;
344 size_t nlen_pnp
, nlen_pp
;
347 while ( (p
=strchr(pp
, cpathsep
)) != NULL
) {
352 nlen_pnp
= strlen(pstr
) + i
;
353 pnp
= pnew_str
= xmalloc((nlen_pnp
+1) * sizeof(char));
358 while ( (p
=strchr(pp
, cpathsep
)) != NULL
) {
359 memcpy(pnp
, pp
, p
-pp
);
366 nlen_pp
= strlen(pp
);
367 memcpy(pnp
, pp
, nlen_pp
+1);
374 print_dependencies(const struct growable
*pdep
, const char* pclass_file
)
379 pstr
= escape_slash(pclass_file
);
380 fprintf(pfsout
, "%s:", pstr
);
383 for( i
=0; i
<pdep
->ncur
; ++i
) {
384 fprintf(pfsout
, " \\\n");
385 pstr
=escape_slash(pdep
->parray
[i
]);
386 fprintf(pfsout
, "\t%s", pstr
);
390 fprintf(pfsout
,"\n\n");
395 is_inner(const char *pstr
)
397 /* return true if character '$' is found in classname */
400 * note that a '$' in a classname is not an exact indicator
401 * for an inner class. Java identifier may legally contain
402 * this chararcter, and so may classnames. In the context
403 * of javadep this doesn't matter since the makefile system
404 * can't cope with classfiles with '$'s in the filename
409 if ( strchr(pstr
, '$') != NULL
)
416 process_class_file(const char *pfilename
, const struct growable
*pfilt
)
418 /* read class file and extract object information
419 * java class files are in bigendian data format
424 uint16 nminor
, nmajor
;
431 struct growable
*pdepen
;
433 file
.pname
= (char*)pfilename
;
435 file
.pfs
= fopen(file
.pname
,"rb");
439 nmagic
= read_uint32(&file
);
441 if ( nmagic
!= 0xCAFEBABE ) {
443 err_quit("%s: invalid magic", file
.pname
);
446 nminor
= read_uint16(&file
);
447 nmajor
= read_uint16(&file
);
449 /* get number of entries in constant pool */
450 ncnt
= read_uint16(&file
);
453 printf("Magic: %x\n", nmagic
);
454 printf("Major %d, Minor %d\n", nmajor
, nminor
);
455 printf("Const_pool_count %d\n", ncnt
);
461 /* There can be ncount entries in the constant_pool table
462 * so at most ncount-1 of them can be of type CONSTANT_Class
463 * (at leat one CONSTANT_Utf8 entry must exist).
464 * Usually way less CONSTANT_Class entries exists, of course
467 pc_pool
= xcalloc(ncnt
,sizeof(utf8_t
));
468 pc_class
= xmalloc((ncnt
-1)*sizeof(uint16
));
470 /* pc_pool[0] is reserved to the java virtual machine and does
471 * not exist in the class file
476 for (i
= 1; i
< ncnt
; i
++) {
481 ntag
= read_uint8(&file
);
483 /* we are only interested in CONSTANT_Class entries and
484 * Utf8 string entries, because they might belong to
485 * CONSTANT_Class entries
489 nindex
= read_uint16(&file
);
490 pc_class
[nclass_cnt
++] = nindex
;
492 case CONSTANT_Fieldref
:
493 case CONSTANT_Methodref
:
494 case CONSTANT_InterfaceMethodref
:
495 skip_bytes(&file
, 4L);
497 case CONSTANT_String
:
498 skip_bytes(&file
, 2L);
500 case CONSTANT_Integer
:
502 skip_bytes(&file
, 4L);
505 case CONSTANT_Double
:
506 skip_bytes(&file
, 8L);
507 /* Long and Doubles take 2(!)
508 * entries in constant_pool_table
512 case CONSTANT_NameAndType
:
513 skip_bytes(&file
, 4L);
516 a_utf8
= read_utf8(&file
);
520 /* Unknown Constant_pool entry, this means we are
523 err_quit("corrupted class file\n");
531 pdepen
= allocate_growable();
533 for (i
= 0; i
< nclass_cnt
; i
++) {
534 char *pstr
, *ptmpstr
;
535 pstr
= ptmpstr
= utf8tolatin1(pc_pool
[pc_class
[i
]]);
536 /* we are not interested in inner classes */
537 if ( is_inner(pstr
) ) {
542 /* strip off evt. array indicators */
543 if ( *ptmpstr
== '[' ) {
544 while ( *ptmpstr
== '[' )
546 /* we only interested in obj. arrays, which are marked with 'L' */
547 if ( *ptmpstr
== 'L' ) {
549 pstr
= strdup(++ptmpstr
);
550 /* remove final ';' from object array name */
551 pstr
[strlen(pstr
)-1] = '\0';
560 add_to_dependencies(pdepen
, pfilt
, pstr
, file
.pname
);
565 print_dependencies(pdepen
, file
.pname
);
566 free_growable(pdepen
);
569 for (i
= 0; i
< ncnt
; i
++)
570 free(pc_pool
[i
].pdata
);
584 err_quit("out of memory");
591 xcalloc(size_t nmemb
, size_t size
)
595 ptr
= calloc(nmemb
, size
);
598 err_quit("out of memory");
604 xrealloc(void *ptr
, size_t size
)
606 void *newptr
= realloc(ptr
, size
);
611 err_quit("out of memory");
617 err_quit(const char* fmt
, ...)
619 /* No dependency file must be generated for any error condition,
620 * just print message and exit.
623 char buffer
[PATH_MAX
];
628 fprintf(stderr
, "%s: ", pprogname
);
629 vsnprintf(buffer
, sizeof(buffer
), fmt
, args
);
630 fputs(buffer
, stderr
);
636 if ( pfsout
&& pfsout
!= stdout
) {
646 /* In some cases we should just do a silent exit */
649 if ( pfsout
&& pfsout
!= stdout
) {
656 int append_to_growable(struct growable
*pgrow
, char *pstr
)
658 /* append an element pstr to pgrow,
659 * return new number of elements
661 grow_if_needed(pgrow
);
662 pgrow
->parray
[pgrow
->ncur
++] = pstr
;
667 grow_if_needed(struct growable
*pgrow
)
669 /* grow growable arrays */
671 if ( pgrow
->ncur
>= pgrow
->nmax
) {
672 pgrow
->parray
= xrealloc(pgrow
->parray
,
673 (NGROW
*pgrow
->nmax
)*sizeof(char*));
674 pgrow
->nmax
*= NGROW
;
679 struct growable
*allocate_growable(void)
681 /* allocate an growable array,
682 * initialize with NGROW_INIT elements
685 struct growable
*pgrow
;
687 pgrow
= xmalloc(sizeof(struct growable
));
688 pgrow
->parray
= xmalloc(NGROW_INIT
*sizeof(char *));
689 pgrow
->nmax
= NGROW_INIT
;
695 free_growable(struct growable
*pgrow
)
698 for( i
= 0; i
< pgrow
->ncur
; i
++ )
699 free(pgrow
->parray
[i
]);
705 create_filters(struct growable
*pfilt
, const struct growable
*pinc
)
709 size_t nlen
, nlen_pstr
;
710 /* break up includes into filter list */
711 for ( i
= 0; i
< pinc
->ncur
; i
++ ) {
712 pp
= pinc
->parray
[i
];
714 while ( (p
= strchr(pp
, csep
)) != NULL
) {
716 pstr
= xmalloc((nlen
+1)*sizeof(char*));
717 memcpy(pstr
, pp
, nlen
);
719 append_to_growable(pfilt
, pstr
);
722 nlen_pstr
= strlen(pp
);
723 pstr
= xmalloc((nlen_pstr
+1)*sizeof(char*));
724 memcpy(pstr
, pp
, nlen_pstr
+1);
725 append_to_growable(pfilt
, pstr
);
734 "usage: %s [-i|-I includepath ... -s|-S seperator "
735 "-o|-O outpath -v|-V -h|-H] <file> ....\n",
740 /* my very simple minded implementation of getopt()
741 * it's to sad that getopt() is not available everywhere
742 * note: this is not a full POSIX conforming getopt()
744 int simple_getopt(char *pargv
[], const char *poptstring
)
746 char *parg
= pargv
[optind
];
748 /* skip all response file arguments */
750 while ( *parg
== '@' )
751 parg
= pargv
[++optind
];
753 if ( parg
[0] == '-' && parg
[1] != '\0' ) {
756 if ( (popt
= strchr(poptstring
, c
)) == NULL
) {
759 fprintf(stderr
, "Unknown option character `\\x%x'.\n", optopt
);
762 if ( *(++popt
) == ':') {
763 if ( parg
[2] != '\0' ) {
766 optarg
= pargv
[++optind
];
780 main(int argc
, char *argv
[])
783 struct growable
*presp
, *pincs
, *pfilters
;
787 presp
= allocate_growable();
789 /* FIXME: cleanup the option parsing */
790 /* search for response file, read it */
791 for ( i
= 1; i
< argc
; i
++ ) {
792 char *parg
= argv
[i
];
793 char buffer
[RES_FILE_BUF
];
795 if ( *parg
== '@' ) {
796 FILE *pfile
= fopen(++parg
, "r");
798 err_quit("%s: %s", parg
, strerror(errno
));
799 while ( !feof(pfile
) ) {
802 if ( fgets(buffer
, RES_FILE_BUF
, pfile
) ) {;
804 while ( (token
= strtok(p
, " \t\n")) != NULL
) {
806 append_to_growable(presp
, strdup(token
));
814 /* copy all arguments incl. response file in one array
815 * for parsing with getopt
817 nall_argc
= argc
+ presp
->ncur
;
818 pall_argv
= xmalloc((nall_argc
+1)*sizeof(char *));
819 memcpy(pall_argv
, argv
, argc
*sizeof(char *));
820 memcpy(pall_argv
+argc
, presp
->parray
, presp
->ncur
*sizeof(char *));
821 *(pall_argv
+argc
+presp
->ncur
) = '\0'; /* terminate */
824 pincs
= allocate_growable();
827 while( (c
= simple_getopt(pall_argv
, ":i:I:s:S:o:OhHvV")) != -1 ) {
829 while( (c
= getopt(nall_argc
, pall_argv
, ":i:I:s:S:o:OhHvV")) != -1 ) {
834 append_to_growable(pincs
, strdup(optarg
));
854 if (isprint (optopt
))
856 "Unknown option `-%c'.\n", optopt
);
859 "Unknown option character `\\x%x'.\n",
865 fprintf(stderr
, "Missing parameter.\n");
876 pfilters
= allocate_growable();
877 create_filters(pfilters
, pincs
);
878 free_growable(pincs
);
882 pfsout
= fopen(pout_file
, "w");
884 err_quit("%s: %s", pout_file
, strerror(errno
));
889 /* the remaining arguments are either class file
890 * names or response files, ignore response file
891 * since they have already been included
893 for ( i
= optind
; i
< nall_argc
; i
++ ) {
894 char *parg
= pall_argv
[i
];
895 if ( *parg
!= '@' ) {
896 process_class_file(parg
, pfilters
);
897 if ( pfsout
!= stdout
) {
899 printf("Processed %s ...\n", parg
);
904 free_growable(pfilters
);
908 free_growable(presp
);
915 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */