tools/pcre/pcregrep.c
author Nicholas Hastings <skamonkey@gmail.com>
Sun Jul 15 12:31:06 2012 -0400 (2012-07-15)
changeset 3579 6d730190a3e4
parent 2762 d2d467bf1b5a
child 3831 babd0eee3c5e
permissions -rw-r--r--
Added missing files to each core and logic makefiles (NPOTB).
dvander@2762
     1
/*************************************************
dvander@2762
     2
*               pcregrep program                 *
dvander@2762
     3
*************************************************/
dvander@2762
     4
dvander@2762
     5
/* This is a grep program that uses the PCRE regular expression library to do
dvander@2762
     6
its pattern matching. On a Unix or Win32 system it can recurse into
dvander@2762
     7
directories.
dvander@2762
     8
dvander@2762
     9
           Copyright (c) 1997-2009 University of Cambridge
dvander@2762
    10
dvander@2762
    11
-----------------------------------------------------------------------------
dvander@2762
    12
Redistribution and use in source and binary forms, with or without
dvander@2762
    13
modification, are permitted provided that the following conditions are met:
dvander@2762
    14
dvander@2762
    15
    * Redistributions of source code must retain the above copyright notice,
dvander@2762
    16
      this list of conditions and the following disclaimer.
dvander@2762
    17
dvander@2762
    18
    * Redistributions in binary form must reproduce the above copyright
dvander@2762
    19
      notice, this list of conditions and the following disclaimer in the
dvander@2762
    20
      documentation and/or other materials provided with the distribution.
dvander@2762
    21
dvander@2762
    22
    * Neither the name of the University of Cambridge nor the names of its
dvander@2762
    23
      contributors may be used to endorse or promote products derived from
dvander@2762
    24
      this software without specific prior written permission.
dvander@2762
    25
dvander@2762
    26
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
dvander@2762
    27
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
dvander@2762
    28
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
dvander@2762
    29
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
dvander@2762
    30
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
dvander@2762
    31
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
dvander@2762
    32
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
dvander@2762
    33
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
dvander@2762
    34
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
dvander@2762
    35
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
dvander@2762
    36
POSSIBILITY OF SUCH DAMAGE.
dvander@2762
    37
-----------------------------------------------------------------------------
dvander@2762
    38
*/
dvander@2762
    39
dvander@2762
    40
#ifdef HAVE_CONFIG_H
dvander@2762
    41
#include "config.h"
dvander@2762
    42
#endif
dvander@2762
    43
dvander@2762
    44
#include <ctype.h>
dvander@2762
    45
#include <locale.h>
dvander@2762
    46
#include <stdio.h>
dvander@2762
    47
#include <string.h>
dvander@2762
    48
#include <stdlib.h>
dvander@2762
    49
#include <errno.h>
dvander@2762
    50
dvander@2762
    51
#include <sys/types.h>
dvander@2762
    52
#include <sys/stat.h>
dvander@2762
    53
dvander@2762
    54
#ifdef HAVE_UNISTD_H
dvander@2762
    55
#include <unistd.h>
dvander@2762
    56
#endif
dvander@2762
    57
dvander@2762
    58
#ifdef SUPPORT_LIBZ
dvander@2762
    59
#include <zlib.h>
dvander@2762
    60
#endif
dvander@2762
    61
dvander@2762
    62
#ifdef SUPPORT_LIBBZ2
dvander@2762
    63
#include <bzlib.h>
dvander@2762
    64
#endif
dvander@2762
    65
dvander@2762
    66
#include "pcre.h"
dvander@2762
    67
dvander@2762
    68
#define FALSE 0
dvander@2762
    69
#define TRUE 1
dvander@2762
    70
dvander@2762
    71
typedef int BOOL;
dvander@2762
    72
dvander@2762
    73
#define MAX_PATTERN_COUNT 100
dvander@2762
    74
#define OFFSET_SIZE 99
dvander@2762
    75
dvander@2762
    76
#if BUFSIZ > 8192
dvander@2762
    77
#define MBUFTHIRD BUFSIZ
dvander@2762
    78
#else
dvander@2762
    79
#define MBUFTHIRD 8192
dvander@2762
    80
#endif
dvander@2762
    81
dvander@2762
    82
/* Values for the "filenames" variable, which specifies options for file name
dvander@2762
    83
output. The order is important; it is assumed that a file name is wanted for
dvander@2762
    84
all values greater than FN_DEFAULT. */
dvander@2762
    85
dvander@2762
    86
enum { FN_NONE, FN_DEFAULT, FN_ONLY, FN_NOMATCH_ONLY, FN_FORCE };
dvander@2762
    87
dvander@2762
    88
/* File reading styles */
dvander@2762
    89
dvander@2762
    90
enum { FR_PLAIN, FR_LIBZ, FR_LIBBZ2 };
dvander@2762
    91
dvander@2762
    92
/* Actions for the -d and -D options */
dvander@2762
    93
dvander@2762
    94
enum { dee_READ, dee_SKIP, dee_RECURSE };
dvander@2762
    95
enum { DEE_READ, DEE_SKIP };
dvander@2762
    96
dvander@2762
    97
/* Actions for special processing options (flag bits) */
dvander@2762
    98
dvander@2762
    99
#define PO_WORD_MATCH     0x0001
dvander@2762
   100
#define PO_LINE_MATCH     0x0002
dvander@2762
   101
#define PO_FIXED_STRINGS  0x0004
dvander@2762
   102
dvander@2762
   103
/* Line ending types */
dvander@2762
   104
dvander@2762
   105
enum { EL_LF, EL_CR, EL_CRLF, EL_ANY, EL_ANYCRLF };
dvander@2762
   106
dvander@2762
   107
dvander@2762
   108
dvander@2762
   109
/*************************************************
dvander@2762
   110
*               Global variables                 *
dvander@2762
   111
*************************************************/
dvander@2762
   112
dvander@2762
   113
/* Jeffrey Friedl has some debugging requirements that are not part of the
dvander@2762
   114
regular code. */
dvander@2762
   115
dvander@2762
   116
#ifdef JFRIEDL_DEBUG
dvander@2762
   117
static int S_arg = -1;
dvander@2762
   118
static unsigned int jfriedl_XR = 0; /* repeat regex attempt this many times */
dvander@2762
   119
static unsigned int jfriedl_XT = 0; /* replicate text this many times */
dvander@2762
   120
static const char *jfriedl_prefix = "";
dvander@2762
   121
static const char *jfriedl_postfix = "";
dvander@2762
   122
#endif
dvander@2762
   123
dvander@2762
   124
static int  endlinetype;
dvander@2762
   125
dvander@2762
   126
static char *colour_string = (char *)"1;31";
dvander@2762
   127
static char *colour_option = NULL;
dvander@2762
   128
static char *dee_option = NULL;
dvander@2762
   129
static char *DEE_option = NULL;
dvander@2762
   130
static char *newline = NULL;
dvander@2762
   131
static char *pattern_filename = NULL;
dvander@2762
   132
static char *stdin_name = (char *)"(standard input)";
dvander@2762
   133
static char *locale = NULL;
dvander@2762
   134
dvander@2762
   135
static const unsigned char *pcretables = NULL;
dvander@2762
   136
dvander@2762
   137
static int  pattern_count = 0;
dvander@2762
   138
static pcre **pattern_list = NULL;
dvander@2762
   139
static pcre_extra **hints_list = NULL;
dvander@2762
   140
dvander@2762
   141
static char *include_pattern = NULL;
dvander@2762
   142
static char *exclude_pattern = NULL;
dvander@2762
   143
static char *include_dir_pattern = NULL;
dvander@2762
   144
static char *exclude_dir_pattern = NULL;
dvander@2762
   145
dvander@2762
   146
static pcre *include_compiled = NULL;
dvander@2762
   147
static pcre *exclude_compiled = NULL;
dvander@2762
   148
static pcre *include_dir_compiled = NULL;
dvander@2762
   149
static pcre *exclude_dir_compiled = NULL;
dvander@2762
   150
dvander@2762
   151
static int after_context = 0;
dvander@2762
   152
static int before_context = 0;
dvander@2762
   153
static int both_context = 0;
dvander@2762
   154
static int dee_action = dee_READ;
dvander@2762
   155
static int DEE_action = DEE_READ;
dvander@2762
   156
static int error_count = 0;
dvander@2762
   157
static int filenames = FN_DEFAULT;
dvander@2762
   158
static int process_options = 0;
dvander@2762
   159
dvander@2762
   160
static BOOL count_only = FALSE;
dvander@2762
   161
static BOOL do_colour = FALSE;
dvander@2762
   162
static BOOL file_offsets = FALSE;
dvander@2762
   163
static BOOL hyphenpending = FALSE;
dvander@2762
   164
static BOOL invert = FALSE;
dvander@2762
   165
static BOOL line_offsets = FALSE;
dvander@2762
   166
static BOOL multiline = FALSE;
dvander@2762
   167
static BOOL number = FALSE;
dvander@2762
   168
static BOOL only_matching = FALSE;
dvander@2762
   169
static BOOL quiet = FALSE;
dvander@2762
   170
static BOOL silent = FALSE;
dvander@2762
   171
static BOOL utf8 = FALSE;
dvander@2762
   172
dvander@2762
   173
/* Structure for options and list of them */
dvander@2762
   174
dvander@2762
   175
enum { OP_NODATA, OP_STRING, OP_OP_STRING, OP_NUMBER, OP_OP_NUMBER,
dvander@2762
   176
       OP_PATLIST };
dvander@2762
   177
dvander@2762
   178
typedef struct option_item {
dvander@2762
   179
  int type;
dvander@2762
   180
  int one_char;
dvander@2762
   181
  void *dataptr;
dvander@2762
   182
  const char *long_name;
dvander@2762
   183
  const char *help_text;
dvander@2762
   184
} option_item;
dvander@2762
   185
dvander@2762
   186
/* Options without a single-letter equivalent get a negative value. This can be
dvander@2762
   187
used to identify them. */
dvander@2762
   188
dvander@2762
   189
#define N_COLOUR       (-1)
dvander@2762
   190
#define N_EXCLUDE      (-2)
dvander@2762
   191
#define N_EXCLUDE_DIR  (-3)
dvander@2762
   192
#define N_HELP         (-4)
dvander@2762
   193
#define N_INCLUDE      (-5)
dvander@2762
   194
#define N_INCLUDE_DIR  (-6)
dvander@2762
   195
#define N_LABEL        (-7)
dvander@2762
   196
#define N_LOCALE       (-8)
dvander@2762
   197
#define N_NULL         (-9)
dvander@2762
   198
#define N_LOFFSETS     (-10)
dvander@2762
   199
#define N_FOFFSETS     (-11)
dvander@2762
   200
dvander@2762
   201
static option_item optionlist[] = {
dvander@2762
   202
  { OP_NODATA,    N_NULL,   NULL,              "",              "  terminate options" },
dvander@2762
   203
  { OP_NODATA,    N_HELP,   NULL,              "help",          "display this help and exit" },
dvander@2762
   204
  { OP_NUMBER,    'A',      &after_context,    "after-context=number", "set number of following context lines" },
dvander@2762
   205
  { OP_NUMBER,    'B',      &before_context,   "before-context=number", "set number of prior context lines" },
dvander@2762
   206
  { OP_OP_STRING, N_COLOUR, &colour_option,    "color=option",  "matched text color option" },
dvander@2762
   207
  { OP_NUMBER,    'C',      &both_context,     "context=number", "set number of context lines, before & after" },
dvander@2762
   208
  { OP_NODATA,    'c',      NULL,              "count",         "print only a count of matching lines per FILE" },
dvander@2762
   209
  { OP_OP_STRING, N_COLOUR, &colour_option,    "colour=option", "matched text colour option" },
dvander@2762
   210
  { OP_STRING,    'D',      &DEE_option,       "devices=action","how to handle devices, FIFOs, and sockets" },
dvander@2762
   211
  { OP_STRING,    'd',      &dee_option,       "directories=action", "how to handle directories" },
dvander@2762
   212
  { OP_PATLIST,   'e',      NULL,              "regex(p)",      "specify pattern (may be used more than once)" },
dvander@2762
   213
  { OP_NODATA,    'F',      NULL,              "fixed_strings", "patterns are sets of newline-separated strings" },
dvander@2762
   214
  { OP_STRING,    'f',      &pattern_filename, "file=path",     "read patterns from file" },
dvander@2762
   215
  { OP_NODATA,    N_FOFFSETS, NULL,            "file-offsets",  "output file offsets, not text" },
dvander@2762
   216
  { OP_NODATA,    'H',      NULL,              "with-filename", "force the prefixing filename on output" },
dvander@2762
   217
  { OP_NODATA,    'h',      NULL,              "no-filename",   "suppress the prefixing filename on output" },
dvander@2762
   218
  { OP_NODATA,    'i',      NULL,              "ignore-case",   "ignore case distinctions" },
dvander@2762
   219
  { OP_NODATA,    'l',      NULL,              "files-with-matches", "print only FILE names containing matches" },
dvander@2762
   220
  { OP_NODATA,    'L',      NULL,              "files-without-match","print only FILE names not containing matches" },
dvander@2762
   221
  { OP_STRING,    N_LABEL,  &stdin_name,       "label=name",    "set name for standard input" },
dvander@2762
   222
  { OP_NODATA,    N_LOFFSETS, NULL,            "line-offsets",  "output line numbers and offsets, not text" },
dvander@2762
   223
  { OP_STRING,    N_LOCALE, &locale,           "locale=locale", "use the named locale" },
dvander@2762
   224
  { OP_NODATA,    'M',      NULL,              "multiline",     "run in multiline mode" },
dvander@2762
   225
  { OP_STRING,    'N',      &newline,          "newline=type",  "set newline type (CR, LF, CRLF, ANYCRLF or ANY)" },
dvander@2762
   226
  { OP_NODATA,    'n',      NULL,              "line-number",   "print line number with output lines" },
dvander@2762
   227
  { OP_NODATA,    'o',      NULL,              "only-matching", "show only the part of the line that matched" },
dvander@2762
   228
  { OP_NODATA,    'q',      NULL,              "quiet",         "suppress output, just set return code" },
dvander@2762
   229
  { OP_NODATA,    'r',      NULL,              "recursive",     "recursively scan sub-directories" },
dvander@2762
   230
  { OP_STRING,    N_EXCLUDE,&exclude_pattern,  "exclude=pattern","exclude matching files when recursing" },
dvander@2762
   231
  { OP_STRING,    N_INCLUDE,&include_pattern,  "include=pattern","include matching files when recursing" },
dvander@2762
   232
  { OP_STRING,    N_EXCLUDE_DIR,&exclude_dir_pattern, "exclude_dir=pattern","exclude matching directories when recursing" },
dvander@2762
   233
  { OP_STRING,    N_INCLUDE_DIR,&include_dir_pattern, "include_dir=pattern","include matching directories when recursing" },
dvander@2762
   234
#ifdef JFRIEDL_DEBUG
dvander@2762
   235
  { OP_OP_NUMBER, 'S',      &S_arg,            "jeffS",         "replace matched (sub)string with X" },
dvander@2762
   236
#endif
dvander@2762
   237
  { OP_NODATA,    's',      NULL,              "no-messages",   "suppress error messages" },
dvander@2762
   238
  { OP_NODATA,    'u',      NULL,              "utf-8",         "use UTF-8 mode" },
dvander@2762
   239
  { OP_NODATA,    'V',      NULL,              "version",       "print version information and exit" },
dvander@2762
   240
  { OP_NODATA,    'v',      NULL,              "invert-match",  "select non-matching lines" },
dvander@2762
   241
  { OP_NODATA,    'w',      NULL,              "word-regex(p)", "force patterns to match only as words"  },
dvander@2762
   242
  { OP_NODATA,    'x',      NULL,              "line-regex(p)", "force patterns to match only whole lines" },
dvander@2762
   243
  { OP_NODATA,    0,        NULL,               NULL,            NULL }
dvander@2762
   244
};
dvander@2762
   245
dvander@2762
   246
/* Tables for prefixing and suffixing patterns, according to the -w, -x, and -F
dvander@2762
   247
options. These set the 1, 2, and 4 bits in process_options, respectively. Note
dvander@2762
   248
that the combination of -w and -x has the same effect as -x on its own, so we
dvander@2762
   249
can treat them as the same. */
dvander@2762
   250
dvander@2762
   251
static const char *prefix[] = {
dvander@2762
   252
  "", "\\b", "^(?:", "^(?:", "\\Q", "\\b\\Q", "^(?:\\Q", "^(?:\\Q" };
dvander@2762
   253
dvander@2762
   254
static const char *suffix[] = {
dvander@2762
   255
  "", "\\b", ")$",   ")$",   "\\E", "\\E\\b", "\\E)$",   "\\E)$" };
dvander@2762
   256
dvander@2762
   257
