1 /*************************************************
3 *************************************************/
5 /* This is a grep program that uses the PCRE regular expression library to do
6 its pattern matching. On a Unix system it can recurse into directories. */
21 #define VERSION "2.0 01-Aug-2001"
22 #define MAX_PATTERN_COUNT 100
25 /*************************************************
27 *************************************************/
29 static char *pattern_filename = NULL;
30 static int pattern_count = 0;
31 static pcre **pattern_list;
32 static pcre_extra **hints_list;
34 static BOOL count_only = FALSE;
35 static BOOL filenames = TRUE;
36 static BOOL filenames_only = FALSE;
37 static BOOL invert = FALSE;
38 static BOOL number = FALSE;
39 static BOOL recurse = FALSE;
40 static BOOL silent = FALSE;
41 static BOOL whole_lines = FALSE;
43 /* Structure for options and list of them */
45 typedef struct option_item {
51 static 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" },
68 /*************************************************
69 * Functions for directory scanning *
70 *************************************************/
72 /* These functions are defined so that they can be made system specific,
73 although at present the only ones are for Unix, and for "no directory recursion
77 /************* Directory scanning in Unix ***********/
80 #include <sys/types.h>
84 typedef DIR directory_type;
87 isdirectory(char *filename)
90 if (stat(filename, &statbuf) < 0)
91 return 0; /* In the expectation that opening as a file will fail */
92 return ((statbuf.st_mode & S_IFMT) == S_IFDIR)? '/' : 0;
96 opendirectory(char *filename)
98 return opendir(filename);
102 readdirectory(directory_type *dir)
106 struct dirent *dent = readdir(dir);
107 if (dent == NULL) return NULL;
108 if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0)
111 return NULL; /* Keep compiler happy; never executed */
115 closedirectory(directory_type *dir)
124 /************* Directory scanning when we can't do it ***********/
126 /* The type is void, and apart from isdirectory(), the functions do nothing. */
128 typedef void directory_type;
130 int isdirectory(char *filename) { return FALSE; }
131 directory_type * opendirectory(char *filename) {}
132 char *readdirectory(directory_type *dir) {}
133 void closedirectory(directory_type *dir) {}
140 /*************************************************
141 * Provide strerror() for non-ANSI libraries *
142 *************************************************/
144 /* Some old-fashioned systems still around (e.g. SunOS4) don't have strerror()
145 in their libraries, but can provide the same facility by this simple
146 alternative function. */
149 extern char *sys_errlist[];
154 if (n < 0 || n >= sys_nerr) return "unknown error number";
155 return sys_errlist[n];
157 #endif /* HAVE_STRERROR */
161 /*************************************************
162 * Grep an individual file *
163 *************************************************/
166 pcregrep(FILE *in, char *name)
174 while (fgets(buffer, sizeof(buffer), in) != NULL)
178 int length = (int)strlen(buffer);
179 if (length > 0 && buffer[length-1] == '\n') buffer[--length] = 0;
182 for (i = 0; !match && i < pattern_count; i++)
184 match = pcre_exec(pattern_list[i], hints_list[i], buffer, length, 0, 0,
186 if (match && whole_lines && offsets[1] != length) match = FALSE;
191 if (count_only) count++;
193 else if (filenames_only)
195 fprintf(stdout, "%s\n", (name == NULL)? "<stdin>" : name);
199 else if (silent) return 0;
203 if (name != NULL) fprintf(stdout, "%s:", name);
204 if (number) fprintf(stdout, "%d:", linenumber);
205 fprintf(stdout, "%s\n", buffer);
214 if (name != NULL) fprintf(stdout, "%s:", name);
215 fprintf(stdout, "%d\n", count);
224 /*************************************************
225 * Grep a file or recurse into a directory *
226 *************************************************/
229 grep_or_recurse(char *filename, BOOL recurse, BOOL show_filenames,
230 BOOL only_one_at_top)
236 /* If the file is a directory and we are recursing, scan each file within it.
237 The scanning code is localized so it can be made system-specific. */
239 if ((sep = isdirectory(filename)) != 0 && recurse)
243 directory_type *dir = opendirectory(filename);
247 fprintf(stderr, "pcregrep: Failed to open directory %s: %s\n", filename,
252 while ((nextfile = readdirectory(dir)) != NULL)
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;
264 /* If the file is not a directory, or we are not recursing, scan it. If this is
265 the first and only argument at top level, we don't show the file name.
266 Otherwise, control is via the show_filenames variable. */
268 in = fopen(filename, "r");
271 fprintf(stderr, "pcregrep: Failed to open %s: %s\n", filename, strerror(errno));
275 rc = pcregrep(in, (show_filenames && !only_one_at_top)? filename : NULL);
283 /*************************************************
285 *************************************************/
290 fprintf(stderr, "Usage: pcregrep [-Vcfhilnrsvx] [long-options] pattern [file] ...\n");
291 fprintf(stderr, "Type `pcregrep --help' for more information.\n");
298 /*************************************************
300 *************************************************/
307 printf("Usage: pcregrep [OPTION]... PATTERN [FILE] ...\n");
308 printf("Search for PATTERN in each FILE or standard input.\n");
309 printf("Example: pcregrep -i 'hello.*world' menu.h main.c\n\n");
311 printf("Options:\n");
313 for (op = optionlist; op->one_char != 0; op++)
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);
321 printf("%.*s%s\n", n, " ", op->help_text);
324 printf("\n -f<filename> or --file=<filename>\n");
325 printf(" Read patterns from <filename> instead of using a command line option.\n");
326 printf(" Trailing white space is removed; blanks lines are ignored.\n");
327 printf(" There is a maximum of %d patterns.\n", MAX_PATTERN_COUNT);
329 printf("\nWith no FILE, read standard input. If fewer than two FILEs given, assume -h.\n");
330 printf("Exit status is 0 if any matches, 1 if no matches, and 2 if trouble.\n");
336 /*************************************************
338 *************************************************/
341 handle_option(int letter, int options)
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;
357 fprintf(stderr, "pcregrep version %s using ", VERSION);
358 fprintf(stderr, "PCRE version %s\n", pcre_version());
363 fprintf(stderr, "pcregrep: Unknown option -%c\n", letter);
373 /*************************************************
375 *************************************************/
378 main(int argc, char **argv)
385 BOOL only_one_at_top;
387 /* Process the options */
389 for (i = 1; i < argc; i++)
391 if (argv[i][0] != '-') break;
393 /* Long name options */
395 if (argv[i][1] == '-')
399 if (strncmp(argv[i]+2, "file=", 5) == 0)
401 pattern_filename = argv[i] + 7;
405 for (op = optionlist; op->one_char != 0; op++)
407 if (strcmp(argv[i]+2, op->long_name) == 0)
409 options = handle_option(op->one_char, options);
413 if (op->one_char == 0)
415 fprintf(stderr, "pcregrep: Unknown option %s\n", argv[i]);
420 /* One-char options */
424 char *s = argv[i] + 1;
429 pattern_filename = s + 1;
430 if (pattern_filename[0] == 0)
434 fprintf(stderr, "pcregrep: File name missing after -f\n");
437 pattern_filename = argv[++i];
441 else options = handle_option(*s++, options);
446 pattern_list = malloc(MAX_PATTERN_COUNT * sizeof(pcre *));
447 hints_list = malloc(MAX_PATTERN_COUNT * sizeof(pcre_extra *));
449 if (pattern_list == NULL || hints_list == NULL)
451 fprintf(stderr, "pcregrep: malloc failed\n");
455 /* Compile the regular expression(s). */
457 if (pattern_filename != NULL)
459 FILE *f = fopen(pattern_filename, "r");
463 fprintf(stderr, "pcregrep: Failed to open %s: %s\n", pattern_filename,
467 while (fgets(buffer, sizeof(buffer), f) != NULL)
469 char *s = buffer + (int)strlen(buffer);
470 if (pattern_count >= MAX_PATTERN_COUNT)
472 fprintf(stderr, "pcregrep: Too many patterns in file (max %d)\n",
476 while (s > buffer && isspace((unsigned char)(s[-1]))) s--;
477 if (s == buffer) continue;
479 pattern_list[pattern_count] = pcre_compile(buffer, options, &error,
481 if (pattern_list[pattern_count++] == NULL)
483 fprintf(stderr, "pcregrep: Error in regex number %d at offset %d: %s\n",
484 pattern_count, errptr, error);
491 /* If no file name, a single regex must be given inline */
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)
499 fprintf(stderr, "pcregrep: Error in regex at offset %d: %s\n", errptr,
506 /* Study the regular expressions, as we will be running them may times */
508 for (j = 0; j < pattern_count; j++)
510 hints_list[j] = pcre_study(pattern_list[j], 0, &error);
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);
520 /* If there are no further arguments, do the business on stdin and exit */
522 if (i >= argc) return pcregrep(stdin, NULL);
524 /* Otherwise, work through the remaining arguments as files or directories.
525 Pass in the fact that there is only one argument at top level - this suppresses
526 the file name if the argument is not a directory. */
528 only_one_at_top = (i == argc - 1);
529 if (filenames_only) filenames = TRUE;
531 for (; i < argc; i++)
533 int frc = grep_or_recurse(argv[i], recurse, filenames, only_one_at_top);
534 if (frc == 0 && rc == 1) rc = 0;