--- embedaddon/pcre/pcregrep.c 2012/02/21 23:05:51 1.1.1.1 +++ embedaddon/pcre/pcregrep.c 2014/06/15 19:46:03 1.1.1.5 @@ -3,11 +3,17 @@ *************************************************/ /* This is a grep program that uses the PCRE regular expression library to do -its pattern matching. On a Unix or Win32 system it can recurse into -directories. +its pattern matching. On Unix-like, Windows, and native z/OS systems it can +recurse into directories, and in z/OS it can handle PDS files. - Copyright (c) 1997-2011 University of Cambridge +Note that for native z/OS, in addition to defining the NATIVE_ZOS macro, an +additional header is required. That header is not included in the main PCRE +distribution because other apparatus is needed to compile pcregrep for z/OS. +The header can be found in the special z/OS distribution, which is available +from www.zaconsultants.net or from www.cbttape.org. + Copyright (c) 1997-2013 University of Cambridge + ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -70,15 +76,16 @@ POSSIBILITY OF SUCH DAMAGE. typedef int BOOL; -#define MAX_PATTERN_COUNT 100 #define OFFSET_SIZE 99 #if BUFSIZ > 8192 -#define PATBUFSIZE BUFSIZ +#define MAXPATLEN BUFSIZ #else -#define PATBUFSIZE 8192 +#define MAXPATLEN 8192 #endif +#define PATBUFSIZE (MAXPATLEN + 10) /* Allows for prefix+suffix */ + /* Values for the "filenames" variable, which specifies options for file name output. The order is important; it is assumed that a file name is wanted for all values greater than FN_DEFAULT. */ @@ -104,6 +111,10 @@ enum { DEE_READ, DEE_SKIP }; enum { EL_LF, EL_CR, EL_CRLF, EL_ANY, EL_ANYCRLF }; +/* Binary file options */ + +enum { BIN_BINARY, BIN_NOMATCH, BIN_TEXT }; + /* In newer versions of gcc, with FORTIFY_SOURCE set (the default in some environments), a warning is issued if the value of fwrite() is ignored. Unfortunately, casting to (void) does not suppress the warning. To get round @@ -135,38 +146,31 @@ static char *colour_string = (char *)"1;31"; static char *colour_option = NULL; static char *dee_option = NULL; static char *DEE_option = NULL; +static char *locale = NULL; static char *main_buffer = NULL; static char *newline = NULL; -static char *pattern_filename = NULL; +static char *om_separator = (char *)""; static char *stdin_name = (char *)"(standard input)"; -static char *locale = NULL; static const unsigned char *pcretables = NULL; -static int pattern_count = 0; -static pcre **pattern_list = NULL; -static pcre_extra **hints_list = NULL; - -static char *include_pattern = NULL; -static char *exclude_pattern = NULL; -static char *include_dir_pattern = NULL; -static char *exclude_dir_pattern = NULL; - -static pcre *include_compiled = NULL; -static pcre *exclude_compiled = NULL; -static pcre *include_dir_compiled = NULL; -static pcre *exclude_dir_compiled = NULL; - static int after_context = 0; static int before_context = 0; +static int binary_files = BIN_BINARY; static int both_context = 0; static int bufthird = PCREGREP_BUFSIZE; static int bufsize = 3*PCREGREP_BUFSIZE; + +#if defined HAVE_WINDOWS_H && HAVE_WINDOWS_H +static int dee_action = dee_SKIP; +#else static int dee_action = dee_READ; +#endif + static int DEE_action = DEE_READ; static int error_count = 0; static int filenames = FN_DEFAULT; -static int only_matching = -1; +static int pcre_options = 0; static int process_options = 0; #ifdef SUPPORT_PCREGREP_JIT @@ -190,13 +194,103 @@ static BOOL number = FALSE; static BOOL omit_zero_count = FALSE; static BOOL resource_error = FALSE; static BOOL quiet = FALSE; +static BOOL show_only_matching = FALSE; static BOOL silent = FALSE; static BOOL utf8 = FALSE; +/* Structure for list of --only-matching capturing numbers. */ + +typedef struct omstr { + struct omstr *next; + int groupnum; +} omstr; + +static omstr *only_matching = NULL; +static omstr *only_matching_last = NULL; + +/* Structure for holding the two variables that describe a number chain. */ + +typedef struct omdatastr { + omstr **anchor; + omstr **lastptr; +} omdatastr; + +static omdatastr only_matching_data = { &only_matching, &only_matching_last }; + +/* Structure for list of file names (for -f and --{in,ex}clude-from) */ + +typedef struct fnstr { + struct fnstr *next; + char *name; +} fnstr; + +static fnstr *exclude_from = NULL; +static fnstr *exclude_from_last = NULL; +static fnstr *include_from = NULL; +static fnstr *include_from_last = NULL; + +static fnstr *file_lists = NULL; +static fnstr *file_lists_last = NULL; +static fnstr *pattern_files = NULL; +static fnstr *pattern_files_last = NULL; + +/* Structure for holding the two variables that describe a file name chain. */ + +typedef struct fndatastr { + fnstr **anchor; + fnstr **lastptr; +} fndatastr; + +static fndatastr exclude_from_data = { &exclude_from, &exclude_from_last }; +static fndatastr include_from_data = { &include_from, &include_from_last }; +static fndatastr file_lists_data = { &file_lists, &file_lists_last }; +static fndatastr pattern_files_data = { &pattern_files, &pattern_files_last }; + +/* Structure for pattern and its compiled form; used for matching patterns and +also for include/exclude patterns. */ + +typedef struct patstr { + struct patstr *next; + char *string; + pcre *compiled; + pcre_extra *hint; +} patstr; + +static patstr *patterns = NULL; +static patstr *patterns_last = NULL; +static patstr *include_patterns = NULL; +static patstr *include_patterns_last = NULL; +static patstr *exclude_patterns = NULL; +static patstr *exclude_patterns_last = NULL; +static patstr *include_dir_patterns = NULL; +static patstr *include_dir_patterns_last = NULL; +static patstr *exclude_dir_patterns = NULL; +static patstr *exclude_dir_patterns_last = NULL; + +/* Structure holding the two variables that describe a pattern chain. A pointer +to such structures is used for each appropriate option. */ + +typedef struct patdatastr { + patstr **anchor; + patstr **lastptr; +} patdatastr; + +static patdatastr match_patdata = { &patterns, &patterns_last }; +static patdatastr include_patdata = { &include_patterns, &include_patterns_last }; +static patdatastr exclude_patdata = { &exclude_patterns, &exclude_patterns_last }; +static patdatastr include_dir_patdata = { &include_dir_patterns, &include_dir_patterns_last }; +static patdatastr exclude_dir_patdata = { &exclude_dir_patterns, &exclude_dir_patterns_last }; + +static patstr **incexlist[4] = { &include_patterns, &exclude_patterns, + &include_dir_patterns, &exclude_dir_patterns }; + +static const char *incexname[4] = { "--include", "--exclude", + "--include-dir", "--exclude-dir" }; + /* Structure for options and list of them */ enum { OP_NODATA, OP_STRING, OP_OP_STRING, OP_NUMBER, OP_LONGNUMBER, - OP_OP_NUMBER, OP_PATLIST }; + OP_OP_NUMBER, OP_OP_NUMBERS, OP_PATLIST, OP_FILELIST, OP_BINFILES }; typedef struct option_item { int type; @@ -225,12 +319,19 @@ used to identify them. */ #define N_M_LIMIT_REC (-14) #define N_BUFSIZE (-15) #define N_NOJIT (-16) +#define N_FILE_LIST (-17) +#define N_BINARY_FILES (-18) +#define N_EXCLUDE_FROM (-19) +#define N_INCLUDE_FROM (-20) +#define N_OM_SEPARATOR (-21) static option_item optionlist[] = { - { OP_NODATA, N_NULL, NULL, "", " terminate options" }, + { OP_NODATA, N_NULL, NULL, "", "terminate options" }, { OP_NODATA, N_HELP, NULL, "help", "display this help and exit" }, { OP_NUMBER, 'A', &after_context, "after-context=number", "set number of following context lines" }, + { OP_NODATA, 'a', NULL, "text", "treat binary files as text" }, { OP_NUMBER, 'B', &before_context, "before-context=number", "set number of prior context lines" }, + { OP_BINFILES, N_BINARY_FILES, NULL, "binary-files=word", "set treatment of binary files" }, { OP_NUMBER, N_BUFSIZE,&bufthird, "buffer-size=number", "set processing buffer size parameter" }, { OP_OP_STRING, N_COLOUR, &colour_option, "color=option", "matched text color option" }, { OP_OP_STRING, N_COLOUR, &colour_option, "colour=option", "matched text colour option" }, @@ -238,12 +339,14 @@ static option_item optionlist[] = { { OP_NODATA, 'c', NULL, "count", "print only a count of matching lines per FILE" }, { OP_STRING, 'D', &DEE_option, "devices=action","how to handle devices, FIFOs, and sockets" }, { OP_STRING, 'd', &dee_option, "directories=action", "how to handle directories" }, - { OP_PATLIST, 'e', NULL, "regex(p)=pattern", "specify pattern (may be used more than once)" }, + { OP_PATLIST, 'e', &match_patdata, "regex(p)=pattern", "specify pattern (may be used more than once)" }, { OP_NODATA, 'F', NULL, "fixed-strings", "patterns are sets of newline-separated strings" }, - { OP_STRING, 'f', &pattern_filename, "file=path", "read patterns from file" }, + { OP_FILELIST, 'f', &pattern_files_data, "file=path", "read patterns from file" }, + { OP_FILELIST, N_FILE_LIST, &file_lists_data, "file-list=path","read files to search from file" }, { OP_NODATA, N_FOFFSETS, NULL, "file-offsets", "output file offsets, not text" }, { OP_NODATA, 'H', NULL, "with-filename", "force the prefixing filename on output" }, { OP_NODATA, 'h', NULL, "no-filename", "suppress the prefixing filename on output" }, + { OP_NODATA, 'I', NULL, "", "treat binary files as not matching (ignore)" }, { OP_NODATA, 'i', NULL, "ignore-case", "ignore case distinctions" }, #ifdef SUPPORT_PCREGREP_JIT { OP_NODATA, N_NOJIT, NULL, "no-jit", "do not use just-in-time compiler optimization" }, @@ -261,21 +364,24 @@ static option_item optionlist[] = { { OP_NODATA, 'M', NULL, "multiline", "run in multiline mode" }, { OP_STRING, 'N', &newline, "newline=type", "set newline type (CR, LF, CRLF, ANYCRLF or ANY)" }, { OP_NODATA, 'n', NULL, "line-number", "print line number with output lines" }, - { OP_OP_NUMBER, 'o', &only_matching, "only-matching=n", "show only the part of the line that matched" }, + { OP_OP_NUMBERS, 'o', &only_matching_data, "only-matching=n", "show only the part of the line that matched" }, + { OP_STRING, N_OM_SEPARATOR, &om_separator, "om-separator=text", "set separator for multiple -o output" }, { OP_NODATA, 'q', NULL, "quiet", "suppress output, just set return code" }, { OP_NODATA, 'r', NULL, "recursive", "recursively scan sub-directories" }, - { OP_STRING, N_EXCLUDE,&exclude_pattern, "exclude=pattern","exclude matching files when recursing" }, - { OP_STRING, N_INCLUDE,&include_pattern, "include=pattern","include matching files when recursing" }, - { OP_STRING, N_EXCLUDE_DIR,&exclude_dir_pattern, "exclude-dir=pattern","exclude matching directories when recursing" }, - { OP_STRING, N_INCLUDE_DIR,&include_dir_pattern, "include-dir=pattern","include matching directories when recursing" }, + { OP_PATLIST, N_EXCLUDE,&exclude_patdata, "exclude=pattern","exclude matching files when recursing" }, + { OP_PATLIST, N_INCLUDE,&include_patdata, "include=pattern","include matching files when recursing" }, + { OP_PATLIST, N_EXCLUDE_DIR,&exclude_dir_patdata, "exclude-dir=pattern","exclude matching directories when recursing" }, + { OP_PATLIST, N_INCLUDE_DIR,&include_dir_patdata, "include-dir=pattern","include matching directories when recursing" }, + { OP_FILELIST, N_EXCLUDE_FROM,&exclude_from_data, "exclude-from=path", "read exclude list from file" }, + { OP_FILELIST, N_INCLUDE_FROM,&include_from_data, "include-from=path", "read include list from file" }, /* These two were accidentally implemented with underscores instead of hyphens in the option names. As this was not discovered for several releases, the incorrect versions are left in the table for compatibility. However, the --help function misses out any option that has an underscore in its name. */ - { OP_STRING, N_EXCLUDE_DIR,&exclude_dir_pattern, "exclude_dir=pattern","exclude matching directories when recursing" }, - { OP_STRING, N_INCLUDE_DIR,&include_dir_pattern, "include_dir=pattern","include matching directories when recursing" }, + { OP_PATLIST, N_EXCLUDE_DIR,&exclude_dir_patdata, "exclude_dir=pattern","exclude matching directories when recursing" }, + { OP_PATLIST, N_INCLUDE_DIR,&include_dir_patdata, "include_dir=pattern","include matching directories when recursing" }, #ifdef JFRIEDL_DEBUG { OP_OP_NUMBER, 'S', &S_arg, "jeffS", "replace matched (sub)string with X" }, @@ -292,7 +398,9 @@ static option_item optionlist[] = { /* Tables for prefixing and suffixing patterns, according to the -w, -x, and -F options. These set the 1, 2, and 4 bits in process_options, respectively. Note that the combination of -w and -x has the same effect as -x on its own, so we -can treat them as the same. */ +can treat them as the same. Note that the MAXPATLEN macro assumes the longest +prefix+suffix is 10 characters; if anything longer is added, it must be +adjusted. */ static const char *prefix[] = { "", "\\b", "^(?:", "^(?:", "\\Q", "\\b\\Q", "^(?:\\Q", "^(?:\\Q" }; @@ -332,27 +440,127 @@ if (resource_error) PCRE_ERROR_JIT_STACKLIMIT); fprintf(stderr, "pcregrep: Check your regex for nested unlimited loops.\n"); } - exit(rc); } /************************************************* +* Add item to chain of patterns * +*************************************************/ + +/* Used to add an item onto a chain, or just return an unconnected item if the +"after" argument is NULL. + +Arguments: + s pattern string to add + after if not NULL points to item to insert after + +Returns: new pattern block +*/ + +static patstr * +add_pattern(char *s, patstr *after) +{ +patstr *p = (patstr *)malloc(sizeof(patstr)); +if (p == NULL) + { + fprintf(stderr, "pcregrep: malloc failed\n"); + pcregrep_exit(2); + } +if (strlen(s) > MAXPATLEN) + { + fprintf(stderr, "pcregrep: pattern is too long (limit is %d bytes)\n", + MAXPATLEN); + return NULL; + } +p->next = NULL; +p->string = s; +p->compiled = NULL; +p->hint = NULL; + +if (after != NULL) + { + p->next = after->next; + after->next = p; + } +return p; +} + + +/************************************************* +* Free chain of patterns * +*************************************************/ + +/* Used for several chains of patterns. + +Argument: pointer to start of chain +Returns: nothing +*/ + +static void +free_pattern_chain(patstr *pc) +{ +while (pc != NULL) + { + patstr *p = pc; + pc = p->next; + if (p->hint != NULL) pcre_free_study(p->hint); + if (p->compiled != NULL) pcre_free(p->compiled); + free(p); + } +} + + +/************************************************* +* Free chain of file names * +*************************************************/ + +/* +Argument: pointer to start of chain +Returns: nothing +*/ + +static void +free_file_chain(fnstr *fn) +{ +while (fn != NULL) + { + fnstr *f = fn; + fn = f->next; + free(f); + } +} + + +/************************************************* * OS-specific functions * *************************************************/ -/* These functions are defined so that they can be made system specific, -although at present the only ones are for Unix, Win32, and for "no support". */ +/* These functions are defined so that they can be made system specific. +At present there are versions for Unix-style environments, Windows, native +z/OS, and "no support". */ -/************* Directory scanning in Unix ***********/ +/************* Directory scanning Unix-style and z/OS ***********/ -#if defined HAVE_SYS_STAT_H && defined HAVE_DIRENT_H && defined HAVE_SYS_TYPES_H +#if (defined HAVE_SYS_STAT_H && defined HAVE_DIRENT_H && defined HAVE_SYS_TYPES_H) || defined NATIVE_ZOS #include #include #include +#if defined NATIVE_ZOS +/************* Directory and PDS/E scanning for z/OS ***********/ +/************* z/OS looks mostly like Unix with USS ************/ +/* However, z/OS needs the #include statements in this header */ +#include "pcrzosfs.h" +/* That header is not included in the main PCRE distribution because + other apparatus is needed to compile pcregrep for z/OS. The header + can be found in the special z/OS distribution, which is available + from www.zaconsultants.net or from www.cbttape.org. */ +#endif + typedef DIR directory_type; +#define FILESEP '/' static int isdirectory(char *filename) @@ -360,7 +568,7 @@ isdirectory(char *filename) struct stat statbuf; if (stat(filename, &statbuf) < 0) return 0; /* In the expectation that opening as a file will fail */ -return ((statbuf.st_mode & S_IFMT) == S_IFDIR)? '/' : 0; +return (statbuf.st_mode & S_IFMT) == S_IFDIR; } static directory_type * @@ -389,7 +597,7 @@ closedir(dir); } -/************* Test for regular file in Unix **********/ +/************* Test for regular file, Unix-style **********/ static int isregfile(char *filename) @@ -401,11 +609,29 @@ return (statbuf.st_mode & S_IFMT) == S_IFREG; } -/************* Test for a terminal in Unix **********/ +#if defined NATIVE_ZOS +/************* Test for a terminal in z/OS **********/ +/* isatty() does not work in a TSO environment, so always give FALSE.*/ static BOOL is_stdout_tty(void) { +return FALSE; +} + +static BOOL +is_file_tty(FILE *f) +{ +return FALSE; +} + + +/************* Test for a terminal, Unix-style **********/ + +#else +static BOOL +is_stdout_tty(void) +{ return isatty(fileno(stdout)); } @@ -414,10 +640,13 @@ is_file_tty(FILE *f) { return isatty(fileno(f)); } +#endif +/* End of Unix-style or native z/OS environment functions. */ -/************* Directory scanning in Win32 ***********/ +/************* Directory scanning in Windows ***********/ + /* I (Philip Hazel) have no means of testing this code. It was contributed by Lionel Fourquaux. David Burgess added a patch to define INVALID_FILE_ATTRIBUTES when it did not exist. David Byron added a patch that moved the #include of @@ -447,13 +676,15 @@ BOOL first; WIN32_FIND_DATA data; } directory_type; +#define FILESEP '/' + int isdirectory(char *filename) { DWORD attr = GetFileAttributes(filename); if (attr == INVALID_FILE_ATTRIBUTES) return 0; -return ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) ? '/' : 0; +return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; } directory_type * @@ -464,8 +695,8 @@ char *pattern; directory_type *dir; DWORD err; len = strlen(filename); -pattern = (char *) malloc(len + 3); -dir = (directory_type *) malloc(sizeof(*dir)); +pattern = (char *)malloc(len + 3); +dir = (directory_type *)malloc(sizeof(*dir)); if ((pattern == NULL) || (dir == NULL)) { fprintf(stderr, "pcregrep: malloc failed\n"); @@ -517,7 +748,7 @@ free(dir); } -/************* Test for regular file in Win32 **********/ +/************* Test for regular file in Windows **********/ /* I don't know how to do this, or if it can be done; assume all paths are regular if they are not directories. */ @@ -528,7 +759,7 @@ return !isdirectory(filename); } -/************* Test for a terminal in Win32 **********/ +/************* Test for a terminal in Windows **********/ /* I don't know how to do this; assume never */ @@ -544,13 +775,16 @@ is_file_tty(FILE *f) return FALSE; } +/* End of Windows functions */ + /************* Directory scanning when we can't do it ***********/ /* The type is void, and apart from isdirectory(), the functions do nothing. */ #else +#define FILESEP 0 typedef void directory_type; int isdirectory(char *filename) { return 0; } @@ -559,7 +793,7 @@ char *readdirectory(directory_type *dir) { return (cha void closedirectory(directory_type *dir) {} -/************* Test for regular when we can't do it **********/ +/************* Test for regular file when we can't do it **********/ /* Assume all files are regular. */ @@ -580,7 +814,7 @@ is_file_tty(FILE *f) return FALSE; } -#endif +#endif /* End of system-specific functions */ @@ -607,6 +841,230 @@ return sys_errlist[n]; /************************************************* +* Usage function * +*************************************************/ + +static int +usage(int rc) +{ +option_item *op; +fprintf(stderr, "Usage: pcregrep [-"); +for (op = optionlist; op->one_char != 0; op++) + { + if (op->one_char > 0) fprintf(stderr, "%c", op->one_char); + } +fprintf(stderr, "] [long options] [pattern] [files]\n"); +fprintf(stderr, "Type `pcregrep --help' for more information and the long " + "options.\n"); +return rc; +} + + + +/************************************************* +* Help function * +*************************************************/ + +static void +help(void) +{ +option_item *op; + +printf("Usage: pcregrep [OPTION]... [PATTERN] [FILE1 FILE2 ...]\n"); +printf("Search for PATTERN in each FILE or standard input.\n"); +printf("PATTERN must be present if neither -e nor -f is used.\n"); +printf("\"-\" can be used as a file name to mean STDIN.\n"); + +#ifdef SUPPORT_LIBZ +printf("Files whose names end in .gz are read using zlib.\n"); +#endif + +#ifdef SUPPORT_LIBBZ2 +printf("Files whose names end in .bz2 are read using bzlib2.\n"); +#endif + +#if defined SUPPORT_LIBZ || defined SUPPORT_LIBBZ2 +printf("Other files and the standard input are read as plain files.\n\n"); +#else +printf("All files are read as plain files, without any interpretation.\n\n"); +#endif + +printf("Example: pcregrep -i 'hello.*world' menu.h main.c\n\n"); +printf("Options:\n"); + +for (op = optionlist; op->one_char != 0; op++) + { + int n; + char s[4]; + + /* Two options were accidentally implemented and documented with underscores + instead of hyphens in their names, something that was not noticed for quite a + few releases. When fixing this, I left the underscored versions in the list + in case people were using them. However, we don't want to display them in the + help data. There are no other options that contain underscores, and we do not + expect ever to implement such options. Therefore, just omit any option that + contains an underscore. */ + + if (strchr(op->long_name, '_') != NULL) continue; + + if (op->one_char > 0 && (op->long_name)[0] == 0) + n = 31 - printf(" -%c", op->one_char); + else + { + if (op->one_char > 0) sprintf(s, "-%c,", op->one_char); + else strcpy(s, " "); + n = 31 - printf(" %s --%s", s, op->long_name); + } + + if (n < 1) n = 1; + printf("%.*s%s\n", n, " ", op->help_text); + } + +printf("\nNumbers may be followed by K or M, e.g. --buffer-size=100K.\n"); +printf("The default value for --buffer-size is %d.\n", PCREGREP_BUFSIZE); +printf("When reading patterns or file names from a file, trailing white\n"); +printf("space is removed and blank lines are ignored.\n"); +printf("The maximum size of any pattern is %d bytes.\n", MAXPATLEN); + +printf("\nWith no FILEs, read standard input. If fewer than two FILEs given, assume -h.\n"); +printf("Exit status is 0 if any matches, 1 if no matches, and 2 if trouble.\n"); +} + + + +/************************************************* +* Test exclude/includes * +*************************************************/ + +/* If any exclude pattern matches, the path is excluded. Otherwise, unless +there are no includes, the path must match an include pattern. + +Arguments: + path the path to be matched + ip the chain of include patterns + ep the chain of exclude patterns + +Returns: TRUE if the path is not excluded +*/ + +static BOOL +test_incexc(char *path, patstr *ip, patstr *ep) +{ +int plen = strlen(path); + +for (; ep != NULL; ep = ep->next) + { + if (pcre_exec(ep->compiled, NULL, path, plen, 0, 0, NULL, 0) >= 0) + return FALSE; + } + +if (ip == NULL) return TRUE; + +for (; ip != NULL; ip = ip->next) + { + if (pcre_exec(ip->compiled, NULL, path, plen, 0, 0, NULL, 0) >= 0) + return TRUE; + } + +return FALSE; +} + + + +/************************************************* +* Decode integer argument value * +*************************************************/ + +/* Integer arguments can be followed by K or M. Avoid the use of strtoul() +because SunOS4 doesn't have it. This is used only for unpicking arguments, so +just keep it simple. + +Arguments: + option_data the option data string + op the option item (for error messages) + longop TRUE if option given in long form + +Returns: a long integer +*/ + +static long int +decode_number(char *option_data, option_item *op, BOOL longop) +{ +unsigned long int n = 0; +char *endptr = option_data; +while (*endptr != 0 && isspace((unsigned char)(*endptr))) endptr++; +while (isdigit((unsigned char)(*endptr))) + n = n * 10 + (int)(*endptr++ - '0'); +if (toupper(*endptr) == 'K') + { + n *= 1024; + endptr++; + } +else if (toupper(*endptr) == 'M') + { + n *= 1024*1024; + endptr++; + } + +if (*endptr != 0) /* Error */ + { + if (longop) + { + char *equals = strchr(op->long_name, '='); + int nlen = (equals == NULL)? (int)strlen(op->long_name) : + (int)(equals - op->long_name); + fprintf(stderr, "pcregrep: Malformed number \"%s\" after --%.*s\n", + option_data, nlen, op->long_name); + } + else + fprintf(stderr, "pcregrep: Malformed number \"%s\" after -%c\n", + option_data, op->one_char); + pcregrep_exit(usage(2)); + } + +return n; +} + + + +/************************************************* +* Add item to a chain of numbers * +*************************************************/ + +/* Used to add an item onto a chain, or just return an unconnected item if the +"after" argument is NULL. + +Arguments: + n the number to add + after if not NULL points to item to insert after + +Returns: new number block +*/ + +static omstr * +add_number(int n, omstr *after) +{ +omstr *om = (omstr *)malloc(sizeof(omstr)); + +if (om == NULL) + { + fprintf(stderr, "pcregrep: malloc failed\n"); + pcregrep_exit(2); + } +om->next = NULL; +om->groupnum = n; + +if (after != NULL) + { + om->next = after->next; + after->next = om; + } +return om; +} + + + +/************************************************* * Read one line of input * *************************************************/ @@ -625,7 +1083,7 @@ Arguments: Returns: the number of characters read, zero at end of file */ -static int +static unsigned int read_one_line(char *buffer, int length, FILE *f) { int c; @@ -722,12 +1180,12 @@ switch(endlinetype) switch (c) { - case 0x0a: /* LF */ + case '\n': *lenptr = 1; return p; - case 0x0d: /* CR */ - if (p < endptr && *p == 0x0a) + case '\r': + if (p < endptr && *p == '\n') { *lenptr = 2; p++; @@ -766,14 +1224,14 @@ switch(endlinetype) switch (c) { - case 0x0a: /* LF */ - case 0x0b: /* VT */ - case 0x0c: /* FF */ + case '\n': /* LF */ + case '\v': /* VT */ + case '\f': /* FF */ *lenptr = 1; return p; - case 0x0d: /* CR */ - if (p < endptr && *p == 0x0a) + case '\r': /* CR */ + if (p < endptr && *p == '\n') { *lenptr = 2; p++; @@ -781,14 +1239,16 @@ switch(endlinetype) else *lenptr = 1; return p; - case 0x85: /* NEL */ +#ifndef EBCDIC + case 0x85: /* Unicode NEL */ *lenptr = utf8? 2 : 1; return p; - case 0x2028: /* LS */ - case 0x2029: /* PS */ + case 0x2028: /* Unicode LS */ + case 0x2029: /* Unicode PS */ *lenptr = 3; return p; +#endif /* Not EBCDIC */ default: break; @@ -847,7 +1307,7 @@ switch(endlinetype) while (p > startptr) { - register int c; + register unsigned int c; char *pp = p - 1; if (utf8) @@ -872,8 +1332,8 @@ switch(endlinetype) if (endlinetype == EL_ANYCRLF) switch (c) { - case 0x0a: /* LF */ - case 0x0d: /* CR */ + case '\n': /* LF */ + case '\r': /* CR */ return p; default: @@ -882,13 +1342,15 @@ switch(endlinetype) else switch (c) { - case 0x0a: /* LF */ - case 0x0b: /* VT */ - case 0x0c: /* FF */ - case 0x0d: /* CR */ - case 0x85: /* NEL */ - case 0x2028: /* LS */ - case 0x2029: /* PS */ + case '\n': /* LF */ + case '\v': /* VT */ + case '\f': /* FF */ + case '\r': /* CR */ +#ifndef EBCDIE + case 0x85: /* Unicode NEL */ + case 0x2028: /* Unicode LS */ + case 0x2029: /* Unicode PS */ +#endif /* Not EBCDIC */ return p; default: @@ -923,8 +1385,9 @@ Arguments: Returns: nothing */ -static void do_after_lines(int lastmatchnumber, char *lastmatchrestart, - char *endptr, char *printname) +static void +do_after_lines(int lastmatchnumber, char *lastmatchrestart, char *endptr, + char *printname) { if (after_context > 0 && lastmatchnumber > 0) { @@ -956,6 +1419,7 @@ to find all possible matches. Arguments: matchptr the start of the subject length the length of the subject to match + options options for pcre_exec startoffset where to start matching offsets the offets vector to fill in mrc address of where to put the result of pcre_exec() @@ -966,25 +1430,27 @@ Returns: TRUE if there was a match */ static BOOL -match_patterns(char *matchptr, size_t length, int startoffset, int *offsets, - int *mrc) +match_patterns(char *matchptr, size_t length, unsigned int options, + int startoffset, int *offsets, int *mrc) { int i; size_t slen = length; +patstr *p = patterns; const char *msg = "this text:\n\n"; + if (slen > 200) { slen = 200; msg = "text that starts:\n\n"; } -for (i = 0; i < pattern_count; i++) +for (i = 1; p != NULL; p = p->next, i++) { - *mrc = pcre_exec(pattern_list[i], hints_list[i], matchptr, (int)length, - startoffset, PCRE_NOTEMPTY, offsets, OFFSET_SIZE); + *mrc = pcre_exec(p->compiled, p->hint, matchptr, (int)length, + startoffset, options, offsets, OFFSET_SIZE); if (*mrc >= 0) return TRUE; if (*mrc == PCRE_ERROR_NOMATCH) continue; fprintf(stderr, "pcregrep: pcre_exec() gave error %d while matching ", *mrc); - if (pattern_count > 1) fprintf(stderr, "pattern number %d to ", i+1); + if (patterns->next != NULL) fprintf(stderr, "pattern number %d to ", i); fprintf(stderr, "%s", msg); FWRITE(matchptr, 1, slen, stderr); /* In case binary zero included */ fprintf(stderr, "\n\n"); @@ -1044,6 +1510,7 @@ char *lastmatchrestart = NULL; char *ptr = main_buffer; char *endptr; size_t bufflength; +BOOL binary = FALSE; BOOL endhyphenpending = FALSE; BOOL input_line_buffered = line_buffered; FILE *in = NULL; /* Ensure initialized */ @@ -1062,6 +1529,8 @@ of what we have. In the case of libz, a non-zipped .gz plain file. However, if a .bz2 file isn't actually bzipped, the first read will fail. */ +(void)frtype; + #ifdef SUPPORT_LIBZ if (frtype == FR_LIBZ) { @@ -1091,6 +1560,17 @@ else endptr = main_buffer + bufflength; +/* Unless binary-files=text, see if we have a binary file. This uses the same +rule as GNU grep, namely, a search for a binary zero byte near the start of the +file. */ + +if (binary_files != BIN_TEXT) + { + binary = + memchr(main_buffer, 0, (bufflength > 1024)? 1024 : bufflength) != NULL; + if (binary && binary_files == BIN_NOMATCH) return 1; + } + /* Loop while the current pointer is not at the end of the file. For large files, endptr will be at the end of the buffer when we are in the middle of the file, but ptr will never get there, because as soon as it gets over 2/3 of the @@ -1101,6 +1581,7 @@ while (ptr < endptr) int endlinelength; int mrc = 0; int startoffset = 0; + unsigned int options = 0; BOOL match; char *matchptr = ptr; char *t = ptr; @@ -1137,8 +1618,8 @@ while (ptr < endptr) #ifdef JFRIEDL_DEBUG if (jfriedl_XT || jfriedl_XR) { - #include - #include +# include +# include struct timeval start_time, end_time; struct timezone dummy; int i; @@ -1167,7 +1648,7 @@ while (ptr < endptr) for (i = 0; i < jfriedl_XR; i++) - match = (pcre_exec(pattern_list[0], hints_list[0], ptr, length, 0, + match = (pcre_exec(patterns->compiled, patterns->hint, ptr, length, 0, PCRE_NOTEMPTY, offsets, OFFSET_SIZE) >= 0); if (gettimeofday(&end_time, &dummy) != 0) @@ -1182,16 +1663,20 @@ while (ptr < endptr) } #endif - /* We come back here after a match when the -o option (only_matching) is set, - in order to find any further matches in the same line. */ + /* We come back here after a match when show_only_matching is set, in order + to find any further matches in the same line. This applies to + --only-matching, --file-offsets, and --line-offsets. */ ONLY_MATCHING_RESTART: /* Run through all the patterns until one matches or there is an error other than NOMATCH. This code is in a subroutine so that it can be re-used for - finding subsequent matches when colouring matched lines. */ + finding subsequent matches when colouring matched lines. After finding one + match, set PCRE_NOTEMPTY to disable any further matches of null strings in + this line. */ - match = match_patterns(matchptr, length, startoffset, offsets, &mrc); + match = match_patterns(matchptr, length, options, startoffset, offsets, &mrc); + options = PCRE_NOTEMPTY; /* If it's a match or a not-match (as required), do what's wanted. */ @@ -1207,6 +1692,16 @@ while (ptr < endptr) if (count_only) count++; + /* When handling a binary file and binary-files==binary, the "binary" + variable will be set true (it's false in all other cases). In this + situation we just want to output the file name. No need to scan further. */ + + else if (binary) + { + fprintf(stdout, "Binary file %s matches\n", filename); + return 0; + } + /* If all we want is a file name, there is no need to scan any more lines in the file. */ @@ -1220,40 +1715,64 @@ while (ptr < endptr) else if (quiet) return 0; - /* The --only-matching option prints just the substring that matched, or a - captured portion of it, as long as this string is not empty, and the - --file-offsets and --line-offsets options output offsets for the matching - substring (they both force --only-matching = 0). None of these options - prints any context. Afterwards, adjust the start and then jump back to look - for further matches in the same line. If we are in invert mode, however, - nothing is printed and we do not restart - this could still be useful - because the return code is set. */ + /* The --only-matching option prints just the substring that matched, + and/or one or more captured portions of it, as long as these strings are + not empty. The --file-offsets and --line-offsets options output offsets for + the matching substring (all three set show_only_matching). None of these + mutually exclusive options prints any context. Afterwards, adjust the start + and then jump back to look for further matches in the same line. If we are + in invert mode, however, nothing is printed and we do not restart - this + could still be useful because the return code is set. */ - else if (only_matching >= 0) + else if (show_only_matching) { if (!invert) { if (printname != NULL) fprintf(stdout, "%s:", printname); if (number) fprintf(stdout, "%d:", linenumber); + + /* Handle --line-offsets */ + if (line_offsets) fprintf(stdout, "%d,%d\n", (int)(matchptr + offsets[0] - ptr), offsets[1] - offsets[0]); + + /* Handle --file-offsets */ + else if (file_offsets) fprintf(stdout, "%d,%d\n", (int)(filepos + matchptr + offsets[0] - ptr), offsets[1] - offsets[0]); - else if (only_matching < mrc) + + /* Handle --only-matching, which may occur many times */ + + else { - int plen = offsets[2*only_matching + 1] - offsets[2*only_matching]; - if (plen > 0) + BOOL printed = FALSE; + omstr *om; + + for (om = only_matching; om != NULL; om = om->next) { - if (do_colour) fprintf(stdout, "%c[%sm", 0x1b, colour_string); - FWRITE(matchptr + offsets[only_matching*2], 1, plen, stdout); - if (do_colour) fprintf(stdout, "%c[00m", 0x1b); - fprintf(stdout, "\n"); + int n = om->groupnum; + if (n < mrc) + { + int plen = offsets[2*n + 1] - offsets[2*n]; + if (plen > 0) + { + if (printed) fprintf(stdout, "%s", om_separator); + if (do_colour) fprintf(stdout, "%c[%sm", 0x1b, colour_string); + FWRITE(matchptr + offsets[n*2], 1, plen, stdout); + if (do_colour) fprintf(stdout, "%c[00m", 0x1b); + printed = TRUE; + } + } } + + if (printed || printname != NULL || number) fprintf(stdout, "\n"); } - else if (printname != NULL || number) fprintf(stdout, "\n"); + + /* Prepare to repeat to find the next match */ + match = FALSE; if (line_buffered) fflush(stdout); rc = 0; /* Had some success */ @@ -1357,7 +1876,7 @@ while (ptr < endptr) { char *endmatch = ptr + offsets[1]; t = ptr; - while (t < endmatch) + while (t <= endmatch) { t = end_of_line(t, endptr, &endlinelength); if (t < endmatch) linenumber++; else break; @@ -1398,7 +1917,8 @@ while (ptr < endptr) { startoffset = offsets[1]; if (startoffset >= (int)linelength + endlinelength || - !match_patterns(matchptr, length, startoffset, offsets, &mrc)) + !match_patterns(matchptr, length, options, startoffset, offsets, + &mrc)) break; FWRITE(matchptr + startoffset, 1, offsets[0] - startoffset, stdout); fprintf(stdout, "%c[%sm", 0x1b, colour_string); @@ -1516,7 +2036,7 @@ while (ptr < endptr) /* End of file; print final "after" lines if wanted; do_after_lines sets hyphenpending if it prints something. */ -if (only_matching < 0 && !count_only) +if (!show_only_matching && !count_only) { do_after_lines(lastmatchnumber, lastmatchrestart, endptr, printname); hyphenpending |= endhyphenpending; @@ -1560,7 +2080,8 @@ Arguments: dir_recurse TRUE if recursing is wanted (-r or -drecurse) only_one_at_top TRUE if the path is the only one at toplevel -Returns: 0 if there was at least one match +Returns: -1 the file/directory was skipped + 0 if there was at least one match 1 if there were no matches 2 there was some kind of error @@ -1571,10 +2092,9 @@ static int grep_or_recurse(char *pathname, BOOL dir_recurse, BOOL only_one_at_top) { int rc = 1; -int sep; int frtype; -int pathlen; void *handle; +char *lastcomp; FILE *in = NULL; /* Ensure initialized */ #ifdef SUPPORT_LIBZ @@ -1585,6 +2105,15 @@ gzFile ingz = NULL; BZFILE *inbz2 = NULL; #endif +#if defined SUPPORT_LIBZ || defined SUPPORT_LIBBZ2 +int pathlen; +#endif + +#if defined NATIVE_ZOS +int zos_type; +FILE *zos_test_file; +#endif + /* If the file name is "-" we scan stdin */ if (strcmp(pathname, "-") == 0) @@ -1594,14 +2123,62 @@ if (strcmp(pathname, "-") == 0) stdin_name : NULL); } -/* If the file is a directory, skip if skipping or if we are recursing, scan -each file and directory within it, subject to any include or exclude patterns -that were set. The scanning code is localized so it can be made -system-specific. */ +/* Inclusion and exclusion: --include-dir and --exclude-dir apply only to +directories, whereas --include and --exclude apply to everything else. The test +is against the final component of the path. */ -if ((sep = isdirectory(pathname)) != 0) +lastcomp = strrchr(pathname, FILESEP); +lastcomp = (lastcomp == NULL)? pathname : lastcomp + 1; + +/* If the file is a directory, skip if not recursing or if explicitly excluded. +Otherwise, scan the directory and recurse for each path within it. The scanning +code is localized so it can be made system-specific. */ + + +/* For z/OS, determine the file type. */ + +#if defined NATIVE_ZOS +zos_test_file = fopen(pathname,"rb"); + +if (zos_test_file == NULL) + { + if (!silent) fprintf(stderr, "pcregrep: failed to test next file %s\n", + pathname, strerror(errno)); + return -1; + } +zos_type = identifyzosfiletype (zos_test_file); +fclose (zos_test_file); + +/* Handle a PDS in separate code */ + +if (zos_type == __ZOS_PDS || zos_type == __ZOS_PDSE) + { + return travelonpdsdir (pathname, only_one_at_top); + } + +/* Deal with regular files in the normal way below. These types are: + zos_type == __ZOS_PDS_MEMBER + zos_type == __ZOS_PS + zos_type == __ZOS_VSAM_KSDS + zos_type == __ZOS_VSAM_ESDS + zos_type == __ZOS_VSAM_RRDS +*/ + +/* Handle a z/OS directory using common code. */ + +else if (zos_type == __ZOS_HFS) + { +#endif /* NATIVE_ZOS */ + + +/* Handle directories: common code for all OS */ + +if (isdirectory(pathname)) { - if (dee_action == dee_SKIP) return 1; + if (dee_action == dee_SKIP || + !test_incexc(lastcomp, include_dir_patterns, exclude_dir_patterns)) + return -1; + if (dee_action == dee_RECURSE) { char buffer[1024]; @@ -1618,31 +2195,8 @@ if ((sep = isdirectory(pathname)) != 0) while ((nextfile = readdirectory(dir)) != NULL) { - int frc, nflen; - sprintf(buffer, "%.512s%c%.128s", pathname, sep, nextfile); - nflen = (int)(strlen(nextfile)); - - if (isdirectory(buffer)) - { - if (exclude_dir_compiled != NULL && - pcre_exec(exclude_dir_compiled, NULL, nextfile, nflen, 0, 0, NULL, 0) >= 0) - continue; - - if (include_dir_compiled != NULL && - pcre_exec(include_dir_compiled, NULL, nextfile, nflen, 0, 0, NULL, 0) < 0) - continue; - } - else - { - if (exclude_compiled != NULL && - pcre_exec(exclude_compiled, NULL, nextfile, nflen, 0, 0, NULL, 0) >= 0) - continue; - - if (include_compiled != NULL && - pcre_exec(include_compiled, NULL, nextfile, nflen, 0, 0, NULL, 0) < 0) - continue; - } - + int frc; + sprintf(buffer, "%.512s%c%.128s", pathname, FILESEP, nextfile); frc = grep_or_recurse(buffer, dir_recurse, FALSE); if (frc > 1) rc = frc; else if (frc == 0 && rc == 1) rc = 0; @@ -1653,18 +2207,32 @@ if ((sep = isdirectory(pathname)) != 0) } } -/* If the file is not a directory and not a regular file, skip it if that's -been requested. */ +#if defined NATIVE_ZOS + } +#endif -else if (!isregfile(pathname) && DEE_action == DEE_SKIP) return 1; +/* If the file is not a directory, check for a regular file, and if it is not, +skip it if that's been requested. Otherwise, check for an explicit inclusion or +exclusion. */ +else if ( +#if defined NATIVE_ZOS + (zos_type == __ZOS_NOFILE && DEE_action == DEE_SKIP) || +#else /* all other OS */ + (!isregfile(pathname) && DEE_action == DEE_SKIP) || +#endif + !test_incexc(lastcomp, include_patterns, exclude_patterns)) + return -1; /* File skipped */ + /* Control reaches here if we have a regular file, or if we have a directory and recursion or skipping was not requested, or if we have anything else and skipping was not requested. The scan proceeds. If this is the first and only argument at top level, we don't show the file name, unless we are only showing the file name, or the filename was forced (-H). */ +#if defined SUPPORT_LIBZ || defined SUPPORT_LIBBZ2 pathlen = (int)(strlen(pathname)); +#endif /* Open using zlib if it is supported and the file name ends with .gz. */ @@ -1769,96 +2337,7 @@ return rc; - /************************************************* -* Usage function * -*************************************************/ - -static int -usage(int rc) -{ -option_item *op; -fprintf(stderr, "Usage: pcregrep [-"); -for (op = optionlist; op->one_char != 0; op++) - { - if (op->one_char > 0) fprintf(stderr, "%c", op->one_char); - } -fprintf(stderr, "] [long options] [pattern] [files]\n"); -fprintf(stderr, "Type `pcregrep --help' for more information and the long " - "options.\n"); -return rc; -} - - - - -/************************************************* -* Help function * -*************************************************/ - -static void -help(void) -{ -option_item *op; - -printf("Usage: pcregrep [OPTION]... [PATTERN] [FILE1 FILE2 ...]\n"); -printf("Search for PATTERN in each FILE or standard input.\n"); -printf("PATTERN must be present if neither -e nor -f is used.\n"); -printf("\"-\" can be used as a file name to mean STDIN.\n"); - -#ifdef SUPPORT_LIBZ -printf("Files whose names end in .gz are read using zlib.\n"); -#endif - -#ifdef SUPPORT_LIBBZ2 -printf("Files whose names end in .bz2 are read using bzlib2.\n"); -#endif - -#if defined SUPPORT_LIBZ || defined SUPPORT_LIBBZ2 -printf("Other files and the standard input are read as plain files.\n\n"); -#else -printf("All files are read as plain files, without any interpretation.\n\n"); -#endif - -printf("Example: pcregrep -i 'hello.*world' menu.h main.c\n\n"); -printf("Options:\n"); - -for (op = optionlist; op->one_char != 0; op++) - { - int n; - char s[4]; - - /* Two options were accidentally implemented and documented with underscores - instead of hyphens in their names, something that was not noticed for quite a - few releases. When fixing this, I left the underscored versions in the list - in case people were using them. However, we don't want to display them in the - help data. There are no other options that contain underscores, and we do not - expect ever to implement such options. Therefore, just omit any option that - contains an underscore. */ - - if (strchr(op->long_name, '_') != NULL) continue; - - if (op->one_char > 0) sprintf(s, "-%c,", op->one_char); else strcpy(s, " "); - n = 31 - printf(" %s --%s", s, op->long_name); - if (n < 1) n = 1; - printf("%.*s%s\n", n, " ", op->help_text); - } - -printf("\nNumbers may be followed by K or M, e.g. --buffer-size=100K.\n"); -printf("The default value for --buffer-size is %d.\n", PCREGREP_BUFSIZE); -printf("When reading patterns from a file instead of using a command line option,\n"); -printf("trailing white space is removed and blank lines are ignored.\n"); -printf("There is a maximum of %d patterns, each of maximum size %d bytes.\n", - MAX_PATTERN_COUNT, PATBUFSIZE); - -printf("\nWith no FILEs, read standard input. If fewer than two FILEs given, assume -h.\n"); -printf("Exit status is 0 if any matches, 1 if no matches, and 2 if trouble.\n"); -} - - - - -/************************************************* * Handle a single-letter, no data option * *************************************************/ @@ -1872,16 +2351,23 @@ switch(letter) case N_LBUFFER: line_buffered = TRUE; break; case N_LOFFSETS: line_offsets = number = TRUE; break; case N_NOJIT: study_options &= ~PCRE_STUDY_JIT_COMPILE; break; + case 'a': binary_files = BIN_TEXT; break; case 'c': count_only = TRUE; break; case 'F': process_options |= PO_FIXED_STRINGS; break; case 'H': filenames = FN_FORCE; break; + case 'I': binary_files = BIN_NOMATCH; break; case 'h': filenames = FN_NONE; break; case 'i': options |= PCRE_CASELESS; break; case 'l': omit_zero_count = TRUE; filenames = FN_MATCH_ONLY; break; case 'L': filenames = FN_NOMATCH_ONLY; break; case 'M': multiline = TRUE; options |= PCRE_MULTILINE|PCRE_FIRSTLINE; break; case 'n': number = TRUE; break; - case 'o': only_matching = 0; break; + + case 'o': + only_matching_last = add_number(0, only_matching_last); + if (only_matching == NULL) only_matching = only_matching_last; + break; + case 'q': quiet = TRUE; break; case 'r': dee_action = dee_RECURSE; break; case 's': silent = TRUE; break; @@ -1891,7 +2377,7 @@ switch(letter) case 'x': process_options |= PO_LINE_MATCH; break; case 'V': - fprintf(stderr, "pcregrep version %s\n", pcre_version()); + fprintf(stdout, "pcregrep version %s\n", pcre_version()); pcregrep_exit(0); break; @@ -1935,13 +2421,20 @@ return buffer; * Compile a single pattern * *************************************************/ -/* When the -F option has been used, this is called for each substring. -Otherwise it's called for each supplied pattern. +/* Do nothing if the pattern has already been compiled. This is the case for +include/exclude patterns read from a file. +When the -F option has been used, each "pattern" may be a list of strings, +separated by line breaks. They will be matched literally. We split such a +string and compile the first substring, inserting an additional block into the +pattern chain. + Arguments: - pattern the pattern string + p points to the pattern block options the PCRE options - filename the file name, or NULL for a command-line pattern + popts the processing options + fromfile TRUE if the pattern was read from a file + fromtext file name or identifying text (e.g. "include") count 0 if this is the only command line pattern, or number of the command line pattern, or linenumber for a pattern from a file @@ -1950,47 +2443,52 @@ Returns: TRUE on success, FALSE after an error */ static BOOL -compile_single_pattern(char *pattern, int options, char *filename, int count) +compile_pattern(patstr *p, int options, int popts, int fromfile, + const char *fromtext, int count) { char buffer[PATBUFSIZE]; const char *error; +char *ps = p->string; +int patlen = strlen(ps); int errptr; -if (pattern_count >= MAX_PATTERN_COUNT) - { - fprintf(stderr, "pcregrep: Too many %spatterns (max %d)\n", - (filename == NULL)? "command-line " : "", MAX_PATTERN_COUNT); - return FALSE; - } +if (p->compiled != NULL) return TRUE; -sprintf(buffer, "%s%.*s%s", prefix[process_options], bufthird, pattern, - suffix[process_options]); -pattern_list[pattern_count] = - pcre_compile(buffer, options, &error, &errptr, pcretables); -if (pattern_list[pattern_count] != NULL) +if ((popts & PO_FIXED_STRINGS) != 0) { - pattern_count++; - return TRUE; + int ellength; + char *eop = ps + patlen; + char *pe = end_of_line(ps, eop, &ellength); + + if (ellength != 0) + { + if (add_pattern(pe, p) == NULL) return FALSE; + patlen = (int)(pe - ps - ellength); + } } +sprintf(buffer, "%s%.*s%s", prefix[popts], patlen, ps, suffix[popts]); +p->compiled = pcre_compile(buffer, options, &error, &errptr, pcretables); +if (p->compiled != NULL) return TRUE; + /* Handle compile errors */ -errptr -= (int)strlen(prefix[process_options]); -if (errptr > (int)strlen(pattern)) errptr = (int)strlen(pattern); +errptr -= (int)strlen(prefix[popts]); +if (errptr > patlen) errptr = patlen; -if (filename == NULL) +if (fromfile) { - if (count == 0) - fprintf(stderr, "pcregrep: Error in command-line regex " - "at offset %d: %s\n", errptr, error); - else - fprintf(stderr, "pcregrep: Error in %s command-line regex " - "at offset %d: %s\n", ordin(count), errptr, error); + fprintf(stderr, "pcregrep: Error in regex in line %d of %s " + "at offset %d: %s\n", count, fromtext, errptr, error); } else { - fprintf(stderr, "pcregrep: Error in regex in line %d of %s " - "at offset %d: %s\n", count, filename, errptr, error); + if (count == 0) + fprintf(stderr, "pcregrep: Error in %s regex at offset %d: %s\n", + fromtext, errptr, error); + else + fprintf(stderr, "pcregrep: Error in %s %s regex at offset %d: %s\n", + ordin(count), fromtext, errptr, error); } return FALSE; @@ -1999,43 +2497,79 @@ return FALSE; /************************************************* -* Compile one supplied pattern * +* Read and compile a file of patterns * *************************************************/ -/* When the -F option has been used, each string may be a list of strings, -separated by line breaks. They will be matched literally. +/* This is used for --filelist, --include-from, and --exclude-from. Arguments: - pattern the pattern string - options the PCRE options - filename the file name, or NULL for a command-line pattern - count 0 if this is the only command line pattern, or - number of the command line pattern, or - linenumber for a pattern from a file + name the name of the file; "-" is stdin + patptr pointer to the pattern chain anchor + patlastptr pointer to the last pattern pointer + popts the process options to pass to pattern_compile() -Returns: TRUE on success, FALSE after an error +Returns: TRUE if all went well */ static BOOL -compile_pattern(char *pattern, int options, char *filename, int count) +read_pattern_file(char *name, patstr **patptr, patstr **patlastptr, int popts) { -if ((process_options & PO_FIXED_STRINGS) != 0) +int linenumber = 0; +FILE *f; +char *filename; +char buffer[PATBUFSIZE]; + +if (strcmp(name, "-") == 0) { - char *eop = pattern + strlen(pattern); - char buffer[PATBUFSIZE]; + f = stdin; + filename = stdin_name; + } +else + { + f = fopen(name, "r"); + if (f == NULL) + { + fprintf(stderr, "pcregrep: Failed to open %s: %s\n", name, strerror(errno)); + return FALSE; + } + filename = name; + } + +while (fgets(buffer, PATBUFSIZE, f) != NULL) + { + char *s = buffer + (int)strlen(buffer); + while (s > buffer && isspace((unsigned char)(s[-1]))) s--; + *s = 0; + linenumber++; + if (buffer[0] == 0) continue; /* Skip blank lines */ + + /* Note: this call to add_pattern() puts a pointer to the local variable + "buffer" into the pattern chain. However, that pointer is used only when + compiling the pattern, which happens immediately below, so we flatten it + afterwards, as a precaution against any later code trying to use it. */ + + *patlastptr = add_pattern(buffer, *patlastptr); + if (*patlastptr == NULL) return FALSE; + if (*patptr == NULL) *patptr = *patlastptr; + + /* This loop is needed because compiling a "pattern" when -F is set may add + on additional literal patterns if the original contains a newline. In the + common case, it never will, because fgets() stops at a newline. However, + the -N option can be used to give pcregrep a different newline setting. */ + for(;;) { - int ellength; - char *p = end_of_line(pattern, eop, &ellength); - if (ellength == 0) - return compile_single_pattern(pattern, options, filename, count); - sprintf(buffer, "%.*s", (int)(p - pattern - ellength), pattern); - pattern = p; - if (!compile_single_pattern(buffer, options, filename, count)) + if (!compile_pattern(*patlastptr, pcre_options, popts, TRUE, filename, + linenumber)) return FALSE; + (*patlastptr)->string = NULL; /* Insurance */ + if ((*patlastptr)->next == NULL) break; + *patlastptr = (*patlastptr)->next; } } -else return compile_single_pattern(pattern, options, filename, count); + +if (f != stdin) fclose(f); +return TRUE; } @@ -2051,12 +2585,9 @@ main(int argc, char **argv) { int i, j; int rc = 1; -int pcre_options = 0; -int cmd_pattern_count = 0; -int hint_count = 0; -int errptr; BOOL only_one_at_top; -char *patterns[MAX_PATTERN_COUNT]; +patstr *cp; +fnstr *fn; const char *locale_from = "--locale"; const char *error; @@ -2096,7 +2627,7 @@ for (i = 1; i < argc; i++) if (argv[i][1] == 0) { - if (pattern_filename != NULL || pattern_count > 0) break; + if (pattern_files != NULL || patterns != NULL) break; else pcregrep_exit(usage(2)); } @@ -2222,6 +2753,7 @@ for (i = 1; i < argc; i++) { char *s = argv[i] + 1; longop = FALSE; + while (*s != 0) { for (op = optionlist; op->one_char != 0; op++) @@ -2235,19 +2767,25 @@ for (i = 1; i < argc; i++) pcregrep_exit(usage(2)); } - /* Check for a single-character option that has data: OP_OP_NUMBER - is used for one that either has a numerical number or defaults, i.e. the - data is optional. If a digit follows, there is data; if not, carry on + option_data = s+1; + + /* Break out if this is the last character in the string; it's handled + below like a single multi-char option. */ + + if (*option_data == 0) break; + + /* Check for a single-character option that has data: OP_OP_NUMBER(S) + are used for ones that either have a numerical number or defaults, i.e. + the data is optional. If a digit follows, there is data; if not, carry on with other single-character options in the same string. */ - option_data = s+1; - if (op->type == OP_OP_NUMBER) + if (op->type == OP_OP_NUMBER || op->type == OP_OP_NUMBERS) { if (isdigit((unsigned char)s[1])) break; } - else /* Check for end or a dataless option */ + else /* Check for an option with data */ { - if (op->type != OP_NODATA || s[1] == 0) break; + if (op->type != OP_NODATA) break; } /* Handle a single-character option with no data, then loop for the @@ -2267,13 +2805,14 @@ for (i = 1; i < argc; i++) continue; } - /* If the option type is OP_OP_STRING or OP_OP_NUMBER, it's an option that + /* If the option type is OP_OP_STRING or OP_OP_NUMBER(S), it's an option that either has a value or defaults to something. It cannot have data in a separate item. At the moment, the only such options are "colo(u)r", "only-matching", and Jeffrey Friedl's special -S debugging option. */ if (*option_data == 0 && - (op->type == OP_OP_STRING || op->type == OP_OP_NUMBER)) + (op->type == OP_OP_STRING || op->type == OP_OP_NUMBER || + op->type == OP_OP_NUMBERS)) { switch (op->one_char) { @@ -2282,7 +2821,8 @@ for (i = 1; i < argc; i++) break; case 'o': - only_matching = 0; + only_matching_last = add_number(0, only_matching_last); + if (only_matching == NULL) only_matching = only_matching_last; break; #ifdef JFRIEDL_DEBUG @@ -2306,68 +2846,81 @@ for (i = 1; i < argc; i++) option_data = argv[++i]; } - /* If the option type is OP_PATLIST, it's the -e option, which can be called - multiple times to create a list of patterns. */ + /* If the option type is OP_OP_NUMBERS, the value is a number that is to be + added to a chain of numbers. */ - if (op->type == OP_PATLIST) + if (op->type == OP_OP_NUMBERS) { - if (cmd_pattern_count >= MAX_PATTERN_COUNT) - { - fprintf(stderr, "pcregrep: Too many command-line patterns (max %d)\n", - MAX_PATTERN_COUNT); - return 2; - } - patterns[cmd_pattern_count++] = option_data; + unsigned long int n = decode_number(option_data, op, longop); + omdatastr *omd = (omdatastr *)op->dataptr; + *(omd->lastptr) = add_number((int)n, *(omd->lastptr)); + if (*(omd->anchor) == NULL) *(omd->anchor) = *(omd->lastptr); } - /* Otherwise, deal with single string or numeric data values. */ + /* If the option type is OP_PATLIST, it's the -e option, or one of the + include/exclude options, which can be called multiple times to create lists + of patterns. */ - else if (op->type != OP_NUMBER && op->type != OP_LONGNUMBER && - op->type != OP_OP_NUMBER) + else if (op->type == OP_PATLIST) { - *((char **)op->dataptr) = option_data; + patdatastr *pd = (patdatastr *)op->dataptr; + *(pd->lastptr) = add_pattern(option_data, *(pd->lastptr)); + if (*(pd->lastptr) == NULL) goto EXIT2; + if (*(pd->anchor) == NULL) *(pd->anchor) = *(pd->lastptr); } - /* Avoid the use of strtoul() because SunOS4 doesn't have it. This is used - only for unpicking arguments, so just keep it simple. */ + /* If the option type is OP_FILELIST, it's one of the options that names a + file. */ - else + else if (op->type == OP_FILELIST) { - unsigned long int n = 0; - char *endptr = option_data; - while (*endptr != 0 && isspace((unsigned char)(*endptr))) endptr++; - while (isdigit((unsigned char)(*endptr))) - n = n * 10 + (int)(*endptr++ - '0'); - if (toupper(*endptr) == 'K') + fndatastr *fd = (fndatastr *)op->dataptr; + fn = (fnstr *)malloc(sizeof(fnstr)); + if (fn == NULL) { - n *= 1024; - endptr++; + fprintf(stderr, "pcregrep: malloc failed\n"); + goto EXIT2; } - else if (toupper(*endptr) == 'M') + fn->next = NULL; + fn->name = option_data; + if (*(fd->anchor) == NULL) + *(fd->anchor) = fn; + else + (*(fd->lastptr))->next = fn; + *(fd->lastptr) = fn; + } + + /* Handle OP_BINARY_FILES */ + + else if (op->type == OP_BINFILES) + { + if (strcmp(option_data, "binary") == 0) + binary_files = BIN_BINARY; + else if (strcmp(option_data, "without-match") == 0) + binary_files = BIN_NOMATCH; + else if (strcmp(option_data, "text") == 0) + binary_files = BIN_TEXT; + else { - n *= 1024*1024; - endptr++; - } - if (*endptr != 0) - { - if (longop) - { - char *equals = strchr(op->long_name, '='); - int nlen = (equals == NULL)? (int)strlen(op->long_name) : - (int)(equals - op->long_name); - fprintf(stderr, "pcregrep: Malformed number \"%s\" after --%.*s\n", - option_data, nlen, op->long_name); - } - else - fprintf(stderr, "pcregrep: Malformed number \"%s\" after -%c\n", - option_data, op->one_char); + fprintf(stderr, "pcregrep: unknown value \"%s\" for binary-files\n", + option_data); pcregrep_exit(usage(2)); } - if (op->type == OP_LONGNUMBER) - *((unsigned long int *)op->dataptr) = n; - else - *((int *)op->dataptr) = n; } + + /* Otherwise, deal with a single string or numeric data value. */ + + else if (op->type != OP_NUMBER && op->type != OP_LONGNUMBER && + op->type != OP_OP_NUMBER) + { + *((char **)op->dataptr) = option_data; + } + else + { + unsigned long int n = decode_number(option_data, op, longop); + if (op->type == OP_LONGNUMBER) *((unsigned long int *)op->dataptr) = n; + else *((int *)op->dataptr) = n; + } } /* Options have been decoded. If -C was used, its value is used as a default @@ -2380,9 +2933,10 @@ if (both_context > 0) } /* Only one of --only-matching, --file-offsets, or --line-offsets is permitted. -However, the latter two set only_matching. */ +However, all three set show_only_matching because they display, each in their +own way, only the data that has matched. */ -if ((only_matching >= 0 && (file_offsets || line_offsets)) || +if ((only_matching != NULL && (file_offsets || line_offsets)) || (file_offsets && line_offsets)) { fprintf(stderr, "pcregrep: Cannot mix --only-matching, --file-offsets " @@ -2390,7 +2944,8 @@ if ((only_matching >= 0 && (file_offsets || line_offse pcregrep_exit(usage(2)); } -if (file_offsets || line_offsets) only_matching = 0; +if (only_matching != NULL || file_offsets || line_offsets) + show_only_matching = TRUE; /* If a locale has not been provided as an option, see if the LC_CTYPE or LC_ALL environment variable is set, and if so, use it. */ @@ -2514,198 +3069,175 @@ if (jfriedl_XT != 0 || jfriedl_XR != 0) } #endif -/* Get memory for the main buffer, and to store the pattern and hints lists. */ +/* Get memory for the main buffer. */ bufsize = 3*bufthird; main_buffer = (char *)malloc(bufsize); -pattern_list = (pcre **)malloc(MAX_PATTERN_COUNT * sizeof(pcre *)); -hints_list = (pcre_extra **)malloc(MAX_PATTERN_COUNT * sizeof(pcre_extra *)); -if (main_buffer == NULL || pattern_list == NULL || hints_list == NULL) +if (main_buffer == NULL) { fprintf(stderr, "pcregrep: malloc failed\n"); goto EXIT2; } -/* If no patterns were provided by -e, and there is no file provided by -f, +/* If no patterns were provided by -e, and there are no files provided by -f, the first argument is the one and only pattern, and it must exist. */ -if (cmd_pattern_count == 0 && pattern_filename == NULL) +if (patterns == NULL && pattern_files == NULL) { if (i >= argc) return usage(2); - patterns[cmd_pattern_count++] = argv[i++]; + patterns = patterns_last = add_pattern(argv[i++], NULL); + if (patterns == NULL) goto EXIT2; } /* Compile the patterns that were provided on the command line, either by -multiple uses of -e or as a single unkeyed pattern. */ +multiple uses of -e or as a single unkeyed pattern. We cannot do this until +after all the command-line options are read so that we know which PCRE options +to use. When -F is used, compile_pattern() may add another block into the +chain, so we must not access the next pointer till after the compile. */ -for (j = 0; j < cmd_pattern_count; j++) +for (j = 1, cp = patterns; cp != NULL; j++, cp = cp->next) { - if (!compile_pattern(patterns[j], pcre_options, NULL, - (j == 0 && cmd_pattern_count == 1)? 0 : j + 1)) + if (!compile_pattern(cp, pcre_options, process_options, FALSE, "command-line", + (j == 1 && patterns->next == NULL)? 0 : j)) goto EXIT2; } -/* Compile the regular expressions that are provided in a file. */ +/* Read and compile the regular expressions that are provided in files. */ -if (pattern_filename != NULL) +for (fn = pattern_files; fn != NULL; fn = fn->next) { - int linenumber = 0; - FILE *f; - char *filename; - char buffer[PATBUFSIZE]; + if (!read_pattern_file(fn->name, &patterns, &patterns_last, process_options)) + goto EXIT2; + } - if (strcmp(pattern_filename, "-") == 0) - { - f = stdin; - filename = stdin_name; - } - else - { - f = fopen(pattern_filename, "r"); - if (f == NULL) - { - fprintf(stderr, "pcregrep: Failed to open %s: %s\n", pattern_filename, - strerror(errno)); - goto EXIT2; - } - filename = pattern_filename; - } +/* Study the regular expressions, as we will be running them many times. If an +extra block is needed for a limit, set PCRE_STUDY_EXTRA_NEEDED so that one is +returned, even if studying produces no data. */ - while (fgets(buffer, PATBUFSIZE, f) != NULL) - { - char *s = buffer + (int)strlen(buffer); - while (s > buffer && isspace((unsigned char)(s[-1]))) s--; - *s = 0; - linenumber++; - if (buffer[0] == 0) continue; /* Skip blank lines */ - if (!compile_pattern(buffer, pcre_options, filename, linenumber)) - goto EXIT2; - } +if (match_limit > 0 || match_limit_recursion > 0) + study_options |= PCRE_STUDY_EXTRA_NEEDED; - if (f != stdin) fclose(f); - } +/* Unless JIT has been explicitly disabled, arrange a stack for it to use. */ -/* Study the regular expressions, as we will be running them many times. Unless -JIT has been explicitly disabled, arrange a stack for it to use. */ - #ifdef SUPPORT_PCREGREP_JIT if ((study_options & PCRE_STUDY_JIT_COMPILE) != 0) jit_stack = pcre_jit_stack_alloc(32*1024, 1024*1024); #endif -for (j = 0; j < pattern_count; j++) +for (j = 1, cp = patterns; cp != NULL; j++, cp = cp->next) { - hints_list[j] = pcre_study(pattern_list[j], study_options, &error); + cp->hint = pcre_study(cp->compiled, study_options, &error); if (error != NULL) { char s[16]; - if (pattern_count == 1) s[0] = 0; else sprintf(s, " number %d", j); + if (patterns->next == NULL) s[0] = 0; else sprintf(s, " number %d", j); fprintf(stderr, "pcregrep: Error while studying regex%s: %s\n", s, error); goto EXIT2; } - hint_count++; #ifdef SUPPORT_PCREGREP_JIT - if (jit_stack != NULL && hints_list[j] != NULL) - pcre_assign_jit_stack(hints_list[j], NULL, jit_stack); + if (jit_stack != NULL && cp->hint != NULL) + pcre_assign_jit_stack(cp->hint, NULL, jit_stack); #endif } /* If --match-limit or --recursion-limit was set, put the value(s) into the -pcre_extra block for each pattern. */ +pcre_extra block for each pattern. There will always be an extra block because +of the use of PCRE_STUDY_EXTRA_NEEDED above. */ -if (match_limit > 0 || match_limit_recursion > 0) +for (cp = patterns; cp != NULL; cp = cp->next) { - for (j = 0; j < pattern_count; j++) + if (match_limit > 0) { - if (hints_list[j] == NULL) - { - hints_list[j] = malloc(sizeof(pcre_extra)); - if (hints_list[j] == NULL) - { - fprintf(stderr, "pcregrep: malloc failed\n"); - pcregrep_exit(2); - } - } - if (match_limit > 0) - { - hints_list[j]->flags |= PCRE_EXTRA_MATCH_LIMIT; - hints_list[j]->match_limit = match_limit; - } - if (match_limit_recursion > 0) - { - hints_list[j]->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; - hints_list[j]->match_limit_recursion = match_limit_recursion; - } + cp->hint->flags |= PCRE_EXTRA_MATCH_LIMIT; + cp->hint->match_limit = match_limit; } - } -/* If there are include or exclude patterns, compile them. */ - -if (exclude_pattern != NULL) - { - exclude_compiled = pcre_compile(exclude_pattern, 0, &error, &errptr, - pcretables); - if (exclude_compiled == NULL) + if (match_limit_recursion > 0) { - fprintf(stderr, "pcregrep: Error in 'exclude' regex at offset %d: %s\n", - errptr, error); - goto EXIT2; + cp->hint->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; + cp->hint->match_limit_recursion = match_limit_recursion; } } -if (include_pattern != NULL) +/* If there are include or exclude patterns read from the command line, compile +them. -F, -w, and -x do not apply, so the third argument of compile_pattern is +0. */ + +for (j = 0; j < 4; j++) { - include_compiled = pcre_compile(include_pattern, 0, &error, &errptr, - pcretables); - if (include_compiled == NULL) + int k; + for (k = 1, cp = *(incexlist[j]); cp != NULL; k++, cp = cp->next) { - fprintf(stderr, "pcregrep: Error in 'include' regex at offset %d: %s\n", - errptr, error); - goto EXIT2; + if (!compile_pattern(cp, pcre_options, 0, FALSE, incexname[j], + (k == 1 && cp->next == NULL)? 0 : k)) + goto EXIT2; } } -if (exclude_dir_pattern != NULL) +/* Read and compile include/exclude patterns from files. */ + +for (fn = include_from; fn != NULL; fn = fn->next) { - exclude_dir_compiled = pcre_compile(exclude_dir_pattern, 0, &error, &errptr, - pcretables); - if (exclude_dir_compiled == NULL) - { - fprintf(stderr, "pcregrep: Error in 'exclude_dir' regex at offset %d: %s\n", - errptr, error); + if (!read_pattern_file(fn->name, &include_patterns, &include_patterns_last, 0)) goto EXIT2; - } } -if (include_dir_pattern != NULL) +for (fn = exclude_from; fn != NULL; fn = fn->next) { - include_dir_compiled = pcre_compile(include_dir_pattern, 0, &error, &errptr, - pcretables); - if (include_dir_compiled == NULL) - { - fprintf(stderr, "pcregrep: Error in 'include_dir' regex at offset %d: %s\n", - errptr, error); + if (!read_pattern_file(fn->name, &exclude_patterns, &exclude_patterns_last, 0)) goto EXIT2; - } } -/* If there are no further arguments, do the business on stdin and exit. */ +/* If there are no files that contain lists of files to search, and there are +no file arguments, search stdin, and then exit. */ -if (i >= argc) +if (file_lists == NULL && i >= argc) { rc = pcregrep(stdin, FR_PLAIN, stdin_name, (filenames > FN_DEFAULT)? stdin_name : NULL); goto EXIT; } -/* Otherwise, work through the remaining arguments as files or directories. -Pass in the fact that there is only one argument at top level - this suppresses -the file name if the argument is not a directory and filenames are not -otherwise forced. */ +/* If any files that contains a list of files to search have been specified, +read them line by line and search the given files. */ -only_one_at_top = i == argc - 1; /* Catch initial value of i */ +for (fn = file_lists; fn != NULL; fn = fn->next) + { + char buffer[PATBUFSIZE]; + FILE *fl; + if (strcmp(fn->name, "-") == 0) fl = stdin; else + { + fl = fopen(fn->name, "rb"); + if (fl == NULL) + { + fprintf(stderr, "pcregrep: Failed to open %s: %s\n", fn->name, + strerror(errno)); + goto EXIT2; + } + } + while (fgets(buffer, PATBUFSIZE, fl) != NULL) + { + int frc; + char *end = buffer + (int)strlen(buffer); + while (end > buffer && isspace(end[-1])) end--; + *end = 0; + if (*buffer != 0) + { + frc = grep_or_recurse(buffer, dee_action == dee_RECURSE, FALSE); + if (frc > 1) rc = frc; + else if (frc == 0 && rc == 1) rc = 0; + } + } + if (fl != stdin) fclose(fl); + } +/* After handling file-list, work through remaining arguments. Pass in the fact +that there is only one argument at top level - this suppresses the file name if +the argument is not a directory and filenames are not otherwise forced. */ + +only_one_at_top = i == argc - 1 && file_lists == NULL; + for (; i < argc; i++) { int frc = grep_or_recurse(argv[i], dee_action == dee_RECURSE, @@ -2718,20 +3250,27 @@ EXIT: #ifdef SUPPORT_PCREGREP_JIT if (jit_stack != NULL) pcre_jit_stack_free(jit_stack); #endif + if (main_buffer != NULL) free(main_buffer); -if (pattern_list != NULL) + +free_pattern_chain(patterns); +free_pattern_chain(include_patterns); +free_pattern_chain(include_dir_patterns); +free_pattern_chain(exclude_patterns); +free_pattern_chain(exclude_dir_patterns); + +free_file_chain(exclude_from); +free_file_chain(include_from); +free_file_chain(pattern_files); +free_file_chain(file_lists); + +while (only_matching != NULL) { - for (i = 0; i < pattern_count; i++) free(pattern_list[i]); - free(pattern_list); + omstr *this = only_matching; + only_matching = this->next; + free(this); } -if (hints_list != NULL) - { - for (i = 0; i < hint_count; i++) - { - if (hints_list[i] != NULL) pcre_free_study(hints_list[i]); - } - free(hints_list); - } + pcregrep_exit(rc); EXIT2: