xref: /trunk/main/soltools/javadep/javadep.c (revision d32879a4)
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
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
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
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
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 
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
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
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 *
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
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
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 chararcter, 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
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 *
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 *
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 *
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
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
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 
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
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 
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
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
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
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  */
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
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