/* UTF-8 tables - used only when the newline setting is "any". */
dvander@2762
   258
dvander@2762
   259
const int utf8_table3[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01};
dvander@2762
   260
dvander@2762
   261
const char utf8_table4[] = {
dvander@2762
   262
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
dvander@2762
   263
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
dvander@2762
   264
  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
dvander@2762
   265
  3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 };
dvander@2762
   266
dvander@2762
   267
dvander@2762
   268
dvander@2762
   269
/*************************************************
dvander@2762
   270
*            OS-specific functions               *
dvander@2762
   271
*************************************************/
dvander@2762
   272
dvander@2762
   273
/* These functions are defined so that they can be made system specific,
dvander@2762
   274
although at present the only ones are for Unix, Win32, and for "no support". */
dvander@2762
   275
dvander@2762
   276
dvander@2762
   277
/************* Directory scanning in Unix ***********/
dvander@2762
   278
dvander@2762
   279
#if defined HAVE_SYS_STAT_H && defined HAVE_DIRENT_H && defined HAVE_SYS_TYPES_H
dvander@2762
   280
#include <sys/types.h>
dvander@2762
   281
#include <sys/stat.h>
dvander@2762
   282
#include <dirent.h>
dvander@2762
   283
dvander@2762
   284
typedef DIR directory_type;
dvander@2762
   285
dvander@2762
   286
static int
dvander@2762
   287
isdirectory(char *filename)
dvander@2762
   288
{
dvander@2762
   289
struct stat statbuf;
dvander@2762
   290
if (stat(filename, &statbuf) < 0)
dvander@2762
   291
  return 0;        /* In the expectation that opening as a file will fail */
dvander@2762
   292
return ((statbuf.st_mode & S_IFMT) == S_IFDIR)? '/' : 0;
dvander@2762
   293
}
dvander@2762
   294
dvander@2762
   295
static directory_type *
dvander@2762
   296
opendirectory(char *filename)
dvander@2762
   297
{
dvander@2762
   298
return opendir(filename);
dvander@2762
   299
}
dvander@2762
   300
dvander@2762
   301
static char *
dvander@2762
   302
readdirectory(directory_type *dir)
dvander@2762
   303
{
dvander@2762
   304
for (;;)
dvander@2762
   305
  {
dvander@2762
   306
  struct dirent *dent = readdir(dir);
dvander@2762
   307
  if (dent == NULL) return NULL;
dvander@2762
   308
  if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0)
dvander@2762
   309
    return dent->d_name;
dvander@2762
   310
  }
dvander@2762
   311
/* Control never reaches here */
dvander@2762
   312
}
dvander@2762
   313
dvander@2762
   314
static void
dvander@2762
   315
closedirectory(directory_type *dir)
dvander@2762
   316
{
dvander@2762
   317
closedir(dir);
dvander@2762
   318
}
dvander@2762
   319
dvander@2762
   320
dvander@2762
   321
/************* Test for regular file in Unix **********/
dvander@2762
   322
dvander@2762
   323
static int
dvander@2762
   324
isregfile(char *filename)
dvander@2762
   325
{
dvander@2762
   326
struct stat statbuf;
dvander@2762
   327
if (stat(filename, &statbuf) < 0)
dvander@2762
   328
  return 1;        /* In the expectation that opening as a file will fail */
dvander@2762
   329
return (statbuf.st_mode & S_IFMT) == S_IFREG;
dvander@2762
   330
}
dvander@2762
   331
dvander@2762
   332
dvander@2762
   333
/************* Test stdout for being a terminal in Unix **********/
dvander@2762
   334
dvander@2762
   335
static BOOL
dvander@2762
   336
is_stdout_tty(void)
dvander@2762
   337
{
dvander@2762
   338
return isatty(fileno(stdout));
dvander@2762
   339
}
dvander@2762
   340
dvander@2762
   341
dvander@2762
   342
/************* Directory scanning in Win32 ***********/
dvander@2762
   343
dvander@2762
   344
/* I (Philip Hazel) have no means of testing this code. It was contributed by
dvander@2762
   345
Lionel Fourquaux. David Burgess added a patch to define INVALID_FILE_ATTRIBUTES
dvander@2762
   346
when it did not exist. David Byron added a patch that moved the #include of
dvander@2762
   347
<windows.h> to before the INVALID_FILE_ATTRIBUTES definition rather than after.
dvander@2762
   348
*/
dvander@2762
   349
dvander@2762
   350
#elif HAVE_WINDOWS_H
dvander@2762
   351
dvander@2762
   352
#ifndef STRICT
dvander@2762
   353
# define STRICT
dvander@2762
   354
#endif
dvander@2762
   355
#ifndef WIN32_LEAN_AND_MEAN
dvander@2762
   356
# define WIN32_LEAN_AND_MEAN
dvander@2762
   357
#endif
dvander@2762
   358
dvander@2762
   359
#include <windows.h>
dvander@2762
   360
dvander@2762
   361
#ifndef INVALID_FILE_ATTRIBUTES
dvander@2762
   362
#define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF
dvander@2762
   363
#endif
dvander@2762
   364
dvander@2762
   365
typedef struct directory_type
dvander@2762
   366
{
dvander@2762
   367
HANDLE handle;
dvander@2762
   368
BOOL first;
dvander@2762
   369
WIN32_FIND_DATA data;
dvander@2762
   370
} directory_type;
dvander@2762
   371
dvander@2762
   372
int
dvander@2762
   373
isdirectory(char *filename)
dvander@2762
   374
{
dvander@2762
   375
DWORD attr = GetFileAttributes(filename);
dvander@2762
   376
if (attr == INVALID_FILE_ATTRIBUTES)
dvander@2762
   377
  return 0;
dvander@2762
   378
return ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) ? '/' : 0;
dvander@2762
   379
}
dvander@2762
   380
dvander@2762
   381
directory_type *
dvander@2762
   382
opendirectory(char *filename)
dvander@2762
   383
{
dvander@2762
   384
size_t len;
dvander@2762
   385
char *pattern;
dvander@2762
   386
directory_type *dir;
dvander@2762
   387
DWORD err;
dvander@2762
   388
len = strlen(filename);
dvander@2762
   389
pattern = (char *) malloc(len + 3);
dvander@2762
   390
dir = (directory_type *) malloc(sizeof(*dir));
dvander@2762
   391
if ((pattern == NULL) || (dir == NULL))
dvander@2762
   392
  {
dvander@2762
   393
  fprintf(stderr, "pcregrep: malloc failed\n");
dvander@2762
   394
  exit(2);
dvander@2762
   395
  }
dvander@2762
   396
memcpy(pattern, filename, len);
dvander@2762
   397
memcpy(&(pattern[len]), "\\*", 3);
dvander@2762
   398
dir->handle = FindFirstFile(pattern, &(dir->data));
dvander@2762
   399
if (dir->handle != INVALID_HANDLE_VALUE)
dvander@2762
   400
  {
dvander@2762
   401
  free(pattern);
dvander@2762
   402
  dir->first = TRUE;
dvander@2762
   403
  return dir;
dvander@2762
   404
  }
dvander@2762
   405
err = GetLastError();
dvander@2762
   406
free(pattern);
dvander@2762
   407
free(dir);
dvander@2762
   408
errno = (err == ERROR_ACCESS_DENIED) ? EACCES : ENOENT;
dvander@2762
   409
return NULL;
dvander@2762
   410
}
dvander@2762
   411
dvander@2762
   412
char *
dvander@2762
   413
readdirectory(directory_type *dir)
dvander@2762
   414
{
dvander@2762
   415
for (;;)
dvander@2762
   416
  {
dvander@2762
   417
  if (!dir->first)
dvander@2762
   418
    {
dvander@2762
   419
    if (!FindNextFile(dir->handle, &(dir->data)))
dvander@2762
   420
      return NULL;
dvander@2762
   421
    }
dvander@2762
   422
  else
dvander@2762
   423
    {
dvander@2762
   424
    dir->first = FALSE;
dvander@2762
   425
    }
dvander@2762
   426
  if (strcmp(dir->data.cFileName, ".") != 0 && strcmp(dir->data.cFileName, "..") != 0)
dvander@2762
   427
    return dir->data.cFileName;
dvander@2762
   428
  }
dvander@2762
   429
#ifndef _MSC_VER
dvander@2762
   430
return NULL;   /* Keep compiler happy; never executed */
dvander@2762
   431
#endif
dvander@2762
   432
}
dvander@2762
   433
dvander@2762
   434
void
dvander@2762
   435
closedirectory(directory_type *dir)
dvander@2762
   436
{
dvander@2762
   437
FindClose(dir->handle);
dvander@2762
   438
free(dir);
dvander@2762
   439
}
dvander@2762
   440
dvander@2762
   441
dvander@2762
   442
/************* Test for regular file in Win32 **********/
dvander@2762
   443
dvander@2762
   444
/* I don't know how to do this, or if it can be done; assume all paths are
dvander@2762
   445
regular if they are not directories. */
dvander@2762
   446
dvander@2762
   447
int isregfile(char *filename)
dvander@2762
   448
{
dvander@2762
   449
return !isdirectory(filename);
dvander@2762
   450
}
dvander@2762
   451
dvander@2762
   452
dvander@2762
   453
/************* Test stdout for being a terminal in Win32 **********/
dvander@2762
   454
dvander@2762
   455
/* I don't know how to do this; assume never */
dvander@2762
   456
dvander@2762
   457
static BOOL
dvander@2762
   458
is_stdout_tty(void)
dvander@2762
   459
{
dvander@2762
   460
return FALSE;
dvander@2762
   461
}
dvander@2762
   462
dvander@2762
   463
dvander@2762
   464
/************* Directory scanning when we can't do it ***********/
dvander@2762
   465
dvander@2762
   466
/* The type is void, and apart from isdirectory(), the functions do nothing. */
dvander@2762
   467
dvander@2762
   468
#else
dvander@2762
   469
dvander@2762
   470
typedef void directory_type;
dvander@2762
   471
dvander@2762
   472
int isdirectory(char *filename) { return 0; }
dvander@2762
   473
directory_type * opendirectory(char *filename) { return (directory_type*)0;}
dvander@2762
   474
char *readdirectory(directory_type *dir) { return (char*)0;}
dvander@2762
   475
void closedirectory(directory_type *dir) {}
dvander@2762
   476
dvander@2762
   477
dvander@2762
   478
/************* Test for regular when we can't do it **********/
dvander@2762
   479
dvander@2762
   480
/* Assume all files are regular. */
dvander@2762
   481
dvander@2762
   482
int isregfile(char *filename) { return 1; }
dvander@2762
   483
dvander@2762
   484
dvander@2762
   485
/************* Test stdout for being a terminal when we can't do it **********/
dvander@2762
   486
dvander@2762
   487
static BOOL
dvander@2762
   488
is_stdout_tty(void)
dvander@2762
   489
{
dvander@2762
   490
return FALSE;
dvander@2762
   491
}
dvander@2762
   492
dvander@2762
   493
dvander@2762
   494
#endif
dvander@2762
   495
dvander@2762
   496
dvander@2762
   497
dvander@2762
   498
#ifndef HAVE_STRERROR
dvander@2762
   499
/*************************************************
dvander@2762
   500
*     Provide strerror() for non-ANSI libraries  *
dvander@2762
   501
*************************************************/
dvander@2762
   502
dvander@2762
   503
/* Some old-fashioned systems still around (e.g. SunOS4) don't have strerror()
dvander@2762
   504
in their libraries, but can provide the same facility by this simple
dvander@2762
   505
alternative function. */
dvander@2762
   506
dvander@2762
   507
extern int   sys_nerr;
dvander@2762
   508
extern char *sys_errlist[];
dvander@2762
   509
dvander@2762
   510
char *
dvander@2762
   511
strerror(int n)
dvander@2762
   512
{
dvander@2762
   513
if (n < 0 || n >= sys_nerr) return "unknown error number";
dvander@2762
   514
return sys_errlist[n];
dvander@2762
   515
}
dvander@2762
   516
#endif /* HAVE_STRERROR */
dvander@2762
   517
dvander@2762
   518
dvander@2762
   519
dvander@2762
   520
/*************************************************
dvander@2762
   521
*             Find end of line                   *
dvander@2762
   522
*************************************************/
dvander@2762
   523
dvander@2762
   524
/* The length of the endline sequence that is found is set via lenptr. This may
dvander@2762
   525
be zero at the very end of the file if there is no line-ending sequence there.
dvander@2762
   526
dvander@2762
   527
Arguments:
dvander@2762
   528
  p         current position in line
dvander@2762
   529
  endptr    end of available data
dvander@2762
   530
  lenptr    where to put the length of the eol sequence
dvander@2762
   531
dvander@2762
   532
Returns:    pointer to the last byte of the line
dvander@2762
   533
*/
dvander@2762
   534
dvander@2762
   535
static char *
dvander@2762
   536
end_of_line(char *p, char *endptr, int *lenptr)
dvander@2762
   537
{
dvander@2762
   538
switch(endlinetype)
dvander@2762
   539
  {
dvander@2762
   540
  default:      /* Just in case */
dvander@2762
   541
  case EL_LF:
dvander@2762
   542
  while (p < endptr && *p != '\n') p++;
dvander@2762
   543
  if (p < endptr)
dvander@2762
   544
    {
dvander@2762
   545
    *lenptr = 1;
dvander@2762
   546
    return p + 1;
dvander@2762
   547
    }
dvander@2762
   548
  *lenptr = 0;
dvander@2762
   549
  return endptr;
dvander@2762
   550
dvander@2762
   551
  case EL_CR:
dvander@2762
   552
  while (p < endptr && *p != '\r') p++;
dvander@2762
   553
  if (p < endptr)
dvander@2762
   554
    {
dvander@2762
   555
    *lenptr = 1;
dvander@2762
   556
    return p + 1;
dvander@2762
   557
    }
dvander@2762
   558
  *lenptr = 0;
dvander@2762
   559
  return endptr;
dvander@2762
   560
dvander@2762
   561
  case EL_CRLF:
dvander@2762
   562
  for (;;)
dvander@2762
   563
    {
dvander@2762
   564
    while (p < endptr && *p != '\r') p++;
dvander@2762
   565
    if (++p >= endptr)
dvander@2762
   566
      {
dvander@2762
   567
      *lenptr = 0;
dvander@2762
   568
      return endptr;
dvander@2762
   569
      }
dvander@2762
   570
    if (*p == '\n')
dvander@2762
   571
      {
dvander@2762
   572
      *lenptr = 2;
dvander@2762
   573
      return p + 1;
dvander@2762
   574
      }
dvander@2762
   575
    }
dvander@2762
   576
  break;
dvander@2762
   577
dvander@2762
   578
  case EL_ANYCRLF:
dvander@2762
   579
  while (p < endptr)
dvander@2762
   580
    {
dvander@2762
   581
    int extra = 0;
dvander@2762
   582
    register int c = *((unsigned char *)p);
dvander@2762
   583
dvander@2762
   584
    if (utf8 && c >= 0xc0)
dvander@2762
   585
      {
dvander@2762
   586
      int gcii, gcss;
dvander@2762
   587
      extra = utf8_table4[c & 0x3f];  /* Number of additional bytes */
dvander@2762
   588
      gcss = 6*extra;
dvander@2762
   589
      c = (c & utf8_table3[extra]) << gcss;
dvander@2762
   590
      for (gcii = 1; gcii <= extra; gcii++)
dvander@2762
   591
        {
dvander@2762
   592
        gcss -= 6;
dvander@2762
   593
        c |= (p[gcii] & 0x3f) << gcss;
dvander@2762
   594
        }
dvander@2762
   595
      }
dvander@2762
   596
dvander@2762
   597
    p += 1 + extra;
dvander@2762
   598
dvander@2762
   599
    switch (c)
dvander@2762
   600
      {
dvander@2762
   601
      case 0x0a:    /* LF */
dvander@2762
   602
      *lenptr = 1;
dvander@2762
   603
      return p;
dvander@2762
   604
dvander@2762
   605
      case 0x0d:    /* CR */
dvander@2762
   606
      if (p < endptr && *p == 0x0a)
dvander@2762
   607
        {
dvander@2762
   608
        *lenptr = 2;
dvander@2762
   609
        p++;
dvander@2762
   610
        }
dvander@2762
   611
      else *lenptr = 1;
dvander@2762
   612
      return p;
dvander@2762
   613
dvander@2762
   614
      default:
dvander@2762
   615
      break;
dvander@2762
   616
      }
dvander@2762
   617
    }   /* End of loop for ANYCRLF case */
dvander@2762
   618
dvander@2762
   619
  *lenptr = 0;  /* Must have hit the end */
dvander@2762
   620
  return endptr;
dvander@2762
   621
dvander@2762
   622
  case EL_ANY:
dvander@2762
   623
  while (p < endptr)
dvander@2762
   624
    {
dvander@2762
   625
    int extra = 0;
dvander@2762
   626
    register int c = *((unsigned char *)p);
dvander@2762
   627
dvander@2762
   628
    if (utf8 && c >= 0xc0)
dvander@2762
   629
      {
dvander@2762
   630
      int gcii, gcss;
dvander@2762
   631
      extra = utf8_table4[c & 0x3f];  /* Number of additional bytes */
dvander@2762
   632
      gcss = 6*extra;
dvander@2762
   633
      c = (c & utf8_table3[extra]) << gcss;
dvander@2762
   634
      for (gcii = 1; gcii <= extra; gcii++)
dvander@2762
   635
        {
dvander@2762
   636
        gcss -= 6;
dvander@2762
   637
        c |= (p[gcii] & 0x3f) << gcss;
dvander@2762
   638
        }
dvander@2762
   639
      }
dvander@2762
   640
dvander@2762
   641
    p += 1 + extra;
dvander@2762
   642
dvander@2762
   643
    switch (c)
dvander@2762
   644
      {
dvander@2762
   645
      case 0x0a:    /* LF */
dvander@2762
   646
      case 0x0b:    /* VT */
dvander@2762
   647
      case 0x0c:    /* FF */
dvander@2762
   648
      *lenptr = 1;
dvander@2762
   649
      return p;
dvander@2762
   650
dvander@2762
   651
      case 0x0d:    /* CR */
dvander@2762
   652
      if (p < endptr && *p == 0x0a)
dvander@2762
   653
        {
dvander@2762
   654
        *lenptr = 2;
dvander@2762
   655
        p++;
dvander@2762
   656
        }
dvander@2762
   657
      else *lenptr = 1;
dvander@2762
   658
      return p;
dvander@2762
   659
dvander@2762
   660
      case 0x85:    /* NEL */
dvander@2762
   661
      *lenptr = utf8? 2 : 1;
dvander@2762
   662
      return p;
dvander@2762
   663
dvander@2762
   664
      case 0x2028:  /* LS */
dvander@2762
   665
      case 0x2029:  /* PS */
dvander@2762
   666
      *lenptr = 3;
dvander@2762
   667
      return p;
dvander@2762
   668
dvander@2762
   669
      default:
dvander@2762
   670
      break;
dvander@2762
   671
      }
dvander@2762
   672
    }   /* End of loop for ANY case */
dvander@2762
   673
dvander@2762
   674
  *lenptr = 0;  /* Must have hit the end */
dvander@2762
   675
  return endptr;
dvander@2762
   676
  }     /* End of overall switch */
dvander@2762
   677
}
dvander@2762
   678
