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