1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 /* All Java Virtual Machine Specs are from
25 * "The Java Virtual Machine Specification", T. Lindholm, F. Yellin
26 * (JVMS)
27 */
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <limits.h>
36
37 #if defined(UNX) || defined(OS2)
38 #include <unistd.h>
39 #include <netinet/in.h> /* ntohl(), ntohs() */
40 #elif defined(WNT)
41 #include <io.h>
42 #define access _access
43 #define vsnprintf _vsnprintf
44 #define CDECL _cdecl
45 #define F_OK 00
46 #define PATH_MAX _MAX_PATH
47 #define ntohl(x) ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
48 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
49
50 #define ntohs(x) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
51 #endif
52
53 #if defined(OS2)
54 #define CDECL
55 #endif
56
57 /* max. length of line in response file */
58 #define RES_FILE_BUF 65536
59
60 struct file {
61 char *pname;
62 FILE *pfs;
63 };
64
65 struct growable {
66 int ncur;
67 int nmax;
68 char **parray;
69 };
70
71 typedef struct file file_t;
72 typedef unsigned char uint8;
73 typedef unsigned short uint16;
74 typedef unsigned int uint32;
75
76 struct utf8 {
77 uint16 nlen;
78 void *pdata;
79 };
80
81 typedef struct utf8 utf8_t;
82
83 /* The contents of the Constant_pool is described in JVMS p. 93
84 */
85 enum {
86 CONSTANT_Class = 7,
87 CONSTANT_Fieldref = 9,
88 CONSTANT_Methodref = 10,
89 CONSTANT_InterfaceMethodref = 11,
90 CONSTANT_String = 8,
91 CONSTANT_Integer = 3,
92 CONSTANT_Float = 4,
93 CONSTANT_Long = 5,
94 CONSTANT_Double = 6,
95 CONSTANT_NameAndType = 12,
96 CONSTANT_Utf8 = 1,
97 CONSTANT_MethodHandle = 15,
98 CONSTANT_MethodType = 16,
99 CONSTANT_InvokeDynamic = 18
100 };
101
102 enum { NGROW_INIT = 10, NGROW = 2 };
103
104 static char *pprogname = "javadep";
105 static char csep = ';';
106 #if defined (UNX) || defined(OS2)
107 #define CDECL
108 static char cpathsep = '/';
109 #elif defined (WNT) || defined(OS2)
110 static char cpathsep = '\\';
111 #endif
112 static FILE *pfsout = NULL;
113 static char *pout_file = NULL;
114
115
116 /* prototypes */
117 uint8 read_uint8(const file_t *pfile);
118 uint16 read_uint16(const file_t *pfile);
119 uint32 read_uint32(const file_t *pfile);
120 void skip_bytes(const file_t *pfile, const size_t nnum);
121 char *escape_slash(const char *pstr);
122 int is_inner(const char *pstr);
123 void print_dependencies(const struct growable *pdep,
124 const char* pclass_file);
125 void process_class_file(const char *pfilenamem,
126 const struct growable *pfilt);
127 char *utf8tolatin1(const utf8_t a_utf8);
128 void *xmalloc(size_t size);
129 void *xcalloc(size_t nmemb, size_t size);
130 void *xrealloc(void *ptr, size_t size);
131 void grow_if_needed (struct growable *pgrow);
132 int append_to_growable(struct growable *, char *);
133 struct growable *allocate_growable(void);
134 void free_growable(struct growable *pgrowvoid);
135 void create_filters(struct growable *pfilt, const struct growable *pinc);
136 void usage(void);
137 void err_quit(const char *, ...);
138 void silent_quit(void);
139
140 #ifdef WNT
141 /* poor man's getopt() */
142 int simple_getopt(char *pargv[], const char *poptstring);
143 char *optarg = NULL;
144 int optind = 1;
145 int optopt = 0;
146 int opterr = 0;
147 #endif
148
149 uint8
read_uint8(const file_t * pfile)150 read_uint8(const file_t *pfile)
151 {
152 /* read a byte from classfile */
153 int nread;
154 uint8 ndata;
155 nread = fread(&ndata, sizeof(uint8), 1, pfile->pfs);
156 if ( !nread ) {
157 fclose(pfile->pfs);
158 err_quit("%s: truncated class file", pfile->pname);
159 }
160 return ndata;
161 }
162
163 uint16
read_uint16(const file_t * pfile)164 read_uint16(const file_t *pfile)
165 {
166 /* read a short from classfile and convert it to host format */
167 int nread;
168 uint16 ndata;
169 nread = fread(&ndata, sizeof(uint16), 1, pfile->pfs);
170 if ( !nread ) {
171 fclose(pfile->pfs);
172 err_quit("%s: truncated class file", pfile->pname);
173 }
174 ndata = ntohs(ndata);
175 return ndata;
176 }
177
178 uint32
read_uint32(const file_t * pfile)179 read_uint32(const file_t *pfile)
180 {
181 /* read an int from classfile and convert it to host format */
182 int nread;
183 uint32 ndata;
184 nread = fread(&ndata, sizeof(uint32), 1, pfile->pfs);
185 if ( !nread ) {
186 fclose(pfile->pfs);
187 err_quit("%s: truncated class file", pfile->pname);
188 }
189 ndata = ntohl(ndata);
190 return ndata;
191 }
192
193 utf8_t
read_utf8(const file_t * pfile)194 read_utf8(const file_t *pfile)
195 {
196 /* Read a java utf-8-string with uint16 length prependend
197 * from class file. Returns utf8 struct
198 * with fresh allocated datablock,
199 * caller is responsible for freeing.
200 * Data is still in network byteorder
201 */
202
203 utf8_t a_utf8;
204 int nread;
205
206 a_utf8.pdata = NULL;
207
208 a_utf8.nlen = read_uint16(pfile);
209 if (a_utf8.nlen > 0) {
210 a_utf8.pdata = xmalloc(a_utf8.nlen*sizeof(char));
211 nread = fread(a_utf8.pdata, a_utf8.nlen*sizeof(char), 1, pfile->pfs);
212 if ( !nread ) {
213 fclose(pfile->pfs);
214 err_quit("%s: truncated class file", pfile->pname);
215 }
216 }
217
218 return a_utf8;
219 }
220
utf8tolatin1(const utf8_t a_utf8)221 char *utf8tolatin1(const utf8_t a_utf8)
222 {
223 /* function returns fresh allocated zero terminated string,
224 * caller is responsible for freeing
225 */
226
227 /* JVMS p. 101: the null byte is encoded using a two byte format,
228 * Java Virtual Machine Utf8 strings differ in this respect from
229 * standard UTF-8 strings
230 */
231
232 /* Multibyte data is in network byte order */
233
234 char *p;
235 char *pp;
236 char *pstr;
237
238 pstr = pp = xmalloc((a_utf8.nlen+1) * sizeof(char));
239
240 for ( p = (char*)a_utf8.pdata;
241 p < (char*)a_utf8.pdata+a_utf8.nlen;
242 p++ ) {
243 if ( *p & 0x80 ) {
244 err_quit("sorry, real UTF8 decoding not yet implemented\n");
245 } else {
246 *pp++ = *p;
247 }
248 }
249 *pp = '\0';
250
251 return pstr;
252 }
253
254
255 void
skip_bytes(const file_t * pfile,const size_t nnumber)256 skip_bytes(const file_t *pfile, const size_t nnumber)
257 {
258 /* skip a nnumber of bytes in classfile */
259 if ( fseek(pfile->pfs, nnumber, SEEK_CUR) == -1 )
260 err_quit("%s: %s", pfile->pname, strerror(errno));
261 }
262
263 void
add_to_dependencies(struct growable * pdep,const struct growable * pfilt,char * pdepstr,const char * pclass_file)264 add_to_dependencies(struct growable *pdep,
265 const struct growable *pfilt,
266 char *pdepstr,
267 const char *pclass_file)
268 {
269 /* create dependencies */
270 int i;
271 int nlen_filt, nlen_str, nlen_pdepstr;
272 char *pstr, *ptrunc;
273 char path[PATH_MAX+1];
274 char cnp_class_file[PATH_MAX+1];
275 char cnp_str[PATH_MAX+1];
276
277 nlen_pdepstr = strlen(pdepstr);
278 pstr = xmalloc((nlen_pdepstr+6+1)*sizeof(char));
279 memcpy(pstr, pdepstr, nlen_pdepstr+1);
280 strncat(pstr, ".class", 6);
281
282 if ( pfilt->ncur == 0 ) { /* no filters */
283 if ( access(pstr, F_OK) == 0 ) {
284 append_to_growable(pdep, strdup(pstr));
285 }
286 } else {
287 nlen_str = strlen(pstr);
288 for ( i = 0; i < pfilt->ncur; i++ ) {
289 nlen_filt = strlen(pfilt->parray[i]);
290 if ( nlen_filt + 1 + nlen_str > PATH_MAX )
291 err_quit("path to long");
292 memcpy(path, pfilt->parray[i], nlen_filt);
293 path[nlen_filt] = '/';
294 memcpy( path+nlen_filt+1, pstr, nlen_str+1);
295
296 if ( access(path, F_OK) != 0 ) {
297 free(pstr);
298 pstr = NULL;
299 return; /* path doesn't represent a real file, don't bother */
300 }
301
302 /* get the canonical path */
303 #if defined (UNX) || defined(OS2)
304 if ( !(realpath(pclass_file, cnp_class_file)
305 && realpath(path, cnp_str) ) ) {
306 err_quit("can't get the canonical path");
307 }
308 #else
309 if ( !(_fullpath(cnp_class_file, pclass_file, sizeof(cnp_class_file))
310 && _fullpath(cnp_str, path, sizeof(cnp_str)) ) ) {
311 err_quit("can't get the canonical path");
312 }
313 #endif
314
315 /* truncate so that only the package prefix remains */
316 ptrunc = strrchr(cnp_str, cpathsep);
317 *ptrunc = '\0';
318 ptrunc = strrchr(cnp_class_file, cpathsep);
319 *ptrunc = '\0';
320
321 if ( !strcmp(cnp_str, cnp_class_file) ) {
322 free(pstr);
323 pstr = NULL;
324 return; /* identical, don't bother with this one */
325 }
326
327 append_to_growable(pdep, strdup(path));
328 }
329 }
330 free(pstr);
331 return;
332 }
333
334 char *
escape_slash(const char * pstr)335 escape_slash(const char *pstr)
336 {
337 /* returns a fresh allocated string with all cpathsep escaped exchanged
338 * with "$/"
339 *
340 * caller is responsible for freeing
341 */
342
343 const char *pp = pstr;
344 char *p, *pnp;
345 char *pnew_str;
346 int nlen_pnp, nlen_pp;
347 int i = 0;
348
349 while ( (p=strchr(pp, cpathsep)) != NULL ) {
350 ++i;
351 pp = ++p;
352 }
353
354 nlen_pnp = strlen(pstr) + i;
355 pnp = pnew_str = xmalloc((nlen_pnp+1) * sizeof(char));
356
357 pp = pstr;
358
359 if ( i > 0 ) {
360 while ( (p=strchr(pp, cpathsep)) != NULL ) {
361 memcpy(pnp, pp, p-pp);
362 pnp += p-pp;
363 *pnp++ = '$';
364 *pnp++ = '/';
365 pp = ++p;
366 }
367 }
368 nlen_pp = strlen(pp);
369 memcpy(pnp, pp, nlen_pp+1);
370
371 return pnew_str;
372 }
373
374
375 void
print_dependencies(const struct growable * pdep,const char * pclass_file)376 print_dependencies(const struct growable *pdep, const char* pclass_file)
377 {
378 char *pstr;
379 int i;
380
381 pstr = escape_slash(pclass_file);
382 fprintf(pfsout, "%s:", pstr);
383 free(pstr);
384
385 for( i=0; i<pdep->ncur; ++i) {
386 fprintf(pfsout, " \\\n");
387 pstr=escape_slash(pdep->parray[i]);
388 fprintf(pfsout, "\t%s", pstr);
389 free(pstr);
390 }
391
392 fprintf(pfsout,"\n\n");
393 return;
394 }
395
396 int
is_inner(const char * pstr)397 is_inner(const char *pstr)
398 {
399 /* return true if character '$' is found in classname */
400
401 /*
402 * note that a '$' in a classname is not an exact indicator
403 * for an inner class. Java identifier may legally contain
404 * this character, and so may classnames. In the context
405 * of javadep this doesn't matter since the makefile system
406 * can't cope with classfiles with '$'s in the filename
407 * anyway.
408 *
409 */
410
411 if ( strchr(pstr, '$') != NULL )
412 return 1;
413
414 return 0;
415 }
416
417 void
process_class_file(const char * pfilename,const struct growable * pfilt)418 process_class_file(const char *pfilename, const struct growable *pfilt)
419 {
420 /* read class file and extract object information
421 * java class files are in bigendian data format
422 * (JVMS, p. 83)
423 */
424 int i;
425 uint32 nmagic;
426 uint16 nminor, nmajor;
427 uint16 ncnt;
428 uint16 nclass_cnt;
429 utf8_t* pc_pool;
430 uint16* pc_class;
431 file_t file;
432
433 struct growable *pdepen;
434
435 file.pname = (char*)pfilename;
436
437 file.pfs = fopen(file.pname,"rb");
438 if ( !file.pfs )
439 silent_quit();
440
441 nmagic = read_uint32(&file);
442
443 if ( nmagic != 0xCAFEBABE ) {
444 fclose(file.pfs);
445 err_quit("%s: invalid magic", file.pname);
446 }
447
448 nminor = read_uint16(&file);
449 nmajor = read_uint16(&file);
450
451 /* get number of entries in constant pool */
452 ncnt = read_uint16(&file);
453
454 #ifdef DEBUG
455 printf("Magic: %p\n", (void*)nmagic);
456 printf("Major %d, Minor %d\n", nmajor, nminor);
457 printf("Const_pool_count %d\n", ncnt);
458 #endif
459
460 /* There can be ncount entries in the constant_pool table
461 * so at most ncount-1 of them can be of type CONSTANT_Class
462 * (at leat one CONSTANT_Utf8 entry must exist).
463 * Usually way less CONSTANT_Class entries exists, of course
464 */
465
466 pc_pool = xcalloc(ncnt,sizeof(utf8_t));
467 pc_class = xmalloc((ncnt-1)*sizeof(uint16));
468
469 /* pc_pool[0] is reserved to the java virtual machine and does
470 * not exist in the class file
471 */
472
473 nclass_cnt = 0;
474
475 for (i = 1; i < ncnt; i++) {
476 uint8 ntag;
477 uint16 nindex;
478 utf8_t a_utf8;
479
480 ntag = read_uint8(&file);
481
482 /* we are only interested in CONSTANT_Class entries and
483 * Utf8 string entries, because they might belong to
484 * CONSTANT_Class entries
485 */
486 switch(ntag) {
487 case CONSTANT_Class:
488 nindex = read_uint16(&file);
489 pc_class[nclass_cnt++] = nindex;
490 break;
491 case CONSTANT_Fieldref:
492 case CONSTANT_Methodref:
493 case CONSTANT_InterfaceMethodref:
494 skip_bytes(&file, 4L);
495 break;
496 case CONSTANT_String:
497 skip_bytes(&file, 2L);
498 break;
499 case CONSTANT_Integer:
500 case CONSTANT_Float:
501 skip_bytes(&file, 4L);
502 break;
503 case CONSTANT_Long:
504 case CONSTANT_Double:
505 skip_bytes(&file, 8L);
506 /* Long and Doubles take 2(!)
507 * entries in constant_pool_table
508 */
509 i++;
510 break;
511 case CONSTANT_NameAndType:
512 skip_bytes(&file, 4L);
513 break;
514 case CONSTANT_Utf8:
515 a_utf8 = read_utf8(&file);
516 pc_pool[i] = a_utf8;
517 break;
518 case CONSTANT_MethodHandle:
519 skip_bytes(&file, 3);
520 break;
521 case CONSTANT_MethodType:
522 skip_bytes(&file, 2);
523 break;
524 case CONSTANT_InvokeDynamic:
525 skip_bytes(&file, 4);
526 break;
527 default:
528 /* Unknown Constant_pool entry, this means we are
529 * in trouble
530 */
531 err_quit("corrupted class file\n");
532 break;
533
534 }
535 }
536
537 fclose(file.pfs);
538
539 pdepen = allocate_growable();
540
541 for (i = 0; i < nclass_cnt; i++) {
542 char *pstr, *ptmpstr;
543 pstr = ptmpstr = utf8tolatin1(pc_pool[pc_class[i]]);
544 /* we are not interested in inner classes */
545 if ( is_inner(pstr) ) {
546 free(pstr);
547 pstr = NULL;
548 continue;
549 }
550 /* strip off evt. array indicators */
551 if ( *ptmpstr == '[' ) {
552 while ( *ptmpstr == '[' )
553 ptmpstr++;
554 /* we only interested in obj. arrays, which are marked with 'L' */
555 if ( *ptmpstr == 'L' ) {
556 char *p = pstr;
557 pstr = strdup(++ptmpstr);
558 /* remove final ';' from object array name */
559 pstr[strlen(pstr)-1] = '\0';
560 free(p);
561 } else {
562 free(pstr);
563 pstr = NULL;
564 }
565 }
566
567 if (pstr) {
568 add_to_dependencies(pdepen, pfilt, pstr, file.pname);
569 free(pstr);
570 }
571 }
572
573 print_dependencies(pdepen, file.pname);
574 free_growable(pdepen);
575 pdepen = NULL;
576
577 for (i = 0; i < ncnt; i++)
578 free(pc_pool[i].pdata);
579
580 free(pc_class);
581 free(pc_pool);
582 }
583
584 void *
xmalloc(size_t size)585 xmalloc(size_t size)
586 {
587 void *ptr;
588
589 ptr = malloc(size);
590
591 if ( !ptr )
592 err_quit("out of memory");
593
594 return ptr;
595 }
596
597
598 void *
xcalloc(size_t nmemb,size_t size)599 xcalloc(size_t nmemb, size_t size)
600 {
601 void *ptr;
602
603 ptr = calloc(nmemb, size);
604
605 if ( !ptr )
606 err_quit("out of memory");
607
608 return ptr;
609 }
610
611 void *
xrealloc(void * ptr,size_t size)612 xrealloc(void *ptr, size_t size)
613 {
614 ptr = realloc(ptr, size);
615
616 if ( !ptr )
617 err_quit("out of memory");
618
619 return ptr;
620 }
621
622 void
err_quit(const char * fmt,...)623 err_quit(const char* fmt, ...)
624 {
625 /* No dependency file must be generated for any error condition,
626 * just print message and exit.
627 */
628 va_list args;
629 char buffer[PATH_MAX];
630
631 va_start(args, fmt);
632
633 if ( pprogname )
634 fprintf(stderr, "%s: ", pprogname);
635 vsnprintf(buffer, sizeof(buffer), fmt, args);
636 fputs(buffer, stderr);
637 fputc('\n', stderr);
638
639 va_end(args);
640
641 /* clean up */
642 if ( pfsout && pfsout != stdout ) {
643 fclose(pfsout);
644 unlink(pout_file);
645 }
646 exit(1);
647 }
648
649 void
silent_quit()650 silent_quit()
651 {
652 /* In some cases we should just do a silent exit */
653
654 /* clean up */
655 if ( pfsout && pfsout != stdout ) {
656 fclose(pfsout);
657 unlink(pout_file);
658 }
659 exit(0);
660 }
661
append_to_growable(struct growable * pgrow,char * pstr)662 int append_to_growable(struct growable *pgrow, char *pstr)
663 {
664 /* append an element pstr to pgrow,
665 * return new number of elements
666 */
667 grow_if_needed(pgrow);
668 pgrow->parray[pgrow->ncur++] = pstr;
669 return pgrow->ncur;
670 }
671
672 void
grow_if_needed(struct growable * pgrow)673 grow_if_needed(struct growable *pgrow)
674 {
675 /* grow growable arrays */
676
677 if ( pgrow->ncur >= pgrow->nmax ) {
678 pgrow->parray = xrealloc(pgrow->parray,
679 (NGROW*pgrow->nmax)*sizeof(char*));
680 pgrow->nmax *= NGROW;
681 }
682 return;
683 }
684
allocate_growable(void)685 struct growable *allocate_growable(void)
686 {
687 /* allocate an growable array,
688 * initialize with NGROW_INIT elements
689 */
690
691 struct growable *pgrow;
692
693 pgrow = xmalloc(sizeof(struct growable));
694 pgrow->parray = xmalloc(NGROW_INIT*sizeof(char *));
695 pgrow->nmax = NGROW_INIT;
696 pgrow->ncur = 0;
697 return pgrow;
698 }
699
700 void
free_growable(struct growable * pgrow)701 free_growable(struct growable *pgrow)
702 {
703 int i;
704 for( i = 0; i < pgrow->ncur; i++ )
705 free(pgrow->parray[i]);
706 free(pgrow->parray);
707 free(pgrow);
708 }
709
710 void
create_filters(struct growable * pfilt,const struct growable * pinc)711 create_filters(struct growable *pfilt, const struct growable *pinc)
712 {
713 char *p, *pp, *pstr;
714 int i, nlen, nlen_pstr;
715 /* break up includes into filter list */
716 for ( i = 0; i < pinc->ncur; i++ ) {
717 pp = pinc->parray[i];
718
719 while ( (p = strchr(pp, csep)) != NULL) {
720 nlen = p - pp;
721 pstr = xmalloc((nlen+1)*sizeof(char*));
722 memcpy(pstr, pp, nlen);
723 pstr[nlen] = '\0';
724 append_to_growable(pfilt, pstr);
725 pp = p + 1;
726 }
727 nlen_pstr = strlen(pp);
728 pstr = xmalloc((nlen_pstr+1)*sizeof(char*));
729 memcpy(pstr, pp, nlen_pstr+1);
730 append_to_growable(pfilt, pstr);
731 }
732
733 }
734
735 void
usage()736 usage()
737 {
738 fprintf(stderr,
739 "usage: %s [-i|-I includepath ... -s|-S seperator "
740 "-o|-O outpath -v|-V -h|-H] <file> ....\n",
741 pprogname);
742 }
743
744 #ifdef WNT
745 /* my very simple minded implementation of getopt()
746 * it's to sad that getopt() is not available everywhere
747 * note: this is not a full POSIX conforming getopt()
748 */
simple_getopt(char * pargv[],const char * poptstring)749 int simple_getopt(char *pargv[], const char *poptstring)
750 {
751 char *parg = pargv[optind];
752
753 /* skip all response file arguments */
754 if ( parg ) {
755 while ( *parg == '@' )
756 parg = pargv[++optind];
757
758 if ( parg[0] == '-' && parg[1] != '\0' ) {
759 char *popt;
760 int c = parg[1];
761 if ( (popt = strchr(poptstring, c)) == NULL ) {
762 optopt = c;
763 if ( opterr )
764 fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);
765 return '?';
766 }
767 if ( *(++popt) == ':') {
768 if ( parg[2] != '\0' ) {
769 optarg = ++parg;
770 } else {
771 optarg = pargv[++optind];
772 }
773 } else {
774 optarg = NULL;
775 }
776 ++optind;
777 return c;
778 }
779 }
780 return -1;
781 }
782 #endif
783
784 int CDECL
main(int argc,char * argv[])785 main(int argc, char *argv[])
786 {
787 int bv_flag = 0;
788 struct growable *presp, *pincs, *pfilters;
789 int c, i, nall_argc;
790 char **pall_argv;
791
792 presp = allocate_growable();
793
794 /* FIXME: cleanup the option parsing */
795 /* search for response file, read it */
796 for ( i = 1; i < argc; i++ ) {
797 char *parg = argv[i];
798 char buffer[RES_FILE_BUF];
799
800 if ( *parg == '@' ) {
801 FILE *pfile = fopen(++parg, "r");
802 if ( !pfile )
803 err_quit("%s: %s", parg, strerror(errno));
804 while ( !feof(pfile) ) {
805 char *p, *token;
806
807 if ( fgets(buffer, RES_FILE_BUF, pfile) ) {;
808 p = buffer;
809 while ( (token = strtok(p, " \t\n")) != NULL ) {
810 p = NULL;
811 append_to_growable(presp, strdup(token));
812 }
813 }
814 }
815 fclose(pfile);
816 }
817 }
818
819 /* copy all arguments incl. response file in one array
820 * for parsing with getopt
821 */
822 nall_argc = argc + presp->ncur;
823 pall_argv = xmalloc((nall_argc+1)*sizeof(char *));
824 memcpy(pall_argv, argv, argc*sizeof(char *));
825 memcpy(pall_argv+argc, presp->parray, presp->ncur*sizeof(char *));
826 *(pall_argv+argc+presp->ncur) = '\0'; /* terminate */
827
828 opterr = 0;
829 pincs = allocate_growable();
830
831 #ifdef WNT
832 while( (c = simple_getopt(pall_argv, ":i:I:s:S:o:OhHvV")) != -1 ) {
833 #else
834 while( (c = getopt(nall_argc, pall_argv, ":i:I:s:S:o:OhHvV")) != -1 ) {
835 #endif
836 switch(c) {
837 case 'i':
838 case 'I':
839 append_to_growable(pincs, strdup(optarg));
840 break;
841 case 's':
842 case 'S':
843 csep = optarg[0];
844 break;
845 case 'o':
846 case 'O':
847 pout_file = optarg;
848 break;
849 case 'h':
850 case 'H':
851 usage();
852 return 0;
853 break;
854 case 'v':
855 case 'V':
856 bv_flag = 1;
857 break;
858 case '?':
859 if (isprint (optopt))
860 fprintf (stderr,
861 "Unknown option `-%c'.\n", optopt);
862 else
863 fprintf (stderr,
864 "Unknown option character `\\x%x'.\n",
865 optopt);
866 usage();
867 return 1;
868 break;
869 case ':':
870 fprintf(stderr, "Missing parameter.\n");
871 usage();
872 return 1;
873 break;
874 default:
875 usage();
876 return 1;
877 break;
878 }
879 }
880
881 pfilters = allocate_growable();
882 create_filters(pfilters, pincs);
883 free_growable(pincs);
884 pincs = NULL;
885
886 if ( pout_file ) {
887 pfsout = fopen(pout_file, "w");
888 if ( !pfsout )
889 err_quit("%s: %s", pout_file, strerror(errno));
890 } else {
891 pfsout = stdout;
892 }
893
894 /* the remaining arguments are either class file
895 * names or response files, ignore response file
896 * since they have already been included
897 */
898 for ( i = optind; i < nall_argc; i++ ) {
899 char *parg = pall_argv[i];
900 if ( *parg != '@' ) {
901 process_class_file(parg, pfilters);
902 if ( pfsout != stdout ) {
903 if ( bv_flag )
904 printf("Processed %s ...\n", parg);
905 }
906 }
907 }
908
909 free_growable(pfilters);
910 pfilters = NULL;
911 free(pall_argv);
912 pall_argv = NULL;
913 free_growable(presp);
914 presp = NULL;
915
916 fclose(pfsout);
917 exit(0);
918 }
919
920