dvander@2762
   679
dvander@2762
   680
dvander@2762
   681
/*************************************************
dvander@2762
   682
*         Find start of previous line            *
dvander@2762
   683
*************************************************/
dvander@2762
   684
dvander@2762
   685
/* This is called when looking back for before lines to print.
dvander@2762
   686
dvander@2762
   687
Arguments:
dvander@2762
   688
  p         start of the subsequent line
dvander@2762
   689
  startptr  start of available data
dvander@2762
   690
dvander@2762
   691
Returns:    pointer to the start of the previous line
dvander@2762
   692
*/
dvander@2762
   693
dvander@2762
   694
static char *
dvander@2762
   695
previous_line(char *p, char *startptr)
dvander@2762
   696
{
dvander@2762
   697
switch(endlinetype)
dvander@2762
   698
  {
dvander@2762
   699
  default:      /* Just in case */
dvander@2762
   700
  case EL_LF:
dvander@2762
   701
  p--;
dvander@2762
   702
  while (p > startptr && p[-1] != '\n') p--;
dvander@2762
   703
  return p;
dvander@2762
   704
dvander@2762
   705
  case EL_CR:
dvander@2762
   706
  p--;
dvander@2762
   707
  while (p > startptr && p[-1] != '\n') p--;
dvander@2762
   708
  return p;
dvander@2762
   709
dvander@2762
   710
  case EL_CRLF:
dvander@2762
   711
  for (;;)
dvander@2762
   712
    {
dvander@2762
   713
    p -= 2;
dvander@2762
   714
    while (p > startptr && p[-1] != '\n') p--;
dvander@2762
   715
    if (p <= startptr + 1 || p[-2] == '\r') return p;
dvander@2762
   716
    }
dvander@2762
   717
  return p;   /* But control should never get here */
dvander@2762
   718
dvander@2762
   719
  case EL_ANY:
dvander@2762
   720
  case EL_ANYCRLF:
dvander@2762
   721
  if (*(--p) == '\n' && p > startptr && p[-1] == '\r') p--;
dvander@2762
   722
  if (utf8) while ((*p & 0xc0) == 0x80) p--;
dvander@2762
   723
dvander@2762
   724
  while (p > startptr)
dvander@2762
   725
    {
dvander@2762
   726
    register int c;
dvander@2762
   727
    char *pp = p - 1;
dvander@2762
   728
dvander@2762
   729
    if (utf8)
dvander@2762
   730
      {
dvander@2762
   731
      int extra = 0;
dvander@2762
   732
      while ((*pp & 0xc0) == 0x80) pp--;
dvander@2762
   733
      c = *((unsigned char *)pp);
dvander@2762
   734
      if (c >= 0xc0)
dvander@2762
   735
        {
dvander@2762
   736
        int gcii, gcss;
dvander@2762
   737
        extra = utf8_table4[c & 0x3f];  /* Number of additional bytes */
dvander@2762
   738
        gcss = 6*extra;
dvander@2762
   739
        c = (c & utf8_table3[extra]) << gcss;
dvander@2762
   740
        for (gcii = 1; gcii <= extra; gcii++)
dvander@2762
   741
          {
dvander@2762
   742
          gcss -= 6;
dvander@2762
   743
          c |= (pp[gcii] & 0x3f) << gcss;
dvander@2762
   744
          }
dvander@2762
   745
        }
dvander@2762
   746
      }
dvander@2762
   747
    else c = *((unsigned char *)pp);
dvander@2762
   748
dvander@2762
   749
    if (endlinetype == EL_ANYCRLF) switch (c)
dvander@2762
   750
      {
dvander@2762
   751
      case 0x0a:    /* LF */
dvander@2762
   752
      case 0x0d:    /* CR */
dvander@2762
   753
      return p;
dvander@2762
   754
dvander@2762
   755
      default:
dvander@2762
   756
      break;
dvander@2762
   757
      }
dvander@2762
   758
dvander@2762
   759
    else switch (c)
dvander@2762
   760
      {
dvander@2762
   761
      case 0x0a:    /* LF */
dvander@2762
   762
      case 0x0b:    /* VT */
dvander@2762
   763
      case 0x0c:    /* FF */
dvander@2762
   764
      case 0x0d:    /* CR */
dvander@2762
   765
      case 0x85:    /* NEL */
dvander@2762
   766
      case 0x2028:  /* LS */
dvander@2762
   767
      case 0x2029:  /* PS */
dvander@2762
   768
      return p;
dvander@2762
   769
dvander@2762
   770
      default:
dvander@2762
   771
      break;
dvander@2762
   772
      }
dvander@2762
   773
dvander@2762
   774
    p = pp;  /* Back one character */
dvander@2762
   775
    }        /* End of loop for ANY case */
dvander@2762
   776
dvander@2762
   777
  return startptr;  /* Hit start of data */
dvander@2762
   778
  }     /* End of overall switch */
dvander@2762
   779
}
dvander@2762
   780
dvander@2762
   781
dvander@2762
   782
dvander@2762
   783
dvander@2762
   784
dvander@2762
   785
/*************************************************
dvander@2762
   786
*       Print the previous "after" lines         *
dvander@2762
   787
*************************************************/
dvander@2762
   788
dvander@2762
   789
/* This is called if we are about to lose said lines because of buffer filling,
dvander@2762
   790
and at the end of the file. The data in the line is written using fwrite() so
dvander@2762
   791
that a binary zero does not terminate it.
dvander@2762
   792
dvander@2762
   793
Arguments:
dvander@2762
   794
  lastmatchnumber   the number of the last matching line, plus one
dvander@2762
   795
  lastmatchrestart  where we restarted after the last match
dvander@2762
   796
  endptr            end of available data
dvander@2762
   797
  printname         filename for printing
dvander@2762
   798
dvander@2762
   799
Returns:            nothing
dvander@2762
   800
*/
dvander@2762
   801
dvander@2762
   802
static void do_after_lines(int lastmatchnumber, char *lastmatchrestart,
dvander@2762
   803
  char *endptr, char *printname)
dvander@2762
   804
{
dvander@2762
   805
if (after_context > 0 && lastmatchnumber > 0)
dvander@2762
   806
  {
dvander@2762
   807
  int count = 0;
dvander@2762
   808
  while (lastmatchrestart < endptr && count++ < after_context)
dvander@2762
   809
    {
dvander@2762
   810
    int ellength;
dvander@2762
   811
    char *pp = lastmatchrestart;
dvander@2762
   812
    if (printname != NULL) fprintf(stdout, "%s-", printname);
dvander@2762
   813
    if (number) fprintf(stdout, "%d-", lastmatchnumber++);
dvander@2762
   814
    pp = end_of_line(pp, endptr, &ellength);
dvander@2762
   815
    fwrite(lastmatchrestart, 1, pp - lastmatchrestart, stdout);
dvander@2762
   816
    lastmatchrestart = pp;
dvander@2762
   817
    }
dvander@2762
   818
  hyphenpending = TRUE;
dvander@2762
   819
  }
dvander@2762
   820
}
dvander@2762
   821
dvander@2762
   822
dvander@2762
   823
dvander@2762
   824
/*************************************************
dvander@2762
   825
*   Apply patterns to subject till one matches   *
dvander@2762
   826
*************************************************/
dvander@2762
   827
dvander@2762
   828
/* This function is called to run through all patterns, looking for a match. It
dvander@2762
   829
is used multiple times for the same subject when colouring is enabled, in order
dvander@2762
   830
to find all possible matches.
dvander@2762
   831
dvander@2762
   832
Arguments:
dvander@2762
   833
  matchptr    the start of the subject
dvander@2762
   834
  length      the length of the subject to match
dvander@2762
   835
  offsets     the offets vector to fill in
dvander@2762
   836
  mrc         address of where to put the result of pcre_exec()
dvander@2762
   837
dvander@2762
   838
Returns:      TRUE if there was a match
dvander@2762
   839
              FALSE if there was no match
dvander@2762
   840
              invert if there was a non-fatal error
dvander@2762
   841
*/
dvander@2762
   842
dvander@2762
   843
static BOOL
dvander@2762
   844
match_patterns(char *matchptr, size_t length, int *offsets, int *mrc)
dvander@2762
   845
{
dvander@2762
   846
int i;
dvander@2762
   847
for (i = 0; i < pattern_count; i++)
dvander@2762
   848
  {
dvander@2762
   849
  *mrc = pcre_exec(pattern_list[i], hints_list[i], matchptr, length, 0,
dvander@2762
   850
    PCRE_NOTEMPTY, offsets, OFFSET_SIZE);
dvander@2762
   851
  if (*mrc >= 0) return TRUE;
dvander@2762
   852
  if (*mrc == PCRE_ERROR_NOMATCH) continue;
dvander@2762
   853
  fprintf(stderr, "pcregrep: pcre_exec() error %d while matching ", *mrc);
dvander@2762
   854
  if (pattern_count > 1) fprintf(stderr, "pattern number %d to ", i+1);
dvander@2762
   855
  fprintf(stderr, "this text:\n");
dvander@2762
   856
  fwrite(matchptr, 1, length, stderr);  /* In case binary zero included */
dvander@2762
   857
  fprintf(stderr, "\n");
dvander@2762
   858
  if (error_count == 0 &&
dvander@2762
   859
      (*mrc == PCRE_ERROR_MATCHLIMIT || *mrc == PCRE_ERROR_RECURSIONLIMIT))
dvander@2762
   860
    {
dvander@2762
   861
    fprintf(stderr, "pcregrep: error %d means that a resource limit "
dvander@2762
   862
      "was exceeded\n", *mrc);
dvander@2762
   863
    fprintf(stderr, "pcregrep: check your regex for nested unlimited loops\n");
dvander@2762
   864
    }
dvander@2762
   865
  if (error_count++ > 20)
dvander@2762
   866
    {
dvander@2762
   867
    fprintf(stderr, "pcregrep: too many errors - abandoned\n");
dvander@2762
   868
    exit(2);
dvander@2762
   869
    }
dvander@2762
   870
  return invert;    /* No more matching; don't show the line again */
dvander@2762
   871
  }
dvander@2762
   872
dvander@2762
   873
return FALSE;  /* No match, no errors */
dvander@2762
   874
}
dvander@2762
   875
dvander@2762
   876
dvander@2762
   877
dvander@2762
   878
/*************************************************
dvander@2762
   879
*            Grep an individual file             *
dvander@2762
   880
*************************************************/
dvander@2762
   881
dvander@2762
   882
/* This is called from grep_or_recurse() below. It uses a buffer that is three
dvander@2762
   883
times the value of MBUFTHIRD. The matching point is never allowed to stray into
dvander@2762
   884
the top third of the buffer, thus keeping more of the file available for
dvander@2762
   885
context printing or for multiline scanning. For large files, the pointer will
dvander@2762
   886
be in the middle third most of the time, so the bottom third is available for
dvander@2762
   887
"before" context printing.
dvander@2762
   888
dvander@2762
   889
Arguments:
dvander@2762
   890
  handle       the fopened FILE stream for a normal file
dvander@2762
   891
               the gzFile pointer when reading is via libz
dvander@2762
   892
               the BZFILE pointer when reading is via libbz2
dvander@2762
   893
  frtype       FR_PLAIN, FR_LIBZ, or FR_LIBBZ2
dvander@2762
   894
  printname    the file name if it is to be printed for each match
dvander@2762
   895
               or NULL if the file name is not to be printed
dvander@2762
   896
               it cannot be NULL if filenames[_nomatch]_only is set
dvander@2762
   897
dvander@2762
   898
Returns:       0 if there was at least one match
dvander@2762
   899
               1 otherwise (no matches)
dvander@2762
   900
               2 if there is a read error on a .bz2 file
dvander@2762
   901
*/
dvander@2762
   902
dvander@2762
   903
static int
dvander@2762
   904
