Diff for /embedaddon/pcre/pcregrep.c between versions 1.1.1.3 and 1.1.1.4

version 1.1.1.3, 2012/10/09 09:19:17 version 1.1.1.4, 2013/07/22 08:25:55
Line 70  POSSIBILITY OF SUCH DAMAGE. Line 70  POSSIBILITY OF SUCH DAMAGE.
   
 typedef int BOOL;  typedef int BOOL;
   
 #define MAX_PATTERN_COUNT 100  
 #define OFFSET_SIZE 99  #define OFFSET_SIZE 99
   
 #if BUFSIZ > 8192  #if BUFSIZ > 8192
#define PATBUFSIZE BUFSIZ#define MAXPATLEN BUFSIZ
 #else  #else
#define PATBUFSIZE 8192#define MAXPATLEN 8192
 #endif  #endif
   
   #define PATBUFSIZE (MAXPATLEN + 10)   /* Allows for prefix+suffix */
   
 /* Values for the "filenames" variable, which specifies options for file name  /* 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  output. The order is important; it is assumed that a file name is wanted for
 all values greater than FN_DEFAULT. */  all values greater than FN_DEFAULT. */
Line 139  static char *colour_string = (char *)"1;31"; Line 140  static char *colour_string = (char *)"1;31";
 static char *colour_option = NULL;  static char *colour_option = NULL;
 static char *dee_option = NULL;  static char *dee_option = NULL;
 static char *DEE_option = NULL;  static char *DEE_option = NULL;
   static char *locale = NULL;
 static char *main_buffer = NULL;  static char *main_buffer = NULL;
 static char *newline = NULL;  static char *newline = NULL;
static char *pattern_filename = NULL;static char *om_separator = (char *)"";
 static char *stdin_name = (char *)"(standard input)";  static char *stdin_name = (char *)"(standard input)";
 static char *locale = NULL;  
   
 static const unsigned char *pcretables = 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 *file_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 after_context = 0;
 static int before_context = 0;  static int before_context = 0;
 static int binary_files = BIN_BINARY;  static int binary_files = BIN_BINARY;
 static int both_context = 0;  static int both_context = 0;
 static int bufthird = PCREGREP_BUFSIZE;  static int bufthird = PCREGREP_BUFSIZE;
 static int bufsize = 3*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;  static int dee_action = dee_READ;
   #endif
   
 static int DEE_action = DEE_READ;  static int DEE_action = DEE_READ;
 static int error_count = 0;  static int error_count = 0;
 static int filenames = FN_DEFAULT;  static int filenames = FN_DEFAULT;
static int only_matching = -1;static int pcre_options = 0;
 static int process_options = 0;  static int process_options = 0;
   
 #ifdef SUPPORT_PCREGREP_JIT  #ifdef SUPPORT_PCREGREP_JIT
