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