pcregrep(void *handle, int frtype, char *printname)
dvander@2762
   905
{
dvander@2762
   906
int rc = 1;
dvander@2762
   907
int linenumber = 1;
dvander@2762
   908
int lastmatchnumber = 0;
dvander@2762
   909
int count = 0;
dvander@2762
   910
int filepos = 0;
dvander@2762
   911
int offsets[OFFSET_SIZE];
dvander@2762
   912
char *lastmatchrestart = NULL;
dvander@2762
   913
char buffer[3*MBUFTHIRD];
dvander@2762
   914
char *ptr = buffer;
dvander@2762
   915
char *endptr;
dvander@2762
   916
size_t bufflength;
dvander@2762
   917
BOOL endhyphenpending = FALSE;
dvander@2762
   918
FILE *in = NULL;                    /* Ensure initialized */
dvander@2762
   919
dvander@2762
   920
#ifdef SUPPORT_LIBZ
dvander@2762
   921
gzFile ingz = NULL;
dvander@2762
   922
#endif
dvander@2762
   923
dvander@2762
   924
#ifdef SUPPORT_LIBBZ2
dvander@2762
   925
BZFILE *inbz2 = NULL;
dvander@2762
   926
#endif
dvander@2762
   927
dvander@2762
   928
dvander@2762
   929
/* Do the first read into the start of the buffer and set up the pointer to end
dvander@2762
   930
of what we have. In the case of libz, a non-zipped .gz file will be read as a
dvander@2762
   931
plain file. However, if a .bz2 file isn't actually bzipped, the first read will
dvander@2762
   932
fail. */
dvander@2762
   933
dvander@2762
   934
#ifdef SUPPORT_LIBZ
dvander@2762
   935
if (frtype == FR_LIBZ)
dvander@2762
   936
  {
dvander@2762
   937
  ingz = (gzFile)handle;
dvander@2762
   938
  bufflength = gzread (ingz, buffer, 3*MBUFTHIRD);
dvander@2762
   939
  }
dvander@2762
   940
else
dvander@2762
   941
#endif
dvander@2762
   942
dvander@2762
   943
#ifdef SUPPORT_LIBBZ2
dvander@2762
   944
if (frtype == FR_LIBBZ2)
dvander@2762
   945
  {
dvander@2762
   946
  inbz2 = (BZFILE *)handle;
dvander@2762
   947
  bufflength = BZ2_bzread(inbz2, buffer, 3*MBUFTHIRD);
dvander@2762
   948
  if ((int)bufflength < 0) return 2;   /* Gotcha: bufflength is size_t; */
dvander@2762
   949
  }                                    /* without the cast it is unsigned. */
dvander@2762
   950
else
dvander@2762
   951
#endif
dvander@2762
   952
dvander@2762
   953
  {
dvander@2762
   954
  in = (FILE *)handle;
dvander@2762
   955
  bufflength = fread(buffer, 1, 3*MBUFTHIRD, in);
dvander@2762
   956
  }
dvander@2762
   957
dvander@2762
   958
endptr = buffer + bufflength;
dvander@2762
   959
dvander@2762
   960
/* Loop while the current pointer is not at the end of the file. For large
dvander@2762
   961
files, endptr will be at the end of the buffer when we are in the middle of the
dvander@2762
   962
file, but ptr will never get there, because as soon as it gets over 2/3 of the
dvander@2762
   963
way, the buffer is shifted left and re-filled. */
dvander@2762
   964
dvander@2762
   965
while (ptr < endptr)
dvander@2762
   966
  {
dvander@2762
   967
  int endlinelength;
dvander@2762
   968
  int mrc = 0;
dvander@2762
   969
  BOOL match;
dvander@2762
   970
  char *matchptr = ptr;
dvander@2762
   971
  char *t = ptr;
dvander@2762
   972
  size_t length, linelength;
dvander@2762
   973
dvander@2762
   974
  /* At this point, ptr is at the start of a line. We need to find the length
dvander@2762
   975
  of the subject string to pass to pcre_exec(). In multiline mode, it is the
dvander@2762
   976
  length remainder of the data in the buffer. Otherwise, it is the length of
dvander@2762
   977
  the next line, excluding the terminating newline. After matching, we always
dvander@2762
   978
  advance by the length of the next line. In multiline mode the PCRE_FIRSTLINE
dvander@2762
   979
  option is used for compiling, so that any match is constrained to be in the
dvander@2762
   980
  first line. */
dvander@2762
   981
dvander@2762
   982
  t = end_of_line(t, endptr, &endlinelength);
dvander@2762
   983
  linelength = t - ptr - endlinelength;
dvander@2762
   984
  length = multiline? (size_t)(endptr - ptr) : linelength;
dvander@2762
   985
dvander@2762
   986
  /* Extra processing for Jeffrey Friedl's debugging. */
dvander@2762
   987
dvander@2762
   988
#ifdef JFRIEDL_DEBUG
dvander@2762
   989
  if (jfriedl_XT || jfriedl_XR)
dvander@2762
   990
  {
dvander@2762
   991
      #include <sys/time.h>
dvander@2762
   992
      #include <time.h>
dvander@2762
   993
      struct timeval start_time, end_time;
dvander@2762
   994
      struct timezone dummy;
dvander@2762
   995
      int i;
dvander@2762
   996
dvander@2762
   997
      if (jfriedl_XT)
dvander@2762
   998
      {
dvander@2762
   999
          unsigned long newlen = length * jfriedl_XT + strlen(jfriedl_prefix) + strlen(jfriedl_postfix);
dvander@2762
  1000
          const char *orig = ptr;
dvander@2762
  1001
          ptr = malloc(newlen + 1);
dvander@2762
  1002
          if (!ptr) {
dvander@2762
  1003
                  printf("out of memory");
dvander@2762
  1004
                  exit(2);
dvander@2762
  1005
          }
dvander@2762
  1006
          endptr = ptr;
dvander@2762
  1007
          strcpy(endptr, jfriedl_prefix); endptr += strlen(jfriedl_prefix);
dvander@2762
  1008
          for (i = 0; i < jfriedl_XT; i++) {
dvander@2762
  1009
                  strncpy(endptr, orig,  length);
dvander@2762
  1010
                  endptr += length;
dvander@2762
  1011
          }
dvander@2762
  1012
          strcpy(endptr, jfriedl_postfix); endptr += strlen(jfriedl_postfix);
dvander@2762
  1013
          length = newlen;
dvander@2762
  1014
      }
dvander@2762
  1015
dvander@2762
  1016
      if (gettimeofday(&start_time, &dummy) != 0)
dvander@2762
  1017
              perror("bad gettimeofday");
dvander@2762
  1018
dvander@2762
  1019
dvander@2762
  1020
      for (i = 0; i < jfriedl_XR; i++)
dvander@2762
  1021
          match = (pcre_exec(pattern_list[0], hints_list[0], ptr, length, 0,
dvander@2762
  1022
              PCRE_NOTEMPTY, offsets, OFFSET_SIZE) >= 0);
dvander@2762
  1023
dvander@2762
  1024
      if (gettimeofday(&end_time, &dummy) != 0)
dvander@2762
  1025
              perror("bad gettimeofday");
dvander@2762
  1026
dvander@2762
  1027
      double delta = ((end_time.tv_sec + (end_time.tv_usec / 1000000.0))
dvander@2762
  1028
                      -
dvander@2762
  1029
                      (start_time.tv_sec + (start_time.tv_usec / 1000000.0)));
dvander@2762
  1030
dvander@2762
  1031
      printf("%s TIMER[%.4f]\n", match ? "MATCH" : "FAIL", delta);
dvander@2762
  1032
      return 0;
dvander@2762
  1033
  }
dvander@2762
  1034
#endif
dvander@2762
  1035
dvander@2762
  1036
  /* We come back here after a match when the -o option (only_matching) is set,
dvander@2762
  1037
  in order to find any further matches in the same line. */
dvander@2762
  1038
dvander@2762
  1039
  ONLY_MATCHING_RESTART:
dvander@2762
  1040
dvander@2762
  1041
  /* Run through all the patterns until one matches or there is an error other
dvander@2762
  1042
  than NOMATCH. This code is in a subroutine so that it can be re-used for
dvander@2762
  1043
  finding subsequent matches when colouring matched lines. */
dvander@2762
  1044
dvander@2762
  1045
  match = match_patterns(matchptr, length, offsets, &mrc);
dvander@2762
  1046
dvander@2762
  1047
  /* If it's a match or a not-match (as required), do what's wanted. */
dvander@2762
  1048
dvander@2762
  1049
  if (match != invert)
dvander@2762
  1050
    {
dvander@2762
  1051
    BOOL hyphenprinted = FALSE;
dvander@2762
  1052
dvander@2762
  1053
    /* We've failed if we want a file that doesn't have any matches. */
dvander@2762
  1054
dvander@2762
  1055
    if (filenames == FN_NOMATCH_ONLY) return 1;
dvander@2762
  1056
dvander@2762
  1057
    /* Just count if just counting is wanted. */
dvander@2762
  1058
dvander@2762
  1059
    if (count_only) count++;
dvander@2762
  1060
dvander@2762
  1061
    /* If all we want is a file name, there is no need to scan any more lines
dvander@2762
  1062
    in the file. */
dvander@2762
  1063
dvander@2762
  1064
    else if (filenames == FN_ONLY)
dvander@2762
  1065
      {
dvander@2762
  1066
      fprintf(stdout, "%s\n", printname);
dvander@2762
  1067
      return 0;
dvander@2762
  1068
      }
dvander@2762
  1069
dvander@2762
  1070
    /* Likewise, if all we want is a yes/no answer. */
dvander@2762
  1071
dvander@2762
  1072
    else if (quiet) return 0;
dvander@2762
  1073
dvander@2762
  1074
    /* The --only-matching option prints just the substring that matched, and
dvander@2762
  1075
    the --file-offsets and --line-offsets options output offsets for the
dvander@2762
  1076
    matching substring (they both force --only-matching). None of these options
dvander@2762
  1077
    prints any context. Afterwards, adjust the start and length, and then jump
dvander@2762
  1078
    back to look for further matches in the same line. If we are in invert
dvander@2762
  1079
    mode, however, nothing is printed - this could be still useful because the
dvander@2762
  1080
    return code is set. */
dvander@2762
  1081
dvander@2762
  1082
    else if (only_matching)
dvander@2762
  1083
      {
dvander@2762
  1084
      if (!invert)
dvander@2762
  1085
        {
dvander@2762
  1086
        if (printname != NULL) fprintf(stdout, "%s:", printname);
dvander@2762
  1087
        if (number) fprintf(stdout, "%d:", linenumber);
dvander@2762
  1088
        if (line_offsets)
dvander@2762
  1089
          fprintf(stdout, "%d,%d", (int)(matchptr + offsets[0] - ptr),
dvander@2762
  1090
            offsets[1] - offsets[0]);
dvander@2762
  1091
        else if (file_offsets)
dvander@2762
  1092
          fprintf(stdout, "%d,%d", (int)(filepos + matchptr + offsets[0] - ptr),
dvander@2762
  1093
            offsets[1] - offsets[0]);
dvander@2762
  1094
        else
dvander@2762
  1095
          {
dvander@2762
  1096
          if (do_colour) fprintf(stdout, "%c[%sm", 0x1b, colour_string);
dvander@2762
  1097
          fwrite(matchptr + offsets[0], 1, offsets[1] - offsets[0], stdout);
dvander@2762
  1098
          if (do_colour) fprintf(stdout, "%c[00m", 0x1b);
dvander@2762
  1099
          }
dvander@2762
  1100
        fprintf(stdout, "\n");
dvander@2762
  1101
        matchptr += offsets[1];
dvander@2762
  1102
        length -= offsets[1];
dvander@2762
  1103
        match = FALSE;
dvander@2762
  1104
        goto ONLY_MATCHING_RESTART;
dvander@2762
  1105
        }
dvander@2762
  1106
      }
dvander@2762
  1107
dvander@2762
  1108
    /* This is the default case when none of the above options is set. We print
dvander@2762
  1109
    the matching lines(s), possibly preceded and/or followed by other lines of
dvander@2762
  1110
    context. */
dvander@2762
  1111
dvander@2762
  1112
    else
dvander@2762
  1113
      {
dvander@2762
  1114
      /* See if there is a requirement to print some "after" lines from a
dvander@2762
  1115
      previous match. We never print any overlaps. */
dvander@2762
  1116
dvander@2762
  1117
      if (after_context > 0 && lastmatchnumber > 0)
dvander@2762
  1118
        {
dvander@2762
  1119
        int ellength;
dvander@2762
  1120
        int linecount = 0;
dvander@2762
  1121
        char *p = lastmatchrestart;
dvander@2762
  1122
dvander@2762
  1123
        while (p < ptr && linecount < after_context)
dvander@2762
  1124
          {
dvander@2762
  1125
          p = end_of_line(p, ptr, &ellength);
dvander@2762
  1126
          linecount++;
dvander@2762
  1127
          }
dvander@2762
  1128
dvander@2762
  1129
        /* It is important to advance lastmatchrestart during this printing so
dvander@2762
  1130
        that it interacts correctly with any "before" printing below. Print
dvander@2762
  1131
        each line's data using fwrite() in case there are binary zeroes. */
dvander@2762
  1132
dvander@2762
  1133
        while (lastmatchrestart < p)
dvander@2762
  1134
          {
dvander@2762
  1135
          char *pp = lastmatchrestart;
dvander@2762
  1136
          if (printname != NULL) fprintf(stdout, "%s-", printname);
dvander@2762
  1137
          if (number) fprintf(stdout, "%d-", lastmatchnumber++);
dvander@2762
  1138
          pp = end_of_line(pp, endptr, &ellength);
dvander@2762
  1139
          fwrite(lastmatchrestart, 1, pp - lastmatchrestart, stdout);
dvander@2762
  1140
          lastmatchrestart = pp;
dvander@2762
  1141
          }
dvander@2762
  1142
        if (lastmatchrestart != ptr) hyphenpending = TRUE;
dvander@2762
  1143
        }
dvander@2762
  1144
dvander@2762
  1145
      /* If there were non-contiguous lines printed above, insert hyphens. */
dvander@2762
  1146
dvander@2762
  1147
      if (hyphenpending)
dvander@2762
  1148
        {
dvander@2762
  1149
        fprintf(stdout, "--\n");
dvander@2762
  1150
        hyphenpending = FALSE;
dvander@2762
  1151
        hyphenprinted = TRUE;
dvander@2762
  1152
        }
dvander@2762
  1153
dvander@2762
  1154
      /* See if there is a requirement to print some "before" lines for this
dvander@2762
  1155
      match. Again, don't print overlaps. */
dvander@2762
  1156
dvander@2762
  1157
      if (before_context > 0)
dvander@2762
  1158
        {
dvander@2762
  1159
        int linecount = 0;
dvander@2762
  1160
        char *p = ptr;
dvander@2762
  1161
dvander@2762
  1162
        while (p > buffer && (lastmatchnumber == 0 || p > lastmatchrestart) &&
dvander@2762
  1163
               linecount < before_context)
dvander@2762
  1164
          {
dvander@2762
  1165
          linecount++;
dvander@2762
  1166
          p = previous_line(p, buffer);
dvander@2762
  1167
          }
dvander@2762
  1168
dvander@2762
  1169
        if (lastmatchnumber > 0 && p > lastmatchrestart && !hyphenprinted)
dvander@2762
  1170
          fprintf(stdout, "--\n");
dvander@2762
  1171
dvander@2762
  1172
        while (p < ptr)
dvander@2762
  1173
          {
dvander@2762
  1174
          int ellength;
dvander@2762
  1175
          char *pp = p;
dvander@2762
  1176
          if (printname != NULL) fprintf(stdout, "%s-", printname);
dvander@2762
  1177
          if (number) fprintf(stdout, "%d-", linenumber - linecount--);
dvander@2762
  1178
          pp = end_of_line(pp, endptr, &ellength);
dvander@2762
  1179
          fwrite(p, 1, pp - p, stdout);
dvander@2762
  1180
          p = pp;
dvander@2762
  1181
          }
dvander@2762
  1182
        }
dvander@2762
  1183
dvander@2762
  1184
      /* Now print the matching line(s); ensure we set hyphenpending at the end
dvander@2762
  1185
      of the file if any context lines are being output. */
dvander@2762
  1186
dvander@2762
  1187
      if (after_context > 0 || before_context > 0)
dvander@2762
  1188
        endhyphenpending = TRUE;
dvander@2762
  1189
dvander@2762
  1190
      if (printname != NULL) fprintf(stdout, "%s:", printname);
dvander@2762
  1191
      if (number) fprintf(stdout, "%d:", linenumber);
dvander@2762
  1192
dvander@2762
  1193
      /* In multiline mode, we want to print to the end of the line in which
dvander@2762
  1194
      the end of the matched string is found, so we adjust linelength and the
dvander@2762
  1195
      line number appropriately, but only when there actually was a match
dvander@2762
  1196
      (invert not set). Because the PCRE_FIRSTLINE option is set, the start of
dvander@2762
  1197
      the match will always be before the first newline sequence. */
dvander@2762
  1198
dvander@2762
  1199
      if (multiline)
dvander@2762
  1200
        {
dvander@2762
  1201
        int ellength;
dvander@2762
  1202
        char *endmatch = ptr;
dvander@2762
  1203
        if (!invert)
dvander@2762
  1204
          {
dvander@2762
  1205
          endmatch += offsets[1];
dvander@2762
  1206
          t = ptr;
dvander@2762
  1207
          while (t < endmatch)
dvander@2762
  1208
            {
dvander@2762
  1209
            t = end_of_line(t, endptr, &ellength);
dvander@2762
  1210
            if (t <= endmatch) linenumber++; else break;
dvander@2762
  1211
            }
dvander@2762
  1212
          }
dvander@2762
  1213
        endmatch = end_of_line(endmatch, endptr, &ellength);
dvander@2762
  1214
        linelength = endmatch - ptr - ellength;
dvander@2762
  1215
        }
dvander@2762
  1216
dvander@2762
  1217
      /*** NOTE: Use only fwrite() to output the data line, so that binary
dvander@2762
  1218
      zeroes are treated as just another data character. */
dvander@2762
  1219
dvander@2762
  1220
      /* This extra option, for Jeffrey Friedl's debugging requirements,
dvander@2762
  1221
      replaces the matched string, or a specific captured string if it exists,
dvander@2762
  1222
      with X. When this happens, colouring is ignored. */
dvander@2762
  1223
dvander@2762
  1224
#ifdef JFRIEDL_DEBUG
dvander@2762
  1225
      if (S_arg >= 0 && S_arg < mrc)
dvander@2762
  1226
        {
dvander@2762
  1227
        int first = S_arg * 2;
dvander@2762
  1228
        int last  = first + 1;
dvander@2762
  1229
        fwrite(ptr, 1, offsets[first], stdout);
dvander@2762
  1230
        fprintf(stdout, "X");
dvander@2762
  1231
        fwrite(ptr + offsets[last], 1, linelength - offsets[last], stdout);
dvander@2762
  1232
        }
dvander@2762
  1233
      else
dvander@2762
  1234
#endif
dvander@2762
  1235
dvander@2762
  1236
      /* We have to split the line(s) up if colouring, and search for further
dvander@2762
  1237
      matches. */
dvander@2762
  1238
dvander@2762
  1239
      if (do_colour)
dvander@2762
  1240
        {
dvander@2762
  1241
        int last_offset = 0;
dvander@2762
  1242
        fwrite(ptr, 1, offsets[0], stdout);
dvander@2762
  1243
        fprintf(stdout, "%c[%sm", 0x1b, colour_string);
dvander@2762
  1244
        fwrite(ptr + offsets[0], 1, offsets[1] - offsets[0], stdout);
dvander@2762
  1245
        fprintf(stdout, "%c[00m", 0x1b);
dvander@2762
  1246
        for (;;)
dvander@2762
  1247
          {
dvander@2762
  1248
          last_offset += offsets[1];
dvander@2762
  1249
          matchptr += offsets[1];
dvander@2762
  1250
          length -= offsets[1];
dvander@2762
  1251
          if (!match_patterns(matchptr, length, offsets, &mrc)) break;
dvander@2762
  1252
          fwrite(matchptr, 1, offsets[0], stdout);
dvander@2762
  1253
          fprintf(stdout, "%c[%sm", 0x1b, colour_string);
dvander@2762
  1254
          fwrite(matchptr + offsets[0], 1, offsets[1] - offsets[0], stdout);
dvander@2762
  1255
          fprintf(stdout, "%c[00m", 0x1b);
dvander@2762
  1256
          }
dvander@2762
  1257
        fwrite(ptr + last_offset, 1, (linelength + endlinelength) - last_offset,
dvander@2762
  1258
          stdout);
dvander@2762
  1259
        }
dvander@2762
  1260
dvander@2762
  1261
      /* Not colouring; no need to search for further matches */
dvander@2762
  1262
dvander@2762
  1263
      else fwrite(ptr, 1, linelength + endlinelength, stdout);
dvander@2762
  1264
      }
dvander@2762
  1265
dvander@2762
  1266
    /* End of doing what has to be done for a match */
dvander@2762
  1267
dvander@2762
  1268
    rc = 0;    /* Had some success */
dvander@2762
  1269
dvander@2762
  1270
    /* Remember where the last match happened for after_context. We remember
dvander@2762
  1271
    where we are about to restart, and that line's number. */
dvander@2762
  1272
dvander@2762
  1273
    lastmatchrestart = ptr + linelength + endlinelength;
dvander@2762
  1274
    lastmatchnumber = linenumber + 1;
dvander@2762
  1275
    }
dvander@2762
  1276
dvander@2762
  1277
  /* For a match in multiline inverted mode (which of course did not cause
dvander@2762
  1278
  anything to be printed), we have to move on to the end of the match before
dvander@2762
  1279
  proceeding. */
dvander@2762
  1280
dvander@2762
  1281
  if (multiline && invert && match)
dvander@2762
  1282
    {
dvander@2762
  1283
    int ellength;
dvander@2762
  1284
    char *endmatch = ptr + offsets[1];
dvander@2762
  1285
    t = ptr;
dvander@2762
  1286
    while (t < endmatch)
dvander@2762
  1287
      {
dvander@2762
  1288
      t = end_of_line(t, endptr, &ellength);
dvander@2762
  1289
      if (t <= endmatch) linenumber++; else break;
dvander@2762
  1290
      }
dvander@2762
  1291
    endmatch = end_of_line(endmatch, endptr, &ellength);
dvander@2762
  1292
    linelength = endmatch - ptr - ellength;
dvander@2762
  1293
    }
dvander@2762
  1294
dvander@2762
  1295
  /* Advance to after the newline and increment the line number. The file
dvander@2762
  1296
  offset to the current line is maintained in filepos. */
dvander@2762
  1297
dvander@2762
  1298
  ptr += linelength + endlinelength;
dvander@2762
  1299
  filepos += linelength + endlinelength;
dvander@2762
  1300
  linenumber++;
dvander@2762
  1301
dvander@2762
  1302
  /* If we haven't yet reached the end of the file (the buffer is full), and
dvander@2762
  1303
  the current point is in the top 1/3 of the buffer, slide the buffer down by
dvander@2762
  1304
  1/3 and refill it. Before we do this, if some unprinted "after" lines are
dvander@2762
  1305
  about to be lost, print them. */
dvander@2762
  1306
dvander@2762
  1307
  if (bufflength >= sizeof(buffer) && ptr > buffer + 2*MBUFTHIRD)
dvander@2762
  1308
    {
dvander@2762
  1309
    if (after_context > 0 &&
dvander@2762
  1310
        lastmatchnumber > 0 &&
dvander@2762
  1311
        lastmatchrestart < buffer + MBUFTHIRD)
dvander@2762
  1312
      {
dvander@2762
  1313
      do_after_lines(lastmatchnumber, lastmatchrestart, endptr, printname);
dvander@2762
  1314
      lastmatchnumber = 0;
dvander@2762
  1315
      }
dvander@2762
  1316
dvander@2762
  1317
    /* Now do the shuffle */
dvander@2762
  1318
dvander@2762
  1319
    memmove(buffer, buffer + MBUFTHIRD, 2*MBUFTHIRD);
dvander@2762
  1320
    ptr -= MBUFTHIRD;
dvander@2762
  1321
dvander@2762
  1322
#ifdef SUPPORT_LIBZ
dvander@2762
  1323
    if (frtype == FR_LIBZ)
dvander@2762
  1324
      bufflength = 2*MBUFTHIRD +
dvander@2762
  1325
        gzread (ingz, buffer + 2*MBUFTHIRD, MBUFTHIRD);
dvander@2762
  1326
    else
dvander@2762
  1327
#endif
dvander@2762
  1328
dvander@2762
  1329
#ifdef SUPPORT_LIBBZ2
dvander@2762
  1330
    if (frtype == FR_LIBBZ2)
dvander@2762
  1331
      bufflength = 2*MBUFTHIRD +
dvander@2762
  1332
        BZ2_bzread(inbz2, buffer + 2*MBUFTHIRD, MBUFTHIRD);
dvander@2762
  1333
    else
dvander@2762
  1334
#endif
dvander@2762
  1335
dvander@2762
  1336
    bufflength = 2*MBUFTHIRD + fread(buffer + 2*MBUFTHIRD, 1, MBUFTHIRD, in);
dvander@2762
  1337
dvander@2762
  1338
    endptr = buffer + bufflength;
dvander@2762
  1339
dvander@2762
  1340
    /* Adjust any last match point */
dvander@2762
  1341
dvander@2762
  1342
    if (lastmatchnumber > 0) lastmatchrestart -= MBUFTHIRD;
dvander@2762
  1343
    }
dvander@2762
  1344
  }     /* Loop through the whole file */
dvander@2762
  1345
dvander@2762
  1346
/* End of file; print final "after" lines if wanted; do_after_lines sets
dvander@2762
  1347
hyphenpending if it prints something. */
dvander@2762
  1348
dvander@2762
  1349
if (!only_matching && !count_only)
dvander@2762
  1350
  {
dvander@2762
  1351
  do_after_lines(lastmatchnumber, lastmatchrestart, endptr, printname);
dvander@2762
  1352
  hyphenpending |= endhyphenpending;
dvander@2762
  1353
  }
dvander@2762
  1354
dvander@2762
  1355
/* Print the file name if we are looking for those without matches and there
dvander@2762
  1356
were none. If we found a match, we won't have got this far. */
dvander@2762
  1357
dvander@2762
  1358
if (filenames == FN_NOMATCH_ONLY)
dvander@2762
  1359
  {
dvander@2762
  1360
  fprintf(stdout, "%s\n", printname);
dvander@2762
  1361
  return 0;
dvander@2762
  1362
  }
dvander@2762
  1363
dvander@2762
  1364
/* Print the match count if wanted */
dvander@2762
  1365
dvander@2762
  1366
if (count_only)
dvander@2762
  1367
  {
dvander@2762
  1368
  if (printname != NULL) fprintf(stdout, "%s:", printname);
dvander@2762
  1369
  fprintf(stdout, "%d\n", count);
dvander@2762
  1370
  }
dvander@2762
  1371
dvander@2762
  1372
return rc;
dvander@2762
  1373
}
dvander@2762
  1374
