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