Line 196  static BOOL number = FALSE; Line 188  static BOOL number = FALSE;
 static BOOL omit_zero_count = FALSE;  static BOOL omit_zero_count = FALSE;
 static BOOL resource_error = FALSE;  static BOOL resource_error = FALSE;
 static BOOL quiet = FALSE;  static BOOL quiet = FALSE;
   static BOOL show_only_matching = FALSE;
 static BOOL silent = FALSE;  static BOOL silent = FALSE;
 static BOOL utf8 = 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 */  /* Structure for options and list of them */
   
 enum { OP_NODATA, OP_STRING, OP_OP_STRING, OP_NUMBER, OP_LONGNUMBER,  enum { OP_NODATA, OP_STRING, OP_OP_STRING, OP_NUMBER, OP_LONGNUMBER,
       OP_OP_NUMBER, OP_PATLIST, OP_BINFILES };       OP_OP_NUMBER, OP_OP_NUMBERS, OP_PATLIST, OP_FILELIST, OP_BINFILES };
   
 typedef struct option_item {  typedef struct option_item {
   int type;    int type;
Line 233  used to identify them. */ Line 315  used to identify them. */
 #define N_NOJIT        (-16)  #define N_NOJIT        (-16)
 #define N_FILE_LIST    (-17)  #define N_FILE_LIST    (-17)
 #define N_BINARY_FILES (-18)  #define N_BINARY_FILES (-18)
   #define N_EXCLUDE_FROM (-19)
   #define N_INCLUDE_FROM (-20)
   #define N_OM_SEPARATOR (-21)
   
 static option_item optionlist[] = {  static option_item optionlist[] = {
   { OP_NODATA,     N_NULL,   NULL,              "",              "terminate options" },    { OP_NODATA,     N_NULL,   NULL,              "",              "terminate options" },
Line 248  static option_item optionlist[] = { Line 333  static option_item optionlist[] = {
   { OP_NODATA,     'c',      NULL,              "count",         "print only a count of matching lines per FILE" },    { 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,       "devices=action","how to handle devices, FIFOs, and sockets" },
   { OP_STRING,     'd',      &dee_option,       "directories=action", "how to handle directories" },    { 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_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_STRING,     N_FILE_LIST, &file_list,     "file-list=path","read files to search 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,     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,              "with-filename", "force the prefixing filename on output" },
   { OP_NODATA,     'h',      NULL,              "no-filename",   "suppress the prefixing filename on output" },    { OP_NODATA,     'h',      NULL,              "no-filename",   "suppress the prefixing filename on output" },
Line 273  static option_item optionlist[] = { Line 358  static option_item optionlist[] = {
   { OP_NODATA,     'M',      NULL,              "multiline",     "run in multiline mode" },    { 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_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_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,     'q',      NULL,              "quiet",         "suppress output, just set return code" },
   { OP_NODATA,     'r',      NULL,              "recursive",     "recursively scan sub-directories" },    { OP_NODATA,     'r',      NULL,              "recursive",     "recursively scan sub-directories" },
  { OP_STRING,     N_EXCLUDE,&exclude_pattern,  "exclude=pattern","exclude matching files when recursing" },  { OP_PATLIST,    N_EXCLUDE,&exclude_patdata,  "exclude=pattern","exclude matching files when recursing" },
  { OP_STRING,     N_INCLUDE,&include_pattern,  "include=pattern","include matching files when recursing" },  { OP_PATLIST,    N_INCLUDE,&include_patdata,  "include=pattern","include matching files when recursing" },
  { OP_STRING,     N_EXCLUDE_DIR,&exclude_dir_pattern, "exclude-dir=pattern","exclude matching directories when recursing" },  { OP_PATLIST,    N_EXCLUDE_DIR,&exclude_dir_patdata, "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_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    /* These two were accidentally implemented with underscores instead of
   hyphens in the option names. As this was not discovered for several releases,    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    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. */    --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_PATLIST,   N_EXCLUDE_DIR,&exclude_dir_patdata, "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_INCLUDE_DIR,&include_dir_patdata, "include_dir=pattern","include matching directories when recursing" },
   
 #ifdef JFRIEDL_DEBUG  #ifdef JFRIEDL_DEBUG
   { OP_OP_NUMBER, 'S',      &S_arg,            "jeffS",         "replace matched (sub)string with X" },    { OP_OP_NUMBER, 'S',      &S_arg,            "jeffS",         "replace matched (sub)string with X" },
Line 304  static option_item optionlist[] = { Line 392  static option_item optionlist[] = {
 /* Tables for prefixing and suffixing patterns, according to the -w, -x, and -F  /* 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  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  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[] = {  static const char *prefix[] = {
   "", "\\b", "^(?:", "^(?:", "\\Q", "\\b\\Q", "^(?:\\Q", "^(?:\\Q" };    "", "\\b", "^(?:", "^(?:", "\\Q", "\\b\\Q", "^(?:\\Q", "^(?:\\Q" };
Line 344  if (resource_error) Line 434  if (resource_error)
     PCRE_ERROR_JIT_STACKLIMIT);      PCRE_ERROR_JIT_STACKLIMIT);
   fprintf(stderr, "pcregrep: Check your regex for nested unlimited loops.\n");    fprintf(stderr, "pcregrep: Check your regex for nested unlimited loops.\n");
   }    }
   
 exit(rc);  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               *  *            OS-specific functions               *
 *************************************************/  *************************************************/
   
Line 365  although at present the only ones are for Unix, Win32, Line 542  although at present the only ones are for Unix, Win32,
 #include <dirent.h>  #include <dirent.h>
   
 typedef DIR directory_type;  typedef DIR directory_type;
   #define FILESEP '/'
   
 static int  static int
 isdirectory(char *filename)  isdirectory(char *filename)
Line 372  isdirectory(char *filename) Line 550  isdirectory(char *filename)
 struct stat statbuf;  struct stat statbuf;
 if (stat(filename, &statbuf) < 0)  if (stat(filename, &statbuf) < 0)
   return 0;        /* In the expectation that opening as a file will fail */    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 *  static directory_type *
Line 459  BOOL first; Line 637  BOOL first;
 WIN32_FIND_DATA data;  WIN32_FIND_DATA data;
 } directory_type;  } directory_type;
   
   #define FILESEP '/'
   
 int  int
 isdirectory(char *filename)  isdirectory(char *filename)
 {  {
 DWORD attr = GetFileAttributes(filename);  DWORD attr = GetFileAttributes(filename);
 if (attr == INVALID_FILE_ATTRIBUTES)  if (attr == INVALID_FILE_ATTRIBUTES)
   return 0;    return 0;
return ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) ? '/' : 0;return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0;
 }  }
   
 directory_type *  directory_type *
Line 476  char *pattern; Line 656  char *pattern;
 directory_type *dir;  directory_type *dir;
 DWORD err;  DWORD err;
 len = strlen(filename);  len = strlen(filename);
pattern = (char *) malloc(len + 3);pattern = (char *)malloc(len + 3);
dir = (directory_type *) malloc(sizeof(*dir));dir = (directory_type *)malloc(sizeof(*dir));
 if ((pattern == NULL) || (dir == NULL))  if ((pattern == NULL) || (dir == NULL))
   {    {
   fprintf(stderr, "pcregrep: malloc failed\n");    fprintf(stderr, "pcregrep: malloc failed\n");
Line 563  return FALSE; Line 743  return FALSE;
   
 #else  #else
   
   #define FILESEP 0
 typedef void directory_type;  typedef void directory_type;
   
 int isdirectory(char *filename) { return 0; }  int isdirectory(char *filename) { return 0; }
Line 619  return sys_errlist[n]; Line 800  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              *  *            Read one line of input              *
 *************************************************/  *************************************************/
   
Line 734  switch(endlinetype) Line 1139  switch(endlinetype)
   
     switch (c)      switch (c)
       {        {
      case 0x0a:    /* LF */      case '\n':
       *lenptr = 1;        *lenptr = 1;
       return p;        return p;
   
      case 0x0d:    /* CR */      case '\r':
      if (p < endptr && *p == 0x0a)      if (p < endptr && *p == '\n')
         {          {
         *lenptr = 2;          *lenptr = 2;
         p++;          p++;
Line 778  switch(endlinetype) Line 1183  switch(endlinetype)
   
     switch (c)      switch (c)
       {        {
      case 0x0a:    /* LF */      case '\n':    /* LF */
      case 0x0b:    /* VT */      case '\v':    /* VT */
      case 0x0c:    /* FF */      case '\f':    /* FF */
       *lenptr = 1;        *lenptr = 1;
       return p;        return p;
   
      case 0x0d:    /* CR */      case '\r':    /* CR */
      if (p < endptr && *p == 0x0a)      if (p < endptr && *p == '\n')
         {          {
         *lenptr = 2;          *lenptr = 2;
         p++;          p++;
Line 793  switch(endlinetype) Line 1198  switch(endlinetype)
       else *lenptr = 1;        else *lenptr = 1;
       return p;        return p;
   
      case 0x85:    /* NEL */#ifndef EBCDIC
       case 0x85:    /* Unicode NEL */
       *lenptr = utf8? 2 : 1;        *lenptr = utf8? 2 : 1;
       return p;        return p;
   
      case 0x2028:  /* LS */      case 0x2028:  /* Unicode LS */
      case 0x2029:  /* PS */      case 0x2029:  /* Unicode PS */
       *lenptr = 3;        *lenptr = 3;
       return p;        return p;
   #endif  /* Not EBCDIC */
   
       default:        default:
       break;        break;
Line 859  switch(endlinetype) Line 1266  switch(endlinetype)
   
   while (p > startptr)    while (p > startptr)
     {      {
    register int c;    register unsigned int c;
     char *pp = p - 1;      char *pp = p - 1;
   
     if (utf8)      if (utf8)
Line 884  switch(endlinetype) Line 1291  switch(endlinetype)
   
     if (endlinetype == EL_ANYCRLF) switch (c)      if (endlinetype == EL_ANYCRLF) switch (c)
       {        {
      case 0x0a:    /* LF */      case '\n':    /* LF */
      case 0x0d:    /* CR */      case '\r':    /* CR */
       return p;        return p;
   
       default:        default:
Line 894  switch(endlinetype) Line 1301  switch(endlinetype)
   
     else switch (c)      else switch (c)
       {        {
      case 0x0a:    /* LF */      case '\n':    /* LF */
      case 0x0b:    /* VT */      case '\v':    /* VT */
      case 0x0c:    /* FF */      case '\f':    /* FF */
      case 0x0d:    /* CR */      case '\r':    /* CR */
      case 0x85:    /* NEL */#ifndef EBCDIE
      case 0x2028:  /* LS */      case 0x85:    /* Unicode NEL */
      case 0x2029:  /* PS */      case 0x2028:  /* Unicode LS */
       case 0x2029:  /* Unicode PS */
 #endif  /* Not EBCDIC */
       return p;        return p;
   
       default:        default:
Line 935  Arguments: Line 1344  Arguments:
 Returns:            nothing  Returns:            nothing
 */  */
   
static void do_after_lines(int lastmatchnumber, char *lastmatchrestart,static void
  char *endptr, char *printname)do_after_lines(int lastmatchnumber, char *lastmatchrestart, char *endptr,
   char *printname)
 {  {
 if (after_context > 0 && lastmatchnumber > 0)  if (after_context > 0 && lastmatchnumber > 0)
   {    {
Line 968  to find all possible matches. Line 1378  to find all possible matches.
 Arguments:  Arguments:
   matchptr     the start of the subject    matchptr     the start of the subject
   length       the length of the subject to match    length       the length of the subject to match
     options      options for pcre_exec
   startoffset  where to start matching    startoffset  where to start matching
   offsets      the offets vector to fill in    offsets      the offets vector to fill in
   mrc          address of where to put the result of pcre_exec()    mrc          address of where to put the result of pcre_exec()
Line 978  Returns:      TRUE if there was a match Line 1389  Returns:      TRUE if there was a match
 */  */
   
 static BOOL  static BOOL
match_patterns(char *matchptr, size_t length, int startoffset, int *offsets,match_patterns(char *matchptr, size_t length, unsigned int options,
  int *mrc)  int startoffset, int *offsets, int *mrc)
 {  {
 int i;  int i;
 size_t slen = length;  size_t slen = length;
   patstr *p = patterns;
 const char *msg = "this text:\n\n";  const char *msg = "this text:\n\n";
   
 if (slen > 200)  if (slen > 200)
   {    {
   slen = 200;    slen = 200;
   msg = "text that starts:\n\n";    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,  *mrc = pcre_exec(p->compiled, p->hint, matchptr, (int)length,
    startoffset, PCRE_NOTEMPTY, offsets, OFFSET_SIZE);    startoffset, options, offsets, OFFSET_SIZE);
   if (*mrc >= 0) return TRUE;    if (*mrc >= 0) return TRUE;
   if (*mrc == PCRE_ERROR_NOMATCH) continue;    if (*mrc == PCRE_ERROR_NOMATCH) continue;
   fprintf(stderr, "pcregrep: pcre_exec() gave error %d while matching ", *mrc);    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);    fprintf(stderr, "%s", msg);
   FWRITE(matchptr, 1, slen, stderr);   /* In case binary zero included */    FWRITE(matchptr, 1, slen, stderr);   /* In case binary zero included */
   fprintf(stderr, "\n\n");    fprintf(stderr, "\n\n");
Line 1075  of what we have. In the case of libz, a non-zipped .gz Line 1488  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  plain file. However, if a .bz2 file isn't actually bzipped, the first read will
 fail. */  fail. */
   
   (void)frtype;
   
 #ifdef SUPPORT_LIBZ  #ifdef SUPPORT_LIBZ
 if (frtype == FR_LIBZ)  if (frtype == FR_LIBZ)
   {    {
Line 1125  while (ptr < endptr) Line 1540  while (ptr < endptr)
   int endlinelength;    int endlinelength;
   int mrc = 0;    int mrc = 0;
   int startoffset = 0;    int startoffset = 0;
     unsigned int options = 0;
   BOOL match;    BOOL match;
   char *matchptr = ptr;    char *matchptr = ptr;
   char *t = ptr;    char *t = ptr;
Line 1161  while (ptr < endptr) Line 1577  while (ptr < endptr)
 #ifdef JFRIEDL_DEBUG  #ifdef JFRIEDL_DEBUG
   if (jfriedl_XT || jfriedl_XR)    if (jfriedl_XT || jfriedl_XR)
   {    {
      #include <sys/time.h>#     include <sys/time.h>
      #include <time.h>#     include <time.h>
       struct timeval start_time, end_time;        struct timeval start_time, end_time;
       struct timezone dummy;        struct timezone dummy;
       int i;        int i;
Line 1191  while (ptr < endptr) Line 1607  while (ptr < endptr)
   
   
       for (i = 0; i < jfriedl_XR; i++)        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);                PCRE_NOTEMPTY, offsets, OFFSET_SIZE) >= 0);
   
       if (gettimeofday(&end_time, &dummy) != 0)        if (gettimeofday(&end_time, &dummy) != 0)
Line 1206  while (ptr < endptr) Line 1622  while (ptr < endptr)
   }    }
 #endif  #endif
   
  /* We come back here after a match when the -o option (only_matching) is set,  /* We come back here after a match when show_only_matching is set, in order
  in order to find any further matches in the same line. */  to find any further matches in the same line. This applies to
   --only-matching, --file-offsets, and --line-offsets. */
   
   ONLY_MATCHING_RESTART:    ONLY_MATCHING_RESTART:
   
   /* Run through all the patterns until one matches or there is an error other    /* 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    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. */    /* If it's a match or a not-match (as required), do what's wanted. */
   
Line 1254  while (ptr < endptr) Line 1674  while (ptr < endptr)
   
     else if (quiet) return 0;      else if (quiet) return 0;
   
    /* The --only-matching option prints just the substring that matched, or a    /* The --only-matching option prints just the substring that matched,
    captured portion of it, as long as this string is not empty, and the    and/or one or more captured portions of it, as long as these strings are
    --file-offsets and --line-offsets options output offsets for the matching    not empty. The --file-offsets and --line-offsets options output offsets for
    substring (they both force --only-matching = 0). None of these options    the matching substring (all three set show_only_matching). None of these
    prints any context. Afterwards, adjust the start and then jump back to look    mutually exclusive options prints any context. Afterwards, adjust the start
    for further matches in the same line. If we are in invert mode, however,    and then jump back to look for further matches in the same line. If we are
    nothing is printed and we do not restart - this could still be useful    in invert mode, however, nothing is printed and we do not restart - this
    because the return code is set. */    could still be useful because the return code is set. */
   
    else if (only_matching >= 0)    else if (show_only_matching)
       {        {
       if (!invert)        if (!invert)
         {          {
         if (printname != NULL) fprintf(stdout, "%s:", printname);          if (printname != NULL) fprintf(stdout, "%s:", printname);
         if (number) fprintf(stdout, "%d:", linenumber);          if (number) fprintf(stdout, "%d:", linenumber);
   
           /* Handle --line-offsets */
   
         if (line_offsets)          if (line_offsets)
           fprintf(stdout, "%d,%d\n", (int)(matchptr + offsets[0] - ptr),            fprintf(stdout, "%d,%d\n", (int)(matchptr + offsets[0] - ptr),
             offsets[1] - offsets[0]);              offsets[1] - offsets[0]);
   
           /* Handle --file-offsets */
   
         else if (file_offsets)          else if (file_offsets)
           fprintf(stdout, "%d,%d\n",            fprintf(stdout, "%d,%d\n",
             (int)(filepos + matchptr + offsets[0] - ptr),              (int)(filepos + matchptr + offsets[0] - ptr),
             offsets[1] - offsets[0]);              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];          BOOL printed = FALSE;
          if (plen > 0)          omstr *om;
 
           for (om = only_matching; om != NULL; om = om->next)
             {              {
            if (do_colour) fprintf(stdout, "%c[%sm", 0x1b, colour_string);            int n = om->groupnum;
            FWRITE(matchptr + offsets[only_matching*2], 1, plen, stdout);            if (n < mrc)
            if (do_colour) fprintf(stdout, "%c[00m", 0x1b);              {
            fprintf(stdout, "\n");              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;          match = FALSE;
         if (line_buffered) fflush(stdout);          if (line_buffered) fflush(stdout);
         rc = 0;                      /* Had some success */          rc = 0;                      /* Had some success */
Line 1432  while (ptr < endptr) Line 1876  while (ptr < endptr)
           {            {
           startoffset = offsets[1];            startoffset = offsets[1];
           if (startoffset >= (int)linelength + endlinelength ||            if (startoffset >= (int)linelength + endlinelength ||
              !match_patterns(matchptr, length, startoffset, offsets, &mrc))              !match_patterns(matchptr, length, options, startoffset, offsets,
                 &mrc))
             break;              break;
           FWRITE(matchptr + startoffset, 1, offsets[0] - startoffset, stdout);            FWRITE(matchptr + startoffset, 1, offsets[0] - startoffset, stdout);
           fprintf(stdout, "%c[%sm", 0x1b, colour_string);            fprintf(stdout, "%c[%sm", 0x1b, colour_string);
Line 1550  while (ptr < endptr) Line 1995  while (ptr < endptr)
 /* End of file; print final "after" lines if wanted; do_after_lines sets  /* End of file; print final "after" lines if wanted; do_after_lines sets
 hyphenpending if it prints something. */  hyphenpending if it prints something. */
   
if (only_matching < 0 && !count_only)if (!show_only_matching && !count_only)
   {    {
   do_after_lines(lastmatchnumber, lastmatchrestart, endptr, printname);    do_after_lines(lastmatchnumber, lastmatchrestart, endptr, printname);
   hyphenpending |= endhyphenpending;    hyphenpending |= endhyphenpending;
Line 1594  Arguments: Line 2039  Arguments:
   dir_recurse       TRUE if recursing is wanted (-r or -drecurse)    dir_recurse       TRUE if recursing is wanted (-r or -drecurse)
   only_one_at_top   TRUE if the path is the only one at toplevel    only_one_at_top   TRUE if the path is the only one at toplevel
   
Returns:   0 if there was at least one matchReturns:  -1 the file/directory was skipped
            0 if there was at least one match
            1 if there were no matches             1 if there were no matches
            2 there was some kind of error             2 there was some kind of error
   
Line 1605  static int Line 2051  static int
 grep_or_recurse(char *pathname, BOOL dir_recurse, BOOL only_one_at_top)  grep_or_recurse(char *pathname, BOOL dir_recurse, BOOL only_one_at_top)
 {  {
 int rc = 1;  int rc = 1;
 int sep;  
 int frtype;  int frtype;
 void *handle;  void *handle;
   char *lastcomp;
 FILE *in = NULL;           /* Ensure initialized */  FILE *in = NULL;           /* Ensure initialized */
   
 #ifdef SUPPORT_LIBZ  #ifdef SUPPORT_LIBZ
Line 1631  if (strcmp(pathname, "-") == 0) Line 2077  if (strcmp(pathname, "-") == 0)
       stdin_name : NULL);        stdin_name : NULL);
   }    }
   
/* If the file is a directory, skip if skipping or if we are recursing, scan/* Inclusion and exclusion: --include-dir and --exclude-dir apply only to
each file and directory within it, subject to any include or exclude patternsdirectories, whereas --include and --exclude apply to everything else. The test
that were set. The scanning code is localized so it can be madeis against the final component of the path. */
system-specific. */ 
   
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. */
 
 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)    if (dee_action == dee_RECURSE)
     {      {
     char buffer[1024];      char buffer[1024];
Line 1655  if ((sep = isdirectory(pathname)) != 0) Line 2110  if ((sep = isdirectory(pathname)) != 0)
   
     while ((nextfile = readdirectory(dir)) != NULL)      while ((nextfile = readdirectory(dir)) != NULL)
       {        {
      int frc, nflen;      int frc;
      sprintf(buffer, "%.512s%c%.128s", pathname, sep, nextfile);      sprintf(buffer, "%.512s%c%.128s", pathname, FILESEP, 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; 
        } 
 
       frc = grep_or_recurse(buffer, dir_recurse, FALSE);        frc = grep_or_recurse(buffer, dir_recurse, FALSE);
       if (frc > 1) rc = frc;        if (frc > 1) rc = frc;
        else if (frc == 0 && rc == 1) rc = 0;         else if (frc == 0 && rc == 1) rc = 0;
Line 1691  if ((sep = isdirectory(pathname)) != 0) Line 2123  if ((sep = isdirectory(pathname)) != 0)
   }    }
   
 /* If the file is not a directory and not a regular file, skip it if that's  /* If the file is not a directory and not a regular file, skip it if that's
been requested. */been requested. Otherwise, check for explicit include/exclude. */
   
else if (!isregfile(pathname) && DEE_action == DEE_SKIP) return 1;else if ((!isregfile(pathname) && DEE_action == DEE_SKIP) ||
           !test_incexc(lastcomp, include_patterns, exclude_patterns))
         return -1;
   
 /* Control reaches here if we have a regular file, or if we have a directory  /* 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  and recursion or skipping was not requested, or if we have anything else and
Line 1808  return rc; Line 2242  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 && (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("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      *  *    Handle a single-letter, no data option      *
 *************************************************/  *************************************************/
   
Line 1929  switch(letter) Line 2267  switch(letter)
   case 'L': filenames = FN_NOMATCH_ONLY; break;    case 'L': filenames = FN_NOMATCH_ONLY; break;
   case 'M': multiline = TRUE; options |= PCRE_MULTILINE|PCRE_FIRSTLINE; break;    case 'M': multiline = TRUE; options |= PCRE_MULTILINE|PCRE_FIRSTLINE; break;
   case 'n': number = TRUE; 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 'q': quiet = TRUE; break;
   case 'r': dee_action = dee_RECURSE; break;    case 'r': dee_action = dee_RECURSE; break;
   case 's': silent = TRUE; break;    case 's': silent = TRUE; break;
Line 1939  switch(letter) Line 2282  switch(letter)
   case 'x': process_options |= PO_LINE_MATCH; break;    case 'x': process_options |= PO_LINE_MATCH; break;
   
   case 'V':    case 'V':
  fprintf(stderr, "pcregrep version %s\n", pcre_version());  fprintf(stdout, "pcregrep version %s\n", pcre_version());
   pcregrep_exit(0);    pcregrep_exit(0);
   break;    break;
   
Line 1983  return buffer; Line 2326  return buffer;
 *          Compile a single pattern              *  *          Compile a single pattern              *
 *************************************************/  *************************************************/
   
/* When the -F option has been used, this is called for each substring./* Do nothing if the pattern has already been compiled. This is the case for
Otherwise it's called for each supplied pattern.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:  Arguments:
  pattern        the pattern string  p              points to the pattern block
   options        the PCRE options    options        the PCRE options
  filename       the file name, or NULL for a command-line pattern  popts          the processing  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    count          0 if this is the only command line pattern, or
                  number of the command line pattern, or                   number of the command line pattern, or
                  linenumber for a pattern from a file                   linenumber for a pattern from a file
Line 1998  Returns:         TRUE on success, FALSE after an error Line 2348  Returns:         TRUE on success, FALSE after an error
 */  */
   
 static BOOL  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];  char buffer[PATBUFSIZE];
 const char *error;  const char *error;
   char *ps = p->string;
   int patlen = strlen(ps);
 int errptr;  int errptr;
   
if (pattern_count >= MAX_PATTERN_COUNT)if (p->compiled != NULL) return TRUE;
  { 
  fprintf(stderr, "pcregrep: Too many %spatterns (max %d)\n", 
    (filename == NULL)? "command-line " : "", MAX_PATTERN_COUNT); 
  return FALSE; 
  } 
   
sprintf(buffer, "%s%.*s%s", prefix[process_options], bufthird, pattern,if ((popts & PO_FIXED_STRINGS) != 0)
  suffix[process_options]); 
pattern_list[pattern_count] = 
  pcre_compile(buffer, options, &error, &errptr, pcretables); 
if (pattern_list[pattern_count] != NULL) 
   {    {
  pattern_count++;  int ellength;
  return TRUE;  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 */  /* Handle compile errors */
   
errptr -= (int)strlen(prefix[process_options]);errptr -= (int)strlen(prefix[popts]);
if (errptr > (int)strlen(pattern)) errptr = (int)strlen(pattern);if (errptr > patlen) errptr = patlen;
   
if (filename == NULL)if (fromfile)
   {    {
  if (count == 0)  fprintf(stderr, "pcregrep: Error in regex in line %d of %s "
    fprintf(stderr, "pcregrep: Error in command-line regex "    "at offset %d: %s\n", count, fromtext, errptr, error);
      "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); 
   }    }
 else  else
   {    {
  fprintf(stderr, "pcregrep: Error in regex in line %d of %s "  if (count == 0)
    "at offset %d: %s\n", count, filename, errptr, error);    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;  return FALSE;
Line 2047  return FALSE; Line 2402  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,/* This is used for --filelist, --include-from, and --exclude-from.
separated by line breaks. They will be matched literally. 
   
 Arguments:  Arguments:
  pattern        the pattern string  name         the name of the file; "-" is stdin
  options        the PCRE options  patptr       pointer to the pattern chain anchor
  filename       the file name, or NULL for a command-line pattern  patlastptr   pointer to the last pattern pointer
  count          0 if this is the only command line pattern, or  popts        the process options to pass to pattern_compile()
                 number of the command line pattern, or 
                 linenumber for a pattern from a file 
   
Returns:         TRUE on success, FALSE after an errorReturns:       TRUE if all went well
 */  */
   
 static BOOL  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);  f = stdin;
  char buffer[PATBUFSIZE];  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(;;)    for(;;)
     {      {
    int ellength;    if (!compile_pattern(*patlastptr, pcre_options, popts, TRUE, filename,
    char *p = end_of_line(pattern, eop, &ellength);        linenumber))
    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)) 
       return FALSE;        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;
 }  }
   
   
Line 2099  main(int argc, char **argv) Line 2490  main(int argc, char **argv)
 {  {
 int i, j;  int i, j;
 int rc = 1;  int rc = 1;
 int pcre_options = 0;  
 int cmd_pattern_count = 0;  
 int hint_count = 0;  
 int errptr;  
 BOOL only_one_at_top;  BOOL only_one_at_top;
char *patterns[MAX_PATTERN_COUNT];patstr *cp;
 fnstr *fn;
 const char *locale_from = "--locale";  const char *locale_from = "--locale";
 const char *error;  const char *error;
   
Line 2144  for (i = 1; i < argc; i++) Line 2532  for (i = 1; i < argc; i++)
   
   if (argv[i][1] == 0)    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));        else pcregrep_exit(usage(2));
     }      }
   
Line 2270  for (i = 1; i < argc; i++) Line 2658  for (i = 1; i < argc; i++)
     {      {
     char *s = argv[i] + 1;      char *s = argv[i] + 1;
     longop = FALSE;      longop = FALSE;
   
     while (*s != 0)      while (*s != 0)
       {        {
       for (op = optionlist; op->one_char != 0; op++)        for (op = optionlist; op->one_char != 0; op++)
Line 2283  for (i = 1; i < argc; i++) Line 2672  for (i = 1; i < argc; i++)
         pcregrep_exit(usage(2));          pcregrep_exit(usage(2));
         }          }
   
      /* Check for a single-character option that has data: OP_OP_NUMBER      option_data = s+1;
      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      /* 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. */        with other single-character options in the same string. */
   
      option_data = s+1;      if (op->type == OP_OP_NUMBER || op->type == OP_OP_NUMBERS)
      if (op->type == OP_OP_NUMBER) 
         {          {
         if (isdigit((unsigned char)s[1])) break;          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        /* Handle a single-character option with no data, then loop for the
Line 2315  for (i = 1; i < argc; i++) Line 2710  for (i = 1; i < argc; i++)
     continue;      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    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",    separate item. At the moment, the only such options are "colo(u)r",
   "only-matching", and Jeffrey Friedl's special -S debugging option. */    "only-matching", and Jeffrey Friedl's special -S debugging option. */
   
   if (*option_data == 0 &&    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)      switch (op->one_char)
       {        {
Line 2330  for (i = 1; i < argc; i++) Line 2726  for (i = 1; i < argc; i++)
       break;        break;
   
       case 'o':        case 'o':
      only_matching = 0;      only_matching_last = add_number(0, only_matching_last);
       if (only_matching == NULL) only_matching = only_matching_last;
       break;        break;
   
 #ifdef JFRIEDL_DEBUG  #ifdef JFRIEDL_DEBUG
Line 2354  for (i = 1; i < argc; i++) Line 2751  for (i = 1; i < argc; i++)
     option_data = argv[++i];      option_data = argv[++i];
     }      }
   
  /* If the option type is OP_PATLIST, it's the -e option, which can be called  /* If the option type is OP_OP_NUMBERS, the value is a number that is to be
  multiple times to create a list of patterns. */  added to a chain of numbers. */
   
  if (op->type == OP_PATLIST)  if (op->type == OP_OP_NUMBERS)
     {      {
    if (cmd_pattern_count >= MAX_PATTERN_COUNT)    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);
     }
 
   /* 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_PATLIST)
     {
     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);
     }
 
   /* If the option type is OP_FILELIST, it's one of the options that names a
   file. */
 
   else if (op->type == OP_FILELIST)
     {
     fndatastr *fd = (fndatastr *)op->dataptr;
     fn = (fnstr *)malloc(sizeof(fnstr));
     if (fn == NULL)
       {        {
      fprintf(stderr, "pcregrep: Too many command-line patterns (max %d)\n",      fprintf(stderr, "pcregrep: malloc failed\n");
        MAX_PATTERN_COUNT);      goto EXIT2;
      return 2; 
       }        }
    patterns[cmd_pattern_count++] = option_data;    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 */    /* Handle OP_BINARY_FILES */
Line 2386  for (i = 1; i < argc; i++) Line 2813  for (i = 1; i < argc; i++)
       }        }
     }      }
   
  /* Otherwise, deal with single string or numeric data values. */  /* Otherwise, deal with a single string or numeric data value. */
   
   else if (op->type != OP_NUMBER && op->type != OP_LONGNUMBER &&    else if (op->type != OP_NUMBER && op->type != OP_LONGNUMBER &&
            op->type != OP_OP_NUMBER)             op->type != OP_OP_NUMBER)
     {      {
     *((char **)op->dataptr) = option_data;      *((char **)op->dataptr) = option_data;
     }      }
   
   /* Avoid the use of strtoul() because SunOS4 doesn't have it. This is used  
   only for unpicking arguments, so just keep it simple. */  
   
   else    else
     {      {
    unsigned long int n = 0;    unsigned long int n = decode_number(option_data, op, longop);
    char *endptr = option_data;    if (op->type == OP_LONGNUMBER) *((unsigned long int *)op->dataptr) = n;
    while (*endptr != 0 && isspace((unsigned char)(*endptr))) endptr++;      else *((int *)op->dataptr) = n;
    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) 
      { 
      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)); 
      } 
    if (op->type == OP_LONGNUMBER) 
        *((unsigned long int *)op->dataptr) = n; 
    else 
        *((int *)op->dataptr) = n; 
     }      }
   }    }
   
Line 2446  if (both_context > 0) Line 2838  if (both_context > 0)
   }    }
   
 /* Only one of --only-matching, --file-offsets, or --line-offsets is permitted.  /* 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))      (file_offsets && line_offsets))
   {    {
   fprintf(stderr, "pcregrep: Cannot mix --only-matching, --file-offsets "    fprintf(stderr, "pcregrep: Cannot mix --only-matching, --file-offsets "
Line 2456  if ((only_matching >= 0 && (file_offsets || line_offse Line 2849  if ((only_matching >= 0 && (file_offsets || line_offse
   pcregrep_exit(usage(2));    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  /* 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. */  LC_ALL environment variable is set, and if so, use it. */
Line 2580  if (jfriedl_XT != 0 || jfriedl_XR != 0) Line 2974  if (jfriedl_XT != 0 || jfriedl_XR != 0)
   }    }
 #endif  #endif
   
/* Get memory for the main buffer, and to store the pattern and hints lists. *//* Get memory for the main buffer. */
   
 bufsize = 3*bufthird;  bufsize = 3*bufthird;
 main_buffer = (char *)malloc(bufsize);  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");    fprintf(stderr, "pcregrep: malloc failed\n");
   goto EXIT2;    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. */  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);    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  /* 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,  if (!compile_pattern(cp, pcre_options, process_options, FALSE, "command-line",
       (j == 0 && cmd_pattern_count == 1)? 0 : j + 1))       (j == 1 && patterns->next == NULL)? 0 : j))
     goto EXIT2;      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;  if (!read_pattern_file(fn->name, &patterns, &patterns_last, process_options))
  FILE *f;    goto EXIT2;
  char *filename;  }
  char buffer[PATBUFSIZE]; 
   
  if (strcmp(pattern_filename, "-") == 0)/* 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
    f = stdin;returned, even if studying produces no data. */
    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; 
    } 
   
  while (fgets(buffer, PATBUFSIZE, f) != NULL)if (match_limit > 0 || match_limit_recursion > 0)
    {  study_options |= PCRE_STUDY_EXTRA_NEEDED;
    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 (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  #ifdef SUPPORT_PCREGREP_JIT
 if ((study_options & PCRE_STUDY_JIT_COMPILE) != 0)  if ((study_options & PCRE_STUDY_JIT_COMPILE) != 0)
   jit_stack = pcre_jit_stack_alloc(32*1024, 1024*1024);    jit_stack = pcre_jit_stack_alloc(32*1024, 1024*1024);
 #endif  #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)    if (error != NULL)
     {      {
     char s[16];      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);      fprintf(stderr, "pcregrep: Error while studying regex%s: %s\n", s, error);
     goto EXIT2;      goto EXIT2;
     }      }
   hint_count++;  
 #ifdef SUPPORT_PCREGREP_JIT  #ifdef SUPPORT_PCREGREP_JIT
  if (jit_stack != NULL && hints_list[j] != NULL)  if (jit_stack != NULL && cp->hint != NULL)
    pcre_assign_jit_stack(hints_list[j], NULL, jit_stack);    pcre_assign_jit_stack(cp->hint, NULL, jit_stack);
 #endif  #endif
   }    }
   
 /* If --match-limit or --recursion-limit was set, put the value(s) into the  /* 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)    cp->hint->flags |= PCRE_EXTRA_MATCH_LIMIT;
      {    cp->hint->match_limit = match_limit;
      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; 
      } 
     }      }
   
     if (match_limit_recursion > 0)
       {
       cp->hint->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
       cp->hint->match_limit_recursion = match_limit_recursion;
       }
   }    }
   
/* If there are include or exclude patterns, compile them. *//* 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. */
   
if (exclude_pattern != NULL)for (j = 0; j < 4; j++)
   {    {
  exclude_compiled = pcre_compile(exclude_pattern, 0, &error, &errptr,  int k;
    pcretables);  for (k = 1, cp = *(incexlist[j]); cp != NULL; k++, cp = cp->next)
  if (exclude_compiled == NULL) 
     {      {
    fprintf(stderr, "pcregrep: Error in 'exclude' regex at offset %d: %s\n",    if (!compile_pattern(cp, pcre_options, 0, FALSE, incexname[j],
      errptr, error);         (k == 1 && cp->next == NULL)? 0 : k))
    goto EXIT2;      goto EXIT2;
     }      }
   }    }
   
if (include_pattern != NULL)/* Read and compile include/exclude patterns from files. */
 
 for (fn = include_from; fn != NULL; fn = fn->next)
   {    {
  include_compiled = pcre_compile(include_pattern, 0, &error, &errptr,  if (!read_pattern_file(fn->name, &include_patterns, &include_patterns_last, 0))
    pcretables); 
  if (include_compiled == NULL) 
    { 
    fprintf(stderr, "pcregrep: Error in 'include' regex at offset %d: %s\n", 
      errptr, error); 
     goto EXIT2;      goto EXIT2;
     }  
   }    }
   
if (exclude_dir_pattern != NULL)for (fn = exclude_from; fn != NULL; fn = fn->next)
   {    {
  exclude_dir_compiled = pcre_compile(exclude_dir_pattern, 0, &error, &errptr,  if (!read_pattern_file(fn->name, &exclude_patterns, &exclude_patterns_last, 0))
    pcretables); 
  if (exclude_dir_compiled == NULL) 
    { 
    fprintf(stderr, "pcregrep: Error in 'exclude_dir' regex at offset %d: %s\n", 
      errptr, error); 
     goto EXIT2;      goto EXIT2;
     }  
   }    }
   
if (include_dir_pattern != NULL)/* If there are no files that contain lists of files to search, and there are
 no file arguments, search stdin, and then exit. */
 
 if (file_lists == NULL && i >= argc)
   {    {
  include_dir_compiled = pcre_compile(include_dir_pattern, 0, &error, &errptr,  rc = pcregrep(stdin, FR_PLAIN, stdin_name,
    pcretables);    (filenames > FN_DEFAULT)? stdin_name : NULL);
  if (include_dir_compiled == NULL)  goto EXIT;
    { 
    fprintf(stderr, "pcregrep: Error in 'include_dir' regex at offset %d: %s\n", 
      errptr, error); 
    goto EXIT2; 
    } 
   }    }
   
/* If a file that contains a list of files to search has been specified, read/* If any files that contains a list of files to search have been specified,
it line by line and search the given files. Otherwise, if there are no furtherread them line by line and search the given files. */
arguments, do the business on stdin and exit. */ 
   
if (file_list != NULL)for (fn = file_lists; fn != NULL; fn = fn->next)
   {    {
   char buffer[PATBUFSIZE];    char buffer[PATBUFSIZE];
   FILE *fl;    FILE *fl;
  if (strcmp(file_list, "-") == 0) fl = stdin; else  if (strcmp(fn->name, "-") == 0) fl = stdin; else
     {      {
    fl = fopen(file_list, "rb");    fl = fopen(fn->name, "rb");
     if (fl == NULL)      if (fl == NULL)
       {        {
      fprintf(stderr, "pcregrep: Failed to open %s: %s\n", file_list,      fprintf(stderr, "pcregrep: Failed to open %s: %s\n", fn->name,
         strerror(errno));          strerror(errno));
       goto EXIT2;        goto EXIT2;
       }        }
Line 2787  if (file_list != NULL) Line 3134  if (file_list != NULL)
         else if (frc == 0 && rc == 1) rc = 0;          else if (frc == 0 && rc == 1) rc = 0;
       }        }
     }      }
  if (fl != stdin) fclose (fl);  if (fl != stdin) fclose(fl);
   }    }
   
/* Do this only if there was no file list (and no file arguments). *//* 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. */
   
else if (i >= argc)only_one_at_top = i == argc - 1 && file_lists == NULL;
  { 
  rc = pcregrep(stdin, FR_PLAIN, stdin_name, 
    (filenames > FN_DEFAULT)? stdin_name : NULL); 
  goto EXIT; 
  } 
   
 /* After handling file-list or if there are remaining arguments, work through  
 them 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. */  
   
 only_one_at_top = i == argc - 1 && file_list == NULL;  
   
 for (; i < argc; i++)  for (; i < argc; i++)
   {    {
   int frc = grep_or_recurse(argv[i], dee_action == dee_RECURSE,    int frc = grep_or_recurse(argv[i], dee_action == dee_RECURSE,
Line 2818  EXIT: Line 3155  EXIT:
 #ifdef SUPPORT_PCREGREP_JIT  #ifdef SUPPORT_PCREGREP_JIT
 if (jit_stack != NULL) pcre_jit_stack_free(jit_stack);  if (jit_stack != NULL) pcre_jit_stack_free(jit_stack);
 #endif  #endif
   
 if (main_buffer != NULL) free(main_buffer);  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]);  omstr *this = only_matching;
  free(pattern_list);  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);  pcregrep_exit(rc);
   
 EXIT2:  EXIT2:

Removed from v.1.1.1.3  
changed lines
  Added in v.1.1.1.4


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>