dvander@2762
  1375
dvander@2762
  1376
dvander@2762
  1377
/*************************************************
dvander@2762
  1378
*     Grep a file or recurse into a directory    *
dvander@2762
  1379
*************************************************/
dvander@2762
  1380
dvander@2762
  1381
/* Given a path name, if it's a directory, scan all the files if we are
dvander@2762
  1382
recursing; if it's a file, grep it.
dvander@2762
  1383
dvander@2762
  1384
Arguments:
dvander@2762
  1385
  pathname          the path to investigate
dvander@2762
  1386
  dir_recurse       TRUE if recursing is wanted (-r or -drecurse)
dvander@2762
  1387
  only_one_at_top   TRUE if the path is the only one at toplevel
dvander@2762
  1388
dvander@2762
  1389
Returns:   0 if there was at least one match
dvander@2762
  1390
           1 if there were no matches
dvander@2762
  1391
           2 there was some kind of error
dvander@2762
  1392
dvander@2762
  1393
However, file opening failures are suppressed if "silent" is set.
dvander@2762
  1394
*/
dvander@2762
  1395
dvander@2762
  1396
static int
dvander@2762
  1397
grep_or_recurse(char *pathname, BOOL dir_recurse, BOOL only_one_at_top)
dvander@2762
  1398
{
dvander@2762
  1399
int rc = 1;
dvander@2762
  1400
int sep;
dvander@2762
  1401
int frtype;
dvander@2762
  1402
int pathlen;
dvander@2762
  1403
void *handle;
dvander@2762
  1404
FILE *in = NULL;           /* Ensure initialized */
dvander@2762
  1405
dvander@2762
  1406
#ifdef SUPPORT_LIBZ
dvander@2762
  1407
gzFile ingz = NULL;
dvander@2762
  1408
#endif
dvander@2762
  1409
dvander@2762
  1410
#ifdef SUPPORT_LIBBZ2
dvander@2762
  1411
BZFILE *inbz2 = NULL;
dvander@2762
  1412
#endif
dvander@2762
  1413
dvander@2762
  1414
/* If the file name is "-" we scan stdin */
dvander@2762
  1415
dvander@2762
  1416
if (strcmp(pathname, "-") == 0)
dvander@2762
  1417
  {
dvander@2762
  1418
  return pcregrep(stdin, FR_PLAIN,
dvander@2762
  1419
    (filenames > FN_DEFAULT || (filenames == FN_DEFAULT && !only_one_at_top))?
dvander@2762
  1420
      stdin_name : NULL);
dvander@2762
  1421
  }
dvander@2762
  1422
dvander@2762
  1423
/* If the file is a directory, skip if skipping or if we are recursing, scan
dvander@2762
  1424
each file and directory within it, subject to any include or exclude patterns
dvander@2762
  1425
that were set. The scanning code is localized so it can be made
dvander@2762
  1426
system-specific. */
dvander@2762
  1427
dvander@2762
  1428
if ((sep = isdirectory(pathname)) != 0)
dvander@2762
  1429
  {
dvander@2762
  1430
  if (dee_action == dee_SKIP) return 1;
dvander@2762
  1431
  if (dee_action == dee_RECURSE)
dvander@2762
  1432
    {
dvander@2762
  1433
    char buffer[1024];
dvander@2762
  1434
    char *nextfile;
dvander@2762
  1435
    directory_type *dir = opendirectory(pathname);
dvander@2762
  1436
dvander@2762
  1437
    if (dir == NULL)
dvander@2762
  1438
      {
dvander@2762
  1439
      if (!silent)
dvander@2762
  1440
        fprintf(stderr, "pcregrep: Failed to open directory %s: %s\n", pathname,
dvander@2762
  1441
          strerror(errno));
dvander@2762
  1442
      return 2;
dvander@2762
  1443
      }
dvander@2762
  1444
dvander@2762
  1445
    while ((nextfile = readdirectory(dir)) != NULL)
dvander@2762
  1446
      {
dvander@2762
  1447
      int frc, nflen;
dvander@2762
  1448
      sprintf(buffer, "%.512s%c%.128s", pathname, sep, nextfile);
dvander@2762
  1449
      nflen = strlen(nextfile);
dvander@2762
  1450
dvander@2762
  1451
      if (isdirectory(buffer))
dvander@2762
  1452
        {
dvander@2762
  1453
        if (exclude_dir_compiled != NULL &&
dvander@2762
  1454
            pcre_exec(exclude_dir_compiled, NULL, nextfile, nflen, 0, 0, NULL, 0) >= 0)
dvander@2762
  1455
          continue;
dvander@2762
  1456
dvander@2762
  1457
        if (include_dir_compiled != NULL &&
dvander@2762
  1458
            pcre_exec(include_dir_compiled, NULL, nextfile, nflen, 0, 0, NULL, 0) < 0)
dvander@2762
  1459
          continue;
dvander@2762
  1460
        }
dvander@2762
  1461
      else
dvander@2762
  1462
        {
dvander@2762
  1463
        if (exclude_compiled != NULL &&
dvander@2762
  1464
            pcre_exec(exclude_compiled, NULL, nextfile, nflen, 0, 0, NULL, 0) >= 0)
dvander@2762
  1465
          continue;
dvander@2762
  1466
dvander@2762
  1467
        if (include_compiled != NULL &&
dvander@2762
  1468
            pcre_exec(include_compiled, NULL, nextfile, nflen, 0, 0, NULL, 0) < 0)
dvander@2762
  1469
          continue;
dvander@2762
  1470
        }
dvander@2762
  1471
dvander@2762
  1472
      frc = grep_or_recurse(buffer, dir_recurse, FALSE);
dvander@2762
  1473
      if (frc > 1) rc = frc;
dvander@2762
  1474
       else if (frc == 0 && rc == 1) rc = 0;
dvander@2762
  1475
      }
dvander@2762
  1476
dvander@2762
  1477
    closedirectory(dir);
dvander@2762
  1478
    return rc;
dvander@2762
  1479
    }
dvander@2762
  1480
  }
dvander@2762
  1481
dvander@2762
  1482
/* If the file is not a directory and not a regular file, skip it if that's
dvander@2762
  1483
been requested. */
dvander@2762
  1484
dvander@2762
  1485
else if (!isregfile(pathname) && DEE_action == DEE_SKIP) return 1;
dvander@2762
  1486
dvander@2762
  1487
/* Control reaches here if we have a regular file, or if we have a directory
dvander@2762
  1488
and recursion or skipping was not requested, or if we have anything else and
dvander@2762
  1489
skipping was not requested. The scan proceeds. If this is the first and only
dvander@2762
  1490
argument at top level, we don't show the file name, unless we are only showing
dvander@2762
  1491
the file name, or the filename was forced (-H). */
dvander@2762
  1492
dvander@2762
  1493
pathlen = strlen(pathname);
dvander@2762
  1494
dvander@2762
  1495
/* Open using zlib if it is supported and the file name ends with .gz. */
dvander@2762
  1496
dvander@2762
  1497
#ifdef SUPPORT_LIBZ
dvander@2762
  1498
if (pathlen > 3 && strcmp(pathname + pathlen - 3, ".gz") == 0)
dvander@2762
  1499
  {
dvander@2762
  1500
  ingz = gzopen(pathname, "rb");
dvander@2762
  1501
  if (ingz == NULL)
dvander@2762
  1502
    {
dvander@2762
  1503
    if (!silent)
dvander@2762
  1504
      fprintf(stderr, "pcregrep: Failed to open %s: %s\n", pathname,
dvander@2762
  1505
        strerror(errno));
dvander@2762
  1506
    return 2;
dvander@2762
  1507
    }
dvander@2762
  1508
  handle = (void *)ingz;
dvander@2762
  1509
  frtype = FR_LIBZ;
dvander@2762
  1510
  }
dvander@2762
  1511
else
dvander@2762
  1512
#endif
dvander@2762
  1513
dvander@2762
  1514
/* Otherwise open with bz2lib if it is supported and the name ends with .bz2. */
dvander@2762
  1515
dvander@2762
  1516
#ifdef SUPPORT_LIBBZ2
dvander@2762
  1517
if (pathlen > 4 && strcmp(pathname + pathlen - 4, ".bz2") == 0)
dvander@2762
  1518
  {
dvander@2762
  1519
  inbz2 = BZ2_bzopen(pathname, "rb");
dvander@2762
  1520
  handle = (void *)inbz2;
dvander@2762
  1521
  frtype = FR_LIBBZ2;
dvander@2762
  1522
  }
dvander@2762
  1523
else
dvander@2762
  1524
#endif
dvander@2762
  1525
dvander@2762
  1526
/* Otherwise use plain fopen(). The label is so that we can come back here if
dvander@2762
  1527
an attempt to read a .bz2 file indicates that it really is a plain file. */
dvander@2762
  1528
dvander@2762
  1529
#ifdef SUPPORT_LIBBZ2
dvander@2762
  1530
PLAIN_FILE:
dvander@2762
  1531
#endif
dvander@2762
  1532
  {
dvander@2762
  1533
  in = fopen(pathname, "r");
dvander@2762
  1534
  handle = (void *)in;
dvander@2762
  1535
  frtype = FR_PLAIN;
dvander@2762
  1536
  }
dvander@2762
  1537
dvander@2762
  1538
/* All the opening methods return errno when they fail. */
dvander@2762
  1539
dvander@2762
  1540
if (handle == NULL)
dvander@2762
  1541
  {
dvander@2762
  1542
  if (!silent)
dvander@2762
  1543
    fprintf(stderr, "pcregrep: Failed to open %s: %s\n", pathname,
dvander@2762
  1544
      strerror(errno));
dvander@2762
  1545
  return 2;
dvander@2762
  1546
  }
dvander@2762
  1547
dvander@2762
  1548
/* Now grep the file */
dvander@2762
  1549
dvander@2762
  1550
rc = pcregrep(handle, frtype, (filenames > FN_DEFAULT ||
dvander@2762
  1551
  (filenames == FN_DEFAULT && !only_one_at_top))? pathname : NULL);
dvander@2762
  1552
dvander@2762
  1553
/* Close in an appropriate manner. */
dvander@2762
  1554
dvander@2762
  1555
#ifdef SUPPORT_LIBZ
dvander@2762
  1556
if (frtype == FR_LIBZ)
dvander@2762
  1557
  gzclose(ingz);
dvander@2762
  1558
else
dvander@2762
  1559
#endif
dvander@2762
  1560
dvander@2762
  1561
/* If it is a .bz2 file and the result is 2, it means that the first attempt to
dvander@2762
  1562
read failed. If the error indicates that the file isn't in fact bzipped, try
dvander@2762
  1563
again as a normal file. */
dvander@2762
  1564
dvander@2762
  1565
#ifdef SUPPORT_LIBBZ2
dvander@2762
  1566
if (frtype == FR_LIBBZ2)
dvander@2762
  1567
  {
dvander@2762
  1568
  if (rc == 2)
dvander@2762
  1569
    {
dvander@2762
  1570
    int errnum;
dvander@2762
  1571
    const char *err = BZ2_bzerror(inbz2, &errnum);
dvander@2762
  1572
    if (errnum == BZ_DATA_ERROR_MAGIC)
dvander@2762
  1573
      {
dvander@2762
  1574
      BZ2_bzclose(inbz2);
dvander@2762
  1575
      goto PLAIN_FILE;
dvander@2762
  1576
      }
dvander@2762
  1577
    else if (!silent)
dvander@2762
  1578
      fprintf(stderr, "pcregrep: Failed to read %s using bzlib: %s\n",
dvander@2762
  1579
        pathname, err);
dvander@2762
  1580
    }
dvander@2762
  1581
  BZ2_bzclose(inbz2);
dvander@2762
  1582
  }
dvander@2762
  1583
else
dvander@2762
  1584
#endif
dvander@2762
  1585
dvander@2762
  1586
/* Normal file close */
dvander@2762
  1587
dvander@2762
  1588
fclose(in);
dvander@2762
  1589
dvander@2762
  1590
/* Pass back the yield from pcregrep(). */
dvander@2762
  1591
dvander@2762
  1592
return rc;
dvander@2762
  1593
}
dvander@2762
  1594
