OSDN Git Service

2002-09-10 Matthias Klose <doko@debian.org>
[pf3gnuchains/gcc-fork.git] / fastjar / jargrep.c
1 /*
2   jargrep.c - main functions for jargrep utility
3   Copyright (C) 2002 Free Software Foundation
4   Copyright (C) 1999, 2000 Bryan Burns
5   Copyright (C) 2000 Cory Hollingsworth 
6  
7   Parts of this program are base on Bryan Burns work with fastjar 
8   Copyright (C) 1999. 
9
10   This program is free software; you can redistribute it and/or
11   modify it under the terms of the GNU General Public License
12   as published by the Free Software Foundation; either version 2
13   of the License, or (at your option) any later version.
14   
15   This program 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 General Public License for more details.
19   
20   You should have received a copy of the GNU General Public License
21   along with this program; if not, write to the Free Software
22   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 */
24
25 /* Id: jargrep.c,v 1.5 2002/01/03 04:57:56 rodrigc Exp
26
27 Log: jargrep.c,v 
28 Revision 1.5  2002/01/03 04:57:56  rodrigc
29 2001-01-02  Craig Rodrigues  <rodrigc@gcc.gnu.org>
30
31         PR bootstrap/5117
32         * configure.in (AC_CHECK_HEADERS): Check for stdlib.h.
33         * Makefile.am: Move grepjar to bin_PROGRAMS.
34         * config.h.in: Regenerated.
35         * Makefile.in: Regenerated.
36         * aclocal.m4: Regenerated.
37         * jargrep.c: Eliminate some signed/unsigned and default
38         uninitialized warnings. Use HAVE_STDLIB_H instead of
39         STDC_HEADERS macro.
40         * jartool.c: Likewise.
41         * compress.c: Likewise.
42
43 Revision 1.4  2000/12/15 18:45:09  tromey
44         * jargrep.c: Include getopt.h if it exists.
45         (optind): Declare.
46         * configure, config.h: Rebuilt.
47         * configure.in: Check for getopt.h.
48
49 Revision 1.3  2000/12/14 18:45:35  ghazi
50 Warning fixes:
51
52         * compress.c: Include stdlib.h and compress.h.
53         (rcsid): Delete.
54         (report_str_error): Make static.
55         (ez_inflate_str): Delete unused variable.  Add parens in if-stmt.
56         (hrd_inflate_str): Likewise.
57
58         * compress.h (init_compression, end_compression, init_inflation,
59         end_inflation): Prototype void arguments.
60
61         * dostime.c (rcsid): Delete.
62
63         * jargrep.c: Include ctype.h, stdlib.h, zlib.h and compress.h.
64         Make functions static.  Cast ctype function argument to `unsigned
65         char'.  Add parens in if-stmts.  Constify.
66         (Usage): Change into a macro.
67         (jargrep): Remove unused parameter.
68
69         * jartool.c: Constify.  Add parens in if-stmts.  Align
70         signed/unsigned char pointers in functions calls using casts.
71         (rcsid): Delete.
72         (list_jar): Fix printf format specifier.
73         (usage): Chop long string into bits.  Reformat.
74
75         * pushback.c (rcsid): Delete.
76
77 Revision 1.2  2000/12/11 02:59:55  apbianco
78 2000-12-10  Robert Lipe <robertlipe@usa.net>
79
80         * jargrep.c (jargrep): Added null statement after case.
81
82 2000-12-10  Alexandre Petit-Bianco  <apbianco@cygnus.com>
83
84         * Makefile: Removed.
85         * Makefile.in: Rebuilt with `-i' and `--enable-foreign'.
86
87 (http://gcc.gnu.org/ml/gcc/2000-12/msg00294.html)
88
89 Revision 1.1  2000/12/09 03:08:23  apbianco
90 2000-12-08  Alexandre Petit-Bianco  <apbianco@cygnus.com>
91
92         * fastjar: Imported.
93
94 Revision 1.8  2000/09/13 14:02:02  cory
95 Reformatted some of the code to more closly match the layout of the orriginal
96 fastjar utility.
97
98 Revision 1.7  2000/09/12 22:29:36  cory
99 Jargrep now seems to do what I want it to do.  Performs properly on Linux x86,
100 will test some other platforms later.
101
102
103 */
104
105 #include "config.h"
106 #include <stdio.h>
107 #include <unistd.h>
108 #include <regex.h>
109 #include <errno.h>
110 #include <string.h>
111 #include <sys/types.h>
112 #include <sys/stat.h>
113 #include <fcntl.h>
114 #include <ctype.h>
115 #ifdef HAVE_STDLIB_H
116 #include <stdlib.h>
117 #endif
118 #include "jargrep.h"
119 #include "jartool.h"
120 #include "pushback.h"
121 #include "zipfile.h"
122 #include "zlib.h"
123 #include "compress.h"
124 #include <getopt.h>
125
126 void version(void);
127 void help(const char *name);
128
129 #define Usage "Usage: %s [-bcinsVw] [--version|--help] <-e PATTERN | PATTERN> FILE ...\n"
130
131 /*
132 Function name: opt_valid
133 arg:    options Bitfield flag that contains the command line options of grepjar.
134 purpose:        To guard agains the occurance of certain incompatible flags being used
135 together.
136 returns: TRUE if options are valid, FALSE otherwise.
137 */
138
139 static int opt_valid(int options) {
140         int retflag;
141
142         if((options & JG_PRINT_COUNT) && 
143                 (options & (JG_PRINT_BYTEOFFSET | JG_PRINT_LINE_NUMBER)))
144         {
145                 retflag = FALSE;
146         }
147         else retflag = TRUE;
148
149         return retflag;
150 }
151
152 /*
153 Function name: create_regexp
154 args:   regstr  String containing the uncompiled regular expression.  This may be the 
155                                 expression as is passed in through argv.
156                 options This is the flag containing the commandline options that have been
157                                 parsed by getopt.
158 purpose: Handle the exception handling involved with setting upt a new regular 
159 expression.
160 returns: Newly allocated compile regular expression ready to be used in an regexec call.
161 */
162
163 static regex_t *create_regexp(const char *regstr, int options) {
164         regex_t *exp;
165         int errcode;
166         int msgsize;
167         char *errmsg;
168
169         if((exp = (regex_t *) malloc(sizeof(regex_t))))
170         {
171                 if((errcode = regcomp(exp, regstr, (options & JG_IGNORE_CASE) ? REG_ICASE : 0))) {
172                         fprintf(stderr, "regcomp of regex failed,\n");
173                         if((errmsg = (char *) malloc(msgsize = regerror(errcode, exp, NULL, 0) + 1))) {
174                                 regerror(errcode, exp, errmsg, msgsize);
175                                 fprintf(stderr, "Error: %s\n", errmsg);
176                                 free(exp);
177                                 free(errmsg);
178                                 exit(1);
179                         }
180                         else {
181                                 fprintf(stderr, "Malloc of errmsg failed.\n");
182                                 fprintf(stderr, "Error: %s\n", strerror(errno));
183                                 free(exp);
184                                 exit(1);
185                         }
186                 }
187         }
188         else {
189                 fprintf(stderr, "Malloc of regex failed,\n");
190                 fprintf(stderr, "Error: %s\n", strerror(errno));
191                 exit(1);
192         }
193
194         return exp; 
195 }
196
197 /*
198 Function name: check_sig
199 args:   scratch Pointer to array of bytes containing signature.
200                 pbf             Pointer to push back handle for jar file.
201 purpose:        Verify that checksum is correct.
202 returns: 0, 1, or 2.  0 means we are ready to read embedded file information.  1 means
203 we have read beyound the embedded file list and can exit knowing we have read all the
204 relevent information.  2 means we still haven't reached embdedded file list and need to
205 do some more reading.
206 */
207 static int check_sig(ub1 *scratch, pb_file *pbfp) {
208         ub4 signature;
209         int retflag = 0;
210
211         signature = UNPACK_UB4(scratch, 0);
212
213 #ifdef DEBUG    
214     printf("signature is %x\n", signature);
215 #endif
216     if(signature == 0x08074b50){
217 #ifdef DEBUG    
218       printf("skipping data descriptor\n");
219 #endif
220       pb_read(pbfp, scratch, 12);
221       retflag = 2;
222     } else if(signature == 0x02014b50){
223 #ifdef DEBUG    
224       printf("Central header reached.. we're all done!\n");
225 #endif
226       retflag = 1;
227     }else if(signature != 0x04034b50){
228       printf("Ick! %#x\n", signature);
229       retflag = 1;
230     }
231     
232         return retflag;
233 }
234
235 /*
236 Function name: decd_siz
237 args    csize           Pointer to embedded file's compressed size.
238                 usize           Pointer to embedded file's uncmpressed size.
239                 fnlen           Pointer to embedded file's file name length.
240                 elfen           Pointer to length of extra fields in jar file.
241                 flags           Pointer to bitmapped flags.
242                 method          Pointer to indicator of storage method of embedded file.
243                 file_header     Pointer to string containing the above values to be unbacked.
244 Purpose: Unpack the series of values from file_header.
245 */
246
247 static void decd_siz(ub4 *csize, ub4 *usize, ub2 *fnlen, ub2 *eflen, ub2 *flags, ub2 *method, ub1 *file_header) {
248     *csize = UNPACK_UB4(file_header, LOC_CSIZE);
249 #ifdef DEBUG    
250     printf("Compressed size is %u\n", *csize);
251 #endif
252
253         *usize = UNPACK_UB4(file_header, LOC_USIZE);
254 #ifdef DEBUG
255         printf("Uncompressed size is %u\n", *usize);
256 #endif
257
258     *fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
259 #ifdef DEBUG    
260     printf("Filename length is %hu\n", *fnlen);
261 #endif
262
263     *eflen = UNPACK_UB2(file_header, LOC_EFLEN);
264 #ifdef DEBUG    
265     printf("Extra field length is %hu\n", *eflen);
266 #endif
267
268     *flags = UNPACK_UB2(file_header, LOC_EXTRA);
269 #ifdef DEBUG    
270     printf("Flags are %#hx\n", *flags);
271 #endif
272
273     *method = UNPACK_UB2(file_header, LOC_COMP);
274 #ifdef DEBUG
275     printf("Compression method is %#hx\n", *method);
276 #endif
277
278 }
279
280 /*
281 Function name: new_filename
282 args:   pbf             Pointer to push back file handle.  Used for reading input file.
283                 len             Length of file name to be read.
284 purpose:        Read in the embedded file name from jar file.
285 returns: Pointer to newly allocated string containing file name.
286 */
287
288 static char *new_filename(pb_file *pbf, ub4 len) {
289         char *filename;
290
291         if(!(filename = (char *) malloc(len + 1))) {
292                 fprintf(stderr, "Malloc failed of filename\n");
293                 fprintf(stderr, "Error: %s\n", strerror(errno));
294         }
295     pb_read(pbf, filename, len);
296     filename[len] = '\0';
297
298 #ifdef DEBUG    
299     printf("filename is %s\n", filename);
300 #endif
301
302         return filename;
303 }
304
305 /*
306 Funtion name: read_string
307 args:   pbf             Pointer to push back file handle.  Used for reading input file.
308                 size    Size of embedded file in bytes.
309 purpose:        Create a string containing the contents of the embedded noncompressed file.
310 returns: Pointer to newly allocated string containing embedded file contents.
311 */
312
313 static char *read_string(pb_file *pbf, int size) {
314         char *page;
315         
316         if((page = (char *) malloc(size + 1))) {
317                 pb_read(pbf, page, size);
318                 page[size] = '\0';
319         }
320         else {
321                 fprintf(stderr, "Malloc of page buffer failed.\n");
322                 fprintf(stderr, "Error: %s\n", strerror(errno));
323                 exit(1);
324         }
325
326         return page;
327 }
328
329 /*
330 Function name: extract_line
331 args:   stream  String containing the full contents of a file which is to be substringed
332                                 in order to provide line representing our grep output.
333                 begin   Index into stream which regular expression first matches.
334                 end             Index into stream which end of match to the regular expression.
335                 b               Pointer to the index of what will be the beginning of the line when
336                                 string is returned.  Used for -b option.
337 purpose:        Create a string that can be printed by jargrep from the long string stream.
338 The matching line that is printed out by jargrep is generated by this function.
339 returns: Pointer to newly allocated string containing matched expression.
340 */
341
342 static char *extract_line(const char *stream, regoff_t begin, regoff_t end, int *b) {
343         int e;
344         int length;
345         char *retstr;
346
347         for(*b = begin; *b >= 0 && !iscntrl((unsigned char)stream[*b]); (*b)--);
348         (*b)++;
349         for(e = end; stream[e] == '\t' || !iscntrl((unsigned char)stream[e]); e++);
350         length = e - *b;
351         if((retstr = (char *) malloc(length + 1))) {
352                 sprintf(retstr, "%d:", *b);
353                 strncpy(retstr, &(stream[*b]), length);
354                 retstr[length] = '\0';
355         }
356         else {
357                 fprintf(stderr, "Malloc failed of output string.\n");
358                 fprintf(stderr, "Error: %s\n", strerror(errno));
359                 exit(1);
360         }
361
362         return retstr;
363 }
364
365 /*
366 Function name: chk_wrd
367 args:   exp             Pointer to compiled POSIX style regular expression of search target.
368                 str             String known to contain at least one match of exp.
369 purpose: Verify that the occurance of the regular expression in str occurs as a whole
370 word and not a substring of another word.
371 returns: TRUE if it is a word, FALSE of it is a substring.
372 */
373
374 static int chk_wrd(regex_t *exp, const char *str) {
375         int wrd_fnd = FALSE;
376         int regflag;
377         int frnt_ok;
378         int bck_ok;
379         const char *str2;
380         regmatch_t match;
381
382         str2 = str;
383         frnt_ok = bck_ok = FALSE;
384         while(!wrd_fnd && !(regflag = regexec(exp, str2, 1, &match, 0))) {
385                 if(!match.rm_so && (str2 == str)) frnt_ok = TRUE;
386                 else if(!isalnum((unsigned char)str2[match.rm_so - 1])
387                         && str2[match.rm_so - 1] != '_')
388                         frnt_ok = TRUE;
389                 else frnt_ok = FALSE;
390                 if(frnt_ok) {
391                         if(str2[match.rm_eo] == '\0') bck_ok = TRUE;
392                         else if(!isalnum((unsigned char)str2[match.rm_eo])
393                                 && str2[match.rm_eo] != '_')
394                                 bck_ok = TRUE;
395                         else bck_ok = FALSE;
396                 }
397                 wrd_fnd = frnt_ok && bck_ok;
398                 str2 = &(str2[match.rm_eo]);
399         }
400
401         return wrd_fnd;
402 }
403
404 /*
405 Function name: prnt_mtchs
406 args:   exp                     Pointer to compiled POSIX style regular expression of search target.
407                 filename        String containing the name of the embedded file which matches have
408                                         been found in.
409                 stream          String containing the processed contents of the embedded jar file
410                                         represended with filename.
411                 pmatch          Array of regmatch_t matches into stream.
412                 nl_offset       Array of offsets of '\n' characters in stream.  May be NULL if -n is
413                                         not set on command line.
414                 num                     Number of matches in pmatch array.
415                 lines           Number of lines in file.  Not set if -n is not set on command line.
416                 options         Bitwise flag containing flags set to represent the command line 
417                                         options.
418 purpose:        Control output of jargrep.  Output is controlled by which options have been
419 set at the command line.
420 */
421
422 static void prnt_mtchs(regex_t *exp, const char *filename, const char *stream, regmatch_t *pmatch, regmatch_t *nl_offset, int num, int lines, int options) {
423         int i;
424         int j = 0;
425         int ln_cnt;
426         int begin;
427         int o_begin;
428         char *str;
429
430         o_begin = -1;
431         ln_cnt = 0;
432         for(i = 0; i < num; i++) {
433                 str = extract_line(stream, pmatch[i].rm_so, pmatch[i].rm_eo, &begin);
434                 if(begin > o_begin) {
435                         if(!(options & JG_WORD_EXPRESSIONS) || chk_wrd(exp, str)) {
436                                 ln_cnt++;
437                                 if(!(options & JG_PRINT_COUNT)) {
438                                         printf("%s:", filename);
439                                         if(options & JG_PRINT_LINE_NUMBER) {
440                                                 for(; j < lines && nl_offset[j].rm_so < begin; j++);
441                                                 printf("%d:", j + 1);
442                                         }
443                                         if(options & JG_PRINT_BYTEOFFSET) printf("%d:", begin);
444                                         printf("%s\n", str);
445                                 }
446                         }
447                 }
448                 o_begin = begin;
449                 free(str);
450         }
451         if(options & JG_PRINT_COUNT) printf("%s:%d\n", filename, ln_cnt);
452 }
453
454 /*
455 Function name: check_crc
456 args:   pbf             Pointer to pushback file pointer for jar file.
457                 stream  String containing the non modified contents fo the extraced file entry.
458                 usize   Size of file in bytes.
459 purpose:        Verify the CRC matches that as what is stored in the jar file.
460 */
461
462 static void check_crc(pb_file *pbf, const char *stream, ub4 usize) {
463         ub4 crc=0;
464         ub4 lcrc;
465         ub1 scratch[16];
466
467         crc = crc32(crc, NULL, 0);
468         crc = crc32(crc, (const unsigned char *)stream, usize);
469         if(pb_read(pbf, scratch, 16) != 16) {
470                 perror("read");
471         exit(1);
472         }
473         if(UNPACK_UB4(scratch, 0) != 0x08074b50) {
474                 fprintf(stderr, "Error! Missing data descriptor!\n");
475                 exit(1);
476         }
477         lcrc = UNPACK_UB4(scratch, 4);
478         if(crc != lcrc){
479         fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
480               crc, lcrc);
481         exit(1);
482     }
483 }
484
485 /*
486 Function name mk_ascii
487 args:   stream  String that contains the contents of the extraced file entry.
488                 usize   String size.
489 purpose:        Make certain that the contents of the file are ASCII, not binary.  This
490 permits grepping of binary files as well by converting non ASCII and control characters
491 into '\n'.
492 */
493
494 static void mk_ascii(char *stream, size_t usize) {
495         size_t i;
496
497         for(i = 0; i < usize; i++) 
498                 if(stream[i] != '\t'
499                    && (iscntrl((unsigned char)stream[i])
500                        || (unsigned char) stream[i] >= 128))
501                         stream[i] = '\n';
502 }
503
504 /*
505 Funtion name: fnd_match
506 args:   exp                     Pointer to compiled POSIX style regular expression of search target.
507                 str_stream      String that contains the contents of the extracted file entry.
508                 i                       Pointer to counter and index of matches.
509 purpose:        Search str_stream for occurances of the regular expression exp and create
510 an array of matches.
511 returns:  Pointer to newly allocated array of regmatch_t which gives indexes to start
512 and end of matches.  NULL is returned upon no matches found.
513 */
514
515 static regmatch_t *fnd_match(regex_t *exp, const char *str_stream, int *i) {
516         int regflag;
517         regmatch_t match;
518         regmatch_t *match_array;
519         regmatch_t *tmp;
520
521         match_array = NULL;
522         for(*i = 0, regflag = regexec(exp, str_stream, 1, &match, 0); !regflag; 
523                 regflag = regexec(exp, &(str_stream[match.rm_eo]), 1, &match, 0), (*i)++)
524         {
525                 if((tmp = (regmatch_t *) 
526                     realloc(match_array, sizeof(regmatch_t) * ((*i) + 1))))
527                 {
528                         match_array = tmp;
529                         if(*i) {
530                                 match.rm_so += match_array[(*i) - 1].rm_eo;
531                                 match.rm_eo += match_array[(*i) - 1].rm_eo;
532                         }
533                         match_array[*i] = match;
534                 }
535                 else {
536                         fprintf(stderr, "Realloc of match_array failed.\n");
537                         fprintf(stderr, "Error: %s\n", strerror(errno));
538                         exit(1);
539                 }
540         } 
541
542         return match_array;
543 }
544
545 /*
546 Function name: cont_grep
547 args:   exp             Pointer to compiled POSIX style regular expression of search target.
548                 nl_exp  Pointer to compiled POSIX style regular expression of newlines.  This
549                                 argument is NULL unless the -n option is used on the command line.
550                 fd              File descriptor of the jar file being grepped.
551                 pbf             Pointer to pushback file style file stream.  This is for use with
552                                 the pushback.c file io funtions.
553                 options Bitwise flag containing flags set to represent the command line options.
554 purpose:        This function handles single entries in an open jar file.  The header is
555 read and then the embeded file is extracted and grepped.
556 returns: FALSE upon failure, TRUE otherwise.
557 */
558
559 static int cont_grep(regex_t *exp, regex_t *nl_exp, int fd, pb_file *pbf, int options) {
560         int retflag = TRUE;
561         int i;
562         int j;
563         ub4 csize;
564         ub4 usize;
565         ub2 fnlen;
566         ub2 eflen;
567         ub2 flags;
568         ub2 method;
569         ub1 file_header[30];
570         char *filename;
571         char *str_stream;
572         regmatch_t *match_array;
573         regmatch_t *nl_offsets=0;
574
575         if(pb_read(pbf, (file_header + 4), 26) != 26) {
576                 perror("read");
577                 retflag = FALSE;
578         }
579         else {
580                 decd_siz(&csize, &usize, &fnlen, &eflen, &flags, &method, file_header);
581                 filename = new_filename(pbf, fnlen);
582                 lseek(fd, eflen, SEEK_CUR);
583                 if(filename[fnlen - 1] != '/') {
584                         str_stream = (method == 8 || (flags & 0x0008)) ? 
585                                 (char *) inflate_string(pbf, &csize, &usize) : 
586                                         read_string(pbf, csize);
587                         if(flags & 0x008) check_crc(pbf, str_stream, usize);
588                         mk_ascii(str_stream, usize);
589                         match_array = fnd_match(exp, str_stream, &i);
590                         if((options & JG_PRINT_LINE_NUMBER) && i) 
591                                 nl_offsets = fnd_match(nl_exp, str_stream, &j);
592                         prnt_mtchs(exp, filename, str_stream, match_array, nl_offsets, i, j, options);
593                         if(match_array) free(match_array);
594                         free(str_stream);
595                 }
596                 free(filename);
597                 retflag = TRUE;
598         }
599
600         return retflag;
601 }
602
603 /*
604 Funtion name: jargrep
605 args:   exp             Pointer to compiled POSIX style regular expression of search target.
606                 nl_exp  Pointer to compiled regular expression for newlines or NULL.  Only set 
607                                 if -n option is present at command line.
608                 jarfile Filename of jar file to be searched.
609                 options Bitwise flag containing flags set to represent the command line options.
610 purpose:        Open jar file.  Check signatures.  When right signature is found go to deeper
611 grep routine.
612 */
613
614 static void jargrep(regex_t *exp, regex_t *nl_exp, const char *jarfile, int options){
615         int fd;
616         int floop = TRUE;
617         pb_file pbf;
618         ub1 scratch[16];
619
620         if((fd = open(jarfile, O_RDONLY)) == -1) {
621                 if(!(options & JG_SUPRESS_ERROR))
622                         fprintf(stderr, "Error reading file '%s': %s\n", jarfile, strerror(errno));
623         }
624         else {
625                 pb_init(&pbf, fd);      
626                 
627                 do {
628                         if(pb_read(&pbf, scratch, 4) != 4) {
629                                 perror("read");
630                                 floop = FALSE;
631                         }
632                         else {
633                                 switch (check_sig(scratch, &pbf)) {
634                                 case 0:
635                                         floop = cont_grep(exp, nl_exp, fd, &pbf, options);
636                                         break;
637                                 case 1:
638                                         floop = FALSE;
639                                         break;
640                                 case 2:
641                                         /* fall through continue */
642                                         ;
643                                 }
644                         }
645                 } while(floop);
646         }
647 }
648
649 /* This is used to mark options with no short value.  */
650 #define LONG_OPT(Num)  ((Num) + 128)
651
652 #define OPT_HELP     LONG_OPT (0)
653
654 static const struct option option_vec[] =
655 {
656   { "help", no_argument, NULL, OPT_HELP },
657   { "version", no_argument, NULL, 'V' },
658   { NULL, no_argument, NULL, 0 }
659 };
660
661 /*
662 Funtion Name: main
663 args:   argc    number of in coming args.
664                 argv    array of strings.
665 purpose: Entry point of the program.  Parse command line arguments and set options.
666 Set up regular expressions.  Call grep routines for each file as input.
667 returns: 1 on error 0 on success.
668 */
669
670 int main(int argc, char **argv) {
671         int c;
672         int retval = 0;
673         int fileindex;
674         int options = 0;
675         regex_t *regexp;
676         regex_t *nl_exp = NULL;
677         char *regexpstr = NULL;
678
679         while((c = getopt_long(argc, argv, "bce:insVw",
680                                option_vec, NULL)) != -1) {
681                 switch(c) {
682                         case 'b':
683                                 options |= JG_PRINT_BYTEOFFSET;
684                                 break;
685                         case 'c':
686                                 options |= JG_PRINT_COUNT;
687                                 break;
688                         case 'e':
689                                 if(!(regexpstr = (char *) malloc(strlen(optarg) + 1))) {
690                                         fprintf(stderr, "Malloc failure.\n");
691                                         fprintf(stderr, "Error: %s\n", strerror(errno));
692                                         exit(1);
693                                 }
694                                 strcpy(regexpstr, optarg);
695                                 break;
696                         case 'i':
697                                 options |= JG_IGNORE_CASE;
698                                 break;
699                         case 'n':
700                                 options |= JG_PRINT_LINE_NUMBER;
701                                 break;
702                         case 's':
703                                 options |= JG_SUPRESS_ERROR;
704                                 break;
705                         case 'v':
706                                 options |= JG_INVERT;
707                                 break;
708                         case 'V':
709                                 version ();
710                                 break;
711                         case 'w':
712                                 options |= JG_WORD_EXPRESSIONS;
713                                 break;
714                         case OPT_HELP:
715                                 help(argv[0]);
716                                 break;
717                         default:
718                                 fprintf(stderr, Usage, argv[0]);
719                                 exit(1);
720                 }
721         }
722         if(!regexpstr){
723                 if(((argc - optind) >= 2)) {
724                         regexpstr = argv[optind];
725                         fileindex = optind + 1;
726                 }
727                 else {
728                         fprintf(stderr, "Invalid arguments.\n");
729                         fprintf(stderr, Usage, argv[0]);
730                         exit(1);
731                 }
732         }
733         else if((argc - optind) == 1) {
734                 fileindex = optind;
735         }
736         else {
737                 fprintf(stderr, "Invalid arguments.\n");
738                 fprintf(stderr, Usage, argv[0]);
739                 exit(1);
740         }
741
742         if(opt_valid(options)) {
743                 regexp = create_regexp(regexpstr, options);
744                 if(options & JG_PRINT_LINE_NUMBER) nl_exp = create_regexp("\n", 0);
745                 init_inflation();
746                 for(; fileindex < argc; fileindex++)
747                         jargrep(regexp, nl_exp, argv[fileindex], options);
748                 regfree(regexp);
749                 if(options & JG_PRINT_LINE_NUMBER) regfree(nl_exp);
750         }
751         else {
752                 retval = 1;
753                 fprintf(stderr, "Error: Invalid combination of options.\n");
754         }
755
756         return retval;
757 }
758
759 void help(const char *filename)
760 {
761   printf (Usage, filename);
762   printf ("\
763 \n\
764 Search files in a jar file for a pattern.\n\
765 \n\
766    -b                print byte offset of match\n\
767    -c                print number of matches\n\
768    -i                compare case-insensitively\n\
769    -n                print line number of each match\n\
770    -s                suppress error messages\n\
771    -w                force PATTERN to match only whole words\n\
772    -e PATTERN        use PATTERN as regular expression\n\
773    -V|--version      print version number and exit\n\
774    --help            print help\n\
775 ");
776
777   exit (0);
778 }
779
780 void version ()
781 {
782   printf("grepjar (%s) %s\n\n", PACKAGE, VERSION);
783   printf("Copyright 1999, 2000, 2001  Bryan Burns\n");
784   printf("Copyright 2000 Cory Hollingsworth\n");
785   printf("Copyright 2002 Free Software Foundation\n");
786   printf("\
787 This is free software; see the source for copying conditions.  There is NO\n\
788 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
789   exit (0);
790 }