source: trunk/third/pcre/pcregrep.c @ 19309

Revision 19309, 12.9 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19308, which included commits to RCS files with non-trunk default branches.
Line 
1/*************************************************
2*               pcregrep program                 *
3*************************************************/
4
5/* This is a grep program that uses the PCRE regular expression library to do
6its pattern matching. On a Unix system it can recurse into directories. */
7
8#include <ctype.h>
9#include <stdio.h>
10#include <string.h>
11#include <stdlib.h>
12#include <errno.h>
13#include "config.h"
14#include "pcre.h"
15
16#define FALSE 0
17#define TRUE 1
18
19typedef int BOOL;
20
21#define VERSION "2.0 01-Aug-2001"
22#define MAX_PATTERN_COUNT 100
23
24
25/*************************************************
26*               Global variables                 *
27*************************************************/
28
29static char *pattern_filename = NULL;
30static int  pattern_count = 0;
31static pcre **pattern_list;
32static pcre_extra **hints_list;
33
34static BOOL count_only = FALSE;
35static BOOL filenames = TRUE;
36static BOOL filenames_only = FALSE;
37static BOOL invert = FALSE;
38static BOOL number = FALSE;
39static BOOL recurse = FALSE;
40static BOOL silent = FALSE;
41static BOOL whole_lines = FALSE;
42
43/* Structure for options and list of them */
44
45typedef struct option_item {
46  int one_char;
47  char *long_name;
48  char *help_text;
49} option_item;
50
51static option_item optionlist[] = {
52  { -1,  "help",         "display this help and exit" },
53  { 'c', "count",        "print only a count of matching lines per FILE" },
54  { 'h', "no-filename",  "suppress the prefixing filename on output" },
55  { 'i', "ignore-case",  "ignore case distinctions" },
56  { 'l', "files-with-matches", "print only FILE names containing matches" },
57  { 'n', "line-number",  "print line number with output lines" },
58  { 'r', "recursive",    "recursively scan sub-directories" },
59  { 's', "no-messages",  "suppress error messages" },
60  { 'V', "version",      "print version information and exit" },
61  { 'v', "invert-match", "select non-matching lines" },
62  { 'x', "line-regex",   "force PATTERN to match only whole lines" },
63  { 'x', "line-regexp",  "force PATTERN to match only whole lines" },
64  { 0,    NULL,           NULL }
65};
66
67
68/*************************************************
69*       Functions for directory scanning         *
70*************************************************/
71
72/* These functions are defined so that they can be made system specific,
73although at present the only ones are for Unix, and for "no directory recursion
74support". */
75
76
77/************* Directory scanning in Unix ***********/
78
79#if IS_UNIX
80#include <sys/types.h>
81#include <sys/stat.h>
82#include <dirent.h>
83
84typedef DIR directory_type;
85
86int
87isdirectory(char *filename)
88{
89struct stat statbuf;
90if (stat(filename, &statbuf) < 0)
91  return 0;        /* In the expectation that opening as a file will fail */
92return ((statbuf.st_mode & S_IFMT) == S_IFDIR)? '/' : 0;
93}
94
95directory_type *
96opendirectory(char *filename)
97{
98return opendir(filename);
99}
100
101char *
102readdirectory(directory_type *dir)
103{
104for (;;)
105  {
106  struct dirent *dent = readdir(dir);
107  if (dent == NULL) return NULL;
108  if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0)
109    return dent->d_name;
110  }
111return NULL;   /* Keep compiler happy; never executed */
112}
113
114void
115closedirectory(directory_type *dir)
116{
117closedir(dir);
118}
119
120
121#else
122
123
124/************* Directory scanning when we can't do it ***********/
125
126/* The type is void, and apart from isdirectory(), the functions do nothing. */
127
128typedef void directory_type;
129
130int isdirectory(char *filename) { return FALSE; }
131directory_type * opendirectory(char *filename) {}
132char *readdirectory(directory_type *dir) {}
133void closedirectory(directory_type *dir) {}
134
135#endif
136
137
138
139#if ! HAVE_STRERROR
140/*************************************************
141*     Provide strerror() for non-ANSI libraries  *
142*************************************************/
143
144/* Some old-fashioned systems still around (e.g. SunOS4) don't have strerror()
145in their libraries, but can provide the same facility by this simple
146alternative function. */
147
148extern int   sys_nerr;
149extern char *sys_errlist[];
150
151char *
152strerror(int n)
153{
154if (n < 0 || n >= sys_nerr) return "unknown error number";
155return sys_errlist[n];
156}
157#endif /* HAVE_STRERROR */
158
159
160
161/*************************************************
162*              Grep an individual file           *
163*************************************************/
164
165static int
166pcregrep(FILE *in, char *name)
167{
168int rc = 1;
169int linenumber = 0;
170int count = 0;
171int offsets[99];
172char buffer[BUFSIZ];
173
174while (fgets(buffer, sizeof(buffer), in) != NULL)
175  {
176  BOOL match = FALSE;
177  int i;
178  int length = (int)strlen(buffer);
179  if (length > 0 && buffer[length-1] == '\n') buffer[--length] = 0;
180  linenumber++;
181
182  for (i = 0; !match && i < pattern_count; i++)
183    {
184    match = pcre_exec(pattern_list[i], hints_list[i], buffer, length, 0, 0,
185      offsets, 99) >= 0;
186    if (match && whole_lines && offsets[1] != length) match = FALSE;
187    }
188
189  if (match != invert)
190    {
191    if (count_only) count++;
192
193    else if (filenames_only)
194      {
195      fprintf(stdout, "%s\n", (name == NULL)? "<stdin>" : name);
196      return 0;
197      }
198
199    else if (silent) return 0;
200
201    else
202      {
203      if (name != NULL) fprintf(stdout, "%s:", name);
204      if (number) fprintf(stdout, "%d:", linenumber);
205      fprintf(stdout, "%s\n", buffer);
206      }
207
208    rc = 0;
209    }
210  }
211
212if (count_only)
213  {
214  if (name != NULL) fprintf(stdout, "%s:", name);
215  fprintf(stdout, "%d\n", count);
216  }
217
218return rc;
219}
220
221
222
223
224/*************************************************
225*     Grep a file or recurse into a directory    *
226*************************************************/
227
228static int
229grep_or_recurse(char *filename, BOOL recurse, BOOL show_filenames,
230  BOOL only_one_at_top)
231{
232int rc = 1;
233int sep;
234FILE *in;
235
236/* If the file is a directory and we are recursing, scan each file within it.
237The scanning code is localized so it can be made system-specific. */
238
239if ((sep = isdirectory(filename)) != 0 && recurse)
240  {
241  char buffer[1024];
242  char *nextfile;
243  directory_type *dir = opendirectory(filename);
244
245  if (dir == NULL)
246    {
247    fprintf(stderr, "pcregrep: Failed to open directory %s: %s\n", filename,
248      strerror(errno));
249    return 2;
250    }
251
252  while ((nextfile = readdirectory(dir)) != NULL)
253    {
254    int frc;
255    sprintf(buffer, "%.512s%c%.128s", filename, sep, nextfile);
256    frc = grep_or_recurse(buffer, recurse, TRUE, FALSE);
257    if (frc == 0 && rc == 1) rc = 0;
258    }
259
260  closedirectory(dir);
261  return rc;
262  }
263
264/* If the file is not a directory, or we are not recursing, scan it. If this is
265the first and only argument at top level, we don't show the file name.
266Otherwise, control is via the show_filenames variable. */
267
268in = fopen(filename, "r");
269if (in == NULL)
270  {
271  fprintf(stderr, "pcregrep: Failed to open %s: %s\n", filename, strerror(errno));
272  return 2;
273  }
274
275rc = pcregrep(in, (show_filenames && !only_one_at_top)? filename : NULL);
276fclose(in);
277return rc;
278}
279
280
281
282
283/*************************************************
284*                Usage function                  *
285*************************************************/
286
287static int
288usage(int rc)
289{
290fprintf(stderr, "Usage: pcregrep [-Vcfhilnrsvx] [long-options] pattern [file] ...\n");
291fprintf(stderr, "Type `pcregrep --help' for more information.\n");
292return rc;
293}
294
295
296
297
298/*************************************************
299*                Help function                   *
300*************************************************/
301
302static void
303help(void)
304{
305option_item *op;
306
307printf("Usage: pcregrep [OPTION]... PATTERN [FILE] ...\n");
308printf("Search for PATTERN in each FILE or standard input.\n");
309printf("Example: pcregrep -i 'hello.*world' menu.h main.c\n\n");
310
311printf("Options:\n");
312
313for (op = optionlist; op->one_char != 0; op++)
314  {
315  int n;
316  char s[4];
317  if (op->one_char > 0) sprintf(s, "-%c,", op->one_char); else strcpy(s, "   ");
318  printf("  %s --%s%n", s, op->long_name, &n);
319  n = 30 - n;
320  if (n < 1) n = 1;
321  printf("%.*s%s\n", n, "                    ", op->help_text);
322  }
323
324printf("\n  -f<filename>  or  --file=<filename>\n");
325printf("    Read patterns from <filename> instead of using a command line option.\n");
326printf("    Trailing white space is removed; blanks lines are ignored.\n");
327printf("    There is a maximum of %d patterns.\n", MAX_PATTERN_COUNT);
328
329printf("\nWith no FILE, read standard input. If fewer than two FILEs given, assume -h.\n");
330printf("Exit status is 0 if any matches, 1 if no matches, and 2 if trouble.\n");
331}
332
333
334
335
336/*************************************************
337*                Handle an option                *
338*************************************************/
339
340static int
341handle_option(int letter, int options)
342{
343switch(letter)
344  {
345  case -1:  help(); exit(0);
346  case 'c': count_only = TRUE; break;
347  case 'h': filenames = FALSE; break;
348  case 'i': options |= PCRE_CASELESS; break;
349  case 'l': filenames_only = TRUE;
350  case 'n': number = TRUE; break;
351  case 'r': recurse = TRUE; break;
352  case 's': silent = TRUE; break;
353  case 'v': invert = TRUE; break;
354  case 'x': whole_lines = TRUE; options |= PCRE_ANCHORED; break;
355
356  case 'V':
357  fprintf(stderr, "pcregrep version %s using ", VERSION);
358  fprintf(stderr, "PCRE version %s\n", pcre_version());
359  exit(0);
360  break;
361
362  default:
363  fprintf(stderr, "pcregrep: Unknown option -%c\n", letter);
364  exit(usage(2));
365  }
366
367return options;
368}
369
370
371
372
373/*************************************************
374*                Main program                    *
375*************************************************/
376
377int
378main(int argc, char **argv)
379{
380int i, j;
381int rc = 1;
382int options = 0;
383int errptr;
384const char *error;
385BOOL only_one_at_top;
386
387/* Process the options */
388
389for (i = 1; i < argc; i++)
390  {
391  if (argv[i][0] != '-') break;
392
393  /* Long name options */
394
395  if (argv[i][1] == '-')
396    {
397    option_item *op;
398
399    if (strncmp(argv[i]+2, "file=", 5) == 0)
400      {
401      pattern_filename = argv[i] + 7;
402      continue;
403      }
404
405    for (op = optionlist; op->one_char != 0; op++)
406      {
407      if (strcmp(argv[i]+2, op->long_name) == 0)
408        {
409        options = handle_option(op->one_char, options);
410        break;
411        }
412      }
413    if (op->one_char == 0)
414      {
415      fprintf(stderr, "pcregrep: Unknown option %s\n", argv[i]);
416      exit(usage(2));
417      }
418    }
419
420  /* One-char options */
421
422  else
423    {
424    char *s = argv[i] + 1;
425    while (*s != 0)
426      {
427      if (*s == 'f')
428        {
429        pattern_filename = s + 1;
430        if (pattern_filename[0] == 0)
431          {
432          if (i >= argc - 1)
433            {
434            fprintf(stderr, "pcregrep: File name missing after -f\n");
435            exit(usage(2));
436            }
437          pattern_filename = argv[++i];
438          }
439        break;
440        }
441      else options = handle_option(*s++, options);
442      }
443    }
444  }
445
446pattern_list = malloc(MAX_PATTERN_COUNT * sizeof(pcre *));
447hints_list = malloc(MAX_PATTERN_COUNT * sizeof(pcre_extra *));
448
449if (pattern_list == NULL || hints_list == NULL)
450  {
451  fprintf(stderr, "pcregrep: malloc failed\n");
452  return 2;
453  }
454
455/* Compile the regular expression(s). */
456
457if (pattern_filename != NULL)
458  {
459  FILE *f = fopen(pattern_filename, "r");
460  char buffer[BUFSIZ];
461  if (f == NULL)
462    {
463    fprintf(stderr, "pcregrep: Failed to open %s: %s\n", pattern_filename,
464      strerror(errno));
465    return 2;
466    }
467  while (fgets(buffer, sizeof(buffer), f) != NULL)
468    {
469    char *s = buffer + (int)strlen(buffer);
470    if (pattern_count >= MAX_PATTERN_COUNT)
471      {
472      fprintf(stderr, "pcregrep: Too many patterns in file (max %d)\n",
473        MAX_PATTERN_COUNT);
474      return 2;
475      }
476    while (s > buffer && isspace((unsigned char)(s[-1]))) s--;
477    if (s == buffer) continue;
478    *s = 0;
479    pattern_list[pattern_count] = pcre_compile(buffer, options, &error,
480      &errptr, NULL);
481    if (pattern_list[pattern_count++] == NULL)
482      {
483      fprintf(stderr, "pcregrep: Error in regex number %d at offset %d: %s\n",
484        pattern_count, errptr, error);
485      return 2;
486      }
487    }
488  fclose(f);
489  }
490
491/* If no file name, a single regex must be given inline */
492
493else
494  {
495  if (i >= argc) return usage(0);
496  pattern_list[0] = pcre_compile(argv[i++], options, &error, &errptr, NULL);
497  if (pattern_list[0] == NULL)
498    {
499    fprintf(stderr, "pcregrep: Error in regex at offset %d: %s\n", errptr,
500      error);
501    return 2;
502    }
503  pattern_count++;
504  }
505
506/* Study the regular expressions, as we will be running them may times */
507
508for (j = 0; j < pattern_count; j++)
509  {
510  hints_list[j] = pcre_study(pattern_list[j], 0, &error);
511  if (error != NULL)
512    {
513    char s[16];
514    if (pattern_count == 1) s[0] = 0; else sprintf(s, " number %d", j);
515    fprintf(stderr, "pcregrep: Error while studying regex%s: %s\n", s, error);
516    return 2;
517    }
518  }
519
520/* If there are no further arguments, do the business on stdin and exit */
521
522if (i >= argc) return pcregrep(stdin, NULL);
523
524/* Otherwise, work through the remaining arguments as files or directories.
525Pass in the fact that there is only one argument at top level - this suppresses
526the file name if the argument is not a directory. */
527
528only_one_at_top = (i == argc - 1);
529if (filenames_only) filenames = TRUE;
530
531for (; i < argc; i++)
532  {
533  int frc = grep_or_recurse(argv[i], recurse, filenames, only_one_at_top);
534  if (frc == 0 && rc == 1) rc = 0;
535  }
536
537return rc;
538}
539
540/* End */
Note: See TracBrowser for help on using the repository browser.