dvander@2762
  1595
dvander@2762
  1596
dvander@2762
  1597
dvander@2762
  1598
/*************************************************
dvander@2762
  1599
*                Usage function                  *
dvander@2762
  1600
*************************************************/
dvander@2762
  1601
dvander@2762
  1602
static int
dvander@2762
  1603
usage(int rc)
dvander@2762
  1604
{
dvander@2762
  1605
option_item *op;
dvander@2762
  1606
fprintf(stderr, "Usage: pcregrep [-");
dvander@2762
  1607
for (op = optionlist; op->one_char != 0; op++)
dvander@2762
  1608
  {
dvander@2762
  1609
  if (op->one_char > 0) fprintf(stderr, "%c", op->one_char);
dvander@2762
  1610
  }
dvander@2762
  1611
fprintf(stderr, "] [long options] [pattern] [files]\n");
dvander@2762
  1612
fprintf(stderr, "Type `pcregrep --help' for more information and the long "
dvander@2762
  1613
  "options.\n");
dvander@2762
  1614
return rc;
dvander@2762
  1615
}
dvander@2762
  1616
dvander@2762
  1617
dvander@2762
  1618
dvander@2762
  1619
dvander@2762
  1620
/*************************************************
dvander@2762
  1621
*                Help function                   *
dvander@2762
  1622
*************************************************/
dvander@2762
  1623
dvander@2762
  1624
static void
dvander@2762
  1625
help(void)
dvander@2762
  1626
{
dvander@2762
  1627
option_item *op;
dvander@2762
  1628
dvander@2762
  1629
printf("Usage: pcregrep [OPTION]... [PATTERN] [FILE1 FILE2 ...]\n");
dvander@2762
  1630
printf("Search for PATTERN in each FILE or standard input.\n");
dvander@2762
  1631
printf("PATTERN must be present if neither -e nor -f is used.\n");
dvander@2762
  1632
printf("\"-\" can be used as a file name to mean STDIN.\n");
dvander@2762
  1633
dvander@2762
  1634
#ifdef SUPPORT_LIBZ
dvander@2762
  1635
printf("Files whose names end in .gz are read using zlib.\n");
dvander@2762
  1636
#endif
dvander@2762
  1637
dvander@2762
  1638
#ifdef SUPPORT_LIBBZ2
dvander@2762
  1639
printf("Files whose names end in .bz2 are read using bzlib2.\n");
dvander@2762
  1640
#endif
dvander@2762
  1641
dvander@2762
  1642
#if defined SUPPORT_LIBZ || defined SUPPORT_LIBBZ2
dvander@2762
  1643
printf("Other files and the standard input are read as plain files.\n\n");
dvander@2762
  1644
#else
dvander@2762
  1645
printf("All files are read as plain files, without any interpretation.\n\n");
dvander@2762
  1646
#endif
dvander@2762
  1647
dvander@2762
  1648
printf("Example: pcregrep -i 'hello.*world' menu.h main.c\n\n");
dvander@2762
  1649
printf("Options:\n");
dvander@2762
  1650
dvander@2762
  1651
for (op = optionlist; op->one_char != 0; op++)
dvander@2762
  1652
  {
dvander@2762
  1653
  int n;
dvander@2762
  1654
  char s[4];
dvander@2762
  1655
  if (op->one_char > 0) sprintf(s, "-%c,", op->one_char); else strcpy(s, "   ");
dvander@2762
  1656
  n = 30 - printf("  %s --%s", s, op->long_name);
dvander@2762
  1657
  if (n < 1) n = 1;
dvander@2762
  1658
  printf("%.*s%s\n", n, "                    ", op->help_text);
dvander@2762
  1659
  }
dvander@2762
  1660
dvander@2762
  1661
printf("\nWhen reading patterns from a file instead of using a command line option,\n");
dvander@2762
  1662
printf("trailing white space is removed and blank lines are ignored.\n");
dvander@2762
  1663
printf("There is a maximum of %d patterns.\n", MAX_PATTERN_COUNT);
dvander@2762
  1664
dvander@2762
  1665
printf("\nWith no FILEs, read standard input. If fewer than two FILEs given, assume -h.\n");
dvander@2762
  1666
printf("Exit status is 0 if any matches, 1 if no matches, and 2 if trouble.\n");
dvander@2762
  1667
}
dvander@2762
  1668
dvander@2762
  1669
dvander@2762
  1670
dvander@2762
  1671
dvander@2762
  1672
/*************************************************
dvander@2762
  1673
*    Handle a single-letter, no data option      *
dvander@2762
  1674
*************************************************/
dvander@2762
  1675
dvander@2762
  1676
static int
dvander@2762
  1677
handle_option(int letter, int options)
dvander@2762
  1678
{
dvander@2762
  1679
switch(letter)
dvander@2762
  1680
  {
dvander@2762
  1681
  case N_FOFFSETS: file_offsets = TRUE; break;
dvander@2762
  1682
  case N_HELP: help(); exit(0);
dvander@2762
  1683
  case N_LOFFSETS: line_offsets = number = TRUE; break;
dvander@2762
  1684
  case 'c': count_only = TRUE; break;
dvander@2762
  1685
  case 'F': process_options |= PO_FIXED_STRINGS; break;
dvander@2762
  1686
  case 'H': filenames = FN_FORCE; break;
dvander@2762
  1687
  case 'h': filenames = FN_NONE; break;
dvander@2762
  1688
  case 'i': options |= PCRE_CASELESS; break;
dvander@2762
  1689
  case 'l': filenames = FN_ONLY; break;
dvander@2762
  1690
  case 'L': filenames = FN_NOMATCH_ONLY; break;
dvander@2762
  1691
  case 'M': multiline = TRUE; options |= PCRE_MULTILINE|PCRE_FIRSTLINE; break;
dvander@2762
  1692
  case 'n': number = TRUE; break;
dvander@2762
  1693
  case 'o': only_matching = TRUE; break;
dvander@2762
  1694
  case 'q': quiet = TRUE; break;
dvander@2762
  1695
  case 'r': dee_action = dee_RECURSE; break;
dvander@2762
  1696
  case 's': silent = TRUE; break;
dvander@2762
  1697
  case 'u': options |= PCRE_UTF8; utf8 = TRUE; break;
dvander@2762
  1698
  case 'v': invert = TRUE; break;
dvander@2762
  1699
  case 'w': process_options |= PO_WORD_MATCH; break;
dvander@2762
  1700
  case 'x': process_options |= PO_LINE_MATCH; break;
dvander@2762
  1701
dvander@2762
  1702
  case 'V':
dvander@2762
  1703
  fprintf(stderr, "pcregrep version %s\n", pcre_version());
dvander@2762
  1704
  exit(0);
dvander@2762
  1705
  break;
dvander@2762
  1706
dvander@2762
  1707
  default:
dvander@2762
  1708
  fprintf(stderr, "pcregrep: Unknown option -%c\n", letter);
dvander@2762
  1709
  exit(usage(2));
dvander@2762
  1710
  }
dvander@2762
  1711
dvander@2762
  1712
return options;
dvander@2762
  1713
}
dvander@2762
  1714
dvander@2762
  1715
dvander@2762
  1716
dvander@2762
  1717
dvander@2762
  1718
/*************************************************
dvander@2762
  1719
*          Construct printed ordinal             *
dvander@2762
  1720
*************************************************/
dvander@2762
  1721
dvander@2762
  1722
/* This turns a number into "1st", "3rd", etc. */
dvander@2762
  1723
dvander@2762
  1724
static char *
dvander@2762
  1725
ordin(int n)
dvander@2762
  1726
{
dvander@2762
  1727
static char buffer[8];
dvander@2762
  1728
char *p = buffer;
dvander@2762
  1729
sprintf(p, "%d", n);
dvander@2762
  1730
while (*p != 0) p++;
dvander@2762
  1731
switch (n%10)
dvander@2762
  1732
  {
dvander@2762
  1733
  case 1: strcpy(p, "st"); break;
dvander@2762
  1734
  case 2: strcpy(p, "nd"); break;
dvander@2762
  1735
  case 3: strcpy(p, "rd"); break;
dvander@2762
  1736
  default: strcpy(p, "th"); break;
dvander@2762
  1737
  }
dvander@2762
  1738
return buffer;
dvander@2762
  1739
}
dvander@2762
  1740
dvander@2762
  1741
dvander@2762
  1742
dvander@2762
  1743
/*************************************************
dvander@2762
  1744
*          Compile a single pattern              *
dvander@2762
  1745
*************************************************/
dvander@2762
  1746
dvander@2762
  1747
/* When the -F option has been used, this is called for each substring.
dvander@2762
  1748
Otherwise it's called for each supplied pattern.
dvander@2762
  1749
dvander@2762
  1750
Arguments:
dvander@2762
  1751
  pattern        the pattern string
dvander@2762
  1752
  options        the PCRE options
dvander@2762
  1753
  filename       the file name, or NULL for a command-line pattern
dvander@2762
  1754
  count          0 if this is the only command line pattern, or
dvander@2762
  1755
                 number of the command line pattern, or
dvander@2762
  1756
                 linenumber for a pattern from a file
dvander@2762
  1757
dvander@2762
  1758
Returns:         TRUE on success, FALSE after an error
dvander@2762
  1759
*/
dvander@2762
  1760
dvander@2762
  1761
static BOOL
dvander@2762
  1762
compile_single_pattern(char *pattern, int options, char *filename, int count)
dvander@2762
  1763
{
dvander@2762
  1764
char buffer[MBUFTHIRD + 16];
dvander@2762
  1765
const char *error;
dvander@2762
  1766
int errptr;
dvander@2762
  1767
dvander@2762
  1768
if (pattern_count >= MAX_PATTERN_COUNT)
dvander@2762
  1769
  {
dvander@2762
  1770
  fprintf(stderr, "pcregrep: Too many %spatterns (max %d)\n",
dvander@2762
  1771
    (filename == NULL)? "command-line " : "", MAX_PATTERN_COUNT);
dvander@2762
  1772
  return FALSE;
dvander@2762
  1773
  }
dvander@2762
  1774
dvander@2762
  1775
sprintf(buffer, "%s%.*s%s", prefix[process_options], MBUFTHIRD, pattern,
dvander@2762
  1776
  suffix[process_options]);
dvander@2762
  1777
pattern_list[pattern_count] =
dvander@2762
  1778
  pcre_compile(buffer, options, &error, &errptr, pcretables);
dvander@2762
  1779
if (pattern_list[pattern_count] != NULL)
dvander@2762
  1780
  {
dvander@2762
  1781
  pattern_count++;
dvander@2762
  1782
  return TRUE;
dvander@2762
  1783
  }
dvander@2762
  1784
dvander@2762
  1785
/* Handle compile errors */
dvander@2762
  1786
dvander@2762
  1787
errptr -= (int)strlen(prefix[process_options]);
dvander@2762
  1788
if (errptr > (int)strlen(pattern)) errptr = (int)strlen(pattern);
dvander@2762
  1789
dvander@2762
  1790
if (filename == NULL)
dvander@2762
  1791
  {
dvander@2762
  1792
  if (count == 0)
dvander@2762
  1793
    fprintf(stderr, "pcregrep: Error in command-line regex "
dvander@2762
  1794
      "at offset %d: %s\n", errptr, error);
dvander@2762
  1795
  else
dvander@2762
  1796
    fprintf(stderr, "pcregrep: Error in %s command-line regex "
dvander@2762
  1797
      "at offset %d: %s\n", ordin(count), errptr, error);
dvander@2762
  1798
  }
dvander@2762
  1799
else
dvander@2762
  1800
  {
dvander@2762
  1801
  fprintf(stderr, "pcregrep: Error in regex in line %d of %s "
dvander@2762
  1802
    "at offset %d: %s\n", count, filename, errptr, error);
dvander@2762
  1803
  }
dvander@2762
  1804
dvander@2762
  1805
return FALSE;
dvander@2762
  1806
}
dvander@2762
  1807
dvander@2762
  1808
dvander@2762
  1809
dvander@2762
  1810
/*************************************************
dvander@2762
  1811
*           Compile one supplied pattern         *
dvander@2762
  1812
*************************************************/
dvander@2762
  1813
dvander@2762
  1814
/* When the -F option has been used, each string may be a list of strings,
dvander@2762
  1815
separated by line breaks. They will be matched literally.
dvander@2762
  1816
dvander@2762
  1817
Arguments:
dvander@2762
  1818
  pattern        the pattern string
dvander@2762
  1819
  options        the PCRE options
dvander@2762
  1820
  filename       the file name, or NULL for a command-line pattern
dvander@2762
  1821
  count          0 if this is the only command line pattern, or
dvander@2762
  1822
                 number of the command line pattern, or
dvander@2762
  1823
                 linenumber for a pattern from a file
dvander@2762
  1824
dvander@2762
  1825
Returns:         TRUE on success, FALSE after an error
dvander@2762
  1826
*/
dvander@2762
  1827
dvander@2762
  1828
static BOOL
dvander@2762
  1829
compile_pattern(char *pattern, int options, char *filename, int count)
dvander@2762
  1830
{
dvander@2762
  1831
if ((process_options & PO_FIXED_STRINGS) != 0)
dvander@2762
  1832
  {
dvander@2762
  1833
  char *eop = pattern + strlen(pattern);
dvander@2762
  1834
  char buffer[MBUFTHIRD];
dvander@2762
  1835
  for(;;)
dvander@2762
  1836
    {
dvander@2762
  1837
    int ellength;
dvander@2762
  1838
    char *p = end_of_line(pattern, eop, &ellength);
dvander@2762
  1839
    if (ellength == 0)
dvander@2762
  1840
      return compile_single_pattern(pattern, options, filename, count);
dvander@2762
  1841
    sprintf(buffer, "%.*s", (int)(p - pattern - ellength), pattern);
dvander@2762
  1842
    pattern = p;
dvander@2762
  1843
    if (!compile_single_pattern(buffer, options, filename, count))
dvander@2762
  1844
      return FALSE;
dvander@2762
  1845
    }
dvander@2762
  1846
  }
dvander@2762
  1847
else return compile_single_pattern(pattern, options, filename, count);
dvander@2762
  1848
}
dvander@2762
  1849
dvander@2762
  1850
dvander@2762
  1851
dvander@2762
  1852
/*************************************************
dvander@2762
  1853
*                Main program                    *
dvander@2762
  1854
*************************************************/
dvander@2762
  1855
dvander@2762
  1856
/* Returns 0 if something matched, 1 if nothing matched, 2 after an error. */
dvander@2762
  1857
dvander@2762
  1858
int
dvander@2762
  1859
main(int argc, char **argv)
dvander@2762
  1860
{
dvander@2762
  1861
int i, j;
dvander@2762
  1862
int rc = 1;
dvander@2762
  1863
int pcre_options = 0;
dvander@2762
  1864
int cmd_pattern_count = 0;
dvander@2762
  1865
int hint_count = 0;
dvander@2762
  1866
int errptr;
dvander@2762
  1867
BOOL only_one_at_top;
dvander@2762
  1868
char *patterns[MAX_PATTERN_COUNT];
dvander@2762
  1869
const char *locale_from = "--locale";
dvander@2762
  1870
const char *error;
dvander@2762
  1871
dvander@2762
  1872
/* Set the default line ending value from the default in the PCRE library;
dvander@2762
  1873
"lf", "cr", "crlf", and "any" are supported. Anything else is treated as "lf".
dvander@2762
  1874
Note that the return values from pcre_config(), though derived from the ASCII
dvander@2762
  1875
codes, are the same in EBCDIC environments, so we must use the actual values
dvander@2762
  1876
rather than escapes such as as '\r'. */
dvander@2762
  1877
dvander@2762
  1878
(void)pcre_config(PCRE_CONFIG_NEWLINE, &i);
dvander@2762
  1879
switch(i)
dvander@2762
  1880
  {
dvander@2762
  1881
  default:               newline = (char *)"lf"; break;
dvander@2762
  1882
  case 13:               newline = (char *)"cr"; break;
dvander@2762
  1883
  case (13 << 8) | 10:   newline = (char *)"crlf"; break;
dvander@2762
  1884
  case -1:               newline = (char *)"any"; break;
dvander@2762
  1885
  case -2:               newline = (char *)"anycrlf"; break;
dvander@2762
  1886
  }
dvander@2762
  1887
dvander@2762
  1888
/* Process the options */
dvander@2762
  1889
dvander@2762
  1890
for (i = 1; i < argc; i++)
dvander@2762
  1891
  {
dvander@2762
  1892
  option_item *op = NULL;
dvander@2762
  1893
  char *option_data = (char *)"";    /* default to keep compiler happy */
dvander@2762
  1894
  BOOL longop;
dvander@2762
  1895
  BOOL longopwasequals = FALSE;
dvander@2762
  1896
dvander@2762
  1897
  if (argv[i][0] != '-') break;
dvander@2762
  1898
dvander@2762
  1899
  /* If we hit an argument that is just "-", it may be a reference to STDIN,
dvander@2762
  1900
  but only if we have previously had -e or -f to define the patterns. */
dvander@2762
  1901
dvander@2762
  1902
  if (argv[i][1] == 0)
dvander@2762
  1903
    {
dvander@2762
  1904
    if (pattern_filename != NULL || pattern_count > 0) break;
dvander@2762
  1905
      else exit(usage(2));
dvander@2762
  1906
    }
dvander@2762
  1907
dvander@2762
  1908
  /* Handle a long name option, or -- to terminate the options */
dvander@2762
  1909
dvander@2762
  1910
  if (argv[i][1] == '-')
dvander@2762
  1911
    {
dvander@2762
  1912
    char *arg = argv[i] + 2;
dvander@2762
  1913
    char *argequals = strchr(arg, '=');
dvander@2762
  1914
dvander@2762
  1915
    if (*arg == 0)    /* -- terminates options */
dvander@2762
  1916
      {
dvander@2762
  1917
      i++;
dvander@2762
  1918
      break;                /* out of the options-handling loop */
dvander@2762
  1919
      }
dvander@2762
  1920
dvander@2762
  1921
    longop = TRUE;
dvander@2762
  1922
dvander@2762
  1923
    /* Some long options have data that follows after =, for example file=name.
dvander@2762
  1924
    Some options have variations in the long name spelling: specifically, we
dvander@2762
  1925
    allow "regexp" because GNU grep allows it, though I personally go along
dvander@2762
  1926
    with Jeffrey Friedl and Larry Wall in preferring "regex" without the "p".
dvander@2762
  1927
    These options are entered in the table as "regex(p)". No option is in both
dvander@2762
  1928
    these categories, fortunately. */
dvander@2762
  1929
dvander@2762
  1930
    for (op = optionlist; op->one_char != 0; op++)
dvander@2762
  1931
      {
dvander@2762
  1932
      char *opbra = strchr(op->long_name, '(');
dvander@2762
  1933
      char *equals = strchr(op->long_name, '=');
dvander@2762
  1934
      if (opbra == NULL)     /* Not a (p) case */
dvander@2762
  1935
        {
dvander@2762
  1936
        if (equals == NULL)  /* Not thing=data case */
dvander@2762
  1937
          {
dvander@2762
  1938
          if (strcmp(arg, op->long_name) == 0) break;
dvander@2762
  1939
          }
dvander@2762
  1940
        else                 /* Special case xxx=data */
dvander@2762
  1941
          {
dvander@2762
  1942
          int oplen = equals - op->long_name;
dvander@2762
  1943
          int arglen = (argequals == NULL)? (int)strlen(arg) : argequals - arg;
dvander@2762
  1944
          if (oplen == arglen && strncmp(arg, op->long_name, oplen) == 0)
dvander@2762
  1945
            {
dvander@2762
  1946
            option_data = arg + arglen;
dvander@2762
  1947
            if (*option_data == '=')
dvander@2762
  1948
              {
dvander@2762
  1949
              option_data++;
dvander@2762
  1950
              longopwasequals = TRUE;
dvander@2762
  1951
              }
dvander@2762
  1952
            break;
dvander@2762
  1953
            }
dvander@2762
  1954
          }
dvander@2762
  1955
        }
dvander@2762
  1956
      else                   /* Special case xxxx(p) */
dvander@2762
  1957
        {
dvander@2762
  1958
        char buff1[24];
dvander@2762
  1959
        char buff2[24];
dvander@2762
  1960
        int baselen = opbra - op->long_name;
dvander@2762
  1961
        sprintf(buff1, "%.*s", baselen, op->long_name);
dvander@2762
  1962
        sprintf(buff2, "%s%.*s", buff1,
dvander@2762
  1963
          (int)strlen(op->long_name) - baselen - 2, opbra + 1);
dvander@2762
  1964
        if (strcmp(arg, buff1) == 0 || strcmp(arg, buff2) == 0)
dvander@2762
  1965
          break;
dvander@2762
  1966
        }
dvander@2762
  1967
      }
dvander@2762
  1968
dvander@2762
  1969
    if (op->one_char == 0)
dvander@2762
  1970
      {
dvander@2762
  1971
      fprintf(stderr, "pcregrep: Unknown option %s\n", argv[i]);
dvander@2762
  1972
      exit(usage(2));
dvander@2762
  1973
      }
dvander@2762
  1974
    }
dvander@2762
  1975
dvander@2762
  1976
dvander@2762
  1977
  /* Jeffrey Friedl's debugging harness uses these additional options which
dvander@2762
  1978
  are not in the right form for putting in the option table because they use
dvander@2762
  1979
  only one hyphen, yet are more than one character long. By putting them
dvander@2762
  1980
  separately here, they will not get displayed as part of the help() output,
dvander@2762
  1981
  but I don't think Jeffrey will care about that. */
dvander@2762
  1982
dvander@2762
  1983
#ifdef JFRIEDL_DEBUG
dvander@2762
  1984
  else if (strcmp(argv[i], "-pre") == 0) {
dvander@2762
  1985
          jfriedl_prefix = argv[++i];
dvander@2762
  1986
          continue;
dvander@2762
  1987
  } else if (strcmp(argv[i], "-post") == 0) {
dvander@2762
  1988
          jfriedl_postfix = argv[++i];
dvander@2762
  1989
          continue;
dvander@2762
  1990
  } else if (strcmp(argv[i], "-XT") == 0) {
dvander@2762
  1991
          sscanf(argv[++i], "%d", &jfriedl_XT);
dvander@2762
  1992
          continue;
dvander@2762
  1993
  } else if (strcmp(argv[i], "-XR") == 0) {
dvander@2762
  1994
          sscanf(argv[++i], "%d", &jfriedl_XR);
dvander@2762
  1995
          continue;
dvander@2762
  1996
  }
dvander@2762
  1997
#endif
dvander@2762
  1998
dvander@2762
  1999
dvander@2762
  2000
  /* One-char options; many that have no data may be in a single argument; we
dvander@2762
  2001
  continue till we hit the last one or one that needs data. */
dvander@2762
  2002
dvander@2762
  2003
  else
dvander@2762
  2004
    {
dvander@2762
  2005
    char *s = argv[i] + 1;
dvander@2762
  2006
    longop = FALSE;
dvander@2762
  2007
    while (*s != 0)
dvander@2762
  2008
      {
dvander@2762
  2009
      for (op = optionlist; op->one_char != 0; op++)
dvander@2762
  2010
        { if (*s == op->one_char) break; }
dvander@2762
  2011
      if (op->one_char == 0)
dvander@2762
  2012
        {
dvander@2762
  2013
        fprintf(stderr, "pcregrep: Unknown option letter '%c' in \"%s\"\n",
dvander@2762
  2014
          *s, argv[i]);
dvander@2762
  2015
        exit(usage(2));
dvander@2762
  2016
        }
dvander@2762
  2017
      if (op->type != OP_NODATA || s[1] == 0)
dvander@2762
  2018
        {
dvander@2762
  2019
        option_data = s+1;
dvander@2762
  2020
        break;
dvander@2762
  2021
        }
dvander@2762
  2022
      pcre_options = handle_option(*s++, pcre_options);
dvander@2762
  2023
      }
dvander@2762
  2024
    }
dvander@2762
  2025
dvander@2762
  2026
  /* At this point we should have op pointing to a matched option. If the type
dvander@2762
  2027
  is NO_DATA, it means that there is no data, and the option might set
dvander@2762
  2028
  something in the PCRE options. */
dvander@2762
  2029
dvander@2762
  2030
  if (op->type == OP_NODATA)
dvander@2762
  2031
    {
dvander@2762
  2032
    pcre_options = handle_option(op->one_char, pcre_options);
dvander@2762
  2033
    continue;
dvander@2762
  2034
    }
dvander@2762
  2035
dvander@2762
  2036
  /* If the option type is OP_OP_STRING or OP_OP_NUMBER, it's an option that
dvander@2762
  2037
  either has a value or defaults to something. It cannot have data in a
dvander@2762
  2038
  separate item. At the moment, the only such options are "colo(u)r" and
dvander@2762
  2039
  Jeffrey Friedl's special -S debugging option. */
dvander@2762
  2040
dvander@2762
  2041
  if (*option_data == 0 &&
dvander@2762
  2042
      (op->type == OP_OP_STRING || op->type == OP_OP_NUMBER))
dvander@2762
  2043
    {
dvander@2762
  2044
    switch (op->one_char)
dvander@2762
  2045
      {
dvander@2762
  2046
      case N_COLOUR:
dvander@2762
  2047
      colour_option = (char *)"auto";
dvander@2762
  2048
      break;
dvander@2762
  2049
#ifdef JFRIEDL_DEBUG
dvander@2762
  2050
      case 'S':
dvander@2762
  2051
      S_arg = 0;
dvander@2762
  2052
      break;
dvander@2762
  2053
#endif
dvander@2762
  2054
      }
dvander@2762
  2055
    continue;
dvander@2762
  2056
    }
dvander@2762
  2057
dvander@2762
  2058
  /* Otherwise, find the data string for the option. */
dvander@2762
  2059
dvander@2762
  2060
  if (*option_data == 0)
dvander@2762
  2061
    {
dvander@2762
  2062
    if (i >= argc - 1 || longopwasequals)
dvander@2762
  2063
      {
dvander@2762
  2064
      fprintf(stderr, "pcregrep: Data missing after %s\n", argv[i]);
dvander@2762
  2065
      exit(usage(2));
dvander@2762
  2066
      }
dvander@2762
  2067
    option_data = argv[++i];
dvander@2762
  2068
    }
dvander@2762
  2069
dvander@2762
  2070
  /* If the option type is OP_PATLIST, it's the -e option, which can be called
dvander@2762
  2071
  multiple times to create a list of patterns. */
dvander@2762
  2072
dvander@2762
  2073
  if (op->type == OP_PATLIST)
dvander@2762
  2074
    {
dvander@2762
  2075
    if (cmd_pattern_count >= MAX_PATTERN_COUNT)
dvander@2762
  2076
      {
dvander@2762
  2077
      fprintf(stderr, "pcregrep: Too many command-line patterns (max %d)\n",
dvander@2762
  2078
        MAX_PATTERN_COUNT);
dvander@2762
  2079
      return 2;
dvander@2762
  2080
      }
dvander@2762
  2081
    patterns[cmd_pattern_count++] = option_data;
dvander@2762
  2082
    }
dvander@2762
  2083
dvander@2762
  2084
  /* Otherwise, deal with single string or numeric data values. */
dvander@2762
  2085
dvander@2762
  2086
  else if (op->type != OP_NUMBER && op->type != OP_OP_NUMBER)
dvander@2762
  2087
    {
dvander@2762
  2088
    *((char **)op->dataptr) = option_data;
dvander@2762
  2089
    }
dvander@2762
  2090
  else
dvander@2762
  2091
    {
dvander@2762
  2092
    char *endptr;
dvander@2762
  2093
    int n = strtoul(option_data, &endptr, 10);
dvander@2762
  2094
    if (*endptr != 0)
dvander@2762
  2095
      {
dvander@2762
  2096
      if (longop)
dvander@2762
  2097
        {
dvander@2762
  2098
        char *equals = strchr(op->long_name, '=');
dvander@2762
  2099
        int nlen = (equals == NULL)? (int)strlen(op->long_name) :
dvander@2762
  2100
          equals - op->long_name;
dvander@2762
  2101
        fprintf(stderr, "pcregrep: Malformed number \"%s\" after --%.*s\n",
dvander@2762
  2102
          option_data, nlen, op->long_name);
dvander@2762
  2103
        }
dvander@2762
  2104
      else
dvander@2762
  2105
        fprintf(stderr, "pcregrep: Malformed number \"%s\" after -%c\n",
dvander@2762
  2106
          option_data, op->one_char);
dvander@2762
  2107
      exit(usage(2));
dvander@2762
  2108
      }
dvander@2762
  2109
    *((int *)op->dataptr) = n;
dvander@2762
  2110
    }
dvander@2762
  2111
  }
dvander@2762
  2112
dvander@2762
  2113
/* Options have been decoded. If -C was used, its value is used as a default
dvander@2762
  2114
for -A and -B. */
dvander@2762
  2115
dvander@2762
  2116
if (both_context > 0)
dvander@2762
  2117
  {
dvander@2762
  2118
  if (after_context == 0) after_context = both_context;
dvander@2762
  2119
  if (before_context == 0) before_context = both_context;
dvander@2762
  2120
  }
dvander@2762
  2121
dvander@2762
  2122
/* Only one of --only-matching, --file-offsets, or --line-offsets is permitted.
dvander@2762
  2123
However, the latter two set the only_matching flag. */
dvander@2762
  2124
dvander@2762
  2125
if ((only_matching && (file_offsets || line_offsets)) ||
dvander@2762
  2126
    (file_offsets && line_offsets))
dvander@2762
  2127
  {
dvander@2762
  2128
  fprintf(stderr, "pcregrep: Cannot mix --only-matching, --file-offsets "
dvander@2762
  2129
    "and/or --line-offsets\n");
dvander@2762
  2130
  exit(usage(2));
dvander@2762
  2131
  }
dvander@2762
  2132
dvander@2762
  2133
if (file_offsets || line_offsets) only_matching = TRUE;
dvander@2762
  2134
dvander@2762
  2135
/* If a locale has not been provided as an option, see if the LC_CTYPE or
dvander@2762
  2136
LC_ALL environment variable is set, and if so, use it. */
dvander@2762
  2137
dvander@2762
  2138
if (locale == NULL)
dvander@2762
  2139
  {
dvander@2762
  2140
  locale = getenv("LC_ALL");
dvander@2762
  2141
  locale_from = "LCC_ALL";
dvander@2762
  2142
  }
dvander@2762
  2143
dvander@2762
  2144
if (locale == NULL)
dvander@2762
  2145
  {
dvander@2762
  2146
  locale = getenv("LC_CTYPE");
dvander@2762
  2147
  locale_from = "LC_CTYPE";
dvander@2762
  2148
  }
dvander@2762
  2149
dvander@2762
  2150
/* If a locale has been provided, set it, and generate the tables the PCRE
dvander@2762
  2151
needs. Otherwise, pcretables==NULL, which causes the use of default tables. */
dvander@2762
  2152
dvander@2762
  2153
if (locale != NULL)
dvander@2762
  2154
  {
dvander@2762
  2155
  if (setlocale(LC_CTYPE, locale) == NULL)
dvander@2762
  2156
    {
dvander@2762
  2157
    fprintf(stderr, "pcregrep: Failed to set locale %s (obtained from %s)\n",
dvander@2762
  2158
      locale, locale_from);
dvander@2762
  2159
    return 2;
dvander@2762
  2160
    }
dvander@2762
  2161
  pcretables = pcre_maketables();
dvander@2762
  2162
  }
dvander@2762
  2163
dvander@2762
  2164
/* Sort out colouring */
dvander@2762
  2165
dvander@2762
  2166
if (colour_option != NULL && strcmp(colour_option, "never") != 0)
dvander@2762
  2167
  {
dvander@2762
  2168
  if (strcmp(colour_option, "always") == 0) do_colour = TRUE;
dvander@2762
  2169
  else if (strcmp(colour_option, "auto") == 0) do_colour = is_stdout_tty();
dvander@2762
  2170
  else
dvander@2762
  2171
    {
dvander@2762
  2172
    fprintf(stderr, "pcregrep: Unknown colour setting \"%s\"\n",
dvander@2762
  2173
      colour_option);
dvander@2762
  2174
    return 2;
dvander@2762
  2175
    }
dvander@2762
  2176
  if (do_colour)
dvander@2762
  2177
    {
dvander@2762
  2178
    char *cs = getenv("PCREGREP_COLOUR");
dvander@2762
  2179
    if (cs == NULL) cs = getenv("PCREGREP_COLOR");
dvander@2762
  2180
    if (cs != NULL) colour_string = cs;
dvander@2762
  2181
    }
dvander@2762
  2182
  }
dvander@2762
  2183
dvander@2762
  2184
/* Interpret the newline type; the default settings are Unix-like. */
dvander@2762
  2185
dvander@2762
  2186
if (strcmp(newline, "cr") == 0 || strcmp(newline, "CR") == 0)
dvander@2762
  2187
  {
dvander@2762
  2188
  pcre_options |= PCRE_NEWLINE_CR;
dvander@2762
  2189
  endlinetype = EL_CR;
dvander@2762
  2190
  }
dvander@2762
  2191
else if (strcmp(newline, "lf") == 0 || strcmp(newline, "LF") == 0)
dvander@2762
  2192
  {
dvander@2762
  2193
  pcre_options |= PCRE_NEWLINE_LF;
dvander@2762
  2194
  endlinetype = EL_LF;
dvander@2762
  2195
  }
dvander@2762
  2196
else if (strcmp(newline, "crlf") == 0 || strcmp(newline, "CRLF") == 0)
dvander@2762
  2197
  {
dvander@2762
  2198
  pcre_options |= PCRE_NEWLINE_CRLF;
dvander@2762
  2199
  endlinetype = EL_CRLF;
dvander@2762
  2200
  }
dvander@2762
  2201
else if (strcmp(newline, "any") == 0 || strcmp(newline, "ANY") == 0)
dvander@2762
  2202
  {
dvander@2762
  2203
  pcre_options |= PCRE_NEWLINE_ANY;
dvander@2762
  2204
  endlinetype = EL_ANY;
dvander@2762
  2205
  }
dvander@2762
  2206
else if (strcmp(newline, "anycrlf") == 0 || strcmp(newline, "ANYCRLF") == 0)
dvander@2762
  2207
  {
dvander@2762
  2208
  pcre_options |= PCRE_NEWLINE_ANYCRLF;
dvander@2762
  2209
  endlinetype = EL_ANYCRLF;
dvander@2762
  2210
  }
dvander@2762
  2211
else
dvander@2762
  2212
  {
dvander@2762
  2213
  fprintf(stderr, "pcregrep: Invalid newline specifier \"%s\"\n", newline);
dvander@2762
  2214
  return 2;
dvander@2762
  2215
  }
dvander@2762
  2216
dvander@2762
  2217
/* Interpret the text values for -d and -D */
dvander@2762
  2218
dvander@2762
  2219
if (dee_option != NULL)
dvander@2762
  2220
  {
dvander@2762
  2221
  if (strcmp(dee_option, "read") == 0) dee_action = dee_READ;
dvander@2762
  2222
  else if (strcmp(dee_option, "recurse") == 0) dee_action = dee_RECURSE;
dvander@2762
  2223
  else if (strcmp(dee_option, "skip") == 0) dee_action = dee_SKIP;
dvander@2762
  2224
  else
dvander@2762
  2225
    {
dvander@2762
  2226
    fprintf(stderr, "pcregrep: Invalid value \"%s\" for -d\n", dee_option);
dvander@2762
  2227
    return 2;
dvander@2762
  2228
    }
dvander@2762
  2229
  }
dvander@2762
  2230
dvander@2762
  2231
if (DEE_option != NULL)
dvander@2762
  2232
  {
dvander@2762
  2233
  if (strcmp(DEE_option, "read") == 0) DEE_action = DEE_READ;
dvander@2762
  2234
  else if (strcmp(DEE_option, "skip") == 0) DEE_action = DEE_SKIP;
dvander@2762
  2235
  else
dvander@2762
  2236
    {
dvander@2762
  2237
    fprintf(stderr, "pcregrep: Invalid value \"%s\" for -D\n", DEE_option);
dvander@2762
  2238
    return 2;
dvander@2762
  2239
    }
dvander@2762
  2240
  }
dvander@2762
  2241
dvander@2762
  2242
/* Check the values for Jeffrey Friedl's debugging options. */
dvander@2762
  2243
dvander@2762
  2244
#ifdef JFRIEDL_DEBUG
dvander@2762
  2245
if (S_arg > 9)
dvander@2762
  2246
  {
dvander@2762
  2247
  fprintf(stderr, "pcregrep: bad value for -S option\n");
dvander@2762
  2248
  return 2;
dvander@2762
  2249
  }
dvander@2762
  2250
if (jfriedl_XT != 0 || jfriedl_XR != 0)
dvander@2762
  2251
  {
dvander@2762
  2252
  if (jfriedl_XT == 0) jfriedl_XT = 1;
dvander@2762
  2253
  if (jfriedl_XR == 0) jfriedl_XR = 1;
dvander@2762
  2254
  }
dvander@2762
  2255
#endif
dvander@2762
  2256
dvander@2762
  2257
/* Get memory to store the pattern and hints lists. */
dvander@2762
  2258
dvander@2762
  2259
pattern_list = (pcre **)malloc(MAX_PATTERN_COUNT * sizeof(pcre *));
dvander@2762
  2260
hints_list = (pcre_extra **)malloc(MAX_PATTERN_COUNT * sizeof(pcre_extra *));
dvander@2762
  2261
dvander@2762
  2262
if (pattern_list == NULL || hints_list == NULL)
dvander@2762
  2263
  {
dvander@2762
  2264
  fprintf(stderr, "pcregrep: malloc failed\n");
dvander@2762
  2265
  goto EXIT2;
dvander@2762
  2266
  }
dvander@2762
  2267
dvander@2762
  2268
/* If no patterns were provided by -e, and there is no file provided by -f,
dvander@2762
  2269
the first argument is the one and only pattern, and it must exist. */
dvander@2762
  2270
dvander@2762
  2271
if (cmd_pattern_count == 0 && pattern_filename == NULL)
dvander@2762
  2272
  {
dvander@2762
  2273
  if (i >= argc) return usage(2);
dvander@2762
  2274
  patterns[cmd_pattern_count++] = argv[i++];
dvander@2762
  2275
  }
dvander@2762
  2276
dvander@2762
  2277
/* Compile the patterns that were provided on the command line, either by
dvander@2762
  2278
multiple uses of -e or as a single unkeyed pattern. */
dvander@2762
  2279
dvander@2762
  2280
for (j = 0; j < cmd_pattern_count; j++)
dvander@2762
  2281
  {
dvander@2762
  2282
  if (!compile_pattern(patterns[j], pcre_options, NULL,
dvander@2762
  2283
       (j == 0 && cmd_pattern_count == 1)? 0 : j + 1))
dvander@2762
  2284
    goto EXIT2;
dvander@2762
  2285
  }
dvander@2762
  2286
dvander@2762
  2287
/* Compile the regular expressions that are provided in a file. */
dvander@2762
  2288
dvander@2762
  2289
if (pattern_filename != NULL)
dvander@2762
  2290
  {
dvander@2762
  2291
  int linenumber = 0;
dvander@2762
  2292
  FILE *f;
dvander@2762
  2293
  char *filename;
dvander@2762
  2294
  char buffer[MBUFTHIRD];
dvander@2762
  2295
dvander@2762
  2296
  if (strcmp(pattern_filename, "-") == 0)
dvander@2762
  2297
    {
dvander@2762
  2298
    f = stdin;
dvander@2762
  2299
    filename = stdin_name;
dvander@2762
  2300
    }
dvander@2762
  2301
  else
dvander@2762
  2302
    {
dvander@2762
  2303
    f = fopen(pattern_filename, "r");
dvander@2762
  2304
    if (f == NULL)
dvander@2762
  2305
      {
dvander@2762
  2306
      fprintf(stderr, "pcregrep: Failed to open %s: %s\n", pattern_filename,
dvander@2762
  2307
        strerror(errno));
dvander@2762
  2308
      goto EXIT2;
dvander@2762
  2309
      }
dvander@2762
  2310
    filename = pattern_filename;
dvander@2762
  2311
    }
dvander@2762
  2312
dvander@2762
  2313
  while (fgets(buffer, MBUFTHIRD, f) != NULL)
dvander@2762
  2314
    {
dvander@2762
  2315
    char *s = buffer + (int)strlen(buffer);
dvander@2762
  2316
    while (s > buffer && isspace((unsigned char)(s[-1]))) s--;
dvander@2762
  2317
    *s = 0;
dvander@2762
  2318
    linenumber++;
dvander@2762
  2319
    if (buffer[0] == 0) continue;   /* Skip blank lines */
dvander@2762
  2320
    if (!compile_pattern(buffer, pcre_options, filename, linenumber))
dvander@2762
  2321
      goto EXIT2;
dvander@2762
  2322
    }
dvander@2762
  2323
dvander@2762
  2324
  if (f != stdin) fclose(f);
dvander@2762
  2325
  }
dvander@2762
  2326
dvander@2762
  2327
/* Study the regular expressions, as we will be running them many times */
dvander@2762
  2328
dvander@2762
  2329
for (j = 0; j < pattern_count; j++)
dvander@2762
  2330
  {
dvander@2762
  2331
  hints_list[j] = pcre_study(pattern_list[j], 0, &error);
dvander@2762
  2332
  if (error != NULL)
dvander@2762
  2333
    {
dvander@2762
  2334
    char s[16];
dvander@2762
  2335
    if (pattern_count == 1) s[0] = 0; else sprintf(s, " number %d", j);
dvander@2762
  2336
    fprintf(stderr, "pcregrep: Error while studying regex%s: %s\n", s, error);
dvander@2762
  2337
    goto EXIT2;
dvander@2762
  2338
    }
dvander@2762
  2339
  hint_count++;
dvander@2762
  2340
  }
dvander@2762
  2341
dvander@2762
  2342
/* If there are include or exclude patterns, compile them. */
dvander@2762
  2343
dvander@2762
  2344
if (exclude_pattern != NULL)
dvander@2762
  2345
  {
dvander@2762
  2346
  exclude_compiled = pcre_compile(exclude_pattern, 0, &error, &errptr,
dvander@2762
  2347
    pcretables);
dvander@2762
  2348
  if (exclude_compiled == NULL)
dvander@2762
  2349
    {
dvander@2762
  2350
    fprintf(stderr, "pcregrep: Error in 'exclude' regex at offset %d: %s\n",
dvander@2762
  2351
      errptr, error);
dvander@2762
  2352
    goto EXIT2;
dvander@2762
  2353
    }
dvander@2762
  2354
  }
dvander@2762
  2355
dvander@2762
  2356
if (include_pattern != NULL)
dvander@2762
  2357
  {
dvander@2762
  2358
  include_compiled = pcre_compile(include_pattern, 0, &error, &errptr,
dvander@2762
  2359
    pcretables);
dvander@2762
  2360
  if (include_compiled == NULL)
dvander@2762
  2361
    {
dvander@2762
  2362
    fprintf(stderr, "pcregrep: Error in 'include' regex at offset %d: %s\n",
dvander@2762
  2363
      errptr, error);
dvander@2762
  2364
    goto EXIT2;
dvander@2762
  2365
    }
dvander@2762
  2366
  }
dvander@2762
  2367
dvander@2762
  2368
if (exclude_dir_pattern != NULL)
dvander@2762
  2369
  {
dvander@2762
  2370
  exclude_dir_compiled = pcre_compile(exclude_dir_pattern, 0, &error, &errptr,
dvander@2762
  2371
    pcretables);
dvander@2762
  2372
  if (exclude_dir_compiled == NULL)
dvander@2762
  2373
    {
dvander@2762
  2374
    fprintf(stderr, "pcregrep: Error in 'exclude_dir' regex at offset %d: %s\n",
dvander@2762
  2375
      errptr, error);
dvander@2762
  2376
    goto EXIT2;
dvander@2762
  2377
    }
dvander@2762
  2378
  }
dvander@2762
  2379
dvander@2762
  2380
if (include_dir_pattern != NULL)
dvander@2762
  2381
  {
dvander@2762
  2382
  include_dir_compiled = pcre_compile(include_dir_pattern, 0, &error, &errptr,
dvander@2762
  2383
    pcretables);
dvander@2762
  2384
  if (include_dir_compiled == NULL)
dvander@2762
  2385
    {
dvander@2762
  2386
    fprintf(stderr, "pcregrep: Error in 'include_dir' regex at offset %d: %s\n",
dvander@2762
  2387
      errptr, error);
dvander@2762
  2388
    goto EXIT2;
dvander@2762
  2389
    }
dvander@2762
  2390
  }
dvander@2762
  2391
dvander@2762
  2392
/* If there are no further arguments, do the business on stdin and exit. */
dvander@2762
  2393
dvander@2762
  2394
if (i >= argc)
dvander@2762
  2395
  {
dvander@2762
  2396
  rc = pcregrep(stdin, FR_PLAIN, (filenames > FN_DEFAULT)? stdin_name : NULL);
dvander@2762
  2397
  goto EXIT;
dvander@2762
  2398
  }
dvander@2762
  2399
dvander@2762
  2400
/* Otherwise, work through the remaining arguments as files or directories.
dvander@2762
  2401
Pass in the fact that there is only one argument at top level - this suppresses
dvander@2762
  2402
the file name if the argument is not a directory and filenames are not
dvander@2762
  2403
otherwise forced. */
dvander@2762
  2404
dvander@2762
  2405
only_one_at_top = i == argc - 1;   /* Catch initial value of i */
dvander@2762
  2406
dvander@2762
  2407
for (; i < argc; i++)
dvander@2762
  2408
  {
dvander@2762
  2409
  int frc = grep_or_recurse(argv[i], dee_action == dee_RECURSE,
dvander@2762
  2410
    only_one_at_top);
dvander@2762
  2411
  if (frc > 1) rc = frc;
dvander@2762
  2412
    else if (frc == 0 && rc == 1) rc = 0;
dvander@2762
  2413
  }
dvander@2762
  2414
dvander@2762
  2415
EXIT:
dvander@2762
  2416
if (pattern_list != NULL)
dvander@2762
  2417
  {
dvander@2762
  2418
  for (i = 0; i < pattern_count; i++) free(pattern_list[i]);
dvander@2762
  2419
  free(pattern_list);
dvander@2762
  2420
  }
dvander@2762
  2421
if (hints_list != NULL)
dvander@2762
  2422
  {
dvander@2762
  2423
  for (i = 0; i < hint_count; i++) free(hints_list[i]);
dvander@2762
  2424
  free(hints_list);
dvander@2762
  2425
  }
dvander@2762
  2426
return rc;
dvander@2762
  2427
dvander@2762
  2428
EXIT2:
dvander@2762
  2429
rc = 2;
dvander@2762
  2430
goto EXIT;
dvander@2762
  2431
}
dvander@2762
  2432
dvander@2762
  2433
/* End of pcregrep */